/** * Copyright 2012-2026 ShopeX (https://www.shopex.cn) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ class Modal { constructor() { this.modal = document.getElementById('commonModal') this.mask = this.modal.querySelector('.modal-mask') this.container = this.modal.querySelector('.modal-container') this.closeBtn = this.modal.querySelector('.modal-close') this.confirmBtn = this.modal.querySelector('.modal-confirm') this.cancleBtn = this.modal.querySelector('.modal-cancle') this.title = this.modal.querySelector('.modal-title') this.tips = this.modal.querySelector('.modal-tips') this.content = this.modal.querySelector('.modal-content') this.bindEvents() } bindEvents() { this.closeBtn.onclick = () => modal.hide() this.mask.onclick = () => modal.hide() } show(options) { const { title = '', tips = '', fields = [], // 表单字段配置数组 onConfirm = () => {}, onCancel = () => {}, confirmText = '确定', cancelText = '取消', isShowCancleBtn = false } = options this.title.textContent = title if (tips) { this.tips.textContent = tips } // 生成表单内容 const formContent = fields .map((field) => { const { type, name, label, placeholder = '', options = [], required = false, html = '', append = '' } = field let inputHtml = '' if (type === 'input') { inputHtml = ` ` if (append) { inputHtml += append; } return ` ` } else if (type === 'textarea') { inputHtml = ` ` return ` ` } else if (type === 'select') { const optionsHtml = options .map((opt) => ``) .join('') inputHtml = ` ` return ` ` } else if (type === 'date') { inputHtml = ` ` return ` ` } else if (type === 'html') { inputHtml = html return ` ` } else if (type === 'image') { return ` `; } }) .join('') this.content.innerHTML = formContent this.confirmBtn.innerHTML = confirmText this.cancleBtn.innerHTML = cancelText this.cancleBtn.style.display = isShowCancleBtn ? 'block' : 'none' // 绑定确认按钮事件 const submitForm = async (cb) => { const formData = {} let isValid = true for (const field of fields) { if (field.type === 'image') { const fileInput = this.modal.querySelector('input[type="file"]'); // 直接使用保存的 base64 列表 if (field.required && fileInput.uploadedBase64List.length === 0) { showToast(`请上传${field.label}`) isValid = false return } if (field.required && fileInput.uploadedBase64List.length > 4) { showToast(`图片最多上传4张`) isValid = false return } formData[field.name] = fileInput.uploadedBase64List || []; } else if (field.type !== 'html') { const element = this.content.querySelector(`[name="${field.name}"]`) const value = element.value.trim() if (field.required && !value) { showToast(`请${field.type === 'select' ? '选择' : '填写'}${field.label}`) isValid = false return } formData[field.name] = value } } if (isValid && cb) { cb(formData) } } this.confirmBtn.onclick = () => { submitForm(onConfirm) } this.cancleBtn.onclick = () => { // 取消按钮事件 submitForm(onCancel) } this.modal.style.display = 'block' // 初始化自定义select组件 - 解决Mac版微信小程序兼容性问题 this.initCustomSelects(); // 初始化图片上传 this.initFileUpload(); } hide() { this.modal.style.display = 'none' } // 图片上传 initFileUpload() { const fileInputs = this.modal.querySelectorAll('input[type="file"]'); if(fileInputs.length === 0) { return; } fileInputs.forEach(input => { const previewContainer = input.parentElement.querySelector('.file-preview-container'); const uploadBtn = document.createElement('label'); uploadBtn.className = 'upload-btn'; uploadBtn.setAttribute('for', `file-input-${Date.now()}`); // 生成唯一ID uploadBtn.innerHTML = `+`; input.id = uploadBtn.getAttribute('for'); // 设置对应的ID previewContainer.appendChild(uploadBtn); input.style.display = 'none'; // 存储所有已上传文件的 base64 数据 input.uploadedBase64List = []; // 存储已上传的文件对象 let uploadedFiles = []; uploadBtn.addEventListener('click', (e) => { e.preventDefault(); // 阻止事件冒泡 e.stopPropagation(); // 阻止事件传播 input.click(); }); input.addEventListener('change', (e) => { const newFiles = Array.from(e.target.files); newFiles.forEach(file => { if (!file.type.startsWith('image/')) { showToast('请上传图片文件'); return; } if (uploadedFiles.length >= 3) { showToast('最多上传4张图片'); return; } const reader = new FileReader(); reader.onload = (e) => { const base64Data = e.target.result; // 保存 base64 数据 input.uploadedBase64List.push(base64Data); const previewWrapper = document.createElement('div'); previewWrapper.className = 'file-preview-wrapper'; const preview = document.createElement('img'); preview.src = base64Data; preview.className = 'file-preview'; const deleteBtn = document.createElement('span'); deleteBtn.className = 'file-delete-btn'; deleteBtn.innerHTML = '×'; deleteBtn.onclick = () => { previewWrapper.remove(); // 从已上传文件中移除 const index = uploadedFiles.indexOf(file); if (index > -1) { uploadedFiles.splice(index, 1); input.uploadedBase64List.splice(index, 1); } // 显示上传按钮 if(uploadedFiles.length < 4) { uploadBtn.style.display = 'flex'; } }; previewWrapper.appendChild(preview); previewWrapper.appendChild(deleteBtn); previewContainer.insertBefore(previewWrapper, uploadBtn); // 添加到已上传文件 uploadedFiles.push(file); // 隐藏上传按钮 if(uploadedFiles.length >= 3) { uploadBtn.style.display = 'none'; } }; reader.readAsDataURL(file); }); }); }); } // 初始化自定义select组件 - 解决Mac版微信小程序兼容性问题 initCustomSelects() { // 检查是否有自定义select组件可用 if (typeof window.initCustomSelects === 'function') { // 只初始化弹出框内的select组件 const modalSelects = this.modal.querySelectorAll('select.select-box'); if (modalSelects.length > 0) { modalSelects.forEach(select => { // 检查是否已经初始化过 if (select.dataset.customSelectInitialized !== 'true') { try { new window.CustomSelect(select, { onChange: (value, text) => { // 触发原生select的change事件,保持兼容性 const changeEvent = new Event('change', { bubbles: true }); select.dispatchEvent(changeEvent); if (window.customSelectDebug) { console.log('Modal select changed:', select.name, value, text); } } }); select.dataset.customSelectInitialized = 'true'; } catch (error) { console.error('Failed to initialize custom select in modal:', error); } } }); } } } } // 请求配置和方法封装 const request = axios.create({ baseURL: '/api', // 设置基础URL timeout: 30000, // 设置超时时间 headers: { 'Content-Type': 'multipart/form-data' }, // 兼容对象数组 // transformRequest: [ // function (data) { // // 将请求数据转换为FormData格式 // const formData = new FormData() // const appendData = (key, value) => { // if (value === null || value === undefined) { // return // } // if (Array.isArray(value)) { // // 处理数组 // value.forEach((item, index) => { // if (typeof item === 'object' && item !== null) { // // 处理对象数组,使用 key[index][property] 的格式 // Object.keys(item).forEach(prop => { // formData.append(`${key}[${index}][${prop}]`, item[prop]) // }) // } else { // // 处理普通数组 // formData.append(`${key}[${index}]`, item) // } // }) // } else if (typeof value === 'object' && value !== null) { // // 处理对象 // Object.keys(value).forEach(prop => { // appendData(`[${prop}][${key}]`, value[prop]) // }) // } else { // // 处理基本类型 // formData.append(key, value) // } // } // Object.keys(data).forEach(key => { // appendData(key, data[key]) // }) // return formData // } // ] }) // 请求拦截器 request.interceptors.request.use( (config) => { // 可以在这里添加loading状态 // 添加token等通用headers return config }, (error) => { return Promise.reject(error) } ) // 响应拦截器 request.interceptors.response.use( (response) => { // 可以统一处理响应数据 if (response.data.rsp != 'fail') { return response.data } // 处理业务错误 Toast.error(response.data.msg || '请求失败') return Promise.reject(response.data) }, (error) => { // 处理网络错误 Toast.error('网络请求失败,请稍后重试') return Promise.reject(error) } ) // Toast提示组件 const Toast = { error(msg) { // 这里可以替换成你项目中使用的提示组件 console.error(msg) } } // 提示Toast封装 const showToast = (msg) => { const toast = document.getElementById('toast') toast.classList.add('show') toast.textContent = msg // 动画结束后移除类名和隐藏元素 setTimeout(() => { toast.classList.remove('show') }, 1500) } const showLoading = (msg) => { const toast = document.getElementById('toast') toast.classList.add('show') toast.textContent = msg } const hideLoading = (msg) => { const toast = document.getElementById('toast') toast.classList.remove('show') } document.addEventListener('DOMContentLoaded', () => { document.querySelector('.icon-back').onclick = () => { history.back() } document.querySelector('.icon-refresh').onclick = () => { location.reload() } }) // 备注展开收缩功能 const toggleRemark = () => { const remarkContents = document.querySelectorAll('.remark-content'); if (remarkContents.length > 0) { remarkContents.forEach(content => { // 默认收起 content.classList.add('collapsed'); // 找到相邻的箭头图标 const arrowIcon = content.nextElementSibling; if (arrowIcon && arrowIcon.classList.contains('arrow-icon')) { // 默认箭头朝上 // arrowIcon.classList.add('expanded'); // 添加点击事件 arrowIcon.addEventListener('click', function() { // 切换展开/收缩状态 if (content.classList.contains('expanded')) { content.classList.remove('expanded'); content.classList.add('collapsed'); arrowIcon.classList.remove('expanded'); } else { content.classList.remove('collapsed'); content.classList.add('expanded'); arrowIcon.classList.add('expanded'); } }); } }); } } // export { Modal, request, Toast, showToast } // 如果你想让这些在全局可用,可以添加到 window 对象 window.Modal = Modal window.request = request window.Toast = Toast window.showToast = showToast window.showLoading = showLoading window.hideLoading = hideLoading window.toggleRemark = toggleRemark