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 @@
+
+
+
PI
管理后台
@@ -33,6 +40,11 @@
+
← 返回首页
@@ -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 },
},