diff --git a/.claude/launch.json b/.claude/launch.json new file mode 100644 index 0000000..20fd1d3 --- /dev/null +++ b/.claude/launch.json @@ -0,0 +1,19 @@ +{ + "version": "0.0.1", + "configurations": [ + { + "name": "Frontend (Vite)", + "runtimeExecutable": "npm", + "runtimeArgs": ["run", "dev"], + "cwd": "frontend", + "port": 5173 + }, + { + "name": "Backend (FastAPI/uvicorn)", + "runtimeExecutable": "python", + "runtimeArgs": ["-m", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"], + "cwd": "backend", + "port": 8000 + } + ] +} diff --git a/frontend/index.html b/frontend/index.html index 3c36b0d..554e5fb 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -4,9 +4,9 @@ 医药情报 - - + +
diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 98240ae..d4dd48f 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1,3 +1,49 @@ + + diff --git a/frontend/src/components/NewsCard.vue b/frontend/src/components/NewsCard.vue index 7d262bb..0202be4 100644 --- a/frontend/src/components/NewsCard.vue +++ b/frontend/src/components/NewsCard.vue @@ -9,13 +9,13 @@

{{ news.title_zh }}

{{ news.summary }}

- 核心观点{{ news.opinion }} + INSIGHT{{ news.opinion }}
@@ -39,56 +39,76 @@ const timeAgo = computed(() => { const diff = Date.now() - new Date(props.news.published_at).getTime() const h = Math.floor(diff / 3600000) if (h < 1) return '刚刚' - if (h < 24) return `${h}小时前` - return `${Math.floor(h / 24)}天前` + if (h < 24) return `${h}h前` + return `${Math.floor(h / 24)}d前` }) diff --git a/frontend/src/components/ThemeControls.vue b/frontend/src/components/ThemeControls.vue new file mode 100644 index 0000000..e4bfe0c --- /dev/null +++ b/frontend/src/components/ThemeControls.vue @@ -0,0 +1,59 @@ + + + + + diff --git a/frontend/src/styles/theme.css b/frontend/src/styles/theme.css index 6edcca6..9840b33 100644 --- a/frontend/src/styles/theme.css +++ b/frontend/src/styles/theme.css @@ -1,64 +1,147 @@ -/* Light theme design tokens */ +/* ───────────────────────────────────────────────────────────────── + Clinical design-system — IBM Plex Mono + IBM Plex Sans + Light = clinical white | Dark = deep navy #050D12 +───────────────────────────────────────────────────────────────── */ + +/* ── Light ────────────────────────────────────────────────────── */ :root { - --bg: #f0f2f5; - --bg-card: #ffffff; - --bg-hover: #f0f4ff; - --bg-1: #ffffff; - --bg-2: #f8f9fc; - --bg-hi: #f3f4f6; + --bg: #F0F6FA; + --bg-card: #FFFFFF; + --bg-hover: rgba(13,155,142,0.05); + --bg-1: #FFFFFF; + --bg-2: #F5F9FC; + --bg-hi: rgba(13,155,142,0.06); - --blue: #2563eb; - --blue-2: #3b82f6; - --blue-gl: rgba(37, 99, 235, 0.06); - --blue-bd: rgba(37, 99, 235, 0.2); + /* primary accent — clinical teal */ + --blue: #0D9B8E; + --blue-2: #13B5A7; + --blue-gl: rgba(13,155,142,0.06); + --blue-bd: rgba(13,155,142,0.22); - --violet: #7c3aed; - --cyan: #0ea5e9; - --mint: #10b981; - --amber: #f59e0b; - --red: #ef4444; + --violet: #7C3AED; + --cyan: #06B6D4; + --mint: #0D9B8E; + --amber: #B07A0A; + --red: #CC3333; + --link-hover:#0A7D72; - --t1: #111827; - --t2: #374151; - --t3: #6b7280; - --t4: #9ca3af; + --ok-bg: rgba(13,155,142,0.08); + --ok-bd: rgba(13,155,142,0.22); + --ok-text: #0D9B8E; + --soft-blue: rgba(13,155,142,0.07); + --soft-green: rgba(22,163,74,0.07); - --rule: #f3f4f6; - --rule2: #e5e7eb; + --badge-red-bg: rgba(204,51,51,0.10); + --badge-red-text: #CC3333; + --badge-amber-bg: rgba(176,122,10,0.10); + --badge-amber-text:#B07A0A; + --badge-blue-bg: rgba(13,155,142,0.10); + --badge-blue-text: #0D9B8E; + --badge-gray-bg: rgba(5,13,18,0.06); + --badge-gray-text: rgba(5,13,18,0.46); - --sans: 'Noto Sans SC', system-ui, sans-serif; - --mono: 'JetBrains Mono', 'Courier New', monospace; + --t1: #050D12; + --t2: rgba(5,13,18,0.76); + --t3: rgba(5,13,18,0.52); + --t4: rgba(5,13,18,0.36); - --r: 12px; - --r-sm: 8px; + --rule: rgba(13,155,142,0.10); + --rule2: rgba(13,155,142,0.20); + + --sans: 'IBM Plex Sans', system-ui, sans-serif; + --mono: 'IBM Plex Mono', 'Courier New', monospace; + + --r: 8px; + --r-sm: 5px; --r-pill: 9999px; - --shadow-sm: 0 1px 3px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04); - --shadow: 0 4px 12px rgba(0,0,0,0.08); - --shadow-lg: 0 8px 24px rgba(37,99,235,0.12); + /* clinical: no decorative shadows */ + --shadow-sm: none; + --shadow: none; + --shadow-lg: none; } +/* ── Dark ─────────────────────────────────────────────────────── */ +:root[data-theme="dark"] { + --bg: #050D12; + --bg-card: #0B1820; + --bg-hover: rgba(61,217,198,0.06); + --bg-1: #0B1820; + --bg-2: #071018; + --bg-hi: rgba(61,217,198,0.06); + + --blue: #3DD9C6; + --blue-2: #3DD9C6; + --blue-gl: rgba(61,217,198,0.06); + --blue-bd: rgba(61,217,198,0.20); + + --violet: #A78BFA; + --cyan: #22D3EE; + --mint: #3DD9C6; + --amber: #F5C97A; + --red: #FF6B6B; + --link-hover:#5DE8D5; + + --ok-bg: rgba(61,217,198,0.10); + --ok-bd: rgba(61,217,198,0.20); + --ok-text: #3DD9C6; + --soft-blue: rgba(61,217,198,0.06); + --soft-green: rgba(61,217,198,0.06); + + --badge-red-bg: rgba(255,107,107,0.15); + --badge-red-text: #FF6B6B; + --badge-amber-bg: rgba(245,201,122,0.15); + --badge-amber-text:#F5C97A; + --badge-blue-bg: rgba(61,217,198,0.12); + --badge-blue-text: #3DD9C6; + --badge-gray-bg: rgba(255,255,255,0.08); + --badge-gray-text: rgba(255,255,255,0.40); + + --t1: #FFFFFF; + --t2: rgba(255,255,255,0.76); + --t3: rgba(255,255,255,0.50); + --t4: rgba(255,255,255,0.30); + + --rule: rgba(61,217,198,0.08); + --rule2: rgba(61,217,198,0.15); +} + +/* ── Global base ──────────────────────────────────────────────── */ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } -html, body { - height: 100%; +html { font-size: 16px; } + +body { + font-family: var(--sans); background: var(--bg); color: var(--t1); - font-family: var(--sans); - font-size: 14px; line-height: 1.5; + font-size: 14px; -webkit-font-smoothing: antialiased; + transition: background 0.25s, color 0.25s; } a { color: inherit; text-decoration: none; } -button { cursor: pointer; font-family: inherit; border: none; background: none; } -input, select, textarea { font-family: inherit; } +button { background: none; border: none; cursor: pointer; font: inherit; } +input, select, textarea { font: inherit; } -/* Element Plus light theme */ -.el-input__wrapper { background: #fff !important; box-shadow: 0 0 0 1px var(--rule2) !important; } +/* Element Plus overrides */ +.el-input__wrapper { background: var(--bg-card) !important; box-shadow: 0 0 0 1px var(--rule2) !important; } .el-input__inner { color: var(--t1) !important; } -.el-select-dropdown { background: #fff; border-color: var(--rule2); } +.el-select-dropdown { background: var(--bg-card); border-color: var(--rule2); } .el-select-dropdown__item { color: var(--t2); } .el-select-dropdown__item.hover, .el-select-dropdown__item:hover { background: var(--bg-hover); } -.el-table { --el-table-bg-color: #fff; --el-table-header-bg-color: var(--bg-2); } +.el-table { + --el-table-bg-color: var(--bg-card); + --el-table-tr-bg-color: var(--bg-card); + --el-table-header-bg-color: var(--bg-2); + --el-table-text-color: var(--t2); + --el-table-header-text-color: var(--t1); + --el-table-border-color: var(--rule); + --el-table-row-hover-bg-color: var(--bg-hover); +} +:root[data-theme="dark"] .el-popper, +:root[data-theme="dark"] .el-select__popper.el-popper { + background: var(--bg-card); border-color: var(--rule2); color: var(--t2); +} diff --git a/frontend/src/views/Admin.vue b/frontend/src/views/Admin.vue index 29a0636..5732e39 100644 --- a/frontend/src/views/Admin.vue +++ b/frontend/src/views/Admin.vue @@ -2,6 +2,13 @@
+
+ ← 返回首页
@@ -169,12 +181,19 @@ diff --git a/frontend/vite.config.js b/frontend/vite.config.js index 5ada2ae..f4f6a78 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -8,7 +8,8 @@ export default defineConfig({ alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) }, }, server: { - allowedHosts: ['ddc.chenwuzhu.cn'], + host: '0.0.0.0', + allowedHosts: ['localhost', '127.0.0.1', 'ddc.chenwuzhu.cn'], proxy: { '/api': { target: 'http://127.0.0.1:8000', changeOrigin: true }, },