(function() { // 基础配置 const scriptBaseUrl = getScriptBaseUrl(); const API_CONFIG = { listEndpoint: scriptBaseUrl + '/admin/images/list', uploadEndpoint: scriptBaseUrl + '/admin/images/upload' }; // 组件状态 const state = { targetInputId: null, currentPage: 1, totalItems: 0, totalPages: 0, mediaItems: [], isLoading: false, activeTab: 'gallery' }; let closeBtn = null; let resetBtn = null; // 获取脚本基础URL function getScriptBaseUrl() { const script = document.currentScript; if (!script || !script.src) { console.warn('无法获取当前脚本URL,使用相对路径'); return ''; } // 使用URL对象解析完整路径 const url = new URL(script.src); // 拼接 协议 + 域名 + 端口(如果有) const domain = `${url.protocol}//${url.host}`; return domain; } // 工具函数 function formatFileSize(bytes) { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(0)) + ' ' + sizes[i]; } function isImageFile(file) { const extension = file.name.split('.').pop().toLowerCase(); const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'heic', 'heif', 'avif']; return imageExtensions.includes(extension) || /image\/.*/.test(file.type); } // 样式和布局相关 function createStyles() { const link = document.createElement('link'); link.rel = 'stylesheet'; link.href = scriptBaseUrl + '/Static/css/controller.css'; document.head.appendChild(link); } // 防止地址栏遮挡 function setVh() { document.documentElement.style.setProperty('--vh', `${window.innerHeight * 0.01}px`); } // 注册事件监听 window.addEventListener('resize', setVh); window.addEventListener('orientationchange', setVh); function createIconWrapper(className, iconType) { const wrapper = document.createElement('div'); wrapper.className = className; wrapper.replaceChildren(createIconNode(iconType)); return wrapper; } function createImageElement(src, alt = '', className = '', width = '') { const image = document.createElement('img'); image.src = src; image.alt = alt; if (width) { image.width = Number(width); } return image; } function createIconNode(type) { switch (type) { case 'close': return createImageElement('https://yanxuan.nosdn.127.net/0c64ce12c71cf276504cb2e15164d4ff.png', '', '', '25'); case 'reset': return createImageElement('https://yanxuan.nosdn.127.net/8296ea39d8c548ed320f79a483756cd9.jpg', '', '', '25'); case 'preview': return createImageElement('https://yanxuan.nosdn.127.net/520b793c329df6349a25404efefbd0f4.png', '', 'ja-action-icon'); case 'copy': return createImageElement('https://yanxuan.nosdn.127.net/0732b6e6d1f249dad11ac7d004af5964.png', '', 'ja-action-icon'); case 'upload': return createImageElement('https://yanxuan.nosdn.127.net/94494a4dc561ae21157f50764ddf035e.png', '', 'ja-button-icon'); case 'error': return createImageElement('https://yanxuan.nosdn.127.net/74777259ff515f056e8fc340df28fcd7.png', '', 'ja-erro-icon'); default: return document.createTextNode(''); } } function createUploadingIcon() { return createFontIcon('uploading-icon'); } function createLoadingContainer(message = '加载中...') { const container = document.createElement('div'); container.className = 'ja-loading-container'; const icon = document.createElement('i'); icon.className = 'loading-icon'; const text = document.createElement('p'); text.className = 'ja-loading-text'; text.textContent = message; container.append(icon, text); return container; } function createStatusContainer(type, message) { const container = document.createElement('div'); container.className = `ja-${type}-container`; container.appendChild(createIconNode('error')); const text = document.createElement('p'); text.className = `ja-${type}-text`; text.textContent = String(message || ''); container.appendChild(text); return container; } function createMediaCard(item) { const itemElement = document.createElement('div'); itemElement.className = 'ja-media-item'; const thumbnail = document.createElement('div'); thumbnail.className = 'ja-item-thumbnail'; const image = document.createElement('img'); image.src = item.url; image.alt = item.name; image.className = 'ja-thumbnail-image'; image.loading = 'lazy'; const overlay = document.createElement('div'); overlay.className = 'ja-item-overlay'; const copyButton = document.createElement('button'); copyButton.className = 'ja-copy-button'; copyButton.dataset.url = item.url; copyButton.appendChild(createIconNode('copy')); const previewButton = document.createElement('button'); previewButton.className = 'ja-preview-button'; previewButton.dataset.url = item.url; previewButton.appendChild(createIconNode('preview')); overlay.append(copyButton, previewButton); thumbnail.append(image, overlay); const info = document.createElement('div'); info.className = 'ja-item-info'; const name = document.createElement('div'); name.className = 'ja-item-name'; name.textContent = item.name; const details = document.createElement('div'); details.className = 'ja-item-details'; const size = document.createElement('span'); size.className = 'ja-item-size'; size.textContent = formatFileSize(item.size); const dimensions = document.createElement('span'); dimensions.className = 'ja-item-dimensions'; dimensions.textContent = `${item.width}*${item.height}`; details.append(size, dimensions); info.append(name, details); itemElement.append(thumbnail, info); return itemElement; } function createUploadErrorItem(fileName, reason) { const errorItem = document.createElement('div'); errorItem.className = 'ja-upload-error'; const header = document.createElement('div'); header.className = 'ja-error-header'; const name = document.createElement('span'); name.className = 'ja-error-filename'; name.textContent = fileName; const status = document.createElement('span'); status.className = 'ja-error-status'; status.textContent = '错误'; header.append(name, status); const message = document.createElement('div'); message.className = 'ja-error-message'; message.textContent = reason; errorItem.append(header, message); return errorItem; } function createUploadSuccessItem(data) { const wrapper = document.createElement('div'); wrapper.className = 'ja-media-item'; const thumbnail = document.createElement('div'); thumbnail.className = 'ja-item-thumbnail relative'; const image = document.createElement('img'); image.src = data.url; image.alt = data.name; image.className = 'ja-thumbnail-image'; const overlay = document.createElement('div'); overlay.className = 'ja-item-overlay'; const copyButton = document.createElement('button'); copyButton.className = 'ja-copy-button'; copyButton.dataset.url = data.url; copyButton.appendChild(createIconNode('copy')); const previewButton = document.createElement('button'); previewButton.className = 'ja-preview-button'; previewButton.dataset.url = data.url; previewButton.appendChild(createIconNode('preview')); overlay.append(copyButton, previewButton); thumbnail.append(image, overlay); const info = document.createElement('div'); info.className = 'ja-item-info'; const detailTop = document.createElement('div'); detailTop.className = 'ja-item-details'; const name = document.createElement('span'); name.className = 'ja-item-name'; name.textContent = data.name; const success = document.createElement('span'); success.className = 'ja-upload-success'; success.textContent = '已完成'; detailTop.append(name, success); const detailBottom = document.createElement('div'); detailBottom.className = 'ja-item-details'; const size = document.createElement('span'); size.className = 'ja-item-size'; size.textContent = formatFileSize(data.size || 0); const dimensions = document.createElement('span'); dimensions.className = 'ja-item-dimensions'; dimensions.textContent = `${data.width}*${data.height}`; detailBottom.append(size, dimensions); info.append(detailTop, detailBottom); wrapper.append(thumbnail, info); return wrapper; } function createUploadFailureContent(message) { const fragment = document.createDocumentFragment(); const header = document.createElement('div'); header.className = 'ja-upload-header'; const fileName = document.createElement('span'); fileName.className = 'ja-upload-filename'; const failure = document.createElement('span'); failure.className = 'ja-upload-failure'; header.append(fileName, failure); const detail = document.createElement('div'); detail.className = 'ja-upload-error-message'; detail.textContent = message; fragment.append(header, detail); return fragment; } function createPreviewModal(url) { const modal = document.createElement('div'); modal.className = 'ja-image-preview-modal'; const content = document.createElement('div'); content.className = 'ja-modal-content'; const image = document.createElement('img'); image.src = url; image.alt = '预览图片'; image.className = 'ja-modal-image'; const button = document.createElement('button'); button.className = 'ja-close-button'; button.appendChild(createIconNode('close')); content.append(image, button); modal.appendChild(content); return { modal, content, image, button }; } // UI 创建函数 - 所有ID和类名添加ja-前缀并移除avif function createUI() { // 动态插入 CSS,禁止顶部下拉刷新 const style = document.createElement('style'); style.textContent = ` html, body { overscroll-behavior-y: contain; } `; // 创建基础容器 const wrapper = document.createElement('div'); wrapper.id = 'ja-uploader'; const modal = document.createElement('div'); modal.className = 'ja-modal'; modal.setAttribute('role', 'dialog'); const content = document.createElement('div'); content.className = 'ja-up-modal-content'; // 创建关闭按钮 closeBtn = document.createElement('button'); closeBtn.className = 'ja-modal-close'; closeBtn.appendChild(createIconNode('close')); // 创建重置按钮 resetBtn = document.createElement('button'); resetBtn.className = 'ja-img-close'; resetBtn.setAttribute('data-action', 'reset'); resetBtn.appendChild(createIconNode('reset')); // 标签页导航 const tabs = document.createElement('div'); tabs.className = 'ja-tabs'; const galleryTab = document.createElement('button'); galleryTab.className = 'ja-tab-button ja-tab-button-active'; galleryTab.dataset.tab = 'gallery'; galleryTab.textContent = '图库'; const uploadTab = document.createElement('button'); uploadTab.className = 'ja-tab-button'; uploadTab.dataset.tab = 'upload'; uploadTab.textContent = '上传'; // 图库内容 const galleryContent = document.createElement('div'); galleryContent.className = 'ja-tab-content ja-tab-content-active'; galleryContent.dataset.tab = 'gallery'; const mediaGrid = document.createElement('div'); mediaGrid.className = 'mb-2'; mediaGrid.id = 'ja-media-grid'; // 上传内容 const uploadContent = document.createElement('div'); uploadContent.className = 'ja-tab-content'; uploadContent.dataset.tab = 'upload'; // 上传参数 const params = document.createElement('div'); params.className = 'ja-params'; const qualityLabel = document.createElement('label'); qualityLabel.className = 'ja-param-label'; const qualityTitle = document.createElement('span'); qualityTitle.className = 'ja-param-title'; qualityTitle.textContent = '质量设置 (1-100)'; const qualityWrapper = document.createElement('div'); qualityWrapper.className = 'ja-range-wrapper'; const qualityRange = document.createElement('input'); qualityRange.type = 'range'; qualityRange.id = 'ja-quality'; qualityRange.min = '1'; qualityRange.max = '100'; qualityRange.value = '60'; qualityRange.className = 'ja-range-input'; const qualityValue = document.createElement('span'); qualityValue.id = 'ja-quality-value'; qualityValue.className = 'ja-range-span'; qualityValue.textContent = '60'; qualityWrapper.append(qualityRange, qualityValue); qualityLabel.append(qualityTitle, qualityWrapper); const widthLabel = document.createElement('label'); widthLabel.className = 'ja-param-label'; const widthTitle = document.createElement('span'); widthTitle.className = 'ja-param-title'; widthTitle.textContent = '转换宽度'; const widthInput = document.createElement('input'); widthInput.type = 'number'; widthInput.id = 'ja-width'; widthInput.placeholder = '留空为自动'; widthInput.className = 'ja-text-input'; widthLabel.append(widthTitle, widthInput); const heightLabel = document.createElement('label'); heightLabel.className = 'ja-param-label'; const heightTitle = document.createElement('span'); heightTitle.className = 'ja-param-title'; heightTitle.textContent = '转换高度'; const heightInput = document.createElement('input'); heightInput.type = 'number'; heightInput.id = 'ja-height'; heightInput.placeholder = '留空为自动'; heightInput.className = 'ja-text-input'; heightLabel.append(heightTitle, heightInput); // 上传控件 const uploadInput = document.createElement('input'); uploadInput.type = 'file'; uploadInput.id = 'ja-upload-input'; uploadInput.multiple = true; uploadInput.accept = 'image/*'; uploadInput.className = 'ja-hidden'; const uploadBtn = document.createElement('button'); uploadBtn.id = 'ja-upload-btn'; uploadBtn.className = 'ja-button ja-button-primary'; const uploadSvg = createIconWrapper('ja-upload-svg', 'upload'); uploadSvg.id = 'ja-upload-svg'; const uploadStatus = document.createElement('div'); uploadStatus.id = 'ja-upload-status'; uploadStatus.textContent = '开始上传'; uploadBtn.append(uploadSvg, uploadStatus); const uploadResults = document.createElement('div'); uploadResults.id = 'ja-upload-results'; uploadResults.className = 'ja-upload-results'; // 加载更多按钮 const loadMoreBtn = document.createElement('button'); loadMoreBtn.id = 'ja-load-more'; loadMoreBtn.className = 'ja-load-more ja-button ja-button-secondary mt-4'; const loadMoreIcon = document.createElement('i'); loadMoreIcon.className = 'fa fa-refresh mr-2'; loadMoreBtn.append(loadMoreIcon, document.createTextNode(' 加载更多')); document.head.appendChild(style); // 统一建立父子关系 // 标签页按钮 tabs.appendChild(galleryTab); tabs.appendChild(uploadTab); // 图库内容 galleryContent.appendChild(mediaGrid); galleryContent.appendChild(loadMoreBtn); // 上传参数 params.appendChild(qualityLabel); params.appendChild(widthLabel); params.appendChild(heightLabel); // 上传内容 uploadContent.appendChild(params); uploadContent.appendChild(uploadBtn); uploadContent.appendChild(uploadInput); uploadContent.appendChild(uploadResults); // 主内容区域 content.appendChild(closeBtn); content.appendChild(resetBtn); content.appendChild(tabs); content.appendChild(galleryContent); content.appendChild(uploadContent); // 模态框组装 modal.appendChild(content); wrapper.appendChild(modal); // 添加到文档 document.body.appendChild(wrapper); } function updateButtonVisibility(showReset) { if (showReset) { closeBtn.style.display = 'none'; resetBtn.style.display = 'block'; } else { closeBtn.style.display = 'block'; resetBtn.style.display = 'none'; } } // 存储表单状态到本地存储 - 键名移除avif function saveFormState(width, height) { localStorage.setItem('ja-FormState', JSON.stringify({ width, height })); } // 从本地存储获取表单状态 - 键名移除avif function loadFormState() { const state = localStorage.getItem('ja-FormState'); return state ? JSON.parse(state) : { width: '', height: '' }; } // 设置事件监听 - 选择器同步更新 function setupEventListeners() { const qualityInput = document.getElementById('ja-quality'); const qualityValue = document.getElementById('ja-quality-value'); if (qualityInput && qualityValue) { qualityInput.addEventListener('input', function() { qualityValue.textContent = this.value; }); } // 统一处理所有点击事件 document.querySelector('#ja-uploader').addEventListener('click', (e) => { const target = e.target; // 1. 模态框背景点击(关闭模态框) if (target === document.querySelector('.ja-modal')) { toggleModal(); } // else if (target === document.querySelector('.ja-modal-close')) { toggleModal(); } // 2. 标签页切换 else if (target.classList.contains('ja-tab-button')) { const tab = target.dataset.tab; switchTab(tab); // 切换到图库时重新获取列表 if (tab === 'gallery') { fetchMediaList(); } } // 3. 预览按钮 else if (target.classList.contains('ja-preview-button')) { const url = target.dataset.url; previewFile(url); } // 4. 填充按钮 else if (target.classList.contains('ja-copy-button')) { const url = target.dataset.url; urlToPage(url); } // 5. 上传按钮 else if (target.id === 'ja-upload-btn') { document.getElementById('ja-upload-input').click(); } // 6. 上传结果中的关闭按钮 else if (target.closest('.ja-img-close')) { resetUploadForm(); } }); // 文件选择事件 document.getElementById('ja-upload-input').addEventListener('change', handleFileSelect); // 加载更多按钮点击事件 document.getElementById('ja-load-more').addEventListener('click', loadMoreImages); } // 核心功能函数:初始化图库 function Gallery(inputId) { const targetInput = document.getElementById(inputId); if (!targetInput) { showToast('输入框不存在', 'error'); return; } state.targetInputId = inputId; toggleModal(); } // 切换模态窗口 function toggleModal() { fetchMediaList(); const modal = document.querySelector('.ja-modal'); modal.classList.toggle('ja-modal-active'); setVh(); if (!modal.classList.contains('ja-modal-active')) { resetUploadForm(); } resetUploadForm(); } function resetUploadForm() { // 从本地存储加载表单状态 const { width, height } = loadFormState(); // 清空上传结果 const uploadResults = document.getElementById('ja-upload-results'); if (uploadResults) uploadResults.replaceChildren(); // 清空文件选择 const uploadInput = document.getElementById('ja-upload-input'); if (uploadInput) uploadInput.value = ''; // 重置质量滑块 const qualityInput = document.getElementById('ja-quality'); if (qualityInput) qualityInput.value = 60; // 填充宽度和高度 const widthInput = document.getElementById('ja-width'); const heightInput = document.getElementById('ja-height'); if (widthInput) widthInput.value = width ; if (heightInput) heightInput.value = height ; } // 切换标签 function switchTab(tab) { state.activeTab = tab; document.querySelectorAll('.ja-tab-button').forEach(btn => { btn.classList.toggle('ja-tab-button-active', btn.dataset.tab === tab); }); document.querySelectorAll('.ja-tab-content').forEach(content => { content.classList.toggle('ja-tab-content-active', content.dataset.tab === tab); }); } // 获取图片列表 function fetchMediaList() { if (state.isLoading) return; state.isLoading = true; const grid = document.getElementById('ja-media-grid'); const loadMoreBtn = document.getElementById('ja-load-more'); const isInitialLoad = state.currentPage === 1; // 首次加载显示加载状态 if (isInitialLoad) { grid.replaceChildren(createLoadingContainer()); // 初始加载时隐藏加载更多按钮 loadMoreBtn.style.display = 'none'; } else { // 非首次加载时显示加载状态 const loadingIndicator = createLoadingContainer(); grid.appendChild(loadingIndicator); } const itemsPerPage = 20; const params = new URLSearchParams({ page: state.currentPage, limit: itemsPerPage }); fetch(`${API_CONFIG.listEndpoint}?${params}`) .then(response => response.json()) .then(data => { if (data.status === 'success') { state.currentPage = data.page; state.totalItems = data.total; state.totalPages = Math.ceil(data.total / itemsPerPage); // 合并数据 const previousItemCount = state.mediaItems.length; state.mediaItems = isInitialLoad ? data.data : [...state.mediaItems, ...data.data]; renderMediaGrid(); // 显示加载更多按钮 loadMoreBtn.style.display = 'block'; // 控制加载更多按钮显示 if (state.currentPage >= state.totalPages) { loadMoreBtn.replaceChildren(); const doneIcon = document.createElement('i'); doneIcon.className = 'fa fa-check mr-2'; loadMoreBtn.append(doneIcon, document.createTextNode(' 没有更多图片了')); loadMoreBtn.disabled = true; loadMoreBtn.classList.add('opacity-50', 'cursor-not-allowed'); } else { loadMoreBtn.replaceChildren(); const refreshIcon = document.createElement('i'); refreshIcon.className = 'fa fa-refresh mr-2'; loadMoreBtn.append(refreshIcon, document.createTextNode(' 加载更多')); loadMoreBtn.disabled = false; loadMoreBtn.classList.remove('opacity-50', 'cursor-not-allowed'); } // 恢复滚动位置 if (!isInitialLoad) { const newItemsCount = state.mediaItems.length - previousItemCount; const itemHeight = 150; const newContentHeight = newItemsCount * itemHeight; grid.scrollTop += newContentHeight; } } else { // 错误处理 grid.replaceChildren(createStatusContainer('error', data.message)); loadMoreBtn.style.display = 'none'; } }) .catch(error => { console.error('获取图片列表失败:', error); grid.replaceChildren(createStatusContainer('error', '网络错误,请重试')); loadMoreBtn.style.display = 'none'; }) .finally(() => { state.isLoading = false; }); } // 加载更多函数 function loadMoreImages() { if (state.isLoading || state.currentPage >= state.totalPages) return; const grid = document.getElementById('ja-media-grid'); state.currentPage++; // 临时禁用滚动事件 grid.style.overflowY = 'hidden'; fetchMediaList(); // 恢复滚动 setTimeout(() => { grid.style.overflowY = 'auto'; }, 100); } // 渲染图片网格 function renderMediaGrid() { const grid = document.getElementById('ja-media-grid'); const itemsToRender = state.mediaItems; grid.replaceChildren(); if (itemsToRender.length === 0) { // 空状态显示 grid.replaceChildren(createStatusContainer('empty', '没有找到图片')); return; } // 正常渲染图片列表 itemsToRender.forEach(item => { const itemElement = createMediaCard(item); grid.appendChild(itemElement); }); } // 处理文件选择 function handleFileSelect(e) { const files = e.target.files; if (!files.length) return; const validFiles = []; const invalidFiles = []; Array.from(files).forEach(file => { if (!isImageFile(file)) { invalidFiles.push({ file, reason: '不支持的文件类型,仅支持图片' }); return; } if (file.size > 10 * 1024 * 1024) { invalidFiles.push({ file, reason: '文件过大,最大支持10MB' }); return; } validFiles.push(file); }); if (invalidFiles.length > 0) { const uploadResults = document.getElementById('ja-upload-results'); invalidFiles.forEach(({ file, reason }) => { const errorItem = createUploadErrorItem(file.name, reason); uploadResults.appendChild(errorItem); }); } if (validFiles.length > 0) { uploadFiles(validFiles); } } // 上传文件 function uploadFiles(files) { console.log('uploadEndpoint:', API_CONFIG.uploadEndpoint); const quality = document.getElementById('ja-quality').value; const width = document.getElementById('ja-width').value; const height = document.getElementById('ja-height').value; const uploadResults = document.getElementById('ja-upload-results'); saveFormState(width, height); uploadResults.replaceChildren(); files.forEach(file => { const formData = new FormData(); formData.append('image', file); formData.append('quality', quality); if (width) formData.append('width', width); if (height) formData.append('height', height); const progressItem = document.createElement('div'); progressItem.className = 'ja-upload-item'; uploadResults.appendChild(progressItem); const xhr = new XMLHttpRequest(); xhr.open('POST', API_CONFIG.uploadEndpoint, true); // 设置超时时间 xhr.timeout = 120000; // 120秒超时 // 超时处理 xhr.ontimeout = function() { updateButtonVisibility(true); resetUploadButton(); handleUploadError(file, '等待返回超时,已在后台处理,稍后到图片列表中查看;如果没有则是后台处理失败;大部分问题是图片过大', progressItem); }; xhr.upload.addEventListener('progress', (e) => { if (e.lengthComputable) { const percentComplete = (e.loaded / e.total) * 100; const ja = window.ja; progressItem.style.width = `${percentComplete}%`; if (ja.statusText) { ja.statusText.textContent = `上传中 ${Math.round(percentComplete)}%`; ja.upsvg.replaceChildren(createUploadingIcon()); } } }); xhr.onload = function() { // 重置上传按钮状态 resetUploadButton(); // 处理成功响应 if (xhr.status >= 200 && xhr.status < 300) { try { const response = JSON.parse(xhr.responseText); handleUploadSuccess(response, progressItem); } catch (error) { console.error('解析响应失败:', error); console.log('原始响应:', xhr.responseText); // 检查是否是HTML响应 if (xhr.responseText.includes(' 1) { // 如果不在第一页,更新总页数 state.totalPages = Math.ceil(state.totalItems / 20); } } // 处理上传失败 function handleUploadError(file, message, progressItem) { progressItem.className = 'ja-upload-item ja-upload-error'; progressItem.replaceChildren(createUploadFailureContent(message)); } // 预览图片 function previewFile(url) { const { modal, content: modalContent, image, button: closeButton } = createPreviewModal(url); // 关闭模态窗口 closeButton.addEventListener('click', () => { modal.remove(); }); // 点击背景关闭 modal.addEventListener('click', (e) => { if (e.target === modal) { modal.remove(); } }); document.body.appendChild(modal); } // 填充到表单 function urlToPage(text) { if (typeof window.SetImageUrl === 'function') { SetImageUrl(text); } else { navigator.clipboard.writeText(text); showToast('没有找到接收函数,URL已复制到剪贴板', 'success'); } toggleModal(); // 关闭模态框 } // 显示提示消息 function showToast(message, type = 'info') { const toast = document.createElement('div'); toast.className = `ja-toast ja-toast-${type}`; toast.textContent = message; document.body.appendChild(toast); setTimeout(() => { toast.classList.add('ja-toast-visible'); }, 10); setTimeout(() => { toast.classList.remove('ja-toast-visible'); setTimeout(() => { document.body.removeChild(toast); }, 2000); }, 300); } // 初始化 function init() { createStyles(); createUI(); setupEventListeners(); setVh(); updateButtonVisibility(false); } // 执行初始化 init(); function SetImageUrl(url) { if (SetImageUrl.inputElement) { SetImageUrl.inputElement.value = url; } if (SetImageUrl.previewElement) { SetImageUrl.previewElement.src = url; } } function OpenGallery(inputId, previewId) { SetImageUrl.inputElement = document.getElementById(inputId); SetImageUrl.previewElement = previewId !== undefined ? document.getElementById(previewId) : null; toggleModal(); } window.SetImageUrl = SetImageUrl; window.OpenGallery = OpenGallery; window.ja = { statusText: document.getElementById('ja-upload-status'), upsvg: document.getElementById('ja-upload-svg') }; })();