From 27ee72813f587fba11e85eac80f67e2d0674fbce Mon Sep 17 00:00:00 2001 From: kjqwer <2990346238@qq.com> Date: Sun, 28 Sep 2025 09:05:41 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=85=A8=E5=B1=80=E4=B8=BB?= =?UTF-8?q?=E9=A2=98=EF=BC=8C=E6=8B=86=E5=88=86=E5=BE=85=E7=9C=8B=E5=90=8D?= =?UTF-8?q?=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/src/assets/theme.css | 133 +++ .../common/DownloadProgressWidget.vue | 7 +- ui/src/components/common/ErrorMessage.vue | 2 +- ui/src/components/common/LoadingSpinner.vue | 11 +- ui/src/components/common/SettingsWidget.vue | 2 +- ui/src/components/common/UpdateChecker.vue | 146 +-- ui/src/components/common/WatchlistWidget.vue | 987 ++---------------- .../components/common/watchlist/AddModal.vue | 452 ++++++++ .../components/common/watchlist/EditModal.vue | 240 +++++ .../common/watchlist/WatchlistButton.vue | 166 +++ .../common/watchlist/WatchlistContent.vue | 166 +++ .../common/watchlist/WatchlistControls.vue | 160 +++ .../common/watchlist/WatchlistItem.vue | 208 ++++ .../common/watchlist/WatchlistPanel.vue | 197 ++++ ui/src/main.ts | 1 + 15 files changed, 1879 insertions(+), 999 deletions(-) create mode 100644 ui/src/assets/theme.css create mode 100644 ui/src/components/common/watchlist/AddModal.vue create mode 100644 ui/src/components/common/watchlist/EditModal.vue create mode 100644 ui/src/components/common/watchlist/WatchlistButton.vue create mode 100644 ui/src/components/common/watchlist/WatchlistContent.vue create mode 100644 ui/src/components/common/watchlist/WatchlistControls.vue create mode 100644 ui/src/components/common/watchlist/WatchlistItem.vue create mode 100644 ui/src/components/common/watchlist/WatchlistPanel.vue diff --git a/ui/src/assets/theme.css b/ui/src/assets/theme.css new file mode 100644 index 0000000..f78f9ae --- /dev/null +++ b/ui/src/assets/theme.css @@ -0,0 +1,133 @@ +/* 颜色主题变量定义 */ +:root { + /* 主色调 */ + --color-primary: #3b82f6; + --color-primary-light: #dbeafe; + --color-primary-dark: #2563eb; + + /* 背景色 */ + --color-bg-primary: #ffffff; + --color-bg-secondary: #f8fafc; + --color-bg-tertiary: #f1f5f9; + + /* 文本色 */ + --color-text-primary: #1f2937; + --color-text-secondary: #6b7280; + --color-text-tertiary: #9ca3af; + + /* 边框色 */ + --color-border: #e5e7eb; + --color-border-hover: #d1d5db; + + /* 状态色 */ + --color-success: #10b981; + --color-success-light: #d1fae5; + --color-warning: #f59e0b; + --color-warning-light: #fef3c7; + --color-danger: #ef4444; + --color-danger-light: #fee2e2; + --color-info: #06b6d4; + --color-info-light: #cffafe; + + /* 阴影 */ + --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05); + --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); + --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); + + /* 过渡动画 */ + --transition-fast: 0.15s ease; + --transition-normal: 0.2s ease; + --transition-slow: 0.3s ease; + + /* 圆角 */ + --radius-sm: 0.25rem; + --radius-md: 0.375rem; + --radius-lg: 0.5rem; + --radius-xl: 0.75rem; + + /* 间距 */ + --spacing-xs: 0.25rem; + --spacing-sm: 0.5rem; + --spacing-md: 0.75rem; + --spacing-lg: 1rem; + --spacing-xl: 1.5rem; + --spacing-2xl: 2rem; +} + +/* 暗色主题 */ +@media (prefers-color-scheme: dark) { + :root { + /* 主色调 */ + --color-primary: #60a5fa; + --color-primary-light: #1e3a8a; + --color-primary-dark: #3b82f6; + + /* 背景色 */ + --color-bg-primary: #1f2937; + --color-bg-secondary: #374151; + --color-bg-tertiary: #4b5563; + + /* 文本色 */ + --color-text-primary: #f9fafb; + --color-text-secondary: #d1d5db; + --color-text-tertiary: #9ca3af; + + /* 边框色 */ + --color-border: #4b5563; + --color-border-hover: #6b7280; + } +} + +/* 高亮效果类 */ +.highlight-current { + background: var(--color-primary-light) !important; + border-color: var(--color-primary) !important; + color: var(--color-primary) !important; +} + +.highlight-current .item-title { + color: var(--color-primary) !important; + font-weight: 600 !important; +} + +.highlight-current .item-url { + color: var(--color-primary) !important; +} + +/* 悬停效果增强 */ +.hover-primary:hover { + background: var(--color-primary-light); + color: var(--color-primary); + border-color: var(--color-primary); + transform: translateY(-1px); + box-shadow: var(--shadow-md); +} + +.hover-secondary:hover { + background: var(--color-bg-tertiary); + color: var(--color-text-primary); + border-color: var(--color-border-hover); +} + +.hover-danger:hover { + background: var(--color-danger-light); + color: var(--color-danger); + border-color: var(--color-danger); +} + +/* 按钮增强样式 */ +.btn-enhanced { + transition: all var(--transition-normal); + border-radius: var(--radius-md); + font-weight: 500; +} + +.btn-enhanced:hover { + transform: translateY(-1px); + box-shadow: var(--shadow-md); +} + +.btn-enhanced:active { + transform: translateY(0); + box-shadow: var(--shadow-sm); +} \ No newline at end of file diff --git a/ui/src/components/common/DownloadProgressWidget.vue b/ui/src/components/common/DownloadProgressWidget.vue index 4af6b93..7ee999e 100644 --- a/ui/src/components/common/DownloadProgressWidget.vue +++ b/ui/src/components/common/DownloadProgressWidget.vue @@ -241,9 +241,11 @@ onUnmounted(() => { 0% { box-shadow: 0 4px 12px rgba(59, 130, 246, 0.15); } + 50% { box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3); } + 100% { box-shadow: 0 4px 12px rgba(59, 130, 246, 0.15); } @@ -291,6 +293,7 @@ onUnmounted(() => { opacity: 0; transform: translateY(-10px); } + to { opacity: 1; transform: translateY(0); @@ -574,10 +577,10 @@ onUnmounted(() => { width: calc(100vw - 2rem); right: -1rem; } - + .batch-stats { flex-direction: column; gap: 0.5rem; } } - \ No newline at end of file + \ No newline at end of file diff --git a/ui/src/components/common/ErrorMessage.vue b/ui/src/components/common/ErrorMessage.vue index f2f5ec6..1d35191 100644 --- a/ui/src/components/common/ErrorMessage.vue +++ b/ui/src/components/common/ErrorMessage.vue @@ -103,4 +103,4 @@ defineEmits(); width: 1rem; height: 1rem; } - \ No newline at end of file + \ No newline at end of file diff --git a/ui/src/components/common/LoadingSpinner.vue b/ui/src/components/common/LoadingSpinner.vue index e0e91c5..f3757a9 100644 --- a/ui/src/components/common/LoadingSpinner.vue +++ b/ui/src/components/common/LoadingSpinner.vue @@ -57,7 +57,12 @@ defineProps(); } @keyframes spin { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(360deg); } + 0% { + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + } } - \ No newline at end of file + \ No newline at end of file diff --git a/ui/src/components/common/SettingsWidget.vue b/ui/src/components/common/SettingsWidget.vue index 620ceea..aeada8f 100644 --- a/ui/src/components/common/SettingsWidget.vue +++ b/ui/src/components/common/SettingsWidget.vue @@ -12,7 +12,7 @@ - +
diff --git a/ui/src/components/common/UpdateChecker.vue b/ui/src/components/common/UpdateChecker.vue index a0b6dff..0c2e47c 100644 --- a/ui/src/components/common/UpdateChecker.vue +++ b/ui/src/components/common/UpdateChecker.vue @@ -219,21 +219,21 @@ defineExpose({ align-items: center; gap: 0.5rem; padding: 0.5rem 1rem; - border: 1px solid #d1d5db; - border-radius: 0.375rem; - background: white; - color: #6b7280; + border: 1px solid var(--color-border); + border-radius: var(--radius-md); + background: var(--color-bg-primary); + color: var(--color-text-secondary); font-size: 0.875rem; font-weight: 500; cursor: pointer; - transition: all 0.2s; + transition: all var(--transition-normal); white-space: nowrap; } .update-btn:hover:not(:disabled) { - background: #f9fafb; - border-color: #9ca3af; - color: #374151; + background: var(--color-bg-secondary); + border-color: var(--color-border-hover); + color: var(--color-text-primary); } .update-btn:disabled { @@ -242,19 +242,21 @@ defineExpose({ } .update-btn.has-update { - background: #fef3c7; - border-color: #f59e0b; + background: var(--color-warning-light); + border-color: var(--color-warning); color: #92400e; + /* 这个颜色在主题中没有定义,保持原样 */ } .update-btn.has-update:hover:not(:disabled) { background: #fde68a; + /* 这个颜色在主题中没有完全匹配的,保持原样 */ } .update-btn.checking { - background: #dbeafe; - border-color: #3b82f6; - color: #1d4ed8; + background: var(--color-primary-light); + border-color: var(--color-primary); + color: var(--color-primary-dark); } .update-icon { @@ -296,9 +298,9 @@ defineExpose({ } .modal-content { - background: white; - border-radius: 0.5rem; - box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); + background: var(--color-bg-primary); + border-radius: var(--radius-lg); + box-shadow: var(--shadow-lg); width: 100%; max-width: 32rem; max-height: 90vh; @@ -312,7 +314,7 @@ defineExpose({ align-items: center; justify-content: space-between; padding: 1.5rem; - border-bottom: 1px solid #e5e7eb; + border-bottom: 1px solid var(--color-border); } .modal-title { @@ -322,13 +324,13 @@ defineExpose({ margin: 0; font-size: 1.125rem; font-weight: 600; - color: #111827; + color: var(--color-text-primary); } .modal-icon { width: 1.25rem; height: 1.25rem; - color: #3b82f6; + color: var(--color-primary); } .modal-close { @@ -338,16 +340,16 @@ defineExpose({ width: 2rem; height: 2rem; border: none; - border-radius: 0.25rem; + border-radius: var(--radius-sm); background: none; - color: #6b7280; + color: var(--color-text-secondary); cursor: pointer; - transition: all 0.2s; + transition: all var(--transition-normal); } .modal-close:hover { - background: #f3f4f6; - color: #374151; + background: var(--color-bg-secondary); + color: var(--color-text-primary); } .modal-close svg { @@ -370,7 +372,7 @@ defineExpose({ align-items: center; justify-content: space-between; padding: 0.75rem 0; - border-bottom: 1px solid #f3f4f6; + border-bottom: 1px solid var(--color-bg-secondary); } .version-row:last-child { @@ -379,21 +381,21 @@ defineExpose({ .label { font-weight: 500; - color: #374151; + color: var(--color-text-primary); } .version { font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; font-weight: 600; padding: 0.25rem 0.5rem; - border-radius: 0.25rem; - background: #f3f4f6; - color: #374151; + border-radius: var(--radius-sm); + background: var(--color-bg-secondary); + color: var(--color-text-primary); } .version.newer { - background: #dcfce7; - color: #166534; + background: var(--color-success-light); + color: var(--color-success); } .update-available-section, @@ -416,31 +418,31 @@ defineExpose({ } .status-icon.success { - color: #10b981; + color: var(--color-success); } .status-icon.info { - color: #3b82f6; + color: var(--color-primary); } .status-icon.error { - color: #ef4444; + color: var(--color-error); } .status-text { font-weight: 600; - color: #111827; + color: var(--color-text-primary); } .release-info h4 { margin: 0 0 0.5rem 0; font-size: 1.125rem; font-weight: 600; - color: #111827; + color: var(--color-text-primary); } .release-date { - color: #6b7280; + color: var(--color-text-secondary); font-size: 0.875rem; margin-bottom: 1rem; } @@ -449,17 +451,17 @@ defineExpose({ margin: 0 0 0.5rem 0; font-size: 1rem; font-weight: 600; - color: #374151; + color: var(--color-text-primary); } .release-body { - background: #f9fafb; - border: 1px solid #e5e7eb; - border-radius: 0.375rem; + background: var(--color-bg-secondary); + border: 1px solid var(--color-border); + border-radius: var(--radius-md); padding: 1rem; font-size: 0.875rem; line-height: 1.5; - color: #374151; + color: var(--color-text-primary); max-height: 12rem; overflow-y: auto; } @@ -467,16 +469,16 @@ defineExpose({ .update-instructions { margin-top: 1.5rem; padding: 1rem; - background: #fef7cd; - border: 1px solid #fbbf24; - border-radius: 0.375rem; + background: var(--color-warning-light); + border: 1px solid var(--color-warning); + border-radius: var(--radius-md); } .update-instructions h5 { margin: 0 0 1rem 0; font-size: 1rem; font-weight: 600; - color: #92400e; + color: var(--color-warning); } .instructions-content { @@ -497,8 +499,8 @@ defineExpose({ justify-content: center; min-width: 1.5rem; height: 1.5rem; - background: #f59e0b; - color: white; + background: var(--color-warning); + color: var(--color-bg-primary); border-radius: 50%; font-size: 0.75rem; font-weight: 600; @@ -508,25 +510,25 @@ defineExpose({ .step-text { font-size: 0.875rem; line-height: 1.5; - color: #92400e; + color: var(--color-warning); } .step-text code { - background: #fde68a; - border: 1px solid #f59e0b; - border-radius: 0.25rem; + background: var(--color-warning-light); + border: 1px solid var(--color-warning); + border-radius: var(--radius-sm); padding: 0.125rem 0.25rem; font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; font-size: 0.75rem; - color: #92400e; + color: var(--color-warning); } .error-message { - background: #fef2f2; - border: 1px solid #fecaca; - border-radius: 0.375rem; + background: var(--color-error-light); + border: 1px solid var(--color-error); + border-radius: var(--radius-md); padding: 0.75rem; - color: #991b1b; + color: var(--color-error); font-size: 0.875rem; } @@ -535,8 +537,8 @@ defineExpose({ gap: 0.75rem; justify-content: flex-end; padding: 1.5rem; - border-top: 1px solid #e5e7eb; - background: #f9fafb; + border-top: 1px solid var(--color-border); + background: var(--color-bg-secondary); } .btn { @@ -544,42 +546,42 @@ defineExpose({ align-items: center; justify-content: center; padding: 0.5rem 1rem; - border-radius: 0.375rem; + border-radius: var(--radius-md); font-weight: 500; font-size: 0.875rem; cursor: pointer; - transition: all 0.2s; + transition: all var(--transition-normal); border: 1px solid transparent; text-decoration: none; } .btn-primary { - background: #3b82f6; - color: white; + background: var(--color-primary); + color: var(--color-bg-primary); } .btn-primary:hover { - background: #2563eb; + background: var(--color-primary-dark); } .btn-secondary { - background: #6b7280; - color: white; + background: var(--color-text-secondary); + color: var(--color-bg-primary); } .btn-secondary:hover { - background: #4b5563; + background: var(--color-text-secondary); } .btn-outline { - background: white; - color: #374151; - border-color: #d1d5db; + background: var(--color-bg-primary); + color: var(--color-text-primary); + border-color: var(--color-border); } .btn-outline:hover { - background: #f9fafb; - border-color: #9ca3af; + background: var(--color-bg-secondary); + border-color: var(--color-border-hover); } @media (max-width: 640px) { diff --git a/ui/src/components/common/WatchlistWidget.vue b/ui/src/components/common/WatchlistWidget.vue index f073a66..eafc65d 100644 --- a/ui/src/components/common/WatchlistWidget.vue +++ b/ui/src/components/common/WatchlistWidget.vue @@ -1,235 +1,70 @@ @@ -239,6 +74,12 @@ import { useRoute, useRouter } from 'vue-router'; import { useWatchlistStore } from '@/stores/watchlist'; import type { WatchlistItem } from '@/services/watchlist'; +// 导入子组件 +import WatchlistButton from './watchlist/WatchlistButton.vue'; +import WatchlistPanel from './watchlist/WatchlistPanel.vue'; +import EditModal from './watchlist/EditModal.vue'; +import AddModal from './watchlist/AddModal.vue'; + // 状态 const isOpen = ref(false); const addLoading = ref(false); @@ -728,699 +569,10 @@ watch(() => route.fullPath, () => { top: 4.5rem; left: 1rem; z-index: 1000; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; -} - -/* 按钮样式 */ -.watchlist-toggle, -.add-current-toggle { - width: 3rem; - height: 3rem; - border-radius: 50%; - background: white; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - position: relative; - transition: all 0.3s ease; - border: 2px solid #e5e7eb; - color: #6b7280; - margin-bottom: 0.5rem; -} - -.watchlist-toggle:hover, -.add-current-toggle:hover { - transform: scale(1.05); - box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2); -} - -.watchlist-toggle.active { - border-color: #3b82f6; - color: #3b82f6; -} - -.add-current-toggle.added { - border-color: #10b981; - color: #10b981; -} - -.add-current-toggle.update { - border-color: #f59e0b; - color: #f59e0b; -} - -.add-current-toggle.loading { - border-color: #f59e0b; - color: #f59e0b; -} - -.add-current-toggle:disabled { - cursor: not-allowed; - opacity: 0.6; -} - -.watchlist-icon, -.add-icon { - width: 1.5rem; - height: 1.5rem; -} - -.loading-icon { - width: 1.5rem; - height: 1.5rem; - animation: spin 1s linear infinite; -} - -@keyframes spin { - from { - transform: rotate(0deg); - } - - to { - transform: rotate(360deg); - } -} - -.item-count { - position: absolute; - top: -0.25rem; - right: -0.25rem; - background: #ef4444; - color: white; - border-radius: 50%; - width: 1.2rem; - height: 1.2rem; - display: flex; - align-items: center; - justify-content: center; - font-size: 0.7rem; - font-weight: bold; -} - -/* 面板样式 */ -.watchlist-panel { - position: absolute; - top: 0; - left: 4rem; - width: 24rem; - max-height: 32rem; - background: white; - border-radius: 0.75rem; - box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15); - border: 1px solid #e5e7eb; - overflow: hidden; -} - -.watchlist-header { - padding: 1rem; - border-bottom: 1px solid #e5e7eb; - display: flex; - align-items: center; - justify-content: space-between; - background: #f8fafc; -} - -.watchlist-header h3 { - margin: 0; - font-size: 1rem; - font-weight: 600; - color: #1f2937; -} - -.header-actions { - display: flex; - align-items: center; - gap: 0.5rem; -} - -.item-count-text { - font-size: 0.875rem; - color: #6b7280; -} - -.close-btn, -.add-btn { - width: 1.5rem; - height: 1.5rem; - border: none; - background: none; - cursor: pointer; - color: #6b7280; - display: flex; - align-items: center; - justify-content: center; - border-radius: 0.25rem; - transition: all 0.2s; -} - -.close-btn:hover, -.add-btn:hover { - background: #f3f4f6; - color: #374151; -} - -.add-btn:hover { - color: #3b82f6; -} - -.close-icon { - width: 1rem; - height: 1rem; -} - -/* 搜索和排序控制区域样式 */ -.watchlist-controls { - padding: 0.75rem 1rem; - border-bottom: 1px solid #e5e7eb; - display: flex; - align-items: center; - gap: 0.5rem; - background: #f8fafc; -} - -.search-box { - flex: 1; - display: flex; - align-items: center; - background: #f3f4f6; - border: 1px solid #d1d5db; - border-radius: 0.375rem; - padding: 0.375rem 0.75rem; - gap: 0.5rem; -} - -.search-icon { - width: 1.125rem; - height: 1.125rem; - color: #6b7280; -} - -.search-input { - flex: 1; - border: none; - background: none; - font-size: 0.875rem; - color: #374151; - outline: none; -} - -.search-input::placeholder { - color: #9ca3af; -} - -.clear-search-btn { - background: none; - border: none; - cursor: pointer; - color: #6b7280; - padding: 0.25rem; - border-radius: 0.25rem; - transition: all 0.2s; -} - -.clear-search-btn:hover { - background: #f3f4f6; - color: #374151; -} - -.clear-search-btn svg { - width: 0.875rem; - height: 0.875rem; -} - -.sort-controls { - display: flex; - align-items: center; - gap: 0.5rem; -} - -.sort-btn { - display: flex; - align-items: center; - gap: 0.25rem; - background: #f3f4f6; - border: 1px solid #d1d5db; - border-radius: 0.375rem; - padding: 0.375rem 0.75rem; - cursor: pointer; - transition: all 0.2s; -} - -.sort-btn:hover { - background: #e5e7eb; -} - -.sort-icon { - width: 1rem; - height: 1rem; - color: #6b7280; -} - -.sort-text { - font-size: 0.875rem; - color: #374151; -} - -/* 内容样式 */ -.watchlist-content { - max-height: 28rem; - overflow-y: auto; -} - -.loading, -.error, -.empty { - padding: 2rem; display: flex; flex-direction: column; - align-items: center; - gap: 0.5rem; - color: #6b7280; - text-align: center; -} - -.loading-spinner { - width: 2rem; - height: 2rem; - border: 2px solid #e5e7eb; - border-top: 2px solid #3b82f6; - border-radius: 50%; - animation: spin 1s linear infinite; -} - -.error-icon, -.empty-icon { - width: 2.5rem; - height: 2.5rem; - color: #9ca3af; -} - -.retry-btn { - margin-top: 0.5rem; - padding: 0.5rem 1rem; - background: #3b82f6; - color: white; - border: none; - border-radius: 0.375rem; - cursor: pointer; - font-size: 0.875rem; -} - -.retry-btn:hover { - background: #2563eb; -} - -/* 项目列表样式 */ -.items-list { - padding: 0.5rem; -} - -.watchlist-item { - display: flex; - align-items: center; - padding: 0.75rem; - border-radius: 0.5rem; - transition: all 0.2s; - border: 1px solid transparent; - margin-bottom: 0.25rem; -} - -.watchlist-item:hover { - background: #f8fafc; - border-color: #e5e7eb; -} - -.watchlist-item.current { - background: #eff6ff; - border-color: #3b82f6; -} - -.watchlist-item.duplicate { - background: #fef3c7; - /* 浅黄色背景 */ - border-color: #f59e0b; - /* 橙色边框 */ -} - -.watchlist-item.pinned-artist { - background: #f0f9ff; - /* 浅蓝色背景 */ - border-color: #0ea5e9; - /* 蓝色边框 */ - position: relative; -} - -.watchlist-item.pinned-artist::before { - content: '📌'; - position: absolute; - top: 0.5rem; - right: 0.5rem; - font-size: 0.875rem; - opacity: 0.7; -} - -.item-main { - flex: 1; - cursor: pointer; - min-width: 0; -} - -.item-title { - font-weight: 500; - color: #1f2937; - margin-bottom: 0.25rem; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.item-title .duplicate-badge { - margin-left: 0.5rem; - background-color: #f59e0b; - color: white; - padding: 0.25rem 0.5rem; - border-radius: 0.25rem; - font-size: 0.75rem; - font-weight: 600; - white-space: nowrap; -} - -.item-url { - font-size: 0.75rem; - color: #6b7280; - margin-bottom: 0.25rem; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.item-time { - font-size: 0.7rem; - color: #9ca3af; -} - -.item-actions { - display: flex; - gap: 0.25rem; - margin-left: 0.5rem; -} - -.action-btn { - width: 1.75rem; - height: 1.75rem; - border: none; - background: none; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - border-radius: 0.25rem; - transition: all 0.2s; -} - -.action-btn svg { - width: 0.875rem; - height: 0.875rem; -} - -.edit-btn { - color: #6b7280; -} - -.edit-btn:hover { - background: #f3f4f6; - color: #3b82f6; -} - -.delete-btn { - color: #6b7280; -} - -.delete-btn:hover { - background: #fef2f2; - color: #ef4444; -} - -/* 编辑模态框样式 */ -.edit-modal-overlay { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(0, 0, 0, 0.5); - display: flex; - align-items: center; - justify-content: center; - z-index: 2000; -} - -.edit-modal { - background: white; - border-radius: 0.75rem; - width: 90%; - max-width: 32rem; - max-height: 90vh; - overflow-y: auto; - box-shadow: 0 20px 25px rgba(0, 0, 0, 0.15); -} - -.modal-header { - padding: 1rem; - border-bottom: 1px solid #e5e7eb; - display: flex; - align-items: center; - justify-content: space-between; -} - -.modal-header h4 { - margin: 0; - font-size: 1rem; - font-weight: 600; - color: #1f2937; -} - -.modal-content { - padding: 1rem; -} - -.form-group { - margin-bottom: 1rem; -} - -.form-group:last-child { - margin-bottom: 0; -} - -.form-group label { - display: block; - margin-bottom: 0.5rem; - font-size: 0.875rem; - font-weight: 500; - color: #374151; -} - -.form-input { - width: 100%; - padding: 0.5rem; - border: 1px solid #d1d5db; - border-radius: 0.375rem; - font-size: 0.875rem; - transition: border-color 0.2s; -} - -.form-input:focus { - outline: none; - border-color: #3b82f6; - box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); -} - -.form-input:disabled { - background: #f9fafb; - color: #6b7280; - cursor: not-allowed; -} - -.modal-actions { - padding: 1rem; - border-top: 1px solid #e5e7eb; - display: flex; - gap: 0.5rem; - justify-content: flex-end; -} - -.btn { - padding: 0.5rem 1rem; - border-radius: 0.375rem; - font-size: 0.875rem; - font-weight: 500; - cursor: pointer; - transition: all 0.2s; - border: none; -} - -.btn-secondary { - background: #f3f4f6; - color: #374151; -} - -.btn-secondary:hover { - background: #e5e7eb; -} - -.btn-primary { - background: #3b82f6; - color: white; -} - -.btn-primary:hover { - background: #2563eb; -} - -.btn:disabled { - opacity: 0.5; - cursor: not-allowed; -} - -/* 表单帮助文本样式 */ -.form-help { - display: block; - margin-top: 0.25rem; - font-size: 0.75rem; - color: #6b7280; - line-height: 1.4; -} - -/* 快速添加按钮样式 */ -.quick-add-buttons { - display: flex; - flex-wrap: wrap; - gap: 0.5rem; - margin-top: 0.5rem; -} - -.quick-btn { - padding: 0.375rem 0.75rem; - border: 1px solid #d1d5db; - background: white; - color: #374151; - border-radius: 0.375rem; - font-size: 0.75rem; - cursor: pointer; - transition: all 0.2s; -} - -.quick-btn:hover { - border-color: #3b82f6; - color: #3b82f6; - background: #eff6ff; -} - -/* 模式选择器样式 */ -.mode-selector { - display: flex; - border: 1px solid #d1d5db; - border-radius: 0.375rem; - overflow: hidden; -} - -.mode-btn { - flex: 1; - padding: 0.5rem 1rem; - border: none; - background: white; - color: #374151; - cursor: pointer; - transition: all 0.2s; - font-size: 0.875rem; -} - -.mode-btn:first-child { - border-right: 1px solid #d1d5db; -} - -.mode-btn:hover { - background: #f9fafb; -} - -.mode-btn.active { - background: #3b82f6; - color: white; -} - -/* 文本域样式 */ -.form-textarea { - width: 100%; - padding: 0.5rem; - border: 1px solid #d1d5db; - border-radius: 0.375rem; - font-size: 0.875rem; - font-family: monospace; - line-height: 1.4; - resize: vertical; - transition: border-color 0.2s; -} - -.form-textarea:focus { - outline: none; - border-color: #3b82f6; - box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); -} - -/* 复选框样式 */ -.form-checkbox { - margin-right: 0.5rem; - accent-color: #3b82f6; -} - -/* 预览列表样式 */ -.preview-list { - max-height: 200px; - overflow-y: auto; - border: 1px solid #e5e7eb; - border-radius: 0.375rem; - background: #f9fafb; -} - -.preview-item { - display: flex; - align-items: center; - justify-content: space-between; - padding: 0.5rem; - border-bottom: 1px solid #e5e7eb; - font-size: 0.875rem; -} - -.preview-item:last-child { - border-bottom: none; -} - -.preview-url { - flex: 1; - font-family: monospace; - color: #374151; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.preview-status { - padding: 0.125rem 0.5rem; - border-radius: 0.25rem; - font-size: 0.75rem; - font-weight: 500; -} - -.preview-status.new { - background: #dcfce7; - color: #166534; -} - -.preview-status.duplicate { - background: #fef3c7; - color: #92400e; + gap: 8px; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; } /* 响应式设计 */ @@ -1428,10 +580,5 @@ watch(() => route.fullPath, () => { .watchlist-widget { left: 0.5rem; } - - .watchlist-panel { - width: calc(100vw - 5rem); - max-width: 20rem; - } } \ No newline at end of file diff --git a/ui/src/components/common/watchlist/AddModal.vue b/ui/src/components/common/watchlist/AddModal.vue new file mode 100644 index 0000000..d5aef1b --- /dev/null +++ b/ui/src/components/common/watchlist/AddModal.vue @@ -0,0 +1,452 @@ + + + + + \ No newline at end of file diff --git a/ui/src/components/common/watchlist/EditModal.vue b/ui/src/components/common/watchlist/EditModal.vue new file mode 100644 index 0000000..f318a22 --- /dev/null +++ b/ui/src/components/common/watchlist/EditModal.vue @@ -0,0 +1,240 @@ + + + + + \ No newline at end of file diff --git a/ui/src/components/common/watchlist/WatchlistButton.vue b/ui/src/components/common/watchlist/WatchlistButton.vue new file mode 100644 index 0000000..f457caa --- /dev/null +++ b/ui/src/components/common/watchlist/WatchlistButton.vue @@ -0,0 +1,166 @@ + + + + + \ No newline at end of file diff --git a/ui/src/components/common/watchlist/WatchlistContent.vue b/ui/src/components/common/watchlist/WatchlistContent.vue new file mode 100644 index 0000000..7ad4550 --- /dev/null +++ b/ui/src/components/common/watchlist/WatchlistContent.vue @@ -0,0 +1,166 @@ + + + + + \ No newline at end of file diff --git a/ui/src/components/common/watchlist/WatchlistControls.vue b/ui/src/components/common/watchlist/WatchlistControls.vue new file mode 100644 index 0000000..63a0a2b --- /dev/null +++ b/ui/src/components/common/watchlist/WatchlistControls.vue @@ -0,0 +1,160 @@ + + + + + \ No newline at end of file diff --git a/ui/src/components/common/watchlist/WatchlistItem.vue b/ui/src/components/common/watchlist/WatchlistItem.vue new file mode 100644 index 0000000..9f93584 --- /dev/null +++ b/ui/src/components/common/watchlist/WatchlistItem.vue @@ -0,0 +1,208 @@ + + + + + \ No newline at end of file diff --git a/ui/src/components/common/watchlist/WatchlistPanel.vue b/ui/src/components/common/watchlist/WatchlistPanel.vue new file mode 100644 index 0000000..f0dc7a2 --- /dev/null +++ b/ui/src/components/common/watchlist/WatchlistPanel.vue @@ -0,0 +1,197 @@ + + + + + \ No newline at end of file diff --git a/ui/src/main.ts b/ui/src/main.ts index c7a84eb..50b469f 100644 --- a/ui/src/main.ts +++ b/ui/src/main.ts @@ -1,4 +1,5 @@ import './assets/main.css' +import './assets/theme.css' import { createApp } from 'vue' import { createPinia } from 'pinia'