240 lines
8.3 KiB
JavaScript
240 lines
8.3 KiB
JavaScript
// 现代化UI组件库
|
||
const UI = {
|
||
// 初始化所有自定义下拉框
|
||
initSelects() {
|
||
document.querySelectorAll('select.ui-input, select.ui-select').forEach(select => {
|
||
if (select.dataset.uiInit) return; // 已初始化
|
||
this.createCustomSelect(select);
|
||
});
|
||
},
|
||
|
||
// 创建自定义下拉框
|
||
createCustomSelect(select) {
|
||
select.dataset.uiInit = 'true';
|
||
|
||
// 创建包装器
|
||
const wrapper = document.createElement('div');
|
||
wrapper.className = 'ui-select-wrapper';
|
||
select.parentNode.insertBefore(wrapper, select);
|
||
|
||
// 隐藏原生 select
|
||
select.classList.add('ui-select-native');
|
||
wrapper.appendChild(select);
|
||
|
||
// 创建触发器
|
||
const trigger = document.createElement('div');
|
||
trigger.className = 'ui-select-trigger';
|
||
trigger.tabIndex = 0;
|
||
wrapper.appendChild(trigger);
|
||
|
||
// 创建下拉菜单
|
||
const dropdown = document.createElement('div');
|
||
dropdown.className = 'ui-select-dropdown';
|
||
wrapper.appendChild(dropdown);
|
||
|
||
// 更新选项
|
||
const updateOptions = () => {
|
||
dropdown.innerHTML = '';
|
||
const selectedValue = select.value;
|
||
let selectedText = '';
|
||
|
||
Array.from(select.options).forEach(option => {
|
||
const optionEl = document.createElement('div');
|
||
optionEl.className = 'ui-select-option';
|
||
optionEl.dataset.value = option.value;
|
||
optionEl.textContent = option.textContent;
|
||
|
||
if (option.value === selectedValue) {
|
||
optionEl.classList.add('selected');
|
||
selectedText = option.textContent;
|
||
}
|
||
|
||
optionEl.addEventListener('click', () => {
|
||
select.value = option.value;
|
||
select.dispatchEvent(new Event('change', { bubbles: true }));
|
||
wrapper.classList.remove('open');
|
||
updateTrigger();
|
||
});
|
||
|
||
dropdown.appendChild(optionEl);
|
||
});
|
||
|
||
return selectedText;
|
||
};
|
||
|
||
// 更新触发器显示
|
||
const updateTrigger = () => {
|
||
const selectedText = updateOptions();
|
||
if (selectedText) {
|
||
trigger.innerHTML = `<span>${selectedText}</span>`;
|
||
} else {
|
||
trigger.innerHTML = `<span class="placeholder">${select.options[0]?.textContent || '请选择'}</span>`;
|
||
}
|
||
};
|
||
|
||
// 初始化
|
||
updateTrigger();
|
||
|
||
// 点击触发器
|
||
trigger.addEventListener('click', (e) => {
|
||
e.stopPropagation();
|
||
// 关闭其他下拉框
|
||
document.querySelectorAll('.ui-select-wrapper.open').forEach(w => {
|
||
if (w !== wrapper) w.classList.remove('open');
|
||
});
|
||
wrapper.classList.toggle('open');
|
||
});
|
||
|
||
// 键盘支持
|
||
trigger.addEventListener('keydown', (e) => {
|
||
if (e.key === 'Enter' || e.key === ' ') {
|
||
e.preventDefault();
|
||
wrapper.classList.toggle('open');
|
||
} else if (e.key === 'Escape') {
|
||
wrapper.classList.remove('open');
|
||
}
|
||
});
|
||
|
||
// 监听原生 select 变化
|
||
select.addEventListener('change', updateTrigger);
|
||
|
||
// 监听选项变化(用于动态更新)
|
||
const observer = new MutationObserver(updateTrigger);
|
||
observer.observe(select, { childList: true, subtree: true });
|
||
},
|
||
// 显示通知消息
|
||
notify(message, type = 'info', duration = 3000) {
|
||
const container = document.getElementById('notification-container') || this.createNotificationContainer();
|
||
const notification = document.createElement('div');
|
||
notification.className = `notification notification-${type}`;
|
||
notification.innerHTML = `
|
||
<div class="notification-content">
|
||
<span class="notification-icon">${this.getIcon(type)}</span>
|
||
<span class="notification-message">${message}</span>
|
||
</div>
|
||
`;
|
||
container.appendChild(notification);
|
||
setTimeout(() => notification.classList.add('show'), 10);
|
||
setTimeout(() => {
|
||
notification.classList.remove('show');
|
||
setTimeout(() => notification.remove(), 300);
|
||
}, duration);
|
||
},
|
||
|
||
// 显示确认对话框
|
||
confirm(title, message, onConfirm, onCancel) {
|
||
const modal = document.createElement('div');
|
||
modal.className = 'ui-modal';
|
||
modal.innerHTML = `
|
||
<div class="ui-modal-overlay"></div>
|
||
<div class="ui-modal-content ui-modal-confirm">
|
||
<h3>${title}</h3>
|
||
<p>${message}</p>
|
||
<div class="ui-modal-actions">
|
||
<button class="ui-btn ui-btn-secondary" data-action="cancel">取消</button>
|
||
<button class="ui-btn ui-btn-primary" data-action="confirm">确认</button>
|
||
</div>
|
||
</div>
|
||
`;
|
||
document.body.appendChild(modal);
|
||
setTimeout(() => modal.classList.add('show'), 10);
|
||
|
||
modal.querySelector('[data-action="confirm"]').onclick = () => {
|
||
this.closeModal(modal);
|
||
if (onConfirm) onConfirm();
|
||
};
|
||
modal.querySelector('[data-action="cancel"]').onclick = () => {
|
||
this.closeModal(modal);
|
||
if (onCancel) onCancel();
|
||
};
|
||
modal.querySelector('.ui-modal-overlay').onclick = () => {
|
||
this.closeModal(modal);
|
||
if (onCancel) onCancel();
|
||
};
|
||
},
|
||
|
||
// 显示输入对话框
|
||
prompt(title, placeholder, defaultValue, onConfirm, onCancel) {
|
||
const modal = document.createElement('div');
|
||
modal.className = 'ui-modal';
|
||
modal.innerHTML = `
|
||
<div class="ui-modal-overlay"></div>
|
||
<div class="ui-modal-content ui-modal-prompt">
|
||
<h3>${title}</h3>
|
||
<input type="text" class="ui-input" placeholder="${placeholder}" value="${defaultValue || ''}">
|
||
<div class="ui-modal-actions">
|
||
<button class="ui-btn ui-btn-secondary" data-action="cancel">取消</button>
|
||
<button class="ui-btn ui-btn-primary" data-action="confirm">确认</button>
|
||
</div>
|
||
</div>
|
||
`;
|
||
document.body.appendChild(modal);
|
||
setTimeout(() => modal.classList.add('show'), 10);
|
||
|
||
const input = modal.querySelector('.ui-input');
|
||
input.focus();
|
||
input.select();
|
||
|
||
const confirm = () => {
|
||
const value = input.value.trim();
|
||
if (value) {
|
||
this.closeModal(modal);
|
||
if (onConfirm) onConfirm(value);
|
||
}
|
||
};
|
||
|
||
modal.querySelector('[data-action="confirm"]').onclick = confirm;
|
||
input.onkeypress = (e) => { if (e.key === 'Enter') confirm(); };
|
||
modal.querySelector('[data-action="cancel"]').onclick = () => {
|
||
this.closeModal(modal);
|
||
if (onCancel) onCancel();
|
||
};
|
||
modal.querySelector('.ui-modal-overlay').onclick = () => {
|
||
this.closeModal(modal);
|
||
if (onCancel) onCancel();
|
||
};
|
||
},
|
||
|
||
// 关闭模态框
|
||
closeModal(modal) {
|
||
modal.classList.remove('show');
|
||
setTimeout(() => modal.remove(), 300);
|
||
},
|
||
|
||
// 创建通知容器
|
||
createNotificationContainer() {
|
||
const container = document.createElement('div');
|
||
container.id = 'notification-container';
|
||
document.body.appendChild(container);
|
||
return container;
|
||
},
|
||
|
||
// 获取图标
|
||
getIcon(type) {
|
||
const icons = {
|
||
success: '✓',
|
||
error: '✕',
|
||
warning: '⚠',
|
||
info: 'ℹ'
|
||
};
|
||
return icons[type] || icons.info;
|
||
}
|
||
};
|
||
|
||
// 点击外部关闭下拉框
|
||
document.addEventListener('click', (e) => {
|
||
if (!e.target.closest('.ui-select-wrapper')) {
|
||
document.querySelectorAll('.ui-select-wrapper.open').forEach(w => {
|
||
w.classList.remove('open');
|
||
});
|
||
}
|
||
});
|
||
|
||
// 页面加载完成后自动初始化下拉框
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
UI.initSelects();
|
||
});
|
||
|
||
// 提供手动刷新方法(用于动态加载的内容)
|
||
UI.refreshSelects = UI.initSelects;
|