diff --git a/ui/src/components/artwork/ArtworkInfoPanel.vue b/ui/src/components/artwork/ArtworkInfoPanel.vue index 47ec7c4..6fdefad 100644 --- a/ui/src/components/artwork/ArtworkInfoPanel.vue +++ b/ui/src/components/artwork/ArtworkInfoPanel.vue @@ -753,7 +753,7 @@ input:checked+.slider:before { grid-template-columns: auto 1fr auto; gap: var(--spacing-sm); box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.1); - z-index: 2000; + z-index: 1001; } .nav-back { diff --git a/ui/src/components/common/DownloadProgressWidget.vue b/ui/src/components/common/DownloadProgressWidget.vue index 1bda39e..42deb24 100644 --- a/ui/src/components/common/DownloadProgressWidget.vue +++ b/ui/src/components/common/DownloadProgressWidget.vue @@ -209,7 +209,7 @@ onUnmounted(() => { position: fixed; top: 4.5rem; right: 1rem; - z-index: 1000; + z-index: 1002; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; } @@ -554,7 +554,7 @@ onUnmounted(() => { background-clip: text; } -.stat-value.success + .stat-label { +.stat-value.success+.stat-label { color: #047857; } @@ -570,7 +570,7 @@ onUnmounted(() => { background-clip: text; } -.stat-value.error + .stat-label { +.stat-value.error+.stat-label { color: #b91c1c; } diff --git a/ui/src/components/common/RegistryWidget.vue b/ui/src/components/common/RegistryWidget.vue index 1b513f0..d61850a 100644 --- a/ui/src/components/common/RegistryWidget.vue +++ b/ui/src/components/common/RegistryWidget.vue @@ -1,5 +1,5 @@ @@ -419,7 +417,7 @@ const exportRegistry = async () => { if (result.success) { const modeText = useDatabase ? '数据库' : 'JSON文件'; showSuccess(`注册表导出成功(${modeText}模式)`); - + // 创建下载链接 const blob = new Blob([JSON.stringify(result.data, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); @@ -443,7 +441,7 @@ const handleFileImport = async (event: Event) => { // 读取文件内容 const fileContent = await file.text(); const registryData = JSON.parse(fileContent); - + const useDatabase = storageMode.value === 'database'; const result = await registryStore.importRegistry(registryData, useDatabase); if (result.success) { @@ -471,7 +469,7 @@ const rebuildRegistry = async () => { rebuildTaskId.value = result.data.taskId; isRebuildingRegistry.value = true; rebuildStartTime.value = Date.now(); - + // 重置进度 rebuildProgress.value = { scannedArtists: 0, @@ -480,7 +478,7 @@ const rebuildRegistry = async () => { skippedArtworks: 0, currentArtist: '' }; - + // 开始轮询进度 startProgressPolling(); showSuccess('文件系统同步已开始,请等待完成...'); @@ -498,15 +496,15 @@ const startProgressPolling = () => { if (progressPollingInterval.value) { clearInterval(progressPollingInterval.value); } - + progressPollingInterval.value = setInterval(async () => { if (!rebuildTaskId.value) return; - + try { const statusResult = await downloadService.getRegistryRebuildStatus(rebuildTaskId.value); if (statusResult.success && statusResult.data) { const status = statusResult.data; - + // 更新进度信息 if (status.progress) { rebuildProgress.value = { @@ -517,17 +515,17 @@ const startProgressPolling = () => { currentArtist: status.progress.currentArtist || '' }; } - + // 检查任务状态 if (status.status === 'completed') { stopProgressPolling(); isRebuildingRegistry.value = false; rebuildTaskId.value = null; - + const addedCount = rebuildProgress.value.addedArtworks; const skippedCount = rebuildProgress.value.skippedArtworks; showSuccess(`文件系统同步完成!新增 ${addedCount} 个作品,跳过 ${skippedCount} 个已存在作品`); - + // 刷新统计信息 refreshStats(); } else if (status.status === 'failed') { @@ -560,11 +558,11 @@ const stopProgressPolling = () => { // 取消重建任务 const cancelRebuild = async () => { if (!rebuildTaskId.value) return; - + if (!confirm('确定要取消文件系统同步吗?')) { return; } - + try { const result = await downloadService.cancelRegistryRebuild(rebuildTaskId.value); if (result.success) { @@ -584,11 +582,11 @@ const cancelRebuild = async () => { // 格式化已用时间 const formatElapsedTime = (startTime: number): string => { if (!startTime) return '00:00'; - + const elapsed = Math.floor((Date.now() - startTime) / 1000); const minutes = Math.floor(elapsed / 60); const seconds = elapsed % 60; - + return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`; }; @@ -722,11 +720,11 @@ const applyStorageModeConfig = async () => { // 根据迁移模式生成不同的确认消息 let confirmMessage = ''; if (migrationMode.value === 'switch-only') { - confirmMessage = selectedStorageMode.value === 'database' + confirmMessage = selectedStorageMode.value === 'database' ? '确定要切换到数据库存储模式吗?这将仅改变读取方式,不会迁移现有数据。' : '确定要切换到JSON文件存储模式吗?这将仅改变读取方式,不会迁移现有数据。'; } else { - confirmMessage = selectedStorageMode.value === 'database' + confirmMessage = selectedStorageMode.value === 'database' ? '确定要迁移数据并切换到数据库存储模式吗?这将把JSON数据迁移到数据库并覆盖数据库中的现有数据。' : '确定要迁移数据并切换到JSON文件存储模式吗?这将把数据库数据迁移到JSON文件并覆盖JSON文件中的现有数据。'; } @@ -737,7 +735,7 @@ const applyStorageModeConfig = async () => { try { migrationLoading.value = true; - + if (migrationMode.value === 'migrate-data') { // 执行数据迁移 if (selectedStorageMode.value === 'database') { @@ -765,10 +763,10 @@ const applyStorageModeConfig = async () => { const modeText = selectedStorageMode.value === 'database' ? '数据库存储' : 'JSON文件存储'; showSuccess(`已切换到${modeText}模式,数据读取方式已更改`); } - + // 保存存储模式配置到后端 await saveStorageModeConfig(selectedStorageMode.value); - + // 刷新统计信息 await refreshStats(); } catch (error) { @@ -859,6 +857,11 @@ watch(config, () => { bottom: 1rem; left: 1rem; z-index: 1000; + transition: z-index 0.1s ease; +} + +.registry-widget.panel-open { + z-index: 1002; } .registry-toggle { @@ -1148,7 +1151,7 @@ watch(config, () => { right: 0; bottom: 0; background: radial-gradient(circle at 20% 80%, rgba(59, 130, 246, 0.03), transparent 50%), - radial-gradient(circle at 80% 20%, rgba(16, 185, 129, 0.03), transparent 50%); + radial-gradient(circle at 80% 20%, rgba(16, 185, 129, 0.03), transparent 50%); pointer-events: none; } @@ -1790,6 +1793,7 @@ watch(config, () => { 0% { opacity: 0.6; } + 100% { opacity: 1; } @@ -1821,9 +1825,12 @@ watch(config, () => { } @keyframes pulse { - 0%, 100% { + + 0%, + 100% { transform: scale(1); } + 50% { transform: scale(1.1); } @@ -1941,20 +1948,20 @@ watch(config, () => { left: 0; right: 0; bottom: 0; - background: linear-gradient(90deg, - rgba(255, 255, 255, 0.1) 0%, - rgba(255, 255, 255, 0.3) 50%, - rgba(255, 255, 255, 0.1) 100%); + background: linear-gradient(90deg, + rgba(255, 255, 255, 0.1) 0%, + rgba(255, 255, 255, 0.3) 50%, + rgba(255, 255, 255, 0.1) 100%); animation: shimmer 2s infinite; pointer-events: none; } .progress-fill { height: 100%; - background: linear-gradient(90deg, - var(--color-success, #16a34a) 0%, - var(--color-info, #06b6d4) 50%, - var(--color-warning, #f59e0b) 100%); + background: linear-gradient(90deg, + var(--color-success, #16a34a) 0%, + var(--color-info, #06b6d4) 50%, + var(--color-warning, #f59e0b) 100%); border-radius: var(--radius-full, 9999px); transition: width 0.5s ease-out; position: relative; @@ -1968,10 +1975,10 @@ watch(config, () => { left: 0; right: 0; bottom: 0; - background: linear-gradient(90deg, - transparent 0%, - rgba(255, 255, 255, 0.3) 50%, - transparent 100%); + background: linear-gradient(90deg, + transparent 0%, + rgba(255, 255, 255, 0.3) 50%, + transparent 100%); animation: progressShine 1.5s ease-in-out infinite; } @@ -1979,6 +1986,7 @@ watch(config, () => { 0% { transform: translateX(-100%); } + 100% { transform: translateX(100%); } @@ -2183,9 +2191,12 @@ watch(config, () => { } @keyframes pulse { - 0%, 100% { + + 0%, + 100% { opacity: 1; } + 50% { opacity: 0.7; } @@ -2242,15 +2253,15 @@ watch(config, () => { flex-direction: column; align-items: stretch; } - + .migration-status { justify-content: center; } - + .storage-config-actions .action-buttons { flex-direction: column; } - + .storage-config-actions .btn { flex: none; } @@ -2262,34 +2273,34 @@ watch(config, () => { grid-template-columns: repeat(2, 1fr); gap: var(--spacing-xs, 0.25rem); } - + .progress-stat { font-size: 0.65rem; } - + .progress-stat .stat-label { font-size: 0.65rem; } - + .progress-stat .stat-value { font-size: 1rem; } - + .current-status { flex-direction: column; align-items: flex-start; gap: var(--spacing-xs, 0.25rem); } - + .status-value { width: 100%; } - + .progress-bar-container { flex-direction: column; gap: var(--spacing-sm, 0.5rem); } - + .progress-text { align-self: center; } @@ -2299,17 +2310,17 @@ watch(config, () => { .rebuild-progress { padding: var(--spacing-md, 0.75rem); } - + .progress-header { flex-direction: column; align-items: flex-start; gap: var(--spacing-sm, 0.5rem); } - + .progress-stats { grid-template-columns: 1fr; } - + .btn-small { align-self: flex-end; } diff --git a/ui/src/components/common/SettingsWidget.vue b/ui/src/components/common/SettingsWidget.vue index 3dea85f..09564c3 100644 --- a/ui/src/components/common/SettingsWidget.vue +++ b/ui/src/components/common/SettingsWidget.vue @@ -1,5 +1,5 @@