From 249b3b9ea85434345b31acd2760357c3129595a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BB=9D=E5=B0=98?= <237809796@qq.com> Date: Thu, 2 Jul 2026 23:24:11 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84:=20=E8=B7=A8=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E6=A0=B7=E5=BC=8FDRY=E6=94=B6=E5=8F=A3+=E5=AE=A1?= =?UTF-8?q?=E6=89=B9=E5=88=97=E8=A1=A8=E5=A2=9E=E5=BC=BA+=E7=81=B5?= =?UTF-8?q?=E6=84=9F=E6=A8=A1=E5=9D=97=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 全局样式收口: page-header/btn/modal/filter-bar/back-link/empty-state 等通用类从12个视图的scoped重复定义提取到global.css/components.css - 审批列表增强: 筛选栏(状态/风险/工具搜索)+行展开查看完整参数结果 +审批耗时显示+刷新spinner+硬编码颜色全部改用CSS变量 - 灵感模块: scoreTier统一评分阈值+score-bar/assessment-badge提取全局 +statusLabelKey消除重复+i18n状态文案对齐+eval-report--muted统一空态 - Dashboard: df-panel/df-link提取全局(修scoped不渗透)+统计卡可点击跳转 +活跃项目面板只显示active状态+灵感列表按分数排序+刷新spinner - 修复: intent.rs tool_type类型对齐(newtype)+ToolCardList补ai-btn定义 +SkillMention/ImageInput样式不渗透修复(提取全局) --- crates/df-ai/src/intent.rs | 2 +- src/components/ToolCardList.vue | 26 +- src/components/ai/ChatInput.vue | 114 +------- src/components/ai/MentionPopover.vue | 32 +-- .../dashboard/ActiveProjectsPanel.vue | 64 +++-- src/components/dashboard/IdeasPanel.vue | 51 ++-- src/components/dashboard/StatCardRow.vue | 50 ++-- src/components/ideas/IdeaDetail.vue | 105 ++------ src/components/knowledge/KnowledgeDetail.vue | 12 +- src/components/project/DependencyGraph.vue | 30 +-- src/components/project/FileExplorer.vue | 36 +-- src/i18n/en/auditLog.ts | 6 + src/i18n/en/dashboard.ts | 3 + src/i18n/en/ideas.ts | 2 - src/i18n/zh-CN/auditLog.ts | 6 + src/i18n/zh-CN/dashboard.ts | 3 + src/i18n/zh-CN/ideas.ts | 2 - src/styles/components.css | 244 +++++++++++++++++- src/styles/global.css | 40 +++ src/utils/ideaEval.ts | 16 ++ src/views/AuditLog.vue | 222 ++++++++++++---- src/views/Dashboard.vue | 99 +------ src/views/Ideas.vue | 106 +------- src/views/Knowledge.vue | 49 +--- src/views/ProjectDetail.vue | 93 +------ src/views/Projects.vue | 60 +---- src/views/Settings.vue | 8 +- src/views/TaskDetail.vue | 31 +-- src/views/Tasks.vue | 104 +------- 29 files changed, 691 insertions(+), 925 deletions(-) diff --git a/crates/df-ai/src/intent.rs b/crates/df-ai/src/intent.rs index 4bb39d7..f889417 100644 --- a/crates/df-ai/src/intent.rs +++ b/crates/df-ai/src/intent.rs @@ -944,7 +944,7 @@ mod tests { /// 构造测试用 ToolDefinition(仅 name 有意义,description/parameters 填占位)。 fn tool_def(name: &str) -> df_ai_core::types::ToolDefinition { df_ai_core::types::ToolDefinition { - tool_type: "function".to_string(), + tool_type: "function".into(), function: df_ai_core::types::ToolFunction { name: name.to_string(), description: String::new(), diff --git a/src/components/ToolCardList.vue b/src/components/ToolCardList.vue index 4878c5c..6c0eeec 100644 --- a/src/components/ToolCardList.vue +++ b/src/components/ToolCardList.vue @@ -387,9 +387,31 @@ function groupIcon(name: string): string { border-radius: var(--df-radius-sm); background: rgba(255,255,255,0.02); } -.ai-batch-approve .ai-btn--sm { - font-size: 11px; +/* .ai-btn 基类:批量审批按钮(历史重构遗漏定义,补全治本) */ +.ai-btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 4px; + padding: 6px 12px; + border: none; + border-radius: var(--df-radius-sm); + background: var(--df-accent); + color: #fff; + font-family: var(--df-font-sans); + font-size: 12px; + font-weight: 500; + cursor: pointer; + transition: filter 0.15s, background 0.15s; + white-space: nowrap; } +.ai-btn:hover { filter: brightness(1.1); } +.ai-btn--sm { padding: 4px 10px; font-size: 11px; } +.ai-btn--danger { + background: var(--df-danger); + color: #fff; +} +.ai-btn--danger:hover { filter: brightness(1.1); } /* -- 全部展开/收起(U-260618:从 topbar 下沉到分组标题行,移除独立按钮 padding/背景, 高度=文字行高,不撑高分组标题行;融入标题视觉,仅 hover 变色) -- */ diff --git a/src/components/ai/ChatInput.vue b/src/components/ai/ChatInput.vue index 03eb863..4567d58 100644 --- a/src/components/ai/ChatInput.vue +++ b/src/components/ai/ChatInput.vue @@ -813,123 +813,13 @@ defineExpose({ transition: border-color 0.15s; } -/* F-260614-05 Phase 2b: 待发送图片预览行(粘贴/拖拽加入,发送前可移除) */ -.ai-img-preview-row { - display: flex; - flex-wrap: wrap; - gap: 6px; - width: 100%; /* 全宽强制后续 textarea+按钮换行 */ - padding-bottom: 4px; - border-bottom: 0.5px solid var(--df-border); -} -.ai-img-preview { - position: relative; - width: 56px; - height: 56px; - border-radius: var(--df-radius); - overflow: hidden; - border: 0.5px solid var(--df-border); - background: var(--df-bg); -} -.ai-img-preview-thumb { - width: 100%; - height: 100%; - object-fit: cover; - display: block; -} -.ai-img-preview-x { - position: absolute; - top: 2px; - right: 2px; - width: 16px; - height: 16px; - display: flex; - align-items: center; - justify-content: center; - border: none; - border-radius: 50%; - background: color-mix(in srgb, var(--df-bg) 80%, transparent); - color: var(--df-text-dim); - font-size: 14px; - line-height: 1; - cursor: pointer; - padding: 0; - transition: background 0.15s, color 0.15s; -} -.ai-img-preview-x:hover { - background: var(--df-danger); - color: #fff; -} +/* F-260614-05 Phase 2b: 图片预览样式已提取到 styles/components.css 全局(ImageInput.vue 共享) */ .ai-input-wrap:focus-within { border-color: color-mix(in srgb, var(--df-accent) 40%, transparent); } -/* ── 技能 chip / 联想浮层 ── */ -.ai-skill-chip { - display: flex; - align-items: center; - gap: 6px; - margin-bottom: 6px; - padding: 3px 8px; - background: color-mix(in srgb, var(--df-accent) 16%, transparent); - border: 0.5px solid color-mix(in srgb, var(--df-accent) 40%, transparent); - border-radius: var(--df-radius); - font-size: 12px; -} -.ai-skill-chip-name { color: var(--df-accent); font-weight: 600; flex-shrink: 0; } -.ai-skill-chip-desc { color: var(--df-text-dim); flex-shrink: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } -/* 参数格式提示(技能自带 argument_hint 数据,不进 i18n) */ -.ai-skill-chip-main { display: flex; align-items: center; gap: 6px; flex: 1; min-width: 0; flex-wrap: wrap; } -.ai-skill-chip-hint { - color: var(--df-text-dim); - font-family: var(--df-font-mono, ui-monospace, monospace); - font-size: 11px; - background: color-mix(in srgb, var(--df-text-dim) 14%, transparent); - padding: 1px 5px; - border-radius: 3px; -} -.ai-skill-chip-x { background: none; border: none; color: var(--df-text-dim); cursor: pointer; font-size: 14px; line-height: 1; padding: 0 2px; flex-shrink: 0; } -.ai-skill-chip-x:hover { color: var(--df-text); } - -.ai-skill-popover { - position: absolute; - bottom: calc(100% + 4px); - left: 0; - right: 0; - max-height: 240px; - overflow-y: auto; - background: var(--df-bg-card); - border: 0.5px solid var(--df-border); - border-radius: var(--df-radius); - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4); - z-index: 20; -} -.ai-skill-item { - display: flex; - flex-direction: column; - gap: 2px; - padding: 6px 10px; - cursor: pointer; - font-size: 12px; -} -.ai-skill-item-row { display: flex; align-items: center; gap: 8px; min-width: 0; } -.ai-skill-item--active, -.ai-skill-item:hover { - background: color-mix(in srgb, var(--df-accent) 14%, transparent); -} -.ai-skill-item-name { color: var(--df-accent); font-weight: 600; min-width: 90px; } -.ai-skill-item-desc { color: var(--df-text-dim); flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } -.ai-skill-item-src { color: var(--df-text-dim); opacity: 0.6; font-size: 10px; text-transform: uppercase; } -/* 浮层内参数格式提示(等宽 dim 小字,仅在 skill.argument_hint 有值时显) */ -.ai-skill-item-hint { - color: var(--df-text-dim); - font-family: var(--df-font-mono, ui-monospace, monospace); - font-size: 11px; - opacity: 0.85; - word-break: break-all; -} -.ai-skill-empty { padding: 8px 12px; color: var(--df-text-dim); opacity: 0.7; font-size: 12px; } +/* ── 技能 chip / 联想浮层样式已提取到 styles/components.css 全局(SkillMention.vue + MentionPopover.vue 共享)── */ /* ── UX-10 §1.4: @ 实体引用浮层样式已随 MentionPopover.vue 提取 ── */ diff --git a/src/components/ai/MentionPopover.vue b/src/components/ai/MentionPopover.vue index 9631f78..65929e0 100644 --- a/src/components/ai/MentionPopover.vue +++ b/src/components/ai/MentionPopover.vue @@ -38,7 +38,7 @@ const emit = defineEmits<{ diff --git a/src/components/project/FileExplorer.vue b/src/components/project/FileExplorer.vue index 2349e8f..b52af8f 100644 --- a/src/components/project/FileExplorer.vue +++ b/src/components/project/FileExplorer.vue @@ -612,39 +612,9 @@ async function onRemoveModule() { opacity: 0.6; } -/* 按钮 */ -.btn { - padding: 6px 12px; - border: none; - border-radius: var(--df-radius-sm, 4px); - font-size: 12px; - cursor: pointer; - transition: all 0.15s; - display: inline-flex; - align-items: center; - gap: 6px; -} - -.btn-ghost { - background: transparent; - color: var(--df-text-secondary); - border: 0.5px solid var(--df-border); -} - -.btn-ghost:hover:not(:disabled) { - background: var(--df-bg); - color: var(--df-text); -} - -.btn-ghost:disabled { - opacity: 0.5; - cursor: not-allowed; -} - -.btn-sm { - padding: 4px 10px; - font-size: 11px; -} +/* 按钮基础样式 (.btn / .btn-ghost / .btn-sm) 已收敛至 global.css */ +/* FileExplorer 工具栏为紧凑布局,覆 padding/font-size 避免按钮变大 */ +.btn { padding: 6px 12px; font-size: 12px; } /* 主体两栏 — 自适应比例 30% / 70% */ .explorer-body { diff --git a/src/i18n/en/auditLog.ts b/src/i18n/en/auditLog.ts index 91b3b73..5b12fd4 100644 --- a/src/i18n/en/auditLog.ts +++ b/src/i18n/en/auditLog.ts @@ -6,6 +6,12 @@ export default { loading: 'Loading…', empty: 'No audit records', + filter: { + allStatus: 'All status', + allRisk: 'All risk', + toolPlaceholder: 'Search tool name…', + }, + col: { time: 'Requested', tool: 'Tool', diff --git a/src/i18n/en/dashboard.ts b/src/i18n/en/dashboard.ts index 0fd2267..e59df8b 100644 --- a/src/i18n/en/dashboard.ts +++ b/src/i18n/en/dashboard.ts @@ -14,6 +14,8 @@ export default { activeProjects: 'Active Projects', viewAll: 'View all', ideaPool: 'Idea Pool', + goProjectDetail: 'View project details', + goIdeaDetail: 'View idea details', recentDecisions: 'Recent Decisions', openAnnotations: 'Open Annotations', aiBatchProcess: 'AI Batch Process', @@ -39,6 +41,7 @@ export default { stage: { planning: 'Planning', coding: 'Coding', + release: 'Release', done: 'Done', }, ideaStatus: { diff --git a/src/i18n/en/ideas.ts b/src/i18n/en/ideas.ts index 50d69bd..31558a6 100644 --- a/src/i18n/en/ideas.ts +++ b/src/i18n/en/ideas.ts @@ -91,8 +91,6 @@ export default { relatedCancel: 'Cancel', // Idea stats panel (used by IdeasPanel) - statsTitle: '📈 Overview', - statsTotal: 'Total', statsPending: 'Pending', statsPromoted: 'Promoted', statsAvgScore: 'Avg score', diff --git a/src/i18n/zh-CN/auditLog.ts b/src/i18n/zh-CN/auditLog.ts index 6396ee5..316137b 100644 --- a/src/i18n/zh-CN/auditLog.ts +++ b/src/i18n/zh-CN/auditLog.ts @@ -6,6 +6,12 @@ export default { loading: '加载中…', empty: '暂无审计记录', + filter: { + allStatus: '全部状态', + allRisk: '全部风险', + toolPlaceholder: '搜索工具名…', + }, + col: { time: '请求时间', tool: '工具', diff --git a/src/i18n/zh-CN/dashboard.ts b/src/i18n/zh-CN/dashboard.ts index 7268312..c67326f 100644 --- a/src/i18n/zh-CN/dashboard.ts +++ b/src/i18n/zh-CN/dashboard.ts @@ -14,6 +14,8 @@ export default { activeProjects: '活跃项目', viewAll: '查看全部', ideaPool: '灵感', + goProjectDetail: '查看项目详情', + goIdeaDetail: '查看灵感详情', recentDecisions: '最近决策', openAnnotations: '待处理标注', aiBatchProcess: 'AI 批量处理', @@ -39,6 +41,7 @@ export default { stage: { planning: '规划中', coding: '编码中', + release: '发布中', done: '已完成', }, ideaStatus: { diff --git a/src/i18n/zh-CN/ideas.ts b/src/i18n/zh-CN/ideas.ts index 11ce411..23cd921 100644 --- a/src/i18n/zh-CN/ideas.ts +++ b/src/i18n/zh-CN/ideas.ts @@ -82,8 +82,6 @@ export default { relatedCancel: '取消', // 灵感统计看板(IdeasPanel 引用) - statsTitle: '📈 灵感概览', - statsTotal: '总数', statsPending: '待评估', statsPromoted: '已立项', statsAvgScore: '平均分', diff --git a/src/styles/components.css b/src/styles/components.css index 184f324..a7d2e93 100644 --- a/src/styles/components.css +++ b/src/styles/components.css @@ -18,8 +18,100 @@ ← ProjectDetail.vue / TaskDetail.vue / TaskOutputCard.vue 三处复制 - .panel / .panel-header ← ProjectDetail.vue / TaskDetail.vue 两处复制 - .tech-tag / .card-tags ← ProjectCard.vue / Projects.vue 两处复制 + - .error-banner / .error-text / .error-dismiss ← Dashboard.vue / Knowledge.vue 两处复制 ═══════════════════════════════════════════════════════════ */ +/* — Error Banner:页面级错误提示条(danger 底 + 关闭按钮) — + 原 Dashboard.vue / Knowledge.vue diff --git a/src/views/Dashboard.vue b/src/views/Dashboard.vue index 8143e8c..f47bbaf 100644 --- a/src/views/Dashboard.vue +++ b/src/views/Dashboard.vue @@ -7,18 +7,19 @@ {{ $t('dashboard.subtitle') }}
- -
- -
+ +
{{ errorMsg }}
@@ -122,58 +123,12 @@ onMounted(() => { } .dash-header-right { display: flex; gap: 6px; align-items: center; } -/* — Buttons — */ -.df-btn { - display: inline-flex; - align-items: center; - gap: 6px; - padding: 9px 14px; - line-height: 1; - border: none; - border-radius: var(--df-radius-sm); - font-family: var(--df-font-sans); - font-size: 12px; - font-weight: 500; - cursor: pointer; - transition: all 0.15s var(--df-ease); - white-space: nowrap; +/* 刷新按钮图标旋转动画(加载中反馈) */ +.dash-spinner { animation: dash-spin 0.9s linear infinite; } +@keyframes dash-spin { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } } -.df-btn--primary { - background: var(--df-accent); - color: #fff; - -} -.df-btn--primary:hover { - background: var(--df-accent); - filter: brightness(1.1); - -} -.df-btn--ghost { - background: rgba(255,255,255,0.03); - color: var(--df-text-secondary); - border: 0.5px solid var(--df-border); -} -.df-btn--ghost:hover { - background: rgba(255,255,255,0.06); - color: var(--df-text); - border-color: var(--df-border-strong); -} -.df-btn--xs { padding: 4px 10px; font-size: 11px; } -.df-btn:disabled { opacity: 0.4; cursor: not-allowed; } - -/* ═══ 错误条(对齐 Knowledge error-banner) ═══ */ -.error-banner { - display: flex; align-items: center; justify-content: space-between; gap: 12px; - padding: 8px 12px; margin-bottom: 14px; - background: rgba(255,107,107,0.12); border: 0.5px solid var(--df-danger); - border-radius: var(--df-radius-sm); color: var(--df-danger); font-size: 12px; -} -.error-text { word-break: break-word; } -.error-dismiss { - flex-shrink: 0; background: transparent; border: none; color: var(--df-danger); - font-size: 14px; cursor: pointer; line-height: 1; padding: 0 2px; -} -.error-dismiss:hover { opacity: 0.7; } /* ═══ Grid Layout ═══ */ .dash-grid { @@ -183,37 +138,6 @@ onMounted(() => { } .dash-right { display: flex; flex-direction: column; gap: 12px; } -/* ═══ Panel(子面板 df-panel 共享基类,scoped 下子组件需各自声明同名类样式; - 此处保留供未来页内直挂面板,子组件已自带同名 .df-panel 样式) ═══ */ -.df-panel { - background: var(--df-bg-card); - border: 0.5px solid var(--df-border); - border-radius: var(--df-radius-md); - padding: 14px 16px; - animation: fadeInUp 0.5s var(--df-ease) both; -} -.df-panel-head { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 10px; -} -.df-panel-title { - font-size: 12px; - font-weight: 500; - color: var(--df-text); - letter-spacing: -0.1px; -} -.df-link { - font-size: 11px; - color: var(--df-accent); - text-decoration: none; - font-weight: 500; - opacity: 0.8; - transition: opacity 0.15s; -} -.df-link:hover { opacity: 1; text-decoration: underline; } - /* ═══ Responsive ═══ */ /* 窄窗口:单栏 */ @@ -233,6 +157,5 @@ onMounted(() => { @media (min-width: 1600px) { .dashboard { padding: 20px 28px 24px; } .dash-grid { grid-template-columns: 1fr 400px; gap: 16px; } - .df-panel { padding: 16px 18px; } } diff --git a/src/views/Ideas.vue b/src/views/Ideas.vue index 6baac4c..a0771c8 100644 --- a/src/views/Ideas.vue +++ b/src/views/Ideas.vue @@ -134,8 +134,8 @@
@@ -156,6 +156,7 @@ import { useProjectStore } from '../stores/project' import type { IdeaStatus, IdeaQuery } from '../api/types' import { formatDate } from '../utils/time' import { stripMd } from '../utils/markdown' +import { scoreTier } from '../utils/ideaEval' import ConfirmDialog from '../components/ConfirmDialog.vue' import IdeaDetail from '../components/ideas/IdeaDetail.vue' import { useConfirm } from '../composables/useConfirm' @@ -286,11 +287,9 @@ const currentIdea = computed(() => { // B-260615-25:灵感描述 Markdown 渲染已下沉至 IdeaDetail 子组件(共享模块单例渲染器) +// 评分档位 class(高/中/低 三色标),阈值统一走 scoreTier(单一来源,消除 4 处重复) function scoreClass(score: number | null) { - const s = score ?? 0 - if (s >= 80) return 'score-high' - if (s >= 60) return 'score-mid' - return 'score-low' + return 'score-' + scoreTier(score) } // formatDate 由 ../utils/time 提供(统一毫秒字符串解析,根治 Invalid Date) @@ -418,74 +417,7 @@ watch(() => route.params.id, (id) => { diff --git a/src/views/Knowledge.vue b/src/views/Knowledge.vue index 5dd6f68..d0bc0f7 100644 --- a/src/views/Knowledge.vue +++ b/src/views/Knowledge.vue @@ -8,8 +8,8 @@ - -
+ +
{{ store.error }}
@@ -347,26 +347,9 @@ onMounted(() => { @@ -727,16 +705,9 @@ onUnmounted(() => { overflow: hidden; } -.page-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: var(--df-gap-page); -} +/* .page-header / .page-header h1 已提取到 styles/global.css 全局 */ .header-left { display: flex; align-items: center; gap: 12px; } -.back-link { font-size: 13px; color: var(--df-accent); text-decoration: none; } -.back-link:hover { text-decoration: underline; } -.page-header h1 { font-size: 24px; font-weight: 500; color: var(--df-text); } +/* .back-link / .back-link:hover 已提取到 styles/global.css 全局 */ .stage-badge { font-size: 12px; @@ -752,24 +723,9 @@ onUnmounted(() => { .stage-paused { background: rgba(158,158,158,0.15); color: #9e9e9e; } .stage-cancelled { background: rgba(244,67,54,0.15); color: #f44336; } -.header-actions { display: flex; gap: 10px; } +/* .header-actions 已提取到 styles/global.css 全局 */ -/* ===== 按钮 ===== */ -.btn { - padding: 8px 16px; - border: none; - border-radius: var(--df-radius-sm); - font-size: 13px; - cursor: pointer; - transition: all 0.15s; -} -.btn-primary { background: var(--df-accent); color: #fff; } -.btn-primary:hover { background: var(--df-accent-hover); } -.btn-ghost { background: transparent; color: var(--df-text-secondary); border: 0.5px solid var(--df-border); } -.btn-ghost:hover { background: var(--df-bg-card); color: var(--df-text); } -.btn-sm { padding: 4px 12px; font-size: 12px; } -.btn-danger { background: transparent; color: var(--df-danger); border: 0.5px solid rgba(240,101,101,0.4); } -.btn-danger:hover { background: rgba(240,101,101,0.12); } +/* .btn 系列(btn/btn-primary/btn-ghost/btn-sm/btn-danger)已提取到 styles/global.css 全局 */ /* ===== 两栏布局 — 自己滚动,不撑大父级 ===== */ .detail-grid { @@ -856,35 +812,8 @@ onUnmounted(() => { /* ===== 项目信息(左栏 .project-info/.info-item,右栏重复面板已删)===== */ -/* ===== 模态框 ===== */ -.modal-overlay { - position: fixed; inset: 0; - background: rgba(0,0,0,0.5); - display: flex; align-items: center; justify-content: center; - z-index: 100; -} -.modal-box { - background: var(--df-bg-card); - border: 0.5px solid var(--df-border); - border-radius: var(--df-radius-lg); - padding: 24px; - width: 420px; - max-width: 90vw; -} -.modal-title { font-size: 18px; font-weight: 500; color: var(--df-text); margin-bottom: 16px; } -.modal-field { margin-bottom: 14px; } -.modal-field label { display: block; font-size: 12px; color: var(--df-text-dim); margin-bottom: 4px; } -.modal-field input, .modal-field textarea { - width: 100%; padding: 8px 10px; border-radius: var(--df-radius-sm); - border: 0.5px solid var(--df-border); background: var(--df-bg); - color: var(--df-text); font-size: 13px; font-family: inherit; - box-sizing: border-box; -} -.modal-field input:focus, .modal-field textarea:focus { - outline: none; border-color: var(--df-accent); -} -.modal-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 4px; } -.btn:disabled { opacity: 0.4; cursor: not-allowed; } +/* .modal-* 系列(modal-overlay/modal-box/modal-title/modal-field/modal-actions) + 及 .btn:disabled 已提取到 styles/global.css 全局 */ .empty-hint { text-align: center; padding: 24px 12px; diff --git a/src/views/Projects.vue b/src/views/Projects.vue index 219199a..c564398 100644 --- a/src/views/Projects.vue +++ b/src/views/Projects.vue @@ -432,29 +432,11 @@ async function runImport() {