style: 全局 UI 主题皮肤重构与样式模块化

引入 Element Plus 主题定制和主题皮肤 composable,将全局
样式拆分为组件级独立 CSS 文件(侧边栏、顶栏、工作台等),
统一色彩变量和间距规范,重构所有视图和组件样式以适配新
主题系统,优化图表和知识图谱组件视觉表现,提取审计和差
旅报销相关子组件。
This commit is contained in:
caoxiaozhu
2026-05-27 09:17:57 +08:00
parent df49103f23
commit 2dcc72102d
112 changed files with 10983 additions and 8996 deletions

View File

@@ -14,13 +14,13 @@
"updated_at": "2026-05-17T09:28:28.999515+00:00",
"uploaded_by": "admin",
"version_number": 1,
"ingest_status": 1,
"ingest_status_updated_at": "2026-05-26T02:39:00.726523+00:00",
"ingest_status": 4,
"ingest_status_updated_at": "2026-05-26T16:01:08.579777+00:00",
"ingest_completed_at": "2026-05-17T10:01:33.272539+00:00",
"ingest_document_name": "远光《公司支出管理办法2024》.pdf",
"ingest_document_updated_at": "2026-05-17T09:28:28.999515+00:00",
"ingest_document_sha256": "67a74538bce0dec71ccbb947256cc2c9c0e672d148de49406b967ae1379dbece",
"ingest_agent_run_id": "run_3a0b0ecb941b4c8e"
"ingest_agent_run_id": "run_7236fb72747742a3"
},
{
"id": "c7601043d9944ef2bcf4d3f67ed253f7",
@@ -35,13 +35,13 @@
"updated_at": "2026-05-22T07:00:22.328877+00:00",
"uploaded_by": "系统导入",
"version_number": 1,
"ingest_status": 1,
"ingest_status_updated_at": "2026-05-26T02:39:00.731130+00:00",
"ingest_status": 4,
"ingest_status_updated_at": "2026-05-26T16:01:09.863684+00:00",
"ingest_completed_at": "2026-05-22T09:22:25.565409+00:00",
"ingest_document_name": "远光软件会计科目使用说明.xlsx",
"ingest_document_updated_at": "2026-05-22T07:00:22.328877+00:00",
"ingest_document_sha256": "",
"ingest_agent_run_id": "run_8c1ab050c9734d96"
"ingest_agent_run_id": "run_7236fb72747742a3"
},
{
"id": "b0277cd76034437997fbf5219662725a",
@@ -56,13 +56,13 @@
"updated_at": "2026-05-22T07:00:22.011016+00:00",
"uploaded_by": "系统导入",
"version_number": 1,
"ingest_status": 1,
"ingest_status_updated_at": "2026-05-26T02:39:00.735501+00:00",
"ingest_status": 4,
"ingest_status_updated_at": "2026-05-26T16:00:50.652735+00:00",
"ingest_completed_at": "2026-05-23T14:30:33.605531+00:00",
"ingest_document_name": "远光软件财务基础知识手册.docx",
"ingest_document_updated_at": "2026-05-22T07:00:22.011016+00:00",
"ingest_document_sha256": "",
"ingest_agent_run_id": "run_8c1ab050c9734d96"
"ingest_agent_run_id": "run_7236fb72747742a3"
},
{
"id": "23f56f159a3e4bc3b2338056544120dd",
@@ -77,13 +77,13 @@
"updated_at": "2026-05-22T07:00:22.352133+00:00",
"uploaded_by": "系统导入",
"version_number": 1,
"ingest_status": 1,
"ingest_status_updated_at": "2026-05-26T02:39:00.739842+00:00",
"ingest_status": 4,
"ingest_status_updated_at": "2026-05-26T16:00:51.908821+00:00",
"ingest_completed_at": "2026-05-22T09:23:11.334499+00:00",
"ingest_document_name": "远光软件财务术语解释手册.docx",
"ingest_document_updated_at": "2026-05-22T07:00:22.352133+00:00",
"ingest_document_sha256": "",
"ingest_agent_run_id": "run_8c1ab050c9734d96"
"ingest_agent_run_id": "run_7236fb72747742a3"
},
{
"id": "09fbcae74d3b41e498a47e05b45262cb",
@@ -98,13 +98,13 @@
"updated_at": "2026-05-22T07:00:22.304623+00:00",
"uploaded_by": "系统导入",
"version_number": 1,
"ingest_status": 1,
"ingest_status_updated_at": "2026-05-26T02:39:00.744555+00:00",
"ingest_status": 4,
"ingest_status_updated_at": "2026-05-26T16:01:13.581834+00:00",
"ingest_completed_at": "2026-05-22T09:24:18.933073+00:00",
"ingest_document_name": "远光软件高新技术企业税收优惠政策汇总.pdf",
"ingest_document_updated_at": "2026-05-22T07:00:22.304623+00:00",
"ingest_document_sha256": "",
"ingest_agent_run_id": "run_8c1ab050c9734d96"
"ingest_agent_run_id": "run_7236fb72747742a3"
},
{
"id": "5fb3c63fbfe244a280cf3316a20150cd",
@@ -119,13 +119,13 @@
"updated_at": "2026-05-22T07:00:18.153373+00:00",
"uploaded_by": "系统导入",
"version_number": 1,
"ingest_status": 1,
"ingest_status_updated_at": "2026-05-26T02:39:00.762391+00:00",
"ingest_status": 4,
"ingest_status_updated_at": "2026-05-26T16:00:53.906324+00:00",
"ingest_completed_at": "2026-05-22T16:01:43.168774+00:00",
"ingest_document_name": "远光软件公司内部控制基本规范.pdf",
"ingest_document_updated_at": "2026-05-22T07:00:18.153373+00:00",
"ingest_document_sha256": "",
"ingest_agent_run_id": "run_655cdac08d3d4a7f"
"ingest_agent_run_id": "run_7236fb72747742a3"
},
{
"id": "f4ae48231a974240bbaf6c9f3bfd4160",
@@ -140,13 +140,13 @@
"updated_at": "2026-05-22T07:00:18.190399+00:00",
"uploaded_by": "系统导入",
"version_number": 1,
"ingest_status": 1,
"ingest_status_updated_at": "2026-05-26T02:39:00.773116+00:00",
"ingest_status": 4,
"ingest_status_updated_at": "2026-05-26T16:00:55.339114+00:00",
"ingest_completed_at": "2026-05-22T16:03:00.735908+00:00",
"ingest_document_name": "远光软件公司合同管理制度.docx",
"ingest_document_updated_at": "2026-05-22T07:00:18.190399+00:00",
"ingest_document_sha256": "",
"ingest_agent_run_id": "run_655cdac08d3d4a7f"
"ingest_agent_run_id": "run_7236fb72747742a3"
},
{
"id": "b1d08d6a9dc6404aba9098f3b7287353",
@@ -161,13 +161,13 @@
"updated_at": "2026-05-22T07:00:17.798679+00:00",
"uploaded_by": "系统导入",
"version_number": 1,
"ingest_status": 1,
"ingest_status_updated_at": "2026-05-26T02:39:00.784020+00:00",
"ingest_status": 4,
"ingest_status_updated_at": "2026-05-26T16:00:56.741808+00:00",
"ingest_completed_at": "2026-05-22T16:03:46.921675+00:00",
"ingest_document_name": "远光软件公司财务管理制度总则.docx",
"ingest_document_updated_at": "2026-05-22T07:00:17.798679+00:00",
"ingest_document_sha256": "",
"ingest_agent_run_id": "run_655cdac08d3d4a7f"
"ingest_agent_run_id": "run_7236fb72747742a3"
},
{
"id": "c87fc4aabe524c6c81862c20aabe434c",
@@ -182,13 +182,13 @@
"updated_at": "2026-05-22T07:00:18.531598+00:00",
"uploaded_by": "系统导入",
"version_number": 1,
"ingest_status": 1,
"ingest_status_updated_at": "2026-05-26T02:39:00.799323+00:00",
"ingest_status": 4,
"ingest_status_updated_at": "2026-05-26T16:01:19.014702+00:00",
"ingest_completed_at": "2026-05-22T16:04:58.719410+00:00",
"ingest_document_name": "远光软件公司资产管理制度.pdf",
"ingest_document_updated_at": "2026-05-22T07:00:18.531598+00:00",
"ingest_document_sha256": "",
"ingest_agent_run_id": "run_655cdac08d3d4a7f"
"ingest_agent_run_id": "run_7236fb72747742a3"
},
{
"id": "13181df0179a4bacb12a2f65e3772d9b",
@@ -203,13 +203,13 @@
"updated_at": "2026-05-22T07:00:18.221073+00:00",
"uploaded_by": "系统导入",
"version_number": 1,
"ingest_status": 1,
"ingest_status_updated_at": "2026-05-26T02:39:00.814611+00:00",
"ingest_status": 4,
"ingest_status_updated_at": "2026-05-26T16:00:59.485821+00:00",
"ingest_completed_at": "2026-05-22T16:06:08.172318+00:00",
"ingest_document_name": "远光软件公司采购管理办法.xlsx",
"ingest_document_updated_at": "2026-05-22T07:00:18.221073+00:00",
"ingest_document_sha256": "",
"ingest_agent_run_id": "run_655cdac08d3d4a7f"
"ingest_agent_run_id": "run_7236fb72747742a3"
},
{
"id": "396588b0cdd04c86a61ae0b9bd04e06c",
@@ -224,13 +224,13 @@
"updated_at": "2026-05-22T07:00:19.734422+00:00",
"uploaded_by": "系统导入",
"version_number": 1,
"ingest_status": 1,
"ingest_status_updated_at": "2026-05-26T02:39:00.830249+00:00",
"ingest_status": 4,
"ingest_status_updated_at": "2026-05-26T16:01:00.774887+00:00",
"ingest_completed_at": "2026-05-22T16:06:48.466110+00:00",
"ingest_document_name": "远光软件公司差旅费管理办法.docx",
"ingest_document_updated_at": "2026-05-22T07:00:19.734422+00:00",
"ingest_document_sha256": "",
"ingest_agent_run_id": "run_655cdac08d3d4a7f"
"ingest_agent_run_id": "run_7236fb72747742a3"
},
{
"id": "fe5f834f94244b77bb62171d580ecee3",
@@ -245,13 +245,13 @@
"updated_at": "2026-05-22T07:00:20.095824+00:00",
"uploaded_by": "系统导入",
"version_number": 1,
"ingest_status": 1,
"ingest_status_updated_at": "2026-05-26T02:39:00.847094+00:00",
"ingest_status": 4,
"ingest_status_updated_at": "2026-05-26T16:01:02.037101+00:00",
"ingest_completed_at": "2026-05-22T16:07:23.262328+00:00",
"ingest_document_name": "远光软件出差审批流程说明.pdf",
"ingest_document_updated_at": "2026-05-22T07:00:20.095824+00:00",
"ingest_document_sha256": "",
"ingest_agent_run_id": "run_655cdac08d3d4a7f"
"ingest_agent_run_id": "run_7236fb72747742a3"
},
{
"id": "be3fca61e2be421896405082c93cf86c",
@@ -266,13 +266,13 @@
"updated_at": "2026-05-22T07:00:20.128471+00:00",
"uploaded_by": "系统导入",
"version_number": 1,
"ingest_status": 1,
"ingest_status_updated_at": "2026-05-26T02:39:00.865452+00:00",
"ingest_status": 4,
"ingest_status_updated_at": "2026-05-26T16:01:24.076574+00:00",
"ingest_completed_at": "2026-05-22T16:08:02.190081+00:00",
"ingest_document_name": "远光软件国际出差管理规定.docx",
"ingest_document_updated_at": "2026-05-22T07:00:20.128471+00:00",
"ingest_document_sha256": "",
"ingest_agent_run_id": "run_655cdac08d3d4a7f"
"ingest_agent_run_id": "run_7236fb72747742a3"
},
{
"id": "c4421b3049b244a8a92cc53d502e530f",
@@ -287,13 +287,13 @@
"updated_at": "2026-05-22T07:00:19.759954+00:00",
"uploaded_by": "系统导入",
"version_number": 1,
"ingest_status": 1,
"ingest_status_updated_at": "2026-05-26T02:39:00.888420+00:00",
"ingest_status": 4,
"ingest_status_updated_at": "2026-05-26T16:01:25.270086+00:00",
"ingest_completed_at": "2026-05-22T16:09:23.091744+00:00",
"ingest_document_name": "远光软件差旅费标准速查表.xlsx",
"ingest_document_updated_at": "2026-05-22T07:00:19.759954+00:00",
"ingest_document_sha256": "",
"ingest_agent_run_id": "run_655cdac08d3d4a7f"
"ingest_agent_run_id": "run_7236fb72747742a3"
},
{
"id": "e13cc0a8d6474b6caeeedc49c4304558",
@@ -308,13 +308,13 @@
"updated_at": "2026-05-22T07:00:18.922298+00:00",
"uploaded_by": "系统导入",
"version_number": 1,
"ingest_status": 1,
"ingest_status_updated_at": "2026-05-26T02:39:00.905615+00:00",
"ingest_status": 4,
"ingest_status_updated_at": "2026-05-26T16:01:26.510710+00:00",
"ingest_completed_at": "2026-05-22T16:11:04.764727+00:00",
"ingest_document_name": "远光软件公司发票审核标准.xlsx",
"ingest_document_updated_at": "2026-05-22T07:00:18.922298+00:00",
"ingest_document_sha256": "",
"ingest_agent_run_id": "run_655cdac08d3d4a7f"
"ingest_agent_run_id": "run_7236fb72747742a3"
},
{
"id": "7170abfdde6f4e6abad2fc987564c2cf",
@@ -329,13 +329,13 @@
"updated_at": "2026-05-22T07:00:18.560177+00:00",
"uploaded_by": "系统导入",
"version_number": 1,
"ingest_status": 1,
"ingest_status_updated_at": "2026-05-26T02:39:00.919568+00:00",
"ingest_status": 4,
"ingest_status_updated_at": "2026-05-26T16:01:06.719118+00:00",
"ingest_completed_at": "2026-05-22T16:11:54.017817+00:00",
"ingest_document_name": "远光软件公司发票管理规范.docx",
"ingest_document_updated_at": "2026-05-22T07:00:18.560177+00:00",
"ingest_document_sha256": "",
"ingest_agent_run_id": "run_655cdac08d3d4a7f"
"ingest_agent_run_id": "run_7236fb72747742a3"
},
{
"id": "dd0d7b32e832446e8ce9caa06c442685",
@@ -350,13 +350,13 @@
"updated_at": "2026-05-22T07:00:18.888128+00:00",
"uploaded_by": "系统导入",
"version_number": 1,
"ingest_status": 1,
"ingest_status_updated_at": "2026-05-26T02:39:00.934348+00:00",
"ingest_status": 4,
"ingest_status_updated_at": "2026-05-26T16:01:28.865726+00:00",
"ingest_completed_at": "2026-05-22T16:12:23.821434+00:00",
"ingest_document_name": "远光软件公司增值税发票操作指南.pdf",
"ingest_document_updated_at": "2026-05-22T07:00:18.888128+00:00",
"ingest_document_sha256": "",
"ingest_agent_run_id": "run_655cdac08d3d4a7f"
"ingest_agent_run_id": "run_7236fb72747742a3"
},
{
"id": "f268a54ee05e4dfca33fd86bcc077216",
@@ -371,13 +371,13 @@
"updated_at": "2026-05-22T07:00:18.953110+00:00",
"uploaded_by": "系统导入",
"version_number": 1,
"ingest_status": 1,
"ingest_status_updated_at": "2026-05-26T02:39:00.949214+00:00",
"ingest_status": 4,
"ingest_status_updated_at": "2026-05-26T16:01:30.095619+00:00",
"ingest_completed_at": "2026-05-22T16:13:15.450300+00:00",
"ingest_document_name": "远光软件公司电子发票管理办法.docx",
"ingest_document_updated_at": "2026-05-22T07:00:18.953110+00:00",
"ingest_document_sha256": "",
"ingest_agent_run_id": "run_655cdac08d3d4a7f"
"ingest_agent_run_id": "run_7236fb72747742a3"
},
{
"id": "f3f74cb65a9a4a16933368218c5e25de",
@@ -392,13 +392,13 @@
"updated_at": "2026-05-22T07:00:21.585718+00:00",
"uploaded_by": "系统导入",
"version_number": 1,
"ingest_status": 1,
"ingest_status_updated_at": "2026-05-26T02:39:00.963406+00:00",
"ingest_status": 4,
"ingest_status_updated_at": "2026-05-26T16:01:09.790447+00:00",
"ingest_completed_at": "2026-05-22T16:13:44.636629+00:00",
"ingest_document_name": "远光软件企业所得税汇算清缴操作手册.pdf",
"ingest_document_updated_at": "2026-05-22T07:00:21.585718+00:00",
"ingest_document_sha256": "",
"ingest_agent_run_id": "run_655cdac08d3d4a7f"
"ingest_agent_run_id": "run_7236fb72747742a3"
},
{
"id": "56721ca1904b437486a609b85e3d9362",
@@ -413,13 +413,13 @@
"updated_at": "2026-05-22T07:00:20.881351+00:00",
"uploaded_by": "系统导入",
"version_number": 1,
"ingest_status": 1,
"ingest_status_updated_at": "2026-05-26T02:39:00.976986+00:00",
"ingest_status": 4,
"ingest_status_updated_at": "2026-05-26T16:01:11.027818+00:00",
"ingest_completed_at": "2026-05-22T16:14:50.092490+00:00",
"ingest_document_name": "远光软件公司税务管理制度.docx",
"ingest_document_updated_at": "2026-05-22T07:00:20.881351+00:00",
"ingest_document_sha256": "",
"ingest_agent_run_id": "run_655cdac08d3d4a7f"
"ingest_agent_run_id": "run_7236fb72747742a3"
},
{
"id": "2460661167ef456699ab259321db4156",
@@ -434,13 +434,13 @@
"updated_at": "2026-05-22T07:00:21.606227+00:00",
"uploaded_by": "系统导入",
"version_number": 1,
"ingest_status": 1,
"ingest_status_updated_at": "2026-05-26T02:39:00.995972+00:00",
"ingest_status": 4,
"ingest_status_updated_at": "2026-05-26T16:01:33.826025+00:00",
"ingest_completed_at": "2026-05-22T16:15:56.676286+00:00",
"ingest_document_name": "远光软件研发费用加计扣除管理办法.xlsx",
"ingest_document_updated_at": "2026-05-22T07:00:21.606227+00:00",
"ingest_document_sha256": "",
"ingest_agent_run_id": "run_655cdac08d3d4a7f"
"ingest_agent_run_id": "run_7236fb72747742a3"
},
{
"id": "e30f54ea32704fbd9701cc931b447a06",
@@ -455,13 +455,13 @@
"updated_at": "2026-05-22T07:00:21.202633+00:00",
"uploaded_by": "系统导入",
"version_number": 1,
"ingest_status": 1,
"ingest_status_updated_at": "2026-05-26T02:39:01.010947+00:00",
"ingest_status": 4,
"ingest_status_updated_at": "2026-05-26T16:01:13.991763+00:00",
"ingest_completed_at": "2026-05-22T16:16:06.540773+00:00",
"ingest_document_name": "远光软件软件产品增值税即征即退操作指南.pdf",
"ingest_document_updated_at": "2026-05-22T07:00:21.202633+00:00",
"ingest_document_sha256": "",
"ingest_agent_run_id": "run_655cdac08d3d4a7f"
"ingest_agent_run_id": "run_7236fb72747742a3"
},
{
"id": "2d1cd10154e84cb38640dce31f33b529",
@@ -476,13 +476,13 @@
"updated_at": "2026-05-22T07:00:22.379307+00:00",
"uploaded_by": "系统导入",
"version_number": 1,
"ingest_status": 1,
"ingest_status_updated_at": "2026-05-26T02:39:01.025910+00:00",
"ingest_status": 4,
"ingest_status_updated_at": "2026-05-26T16:01:15.257700+00:00",
"ingest_completed_at": "2026-05-22T16:23:24.252614+00:00",
"ingest_document_name": "远光软件公司预算管理制度.docx",
"ingest_document_updated_at": "2026-05-22T07:00:22.379307+00:00",
"ingest_document_sha256": "",
"ingest_agent_run_id": "run_655cdac08d3d4a7f"
"ingest_agent_run_id": "run_7236fb72747742a3"
},
{
"id": "229b3a79fef14360ba3cbd0a55e5e20c",
@@ -497,13 +497,13 @@
"updated_at": "2026-05-22T07:00:22.760169+00:00",
"uploaded_by": "系统导入",
"version_number": 1,
"ingest_status": 1,
"ingest_status_updated_at": "2026-05-26T02:39:01.044022+00:00",
"ingest_status": 4,
"ingest_status_updated_at": "2026-05-26T16:01:16.510610+00:00",
"ingest_completed_at": "2026-05-22T16:23:29.997956+00:00",
"ingest_document_name": "远光软件年度预算编制指南.pdf",
"ingest_document_updated_at": "2026-05-22T07:00:22.760169+00:00",
"ingest_document_sha256": "",
"ingest_agent_run_id": "run_655cdac08d3d4a7f"
"ingest_agent_run_id": "run_7236fb72747742a3"
},
{
"id": "a40da5544dea4efcade070274b84a54e",
@@ -518,13 +518,13 @@
"updated_at": "2026-05-22T07:00:22.848272+00:00",
"uploaded_by": "系统导入",
"version_number": 1,
"ingest_status": 1,
"ingest_status_updated_at": "2026-05-26T02:39:17.402454+00:00",
"ingest_status": 4,
"ingest_status_updated_at": "2026-05-26T16:01:38.728430+00:00",
"ingest_completed_at": "2026-05-22T16:24:37.382612+00:00",
"ingest_document_name": "远光软件预算执行分析报告模板.docx",
"ingest_document_updated_at": "2026-05-22T07:00:22.848272+00:00",
"ingest_document_sha256": "",
"ingest_agent_run_id": "run_655cdac08d3d4a7f"
"ingest_agent_run_id": "run_7236fb72747742a3"
},
{
"id": "dcd982e40ce94105824e59ecbbae75cb",
@@ -539,13 +539,13 @@
"updated_at": "2026-05-22T07:00:22.803708+00:00",
"uploaded_by": "系统导入",
"version_number": 1,
"ingest_status": 1,
"ingest_status_updated_at": "2026-05-26T02:39:17.417444+00:00",
"ingest_status": 4,
"ingest_status_updated_at": "2026-05-26T16:01:19.050297+00:00",
"ingest_completed_at": "2026-05-22T16:24:45.161319+00:00",
"ingest_document_name": "远光软件预算编制模板.xlsx",
"ingest_document_updated_at": "2026-05-22T07:00:22.803708+00:00",
"ingest_document_sha256": "",
"ingest_agent_run_id": "run_655cdac08d3d4a7f"
"ingest_agent_run_id": "run_7236fb72747742a3"
},
{
"id": "79cb9276398b4216ba17d5623aadf75f",
@@ -560,13 +560,13 @@
"updated_at": "2026-05-22T07:00:21.971983+00:00",
"uploaded_by": "系统导入",
"version_number": 1,
"ingest_status": 1,
"ingest_status_updated_at": "2026-05-26T02:39:17.433923+00:00",
"ingest_status": 4,
"ingest_status_updated_at": "2026-05-26T16:01:20.323058+00:00",
"ingest_completed_at": "2026-05-22T16:25:33.968414+00:00",
"ingest_document_name": "远光软件财务共享服务SLA标准.xlsx",
"ingest_document_updated_at": "2026-05-22T07:00:21.971983+00:00",
"ingest_document_sha256": "",
"ingest_agent_run_id": "run_655cdac08d3d4a7f"
"ingest_agent_run_id": "run_7236fb72747742a3"
},
{
"id": "f841ca416b5d404994a7c4a310e35569",
@@ -581,13 +581,13 @@
"updated_at": "2026-05-22T07:00:21.634300+00:00",
"uploaded_by": "系统导入",
"version_number": 1,
"ingest_status": 1,
"ingest_status_updated_at": "2026-05-26T02:39:17.450037+00:00",
"ingest_status": 4,
"ingest_status_updated_at": "2026-05-26T16:01:21.585474+00:00",
"ingest_completed_at": "2026-05-22T16:26:05.301987+00:00",
"ingest_document_name": "远光软件财务共享服务中心运营管理办法.docx",
"ingest_document_updated_at": "2026-05-22T07:00:21.634300+00:00",
"ingest_document_sha256": "",
"ingest_agent_run_id": "run_655cdac08d3d4a7f"
"ingest_agent_run_id": "run_7236fb72747742a3"
},
{
"id": "d1ad784de58a4c4a802a0b9fbce29f62",
@@ -602,13 +602,13 @@
"updated_at": "2026-05-22T07:00:21.945868+00:00",
"uploaded_by": "系统导入",
"version_number": 1,
"ingest_status": 1,
"ingest_status_updated_at": "2026-05-26T02:39:17.471635+00:00",
"ingest_status": 4,
"ingest_status_updated_at": "2026-05-26T16:01:43.752235+00:00",
"ingest_completed_at": "2026-05-22T16:26:54.048075+00:00",
"ingest_document_name": "远光软件财务共享服务操作手册.pdf",
"ingest_document_updated_at": "2026-05-22T07:00:21.945868+00:00",
"ingest_document_sha256": "",
"ingest_agent_run_id": "run_655cdac08d3d4a7f"
"ingest_agent_run_id": "run_7236fb72747742a3"
},
{
"id": "ce50d015861f4633a634a2eae416fa2e",
@@ -623,13 +623,13 @@
"updated_at": "2026-05-22T07:00:19.662743+00:00",
"uploaded_by": "系统导入",
"version_number": 1,
"ingest_status": 1,
"ingest_status_updated_at": "2026-05-26T02:39:17.489793+00:00",
"ingest_status": 4,
"ingest_status_updated_at": "2026-05-26T16:01:24.093834+00:00",
"ingest_completed_at": "2026-05-22T16:27:31.775974+00:00",
"ingest_document_name": "远光软件报销流程培训手册.pdf",
"ingest_document_updated_at": "2026-05-22T07:00:19.662743+00:00",
"ingest_document_sha256": "",
"ingest_agent_run_id": "run_655cdac08d3d4a7f"
"ingest_agent_run_id": "run_7236fb72747742a3"
},
{
"id": "56a0e13b705e49468d46629f3b5f691a",
@@ -644,13 +644,13 @@
"updated_at": "2026-05-22T07:00:19.323921+00:00",
"uploaded_by": "系统导入",
"version_number": 1,
"ingest_status": 1,
"ingest_status_updated_at": "2026-05-26T02:39:17.505506+00:00",
"ingest_status": 4,
"ingest_status_updated_at": "2026-05-26T16:01:25.246857+00:00",
"ingest_completed_at": "2026-05-22T16:27:44.244066+00:00",
"ingest_document_name": "远光软件新员工财务培训课件.pdf",
"ingest_document_updated_at": "2026-05-22T07:00:19.323921+00:00",
"ingest_document_sha256": "",
"ingest_agent_run_id": "run_655cdac08d3d4a7f"
"ingest_agent_run_id": "run_7236fb72747742a3"
},
{
"id": "22ef5d13bb5e4307a8097628eaa3d398",
@@ -665,13 +665,13 @@
"updated_at": "2026-05-22T07:00:18.988700+00:00",
"uploaded_by": "系统导入",
"version_number": 1,
"ingest_status": 1,
"ingest_status_updated_at": "2026-05-26T02:39:17.520887+00:00",
"ingest_status": 4,
"ingest_status_updated_at": "2026-05-26T16:01:26.471932+00:00",
"ingest_completed_at": "2026-05-22T16:28:24.573683+00:00",
"ingest_document_name": "远光软件财务制度培训手册.docx",
"ingest_document_updated_at": "2026-05-22T07:00:18.988700+00:00",
"ingest_document_sha256": "",
"ingest_agent_run_id": "run_655cdac08d3d4a7f"
"ingest_agent_run_id": "run_7236fb72747742a3"
},
{
"id": "78d1a28f1c934f46b762fb1466d4be32",
@@ -686,13 +686,13 @@
"updated_at": "2026-05-22T07:00:19.686485+00:00",
"uploaded_by": "系统导入",
"version_number": 1,
"ingest_status": 1,
"ingest_status_updated_at": "2026-05-26T02:39:17.542919+00:00",
"ingest_status": 4,
"ingest_status_updated_at": "2026-05-26T16:01:48.525207+00:00",
"ingest_completed_at": "2026-05-22T16:29:03.349502+00:00",
"ingest_document_name": "远光软件财务培训课程安排.xlsx",
"ingest_document_updated_at": "2026-05-22T07:00:19.686485+00:00",
"ingest_document_sha256": "",
"ingest_agent_run_id": "run_655cdac08d3d4a7f"
"ingest_agent_run_id": "run_7236fb72747742a3"
},
{
"id": "91fbf156593a4dcc956780962195ffd7",
@@ -707,13 +707,13 @@
"updated_at": "2026-05-22T07:00:20.476077+00:00",
"uploaded_by": "系统导入",
"version_number": 1,
"ingest_status": 1,
"ingest_status_updated_at": "2026-05-26T02:39:17.558881+00:00",
"ingest_status": 4,
"ingest_status_updated_at": "2026-05-26T16:01:49.746825+00:00",
"ingest_completed_at": "2026-05-22T16:29:29.050791+00:00",
"ingest_document_name": "远光软件报销问题处理指引.xlsx",
"ingest_document_updated_at": "2026-05-22T07:00:20.476077+00:00",
"ingest_document_sha256": "",
"ingest_agent_run_id": "run_655cdac08d3d4a7f"
"ingest_agent_run_id": "run_7236fb72747742a3"
},
{
"id": "a24793b7f7de4749a7c531d1713a4a2b",
@@ -728,13 +728,13 @@
"updated_at": "2026-05-22T07:00:20.453567+00:00",
"uploaded_by": "系统导入",
"version_number": 1,
"ingest_status": 1,
"ingest_status_updated_at": "2026-05-26T02:39:17.575410+00:00",
"ingest_status": 4,
"ingest_status_updated_at": "2026-05-26T16:01:30.343781+00:00",
"ingest_completed_at": "2026-05-22T16:35:03.548506+00:00",
"ingest_document_name": "远光软件财务制度问答汇总.pdf",
"ingest_document_updated_at": "2026-05-22T07:00:20.453567+00:00",
"ingest_document_sha256": "",
"ingest_agent_run_id": "run_655cdac08d3d4a7f"
"ingest_agent_run_id": "run_7236fb72747742a3"
},
{
"id": "3acd9c2df63b4a438c7eab876269b25d",
@@ -749,13 +749,13 @@
"updated_at": "2026-05-22T07:00:20.158497+00:00",
"uploaded_by": "系统导入",
"version_number": 1,
"ingest_status": 1,
"ingest_status_updated_at": "2026-05-26T02:39:17.593165+00:00",
"ingest_status": 4,
"ingest_status_updated_at": "2026-05-26T16:01:31.573128+00:00",
"ingest_completed_at": "2026-05-22T16:35:27.056080+00:00",
"ingest_document_name": "远光软件财务报销常见问题解答.docx",
"ingest_document_updated_at": "2026-05-22T07:00:20.158497+00:00",
"ingest_document_sha256": "",
"ingest_agent_run_id": "run_655cdac08d3d4a7f"
"ingest_agent_run_id": "run_7236fb72747742a3"
}
]
}

276
web/package-lock.json generated
View File

@@ -9,14 +9,14 @@
"version": "0.1.0",
"dependencies": {
"@antv/g6": "^5.1.1",
"@primevue/themes": "^4.5.4",
"@element-plus/icons-vue": "^2.3.2",
"@vitejs/plugin-vue": "^5.2.4",
"@vueuse/motion": "^3.0.3",
"chart.js": "^4.5.1",
"element-plus": "^2.14.0",
"markdown-it": "^14.1.1",
"pg": "^8.13.1",
"primeicons": "^7.0.0",
"primevue": "^4.5.5",
"vite": "^5.4.19",
"vue": "^3.5.13",
"vue-chartjs": "^5.3.3",
@@ -343,6 +343,24 @@
"node": ">=6.9.0"
}
},
"node_modules/@ctrl/tinycolor": {
"version": "4.2.0",
"resolved": "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-4.2.0.tgz",
"integrity": "sha512-kzyuwOAQnXJNLS9PSyrk0CWk35nWJW/zl/6KvnTBMFK65gm7U1/Z5BqjxeapjZCIhQcM/DsrEmcbRwDyXyXK4A==",
"license": "MIT",
"engines": {
"node": ">=14"
}
},
"node_modules/@element-plus/icons-vue": {
"version": "2.3.2",
"resolved": "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.3.2.tgz",
"integrity": "sha512-OzIuTaIfC8QXEPmJvB4Y4kw34rSXdCJzxcD1kFStBvr8bK6X1zQAYDo0CNMjojnfTqRQCJ0I7prlErcoRiET2A==",
"license": "MIT",
"peerDependencies": {
"vue": "^3.2.0"
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
@@ -711,6 +729,31 @@
"node": ">=12"
}
},
"node_modules/@floating-ui/core": {
"version": "1.7.5",
"resolved": "https://registry.npmmirror.com/@floating-ui/core/-/core-1.7.5.tgz",
"integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==",
"license": "MIT",
"dependencies": {
"@floating-ui/utils": "^0.2.11"
}
},
"node_modules/@floating-ui/dom": {
"version": "1.7.6",
"resolved": "https://registry.npmmirror.com/@floating-ui/dom/-/dom-1.7.6.tgz",
"integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==",
"license": "MIT",
"dependencies": {
"@floating-ui/core": "^1.7.5",
"@floating-ui/utils": "^0.2.11"
}
},
"node_modules/@floating-ui/utils": {
"version": "0.2.11",
"resolved": "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.2.11.tgz",
"integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==",
"license": "MIT"
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
@@ -799,86 +842,15 @@
"node": ">=18.12.0"
}
},
"node_modules/@primeuix/styled": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/@primeuix/styled/-/styled-0.7.4.tgz",
"integrity": "sha512-QSO/NpOQg8e9BONWRBx9y8VGMCMYz0J/uKfNJEya/RGEu7ARx0oYW0ugI1N3/KB1AAvyGxzKBzGImbwg0KUiOQ==",
"node_modules/@popperjs/core": {
"name": "@sxzz/popperjs-es",
"version": "2.11.8",
"resolved": "https://registry.npmmirror.com/@sxzz/popperjs-es/-/popperjs-es-2.11.8.tgz",
"integrity": "sha512-wOwESXvvED3S8xBmcPWHs2dUuzrE4XiZeFu7e1hROIJkm02a49N120pmOXxY33sBb6hArItm5W5tcg1cBtV+HQ==",
"license": "MIT",
"dependencies": {
"@primeuix/utils": "^0.6.1"
},
"engines": {
"node": ">=12.11.0"
}
},
"node_modules/@primeuix/styles": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@primeuix/styles/-/styles-2.0.3.tgz",
"integrity": "sha512-2ykAB6BaHzR/6TwF8ShpJTsZrid6cVIEBVlookSdvOdmlWuevGu5vWOScgIwqWwlZcvkFYAGR/SUV3OHCTBMdw==",
"license": "MIT",
"dependencies": {
"@primeuix/styled": "^0.7.4"
}
},
"node_modules/@primeuix/themes": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@primeuix/themes/-/themes-2.0.3.tgz",
"integrity": "sha512-3fS1883mtCWhgUgNf/feiaaDSOND4EBIOu9tZnzJlJ8QtYyL6eFLcA6V3ymCWqLVXQ1+lTVEZv1gl47FIdXReg==",
"license": "MIT",
"dependencies": {
"@primeuix/styled": "^0.7.4"
}
},
"node_modules/@primeuix/utils": {
"version": "0.6.4",
"resolved": "https://registry.npmjs.org/@primeuix/utils/-/utils-0.6.4.tgz",
"integrity": "sha512-pZ5f+vj7wSzRhC7KoEQRU5fvYAe+RP9+m39CTscZ3UywCD1Y2o6Fe1rRgklMPSkzUcty2jzkA0zMYkiJBD1hgg==",
"license": "MIT",
"engines": {
"node": ">=12.11.0"
}
},
"node_modules/@primevue/core": {
"version": "4.5.5",
"resolved": "https://registry.npmjs.org/@primevue/core/-/core-4.5.5.tgz",
"integrity": "sha512-JpkXhq1ddc70JdsC3CC4dM+UbeeWuCW/8DpS9dNBfrOk824TLSlRlMEGFyVKqRMn5WPQvYLiy3xXfLQeNdSqhQ==",
"license": "MIT",
"dependencies": {
"@primeuix/styled": "^0.7.4",
"@primeuix/utils": "^0.6.2"
},
"engines": {
"node": ">=12.11.0"
},
"peerDependencies": {
"vue": "^3.5.0"
}
},
"node_modules/@primevue/icons": {
"version": "4.5.5",
"resolved": "https://registry.npmjs.org/@primevue/icons/-/icons-4.5.5.tgz",
"integrity": "sha512-eteOhTdAOXEYE9qW1AOrBBgDxQ2szHJxSkEK1XVdV2TKxGM5FQf03Ovms0VDyZTc16XBIgvwYjXJQS0BPbhPaA==",
"license": "MIT",
"dependencies": {
"@primeuix/utils": "^0.6.2",
"@primevue/core": "4.5.5"
},
"engines": {
"node": ">=12.11.0"
}
},
"node_modules/@primevue/themes": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/@primevue/themes/-/themes-4.5.4.tgz",
"integrity": "sha512-rUFZxMHLanTZdvZq4zgZPk+KRBZ3s7fE3bBK32OrZBkHQhEJmkJ7Ftd4w4QFlXyz1B7c+k5invZiOOCjwHXg9Q==",
"deprecated": "Deprecated. This package is no longer maintained. Please migrate to @primeuix/themes: https://www.npmjs.com/package/@primeuix/themes",
"license": "MIT",
"dependencies": {
"@primeuix/styled": "^0.7.4",
"@primeuix/themes": "^2.0.2"
},
"engines": {
"node": ">=12.11.0"
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/popperjs"
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
@@ -1347,6 +1319,21 @@
"integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",
"license": "MIT"
},
"node_modules/@types/lodash": {
"version": "4.17.24",
"resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.17.24.tgz",
"integrity": "sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==",
"license": "MIT"
},
"node_modules/@types/lodash-es": {
"version": "4.17.12",
"resolved": "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.12.tgz",
"integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
"license": "MIT",
"dependencies": {
"@types/lodash": "*"
}
},
"node_modules/@types/web-bluetooth": {
"version": "0.0.21",
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz",
@@ -1561,6 +1548,12 @@
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"license": "Python-2.0"
},
"node_modules/async-validator": {
"version": "4.2.5",
"resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz",
"integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==",
"license": "MIT"
},
"node_modules/base64-arraybuffer": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
@@ -2004,6 +1997,12 @@
"lodash": "^4.17.15"
}
},
"node_modules/dayjs": {
"version": "1.11.21",
"resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.21.tgz",
"integrity": "sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==",
"license": "MIT"
},
"node_modules/defu": {
"version": "6.1.7",
"resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz",
@@ -2030,6 +2029,70 @@
"url": "https://dotenvx.com"
}
},
"node_modules/element-plus": {
"version": "2.14.0",
"resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.14.0.tgz",
"integrity": "sha512-POgH+TtoreaEKWqYYAVQyE6i8rQMEFqAEublyF29dBA5yASWPLKY6EzfeqBTr2Uv26mPss4vSrMrNPyaK7LX5w==",
"license": "MIT",
"dependencies": {
"@ctrl/tinycolor": "^4.2.0",
"@element-plus/icons-vue": "^2.3.2",
"@floating-ui/dom": "^1.0.1",
"@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.8",
"@types/lodash": "^4.17.24",
"@types/lodash-es": "^4.17.12",
"@vueuse/core": "14.3.0",
"async-validator": "^4.2.5",
"dayjs": "^1.11.20",
"lodash": "^4.18.1",
"lodash-es": "^4.18.1",
"lodash-unified": "^1.0.3",
"memoize-one": "^6.0.0",
"normalize-wheel-es": "^1.2.0",
"vue-component-type-helpers": "^3.2.8"
},
"peerDependencies": {
"vue": "^3.3.7"
}
},
"node_modules/element-plus/node_modules/@vueuse/core": {
"version": "14.3.0",
"resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-14.3.0.tgz",
"integrity": "sha512-aHfz47g0ZhMtTVHmIzMVpJy8ePhhOy68GY5bv110+5DVtZ+W7BsOx+m61UNQqfrWyPztIHIanWa3E2tib3NFIw==",
"license": "MIT",
"dependencies": {
"@types/web-bluetooth": "^0.0.21",
"@vueuse/metadata": "14.3.0",
"@vueuse/shared": "14.3.0"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"vue": "^3.5.0"
}
},
"node_modules/element-plus/node_modules/@vueuse/metadata": {
"version": "14.3.0",
"resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-14.3.0.tgz",
"integrity": "sha512-BwxmbAzwAVF50+MW57GXOUEV61nFBGnlBvrTqj49PqWJu3uw7hdu72ztXeZ33RdZtDY6kO+bfCAE1PCn88Tktw==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/element-plus/node_modules/@vueuse/shared": {
"version": "14.3.0",
"resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-14.3.0.tgz",
"integrity": "sha512-bZpge9eSXwa4ToSiqJ7j6KRwhAsneMFoSz3LMWKQDkqimm3D/tbFlrklrs/IOqC8tEcYmXQZJ6N0UrjhBirVCg==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"vue": "^3.5.0"
}
},
"node_modules/entities": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
@@ -2292,6 +2355,23 @@
"integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
"license": "MIT"
},
"node_modules/lodash-es": {
"version": "4.18.1",
"resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.18.1.tgz",
"integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==",
"license": "MIT"
},
"node_modules/lodash-unified": {
"version": "1.0.3",
"resolved": "https://registry.npmmirror.com/lodash-unified/-/lodash-unified-1.0.3.tgz",
"integrity": "sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==",
"license": "MIT",
"peerDependencies": {
"@types/lodash-es": "*",
"lodash": "*",
"lodash-es": "*"
}
},
"node_modules/magic-string": {
"version": "0.30.21",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
@@ -2336,6 +2416,12 @@
"integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
"license": "MIT"
},
"node_modules/memoize-one": {
"version": "6.0.0",
"resolved": "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz",
"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==",
"license": "MIT"
},
"node_modules/ml-array-max": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/ml-array-max/-/ml-array-max-2.0.0.tgz",
@@ -2425,6 +2511,12 @@
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/normalize-wheel-es": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz",
"integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==",
"license": "BSD-3-Clause"
},
"node_modules/ohash": {
"version": "2.0.11",
"resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz",
@@ -2651,22 +2743,6 @@
"integrity": "sha512-jK3Et9UzwzTsd6tzl2RmwrVY/b8raJ3QZLzoDACj+oTJ0oX7L9Hy+XnVwgo4QVKlKpnP/Ur13SXV/pVh4LzaDw==",
"license": "MIT"
},
"node_modules/primevue": {
"version": "4.5.5",
"resolved": "https://registry.npmjs.org/primevue/-/primevue-4.5.5.tgz",
"integrity": "sha512-Kv5REIewCdP806QaoU+4nBXfmpzOGFKkZ9qH4KsL6MjiAQVc4PUzypt8erl4r3Vzh3nr3aWZIxkxYRRsLGiX2A==",
"license": "MIT",
"dependencies": {
"@primeuix/styled": "^0.7.4",
"@primeuix/styles": "^2.0.3",
"@primeuix/utils": "^0.6.2",
"@primevue/core": "4.5.5",
"@primevue/icons": "4.5.5"
},
"engines": {
"node": ">=12.11.0"
}
},
"node_modules/punycode.js": {
"version": "2.3.1",
"resolved": "https://registry.npmmirror.com/punycode.js/-/punycode.js-2.3.1.tgz",
@@ -3010,6 +3086,12 @@
"vue": "^3.0.0-0 || ^2.7.0"
}
},
"node_modules/vue-component-type-helpers": {
"version": "3.3.2",
"resolved": "https://registry.npmmirror.com/vue-component-type-helpers/-/vue-component-type-helpers-3.3.2.tgz",
"integrity": "sha512-l4Z2Y34m7nFMlx8vrslJaVtXxUpzgDMSESC7TakG/c5kwjYT/do+E0NcT2/vWDzaoIhsShg/2OKwX7Q4nbzC0g==",
"license": "MIT"
},
"node_modules/vue-router": {
"version": "4.5.1",
"resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.5.1.tgz",

View File

@@ -11,14 +11,14 @@
},
"dependencies": {
"@antv/g6": "^5.1.1",
"@primevue/themes": "^4.5.4",
"@element-plus/icons-vue": "^2.3.2",
"@vitejs/plugin-vue": "^5.2.4",
"@vueuse/motion": "^3.0.3",
"chart.js": "^4.5.1",
"element-plus": "^2.14.0",
"markdown-it": "^14.1.1",
"pg": "^8.13.1",
"primeicons": "^7.0.0",
"primevue": "^4.5.5",
"vite": "^5.4.19",
"vue": "^3.5.13",
"vue-chartjs": "^5.3.3",

View File

@@ -1,8 +1,8 @@
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="headerGradient" x1="12" y1="10" x2="54" y2="56" gradientUnits="userSpaceOnUse">
<stop stop-color="#34D399"/>
<stop offset="1" stop-color="#059669"/>
<stop stop-color="#6FA6C8"/>
<stop offset="1" stop-color="#3A7CA5"/>
</linearGradient>
</defs>
<circle cx="32" cy="32" r="32" fill="url(#headerGradient)"/>

Before

Width:  |  Height:  |  Size: 742 B

After

Width:  |  Height:  |  Size: 742 B

View File

@@ -15,9 +15,9 @@
}
.app {
--sidebar-expanded-width: 220px;
--sidebar-expanded-width: 184px;
--sidebar-collapsed-width: 64px;
--sidebar-motion: 320ms cubic-bezier(0.22, 1, 0.36, 1);
--sidebar-motion: 220ms cubic-bezier(0.4, 0, 0.2, 1);
height: var(--desktop-stage-height, 100dvh);
min-height: var(--desktop-stage-height, 100dvh);
@@ -27,18 +27,23 @@
}
.app-sidebar {
flex: 0 0 auto;
flex: 0 0 var(--sidebar-expanded-width);
width: var(--sidebar-expanded-width);
min-width: 0;
overflow: hidden;
will-change: width;
transition: width var(--sidebar-motion);
position: relative;
overflow: visible;
z-index: 200;
will-change: width, flex-basis;
transition:
width var(--sidebar-motion),
flex-basis var(--sidebar-motion),
box-shadow 160ms var(--ease);
}
.app.sidebar-collapsed .app-sidebar {
flex-basis: var(--sidebar-collapsed-width);
width: var(--sidebar-collapsed-width);
overflow: visible;
position: relative;
z-index: 200;
}
@@ -58,7 +63,7 @@
place-items: center;
padding: 24px;
background:
radial-gradient(circle at top left, rgba(16, 185, 129, 0.16), transparent 24rem),
radial-gradient(circle at top left, rgba(var(--theme-primary-rgb, 58, 124, 165), 0.14), transparent 24rem),
radial-gradient(circle at bottom right, rgba(59, 130, 246, 0.14), transparent 28rem),
#f8fafc;
}
@@ -89,9 +94,9 @@
min-height: 28px;
align-items: center;
padding: 0 10px;
border-radius: 999px;
background: rgba(16, 185, 129, 0.12);
color: #059669;
border-radius: 4px;
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
font-size: 12px;
font-weight: 800;
letter-spacing: 0.12em;
@@ -173,18 +178,30 @@
}
@media (max-width: 760px) {
.app {
display: block;
display: flex;
width: 100vw;
overflow: hidden;
}
.app-sidebar {
width: 100%;
width: var(--sidebar-collapsed-width);
flex: 0 0 var(--sidebar-collapsed-width);
transition: none;
}
.app > .main {
flex: 1 1 auto;
width: calc(100vw - var(--sidebar-collapsed-width));
}
.workarea { padding: 18px 16px 28px; }
}
@media (prefers-reduced-motion: reduce) {
.app-sidebar {
transition: none;
transition:
width 120ms ease-out !important,
flex-basis 120ms ease-out !important;
transition-duration: 120ms, 120ms !important;
}
}

View File

@@ -41,7 +41,7 @@
.application-dialog-eyebrow {
display: block;
margin-bottom: 4px;
color: #059669;
color: var(--theme-primary-active);
font-size: 12px;
font-weight: 800;
}
@@ -106,8 +106,8 @@
}
.application-input-panel textarea:focus {
border-color: #10b981;
box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.12);
border-color: var(--theme-primary);
box-shadow: 0 0 0 3px var(--theme-focus-ring);
}
.application-example-row {
@@ -144,7 +144,7 @@
.primary-parse-btn,
.confirm-btn {
border: 0;
background: #059669;
background: var(--theme-gradient-primary);
color: #fff;
}
@@ -181,7 +181,7 @@
}
.ontology-panel-head strong {
color: #059669;
color: var(--theme-primary-active);
}
.ontology-empty-state {
@@ -215,8 +215,8 @@
.ontology-chip {
border-radius: 8px;
background: #eefbf5;
color: #047857;
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
padding: 6px 10px;
font-size: 12px;
font-weight: 800;
@@ -269,7 +269,7 @@
.application-policy-strip i {
margin-top: 2px;
color: #059669;
color: var(--theme-primary);
font-size: 20px;
}

View File

@@ -0,0 +1,705 @@
.workbench {
min-width: 0;
display: grid;
gap: 16px;
padding-bottom: 10px;
}
.assistant-hero {
position: relative;
overflow: hidden;
display: grid;
grid-template-columns: 228px minmax(0, 1fr);
gap: 18px;
padding: 20px 24px 20px 18px;
border: 1px solid rgba(var(--theme-primary-rgb, 58, 124, 165), 0.18);
background:
radial-gradient(circle at top left, rgba(var(--theme-primary-rgb, 58, 124, 165), 0.12), transparent 34%),
radial-gradient(circle at right 20%, rgba(59, 130, 246, 0.07), transparent 28%),
linear-gradient(135deg, #f7fbff 0%, #ffffff 48%, #f5fbff 100%);
}
.assistant-hero::before,
.assistant-hero::after {
content: "";
position: absolute;
border-radius: 999px;
background: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.06);
pointer-events: none;
}
.assistant-hero::before {
right: -48px;
bottom: -58px;
width: 220px;
height: 220px;
}
.assistant-hero::after {
right: 92px;
top: -44px;
width: 140px;
height: 140px;
}
.assistant-visual {
position: relative;
min-height: 196px;
display: flex;
align-items: flex-end;
justify-content: flex-start;
padding: 0 0 10px 8px;
}
.assistant-visual::before {
content: "";
position: absolute;
inset: auto auto -78px -58px;
width: 264px;
height: 228px;
border-radius: 50%;
background: radial-gradient(circle at 48% 38%, rgba(255, 255, 255, 0.92) 0%, rgba(224, 242, 254, 0.84) 58%, rgba(224, 242, 254, 0) 100%);
pointer-events: none;
}
.assistant-visual::after {
content: "";
position: absolute;
left: 52px;
bottom: 18px;
width: 132px;
height: 18px;
border-radius: 999px;
background: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.14);
filter: blur(12px);
pointer-events: none;
}
.assistant-glow {
position: absolute;
left: 24px;
bottom: 22px;
width: 176px;
height: 176px;
border-radius: 50%;
background: radial-gradient(circle, rgba(255, 255, 255, 0.98) 0%, rgba(224, 242, 254, 0.9) 58%, rgba(224, 242, 254, 0) 100%);
box-shadow: 0 24px 48px rgba(var(--theme-primary-rgb, 58, 124, 165), 0.12);
pointer-events: none;
}
.assistant-image {
position: relative;
z-index: 1;
width: 184px;
max-width: 100%;
height: auto;
object-fit: contain;
object-position: left bottom;
filter: drop-shadow(0 22px 28px rgba(15, 23, 42, 0.16));
}
.assistant-copy {
position: relative;
z-index: 1;
display: grid;
gap: 10px;
align-content: center;
}
.assistant-copy h3 {
color: #0f172a;
font-size: 26px;
line-height: 1.25;
font-weight: 800;
}
.assistant-copy p {
max-width: 760px;
color: #5b6b83;
font-size: 14px;
line-height: 1.6;
}
.assistant-input {
display: flex;
align-items: center;
min-height: 48px;
padding: 4px 14px;
border: 1px solid rgba(148, 163, 184, 0.28);
border-radius: 4px;
background: rgba(255, 255, 255, 0.92);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.7);
}
.assistant-file-input {
display: none;
}
.assistant-input textarea {
min-width: 0;
flex: 1;
height: 22px;
min-height: 22px;
max-height: 22px;
resize: none;
border: 0;
padding: 1px 0;
background: transparent;
color: #0f172a;
font-size: 15px;
line-height: 22px;
overflow: hidden;
}
.assistant-input textarea::placeholder {
color: #94a3b8;
}
.assistant-input textarea:focus {
outline: none;
}
.hero-action,
.secondary-action,
.ghost-action,
.row-action,
.link-action,
.row-link {
border: 0;
background: transparent;
}
.hero-action {
height: 40px;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 0 16px;
border-radius: 4px;
background: linear-gradient(135deg, var(--theme-primary), var(--theme-primary-active));
color: #fff;
font-size: 14px;
font-weight: 800;
white-space: nowrap;
box-shadow: 0 10px 22px rgba(var(--theme-primary-rgb, 58, 124, 165), 0.18);
}
.hero-action .mdi,
.secondary-action .mdi,
.ghost-action .mdi {
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 14px;
line-height: 1;
}
.hero-action span,
.secondary-action span,
.ghost-action span {
display: inline-flex;
align-items: center;
line-height: 1;
}
.assistant-tools {
display: flex;
align-items: center;
gap: 10px;
flex-wrap: wrap;
}
.assistant-file-strip {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.assistant-file-note,
.assistant-file-chip {
display: inline-flex;
align-items: center;
min-height: 30px;
padding: 0 12px;
border-radius: 999px;
font-size: 12px;
font-weight: 700;
}
.assistant-file-note {
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
}
.assistant-file-chip {
max-width: 220px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
border: 1px solid rgba(148, 163, 184, 0.24);
background: rgba(255, 255, 255, 0.9);
color: #475569;
}
.assistant-file-clear {
border: 0;
background: transparent;
color: #64748b;
font-size: 12px;
font-weight: 700;
cursor: pointer;
}
.ghost-action {
height: 40px;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 0 16px;
border: 1px solid rgba(var(--theme-primary-rgb, 58, 124, 165), 0.22);
border-radius: 4px;
background: linear-gradient(180deg, rgba(255, 255, 255, 0.96), rgba(248, 251, 255, 0.92));
color: var(--theme-primary-active);
font-size: 14px;
font-weight: 700;
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.9),
0 6px 14px rgba(var(--theme-primary-rgb, 58, 124, 165), 0.08);
}
.ghost-action .mdi {
color: var(--theme-primary);
}
.secondary-action {
height: 40px;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 0 16px;
border: 1px solid rgba(59, 130, 246, 0.18);
border-radius: 4px;
background: linear-gradient(180deg, rgba(244, 249, 255, 0.96), rgba(234, 244, 255, 0.9));
color: #1d4ed8;
font-size: 14px;
font-weight: 700;
white-space: nowrap;
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.92),
0 6px 14px rgba(37, 99, 235, 0.08);
}
.secondary-action .mdi {
color: #2563eb;
}
.hero-action:disabled,
.secondary-action:disabled,
.ghost-action:disabled {
cursor: not-allowed;
opacity: 0.68;
box-shadow: none;
}
.workbench-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 20px;
}
.list-panel,
.policy-panel {
padding: 20px 22px;
}
.section-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-bottom: 16px;
}
.section-head h3 {
color: #0f172a;
font-size: 17px;
font-weight: 700;
}
.title-with-badge {
display: inline-flex;
align-items: center;
gap: 8px;
min-width: 0;
}
.alert-badge {
min-width: 22px;
height: 22px;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0 7px;
border-radius: 999px;
background: #ef4444;
color: #fff;
font-size: 12px;
font-weight: 800;
line-height: 1;
box-shadow: 0 6px 14px rgba(239, 68, 68, 0.22);
}
.link-action {
display: inline-flex;
align-items: center;
gap: 4px;
color: var(--theme-primary-active);
font-size: 14px;
font-weight: 700;
}
.list-body {
display: grid;
}
.todo-row,
.progress-row {
display: grid;
grid-template-columns: 56px minmax(0, 1fr) auto;
gap: 14px;
align-items: center;
padding: 14px 0;
border-top: 1px solid #edf2f7;
}
.todo-row:first-child,
.progress-row:first-child {
padding-top: 4px;
border-top: 0;
}
.todo-copy {
min-width: 0;
}
.todo-copy strong {
display: block;
color: #0f172a;
font-size: 15px;
font-weight: 700;
line-height: 1.4;
}
.todo-copy p {
margin-top: 4px;
color: #6b7280;
font-size: 14px;
line-height: 1.5;
}
.todo-advice {
display: flex;
align-items: flex-start;
gap: 8px;
flex-wrap: wrap;
}
.todo-advice-label {
display: inline-flex;
align-items: center;
min-height: 22px;
padding: 0 8px;
border-radius: 999px;
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
font-size: 12px;
font-weight: 800;
white-space: nowrap;
}
.todo-advice-text {
color: #64748b;
}
.row-action {
height: 38px;
padding: 0 16px;
border: 1px solid rgba(var(--theme-primary-rgb, 58, 124, 165), 0.36);
border-radius: 4px;
color: var(--theme-primary-active);
font-size: 14px;
font-weight: 700;
white-space: nowrap;
}
.progress-row {
grid-template-columns: 56px minmax(0, 1fr) minmax(84px, auto) minmax(104px, auto);
gap: 14px 16px;
}
.progress-copy strong {
margin-bottom: 2px;
}
.progress-amount {
color: #0f172a;
font-size: 20px;
font-weight: 800;
line-height: 1;
text-align: right;
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
.progress-status {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 104px;
min-height: 34px;
padding: 6px 14px;
border-radius: 999px;
font-size: 13px;
font-weight: 800;
white-space: nowrap;
justify-self: end;
}
.progress-status.success,
.policy-status.success {
background: var(--success-soft);
color: var(--success);
}
.progress-status.info,
.policy-status.info {
background: #eff6ff;
color: #3b82f6;
}
.progress-status.mint {
background: var(--success-soft);
color: var(--success);
}
.policy-table {
border: 1px solid #e7edf5;
border-radius: 4px;
overflow: hidden;
}
.policy-row {
display: grid;
grid-template-columns: 2.2fr 2.4fr 1fr;
gap: 16px;
align-items: center;
min-height: 56px;
padding: 0 18px;
border-top: 1px solid #edf2f7;
}
.policy-head {
min-height: 44px;
background: #f8fbff;
color: #64748b;
font-size: 12px;
font-weight: 800;
border-top: 0;
}
.policy-row strong,
.policy-row span {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.policy-row strong {
color: #0f172a;
font-size: 14px;
font-weight: 700;
}
.policy-row span {
color: #64748b;
font-size: 14px;
}
.policy-title-cell,
.policy-summary-cell {
justify-self: stretch;
text-align: left;
}
.policy-date-cell {
justify-self: center;
text-align: center;
}
@media (max-width: 1320px) {
.assistant-copy h3 {
font-size: 24px;
}
.policy-row {
grid-template-columns: 1.8fr 1.8fr 1fr;
}
}
@media (max-width: 1440px) {
.workbench {
gap: 14px;
}
.assistant-hero {
gap: 16px;
padding: 18px 20px 18px 16px;
}
.assistant-copy h3 {
font-size: 24px;
}
.assistant-visual {
min-height: 184px;
}
.assistant-image {
width: 172px;
}
.workbench-grid {
gap: 16px;
}
.list-panel,
.policy-panel {
padding: 18px 20px;
}
.policy-row {
min-height: 52px;
padding: 0 16px;
}
}
@media (max-width: 1080px) {
.assistant-hero {
grid-template-columns: 1fr;
gap: 8px;
}
.assistant-visual {
min-height: 188px;
justify-content: center;
padding: 0 0 8px;
}
.assistant-visual::before,
.assistant-visual::after,
.assistant-glow {
left: 50%;
transform: translateX(-50%);
}
.assistant-visual::before {
inset: auto auto -82px 50%;
}
.assistant-image {
width: 176px;
}
.workbench-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 860px) {
.assistant-hero,
.list-panel,
.policy-panel {
padding: 18px;
}
.assistant-input {
flex-direction: column;
align-items: stretch;
padding: 14px;
}
.assistant-visual {
min-height: 160px;
}
.assistant-glow {
width: 148px;
height: 148px;
}
.assistant-image {
width: 150px;
}
.assistant-input textarea {
height: 40px;
min-height: 40px;
max-height: 40px;
line-height: 1.5;
}
.hero-action,
.secondary-action,
.ghost-action,
.row-action {
width: 100%;
justify-content: center;
}
.assistant-file-chip {
max-width: 100%;
}
.todo-row,
.progress-row {
grid-template-columns: 56px minmax(0, 1fr);
}
.progress-amount {
grid-column: 2;
text-align: left;
font-size: 18px;
}
.row-action,
.progress-status {
grid-column: 2;
justify-self: start;
}
.policy-table {
border: 0;
border-radius: 0;
}
.policy-head {
display: none;
}
.policy-row {
grid-template-columns: 1fr;
gap: 8px;
padding: 16px 0;
border-top: 1px solid #edf2f7;
}
.policy-row strong,
.policy-row span {
white-space: normal;
}
}

View File

@@ -174,7 +174,7 @@
.risk-sim-message-row.user .risk-sim-bubble {
grid-column: 1;
justify-self: end;
background: #0f766e;
background: var(--theme-primary);
color: #fff;
}
@@ -191,7 +191,7 @@
border: 1px solid #dbe5ef;
border-radius: 12px;
background: #fff;
color: #0f766e;
color: var(--theme-primary-active);
}
.risk-sim-message-row.user .risk-sim-avatar {
@@ -483,8 +483,8 @@
.risk-sim-recognized-fields em {
padding: 3px 7px;
border-radius: 999px;
background: #ecfdf5;
color: #047857;
background: var(--success-soft);
color: var(--success-hover);
font-size: 11px;
font-style: normal;
font-weight: 850;
@@ -574,9 +574,9 @@
}
.risk-sim-file-chip.recognized {
border-color: #bbf7d0;
background: #ecfdf5;
color: #047857;
border-color: var(--success-line);
background: var(--success-soft);
color: var(--success-hover);
}
.risk-sim-file-chip.failed {
@@ -611,8 +611,8 @@
}
.risk-sim-send-btn {
border-color: #0f766e;
background: #0f766e;
border-color: var(--theme-primary);
background: var(--theme-primary);
color: #fff;
}
@@ -634,8 +634,8 @@
}
.risk-sim-composer-shell:focus-within {
border-color: rgba(15, 118, 110, 0.58);
box-shadow: 0 0 0 3px rgba(15, 118, 110, 0.1);
border-color: rgba(var(--theme-primary-rgb), 0.58);
box-shadow: 0 0 0 3px var(--theme-focus-ring);
}
.risk-sim-composer-shell textarea {
@@ -699,8 +699,8 @@
}
.risk-sim-step.done i {
background: #ecfdf5;
color: #047857;
background: var(--success-soft);
color: var(--success-hover);
}
.risk-sim-step.running i {
@@ -823,8 +823,8 @@
}
.risk-sim-primary-btn {
border: 1px solid #0f766e;
background: #0f766e;
border: 1px solid var(--theme-primary);
background: var(--theme-primary);
color: #fff;
}

View File

@@ -0,0 +1,533 @@
.rail {
--rail-motion-duration: 220ms;
--rail-motion-ease: cubic-bezier(0.4, 0, 0.2, 1);
--rail-fade-duration: 110ms;
--rail-label-delay: 55ms;
position: sticky;
top: 0;
width: 100%;
height: var(--desktop-stage-height, 100dvh);
min-height: var(--desktop-stage-height, 100dvh);
min-width: 0;
display: flex;
flex-direction: column;
overflow: visible;
background: linear-gradient(180deg, rgba(255, 255, 255, 0.98), rgba(248, 250, 252, 0.96)), #fff;
border-right: 1px solid #dbe4ee;
box-shadow: 1px 0 0 rgba(15, 23, 42, 0.02);
z-index: 20;
contain: layout;
transform: translateZ(0);
transition:
background 240ms var(--ease),
box-shadow 240ms var(--ease);
}
.rail-brand {
position: relative;
min-height: 112px;
display: flex;
align-items: flex-start;
justify-content: flex-start;
gap: 12px;
padding: 54px 16px 8px;
overflow: visible;
transition:
min-height var(--rail-motion-duration) var(--rail-motion-ease),
padding var(--rail-motion-duration) var(--rail-motion-ease),
gap var(--rail-motion-duration) var(--rail-motion-ease);
will-change: min-height, padding, gap;
}
.brand-mark {
flex: 0 0 auto;
width: 32px;
height: 32px;
display: grid;
place-items: center;
color: var(--theme-primary-active);
border-radius: 4px;
overflow: hidden;
transform: translateY(-8px);
transition:
color 160ms var(--ease),
transform var(--rail-motion-duration) var(--rail-motion-ease);
will-change: transform;
}
.custom-logo {
width: 100%;
height: 100%;
object-fit: contain;
}
.brand-mark svg {
width: 32px;
height: 32px;
fill: currentColor;
}
.brand-name {
flex: 1 1 auto;
min-width: 0;
max-width: 124px;
color: #0f172a;
font-size: 16px;
font-weight: 800;
white-space: nowrap;
overflow: hidden;
transform: translateY(-8px);
transition:
max-width var(--rail-motion-duration) var(--rail-motion-ease),
opacity var(--rail-fade-duration) var(--rail-motion-ease) var(--rail-label-delay),
transform var(--rail-fade-duration) var(--rail-motion-ease) var(--rail-label-delay);
will-change: max-width, opacity, transform;
}
.rail-collapse-btn {
position: absolute;
right: -14px;
top: 55px;
z-index: 30;
width: 28px;
height: 28px;
display: inline-grid;
place-items: center;
cursor: pointer;
border: 1px solid #dbe4ee;
border-radius: 999px;
background: rgba(255, 255, 255, 0.96);
color: #64748b;
box-shadow:
0 8px 18px rgba(15, 23, 42, 0.12),
0 0 0 3px rgba(248, 250, 252, 0.92);
transition:
top var(--rail-motion-duration) var(--rail-motion-ease),
right var(--rail-motion-duration) var(--rail-motion-ease),
width var(--rail-motion-duration) var(--rail-motion-ease),
height var(--rail-motion-duration) var(--rail-motion-ease),
transform var(--rail-motion-duration) var(--rail-motion-ease),
background 180ms var(--ease),
border-color 180ms var(--ease),
color 180ms var(--ease),
box-shadow 180ms var(--ease);
}
.rail-collapse-btn:hover {
border-color: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.28);
background: #fff;
color: var(--theme-primary-active);
box-shadow:
0 10px 22px rgba(var(--theme-primary-rgb, 58, 124, 165), 0.16),
0 0 0 3px rgba(248, 250, 252, 0.96);
}
.rail-collapse-btn .mdi {
font-size: 17px;
line-height: 1;
}
.rail-nav {
display: flex;
flex-direction: column;
gap: 8px;
padding: 4px 8px 16px;
overflow-y: auto;
overflow-x: hidden;
flex: 1;
transition:
padding var(--rail-motion-duration) var(--rail-motion-ease),
gap var(--rail-motion-duration) var(--rail-motion-ease);
will-change: padding, gap;
}
.nav-btn {
width: 100%;
min-height: 48px;
position: relative;
display: flex;
align-items: center;
gap: 12px;
padding: 0;
border: 1px solid transparent;
border-radius: 4px;
background: transparent;
color: #64748b;
text-align: left;
overflow: hidden;
transition:
gap var(--rail-motion-duration) var(--rail-motion-ease),
background 180ms var(--ease),
border-color 180ms var(--ease),
color 180ms var(--ease);
will-change: gap;
}
.nav-btn:hover {
background: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.08);
color: var(--theme-primary-active);
}
.nav-btn.active {
background: var(--theme-primary-soft);
border-color: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.18);
color: var(--theme-primary-active);
}
.nav-icon {
flex: 0 0 48px;
width: 48px;
height: 36px;
display: grid;
place-items: center;
border-radius: 4px;
color: currentColor;
transition: color 180ms var(--ease);
}
.nav-btn :deep(svg) {
width: 19px;
height: 19px;
stroke: currentColor;
stroke-width: 2;
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
}
.nav-label {
flex: 1;
min-width: 0;
max-width: 128px;
color: currentColor;
font-size: 14px;
font-weight: 700;
white-space: nowrap;
overflow: hidden;
opacity: 1;
transition:
max-width var(--rail-motion-duration) var(--rail-motion-ease),
opacity var(--rail-fade-duration) var(--rail-motion-ease) var(--rail-label-delay),
transform var(--rail-fade-duration) var(--rail-motion-ease) var(--rail-label-delay);
will-change: max-width, opacity, transform;
}
.nav-badge {
flex: 0 0 auto;
min-width: 34px;
height: 22px;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0 8px;
border-radius: 999px;
background: #ff5b67;
color: #fff;
font-size: 12px;
font-weight: 800;
transition:
min-width var(--rail-motion-duration) var(--rail-motion-ease),
max-width var(--rail-motion-duration) var(--rail-motion-ease),
padding var(--rail-motion-duration) var(--rail-motion-ease),
opacity var(--rail-fade-duration) var(--rail-motion-ease) var(--rail-label-delay);
will-change: min-width, max-width, padding, opacity;
}
.nav-unread-dot {
flex: 0 0 auto;
width: 8px;
height: 8px;
border: 2px solid #fff;
border-radius: 999px;
background: #ef4444;
box-shadow: 0 6px 14px rgba(239, 68, 68, 0.26);
}
.rail-user {
position: relative;
min-width: 0;
min-height: 78px;
margin: 0;
padding: 16px 20px 18px;
border-top: 1px solid #edf2f7;
transition: padding var(--rail-motion-duration) var(--rail-motion-ease);
}
.user-summary {
position: relative;
min-width: 0;
min-height: 42px;
display: flex;
align-items: center;
gap: 10px;
padding: 4px;
color: #64748b;
border-radius: 4px;
cursor: pointer;
transition:
gap var(--rail-motion-duration) var(--rail-motion-ease),
padding var(--rail-motion-duration) var(--rail-motion-ease),
background 180ms var(--ease);
}
.rail-user:hover .user-summary {
background: rgba(255, 255, 255, 0.72);
}
.user-avatar {
flex: 0 0 36px;
width: 36px;
height: 36px;
display: grid;
place-items: center;
border: 2px solid #fff;
border-radius: 999px;
background: linear-gradient(135deg, var(--theme-primary), var(--theme-primary-active));
box-shadow: 0 6px 14px rgba(var(--theme-primary-rgb, 58, 124, 165), 0.18);
color: #fff;
font-size: 14px;
font-weight: 800;
transition:
flex-basis var(--rail-motion-duration) var(--rail-motion-ease),
width var(--rail-motion-duration) var(--rail-motion-ease),
height var(--rail-motion-duration) var(--rail-motion-ease);
}
.user-copy {
flex: 1;
min-width: 0;
max-width: 116px;
display: flex;
flex-direction: column;
gap: 2px;
opacity: 1;
transition:
max-width var(--rail-motion-duration) var(--rail-motion-ease),
opacity var(--rail-fade-duration) var(--rail-motion-ease) var(--rail-label-delay),
transform var(--rail-fade-duration) var(--rail-motion-ease) var(--rail-label-delay);
will-change: max-width, opacity, transform;
}
.user-copy strong {
color: #334155;
font-size: 14px;
font-weight: 750;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.user-copy span {
color: #64748b;
font-size: 12px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.user-summary .mdi {
flex: 0 0 18px;
font-size: 18px;
transition:
max-width var(--rail-motion-duration) var(--rail-motion-ease),
opacity var(--rail-fade-duration) var(--rail-motion-ease) var(--rail-label-delay);
will-change: max-width, opacity;
}
.user-menu {
position: absolute;
right: 20px;
bottom: calc(100% - 6px);
min-width: 132px;
padding: 8px;
border: 1px solid rgba(226, 232, 240, 0.96);
border-radius: 4px;
background: rgba(255, 255, 255, 0.98);
box-shadow: 0 16px 32px rgba(15, 23, 42, 0.1);
opacity: 0;
transform: translateY(8px);
pointer-events: none;
transition: all 180ms var(--ease);
z-index: 4;
}
.rail-user:hover .user-menu {
opacity: 1;
transform: translateY(0);
pointer-events: auto;
}
.user-menu-item {
width: 100%;
height: 38px;
display: flex;
align-items: center;
gap: 8px;
padding: 0 12px;
border: 0;
border-radius: 4px;
background: transparent;
color: #dc2626;
font-size: 13px;
font-weight: 700;
transition: all 180ms var(--ease);
}
/* ========================================= */
/* COLLAPSED STATE */
/* ========================================= */
.rail-collapsed .rail-brand {
min-height: 136px;
justify-content: flex-start;
gap: 0;
padding: 52px 16px 8px;
}
.rail-collapsed .brand-mark {
width: 32px;
height: 32px;
margin: 0;
transform: translateY(-6px);
}
.rail-collapsed .brand-mark svg {
width: 32px;
height: 32px;
}
.rail-collapsed .brand-name {
position: absolute;
width: 1px;
height: 1px;
max-width: 0;
margin: 0;
opacity: 0;
overflow: hidden;
clip: rect(0 0 0 0);
pointer-events: none;
transform: translateX(-6px);
transition-delay: 0ms;
}
.rail-collapsed .rail-collapse-btn {
top: 96px;
right: -14px;
width: 28px;
height: 28px;
transform: none;
}
.rail-collapsed .rail-nav {
gap: 8px;
padding: 4px 8px 16px;
}
.rail-collapsed .nav-btn {
justify-content: flex-start;
gap: 0;
transform: translateX(0);
}
.rail-collapsed .nav-icon {
width: 48px;
height: 36px;
flex: 0 0 48px;
transform: translateX(0);
}
.rail-collapsed .nav-label {
max-width: 0;
opacity: 0;
transform: translateX(-6px);
transition-delay: 0ms;
}
.rail-collapsed .nav-badge {
max-width: 0;
min-width: 0;
padding: 0;
opacity: 0;
overflow: hidden;
transition-delay: 0ms;
}
.rail-collapsed .nav-unread-dot {
position: absolute;
top: 10px;
right: 11px;
width: 9px;
height: 9px;
}
.rail-collapsed {
overflow: visible;
}
.rail-collapsed .rail-user {
position: relative;
z-index: 6;
padding: 14px 8px;
overflow: visible;
}
.rail-collapsed .user-summary {
justify-content: center;
padding: 4px;
gap: 0;
}
.rail-user-menu-floating {
position: fixed;
z-index: 12000;
min-width: 132px;
padding: 8px;
border: 1px solid rgba(226, 232, 240, 0.96);
border-radius: 4px;
background: rgba(255, 255, 255, 0.98);
box-shadow: 0 16px 32px rgba(15, 23, 42, 0.14);
transform: translateY(-50%);
animation: railUserMenuIn 180ms var(--rail-motion-ease) both;
}
.rail-user-menu-floating .user-menu-item {
width: 100%;
}
@keyframes railUserMenuIn {
from {
opacity: 0;
transform: translateY(-50%) translateX(-6px);
}
to {
opacity: 1;
transform: translateY(-50%) translateX(0);
}
}
.rail-collapsed .user-copy,
.rail-collapsed .user-summary .mdi {
max-width: 0;
opacity: 0;
overflow: hidden;
transform: translateX(-6px);
transition-delay: 0ms;
}
@media (max-width: 980px) {
.rail {
position: relative;
height: auto;
}
}
@media (prefers-reduced-motion: reduce) {
.rail *,
.rail *::before,
.rail *::after {
transition-duration: 120ms !important;
animation-duration: 120ms !important;
}
}

View File

@@ -0,0 +1,459 @@
.topbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 24px;
padding: 18px 24px 20px;
background: #fff;
}
.topbar.chat-mode {
padding-bottom: 16px;
border-bottom: 1px solid #eef2f7;
}
.title-group {
min-width: 0;
}
.eyebrow {
display: inline-block;
margin-bottom: 8px;
padding: 3px 10px;
border-radius: 6px;
background: linear-gradient(135deg, rgba(var(--theme-primary-rgb, 58, 124, 165), 0.12), rgba(59, 130, 246, 0.08));
color: var(--theme-primary-active);
font-size: 11px;
font-weight: 800;
letter-spacing: 1.2px;
text-transform: uppercase;
}
.topbar h1 {
color: #0f172a;
font-size: 26px;
font-weight: 800;
letter-spacing: 0;
line-height: 1.2;
}
.topbar p {
margin-top: 6px;
max-width: 720px;
color: #64748b;
font-size: 14px;
line-height: 1.5;
}
.top-actions {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 14px;
flex-wrap: wrap;
}
.range-combo {
position: relative;
display: inline-flex;
align-items: center;
gap: 8px;
}
.range-shell {
height: 42px;
display: inline-flex;
align-items: center;
padding: 3px;
border: 1px solid #d7e0ea;
border-radius: 4px;
background: #fff;
box-shadow: 0 1px 2px rgba(15, 23, 42, .04);
}
.range-meta {
height: 34px;
display: inline-flex;
align-items: center;
gap: 8px;
padding: 0 12px;
border-right: 1px solid #e2e8f0;
color: #334155;
font-size: 13px;
font-weight: 650;
white-space: nowrap;
}
.range-meta .mdi {
color: var(--theme-primary);
}
.range-tabs {
display: inline-flex;
align-items: center;
gap: 2px;
padding-left: 3px;
}
.range-tabs button {
height: 34px;
min-width: 54px;
padding: 0 14px;
border: 0;
border-radius: 4px;
background: transparent;
color: #64748b;
font-size: 13px;
font-weight: 700;
white-space: nowrap;
transition: background 160ms ease, color 160ms ease, box-shadow 160ms ease;
}
.range-tabs button:hover:not(.active) {
background: #f1f5f9;
color: #334155;
}
.range-tabs button.active {
background: var(--theme-primary);
color: #fff;
box-shadow: 0 6px 14px rgba(var(--theme-primary-rgb, 58, 124, 165), .18);
}
.custom-range-wrap {
position: relative;
}
.custom-range-btn {
height: 42px;
display: inline-flex;
align-items: center;
gap: 8px;
padding: 0 13px;
border: 1px solid #d7e0ea;
border-radius: 4px;
background: #fff;
color: #334155;
font-size: 13px;
font-weight: 750;
white-space: nowrap;
}
.custom-range-btn:hover,
.custom-range-btn.active {
border-color: rgba(var(--theme-primary-rgb, 58, 124, 165), .34);
background: #f5fbff;
color: var(--theme-primary-active);
}
.calendar-popover {
position: absolute;
top: calc(100% + 10px);
right: 0;
width: 336px;
z-index: 40;
display: grid;
gap: 14px;
padding: 16px;
border: 1px solid #d7e0ea;
border-radius: 4px;
background: #fff;
box-shadow: 0 18px 42px rgba(15, 23, 42, .16);
}
.calendar-popover header,
.calendar-popover footer {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.calendar-popover header strong {
color: #0f172a;
font-size: 15px;
}
.calendar-popover header button {
width: 30px;
height: 30px;
display: grid;
place-items: center;
border: 0;
border-radius: 4px;
background: transparent;
color: #64748b;
}
.date-fields {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
}
.date-fields label {
display: grid;
gap: 6px;
}
.date-fields span {
color: #64748b;
font-size: 12px;
font-weight: 700;
}
.date-fields input {
width: 100%;
height: 38px;
padding: 0 9px;
border: 1px solid #d7e0ea;
border-radius: 4px;
color: #0f172a;
font-size: 13px;
}
.ghost-btn,
.apply-btn {
height: 36px;
padding: 0 14px;
border-radius: 8px;
font-size: 13px;
font-weight: 750;
}
.ghost-btn {
border: 1px solid #d7e0ea;
background: #fff;
color: #334155;
}
.apply-btn {
border: 0;
background: var(--theme-primary);
color: #fff;
}
.apply-btn:disabled {
cursor: not-allowed;
background: #cbd5e1;
}
.kpi-chips {
display: flex;
gap: 10px;
}
.detail-alert-strip {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 10px;
flex-wrap: wrap;
}
.detail-alert-pill {
display: inline-flex;
align-items: center;
gap: 6px;
min-height: 32px;
padding: 0 12px;
border: 1px solid #fed7aa;
border-radius: 999px;
background: #fff7ed;
color: #ea580c;
font-size: 12px;
font-weight: 800;
white-space: nowrap;
}
.detail-alert-pill i {
font-size: 14px;
}
.detail-alert-pill.success {
border-color: var(--success-line);
background: var(--success-soft);
color: var(--success);
}
.detail-alert-pill.danger {
border-color: #fecaca;
background: #fff1f2;
color: #dc2626;
}
.kpi-chip {
display: grid;
grid-template-columns: auto auto;
grid-template-rows: auto auto;
gap: 2px 10px;
padding: 8px 16px;
border-radius: 4px;
background: linear-gradient(135deg, color-mix(in srgb, var(--chip-color) 8%, #fff), color-mix(in srgb, var(--chip-color) 3%, #f8fafc));
border: 1px solid color-mix(in srgb, var(--chip-color) 18%, #e2e8f0);
}
.chip-value {
grid-row: 1 / 3;
align-self: center;
color: #0f172a;
font-size: 22px;
font-weight: 850;
line-height: 1;
}
.chip-value small {
font-size: 13px;
font-weight: 700;
margin-left: 2px;
}
.chip-label {
color: #64748b;
font-size: 12px;
font-weight: 700;
}
.chip-delta {
color: #94a3b8;
font-size: 11px;
font-weight: 700;
}
.chip-delta.up { color: var(--success); }
.chip-delta.down { color: #f59e0b; }
.topbar-spacer {
flex: 1;
min-width: 0;
}
.create-top-btn {
height: 40px;
display: inline-flex;
align-items: center;
gap: 8px;
padding: 0 18px;
border: 0;
border-radius: 8px;
background: var(--theme-primary);
color: #fff;
font-size: 14px;
font-weight: 750;
box-shadow: 0 8px 18px rgba(var(--theme-primary-rgb, 58, 124, 165), 0.18);
white-space: nowrap;
}
.create-top-btn:hover {
background: var(--theme-primary-active);
}
@media (max-width: 1120px) {
.range-combo {
width: 100%;
justify-content: flex-end;
}
}
@media (max-width: 960px) {
.topbar {
flex-direction: column;
align-items: stretch;
}
.top-actions,
.search-wrap,
.search-wrap.wide,
.detail-alert-strip,
.month-chip,
.qa-filter,
.new-question-btn {
width: 100%;
}
.range-combo {
justify-content: stretch;
}
.range-shell {
flex: 1;
}
.range-meta {
flex: 1;
}
.custom-range-btn {
width: 100%;
justify-content: center;
}
.calendar-popover {
right: auto;
left: 0;
}
}
@media (max-width: 640px) {
.topbar {
gap: 14px;
padding: 16px 16px 14px;
}
.topbar h1 {
font-size: 24px;
}
.kpi-chips {
width: 100%;
overflow-x: auto;
padding-bottom: 2px;
scrollbar-width: thin;
}
.kpi-chip {
min-width: 118px;
padding: 8px 12px;
}
.chip-label,
.chip-delta {
white-space: nowrap;
}
.range-combo {
display: grid;
gap: 8px;
}
.range-shell {
height: auto;
display: grid;
gap: 4px;
}
.range-meta {
width: 100%;
border-right: 0;
border-bottom: 1px solid #e2e8f0;
justify-content: center;
}
.range-tabs {
width: 100%;
padding-left: 0;
}
.range-tabs button {
flex: 1;
padding: 0 8px;
}
.calendar-popover {
width: min(336px, calc(100vw - 32px));
}
.date-fields {
grid-template-columns: 1fr;
}
}

View File

@@ -0,0 +1,381 @@
.insight-panel-shell {
flex: none;
display: flex;
width: clamp(300px, 28vw, 420px);
min-width: 0;
min-height: 0;
max-width: 100%;
overflow: hidden;
transition: width 360ms cubic-bezier(0.22, 1, 0.36, 1);
}
.insight-panel-shell.collapsed {
width: 0;
}
.insight-panel {
flex: 1;
min-width: 0;
min-height: 0;
display: grid;
grid-template-rows: auto minmax(0, 1fr);
overflow: hidden;
border: 1px solid rgba(189, 201, 214, 0.74);
border-radius: 16px;
background: #ffffff;
box-shadow: 0 14px 32px rgba(148, 163, 184, 0.16);
}
.insight-head {
display: grid;
gap: 12px;
padding: 16px;
border-bottom: 1px solid #e2e8f0;
background: linear-gradient(180deg, #f8fbff, #ffffff);
}
.insight-head h3 {
margin: 6px 0 0;
color: #0f172a;
font-size: var(--wb-fs-insight-title, 17px);
font-weight: 850;
}
.insight-head p {
margin: 6px 0 0;
color: #64748b;
font-size: var(--wb-fs-insight-body, 12px);
line-height: 1.55;
}
.insight-body {
min-height: 0;
display: grid;
gap: 12px;
align-content: start;
padding: 14px;
overflow-y: auto;
}
.intent-pill,
.flow-status-chip,
.status-pill {
min-height: 26px;
display: inline-flex;
align-items: center;
padding: 0 10px;
border-radius: 999px;
background: #eff6ff;
color: #2563eb;
font-size: 11px;
font-weight: 800;
}
.confidence-card,
.insight-card,
.review-side-card,
.review-flow-panel {
display: grid;
gap: 10px;
padding: 12px;
border: 1px solid #e2e8f0;
border-radius: 12px;
background: #ffffff;
}
.confidence-card {
grid-template-columns: minmax(0, 1fr) auto;
align-items: center;
background: #f8fbff;
}
.confidence-card span,
.card-head p,
.note-block p,
.review-side-head p,
.review-side-risk-summary,
.flow-step-tool,
.flow-step-detail,
.flow-step-card time {
margin: 0;
color: #64748b;
font-size: var(--wb-fs-insight-body, 12px);
line-height: 1.55;
}
.confidence-card strong,
.card-head h4,
.note-block strong,
.review-side-head strong,
.flow-step-card strong,
.review-side-metric-copy strong,
.review-side-category-copy strong {
color: #0f172a;
font-weight: 850;
}
.card-head,
.review-side-head,
.flow-step-card header,
.review-document-switch-head,
.review-flow-summary {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 10px;
}
.card-head h4 {
margin: 0;
font-size: var(--wb-fs-insight-h4, 14px);
}
.note-block {
display: grid;
gap: 6px;
padding: 10px;
border-radius: 10px;
background: #f8fbff;
}
.capability-chip-row,
.citation-stack,
.knowledge-question-list,
.review-flow-list,
.review-side-risk-list,
.review-document-warning-list {
display: grid;
gap: 8px;
}
.risk-chip,
.review-document-meta-chip,
.review-side-confidence {
min-height: 24px;
display: inline-flex;
align-items: center;
padding: 0 8px;
border-radius: 999px;
background: #eff6ff;
color: #2563eb;
font-size: 11px;
font-weight: 800;
}
.citation-card,
.knowledge-question-btn,
.review-side-metric-card,
.review-side-category-card,
.review-side-risk-item,
.flow-step-card,
.review-document-preview-card {
border: 1px solid #e2e8f0;
border-radius: 10px;
background: #ffffff;
}
.knowledge-question-btn,
.review-side-metric-card,
.review-side-category-card,
.review-side-risk-item {
width: 100%;
display: grid;
gap: 8px;
padding: 10px;
color: #334155;
font: inherit;
text-align: left;
}
.knowledge-question-btn {
grid-template-columns: 30px minmax(0, 1fr) auto;
align-items: center;
}
.knowledge-question-index {
width: 30px;
height: 30px;
display: grid;
place-items: center;
border-radius: 8px;
background: #eff6ff;
color: #2563eb;
font-size: 11px;
font-weight: 850;
}
.review-side-grid,
.review-side-category-grid,
.review-document-edit-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
gap: 8px;
}
.review-side-metric-card {
grid-template-columns: 30px minmax(0, 1fr) auto;
align-items: start;
}
.review-side-metric-icon,
.review-side-risk-icon {
width: 30px;
height: 30px;
display: grid;
place-items: center;
border-radius: 8px;
background: #eff6ff;
color: var(--theme-primary, #3a7ca5);
}
.review-side-metric-copy,
.review-side-category-copy,
.review-side-risk-copy {
display: grid;
gap: 4px;
min-width: 0;
}
.review-side-metric-copy small,
.review-side-edit-hint,
.review-side-category-copy p {
margin: 0;
color: #64748b;
font-size: 11px;
line-height: 1.45;
}
.review-side-category-card.active {
border-color: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.55);
background: var(--theme-primary-soft, #eaf4fa);
}
.review-insight-tools,
.review-document-nav,
.review-document-meta-chip-row {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.review-insight-switch-icon-btn,
.flow-icon-btn,
.review-document-nav-btn,
.review-side-save-pill {
border: 1px solid #cbd5e1;
border-radius: 8px;
background: #ffffff;
color: #334155;
}
.review-insight-switch-icon-btn,
.flow-icon-btn,
.review-document-nav-btn {
width: 32px;
height: 32px;
display: grid;
place-items: center;
}
.review-insight-switch-icon-btn.active,
.review-side-save-pill {
border-color: var(--theme-primary, #3a7ca5);
background: var(--theme-primary, #3a7ca5);
color: #ffffff;
}
.review-flow-list {
position: relative;
}
.flow-step-item {
display: grid;
grid-template-columns: 28px minmax(0, 1fr);
gap: 8px;
}
.flow-step-rail span {
width: 26px;
height: 26px;
display: grid;
place-items: center;
border-radius: 999px;
background: #eff6ff;
color: #2563eb;
font-size: 11px;
font-weight: 850;
}
.flow-step-card {
padding: 10px;
}
.flow-empty-state,
.review-side-empty,
.review-document-preview-placeholder {
display: grid;
place-items: center;
gap: 8px;
padding: 18px;
border: 1px dashed #cbd5e1;
border-radius: 10px;
background: #f8fbff;
color: #64748b;
text-align: center;
}
.review-inline-input,
.review-document-edit-field input,
.review-document-edit-field textarea {
width: 100%;
min-height: 34px;
border: 1px solid #cbd5e1;
border-radius: 8px;
background: #ffffff;
color: #0f172a;
font: inherit;
}
.review-document-edit-field {
display: grid;
gap: 6px;
}
.review-document-edit-field span {
color: #64748b;
font-size: 11px;
font-weight: 800;
}
.review-document-scroll {
display: grid;
gap: 10px;
}
.review-document-preview-card {
min-height: 140px;
display: grid;
place-items: center;
overflow: hidden;
background: #f8fbff;
}
.review-document-preview-card img {
max-width: 100%;
max-height: 240px;
object-fit: contain;
}
.review-side-save-pill {
min-height: 34px;
padding: 0 12px;
font-size: 12px;
font-weight: 800;
}
@media (max-width: 1440px) {
.insight-panel-shell:not(.collapsed) {
width: 100%;
max-height: min(34dvh, 360px);
}
}

View File

@@ -0,0 +1,469 @@
.message-row {
display: grid;
grid-template-columns: 38px minmax(0, 1fr);
align-items: start;
gap: 12px;
}
.message-row.user {
grid-template-columns: minmax(0, 1fr) 38px;
}
.message-row.user .message-avatar {
order: 2;
}
.message-row.user .message-bubble {
order: 1;
justify-self: end;
background: linear-gradient(135deg, #eaf3ff, #f7fbff);
border-color: rgba(96, 165, 250, 0.3);
}
.message-avatar {
width: 38px;
height: 38px;
display: grid;
place-items: center;
border-radius: 999px;
overflow: hidden;
background: #eff6ff;
box-shadow: 0 8px 18px rgba(148, 163, 184, 0.22);
}
.message-avatar img {
width: 100%;
height: 100%;
display: block;
object-fit: cover;
}
.message-bubble {
max-width: min(100%, 760px);
padding: 12px 14px;
border: 1px solid #d8e4f0;
border-radius: 14px;
background: #ffffff;
color: #24324a;
font-size: var(--wb-fs-bubble, 13px);
line-height: 1.62;
box-shadow: 0 10px 22px rgba(148, 163, 184, 0.14);
}
.message-bubble-application-preview {
max-width: min(100%, 980px);
}
.message-bubble-review-risk-low,
.message-bubble-review-risk-medium,
.message-bubble-review-risk-high {
background: #ffffff;
}
.message-bubble-review-risk-low {
border-color: rgba(37, 99, 235, 0.56);
}
.message-bubble-review-risk-medium {
border-color: rgba(217, 119, 6, 0.58);
}
.message-bubble-review-risk-high {
border-color: rgba(220, 38, 38, 0.58);
}
.message-meta {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-bottom: 8px;
}
.message-meta strong {
color: #0f172a;
font-size: var(--wb-fs-bubble-meta, 12px);
font-weight: 800;
}
.message-meta time {
color: #64748b;
font-size: var(--wb-fs-bubble-time, 11px);
font-weight: 700;
}
.message-answer-content {
color: #24324a;
}
.message-answer-markdown :deep(p),
.message-answer-markdown :deep(li),
.message-answer-markdown :deep(td),
.message-answer-markdown :deep(th),
.message-answer-markdown :deep(blockquote) {
margin: 0;
color: inherit;
line-height: 1.62;
}
.message-answer-markdown :deep(p + p),
.message-answer-markdown :deep(p + ul),
.message-answer-markdown :deep(ul + p),
.message-answer-markdown :deep(ol + p) {
margin-top: 8px;
}
.message-answer-markdown :deep(strong) {
color: #0f172a;
font-weight: 850;
}
.welcome-quick-actions {
margin-top: 14px;
padding-top: 12px;
border-top: 1px solid #e2e8f0;
}
.welcome-quick-actions-title {
margin: 0 0 12px;
color: #64748b;
font-size: var(--wb-fs-chip, 12px);
font-weight: 750;
}
.welcome-quick-action-grid,
.message-suggested-actions,
.message-detail-chip-row,
.message-files {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.welcome-quick-action-btn,
.message-suggested-action-btn,
.review-footer-btn,
.expense-query-record-card,
.expense-query-risk-chip {
border: 1px solid #cbd5e1;
background: #ffffff;
color: #334155;
font: inherit;
}
.welcome-quick-action-btn,
.review-footer-btn {
min-height: 32px;
display: inline-flex;
align-items: center;
gap: 6px;
padding: 0 12px;
border-radius: 8px;
font-size: var(--wb-fs-chip, 12px);
font-weight: 750;
}
.welcome-quick-action-btn i {
color: var(--theme-primary, #3a7ca5);
font-size: 15px;
}
.welcome-quick-action-btn:hover:not(:disabled),
.message-suggested-action-btn:hover:not(:disabled),
.review-footer-btn:hover:not(:disabled) {
border-color: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.55);
background: var(--theme-primary-soft, #eaf4fa);
color: var(--theme-primary-active, #2f6d95);
}
.message-meta-row {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-top: 10px;
}
.message-meta-chip,
.message-risk-chip,
.file-chip {
min-height: 26px;
display: inline-flex;
align-items: center;
gap: 5px;
padding: 0 9px;
border-radius: 999px;
background: #eff6ff;
color: #2563eb;
font-size: 11px;
font-weight: 750;
}
.message-suggested-actions {
margin-top: 12px;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
}
.message-suggested-action-btn {
min-height: 54px;
display: grid;
grid-template-columns: 30px minmax(0, 1fr) auto;
align-items: center;
gap: 10px;
padding: 10px 12px;
border-radius: 10px;
text-align: left;
}
.message-suggested-action-icon {
width: 30px;
height: 30px;
display: grid;
place-items: center;
border-radius: 8px;
background: #eff6ff;
color: var(--theme-primary, #3a7ca5);
}
.message-suggested-action-copy {
display: grid;
gap: 3px;
min-width: 0;
}
.message-suggested-action-title {
color: #0f172a;
font-size: 12px;
font-weight: 800;
}
.message-suggested-action-btn small {
color: #64748b;
font-size: 11px;
line-height: 1.4;
}
.message-detail-block,
.application-preview-table,
.draft-preview {
margin-top: 12px;
padding: 12px;
border: 1px solid #e2e8f0;
border-radius: 12px;
background: #f8fbff;
}
.message-detail-block > strong,
.expense-query-block > strong {
display: block;
margin-bottom: 8px;
color: #0f172a;
font-size: 12px;
font-weight: 850;
}
.application-preview-table {
display: grid;
padding: 0;
overflow: hidden;
}
.application-preview-row {
display: grid;
grid-template-columns: minmax(96px, 0.42fr) minmax(0, 1fr);
min-height: 38px;
border-top: 1px solid #e2e8f0;
}
.application-preview-row:first-child {
border-top: 0;
}
.application-preview-row.head {
background: #eff6ff;
color: #334155;
font-size: 12px;
font-weight: 850;
}
.application-preview-row > span {
display: flex;
align-items: center;
gap: 8px;
min-width: 0;
padding: 8px 10px;
}
.application-preview-label {
color: #64748b;
border-right: 1px solid #e2e8f0;
}
.application-preview-value {
color: #0f172a;
font-weight: 750;
}
.application-preview-input {
width: 100%;
min-height: 32px;
border: 1px solid #cbd5e1;
border-radius: 8px;
background: #ffffff;
color: #0f172a;
}
.application-preview-edit-btn {
width: 28px;
height: 28px;
display: inline-grid;
place-items: center;
border: 1px solid #cbd5e1;
border-radius: 8px;
background: #ffffff;
color: #2563eb;
}
.expense-query-record-list,
.message-citation-list {
display: grid;
gap: 8px;
}
.expense-query-record-card {
width: 100%;
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
gap: 10px;
padding: 10px;
border-radius: 10px;
text-align: left;
}
.expense-query-record-main {
display: grid;
gap: 6px;
min-width: 0;
}
.expense-query-record-top,
.expense-query-record-meta,
.expense-query-summary-row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
}
.expense-query-record-top strong {
color: #0f172a;
font-size: 12px;
font-weight: 850;
}
.expense-query-record-card p,
.expense-query-record-meta span,
.expense-query-window-label,
.expense-query-hint {
margin: 0;
color: #64748b;
font-size: 11px;
line-height: 1.45;
}
.expense-query-record-status,
.expense-query-summary-chip {
min-height: 22px;
display: inline-flex;
align-items: center;
padding: 0 8px;
border-radius: 999px;
background: #eff6ff;
color: #2563eb;
font-size: 11px;
font-weight: 800;
}
.expense-query-empty {
display: flex;
align-items: center;
gap: 8px;
color: #64748b;
font-size: 12px;
}
.review-plain-followup,
.draft-preview {
display: grid;
gap: 8px;
}
.review-plain-lead {
margin: 0;
color: #0f172a;
font-size: 14px;
font-weight: 850;
}
.review-plain-summary,
.review-plain-note {
margin: 0;
color: #64748b;
font-size: 12px;
line-height: 1.55;
}
.review-plain-list {
display: grid;
gap: 6px;
margin: 0;
padding-left: 18px;
color: #334155;
font-size: 12px;
}
.review-footer-actions {
margin-top: 10px;
}
.review-footer-btn.primary {
border-color: var(--theme-primary, #3a7ca5);
background: var(--theme-primary, #3a7ca5);
color: #ffffff;
}
.draft-preview header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
}
.draft-preview pre {
margin: 0;
overflow: auto;
color: #334155;
font-size: 12px;
line-height: 1.5;
white-space: pre-wrap;
}
@media (max-width: 760px) {
.message-row,
.message-row.user {
grid-template-columns: 1fr;
}
.message-avatar {
display: none;
}
.message-row.user .message-bubble {
justify-self: stretch;
}
.message-bubble {
max-width: 100%;
border-radius: 12px;
}
}

View File

@@ -0,0 +1,131 @@
:root {
--theme-primary: #3a7ca5;
--theme-primary-hover: #2f6d95;
--theme-primary-active: #255b7d;
--theme-primary-soft: #eaf4fa;
--theme-primary-soft-strong: #d4e8f3;
--theme-primary-rgb: 58, 124, 165;
--theme-focus-ring: rgba(58, 124, 165, 0.12);
--el-color-primary: var(--theme-primary);
--el-color-primary-light-3: var(--theme-primary-hover);
--el-color-primary-light-5: var(--theme-primary-light-5);
--el-color-primary-light-7: var(--theme-primary-soft-strong);
--el-color-primary-light-8: var(--theme-primary-soft);
--el-color-primary-light-9: var(--theme-primary-light-9);
--el-color-primary-dark-2: var(--theme-primary-active);
--el-color-success: var(--success);
--el-color-success-light-9: var(--success-soft);
--el-color-warning: var(--warning);
--el-color-warning-light-9: var(--warning-soft);
--el-color-danger: var(--danger);
--el-color-danger-light-9: var(--danger-soft);
--el-color-info: var(--info);
--el-border-radius-base: 4px;
--el-border-radius-small: 3px;
--el-border-radius-round: 4px;
--el-border-color: var(--line-strong);
--el-border-color-light: var(--line);
--el-border-color-lighter: #edf2f7;
--el-fill-color-light: var(--bg);
--el-fill-color-lighter: var(--surface-soft);
--el-fill-color-blank: var(--surface);
--el-text-color-primary: var(--ink);
--el-text-color-regular: var(--text);
--el-text-color-secondary: var(--muted);
--el-font-family: Inter, "SF Pro Text", "Segoe UI", "Microsoft YaHei", "PingFang SC", sans-serif;
--el-font-size-base: 14px;
--el-box-shadow-light: 0 8px 22px rgba(15, 23, 42, 0.08);
--el-box-shadow-lighter: 0 4px 14px rgba(15, 23, 42, 0.06);
--el-transition-duration: 180ms;
--el-transition-function-ease-in-out-bezier: cubic-bezier(0.2, 0, 0, 1);
}
.el-button {
font-weight: 600;
letter-spacing: 0;
}
.el-button,
.el-input__wrapper,
.el-select__wrapper,
.el-textarea__inner,
.el-date-editor.el-input__wrapper,
.el-popover.el-popper,
.el-popper.is-light,
.el-message-box,
.el-dialog {
border-radius: 4px;
}
.el-input__wrapper,
.el-select__wrapper {
min-height: 34px;
box-shadow: 0 0 0 1px var(--el-border-color) inset;
transition:
box-shadow 160ms var(--ease),
background-color 160ms var(--ease),
border-color 160ms var(--ease);
}
.el-input__wrapper:hover,
.el-select__wrapper:hover {
box-shadow: 0 0 0 1px #b8c2d2 inset;
}
.el-input__wrapper.is-focus,
.el-select__wrapper.is-focused {
box-shadow:
0 0 0 1px var(--el-color-primary) inset,
0 0 0 3px var(--theme-focus-ring);
}
.el-select-dropdown,
.el-picker__popper,
.el-dropdown__popper,
.el-popover.el-popper {
border: 1px solid rgba(148, 163, 184, 0.26);
box-shadow: 0 16px 36px rgba(15, 23, 42, 0.12);
}
.el-select-dropdown__item {
height: 34px;
color: #334155;
font-size: 13px;
line-height: 34px;
}
.el-select-dropdown__item.is-hovering {
background: #f1f5f9;
}
.el-select-dropdown__item.is-selected {
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
font-weight: 700;
}
.el-scrollbar__bar {
opacity: 0.28;
}
.el-overlay {
background-color: rgba(15, 23, 42, 0.42);
}
.el-dialog,
.el-message-box {
box-shadow: 0 24px 70px rgba(15, 23, 42, 0.18);
}
.el-table {
--el-table-header-bg-color: #f8fafc;
--el-table-header-text-color: #334155;
--el-table-row-hover-bg-color: #f8fafc;
color: #334155;
}
.el-pagination {
--el-pagination-button-bg-color: #ffffff;
--el-pagination-hover-color: var(--theme-primary-active);
font-weight: 600;
}

View File

@@ -7,21 +7,64 @@
--muted: #64748b;
--line: #e2e8f0;
--line-strong: #cbd5e1;
--primary: #10b981;
--primary-soft: #ecfdf5;
--secondary: #3b82f6;
--primary: #3a7ca5;
--primary-hover: #2f6d95;
--primary-active: #255b7d;
--primary-soft: #eaf4fa;
--primary-soft-strong: #d4e8f3;
--primary-rgb: 58, 124, 165;
--secondary: #4f6f9f;
--secondary-rgb: 79, 111, 159;
--theme-primary: var(--primary);
--theme-primary-hover: var(--primary-hover);
--theme-primary-active: var(--primary-active);
--theme-primary-soft: var(--primary-soft);
--theme-primary-soft-strong: var(--primary-soft-strong);
--theme-primary-light-5: color-mix(in srgb, var(--theme-primary) 52%, white);
--theme-primary-light-9: color-mix(in srgb, var(--theme-primary) 8%, white);
--theme-primary-contrast: #ffffff;
--theme-primary-shadow: rgba(var(--theme-primary-rgb), 0.16);
--theme-gradient-primary: linear-gradient(135deg, var(--theme-primary), var(--theme-primary-active));
--theme-primary-rgb: var(--primary-rgb);
--theme-secondary: var(--secondary);
--theme-secondary-rgb: var(--secondary-rgb);
--theme-focus-ring: rgba(var(--theme-primary-rgb), 0.12);
--purple: #8b5cf6;
--orange: #f59e0b;
--red: #ef4444;
--success: #10b981;
--success-soft: #ecfdf5;
--warning: #f59e0b;
--warning-soft: #fffbeb;
--success: #2f855a;
--success-hover: #276749;
--success-active: #22543d;
--success-soft: #f0f7f2;
--success-line: #cde6d5;
--success-rgb: 47, 133, 90;
--warning: #b7791f;
--warning-hover: #975a16;
--warning-active: #7b4514;
--warning-soft: #fff8eb;
--warning-line: #efd9af;
--warning-rgb: 183, 121, 31;
--danger: #ef4444;
--danger-hover: #dc2626;
--danger-active: #b91c1c;
--danger-soft: #fef2f2;
--danger-line: #fecaca;
--danger-rgb: 239, 68, 68;
--info: #475569;
--info-hover: #334155;
--info-active: #1e293b;
--info-soft: #f1f5f9;
--info-line: #cbd5e1;
--info-rgb: 71, 85, 105;
--chart-primary: var(--theme-primary);
--chart-primary-rgb: var(--theme-primary-rgb);
--chart-blue: #4f6f9f;
--chart-purple: #6e7fa6;
--chart-amber: #b58b4c;
--chart-danger: var(--danger);
--nav: #0b1220;
--nav-muted: #7d89a5;
--radius: 8px;
--radius: 4px;
--ease: cubic-bezier(.2, .8, .2, 1);
--desktop-ui-scale: 1;
--desktop-ui-inverse-scale: 1;
@@ -38,7 +81,7 @@ body { margin: 0; min-height: 100dvh; color: var(--text); background: var(--bg);
.mdi { line-height: 1; vertical-align: middle; }
button, input, select, textarea { font: inherit; }
button { cursor: pointer; }
button:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible { outline: 3px solid rgba(16,185,129,.20); outline-offset: 2px; }
button:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible { outline: 3px solid var(--theme-focus-ring, rgba(58,124,165,.18)); outline-offset: 2px; }
.eyebrow { color: var(--primary); font-size: 12px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; }
h1, h2, h3, p { margin: 0; }
@@ -60,7 +103,7 @@ h1 { margin-top: 4px; color: var(--ink); font-size: 24px; line-height: 1.25; fon
}
.btn:hover { transform: translateY(-1px); box-shadow: 0 10px 24px rgba(16,24,40,.08); }
.btn:active, .mini-btn:active, .chip:active, .nav-btn:active { transform: scale(.97); }
.btn.primary { border-color: transparent; background: var(--primary); color: #fff; box-shadow: 0 12px 24px rgba(51,92,255,.22); }
.btn.primary { border-color: transparent; background: var(--theme-primary); color: #fff; box-shadow: 0 10px 20px rgba(var(--theme-primary-rgb, 58, 124, 165), .18); }
.btn.success { border-color: transparent; background: var(--success); color: #fff; }
.btn.danger { border-color: rgba(180,35,24,.18); background: var(--danger-soft); color: var(--danger); }
.btn.ghost { background: transparent; }
@@ -110,13 +153,13 @@ h1 { margin-top: 4px; color: var(--ink); font-size: 24px; line-height: 1.25; fon
display: inline-grid;
place-items: center;
border: 3px solid #e2e8f0;
border-top-color: #10b981;
border-top-color: var(--primary);
border-radius: 50%;
animation: table-spinner-rotate .8s linear infinite !important;
}
.table-loading.sky .table-loading__spinner {
border-top-color: #0ea5e9;
border-top-color: var(--primary);
}
.table-loading.detail .table-loading__spinner {

View File

@@ -16,8 +16,8 @@
}
.opinion-wrap textarea:focus {
border-color: #10b981;
box-shadow: 0 0 0 3px rgba(16, 185, 129, .12);
border-color: var(--theme-primary);
box-shadow: 0 0 0 3px var(--theme-focus-ring);
outline: none;
}
@@ -55,8 +55,8 @@
.risk-total span { font-weight: 750; }
.risk-total.high {
background: #fee2e2;
color: #dc2626;
background: var(--danger-soft);
color: var(--danger-hover);
}
.risk-total.high strong { font-size: 16px; font-weight: 900; }
@@ -87,8 +87,8 @@
flex-shrink: 0;
}
.risk-row.high .risk-icon { background: #fef2f2; color: #ef4444; }
.risk-row.medium .risk-icon { background: #fff7ed; color: #f97316; }
.risk-row.high .risk-icon { background: var(--danger-soft); color: var(--danger); }
.risk-row.medium .risk-icon { background: var(--warning-soft); color: var(--warning-active); }
.risk-text {
flex: 1;
@@ -105,8 +105,8 @@
flex-shrink: 0;
}
.risk-level.high { background: #fef2f2; color: #ef4444; }
.risk-level.medium { background: #fff7ed; color: #f97316; }
.risk-level.high { background: var(--danger-soft); color: var(--danger); }
.risk-level.medium { background: var(--warning-soft); color: var(--warning-active); }
/* ── Side Dual ── */
.side-dual {
@@ -134,7 +134,7 @@
.reminder-list li i {
margin-top: 2px;
color: #f59e0b;
color: var(--warning);
font-size: 14px;
flex-shrink: 0;
}
@@ -195,35 +195,35 @@
.action-btn.supplement {
background: #fff;
border-color: #fed7aa;
color: #ea580c;
border-color: var(--warning-line);
color: var(--warning-active);
}
.action-btn.supplement:hover {
background: #fff7ed;
box-shadow: 0 4px 12px rgba(234, 88, 12, .12);
background: var(--warning-soft);
box-shadow: 0 4px 12px rgba(var(--warning-rgb), .12);
}
.action-btn.reject {
background: #fff;
border-color: #fecaca;
color: #ef4444;
border-color: var(--danger-line);
color: var(--danger);
}
.action-btn.reject:hover {
background: #fef2f2;
box-shadow: 0 4px 12px rgba(239, 68, 68, .12);
background: var(--danger-soft);
box-shadow: 0 4px 12px rgba(var(--danger-rgb), .12);
}
.action-btn.approve {
background: #059669;
background: var(--theme-primary);
color: #fff;
box-shadow: 0 4px 16px rgba(5, 150, 105, .25);
box-shadow: 0 4px 16px var(--theme-primary-shadow);
}
.action-btn.approve:hover {
background: #047857;
box-shadow: 0 8px 24px rgba(5, 150, 105, .30);
background: var(--theme-primary-active);
box-shadow: 0 8px 24px rgba(var(--theme-primary-rgb), .30);
}
.action-btn:active { transform: scale(.97); }

View File

@@ -34,15 +34,15 @@
font-weight: 750;
}
.status-tabs button.active { color: #059669; }
.status-tabs button.active { color: var(--theme-primary-active); }
.status-tabs button.active::after {
content: "";
position: absolute;
left: 0; right: 0; bottom: -1px;
height: 3px;
border-radius: 999px 999px 0 0;
background: #10b981;
border-radius: 2px 2px 0 0;
background: var(--theme-primary);
}
.list-toolbar {
@@ -86,8 +86,8 @@
}
.list-search input:focus {
border-color: #10b981;
box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.14);
border-color: var(--theme-primary);
box-shadow: 0 0 0 3px var(--theme-focus-ring);
outline: none;
}
@@ -108,7 +108,7 @@
}
.filter-btn { min-width: 104px; justify-content: space-between; }
.filter-btn:hover, .page-size:hover { border-color: rgba(16, 185, 129, .32); color: #0f9f78; }
.filter-btn:hover, .page-size:hover { border-color: rgba(var(--theme-primary-rgb), .32); color: var(--theme-primary-active); }
.hint { display: inline-flex; align-items: center; gap: 7px; margin-top: 10px; color: #64748b; font-size: 13px; }
@@ -119,7 +119,7 @@
overflow-y: auto;
border: 1px solid #edf2f7;
border-radius: 10px;
background: linear-gradient(180deg, #fcfefd 0%, #f4f8f6 100%);
background: linear-gradient(180deg, #fcfeff 0%, #f4f8fc 100%);
display: flex;
flex-direction: column;
align-items: stretch;
@@ -145,13 +145,13 @@
padding: 28px 20px;
text-align: center;
color: #64748b;
background: linear-gradient(180deg, #fcfffd 0%, #f5f9f7 100%);
background: linear-gradient(180deg, #fcfeff 0%, #f5f9fc 100%);
align-self: center;
}
.table-state .mdi {
font-size: 28px;
color: #10b981;
color: var(--theme-primary);
}
.table-state strong {
@@ -167,7 +167,7 @@
}
.table-state.error .mdi {
color: #ef4444;
color: var(--danger);
}
.state-action {
@@ -176,10 +176,10 @@
align-items: center;
justify-content: center;
padding: 0 14px;
border: 1px solid rgba(16, 185, 129, 0.28);
border: 1px solid rgba(var(--theme-primary-rgb), 0.28);
border-radius: 8px;
background: #fff;
color: #059669;
color: var(--theme-primary-active);
font-size: 13px;
font-weight: 760;
}
@@ -207,11 +207,11 @@ th {
tbody tr { cursor: pointer; }
tbody tr:hover, tbody tr.spotlight {
background: linear-gradient(90deg, rgba(16, 185, 129, .08), rgba(16, 185, 129, .03));
background: linear-gradient(90deg, rgba(var(--theme-primary-rgb), .08), rgba(var(--theme-primary-rgb), .03));
}
tbody tr:last-child td { border-bottom: 0; }
.doc-id { color: #1d4ed8; font-weight: 800; }
.doc-id { color: var(--theme-primary-active); font-weight: 800; }
.person { display: inline-flex; align-items: center; gap: 8px; white-space: nowrap; }
@@ -219,7 +219,7 @@ tbody tr:last-child td { border-bottom: 0; }
width: 24px; height: 24px;
display: grid; place-items: center;
border-radius: 999px;
background: #dbeafe; color: #1d4ed8;
background: var(--theme-primary-soft); color: var(--theme-primary-active);
font-size: 12px; font-weight: 900;
}
@@ -231,26 +231,26 @@ tbody tr:last-child td { border-bottom: 0; }
font-size: 12px; font-weight: 750; white-space: nowrap;
}
.risk-tag.low { background: #dcfce7; color: #059669; }
.risk-tag.medium { background: #ffedd5; color: #f97316; }
.risk-tag.high { background: #fee2e2; color: #ef4444; }
.risk-tag.low { background: var(--success-soft); color: var(--success); }
.risk-tag.medium { background: var(--warning-soft); color: var(--warning-active); }
.risk-tag.high { background: var(--danger-soft); color: var(--danger); }
.sla { font-size: 13px; font-weight: 850; }
.sla.safe { color: #059669; }
.sla.warning { color: #f97316; }
.sla.danger { color: #ef4444; }
.sla.safe { color: var(--success); }
.sla.warning { color: var(--warning-active); }
.sla.danger { color: var(--danger); }
.status-tag.pending { background: #eff6ff; color: #2563eb; }
.status-tag.urgent { background: #fff7ed; color: #f97316; }
.status-tag.done { background: #ecfdf5; color: #059669; }
.status-tag.pending { background: var(--theme-primary-soft); color: var(--theme-primary-active); }
.status-tag.urgent { background: var(--warning-soft); color: var(--warning-active); }
.status-tag.done { background: var(--success-soft); color: var(--success); }
.more-btn {
width: auto; height: auto;
display: inline-flex; align-items: center; justify-content: center;
border: 0; background: transparent;
color: #2563eb; font-size: 13px; font-weight: 800;
color: var(--theme-primary-active); font-size: 13px; font-weight: 800;
}
.more-btn:hover { color: #059669; }
.more-btn:hover { color: var(--theme-primary-active); }
.list-foot {
display: grid;
@@ -272,8 +272,8 @@ tbody tr:last-child td { border-bottom: 0; }
font-size: 14px; font-weight: 800;
transition: background 160ms ease, color 160ms ease, box-shadow 160ms ease;
}
.pager button:hover:not(.active) { background: #fff; color: #059669; box-shadow: 0 1px 4px rgba(15, 23, 42, .08); }
.pager button.active { background: #059669; color: #fff; box-shadow: 0 8px 16px rgba(5, 150, 105, .20); }
.pager button:hover:not(.active) { background: #fff; color: var(--theme-primary-active); box-shadow: 0 1px 4px rgba(15, 23, 42, .08); }
.pager button.active { background: var(--theme-primary); color: #fff; box-shadow: 0 8px 16px var(--theme-primary-shadow); }
.pager span { color: #64748b; font-weight: 800; }
.page-size {
@@ -318,8 +318,8 @@ tbody tr:last-child td { border-bottom: 0; }
display: grid;
place-items: center;
border-radius: 999px;
background: linear-gradient(135deg, #dbeafe, #ecfdf5);
color: #0f766e;
background: linear-gradient(135deg, var(--theme-primary-soft-strong), var(--theme-primary-soft));
color: var(--theme-primary-active);
font-size: 26px;
font-weight: 900;
}
@@ -334,8 +334,8 @@ tbody tr:last-child td { border-bottom: 0; }
margin-left: 8px;
padding: 3px 9px;
border-radius: 6px;
background: #dcfce7;
color: #059669;
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
font-size: 12px;
}
@@ -377,16 +377,16 @@ tbody tr:last-child td { border-bottom: 0; }
font-weight: 850;
}
.risk-pill.high { background: #fee2e2; color: #ef4444; }
.risk-pill.medium { background: #ffedd5; color: #f97316; }
.risk-pill.low { background: #dcfce7; color: #059669; }
.state-pill { background: #dbeafe; color: #2563eb; }
.risk-pill.high { background: var(--danger-soft); color: var(--danger); }
.risk-pill.medium { background: var(--warning-soft); color: var(--warning-active); }
.risk-pill.low { background: var(--success-soft); color: var(--success); }
.state-pill { background: var(--theme-primary-soft); color: var(--theme-primary-active); }
.countdown {
display: inline-flex;
align-items: center;
gap: 6px;
color: #f97316 !important;
color: var(--warning-active) !important;
}
.hero-summary-panel {
@@ -417,7 +417,7 @@ tbody tr:last-child td { border-bottom: 0; }
}
.hero-summary-icon {
color: #059669;
color: var(--theme-primary);
font-size: 14px;
}
@@ -456,8 +456,8 @@ tbody tr:last-child td { border-bottom: 0; }
width: 8px;
height: 8px;
border-radius: 999px;
background: #10b981;
box-shadow: 0 0 0 4px rgba(16, 185, 129, .12);
background: var(--theme-primary);
box-shadow: 0 0 0 4px rgba(var(--theme-primary-rgb), .12);
}
.progress-line {
@@ -485,7 +485,7 @@ tbody tr:last-child td { border-bottom: 0; }
background: #dbe4ee;
}
.progress-step.active::before { background: #10b981; }
.progress-step.active::before { background: var(--theme-primary); }
.progress-step:first-child::before { left: 50%; }
.progress-step:last-child::before { right: 50%; }
@@ -507,20 +507,20 @@ tbody tr:last-child td { border-bottom: 0; }
position: absolute;
inset: -4px;
z-index: -1;
border: 2px solid rgba(16, 185, 129, .42);
border: 2px solid rgba(var(--theme-primary-rgb), .42);
border-radius: 999px;
pointer-events: none;
}
.progress-step.active span {
background: #059669;
background: var(--theme-primary-active);
color: #fff;
}
.progress-step.current span {
background: #10b981 !important;
background: var(--theme-primary) !important;
color: #fff !important;
box-shadow: 0 0 0 4px rgba(16, 185, 129, .15) !important;
box-shadow: 0 0 0 4px rgba(var(--theme-primary-rgb), .15) !important;
animation: breathe-dot 3s ease-in-out infinite !important;
transform-origin: center !important;
}
@@ -528,11 +528,11 @@ tbody tr:last-child td { border-bottom: 0; }
@keyframes breathe-dot {
0%, 100% {
transform: scale(1);
box-shadow: 0 4px 12px rgba(16, 185, 129, .3), 0 0 0 4px rgba(16, 185, 129, .15);
box-shadow: 0 4px 12px rgba(var(--theme-primary-rgb), .3), 0 0 0 4px rgba(var(--theme-primary-rgb), .15);
}
50% {
transform: scale(1.12);
box-shadow: 0 4px 20px rgba(16, 185, 129, .5), 0 0 0 10px rgba(16, 185, 129, .08);
box-shadow: 0 4px 20px rgba(var(--theme-primary-rgb), .5), 0 0 0 10px rgba(var(--theme-primary-rgb), .08);
}
}
@@ -541,11 +541,11 @@ tbody tr:last-child td { border-bottom: 0; }
font-size: 12px;
}
.progress-step.current strong { color: #059669; }
.progress-step.current strong { color: var(--theme-primary-active); }
.progress-step small { font-size: 11px; }
.progress-step.current small {
color: #059669;
color: var(--theme-primary-active);
}
.detail-grid {
@@ -596,9 +596,9 @@ tbody tr:last-child td { border-bottom: 0; }
min-width: 102px;
min-height: 34px;
padding: 0 12px;
border-radius: 999px;
background: #ecfdf5;
color: #047857;
border-radius: 4px;
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
font-size: 14px;
font-weight: 900;
}
@@ -689,19 +689,19 @@ tbody tr:last-child td { border-bottom: 0; }
width: max-content;
margin-top: 7px;
padding: 2px 8px;
border-radius: 999px;
border-radius: 4px;
font-size: 11px;
font-weight: 800;
}
.over-tag.ok {
background: #ecfdf5;
color: #059669;
background: var(--success-soft);
color: var(--success);
}
.over-tag.bad {
background: #fef2f2;
color: #ef4444;
background: var(--danger-soft);
color: var(--danger);
}
.expense-attachment-main {
@@ -717,22 +717,22 @@ tbody tr:last-child td { border-bottom: 0; }
align-items: center;
min-height: 24px;
padding: 0 8px;
border-radius: 999px;
border-radius: 4px;
font-size: 11px;
font-weight: 800;
white-space: nowrap;
}
.attachment-pill.ok { background: #ecfdf5; color: #059669; }
.attachment-pill.partial { background: #fff7ed; color: #ea580c; }
.attachment-pill.missing { background: #fef2f2; color: #ef4444; }
.attachment-pill.neutral { background: #eef2ff; color: #4f46e5; }
.attachment-pill.ok { background: var(--success-soft); color: var(--success); }
.attachment-pill.partial { background: var(--warning-soft); color: var(--warning-active); }
.attachment-pill.missing { background: var(--danger-soft); color: var(--danger); }
.attachment-pill.neutral { background: var(--info-soft); color: var(--info); }
.inline-action {
min-height: 24px;
padding: 0 8px;
border: 1px solid #dbe4ee;
border-radius: 999px;
border-radius: 4px;
background: #fff;
color: #334155;
font-size: 11px;
@@ -740,13 +740,13 @@ tbody tr:last-child td { border-bottom: 0; }
}
.inline-action:hover {
border-color: rgba(16, 185, 129, .36);
color: #047857;
border-color: rgba(var(--theme-primary-rgb), .36);
color: var(--theme-primary-active);
}
.risk-inline-tag.low { background: #ecfdf5; color: #059669; }
.risk-inline-tag.medium { background: #fff7ed; color: #ea580c; }
.risk-inline-tag.high { background: #fef2f2; color: #dc2626; }
.risk-inline-tag.low { background: var(--success-soft); color: var(--success); }
.risk-inline-tag.medium { background: var(--warning-soft); color: var(--warning-active); }
.risk-inline-tag.high { background: var(--danger-soft); color: var(--danger-hover); }
.expense-risk p {
margin-top: 6px;
@@ -773,7 +773,7 @@ tbody tr:last-child td { border-bottom: 0; }
align-items: center;
gap: 6px;
padding: 6px 10px;
border-radius: 999px;
border-radius: 4px;
background: #fff;
border: 1px solid #e2e8f0;
color: #334155;
@@ -782,7 +782,7 @@ tbody tr:last-child td { border-bottom: 0; }
}
.expense-file-chip i {
color: #059669;
color: var(--theme-primary);
font-size: 12px;
}
@@ -814,13 +814,13 @@ tbody tr:last-child td { border-bottom: 0; }
}
.detail-attachment.missing {
border-color: #fb923c;
border-color: var(--warning-line);
border-style: dashed;
background: #fff7ed;
background: var(--warning-soft);
}
.detail-attachment i { color: #059669; font-size: 24px; }
.detail-attachment.missing i { color: #f97316; }
.detail-attachment i { color: var(--theme-primary); font-size: 24px; }
.detail-attachment.missing i { color: var(--warning-active); }
.detail-attachment strong {
display: block;
@@ -862,7 +862,7 @@ tbody tr:last-child td { border-bottom: 0; }
font-weight: 750;
}
.risk-card header b { color: #ef4444; }
.risk-card header b { color: var(--danger); }
.risk-list {
display: grid;
@@ -883,9 +883,9 @@ tbody tr:last-child td { border-bottom: 0; }
}
.risk-list div:last-child { border-bottom: 0; }
.risk-list i { color: #f97316; }
.risk-list b.high { color: #ef4444; }
.risk-list b.medium { color: #f97316; }
.risk-list i { color: var(--warning-active); }
.risk-list b.high { color: var(--danger); }
.risk-list b.medium { color: var(--warning-active); }
.risk-note {
margin-top: 12px;
@@ -951,24 +951,24 @@ tbody tr:last-child td { border-bottom: 0; }
.approve-action {
min-width: 92px;
border: 1px solid #059669;
background: #059669;
border: 1px solid var(--theme-primary);
background: var(--theme-primary);
color: #fff;
box-shadow: 0 4px 10px rgba(5, 150, 105, .14);
box-shadow: 0 4px 10px var(--theme-primary-shadow);
}
.reject-action {
min-width: 86px;
border: 1px solid #fecaca;
border: 1px solid var(--danger-line);
background: #fff;
color: #ef4444;
color: var(--danger);
}
.supplement-action {
min-width: 86px;
border: 1px solid #fed7aa;
border: 1px solid var(--warning-line);
background: #fff;
color: #ea580c;
color: var(--warning-active);
}
/* ────────── Detail Modal Overlay ────────── */
@@ -1020,9 +1020,9 @@ tbody tr:last-child td { border-bottom: 0; }
.req-badge {
padding: 6px 14px;
border-radius: 3px;
background: #eff6ff;
border-left: 3px solid #1d4ed8;
color: #1d4ed8;
background: var(--theme-primary-soft);
border-left: 3px solid var(--theme-primary-active);
color: var(--theme-primary-active);
font-size: 13px;
font-weight: 850;
letter-spacing: .02em;
@@ -1058,23 +1058,23 @@ tbody tr:last-child td { border-bottom: 0; }
}
.header-indicator.high {
background: #fee2e2;
color: #dc2626;
background: var(--danger-soft);
color: var(--danger-hover);
}
.header-indicator.medium {
background: #ffedd5;
color: #ea580c;
background: var(--warning-soft);
color: var(--warning-active);
}
.header-indicator.low {
background: #dcfce7;
color: #059669;
background: var(--success-soft);
color: var(--success);
}
.header-indicator.status {
background: #dbeafe;
color: #1d4ed8;
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
}
.close-btn {
@@ -1128,14 +1128,14 @@ tbody tr:last-child td { border-bottom: 0; }
.progress-node.done .node-dot,
.progress-node.active .node-dot {
background: #059669;
background: var(--theme-primary-active);
color: #fff;
box-shadow: 0 4px 12px rgba(5, 150, 105, .25);
box-shadow: 0 4px 12px var(--theme-primary-shadow);
}
.progress-node.current .node-dot {
background: #10b981;
box-shadow: 0 4px 12px rgba(5, 150, 105, .25), 0 0 0 5px rgba(5, 150, 105, .1);
background: var(--theme-primary);
box-shadow: 0 4px 12px var(--theme-primary-shadow), 0 0 0 5px rgba(var(--theme-primary-rgb), .1);
animation: breathe-pulse 2s ease-in-out infinite;
}
@@ -1143,12 +1143,12 @@ tbody tr:last-child td { border-bottom: 0; }
0%, 100% {
opacity: 1;
transform: scale(1);
box-shadow: 0 4px 12px rgba(5, 150, 105, .25), 0 0 0 4px rgba(5, 150, 105, .15);
box-shadow: 0 4px 12px var(--theme-primary-shadow), 0 0 0 4px rgba(var(--theme-primary-rgb), .15);
}
50% {
opacity: 0.7;
transform: scale(1.1);
box-shadow: 0 4px 12px rgba(5, 150, 105, .4), 0 0 0 10px rgba(5, 150, 105, .08);
box-shadow: 0 4px 12px rgba(var(--theme-primary-rgb), .4), 0 0 0 10px rgba(var(--theme-primary-rgb), .08);
}
}
@@ -1166,7 +1166,7 @@ tbody tr:last-child td { border-bottom: 0; }
white-space: nowrap;
}
.progress-node.current .node-label strong { color: #059669; }
.progress-node.current .node-label strong { color: var(--theme-primary-active); }
.node-label small {
color: #94a3b8;
@@ -1185,7 +1185,7 @@ tbody tr:last-child td { border-bottom: 0; }
transition: background 300ms ease;
}
.node-line.filled { background: #10b981; }
.node-line.filled { background: var(--theme-primary); }
/* ── Modal Body ── */
.modal-body {
@@ -1220,10 +1220,10 @@ tbody tr:last-child td { border-bottom: 0; }
transition: background 160ms ease;
}
.metric-block:hover { background: #fafffe; }
.metric-block:hover { background: #f8fbff; }
.metric-block.amount {
background: #ecfdf5;
background: var(--theme-primary-soft);
}
.metric-label {
@@ -1245,9 +1245,9 @@ tbody tr:last-child td { border-bottom: 0; }
}
.metric-value.sla { display: flex; align-items: center; gap: 6px; font-size: 20px; }
.metric-value.sla.safe { color: #059669; }
.metric-value.sla.warning { color: #f59e0b; }
.metric-value.sla.danger { color: #ef4444; }
.metric-value.sla.safe { color: var(--success); }
.metric-value.sla.warning { color: var(--warning); }
.metric-value.sla.danger { color: var(--danger); }
/* ── Content Card ── */
.content-card {
@@ -1284,7 +1284,7 @@ tbody tr:last-child td { border-bottom: 0; }
}
.card-title i {
color: #059669;
color: var(--theme-primary);
font-size: 18px;
}
@@ -1297,15 +1297,15 @@ tbody tr:last-child td { border-bottom: 0; }
.card-badge {
padding: 4px 10px;
border-radius: 6px;
background: #f0fdf4;
color: #059669;
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
font-size: 12px;
font-weight: 800;
}
.card-badge.warn {
background: #fff7ed;
color: #ea580c;
background: var(--warning-soft);
color: var(--warning-active);
}
/* ── Summary Grid ── */
@@ -1325,14 +1325,14 @@ tbody tr:last-child td { border-bottom: 0; }
transition: background 160ms ease;
}
.summary-cell:hover { background: #fafffe; }
.summary-cell:hover { background: #f8fbff; }
.cell-icon {
width: 38px; height: 38px;
display: grid; place-items: center;
border-radius: 4px;
background: #ecfdf5;
color: #059669;
background: var(--theme-primary-soft);
color: var(--theme-primary);
font-size: 18px;
flex-shrink: 0;
}
@@ -1402,7 +1402,7 @@ tbody tr:last-child td { border-bottom: 0; }
}
.total-amount {
color: #059669 !important;
color: var(--theme-primary-active) !important;
font-size: 16px;
}
@@ -1416,8 +1416,8 @@ tbody tr:last-child td { border-bottom: 0; }
font-weight: 800;
}
.over-badge.ok { background: #ecfdf5; color: #059669; }
.over-badge.bad { background: #fef2f2; color: #ef4444; }
.over-badge.ok { background: var(--success-soft); color: var(--success); }
.over-badge.bad { background: var(--danger-soft); color: var(--danger); }
/* ── Attachments (sidebar list) ── */
.attachment-list-side {
@@ -1440,8 +1440,8 @@ tbody tr:last-child td { border-bottom: 0; }
.attachment-row:hover { background: #fafbfd; }
.attachment-row.missing {
border: 1px dashed #fbbf24;
background: #fffbeb;
border: 1px dashed var(--warning-line);
background: var(--warning-soft);
}
.file-icon-sm {
@@ -1452,9 +1452,9 @@ tbody tr:last-child td { border-bottom: 0; }
flex-shrink: 0;
}
.file-icon-sm.pdf { background: #fef2f2; color: #ef4444; }
.file-icon-sm.img { background: #ecfdf5; color: #059669; }
.file-icon-sm.miss { background: #fff7ed; color: #f59e0b; }
.file-icon-sm.pdf { background: var(--danger-soft); color: var(--danger); }
.file-icon-sm.img { background: var(--theme-primary-soft); color: var(--theme-primary); }
.file-icon-sm.miss { background: var(--warning-soft); color: var(--warning); }
.file-detail strong {
display: block;

View File

@@ -1,7 +1,7 @@
.archive-page .status-tag.archived {
color: #0f766e;
background: rgba(16, 185, 129, 0.12);
border: 1px solid rgba(16, 185, 129, 0.22);
color: var(--theme-primary-active);
background: var(--theme-primary-soft);
border: 1px solid rgba(var(--theme-primary-rgb), 0.22);
}
.archive-page .risk-tag.none {
@@ -9,44 +9,45 @@
color: #64748b;
}
.archive-dropdown-filter {
position: relative;
.archive-filter-control {
display: inline-flex;
}
.archive-dropdown-menu {
position: absolute;
top: calc(100% + 8px);
left: 0;
z-index: 12;
min-width: 148px;
max-height: 280px;
.archive-filter-trigger {
gap: 10px;
}
.archive-filter-trigger:focus-visible {
border-color: var(--theme-primary);
box-shadow: 0 0 0 3px var(--theme-focus-ring);
outline: none;
}
:global(.archive-filter-menu) {
min-width: 156px;
}
:global(.archive-filter-menu .el-dropdown-menu) {
padding: 6px;
border: 1px solid #d7e0ea;
border-radius: 10px;
background: #fff;
box-shadow: 0 16px 32px rgba(15, 23, 42, 0.12);
overflow-y: auto;
}
.archive-dropdown-option {
display: block;
width: 100%;
min-height: 36px;
padding: 0 12px;
border: 0;
border-radius: 8px;
background: transparent;
:global(.archive-filter-option) {
min-height: 34px;
border-radius: 4px;
color: #334155;
font-size: 13px;
font-weight: 600;
text-align: left;
cursor: pointer;
}
.archive-dropdown-option:hover,
.archive-dropdown-option.active {
background: rgba(16, 185, 129, 0.1);
color: #047857;
:global(.archive-filter-option .mdi-check) {
margin-right: 8px;
color: var(--theme-primary-active);
}
:global(.archive-filter-option.is-active),
:global(.archive-filter-option:hover) {
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
}
.archive-page .hint {

View File

@@ -56,8 +56,8 @@
}
.compare-sheet-list b.success {
background: #dcfce7;
color: #059669;
background: var(--success-soft);
color: var(--success-hover);
}
.compare-sheet-list b.danger {
@@ -106,8 +106,8 @@
}
.compare-table-wrap td b.success {
background: #dcfce7;
color: #059669;
background: var(--success-soft);
color: var(--success-hover);
}
.compare-table-wrap td b.warning {
@@ -151,8 +151,8 @@
.review-submit-form input:focus,
.review-submit-form select:focus {
outline: 0;
border-color: rgba(16, 185, 129, 0.5);
box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.12);
border-color: rgba(var(--theme-primary-rgb), 0.5);
box-shadow: 0 0 0 3px var(--theme-focus-ring);
}
.review-submit-test-state,
@@ -182,7 +182,7 @@
}
.review-submit-test-state strong.passed {
color: #047857;
color: var(--success-hover);
}
.review-submit-test-state p {
@@ -335,7 +335,7 @@
gap: 8px;
margin-top: 14px;
padding: 10px 12px;
border: 1px solid rgba(14, 165, 233, 0.22);
border: 1px solid rgba(58, 124, 165, 0.22);
border-radius: 12px;
background: linear-gradient(180deg, rgba(240, 249, 255, 0.96), rgba(224, 242, 254, 0.9));
color: #075985;
@@ -417,10 +417,10 @@
min-height: 38px;
margin-bottom: 10px;
padding: 0 12px;
border: 1px solid #e0f2fe;
border: 1px solid #eaf4fa;
border-radius: 10px;
background: #f0f9ff;
color: #0369a1;
color: #255b7d;
font-size: 12px;
font-weight: 700;
}
@@ -451,8 +451,8 @@
}
.reviewer-card {
border-color: rgba(16, 185, 129, 0.24);
background: linear-gradient(180deg, #ffffff, #f8fffc);
border-color: rgba(var(--theme-primary-rgb), 0.24);
background: linear-gradient(180deg, #ffffff, var(--theme-primary-soft));
}
.review-list {
@@ -515,7 +515,7 @@
.version-row.active {
border-radius: 10px;
background: rgba(16, 185, 129, 0.08);
background: var(--theme-primary-soft);
}
.version-main {
@@ -564,8 +564,8 @@
justify-content: center;
padding: 0 7px;
border-radius: 999px;
background: #dcfce7;
color: #059669;
background: var(--success-soft);
color: var(--success-hover);
font-size: 11px;
font-weight: 850;
}
@@ -599,8 +599,8 @@
}
.version-state.success {
background: #dcfce7;
color: #059669;
background: var(--success-soft);
color: var(--success-hover);
}
.version-state.warning {
@@ -814,8 +814,8 @@
.test-state.success,
.tool-state.safe {
background: #dcfce7;
color: #059669;
background: var(--success-soft);
color: var(--success-hover);
}
.test-state.warning,
@@ -908,7 +908,7 @@
width: 16px;
height: 16px;
margin-top: 3px;
accent-color: #0f766e;
accent-color: var(--theme-primary);
}
.risk-rule-create-toggle span {
@@ -963,8 +963,8 @@
.risk-rule-create-form select:focus,
.risk-rule-create-form textarea:focus {
outline: 0;
border-color: rgba(16, 185, 129, 0.5);
box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.12);
border-color: rgba(var(--theme-primary-rgb), 0.5);
box-shadow: 0 0 0 3px var(--theme-focus-ring);
}
.risk-rule-create-form input:not([type='checkbox'])::placeholder,
@@ -984,7 +984,7 @@
}
.publish-summary strong {
color: #059669;
color: var(--theme-primary-active);
font-size: 14px;
font-weight: 850;
}
@@ -1031,8 +1031,8 @@
}
.minor-action.success-action {
border-color: rgba(5, 150, 105, 0.26);
color: #059669;
border-color: rgba(var(--success-rgb), 0.26);
color: var(--success-hover);
}
.minor-action.enable-action {
@@ -1041,8 +1041,8 @@
}
.minor-action.enable-action.is-on {
border-color: rgba(5, 150, 105, 0.26);
color: #059669;
border-color: rgba(var(--success-rgb), 0.26);
color: var(--success-hover);
}
.minor-action.enable-action i {
@@ -1167,10 +1167,10 @@
}
.major-action {
border: 1px solid #059669;
background: #059669;
border: 1px solid var(--theme-primary);
background: var(--theme-primary);
color: #fff;
box-shadow: 0 4px 12px rgba(5, 150, 105, .16);
box-shadow: 0 4px 12px var(--theme-primary-shadow);
}
.back-action:hover,
@@ -1216,8 +1216,8 @@
}
.mini-btn.primary {
border-color: #059669;
background: #059669;
border-color: var(--theme-primary);
background: var(--theme-primary);
color: #fff;
}
@@ -1584,8 +1584,8 @@
}
.json-risk-mode-pill.low {
background: #ecfdf5;
color: #059669;
background: var(--success-soft);
color: var(--success-hover);
}
.json-risk-editor-body {
@@ -1760,8 +1760,8 @@
}
.json-risk-meta-badge.test-passed {
background: #ecfdf5;
color: #047857;
background: var(--success-soft);
color: var(--success-hover);
}
.json-risk-meta-badge.test-pending {
@@ -1785,8 +1785,8 @@
}
.meta-status-indicator.is-active .indicator-dot {
background: #10b981;
box-shadow: 0 0 4px rgba(16, 185, 129, 0.4);
background: var(--success);
box-shadow: 0 0 4px rgba(var(--success-rgb), 0.4);
}
.json-risk-editor-toolbar {

View File

@@ -66,7 +66,7 @@
bottom: -13px;
height: 3px;
border-radius: 999px;
background: #10b981;
background: var(--theme-primary);
}
.list-toolbar {
@@ -93,7 +93,7 @@
gap: 8px;
padding: 0 11px;
border: 1px solid #d7e0ea;
border-radius: 8px;
border-radius: 4px;
background: #fff;
color: #64748b;
}
@@ -118,8 +118,8 @@
}
.search-filter:focus-within {
border-color: rgba(16, 185, 129, 0.48);
box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.1);
border-color: rgba(var(--theme-primary-rgb), 0.48);
box-shadow: 0 0 0 3px var(--theme-focus-ring);
}
.picker-trigger,
@@ -127,7 +127,7 @@
.create-btn,
.row-action {
min-height: 38px;
border-radius: 8px;
border-radius: 4px;
font-size: 13px;
font-weight: 760;
}
@@ -166,9 +166,9 @@
.picker-trigger:hover,
.picker-filter.open .picker-trigger {
border-color: rgba(16, 185, 129, 0.34);
background: #f6fffb;
color: #0f9f78;
border-color: rgba(var(--theme-primary-rgb), 0.34);
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
}
.picker-popover {
@@ -181,7 +181,7 @@
gap: 14px;
padding: 16px;
border: 1px solid #d7e0ea;
border-radius: 12px;
border-radius: 4px;
background: #fff;
box-shadow: 0 18px 42px rgba(15, 23, 42, 0.16);
}
@@ -204,7 +204,7 @@
display: grid;
place-items: center;
border: 0;
border-radius: 8px;
border-radius: 4px;
background: transparent;
color: #64748b;
}
@@ -227,7 +227,7 @@
align-items: center;
padding: 0 12px;
border: 1px solid #d7e0ea;
border-radius: 8px;
border-radius: 4px;
background: #fff;
color: #334155;
font-size: 13px;
@@ -237,9 +237,9 @@
.picker-option:hover,
.picker-option.active {
border-color: rgba(16, 185, 129, 0.32);
background: rgba(16, 185, 129, 0.08);
color: #059669;
border-color: rgba(var(--theme-primary-rgb), 0.32);
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
}
.toolbar-actions {
@@ -259,8 +259,8 @@
}
.ghost-filter-btn:hover {
border-color: rgba(16, 185, 129, 0.28);
color: #059669;
border-color: rgba(var(--theme-primary-rgb), 0.28);
color: var(--theme-primary-active);
}
.create-btn {
@@ -269,9 +269,9 @@
gap: 8px;
padding: 0 14px;
border: 0;
background: #059669;
background: var(--theme-primary);
color: #fff;
box-shadow: 0 8px 18px rgba(5, 150, 105, 0.18);
box-shadow: 0 8px 18px var(--theme-primary-shadow);
}
.create-btn:disabled {
@@ -307,8 +307,8 @@
align-items: center;
padding: 0 10px;
border-radius: 999px;
background: rgba(16, 185, 129, 0.1);
color: #047857;
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
font-size: 12px;
font-weight: 800;
}
@@ -376,7 +376,7 @@
}
.table-state.empty i {
color: #0ea5e9;
color: #3a7ca5;
}
.table-state p,
@@ -402,10 +402,10 @@
align-items: center;
justify-content: center;
padding: 0 14px;
border: 1px solid rgba(16, 185, 129, 0.28);
border: 1px solid rgba(var(--theme-primary-rgb), 0.28);
border-radius: 8px;
background: #fff;
color: #059669;
color: var(--theme-primary-active);
font-size: 13px;
font-weight: 760;
}
@@ -487,7 +487,7 @@ tbody tr.is-disabled:hover {
font-weight: 900;
}
.skill-avatar.emerald { background: linear-gradient(135deg, #10b981, #059669); }
.skill-avatar.primary { background: var(--theme-gradient-primary); }
.skill-avatar.rose { background: linear-gradient(135deg, #f43f5e, #e11d48); }
.skill-avatar.violet { background: linear-gradient(135deg, #8b5cf6, #7c3aed); }
.skill-avatar.blue { background: linear-gradient(135deg, #3b82f6, #2563eb); }
@@ -529,8 +529,8 @@ tbody tr.is-disabled:hover {
}
.status-pill.success {
background: #dcfce7;
color: #059669;
background: var(--success-soft);
color: var(--success-hover);
}
.status-pill.warning {
@@ -544,8 +544,8 @@ tbody tr.is-disabled:hover {
}
.status-pill.info {
background: #e0f2fe;
color: #0284c7;
background: #eaf4fa;
color: #2f6d95;
}
.status-pill.danger {
@@ -560,13 +560,13 @@ tbody tr.is-disabled:hover {
.row-action {
padding: 0 12px;
border: 1px solid rgba(16, 185, 129, 0.32);
border: 1px solid rgba(var(--theme-primary-rgb), 0.32);
background: #fff;
color: #059669;
color: var(--theme-primary-active);
}
.row-action:hover {
background: rgba(16, 185, 129, 0.08);
background: var(--theme-primary-soft);
}
.detail-scroll {
@@ -620,7 +620,7 @@ tbody tr.is-disabled:hover {
font-weight: 800;
}
.skill-badge.emerald { background: linear-gradient(135deg, #10b981, #059669); }
.skill-badge.primary { background: var(--theme-gradient-primary); }
.skill-badge.rose { background: linear-gradient(135deg, #f43f5e, #e11d48); }
.skill-badge.violet { background: linear-gradient(135deg, #8b5cf6, #7c3aed); }
.skill-badge.blue { background: linear-gradient(135deg, #3b82f6, #2563eb); }
@@ -658,9 +658,9 @@ tbody tr.is-disabled:hover {
gap: 6px;
margin-top: 12px;
padding: 12px 14px;
border: 1px solid rgba(16, 185, 129, 0.16);
border: 1px solid rgba(var(--theme-primary-rgb), 0.16);
border-radius: 12px;
background: linear-gradient(180deg, #f8fffc, #ffffff);
background: linear-gradient(180deg, var(--theme-primary-soft), #ffffff);
}
.review-note-block strong {
@@ -695,7 +695,7 @@ tbody tr.is-disabled:hover {
}
.hero-review-meta i {
color: #059669;
color: var(--theme-primary);
font-size: 15px;
}
@@ -1041,12 +1041,12 @@ tbody tr.is-disabled:hover {
}
.version-pair-card.published {
background: rgba(16, 185, 129, 0.1);
background: var(--success-soft);
}
.version-pair-card.published b {
background: #dcfce7;
color: #059669;
background: var(--success-line);
color: var(--success-hover);
}
.version-pair-card.working {
@@ -1115,8 +1115,8 @@ tbody tr.is-disabled:hover {
}
.change-center-item.active {
border-color: rgba(16, 185, 129, 0.35);
background: rgba(16, 185, 129, 0.05);
border-color: rgba(var(--theme-primary-rgb), 0.35);
background: var(--theme-primary-soft);
}
.change-center-item > button {
@@ -1176,13 +1176,13 @@ tbody tr.is-disabled:hover {
}
.change-record-item:hover {
border-color: rgba(16, 185, 129, 0.35);
border-color: rgba(var(--theme-primary-rgb), 0.35);
box-shadow: 0 10px 24px rgba(15, 23, 42, 0.08);
transform: translateY(-1px);
}
.change-record-item:focus-visible {
outline: 3px solid rgba(16, 185, 129, 0.18);
outline: 3px solid var(--theme-focus-ring);
outline-offset: 1px;
}
@@ -1387,8 +1387,8 @@ tbody tr.is-disabled:hover {
}
.rule-timeline-item > i.success {
background: #dcfce7;
color: #059669;
background: var(--success-soft);
color: var(--success-hover);
}
.rule-timeline-item > i.warning {
@@ -1459,15 +1459,6 @@ tbody tr.is-disabled:hover {
font-weight: 850;
}
.compare-toolbar select {
width: 100%;
min-height: 40px;
padding: 0 12px;
border: 1px solid #cbd5e1;
border-radius: 12px;
background: #fff;
}
.compare-toolbar i {
margin-bottom: 10px;
color: #94a3b8;
@@ -1512,4 +1503,3 @@ tbody tr.is-disabled:hover {
display: grid;
gap: 10px;
}

View File

@@ -4,7 +4,7 @@
place-items: center;
padding: 32px;
background:
radial-gradient(circle at top, rgba(16, 185, 129, 0.16), transparent 32%),
radial-gradient(circle at top, rgba(var(--theme-primary-rgb), 0.16), transparent 32%),
linear-gradient(180deg, #08130f 0%, #0f1f18 100%);
}
@@ -27,8 +27,8 @@
place-items: center;
margin: 0 auto;
border-radius: 20px;
background: linear-gradient(135deg, rgba(16, 185, 129, 0.2), rgba(5, 150, 105, 0.28));
color: #4ade80;
background: linear-gradient(135deg, rgba(var(--theme-primary-rgb), 0.2), rgba(var(--theme-primary-rgb), 0.28));
color: var(--theme-primary-soft-strong);
font-size: 32px;
}
@@ -56,13 +56,13 @@
justify-content: center;
gap: 8px;
padding: 0 18px;
border: 1px solid rgba(16, 185, 129, 0.2);
border: 1px solid rgba(var(--theme-primary-rgb), 0.2);
border-radius: 10px;
background: #059669;
background: var(--theme-primary);
color: #fff;
font-size: 14px;
font-weight: 760;
box-shadow: 0 16px 30px rgba(5, 150, 105, 0.2);
box-shadow: 0 16px 30px var(--theme-primary-shadow);
}
.retry-btn:disabled {

View File

@@ -111,13 +111,11 @@
.budget-edit-form-grid label.required > span::after {
content: "*";
margin-left: 3px;
color: #ef4444;
color: var(--danger);
}
.budget-edit-form-grid select,
.budget-edit-textarea textarea,
.budget-edit-table input,
.budget-edit-table select {
.budget-edit-table input {
width: 100%;
border: 1px solid #dbe4ee;
border-radius: 6px;
@@ -128,17 +126,15 @@
transition: border-color 160ms ease, box-shadow 160ms ease;
}
.budget-edit-form-grid select {
height: 38px;
padding: 0 34px 0 12px;
.budget-edit-form-grid .enterprise-select,
.budget-edit-table .enterprise-select {
width: 100%;
}
.budget-edit-form-grid select:focus,
.budget-edit-textarea textarea:focus,
.budget-edit-table input:focus,
.budget-edit-table select:focus {
border-color: #10b981;
box-shadow: 0 0 0 3px rgba(16, 185, 129, .12);
.budget-edit-table input:focus {
border-color: var(--theme-primary);
box-shadow: 0 0 0 3px var(--theme-focus-ring);
}
.budget-edit-textarea {
@@ -207,7 +203,7 @@
}
.budget-edit-table th i {
color: #ef4444;
color: var(--danger);
font-style: normal;
}
@@ -217,8 +213,7 @@
.budget-edit-table th:nth-child(4) { width: 120px; }
.budget-edit-table th:nth-child(6) { width: 68px; }
.budget-edit-table input,
.budget-edit-table select {
.budget-edit-table input {
height: 34px;
padding: 0 10px;
text-align: center;
@@ -242,8 +237,8 @@
}
.budget-row-delete:hover {
background: #fef2f2;
color: #dc2626;
background: var(--danger-soft);
color: var(--danger-hover);
}
.budget-add-row-btn {
@@ -253,10 +248,10 @@
display: inline-flex;
align-items: center;
gap: 5px;
border: 1px solid rgba(16, 185, 129, .42);
border: 1px solid rgba(var(--theme-primary-rgb), .42);
border-radius: 6px;
background: #fff;
color: #059669;
color: var(--theme-primary-active);
font-size: 13px;
font-weight: 800;
cursor: pointer;
@@ -284,7 +279,7 @@
}
.budget-edit-total strong {
color: #059669;
color: var(--theme-primary-active);
font-size: 16px;
font-weight: 800;
}
@@ -315,16 +310,16 @@
}
.budget-edit-draft {
border: 1px solid #10b981;
border: 1px solid var(--theme-primary);
background: #fff;
color: #059669;
color: var(--theme-primary-active);
}
.budget-edit-publish {
border: 0;
background: linear-gradient(135deg, #10b981, #059669);
background: linear-gradient(135deg, var(--theme-primary), var(--theme-primary-active));
color: #fff;
box-shadow: 0 10px 24px rgba(5, 150, 105, .20);
box-shadow: 0 10px 24px var(--theme-primary-shadow);
}
.budget-dialog-fade-enter-active,
@@ -421,10 +416,6 @@
font-size: 12px;
}
.budget-edit-form-grid select {
height: 34px;
}
.budget-edit-textarea {
margin-top: 8px;
}
@@ -441,8 +432,7 @@
padding: 6px 8px;
}
.budget-edit-table input,
.budget-edit-table select {
.budget-edit-table input {
height: 30px;
padding: 0 8px;
}

View File

@@ -14,7 +14,7 @@
}
.budget-summary-card {
--accent: #10b981;
--accent: var(--theme-primary);
position: relative;
min-height: 112px;
padding: 12px 14px 10px;
@@ -35,16 +35,16 @@
transform: translateY(-1px);
}
.budget-summary-card.green {
--accent: #10b981;
.budget-summary-card.primary {
--accent: var(--theme-primary);
}
.budget-summary-card.blue {
--accent: #3b82f6;
.budget-summary-card.info {
--accent: var(--theme-secondary);
}
.budget-summary-card.orange {
--accent: #f59e0b;
.budget-summary-card.warning {
--accent: var(--warning);
}
.budget-summary-head {
@@ -134,13 +134,13 @@
}
.comparison-pill.up {
background: rgba(22, 163, 74, .08);
color: #16a34a;
background: var(--success-soft);
color: var(--success);
}
.comparison-pill.down {
background: rgba(239, 68, 68, .08);
color: #dc2626;
background: var(--danger-soft);
color: var(--danger-hover);
}
.budget-filter-bar {
@@ -173,35 +173,15 @@
white-space: nowrap;
}
.budget-filter-bar select {
min-height: 38px;
.budget-filter-bar .enterprise-select {
min-width: 128px;
border: 1px solid #d7e0ea;
border-radius: 8px;
background: #fff;
color: #334155;
padding: 0 34px 0 14px;
font-size: 14px;
font-weight: 750;
transition: border-color 160ms ease, box-shadow 160ms ease, color 160ms ease;
}
.budget-filter-bar select:hover {
border-color: rgba(16, 185, 129, .32);
color: #0f9f78;
}
.budget-filter-bar select:focus {
border-color: #10b981;
box-shadow: 0 0 0 3px rgba(16, 185, 129, .14);
outline: none;
}
.budget-primary-btn {
min-height: 40px;
border: 0;
border-radius: 10px;
background: linear-gradient(135deg, #10b981, #059669);
border-radius: 8px;
background: linear-gradient(135deg, var(--theme-primary), var(--theme-primary-active));
color: #fff;
padding: 0 18px;
display: inline-flex;
@@ -212,13 +192,13 @@
font-weight: 800;
white-space: nowrap;
cursor: pointer;
box-shadow: 0 10px 24px rgba(5, 150, 105, .2);
box-shadow: 0 10px 24px var(--theme-primary-shadow);
transition: transform 160ms ease, box-shadow 160ms ease, filter 160ms ease;
}
.budget-primary-btn:hover {
transform: translateY(-1px);
box-shadow: 0 14px 28px rgba(5, 150, 105, .24);
box-shadow: 0 14px 28px rgba(var(--theme-primary-rgb), .24);
filter: saturate(1.02);
}
@@ -241,8 +221,8 @@
}
.budget-ghost-btn:hover {
border-color: rgba(16, 185, 129, .32);
color: #0f9f78;
border-color: rgba(var(--theme-primary-rgb), .32);
color: var(--theme-primary-active);
box-shadow: 0 1px 4px rgba(15, 23, 42, .08);
}
@@ -330,8 +310,8 @@
}
.department-list button.active {
background: #e9f7f1;
color: #07965f;
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
}
.budget-table-wrap {
@@ -392,24 +372,24 @@
}
.budget-rate em.ok {
background: #13a66b;
background: var(--success);
}
.budget-rate em.warn {
background: #f2a51a;
background: var(--warning);
}
.budget-rate em.danger {
background: #ef4444;
background: var(--danger);
}
.budget-warning-red {
color: #e24b4b !important;
color: var(--danger) !important;
font-weight: 800;
}
.budget-warning-yellow {
color: #e3a008 !important;
color: var(--warning-active) !important;
font-weight: 800;
}
@@ -423,7 +403,7 @@
.budget-row-actions button {
border: 0;
background: transparent;
color: #1c7ed6;
color: var(--theme-primary-active);
font-size: 14px;
font-weight: 800;
}
@@ -467,14 +447,14 @@
.budget-pager button:hover:not(.active):not(:disabled) {
background: #fff;
color: #059669;
color: var(--theme-primary-active);
box-shadow: 0 1px 4px rgba(15, 23, 42, .08);
}
.budget-pager button.active {
background: #059669;
background: var(--theme-primary-active);
color: #fff;
box-shadow: 0 8px 16px rgba(5, 150, 105, .20);
box-shadow: 0 8px 16px var(--theme-primary-shadow);
}
.budget-pager button:disabled {
@@ -502,19 +482,8 @@
transition: border-color 160ms ease, color 160ms ease;
}
.budget-page-size:hover {
border-color: rgba(16, 185, 129, .32);
color: #0f9f78;
}
.budget-page-size select {
appearance: none;
border: 0;
background: transparent;
color: inherit;
font: inherit;
outline: none;
cursor: pointer;
.budget-page-size-select {
width: 112px;
}
.budget-bottom-grid {
@@ -526,7 +495,7 @@
.budget-card-head button {
border: 0;
background: transparent;
color: #1c7ed6;
color: var(--theme-primary-active);
font-size: 14px;
font-weight: 800;
}
@@ -549,11 +518,11 @@
width: 10px;
height: 10px;
border-radius: 3px;
background: #13a66b;
background: var(--theme-primary);
}
.legend-line.occupied {
background: #f59e0b;
background: var(--warning);
}
.legend-line.available {
@@ -587,11 +556,11 @@
.budget-alert-empty-icon {
width: 44px;
height: 44px;
border-radius: 12px;
border-radius: 8px;
display: grid;
place-items: center;
background: #e9f7f1;
color: #059669;
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
font-size: 24px;
}
@@ -629,15 +598,15 @@
}
.budget-alert-row i.danger {
background: #ef4444;
background: var(--danger);
}
.budget-alert-row i.warn {
background: #f59e0b;
background: var(--warning);
}
.budget-alert-row i.ok {
background: #13a66b;
background: var(--success);
}
.budget-alert-row strong {
@@ -718,7 +687,7 @@
}
.budget-filter-bar label,
.budget-filter-bar select,
.budget-filter-bar .enterprise-select,
.budget-filter-set,
.budget-action-set,
.budget-primary-btn,

View File

@@ -7,14 +7,14 @@
.conversation-list, .info-panel { display: grid; grid-template-rows: auto minmax(0, 1fr); }
.side-panel header, .info-panel header { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 14px; }
.side-panel h3, .info-panel h3 { display: inline-flex; align-items: center; gap: 8px; color: #0f172a; font-size: 17px; font-weight: 850; }
.outline-action, .info-panel header button { height: 34px; display: inline-flex; align-items: center; gap: 6px; border: 1px solid #d8e3ed; border-radius: 8px; background: #fff; color: #0f9f78; font-size: 13px; font-weight: 750; white-space: nowrap; }
.outline-action, .info-panel header button { height: 34px; display: inline-flex; align-items: center; gap: 6px; border: 1px solid #d8e3ed; border-radius: 8px; background: #fff; color: var(--theme-primary-active); font-size: 13px; font-weight: 750; white-space: nowrap; }
.outline-action { padding: 0 12px; }
.info-panel header button { border: 0; color: #64748b; }
.session-scroll, .top-question-list, .similar-scroll { min-height: 0; overflow-y: auto; scrollbar-width: thin; scrollbar-color: #cbd5e1 transparent; }
.session-scroll { display: grid; align-content: start; gap: 4px; padding-right: 4px; }
.session-row { width: 100%; min-height: 48px; display: grid; grid-template-columns: 22px minmax(0, 1fr) auto; align-items: center; gap: 10px; padding: 0 10px; border: 0; border-radius: 8px; background: transparent; color: #334155; text-align: left; }
.session-row.active, .session-row:hover { background: #eaf8f1; color: #0f8f68; }
.session-row span { color: #10b981; }
.session-row.active, .session-row:hover { background: var(--theme-primary-light-9); color: var(--theme-primary-active); }
.session-row span { color: var(--theme-primary); }
.session-row strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 14px; font-weight: 700; }
.session-row time { color: #94a3b8; font-size: 12px; }
.chat-panel { height: 100%; min-height: 0; display: grid; grid-template-rows: minmax(0, 1fr) auto; overflow: hidden; }
@@ -22,20 +22,20 @@
.talk-row { display: grid; grid-template-columns: 38px minmax(0, 1fr); gap: 12px; align-items: start; }
.avatar { width: 36px; height: 36px; display: grid; place-items: center; border-radius: 999px; color: #fff; font-size: 15px; font-weight: 850; }
.user-avatar { background: linear-gradient(135deg, #26364d, #61748a); }
.assistant-avatar { background: #10b981; font-size: 20px; }
.assistant-avatar { background: var(--theme-primary); font-size: 20px; }
.talk-content header { display: flex; align-items: center; gap: 10px; margin-bottom: 6px; }
.talk-content header strong { color: #334155; font-size: 14px; font-weight: 800; }
.talk-content header time { color: #94a3b8; font-size: 12px; }
.user-question { display: inline-block; margin: 0; padding: 9px 16px; border-radius: 8px; background: #e8f5ef; color: #334155; font-size: 14px; line-height: 1.5; }
.user-question { display: inline-block; margin: 0; padding: 9px 16px; border-radius: 8px; background: var(--theme-primary-light-9); color: #334155; font-size: 14px; line-height: 1.5; }
.answer-card, .agent-answer-markdown { max-width: 760px; border: 1px solid #dce5ef; border-radius: 10px; background: #fff; color: #334155; box-shadow: 0 8px 24px rgba(15, 23, 42, .04); }
.answer-card { display: grid; gap: 10px; padding: 13px 18px; }
.answer-card.compact { gap: 10px; }
.answer-card h4 { margin: 0 0 5px; color: #10a272; font-size: 13px; font-weight: 850; }
.answer-card h4 { margin: 0 0 5px; color: var(--theme-primary-active); font-size: 13px; font-weight: 850; }
.answer-card p, .answer-card ul { margin: 0; font-size: 14px; line-height: 1.58; }
.answer-card ul { padding-left: 18px; }
.answer-card footer { display: flex; align-items: center; justify-content: flex-end; gap: 10px; color: #64748b; font-size: 12px; }
.answer-card footer button { width: 28px; height: 28px; display: grid; place-items: center; border: 0; border-radius: 6px; background: transparent; color: #64748b; }
.answer-card footer button:hover { background: #f1f5f9; color: #0f9f78; }
.answer-card footer button:hover { background: #f1f5f9; color: var(--theme-primary-active); }
.agent-answer-content { max-width: 760px; display: grid; gap: 10px; }
.agent-answer-markdown { padding: 12px 16px; overflow-x: auto; font-size: 14px; }
.agent-answer-markdown :deep(h1),
@@ -59,20 +59,20 @@
.agent-answer-markdown :deep(li) { line-height: 1.65; }
.agent-answer-markdown :deep(ul),
.agent-answer-markdown :deep(ol) { padding-left: 22px; }
.agent-answer-markdown :deep(blockquote) { padding: 10px 12px; border-left: 4px solid #86efac; border-radius: 0 10px 10px 0; background: #f0fdf4; color: #475569; }
.agent-answer-markdown :deep(blockquote) { padding: 10px 12px; border-left: 4px solid var(--theme-primary-soft-strong); border-radius: 0 10px 10px 0; background: var(--theme-primary-light-9); color: #475569; }
.agent-answer-markdown :deep(strong) { color: #0f172a; }
.agent-answer-markdown :deep(code) { padding: 2px 6px; border-radius: 6px; background: #e2e8f0; font-size: 12px; }
.agent-answer-markdown :deep(pre) { overflow-x: auto; padding: 12px; border-radius: 10px; background: #0f172a; color: #e2e8f0; }
.agent-answer-markdown :deep(pre code) { padding: 0; background: transparent; color: inherit; }
.agent-answer-markdown :deep(a) { color: #059669; text-decoration: underline; }
.agent-answer-markdown :deep(a) { color: var(--theme-primary-active); text-decoration: underline; }
.agent-answer-markdown :deep(table) { width: auto; max-width: 100%; border: 1px solid #dce5ef; border-radius: 10px; border-collapse: collapse; background: #fff; box-shadow: 0 8px 24px rgba(15, 23, 42, .04); font-size: 13px; }
.agent-answer-markdown :deep(th),
.agent-answer-markdown :deep(td) { padding: 10px 12px; border-bottom: 1px solid #e2e8f0; text-align: left; white-space: nowrap; }
.agent-answer-markdown :deep(th) { background: #eef7f2; color: #0f172a; font-weight: 800; }
.agent-answer-markdown :deep(th) { background: var(--theme-primary-light-9); color: #0f172a; font-weight: 800; }
.agent-answer-markdown :deep(td) { color: #334155; font-weight: 650; }
.agent-answer-markdown :deep(tbody tr:last-child td) { border-bottom: 0; }
.agent-meta-row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; max-width: 760px; }
.agent-meta-chip { min-height: 26px; display: inline-flex; align-items: center; padding: 0 10px; border-radius: 999px; background: #eef7f2; color: #0f766e; font-size: 12px; font-weight: 760; }
.agent-meta-chip { min-height: 26px; display: inline-flex; align-items: center; padding: 0 10px; border-radius: 999px; background: var(--theme-primary-light-9); color: var(--theme-primary-active); font-size: 12px; font-weight: 760; }
.agent-detail-block { max-width: 760px; margin-top: 10px; display: grid; gap: 8px; }
.agent-detail-block > strong { color: #0f172a; font-size: 12px; font-weight: 820; }
.agent-citation-disclosure { overflow: hidden; border: 1px solid #dce5ef; border-radius: 10px; background: #fff; }
@@ -103,22 +103,22 @@
.prompt-toolbar { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; overflow-x: auto; }
.prompt-toolbar span { flex: 0 0 auto; color: #64748b; font-size: 13px; font-weight: 800; }
.prompt-toolbar button { height: 34px; flex: 0 0 auto; display: inline-flex; align-items: center; gap: 7px; padding: 0 14px; border: 1px solid #dce5ef; border-radius: 8px; background: #fff; color: #334155; font-size: 13px; font-weight: 750; }
.prompt-toolbar button i { color: #10b981; }
.prompt-toolbar button i { color: var(--theme-primary); }
.prompt-toolbar .icon-refresh { width: 34px; padding: 0; justify-content: center; }
.composer { min-height: 64px; display: grid; grid-template-columns: minmax(0, 1fr) 48px; align-items: center; gap: 10px; padding: 8px; border: 1px solid #cbd8e5; border-radius: 8px; background: linear-gradient(180deg, #fff, #fbfdff); box-shadow: 0 1px 2px rgba(15, 23, 42, .04); transition: border-color 160ms ease, box-shadow 160ms ease, background 160ms ease; }
.composer:focus-within { border-color: rgba(16, 185, 129, .58); background: #fff; box-shadow: 0 0 0 3px rgba(16, 185, 129, .11), 0 10px 24px rgba(15, 23, 42, .06); }
.composer:focus-within { border-color: rgba(var(--theme-primary-rgb), .58); background: #fff; box-shadow: 0 0 0 3px var(--theme-focus-ring), 0 10px 24px rgba(15, 23, 42, .06); }
.composer textarea { height: 48px; min-height: 48px; resize: none; border: 0; padding: 5px 8px; background: transparent; color: #0f172a; font-size: 14px; line-height: 1.55; }
.composer textarea::placeholder { color: #94a3b8; }
.composer textarea:focus { outline: none; }
.send-button { width: 48px; height: 48px; display: grid; place-items: center; border: 0; border-radius: 8px; background: #10b981; color: #fff; font-size: 20px; box-shadow: 0 8px 18px rgba(16, 185, 129, .20); transition: background 160ms ease, transform 160ms ease, box-shadow 160ms ease; }
.send-button:hover { background: #0ea672; box-shadow: 0 10px 22px rgba(16, 185, 129, .24); }
.send-button { width: 48px; height: 48px; display: grid; place-items: center; border: 0; border-radius: 8px; background: var(--theme-primary); color: #fff; font-size: 20px; box-shadow: 0 8px 18px var(--theme-primary-shadow); transition: background 160ms ease, transform 160ms ease, box-shadow 160ms ease; }
.send-button:hover { background: var(--theme-primary-hover); box-shadow: 0 10px 22px rgba(var(--theme-primary-rgb), .24); }
.send-button:active { transform: scale(.96); }
.send-button:disabled { background: #94a3b8; box-shadow: none; transform: none; }
.hot-top-panel h3 i { color: #ef4444; }
.top-question-list { display: grid; align-content: start; gap: 8px; padding-right: 4px; }
.top-question-list button { min-height: 42px; display: grid; grid-template-columns: 34px minmax(0, 1fr) 14px; align-items: center; gap: 10px; padding: 0 8px; border: 1px solid #e2e8f0; border-radius: 8px; background: #fff; color: #334155; text-align: left; }
.top-question-list button:hover { border-color: rgba(16, 185, 129, .32); color: #0f9f78; }
.top-question-list strong { color: #10b981; font-size: 13px; font-weight: 850; font-variant-numeric: tabular-nums; }
.top-question-list button:hover { border-color: rgba(var(--theme-primary-rgb), .32); color: var(--theme-primary-active); }
.top-question-list strong { color: var(--theme-primary); font-size: 13px; font-weight: 850; font-variant-numeric: tabular-nums; }
.top-question-list span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 14px; font-weight: 750; }
.top-question-list i { color: #94a3b8; }
.similar-panel { display: grid; }
@@ -127,19 +127,19 @@
.similar-row:first-child { border-top: 0; }
.similar-row span { min-width: 0; display: inline-flex; align-items: center; gap: 8px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 14px; font-weight: 700; }
.similar-row span i { color: #64748b; font-size: 17px; }
.similar-row strong { height: 26px; display: inline-flex; align-items: center; justify-content: center; border-radius: 8px; background: #e8f8f0; color: #15945f; font-size: 13px; font-weight: 850; }
.similar-row strong { height: 26px; display: inline-flex; align-items: center; justify-content: center; border-radius: 8px; background: var(--theme-primary-light-9); color: var(--theme-primary-active); font-size: 13px; font-weight: 850; }
.similar-row > i { color: #94a3b8; }
.semantic-debug-panel { display: grid; grid-template-rows: auto minmax(0, 1fr); }
.semantic-debug-body { min-height: 0; display: grid; grid-template-rows: auto auto auto auto minmax(0, 1fr); gap: 12px; }
.semantic-debug-input { display: grid; gap: 8px; }
.semantic-debug-input span { color: #334155; font-size: 13px; font-weight: 800; }
.semantic-debug-input textarea { min-height: 98px; resize: vertical; padding: 12px 13px; border: 1px solid #d7e0ea; border-radius: 10px; background: linear-gradient(180deg, #ffffff, #f7fbff); color: #0f172a; font-size: 13px; line-height: 1.6; }
.semantic-debug-input textarea:focus { outline: none; border-color: rgba(16, 185, 129, .48); box-shadow: 0 0 0 3px rgba(16, 185, 129, .11); }
.semantic-debug-input textarea:focus { outline: none; border-color: rgba(var(--theme-primary-rgb), .48); box-shadow: 0 0 0 3px var(--theme-focus-ring); }
.semantic-debug-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.semantic-chip { min-height: 30px; padding: 0 10px; border: 1px solid #d8e3ed; border-radius: 999px; background: #f8fbff; color: #334155; font-size: 12px; font-weight: 760; text-align: left; }
.semantic-chip:hover { border-color: rgba(16, 185, 129, .32); background: #eefbf5; color: #0f8f68; }
.semantic-chip:hover { border-color: rgba(var(--theme-primary-rgb), .32); background: var(--theme-primary-light-9); color: var(--theme-primary-active); }
.semantic-debug-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.semantic-parse-btn { min-height: 36px; display: inline-flex; align-items: center; gap: 8px; padding: 0 14px; border: 0; border-radius: 9px; background: linear-gradient(135deg, #0f766e, #10b981); color: #fff; font-size: 13px; font-weight: 800; box-shadow: 0 10px 20px rgba(16, 185, 129, .18); }
.semantic-parse-btn { min-height: 36px; display: inline-flex; align-items: center; gap: 8px; padding: 0 14px; border: 0; border-radius: 9px; background: var(--theme-gradient-primary); color: #fff; font-size: 13px; font-weight: 800; box-shadow: 0 10px 20px var(--theme-primary-shadow); }
.semantic-parse-btn:disabled { background: #cbd5e1; box-shadow: none; }
.semantic-inline-meta { color: #64748b; font-size: 12px; }
.semantic-debug-error { margin: 0; padding: 10px 12px; border-radius: 10px; background: #fff1f2; color: #be123c; font-size: 12px; line-height: 1.55; }
@@ -150,7 +150,7 @@
.semantic-result-card strong { color: #0f172a; font-size: 14px; font-weight: 850; word-break: break-word; }
.semantic-field-list { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
.semantic-field-list section { min-height: 72px; padding: 12px; border-radius: 10px; background: #f8fafc; }
.semantic-field-list h4, .semantic-json-block h4 { margin: 0 0 6px; color: #0f766e; font-size: 12px; font-weight: 850; letter-spacing: .02em; }
.semantic-field-list h4, .semantic-json-block h4 { margin: 0 0 6px; color: var(--theme-primary-active); font-size: 12px; font-weight: 850; letter-spacing: .02em; }
.semantic-field-list p { margin: 0; color: #334155; font-size: 12px; line-height: 1.6; word-break: break-word; }
.semantic-json-block { min-height: 0; display: grid; grid-template-rows: auto minmax(0, 1fr); overflow: hidden; }
.semantic-json-block pre { min-height: 0; margin: 0; padding: 12px; overflow: auto; border-radius: 10px; background: #0f172a; color: #d7f7ea; font-size: 11px; line-height: 1.55; }

View File

@@ -36,7 +36,7 @@
}
.status-tabs button.active {
color: #059669;
color: var(--theme-primary-active);
}
.status-tabs button.active::after {
@@ -47,7 +47,7 @@
bottom: -1px;
height: 3px;
border-radius: 999px 999px 0 0;
background: #10b981;
background: var(--theme-primary);
}
.scope-tab-badge {
@@ -101,7 +101,7 @@
height: 38px;
padding: 0 12px 0 36px;
border: 1px solid #d7e0ea;
border-radius: 8px;
border-radius: 4px;
background: #fff;
color: #0f172a;
font-size: 13px;
@@ -113,13 +113,12 @@
}
.list-search input:focus {
border-color: #10b981;
box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.14);
border-color: var(--theme-primary);
box-shadow: 0 0 0 3px rgba(58, 124, 165, 0.14);
outline: none;
}
.filter-btn,
.page-size {
.filter-btn {
min-height: 38px;
display: inline-flex;
align-items: center;
@@ -127,7 +126,7 @@
gap: 9px;
padding: 0 14px;
border: 1px solid #d7e0ea;
border-radius: 8px;
border-radius: 4px;
background: #fff;
color: #334155;
font-size: 14px;
@@ -140,10 +139,9 @@
justify-content: space-between;
}
.filter-btn:hover,
.page-size:hover {
border-color: rgba(16, 185, 129, .32);
color: #0f9f78;
.filter-btn:hover {
border-color: rgba(58, 124, 165, .32);
color: var(--theme-primary-active);
}
.document-filter,
@@ -152,12 +150,11 @@
}
.document-filter-menu,
.date-range-popover,
.page-size-dropdown {
.date-range-popover {
position: absolute;
z-index: 40;
border: 1px solid #d7e0ea;
border-radius: 10px;
border-radius: 4px;
background: #fff;
box-shadow: 0 16px 32px rgba(15, 23, 42, 0.12);
overflow: hidden;
@@ -172,14 +169,13 @@
overflow-y: auto;
}
.document-filter-menu button,
.page-size-dropdown button {
.document-filter-menu button {
display: block;
width: 100%;
min-height: 36px;
padding: 0 12px;
border: 0;
border-radius: 8px;
border-radius: 4px;
background: transparent;
color: #334155;
font-size: 13px;
@@ -190,8 +186,8 @@
.document-filter-menu button:hover,
.document-filter-menu button.active {
background: rgba(16, 185, 129, 0.1);
color: #047857;
background: rgba(58, 124, 165, 0.1);
color: var(--theme-primary-active);
}
.date-range-trigger {
@@ -233,7 +229,7 @@
display: grid;
place-items: center;
border: 0;
border-radius: 8px;
border-radius: 4px;
background: transparent;
color: #64748b;
}
@@ -260,7 +256,7 @@
height: 38px;
padding: 0 9px;
border: 1px solid #d7e0ea;
border-radius: 8px;
border-radius: 4px;
color: #0f172a;
font-size: 13px;
}
@@ -269,7 +265,7 @@
.apply-btn {
height: 36px;
padding: 0 14px;
border-radius: 8px;
border-radius: 4px;
font-size: 13px;
font-weight: 750;
}
@@ -282,7 +278,7 @@
.apply-btn {
border: 0;
background: #10b981;
background: var(--theme-primary);
color: #fff;
}
@@ -300,12 +296,12 @@
padding: 0 18px;
border: 0;
border-radius: 10px;
background: linear-gradient(135deg, #10b981, #059669);
background: linear-gradient(135deg, var(--theme-primary), var(--theme-primary-active));
color: #fff;
font-size: 14px;
font-weight: 800;
white-space: nowrap;
box-shadow: 0 10px 24px rgba(5, 150, 105, 0.2);
box-shadow: 0 10px 24px var(--theme-primary-shadow);
transition: transform 160ms ease, box-shadow 160ms ease, filter 160ms ease;
}
@@ -318,13 +314,13 @@
.create-request-btn:hover {
transform: translateY(-1px);
box-shadow: 0 14px 28px rgba(5, 150, 105, 0.24);
box-shadow: 0 14px 28px var(--theme-primary-shadow);
filter: saturate(1.02);
}
.create-request-btn.secondary:hover {
border-color: rgba(16, 185, 129, .32);
color: #047857;
border-color: rgba(58, 124, 165, .32);
color: var(--theme-primary-active);
box-shadow: 0 8px 18px rgba(15, 23, 42, 0.08);
}
@@ -345,7 +341,7 @@
}
.status-filter-trigger > .mdi:first-child {
color: #10b981;
color: var(--theme-primary);
}
.status-filter-menu {
@@ -388,7 +384,7 @@
.table-state .mdi {
font-size: 28px;
color: #10b981;
color: var(--theme-primary);
}
.table-state strong {
@@ -469,7 +465,7 @@ tbody tr {
}
tbody tr:hover {
background: linear-gradient(90deg, rgba(16, 185, 129, .08), rgba(16, 185, 129, .03));
background: linear-gradient(90deg, rgba(58, 124, 165, .08), rgba(58, 124, 165, .03));
}
tbody tr:last-child td {
@@ -477,7 +473,7 @@ tbody tr:last-child td {
}
.doc-id {
color: #059669;
color: var(--theme-primary-active);
font-weight: 800;
}
@@ -526,8 +522,8 @@ tbody tr:last-child td {
}
.doc-kind-tag.reimbursement {
background: #ecfdf5;
color: #059669;
background: var(--theme-primary-light-9);
color: var(--theme-primary-active);
}
.doc-kind-tag.application {
@@ -546,8 +542,8 @@ tbody tr:last-child td {
.type-tag.travel,
.type-tag.hotel,
.type-tag.transport {
background: #ecfdf5;
color: #047857;
background: var(--theme-primary-light-9);
color: var(--theme-primary-active);
}
.type-tag.entertainment,
@@ -589,9 +585,9 @@ tbody tr:last-child td {
.status-tag.success,
.status-tag.archived {
border-color: #bbf7d0;
background: #ecfdf5;
color: #059669;
border-color: var(--success-line);
background: var(--success-soft);
color: var(--success-active);
}
.status-tag.warning,
@@ -650,14 +646,14 @@ tbody tr:last-child td {
.pager button:hover:not(.active) {
background: #fff;
color: #059669;
color: var(--theme-primary-active);
box-shadow: 0 1px 4px rgba(15, 23, 42, .08);
}
.pager button.active {
background: #059669;
background: var(--theme-primary-active);
color: #fff;
box-shadow: 0 8px 16px rgba(5, 150, 105, .20);
box-shadow: 0 8px 16px var(--theme-primary-shadow);
}
.pager button:disabled {
@@ -665,40 +661,11 @@ tbody tr:last-child td {
cursor: not-allowed;
}
.page-size-wrap {
position: relative;
.page-size-select {
width: 118px;
justify-self: end;
}
.page-size {
min-width: 112px;
border-radius: 10px;
background: #fff;
box-shadow: 0 1px 2px rgba(15, 23, 42, .04);
}
.page-size-dropdown {
right: 0;
bottom: calc(100% + 6px);
display: grid;
}
.page-size-dropdown button {
border-radius: 0;
text-align: center;
padding: 0 20px;
}
.page-size-dropdown button:hover {
background: #f0fdf4;
color: #059669;
}
.page-size-dropdown button.active {
background: #059669;
color: #fff;
}
@media (max-width: 1200px) {
.document-toolbar,
.list-foot {
@@ -730,7 +697,7 @@ tbody tr:last-child td {
.document-status-filter,
.list-search,
.filter-btn,
.page-size {
.page-size-select {
width: 100%;
}

View File

@@ -55,7 +55,7 @@
}
.status-tabs button.active {
color: #059669;
color: var(--theme-primary-active);
}
.status-tabs button.active::after {
@@ -66,7 +66,7 @@
bottom: -1px;
height: 3px;
border-radius: 999px 999px 0 0;
background: #10b981;
background: var(--theme-primary);
}
.status-tabs button small {
@@ -84,8 +84,8 @@
}
.status-tabs button.active small {
background: rgba(16, 185, 129, 0.12);
color: #059669;
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
}
.list-toolbar {
@@ -124,7 +124,7 @@
height: 38px;
padding: 0 12px 0 36px;
border: 1px solid #d7e0ea;
border-radius: 8px;
border-radius: 4px;
background: #fff;
color: #0f172a;
font-size: 13px;
@@ -136,8 +136,8 @@
.list-search input:focus {
outline: none;
border-color: rgba(16, 185, 129, 0.6);
box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.12);
border-color: rgba(var(--theme-primary-rgb), 0.6);
box-shadow: 0 0 0 3px var(--theme-focus-ring);
}
.picker-trigger,
@@ -147,7 +147,7 @@
.create-btn,
.row-action {
min-height: 38px;
border-radius: 8px;
border-radius: 4px;
font-size: 14px;
font-weight: 750;
}
@@ -164,7 +164,7 @@
gap: 9px;
padding: 0 34px 0 12px;
border: 1px solid #d7e0ea;
border-radius: 8px;
border-radius: 4px;
background: #fff;
color: #334155;
white-space: nowrap;
@@ -187,9 +187,9 @@
.picker-trigger:hover,
.picker-filter.open .picker-trigger {
border-color: rgba(16, 185, 129, 0.34);
background: #f6fffb;
color: #0f9f78;
border-color: rgba(var(--theme-primary-rgb), 0.34);
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
}
.toolbar-actions {
@@ -208,7 +208,7 @@
gap: 14px;
padding: 16px;
border: 1px solid #d7e0ea;
border-radius: 12px;
border-radius: 4px;
background: #fff;
box-shadow: 0 18px 42px rgba(15, 23, 42, 0.16);
}
@@ -231,7 +231,7 @@
display: grid;
place-items: center;
border: 0;
border-radius: 8px;
border-radius: 4px;
background: transparent;
color: #64748b;
}
@@ -254,7 +254,7 @@
align-items: center;
padding: 0 12px;
border: 1px solid #d7e0ea;
border-radius: 8px;
border-radius: 4px;
background: #fff;
color: #334155;
font-size: 13px;
@@ -263,14 +263,14 @@
}
.picker-option:hover {
border-color: rgba(16, 185, 129, 0.28);
background: #f0fdf4;
color: #047857;
border-color: rgba(var(--theme-primary-rgb), 0.28);
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
}
.picker-option.active {
border-color: #10b981;
background: #10b981;
border-color: var(--theme-primary);
background: var(--theme-primary);
color: #fff;
}
@@ -279,9 +279,9 @@
align-items: center;
gap: 7px;
padding: 0 14px;
border: 1px solid rgba(5, 150, 105, 0.16);
background: #f8fffb;
color: #047857;
border: 1px solid rgba(var(--theme-primary-rgb), 0.18);
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
}
.template-btn,
@@ -297,9 +297,9 @@
.template-btn:hover,
.export-btn:hover {
border-color: rgba(16, 185, 129, 0.34);
background: #f6fffb;
color: #0f9f78;
border-color: rgba(var(--theme-primary-rgb), 0.34);
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
}
.create-btn {
@@ -308,9 +308,9 @@
gap: 8px;
padding: 0 14px;
border: 0;
background: #059669;
background: var(--theme-primary);
color: #fff;
box-shadow: 0 8px 18px rgba(5, 150, 105, 0.18);
box-shadow: 0 8px 18px var(--theme-primary-shadow);
}
.create-btn:disabled,
@@ -445,86 +445,25 @@
.pager button:hover:not(.active) {
background: #fff;
color: #059669;
color: var(--theme-primary-active);
box-shadow: 0 1px 4px rgba(15, 23, 42, 0.08);
}
.pager button.active {
background: #059669;
background: var(--theme-primary);
color: #fff;
box-shadow: 0 8px 16px rgba(5, 150, 105, 0.2);
box-shadow: 0 8px 16px var(--theme-primary-shadow);
}
.page-nav {
color: #64748b;
}
.page-size {
min-height: 38px;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 9px;
min-width: 112px;
padding: 0 14px;
border: 1px solid #d7e0ea;
border-radius: 10px;
background: #fff;
color: #334155;
font-size: 14px;
font-weight: 750;
white-space: nowrap;
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
}
.page-size:hover {
border-color: rgba(16, 185, 129, 0.32);
color: #0f9f78;
}
.page-size-wrap {
position: relative;
.page-size-select {
width: 112px;
justify-self: end;
}
.page-size-dropdown {
position: absolute;
bottom: calc(100% + 6px);
right: 0;
z-index: 40;
display: grid;
border: 1px solid #d7e0ea;
border-radius: 10px;
background: #fff;
box-shadow: 0 12px 32px rgba(15, 23, 42, 0.14);
overflow: hidden;
}
.page-size-dropdown button {
height: 36px;
display: grid;
place-items: center;
border: 0;
border-radius: 0;
background: transparent;
color: #334155;
font-size: 13px;
font-weight: 750;
white-space: nowrap;
padding: 0 20px;
transition: background 120ms ease, color 120ms ease;
}
.page-size-dropdown button:hover {
background: #f0fdf4;
color: #059669;
}
.page-size-dropdown button.active {
background: #059669;
color: #fff;
}
.table-state {
width: 100%;
min-height: 220px;
@@ -539,7 +478,7 @@
.table-state i {
font-size: 26px;
color: #059669;
color: var(--theme-primary);
}
.table-state.error i {
@@ -559,10 +498,10 @@
.state-action {
min-height: 36px;
padding: 0 14px;
border: 1px solid rgba(5, 150, 105, 0.22);
border: 1px solid rgba(var(--theme-primary-rgb), 0.22);
border-radius: 8px;
background: #ecfdf5;
color: #047857;
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
font-size: 13px;
font-weight: 760;
}
@@ -644,7 +583,7 @@ tbody tr:hover {
}
tbody tr.spotlight {
background: linear-gradient(90deg, rgba(16, 185, 129, 0.05), rgba(59, 130, 246, 0.03));
background: linear-gradient(90deg, rgba(var(--theme-primary-rgb), 0.05), rgba(var(--theme-primary-rgb), 0.02));
}
tbody tr:last-child td {
@@ -665,7 +604,7 @@ tbody tr:last-child td {
display: grid;
place-items: center;
border-radius: 11px;
background: linear-gradient(135deg, #10b981, #059669);
background: var(--theme-gradient-primary);
color: #fff;
font-size: 13px;
font-weight: 900;
@@ -709,8 +648,8 @@ tbody tr:last-child td {
}
.status-pill.success {
background: #dcfce7;
color: #059669;
background: var(--success-soft);
color: var(--success-hover);
}
.status-pill.warning {
@@ -733,8 +672,8 @@ tbody tr:last-child td {
}
.role-pill {
background: #ecfdf5;
color: #059669;
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
}
.more-pill {
@@ -744,9 +683,9 @@ tbody tr:last-child td {
.row-action {
padding: 0 12px;
border: 1px solid rgba(16, 185, 129, 0.32);
border: 1px solid rgba(var(--theme-primary-rgb), 0.32);
background: #fff;
color: #059669;
color: var(--theme-primary-active);
}
.detail-scroll {
@@ -774,7 +713,7 @@ tbody tr:last-child td {
display: grid;
place-items: center;
border-radius: 18px;
background: linear-gradient(135deg, #10b981, #047857);
background: var(--theme-gradient-primary);
color: #fff;
font-size: 24px;
font-weight: 900;
@@ -950,8 +889,8 @@ tbody tr:last-child td {
.manager-picker.open .manager-picker-trigger,
.manager-picker-trigger:hover {
border-color: rgba(16, 185, 129, 0.34);
background: #f6fffb;
border-color: rgba(var(--theme-primary-rgb), 0.34);
background: var(--theme-primary-soft);
}
.manager-picker-label {
@@ -987,8 +926,8 @@ tbody tr:last-child td {
.manager-picker-panel input[type='search']:focus {
outline: none;
border-color: rgba(16, 185, 129, 0.6);
box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.12);
border-color: rgba(var(--theme-primary-rgb), 0.6);
box-shadow: 0 0 0 3px var(--theme-focus-ring);
}
.manager-picker-options {
@@ -1022,8 +961,8 @@ tbody tr:last-child td {
.manager-picker-option:hover,
.manager-picker-option.active {
border-color: rgba(16, 185, 129, 0.32);
background: linear-gradient(180deg, rgba(240, 253, 244, 0.85), #ffffff);
border-color: rgba(var(--theme-primary-rgb), 0.32);
background: linear-gradient(180deg, var(--theme-primary-soft), #ffffff);
}
.manager-picker-empty {
@@ -1051,8 +990,8 @@ tbody tr:last-child td {
}
.role-card.active {
border-color: rgba(16, 185, 129, 0.32);
background: linear-gradient(180deg, rgba(240, 253, 244, 0.85), #ffffff);
border-color: rgba(var(--theme-primary-rgb), 0.32);
background: linear-gradient(180deg, var(--theme-primary-soft), #ffffff);
}
.role-card input {
@@ -1174,7 +1113,7 @@ td.cell-updated {
}
.publish-summary strong {
color: #059669;
color: var(--theme-primary-active);
font-size: 14px;
font-weight: 850;
}
@@ -1221,10 +1160,10 @@ td.cell-updated {
}
.major-action {
border: 1px solid #059669;
background: #059669;
border: 1px solid var(--theme-primary);
background: var(--theme-primary);
color: #fff;
box-shadow: 0 4px 12px rgba(5, 150, 105, 0.16);
box-shadow: 0 4px 12px var(--theme-primary-shadow);
}
.minor-action:disabled,
@@ -1286,7 +1225,7 @@ td.cell-updated {
width: min(280px, calc(100vw - 64px));
}
.page-size,
.page-size-select,
.pager {
justify-self: stretch;
}

View File

@@ -61,7 +61,7 @@
}
.detail-state i {
color: #10b981;
color: var(--theme-primary);
font-size: 28px;
}
@@ -212,8 +212,8 @@
}
.trace-step.active {
border-color: rgba(16, 185, 129, 0.32);
box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.08);
border-color: rgba(var(--theme-primary-rgb), 0.32);
box-shadow: 0 0 0 3px rgba(var(--theme-primary-rgb), 0.08);
}
.step-index {
@@ -224,7 +224,7 @@
justify-content: center;
border-radius: 999px;
background: #ecfeff;
color: #0f766e;
color: var(--theme-primary-active);
font-size: 12px;
font-weight: 800;
}
@@ -287,8 +287,8 @@
}
.status-pill.success {
background: rgba(22, 163, 74, 0.12);
color: #166534;
background: rgba(var(--success-rgb), 0.12);
color: var(--success-active);
}
.status-pill.warning,

View File

@@ -9,8 +9,8 @@
padding: 48px clamp(40px, 5vw, 86px);
overflow: hidden;
background:
linear-gradient(120deg, rgba(236,253,245,.72), transparent 34%),
linear-gradient(105deg, #f8fafc 0%, #f4fbf8 44%, #f8fafc 100%);
linear-gradient(120deg, rgba(var(--theme-primary-rgb, 58, 124, 165), .10), transparent 34%),
linear-gradient(105deg, #f8fafc 0%, #f5faff 44%, #f8fafc 100%);
}
.login-page::before {
@@ -21,8 +21,8 @@
background:
linear-gradient(90deg, rgba(15,23,42,.045) 1px, transparent 1px),
linear-gradient(0deg, rgba(15,23,42,.04) 1px, transparent 1px),
radial-gradient(circle at 28% 72%, rgba(16,185,129,.12), transparent 28%),
radial-gradient(circle at 75% 22%, rgba(245,158,11,.08), transparent 30%);
radial-gradient(circle at 28% 72%, rgba(var(--theme-primary-rgb, 58, 124, 165), .10), transparent 28%),
radial-gradient(circle at 75% 22%, rgba(37,99,235,.06), transparent 30%);
background-size: 72px 72px, 72px 72px, auto, auto;
mask-image: linear-gradient(100deg, rgba(0,0,0,.7), rgba(0,0,0,.32) 48%, rgba(0,0,0,.16));
pointer-events: none;
@@ -37,11 +37,11 @@
width: min(820px, 58vw);
height: min(560px, 64vh);
border: 1px solid rgba(148,163,184,.22);
border-radius: 18px;
border-radius: 8px;
background:
linear-gradient(90deg, transparent 0 28%, rgba(15,23,42,.055) 28% calc(28% + 1px), transparent calc(28% + 1px)),
repeating-linear-gradient(0deg, transparent 0 35px, rgba(15,23,42,.05) 36px),
linear-gradient(135deg, rgba(255,255,255,.74), rgba(236,253,245,.32));
linear-gradient(135deg, rgba(255,255,255,.74), rgba(var(--theme-primary-rgb, 58, 124, 165), .10));
box-shadow: 0 34px 80px rgba(15,23,42,.08);
transform: rotate(-7deg);
pointer-events: none;
@@ -65,7 +65,7 @@
height: 34px;
display: inline-grid;
place-items: center;
color: #059669;
color: var(--theme-primary-active);
}
:deep(.logo-mark svg) {
@@ -86,7 +86,7 @@
}
.eyebrow-text {
color: #059669;
color: var(--theme-primary-active);
font-size: 14px;
font-weight: 900;
letter-spacing: .08em;
@@ -131,7 +131,7 @@
width: 230px;
height: 62px;
border-radius: 50%;
background: linear-gradient(90deg, rgba(16,185,129,.16), rgba(245,158,11,.12));
background: linear-gradient(90deg, rgba(var(--theme-primary-rgb, 58, 124, 165), .14), rgba(37,99,235,.08));
filter: blur(4px);
}
@@ -139,10 +139,10 @@
position: absolute;
z-index: 0;
display: block;
border: 1px solid rgba(16,185,129,.18);
border: 1px solid rgba(var(--theme-primary-rgb, 58, 124, 165), .22);
border-left: 0;
border-bottom: 0;
border-radius: 0 22px 0 0;
border-radius: 0 8px 0 0;
}
.flow-line::after {
@@ -153,8 +153,8 @@
width: 8px;
height: 8px;
border-radius: 999px;
background: #10b981;
box-shadow: 0 0 0 5px rgba(16,185,129,.12);
background: var(--theme-primary);
box-shadow: 0 0 0 5px rgba(var(--theme-primary-rgb, 58, 124, 165), .12);
}
.flow-a {
@@ -197,7 +197,7 @@
display: grid;
gap: 7px;
padding: 17px 18px;
border-radius: 14px;
border-radius: 8px;
}
.metric-card span {
@@ -219,7 +219,7 @@
font-weight: 700;
}
.up { color: #059669; }
.up { color: var(--success); }
.danger { color: #ef4444; }
.amount { left: 20px; top: 20px; }
@@ -252,7 +252,7 @@
width: 220px;
height: 214px;
padding: 28px 28px;
border-radius: 16px;
border-radius: 8px;
transform: rotate(2deg);
}
@@ -281,10 +281,10 @@
display: grid;
place-items: center;
border-radius: 999px;
background: linear-gradient(135deg, #6ee7b7, #059669);
background: linear-gradient(135deg, var(--theme-primary), var(--theme-primary-active));
color: #fff;
font-size: 27px;
box-shadow: 0 14px 28px rgba(5,150,105,.25);
box-shadow: 0 14px 28px rgba(var(--theme-primary-rgb, 58, 124, 165), .22);
}
.shield-art {
@@ -337,7 +337,7 @@
gap: 12px;
padding: 12px 14px;
border: 1px solid rgba(215, 224, 234, .82);
border-radius: 13px;
border-radius: 6px;
background: rgba(255,255,255,.76);
box-shadow: 0 12px 30px rgba(65, 88, 110, .08);
backdrop-filter: blur(16px);
@@ -352,7 +352,7 @@
font-size: 21px;
}
.feature-strip .green { background: #dff7ee; color: #059669; }
.feature-strip .primary { background: var(--theme-primary-soft); color: var(--theme-primary-active); }
.feature-strip .red { background: #fee2e2; color: #ef4444; }
.feature-strip .blue { background: #dbeafe; color: #3b82f6; }
@@ -379,7 +379,7 @@
display: grid;
padding: 58px 60px 44px;
border: 1px solid rgba(215, 224, 234, .96);
border-radius: 20px;
border-radius: 8px;
background: rgba(255,255,255,.86);
box-shadow: 0 24px 64px rgba(65, 88, 110, .16);
backdrop-filter: blur(18px);
@@ -450,9 +450,9 @@
}
.field input:focus {
border-color: #10b981;
border-color: var(--theme-primary);
background: #fff;
box-shadow: 0 0 0 3px rgba(16,185,129,.13);
box-shadow: 0 0 0 3px var(--theme-focus-ring, rgba(58, 124, 165, .14));
outline: none;
}
@@ -471,7 +471,7 @@
.field-icon-btn:hover {
background: #f1f5f9;
color: #059669;
color: var(--theme-primary-active);
}
.form-meta {
@@ -503,7 +503,7 @@
.remember input {
width: 16px;
height: 16px;
accent-color: #059669;
accent-color: var(--theme-primary);
}
.link-btn {
@@ -529,13 +529,13 @@
.submit-btn {
margin-top: 4px;
border: 0;
background: linear-gradient(135deg, #10b981, #059669);
background: linear-gradient(135deg, var(--theme-primary), var(--theme-primary-active));
color: #fff;
box-shadow: 0 16px 30px rgba(5,150,105,.20);
box-shadow: 0 16px 30px rgba(var(--theme-primary-rgb, 58, 124, 165), .20);
}
.submit-btn:hover {
background: linear-gradient(135deg, #13c990, #047857);
background: linear-gradient(135deg, var(--theme-primary-hover), var(--theme-primary-active));
}
.submit-btn:disabled,
@@ -571,13 +571,13 @@
}
.sso-btn {
border: 1px solid #10b981;
border: 1px solid var(--theme-primary);
background: rgba(255,255,255,.78);
color: #059669;
color: var(--theme-primary-active);
}
.sso-btn:hover {
background: #ecfdf5;
background: var(--theme-primary-soft);
}
.security-note {
@@ -689,7 +689,7 @@
.login-card {
padding: 32px 22px 24px;
border-radius: 14px;
border-radius: 8px;
}
.card-head h2 {

View File

@@ -70,7 +70,7 @@
.console-tabs button:hover,
.console-tabs button.active {
color: #0f766e;
color: var(--theme-primary-active);
}
.console-tabs button.active::after {
@@ -81,7 +81,7 @@
bottom: -11px;
height: 2px;
border-radius: 999px;
background: #10b981;
background: var(--theme-primary);
}
.console-toolbar {
@@ -102,7 +102,6 @@
font-weight: 700;
}
.filter-field select,
.field-input {
min-height: 38px;
border: 1px solid #d8e1eb;
@@ -110,12 +109,6 @@
background: #fff;
}
.filter-field select {
padding: 0 12px;
color: #0f172a;
font-size: 13px;
}
.field-input {
display: flex;
align-items: center;
@@ -148,7 +141,7 @@
}
.toolbar-btn.primary {
background: linear-gradient(135deg, #0f9f8d, #10b981);
background: var(--theme-gradient-primary);
border-color: transparent;
color: #fff;
}
@@ -247,12 +240,12 @@
}
.log-table tbody tr:hover {
background: rgba(16, 185, 129, 0.04);
background: rgba(var(--theme-primary-rgb), 0.04);
}
.log-table tbody tr.active {
background: rgba(16, 185, 129, 0.08);
box-shadow: inset 3px 0 0 #10b981;
background: rgba(var(--theme-primary-rgb), 0.08);
box-shadow: inset 3px 0 0 var(--theme-primary);
}
.log-table tbody td {
@@ -363,14 +356,14 @@
.pager button:hover:not(.active):not(:disabled) {
background: #fff;
color: #059669;
color: var(--theme-primary-active);
box-shadow: 0 1px 4px rgba(15, 23, 42, 0.08);
}
.pager button.active {
background: #059669;
background: var(--theme-primary);
color: #fff;
box-shadow: 0 8px 16px rgba(5, 150, 105, 0.2);
box-shadow: 0 8px 16px var(--theme-primary-shadow);
}
.page-nav:disabled {
@@ -378,63 +371,11 @@
opacity: 0.45;
}
.page-size-wrap {
position: relative;
.page-size-select {
width: 112px;
justify-self: end;
}
.page-size {
min-width: 112px;
min-height: 38px;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 9px;
padding: 0 14px;
border: 1px solid #d7e0ea;
border-radius: 10px;
background: #fff;
color: #334155;
font-size: 14px;
font-weight: 750;
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
}
.page-size-dropdown {
position: absolute;
right: 0;
bottom: calc(100% + 6px);
z-index: 40;
display: grid;
overflow: hidden;
border: 1px solid #d7e0ea;
border-radius: 10px;
background: #fff;
box-shadow: 0 12px 32px rgba(15, 23, 42, 0.14);
}
.page-size-dropdown button {
height: 36px;
padding: 0 20px;
border: 0;
border-radius: 0;
background: transparent;
color: #334155;
font-size: 13px;
font-weight: 750;
white-space: nowrap;
}
.page-size-dropdown button:hover {
background: #f0fdf4;
color: #059669;
}
.page-size-dropdown button.active {
background: #059669;
color: #fff;
}
.analytics-row {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(260px, 0.48fr);
@@ -473,8 +414,8 @@
align-items: center;
padding: 0 9px;
border-radius: 999px;
background: #ecfdf5;
color: #059669;
background: var(--theme-primary-light-9);
color: var(--theme-primary-active);
font-size: 12px;
font-weight: 700;
}
@@ -540,7 +481,7 @@
}
.trend-line.total {
stroke: #10b981;
stroke: var(--theme-primary);
}
.trend-line.failed {
@@ -644,9 +585,9 @@
}
.status-pill.success {
background: rgba(22, 163, 74, 0.12);
border-color: rgba(22, 163, 74, 0.2);
color: #166534;
background: rgba(var(--success-rgb), 0.12);
border-color: rgba(var(--success-rgb), 0.2);
color: var(--success-active);
}
.status-pill.warning {
@@ -761,8 +702,7 @@
}
.pager,
.page-size,
.page-size-wrap {
.page-size-select {
justify-self: stretch;
}
}

View File

@@ -98,8 +98,8 @@
}
.kpi-badge.down {
background: rgba(22, 163, 74, 0.08);
color: #16a34a;
background: rgba(var(--success-rgb), 0.08);
color: var(--success);
}
.kpi-badge .mdi {
@@ -157,9 +157,13 @@
.card-head h3 {
color: #1e293b;
font-size: 16px;
font-size: 15px;
font-weight: 600;
line-height: 1.35;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
flex: 1;
}
.card-head .mdi {
@@ -169,14 +173,7 @@
}
.card-select {
height: 30px;
min-width: 82px;
padding: 0 8px;
border: 1px solid #cbd5e1;
border-radius: 4px;
background: #fff;
color: #334155;
font-size: 14px;
width: 110px;
}
.panel-note {
@@ -226,8 +223,8 @@
display: grid;
place-items: center;
border-radius: 999px;
background: #e2f6ef;
color: #047857;
background: var(--theme-primary-light-9);
color: var(--theme-primary-active);
font-size: 13px;
font-weight: 700;
flex: 0 0 auto;
@@ -272,8 +269,8 @@
}
.status-tag.success {
background: rgba(16,185,129,.10);
color: #16a34a;
background: rgba(var(--success-rgb), .10);
color: var(--success);
}
.text-link {
@@ -286,7 +283,7 @@
border: 0;
border-top: 1px solid #f1f5f9;
background: transparent;
color: #10b981;
color: var(--theme-primary);
font-size: 14px;
}

View File

@@ -131,8 +131,8 @@
}
.folder-tree button.active {
background: #dcfce7;
color: #059669;
background: var(--theme-primary-light-9);
color: var(--theme-primary-active);
font-weight: 850;
}
@@ -159,10 +159,10 @@
align-items: center;
justify-content: center;
gap: 8px;
border: 1px solid rgba(16, 185, 129, .28);
border: 1px solid rgba(var(--theme-primary-rgb), .28);
border-radius: 8px;
background: #f0fdf4;
color: #059669;
background: var(--theme-primary-light-9);
color: var(--theme-primary-active);
font-size: 13px;
font-weight: 850;
}
@@ -178,9 +178,9 @@
}
.knowledge-sync-btn:not(:disabled):hover {
border-color: rgba(16, 185, 129, 0.38);
background: #ecfdf5;
color: #047857;
border-color: rgba(var(--theme-primary-rgb), 0.38);
background: var(--theme-primary-light-9);
color: var(--theme-primary-active);
}
.folder-sync-meta {
@@ -299,8 +299,8 @@ th {
}
.doc-row.selected {
background: linear-gradient(90deg, rgba(16, 185, 129, 0.08), rgba(59, 130, 246, 0.04));
box-shadow: inset 3px 0 0 #10b981;
background: linear-gradient(90deg, rgba(var(--theme-primary-rgb), 0.08), rgba(59, 130, 246, 0.04));
box-shadow: inset 3px 0 0 var(--theme-primary);
}
.file-name {
@@ -316,7 +316,7 @@ th {
.file-name .word,
.viewer-filetype.word { color: #2563eb; }
.file-name .excel,
.viewer-filetype.excel { color: #10b981; }
.viewer-filetype.excel { color: var(--success); }
.doc-tag {
display: inline-flex;
@@ -342,8 +342,8 @@ th {
}
.state-tag.success {
background: #dcfce7;
color: #059669;
background: var(--success-soft);
color: var(--success-hover);
}
.state-tag.muted {
@@ -394,7 +394,7 @@ th {
}
.more-btn.ingest {
color: #0f766e;
color: var(--theme-primary-active);
}
.more-btn.llm-wiki-view {
@@ -464,14 +464,14 @@ th {
.pager button:hover:not(.active) {
background: #fff;
color: #059669;
color: var(--theme-primary-active);
box-shadow: 0 1px 4px rgba(15, 23, 42, .08);
}
.pager button.active {
background: #059669;
background: var(--theme-primary);
color: #fff;
box-shadow: 0 8px 16px rgba(5, 150, 105, .20);
box-shadow: 0 8px 16px var(--theme-primary-shadow);
}
.list-foot .page-summary {
@@ -484,74 +484,11 @@ th {
color: #64748b;
}
.page-size-wrap {
position: relative;
.page-size-select {
width: 112px;
justify-self: end;
}
.page-size {
justify-self: end;
min-width: 112px;
min-height: 38px;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 9px;
padding: 0 14px;
border: 1px solid #d7e0ea;
border-radius: 10px;
background: #fff;
box-shadow: 0 1px 2px rgba(15, 23, 42, .04);
color: #334155;
font-size: 14px;
font-weight: 750;
white-space: nowrap;
transition: border-color 160ms ease, color 160ms ease;
}
.page-size:hover {
border-color: rgba(16, 185, 129, .32);
color: #0f9f78;
}
.page-size-dropdown {
position: absolute;
bottom: calc(100% + 6px);
right: 0;
z-index: 40;
display: grid;
border: 1px solid #d7e0ea;
border-radius: 10px;
background: #fff;
box-shadow: 0 12px 32px rgba(15, 23, 42, .14);
overflow: hidden;
}
.page-size-dropdown button {
height: 36px;
display: grid;
place-items: center;
border: 0;
border-radius: 0;
background: transparent;
color: #334155;
font-size: 13px;
font-weight: 750;
white-space: nowrap;
padding: 0 20px;
transition: background 120ms ease, color 120ms ease;
}
.page-size-dropdown button:hover {
background: #f0fdf4;
color: #059669;
}
.page-size-dropdown button.active {
background: #059669;
color: #fff;
}
.preview-panel {
height: 100%;
min-height: 0;
@@ -671,7 +608,7 @@ th {
.llm-wiki-overlay {
background:
radial-gradient(circle at top, rgba(15, 118, 110, 0.12), transparent 30%),
radial-gradient(circle at top, rgba(var(--theme-primary-rgb), 0.12), transparent 30%),
rgba(15, 23, 42, 0.58);
}
@@ -706,8 +643,8 @@ th {
}
.llm-wiki-count {
background: #ecfdf5;
color: #047857;
background: var(--theme-primary-light-9);
color: var(--theme-primary-active);
}
.llm-wiki-stat-grid {
@@ -838,8 +775,8 @@ th {
.llm-wiki-editor:focus {
outline: none;
border-color: rgba(16, 185, 129, 0.42);
box-shadow: 0 0 0 4px rgba(16, 185, 129, 0.12);
border-color: rgba(var(--theme-primary-rgb), 0.42);
box-shadow: 0 0 0 4px var(--theme-focus-ring);
}
.llm-wiki-candidate-list {
@@ -1253,8 +1190,7 @@ th {
}
.pager,
.page-size-wrap,
.page-size {
.page-size-select {
justify-self: stretch;
}

View File

@@ -47,8 +47,8 @@
}
.list-search input:focus {
border-color: #10b981;
box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.14);
border-color: var(--theme-primary);
box-shadow: 0 0 0 3px var(--theme-focus-ring);
outline: none;
}
@@ -70,7 +70,7 @@
}
.status-tabs button.active {
color: #059669;
color: var(--theme-primary-active);
}
.status-tabs button.active::after {
@@ -81,7 +81,7 @@
bottom: -1px;
height: 3px;
border-radius: 999px 999px 0 0;
background: #10b981;
background: var(--theme-primary);
}
.list-toolbar {
@@ -108,23 +108,22 @@
padding: 0 18px;
border: 0;
border-radius: 10px;
background: linear-gradient(135deg, #10b981, #059669);
background: var(--theme-gradient-primary);
color: #fff;
font-size: 14px;
font-weight: 800;
white-space: nowrap;
box-shadow: 0 10px 24px rgba(5, 150, 105, 0.2);
box-shadow: 0 10px 24px var(--theme-primary-shadow);
transition: transform 160ms ease, box-shadow 160ms ease, filter 160ms ease;
}
.create-request-btn:hover {
transform: translateY(-1px);
box-shadow: 0 14px 28px rgba(5, 150, 105, 0.24);
box-shadow: 0 14px 28px rgba(var(--theme-primary-rgb), 0.24);
filter: saturate(1.02);
}
.filter-btn,
.page-size {
.filter-btn {
min-height: 38px;
display: inline-flex;
align-items: center;
@@ -232,8 +231,8 @@
}
.date-range-fields input:focus {
border-color: #10b981;
box-shadow: 0 0 0 3px rgba(16, 185, 129, .12);
border-color: var(--theme-primary);
box-shadow: 0 0 0 3px var(--theme-focus-ring);
outline: none;
}
@@ -254,7 +253,7 @@
.apply-btn {
border: 0;
background: #10b981;
background: var(--theme-primary);
color: #fff;
}
@@ -263,10 +262,9 @@
background: #cbd5e1;
}
.filter-btn:hover,
.page-size:hover {
border-color: rgba(16, 185, 129, .32);
color: #0f9f78;
.filter-btn:hover {
border-color: rgba(var(--theme-primary-rgb), .32);
color: var(--theme-primary-active);
}
.hint {
@@ -289,7 +287,7 @@
overflow-y: auto;
border: 1px solid #edf2f7;
border-radius: 10px;
background: linear-gradient(180deg, #fcfefd 0%, #f4f8f6 100%);
background: linear-gradient(180deg, #fcfefd 0%, var(--theme-primary-light-9) 100%);
display: flex;
flex-direction: column;
align-items: stretch;
@@ -316,13 +314,13 @@
padding: 28px 20px;
text-align: center;
color: #64748b;
background: linear-gradient(180deg, #fcfffd 0%, #f5f9f7 100%);
background: linear-gradient(180deg, #fcfffd 0%, var(--theme-primary-light-9) 100%);
align-self: center;
}
.table-state .mdi {
font-size: 28px;
color: #10b981;
color: var(--theme-primary);
}
.table-state strong {
@@ -399,7 +397,7 @@ tbody tr {
}
tbody tr:hover {
background: linear-gradient(90deg, rgba(16, 185, 129, .08), rgba(16, 185, 129, .03));
background: linear-gradient(90deg, rgba(var(--theme-primary-rgb), .08), rgba(var(--theme-primary-rgb), .03));
}
tbody tr:last-child td {
@@ -407,7 +405,7 @@ tbody tr:last-child td {
}
.doc-id {
color: #059669;
color: var(--theme-primary-active);
font-weight: 800;
}
@@ -424,8 +422,8 @@ tbody tr:last-child td {
}
.type-tag.travel {
background: #ecfdf5;
color: #059669;
background: var(--theme-primary-light-9);
color: var(--theme-primary-active);
}
.type-tag.entertainment {
@@ -435,8 +433,8 @@ tbody tr:last-child td {
.type-tag.hotel,
.type-tag.transport {
background: #ecfdf5;
color: #047857;
background: var(--theme-primary-light-9);
color: var(--theme-primary-active);
}
.type-tag.meal {
@@ -479,9 +477,9 @@ tbody tr:last-child td {
}
.status-tag.success {
border-color: #bbf7d0;
background: #ecfdf5;
color: #059669;
border-color: var(--success-line);
background: var(--success-soft);
color: var(--success-hover);
}
.status-tag.warning {
@@ -546,69 +544,23 @@ tbody tr:last-child td {
.pager button:hover:not(.active) {
background: #fff;
color: #059669;
color: var(--theme-primary-active);
box-shadow: 0 1px 4px rgba(15, 23, 42, .08);
}
.pager button.active {
background: #059669;
background: var(--theme-primary);
color: #fff;
box-shadow: 0 8px 16px rgba(5, 150, 105, .20);
box-shadow: 0 8px 16px var(--theme-primary-shadow);
}
.page-nav {
color: #64748b;
}
.page-size {
.page-size-select {
width: 112px;
justify-self: end;
min-width: 112px;
border-radius: 10px;
background: #fff;
box-shadow: 0 1px 2px rgba(15, 23, 42, .04);
}
.page-size-wrap {
position: relative;
justify-self: end;
}
.page-size-dropdown {
position: absolute;
bottom: calc(100% + 6px);
right: 0;
z-index: 40;
display: grid;
border: 1px solid #d7e0ea;
border-radius: 10px;
background: #fff;
box-shadow: 0 12px 32px rgba(15, 23, 42, .14);
overflow: hidden;
}
.page-size-dropdown button {
height: 36px;
display: grid;
place-items: center;
border: 0;
border-radius: 0;
background: transparent;
color: #334155;
font-size: 13px;
font-weight: 750;
white-space: nowrap;
padding: 0 20px;
transition: background 120ms ease, color 120ms ease;
}
.page-size-dropdown button:hover {
background: #f0fdf4;
color: #059669;
}
.page-size-dropdown button.active {
background: #059669;
color: #fff;
}
@media (max-width: 1200px) {
@@ -629,7 +581,7 @@ tbody tr:last-child td {
}
.filter-btn,
.page-size {
.page-size-select {
width: 100%;
}
@@ -643,7 +595,7 @@ tbody tr:last-child td {
}
.pager,
.page-size {
.page-size-select {
justify-self: stretch;
}
}

View File

@@ -1,9 +1,9 @@
/* 设置页表单/卡片/开关供 SettingsView 子面板各自 scoped 引入 */
/* 设置页表单卡片开关样式,供 SettingsView 子面板 scoped 引入 */
.settings-card {
padding: 0;
border: 1px solid #e2e8f0;
border-radius: 12px;
border-radius: 6px;
background: #ffffff;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.02), 0 4px 16px rgba(0, 0, 0, 0.03);
overflow: hidden;
@@ -99,13 +99,12 @@
font-style: normal;
}
.field input,
.field select {
.field input {
width: 100%;
min-height: 44px;
padding: 0 14px;
border: 1px solid #cbd5e1;
border-radius: 12px;
border-radius: 4px;
background: #ffffff;
color: #0f172a;
font-size: 13px;
@@ -117,11 +116,10 @@
color: #94a3b8;
}
.field input:focus,
.field select:focus {
.field input:focus {
outline: none;
border-color: #10b981;
box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.08);
border-color: var(--theme-primary);
box-shadow: 0 0 0 3px var(--theme-focus-ring);
background: #ffffff;
}
@@ -138,7 +136,7 @@
gap: 16px;
padding: 16px 20px;
border: 1px solid #e2e8f0;
border-radius: 16px;
border-radius: 6px;
background: #ffffff;
text-align: left;
transition: all 0.25s ease;
@@ -147,9 +145,9 @@
}
.switch-row:hover {
border-color: rgba(16, 185, 129, 0.25);
background: linear-gradient(180deg, #ffffff 0%, rgba(16, 185, 129, 0.01) 100%);
box-shadow: 0 4px 12px rgba(16, 185, 129, 0.03);
border-color: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.25);
background: linear-gradient(180deg, #ffffff 0%, rgba(var(--theme-primary-rgb, 58, 124, 165), 0.02) 100%);
box-shadow: 0 4px 12px rgba(var(--theme-primary-rgb, 58, 124, 165), 0.04);
transform: translateY(-1px);
}
@@ -198,7 +196,7 @@
}
.switch-btn.active {
background-color: #10b981;
background-color: var(--theme-primary);
}
.switch-btn.active i {

View File

@@ -8,9 +8,9 @@
/* Master Control Hero Card Active Hover & Color */
.hermes-hero-card.active {
border-color: rgba(16, 185, 129, 0.25);
background: linear-gradient(180deg, #ffffff 0%, rgba(16, 185, 129, 0.02) 100%);
box-shadow: 0 1px 3px rgba(16, 185, 129, 0.01), 0 8px 24px rgba(16, 185, 129, 0.05);
border-color: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.25);
background: linear-gradient(180deg, #ffffff 0%, rgba(var(--theme-primary-rgb, 58, 124, 165), 0.02) 100%);
box-shadow: 0 1px 3px rgba(var(--theme-primary-rgb, 58, 124, 165), 0.01), 0 8px 24px rgba(var(--theme-primary-rgb, 58, 124, 165), 0.05);
}
.hermes-hero-card .model-icon-box {
@@ -18,9 +18,9 @@
}
.hermes-hero-card .model-icon-box.active {
background: rgba(16, 185, 129, 0.1);
color: #10b981;
border-color: rgba(16, 185, 129, 0.15);
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
border-color: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.18);
}
/* Pulse Dot */
@@ -37,19 +37,19 @@
}
.status-pulse-dot.active {
background-color: #10b981;
background-color: var(--theme-primary);
animation: pulse-ring 1.8s cubic-bezier(0.455, 0.03, 0.515, 0.955) infinite;
}
@keyframes pulse-ring {
0% {
box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.4);
box-shadow: 0 0 0 0 rgba(var(--theme-primary-rgb, 58, 124, 165), 0.36);
}
70% {
box-shadow: 0 0 0 6px rgba(16, 185, 129, 0);
box-shadow: 0 0 0 6px rgba(var(--theme-primary-rgb, 58, 124, 165), 0);
}
100% {
box-shadow: 0 0 0 0 rgba(16, 185, 129, 0);
box-shadow: 0 0 0 0 rgba(var(--theme-primary-rgb, 58, 124, 165), 0);
}
}
@@ -65,8 +65,8 @@
}
.status-badge.active {
background: rgba(16, 185, 129, 0.1);
color: #10b981;
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
}
/* Task Section */
@@ -93,7 +93,7 @@
padding: 20px;
background: #ffffff;
border: 1px solid #e2e8f0;
border-radius: 16px;
border-radius: 6px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.01), 0 2px 8px rgba(0, 0, 0, 0.02);
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
}
@@ -117,7 +117,7 @@
justify-content: center;
width: 44px;
height: 44px;
border-radius: 12px;
border-radius: 6px;
font-size: 20px;
transition: all 0.3s ease;
}
@@ -185,13 +185,13 @@
.frequency-badge.active {
background: #f0f9ff;
color: #0284c7;
color: #2f6d95;
}
.time-picker-wrapper input[type="time"] {
padding: 5px 8px;
border: 1px solid #e2e8f0;
border-radius: 8px;
border-radius: 4px;
background: #f8fafc;
color: #334155;
font-size: 12px;
@@ -207,9 +207,9 @@
}
.time-picker-wrapper input[type="time"]:focus {
border-color: #10b981;
border-color: var(--theme-primary);
background: #ffffff;
box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.08);
box-shadow: 0 0 0 3px var(--theme-focus-ring);
}
.time-picker-placeholder {

View File

@@ -10,7 +10,7 @@
display: grid;
grid-template-columns: 240px minmax(0, 1fr);
overflow: hidden;
border-radius: 24px;
border-radius: 4px;
background: #ffffff;
border: 1px solid #e2e8f0;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.02), 0 4px 16px rgba(0, 0, 0, 0.03);
@@ -35,7 +35,7 @@
}
.nav-kicker {
color: #10b981;
color: var(--primary);
font-size: 10px;
font-weight: 800;
letter-spacing: 0.08em;
@@ -70,7 +70,7 @@
display: block;
padding: 10px 14px;
border: 1px solid transparent;
border-radius: 12px;
border-radius: 4px;
background: transparent;
color: #334155;
text-align: left;
@@ -84,9 +84,9 @@
}
.settings-nav-item.active {
background: #ecfdf5;
color: #059669;
border-color: rgba(16, 185, 129, 0.12);
background: var(--primary-soft);
color: var(--primary-active);
border-color: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.22);
}
.nav-item-copy {
@@ -109,7 +109,7 @@
}
.settings-nav-item.active .nav-item-copy small {
color: #059669;
color: var(--primary-active);
opacity: 0.8;
}
@@ -144,9 +144,9 @@
align-items: center;
min-height: 24px;
padding: 0 10px;
border-radius: 99px;
background: #ecfdf5;
color: #059669;
border-radius: 4px;
background: var(--primary-soft);
color: var(--primary-active);
font-size: 12px;
font-weight: 700;
}
@@ -181,20 +181,20 @@
gap: 8px;
padding: 0 20px;
border: 0;
border-radius: 12px;
background: #10b981;
border-radius: 4px;
background: var(--primary);
color: #ffffff;
font-size: 13px;
font-weight: 700;
box-shadow: 0 4px 12px rgba(16, 185, 129, 0.15);
box-shadow: 0 4px 12px rgba(var(--theme-primary-rgb, 58, 124, 165), 0.16);
transition: all 0.2s ease;
cursor: pointer;
}
.save-button:hover {
transform: translateY(-1px);
box-shadow: 0 6px 20px rgba(16, 185, 129, 0.25);
background: #059669;
box-shadow: 0 6px 20px rgba(var(--theme-primary-rgb, 58, 124, 165), 0.22);
background: var(--primary-hover);
}
.settings-content {
@@ -219,10 +219,10 @@
justify-content: center;
gap: 6px;
padding: 0 12px;
border: 1px solid rgba(16, 185, 129, 0.2);
border-radius: 10px;
background: #ecfdf5;
color: #059669;
border: 1px solid rgba(var(--theme-primary-rgb, 58, 124, 165), 0.22);
border-radius: 4px;
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
font-size: 12.5px;
font-weight: 700;
transition: all 0.2s ease;
@@ -231,10 +231,10 @@
.test-button:hover:not(:disabled) {
transform: translateY(-1px);
border-color: #10b981;
background: #10b981;
border-color: var(--theme-primary);
background: var(--theme-primary);
color: #ffffff;
box-shadow: 0 4px 12px rgba(16, 185, 129, 0.15);
box-shadow: 0 4px 12px rgba(58, 124, 165, 0.15);
}
.test-button:disabled {
@@ -259,7 +259,7 @@
gap: 10px;
padding: 0 40px 0 14px;
border: 1px solid #cbd5e1;
border-radius: 12px;
border-radius: 4px;
background: #ffffff;
color: #0f172a;
font-size: 13px;
@@ -287,18 +287,18 @@
.session-picker-trigger:hover,
.session-picker-filter.open .session-picker-trigger {
border-color: #10b981;
border-color: var(--theme-primary);
background: #ffffff;
color: #059669;
color: var(--theme-primary-active);
}
.session-picker-filter.open .session-picker-trigger {
box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.08);
box-shadow: 0 0 0 3px var(--theme-focus-ring);
}
.session-picker-filter.open .session-picker-trigger .mdi,
.session-picker-trigger:hover .mdi {
color: #059669;
color: var(--theme-primary-active);
}
.session-picker-popover {
@@ -311,7 +311,7 @@
gap: 12px;
padding: 16px;
border: 1px solid #cbd5e1;
border-radius: 12px;
border-radius: 4px;
background: #ffffff;
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.05), 0 8px 10px -6px rgba(0, 0, 0, 0.05);
}
@@ -335,7 +335,7 @@
display: grid;
place-items: center;
border: 0;
border-radius: 8px;
border-radius: 4px;
background: transparent;
color: #64748b;
cursor: pointer;
@@ -360,7 +360,7 @@
align-items: center;
padding: 0 12px;
border: 1px solid #cbd5e1;
border-radius: 8px;
border-radius: 4px;
background: #ffffff;
color: #334155;
font-size: 13px;
@@ -372,9 +372,123 @@
.session-picker-option:hover,
.session-picker-option.active {
border-color: #10b981;
background: #ecfdf5;
color: #059669;
border-color: var(--theme-primary);
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
}
.skin-option-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 12px;
}
.skin-option {
min-height: 104px;
display: grid;
grid-template-columns: auto minmax(0, 1fr) auto;
align-items: center;
gap: 14px;
padding: 16px;
border: 1px solid #d8dee8;
border-radius: 4px;
background: #ffffff;
color: #334155;
text-align: left;
transition:
border-color 160ms var(--ease),
background 160ms var(--ease),
box-shadow 160ms var(--ease);
}
.skin-option:hover,
.skin-option.active {
border-color: var(--primary);
background: var(--theme-primary-light-9);
box-shadow: 0 0 0 3px var(--theme-focus-ring);
}
.skin-swatch {
width: 64px;
height: 38px;
display: grid;
grid-template-columns: 1.3fr 1fr 1fr 1fr;
grid-template-rows: 1fr;
border: 1px solid #d8dee8;
border-radius: 4px;
overflow: hidden;
background: #ffffff;
}
.skin-swatch i + i {
border-left: 1px solid rgba(255, 255, 255, 0.72);
}
.skin-copy {
display: grid;
gap: 4px;
}
.skin-copy strong {
color: #111827;
font-size: 14px;
font-weight: 700;
}
.skin-copy small {
color: #64748b;
font-size: 12px;
line-height: 1.45;
}
.skin-current {
min-height: 24px;
display: inline-flex;
align-items: center;
padding: 0 8px;
border: 1px solid rgba(var(--theme-primary-rgb, 58, 124, 165), 0.26);
border-radius: 4px;
background: var(--primary-soft);
color: var(--primary-active);
font-size: 12px;
font-weight: 700;
}
.skin-preview-panel {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
padding: 16px;
border: 1px solid #d8dee8;
border-radius: 4px;
background: linear-gradient(180deg, #ffffff 0%, var(--theme-primary-light-9) 100%);
}
.skin-preview-panel div {
display: grid;
gap: 4px;
}
.skin-preview-panel strong {
color: #111827;
font-size: 14px;
}
.skin-preview-panel span {
color: #64748b;
font-size: 12px;
}
.skin-preview-action {
min-height: 34px;
padding: 0 14px;
border: 1px solid var(--primary);
border-radius: 4px;
background: var(--theme-gradient-primary);
color: #fff;
font-size: 13px;
font-weight: 700;
}
.secret-bound-state {
@@ -382,7 +496,7 @@
display: inline-flex;
align-items: center;
gap: 6px;
color: #059669;
color: var(--theme-primary-active);
font-size: 12px;
font-weight: 600;
line-height: 1.45;
@@ -398,7 +512,7 @@
gap: 8px;
margin-top: 16px;
padding: 12px 14px;
border-radius: 12px;
border-radius: 6px;
font-size: 12px;
font-weight: 600;
line-height: 1.6;
@@ -410,13 +524,13 @@
}
.test-feedback.is-success {
background: #ecfdf5;
color: #059669;
background: var(--success-soft);
color: var(--success-hover);
}
.test-feedback.is-error {
background: #fef2f2;
color: #b91c1c;
background: var(--danger-soft);
color: var(--danger-active);
}
.test-feedback.is-testing {
@@ -434,13 +548,13 @@
display: grid;
place-items: center;
border: 1px dashed #cbd5e1;
border-radius: 20px;
border-radius: 8px;
background:
linear-gradient(45deg, #f8fafc 25%, transparent 25%, transparent 75%, #f8fafc 75%, #f8fafc),
linear-gradient(45deg, #f8fafc 25%, transparent 25%, transparent 75%, #f8fafc 75%, #f8fafc);
background-position: 0 0, 9px 9px;
background-size: 18px 18px;
color: #10b981;
color: var(--theme-primary);
font-size: 32px;
}
@@ -450,9 +564,9 @@
align-items: center;
gap: 18px;
padding: 24px;
border: 1px solid rgba(16, 185, 129, 0.15);
border-radius: 20px;
background: linear-gradient(135deg, rgba(16, 185, 129, 0.04) 0%, rgba(59, 130, 246, 0.02) 100%);
border: 1px solid rgba(58, 124, 165, 0.15);
border-radius: 8px;
background: linear-gradient(135deg, rgba(var(--theme-primary-rgb, 58, 124, 165), 0.06) 0%, rgba(var(--theme-secondary-rgb, 37, 99, 235), 0.03) 100%);
}
.preview-icon {
@@ -460,11 +574,11 @@
height: 64px;
display: grid;
place-items: center;
border-radius: 16px;
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
border-radius: 8px;
background: var(--theme-gradient-primary);
color: #ffffff;
font-size: 28px;
box-shadow: 0 8px 20px rgba(16, 185, 129, 0.15);
box-shadow: 0 8px 20px var(--theme-primary-shadow);
}
.preview-copy strong {
@@ -495,9 +609,9 @@
align-items: center;
justify-content: center;
padding: 0 10px;
border-radius: 999px;
background: #ecfdf5;
color: #059669;
border-radius: 4px;
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
font-size: 11px;
font-weight: 700;
white-space: nowrap;
@@ -515,7 +629,7 @@
min-height: 34px;
padding: 0 16px;
border: 1px solid #cbd5e1;
border-radius: 999px;
border-radius: 4px;
background: #ffffff;
color: #64748b;
font-size: 12px;
@@ -525,16 +639,16 @@
}
.level-chip:hover {
border-color: #10b981;
color: #10b981;
background: #ecfdf5;
border-color: var(--theme-primary);
color: var(--theme-primary);
background: var(--theme-primary-soft);
}
.level-chip.active {
border-color: #10b981;
background: #10b981;
border-color: var(--theme-primary);
background: var(--theme-primary);
color: #ffffff;
box-shadow: 0 4px 12px rgba(16, 185, 129, 0.2);
box-shadow: 0 4px 12px var(--theme-primary-shadow);
}
.range-shell {
@@ -544,13 +658,13 @@
gap: 14px;
padding: 0 14px;
border: 1px solid #cbd5e1;
border-radius: 12px;
border-radius: 4px;
background: #ffffff;
}
.range-shell input[type='range'] {
flex: 1 1 auto;
accent-color: #10b981;
accent-color: var(--theme-primary);
cursor: pointer;
}
@@ -566,7 +680,7 @@
margin-bottom: 20px;
}
/* 大语言模型配置卡片特定图标与标题排版 */
/* 大语言模型配置卡片图标与标题排版 */
.card-title-with-icon {
display: flex;
align-items: center;
@@ -578,7 +692,7 @@
height: 40px;
display: grid;
place-items: center;
border-radius: 10px;
border-radius: 6px;
font-size: 18px;
flex: 0 0 auto;
background: #f8fafc;
@@ -681,7 +795,7 @@
.settings-card {
padding: 20px 16px;
border-radius: 16px;
border-radius: 6px;
}
.settings-nav {

View File

@@ -3,8 +3,8 @@
display: grid;
grid-template-columns: minmax(320px, 392px) minmax(0, 1fr);
background:
radial-gradient(circle at top left, rgba(16, 185, 129, 0.24), transparent 24rem),
radial-gradient(circle at 36% 14%, rgba(16, 185, 129, 0.16), transparent 18rem),
radial-gradient(circle at top left, rgba(var(--theme-primary-rgb), 0.24), transparent 24rem),
radial-gradient(circle at 36% 14%, rgba(var(--theme-primary-rgb), 0.16), transparent 18rem),
linear-gradient(135deg, #04110d 0%, #0b1f18 26%, #10281f 26%, #eef5f1 26%, #f6fbf8 100%);
}
@@ -38,10 +38,10 @@
inset: 0;
border-radius: 18px;
background:
linear-gradient(145deg, rgba(209, 250, 229, 0.96), rgba(52, 211, 153, 0.88)),
linear-gradient(145deg, rgba(16, 185, 129, 0.4), rgba(5, 150, 105, 0.6));
linear-gradient(145deg, color-mix(in srgb, var(--theme-primary-soft) 96%, white), rgba(var(--theme-primary-rgb), 0.58)),
linear-gradient(145deg, rgba(var(--theme-primary-rgb), 0.4), rgba(var(--theme-primary-rgb), 0.6));
box-shadow:
0 18px 36px rgba(16, 185, 129, 0.2),
0 18px 36px rgba(var(--theme-primary-rgb), 0.2),
inset 0 1px 0 rgba(255, 255, 255, 0.46);
transform: rotate(-8deg);
}
@@ -51,7 +51,7 @@
position: absolute;
inset: 7px;
border-radius: 14px;
border: 1px solid rgba(4, 120, 87, 0.22);
border: 1px solid rgba(var(--theme-primary-rgb), 0.22);
}
.setup-brand-core {
@@ -64,7 +64,7 @@
align-items: center;
justify-content: center;
background: rgba(3, 32, 24, 0.92);
color: #d1fae5;
color: var(--theme-primary-soft-strong);
font-size: 15px;
font-weight: 800;
letter-spacing: 0.14em;
@@ -77,22 +77,22 @@
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
color: rgba(167, 243, 208, 0.86);
color: color-mix(in srgb, var(--theme-primary-soft) 86%, transparent);
}
.setup-kicker-light {
color: rgba(209, 250, 229, 0.82);
color: color-mix(in srgb, var(--theme-primary-soft) 82%, transparent);
}
.setup-context h1 {
color: #f4fff8;
color: #f8fafc;
font-size: clamp(1.9rem, 2.4vw, 2.5rem);
line-height: 1.08;
text-shadow: 0 12px 36px rgba(2, 12, 8, 0.34);
}
.setup-lead {
color: rgba(220, 252, 231, 0.84);
color: rgba(226, 232, 240, 0.84);
font-size: 14px;
line-height: 1.8;
}
@@ -105,13 +105,13 @@
.setup-nav-item {
width: 100%;
padding: 14px 14px 14px 12px;
border: 1px solid rgba(110, 231, 183, 0.12);
border: 1px solid rgba(var(--theme-primary-rgb), 0.14);
border-radius: 8px;
display: grid;
grid-template-columns: 44px minmax(0, 1fr) 18px;
align-items: center;
gap: 12px;
background: linear-gradient(160deg, rgba(10, 23, 18, 0.82), rgba(15, 39, 31, 0.72));
background: linear-gradient(160deg, rgba(10, 18, 27, 0.82), rgba(16, 31, 43, 0.72));
color: inherit;
text-align: left;
transition: transform 160ms ease, border-color 160ms ease, box-shadow 160ms ease;
@@ -119,16 +119,16 @@
.setup-nav-item:hover {
transform: translateY(-1px);
border-color: rgba(110, 231, 183, 0.22);
border-color: rgba(var(--theme-primary-rgb), 0.28);
}
.setup-nav-item.is-active {
border-color: rgba(16, 185, 129, 0.4);
border-color: rgba(var(--theme-primary-rgb), 0.4);
box-shadow: 0 14px 28px rgba(3, 10, 7, 0.22);
}
.setup-nav-item.is-complete {
background: linear-gradient(160deg, rgba(8, 31, 23, 0.96), rgba(12, 58, 44, 0.86));
background: linear-gradient(160deg, rgba(10, 25, 36, 0.96), rgba(20, 50, 68, 0.86));
}
.setup-nav-index {
@@ -138,8 +138,8 @@
display: inline-flex;
align-items: center;
justify-content: center;
background: rgba(16, 185, 129, 0.16);
color: #d1fae5;
background: rgba(var(--theme-primary-rgb), 0.16);
color: var(--theme-primary-soft-strong);
font-size: 12px;
font-weight: 700;
letter-spacing: 0.08em;
@@ -151,12 +151,12 @@
}
.setup-nav-copy strong {
color: #f0fdf4;
color: var(--theme-primary-soft);
font-size: 14px;
}
.setup-nav-copy small {
color: rgba(209, 250, 229, 0.72);
color: color-mix(in srgb, var(--theme-primary-soft) 72%, transparent);
font-size: 12px;
line-height: 1.55;
}
@@ -175,13 +175,13 @@
}
.setup-progress strong {
color: #f0fdf4;
color: var(--theme-primary-soft);
font-size: 15px;
}
.setup-progress p {
margin-top: 8px;
color: rgba(209, 250, 229, 0.72);
color: color-mix(in srgb, var(--theme-primary-soft) 72%, transparent);
font-size: 13px;
line-height: 1.65;
}
@@ -195,7 +195,7 @@
}
.setup-complete p {
color: rgba(209, 250, 229, 0.76);
color: color-mix(in srgb, var(--theme-primary-soft) 76%, transparent);
font-size: 13px;
line-height: 1.6;
}
@@ -208,7 +208,7 @@
display: flex;
align-items: center;
gap: 8px;
color: rgba(209, 250, 229, 0.86);
color: color-mix(in srgb, var(--theme-primary-soft) 86%, transparent);
font-size: 13px;
line-height: 1.5;
}
@@ -219,7 +219,7 @@
align-content: start;
gap: 24px;
background:
radial-gradient(circle at top left, rgba(16, 185, 129, 0.08), transparent 16rem),
radial-gradient(circle at top left, rgba(var(--theme-primary-rgb), 0.08), transparent 16rem),
linear-gradient(180deg, rgba(255, 255, 255, 0.04), rgba(255, 255, 255, 0));
}
@@ -229,7 +229,7 @@
align-items: flex-start;
gap: 16px;
padding: 20px 22px;
border: 1px solid rgba(16, 185, 129, 0.14);
border: 1px solid rgba(var(--theme-primary-rgb), 0.14);
border-radius: 8px;
background: linear-gradient(135deg, #063b2e, #0f5f49);
box-shadow: 0 16px 34px rgba(6, 59, 46, 0.16);
@@ -253,20 +253,20 @@
min-height: 32px;
padding: 0 12px;
border-radius: 999px;
background: rgba(240, 253, 244, 0.14);
color: #d1fae5;
background: rgba(255, 255, 255, 0.12);
color: var(--theme-primary-soft-strong);
font-size: 12px;
font-weight: 700;
border: 1px solid rgba(209, 250, 229, 0.18);
border: 1px solid color-mix(in srgb, var(--theme-primary-soft) 18%, transparent);
}
.setup-chip.is-success {
background: rgba(16, 185, 129, 0.22);
background: rgba(var(--theme-primary-rgb), 0.22);
}
.setup-form {
padding: 30px 32px;
border: 1px solid rgba(16, 185, 129, 0.18);
border: 1px solid rgba(var(--theme-primary-rgb), 0.18);
border-radius: 8px;
background: linear-gradient(180deg, rgba(244, 255, 248, 0.98), rgba(255, 255, 255, 0.94));
box-shadow: 0 24px 60px rgba(15, 23, 42, 0.12), 0 1px 0 rgba(255, 255, 255, 0.6) inset;
@@ -329,7 +329,7 @@
.optional-block {
padding: 18px 18px 0;
border: 1px dashed rgba(16, 185, 129, 0.22);
border: 1px dashed rgba(var(--theme-primary-rgb), 0.22);
border-radius: 8px;
background: rgba(240, 253, 244, 0.52);
}
@@ -353,8 +353,8 @@
border-radius: 999px;
display: inline-flex;
align-items: center;
background: rgba(16, 185, 129, 0.12);
color: #047857;
background: rgba(var(--theme-primary-rgb), 0.12);
color: var(--theme-primary-active);
font-size: 12px;
font-weight: 700;
}
@@ -375,8 +375,8 @@
}
.field input:focus {
border-color: #10b981;
box-shadow: 0 0 0 4px rgba(16, 185, 129, 0.12);
border-color: var(--theme-primary);
box-shadow: 0 0 0 4px var(--theme-focus-ring);
}
.field-span-2 {
@@ -406,7 +406,7 @@
inset: 0 auto auto 0;
width: 100%;
height: 3px;
background: linear-gradient(90deg, rgba(209, 250, 229, 0.92), rgba(16, 185, 129, 0.48));
background: linear-gradient(90deg, color-mix(in srgb, var(--theme-primary-soft) 92%, transparent), rgba(var(--theme-primary-rgb), 0.48));
}
.setup-runtime article:nth-child(1) {
@@ -453,9 +453,9 @@
}
.setup-status.is-success {
border: 1px solid rgba(16, 185, 129, 0.18);
background: #ecfdf5;
color: #047857;
border: 1px solid rgba(var(--theme-primary-rgb), 0.18);
background: var(--success-soft);
color: var(--success-active);
}
.setup-status.is-danger {
@@ -502,19 +502,19 @@
}
.primary-btn {
background: linear-gradient(135deg, #10b981, #0f766e);
background: var(--theme-gradient-primary);
color: #fff;
box-shadow: 0 14px 28px rgba(16, 185, 129, 0.24);
box-shadow: 0 14px 28px rgba(var(--theme-primary-rgb), 0.24);
}
.secondary-btn {
background: rgba(240, 253, 244, 0.94);
color: #1f4f41;
border-color: rgba(16, 185, 129, 0.18);
border-color: rgba(var(--theme-primary-rgb), 0.18);
}
.secondary-btn-strong {
background: linear-gradient(135deg, rgba(16, 185, 129, 0.14), rgba(5, 150, 105, 0.12));
background: linear-gradient(135deg, rgba(var(--theme-primary-rgb), 0.14), rgba(var(--theme-primary-rgb), 0.12));
color: #065f46;
}
@@ -553,7 +553,7 @@
grid-template-rows: auto minmax(0, 1fr);
gap: 20px;
background:
radial-gradient(circle at top right, rgba(16, 185, 129, 0.16), transparent 18rem),
radial-gradient(circle at top right, rgba(var(--theme-primary-rgb), 0.16), transparent 18rem),
linear-gradient(180deg, #05251d 0%, #081611 100%);
box-shadow: 0 30px 80px rgba(0, 0, 0, 0.42);
}
@@ -574,7 +574,7 @@
.setup-startup-head span {
display: block;
margin-top: 8px;
color: rgba(209, 250, 229, 0.78);
color: color-mix(in srgb, var(--theme-primary-soft) 78%, transparent);
font-size: 13px;
line-height: 1.6;
}
@@ -628,7 +628,7 @@
.setup-startup-step .pi {
margin-top: 2px;
color: rgba(209, 250, 229, 0.46);
color: color-mix(in srgb, var(--theme-primary-soft) 46%, transparent);
}
.setup-startup-step strong {
@@ -639,7 +639,7 @@
.setup-startup-step span {
display: block;
margin-top: 4px;
color: rgba(209, 250, 229, 0.68);
color: color-mix(in srgb, var(--theme-primary-soft) 68%, transparent);
font-size: 12px;
line-height: 1.5;
}
@@ -653,11 +653,11 @@
}
.setup-startup-step.is-success {
border-color: rgba(16, 185, 129, 0.32);
border-color: rgba(var(--theme-primary-rgb), 0.32);
}
.setup-startup-step.is-success .pi {
color: #34d399;
color: var(--theme-primary-light-5);
}
.setup-startup-step.is-error {
@@ -714,7 +714,7 @@
.setup-page {
grid-template-columns: 1fr;
background:
radial-gradient(circle at top right, rgba(16, 185, 129, 0.2), transparent 22rem),
radial-gradient(circle at top right, rgba(var(--theme-primary-rgb), 0.2), transparent 22rem),
linear-gradient(180deg, #04110d 0%, #10281f 36%, #eef5f1 36%, #f6fbf8 100%);
}

View File

@@ -35,8 +35,8 @@
}
.travel-calculator-field input:focus {
border-color: rgba(59, 130, 246, 0.46);
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
border-color: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.46);
box-shadow: 0 0 0 3px rgba(var(--theme-primary-rgb, 58, 124, 165), 0.1);
outline: none;
}
@@ -145,7 +145,7 @@
.composer-date-apply-btn {
border: 0;
background: linear-gradient(135deg, #22c55e, #10b981);
background: linear-gradient(135deg, var(--theme-primary), var(--theme-primary-active));
color: #fff;
}
@@ -182,9 +182,9 @@
min-height: 28px;
padding: 0 8px 0 10px;
border-radius: 999px;
border: 1px solid rgba(59, 130, 246, 0.28);
background: linear-gradient(135deg, rgba(59, 130, 246, 0.14), rgba(16, 185, 129, 0.12));
color: #1d4ed8;
border: 1px solid rgba(var(--theme-primary-rgb, 58, 124, 165), 0.28);
background: linear-gradient(135deg, rgba(var(--theme-primary-rgb, 58, 124, 165), 0.14), rgba(var(--theme-primary-rgb, 58, 124, 165), 0.08));
color: var(--theme-primary-active);
font-size: 11px;
font-weight: 800;
flex: none;
@@ -192,7 +192,7 @@
.composer-biz-time-tag i {
font-size: 14px;
color: #2563eb;
color: var(--theme-primary);
}
.composer-biz-time-tag-label {
@@ -422,10 +422,10 @@
}
.send-btn {
background: linear-gradient(135deg, #22c55e, #10b981);
background: linear-gradient(135deg, var(--theme-primary), var(--theme-primary-active));
color: #fff;
font-size: var(--wb-fs-tool-icon);
box-shadow: 0 8px 18px rgba(16, 185, 129, 0.18);
box-shadow: 0 8px 18px rgba(var(--theme-primary-rgb, 58, 124, 165), 0.18);
}
.send-btn:disabled {
@@ -468,8 +468,8 @@
height: 150px;
border-radius: 0 0 0 140px;
background:
radial-gradient(circle at 0 100%, rgba(16, 185, 129, 0.14), transparent 54%),
linear-gradient(135deg, rgba(16, 185, 129, 0.14), rgba(96, 165, 250, 0.06));
radial-gradient(circle at 0 100%, rgba(var(--theme-primary-rgb, 58, 124, 165), 0.14), transparent 54%),
linear-gradient(135deg, rgba(var(--theme-primary-rgb, 58, 124, 165), 0.14), rgba(96, 165, 250, 0.06));
opacity: 0.9;
pointer-events: none;
}
@@ -502,11 +502,11 @@
align-items: center;
padding: 0 10px;
border-radius: 999px;
background: rgba(240, 253, 244, 0.95);
color: #059669;
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
font-size: 11px;
font-weight: 800;
border: 1px solid rgba(16, 185, 129, 0.12);
border: 1px solid rgba(var(--theme-primary-rgb, 58, 124, 165), 0.16);
}
.review-insight-title-row {
@@ -617,8 +617,8 @@
}
.intent-pill.draft {
background: #ecfdf5;
color: #059669;
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
}
.intent-pill.approval {
@@ -719,7 +719,7 @@
}
.review-side-intent-row i {
color: #059669;
color: var(--theme-primary);
font-size: 16px;
}
@@ -765,9 +765,9 @@
.review-side-metric-card.editable:hover,
.review-side-metric-card.editing {
border-color: rgba(16, 185, 129, 0.34);
border-color: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.34);
background: #ffffff;
box-shadow: 0 4px 12px rgba(16, 185, 129, 0.08);
box-shadow: 0 4px 12px rgba(var(--theme-primary-rgb, 58, 124, 165), 0.08);
transform: translateY(-1px);
}
@@ -786,9 +786,9 @@
.review-side-metric-card.editable:hover .review-side-metric-icon,
.review-side-metric-card.editing .review-side-metric-icon {
color: #059669;
border-color: rgba(16, 185, 129, 0.22);
background: #ecfdf5;
color: var(--theme-primary);
border-color: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.22);
background: var(--theme-primary-soft);
}
.review-side-metric-card.invalid .review-side-metric-icon {
@@ -820,7 +820,7 @@
width: 100%;
min-height: 34px;
padding: 0 10px;
border: 1px solid rgba(16, 185, 129, 0.2);
border: 1px solid rgba(var(--theme-primary-rgb, 58, 124, 165), 0.2);
border-radius: 10px;
background: rgba(255, 255, 255, 0.96);
color: #0f172a;
@@ -844,8 +844,8 @@
.review-inline-input:focus {
outline: none;
border-color: rgba(16, 185, 129, 0.42);
box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.08);
border-color: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.42);
box-shadow: 0 0 0 3px rgba(var(--theme-primary-rgb, 58, 124, 165), 0.08);
}
.review-inline-select-list {
@@ -869,9 +869,9 @@
}
.review-inline-select-option.active {
border-color: rgba(16, 185, 129, 0.36);
background: rgba(240, 253, 244, 0.94);
color: #047857;
border-color: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.36);
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
}
.review-inline-error {
@@ -900,7 +900,7 @@
}
.review-side-edit-hint.upload {
color: #059669;
color: var(--theme-primary-active);
}
.review-side-metric-card:hover .review-side-edit-hint,
@@ -935,7 +935,7 @@
}
.review-side-confidence {
color: #10b981;
color: var(--success);
font-size: 12px;
font-weight: 900;
}
@@ -961,9 +961,9 @@
}
.review-side-category-card.active {
border-color: rgba(52, 211, 153, 0.62);
background: rgba(240, 253, 244, 0.9);
box-shadow: inset 0 0 0 1px rgba(16, 185, 129, 0.08);
border-color: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.5);
background: var(--theme-primary-soft);
box-shadow: inset 0 0 0 1px rgba(var(--theme-primary-rgb, 58, 124, 165), 0.08);
}
.review-side-category-copy {
@@ -988,7 +988,7 @@
}
.review-side-group-check {
color: #10b981;
color: var(--theme-primary);
font-size: 18px;
}
@@ -1014,9 +1014,9 @@
}
.review-other-category-option.active {
border-color: rgba(16, 185, 129, 0.36);
background: rgba(240, 253, 244, 0.94);
color: #047857;
border-color: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.36);
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
}
.review-side-risk-card {
@@ -1064,8 +1064,8 @@
display: grid;
place-items: center;
border-radius: 10px;
background: rgba(14, 165, 233, 0.12);
color: #0284c7;
background: rgba(58, 124, 165, 0.12);
color: #2f6d95;
font-size: 16px;
}
@@ -1080,8 +1080,8 @@
}
.review-side-risk-item.low .review-side-risk-icon {
background: rgba(14, 165, 233, 0.12);
color: #0284c7;
background: rgba(58, 124, 165, 0.12);
color: #2f6d95;
}
.review-side-risk-copy {
@@ -1121,7 +1121,7 @@
padding: 0;
border: 0;
background: transparent;
color: #059669;
color: var(--theme-primary-active);
font-size: 12px;
font-weight: 850;
}
@@ -1173,10 +1173,10 @@
align-items: center;
gap: 6px;
padding: 0 14px;
border: 1px solid rgba(16, 185, 129, 0.22);
border: 1px solid rgba(var(--theme-primary-rgb, 58, 124, 165), 0.22);
border-radius: 999px;
background: rgba(255, 255, 255, 0.94);
color: #059669;
color: var(--theme-primary-active);
font-size: 12px;
font-weight: 850;
box-shadow: 0 8px 18px rgba(15, 23, 42, 0.08);
@@ -1270,8 +1270,8 @@
align-items: center;
padding: 0 10px;
border-radius: 999px;
background: rgba(236, 253, 245, 0.92);
color: #059669;
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
font-size: 11px;
font-weight: 850;
}
@@ -1296,9 +1296,9 @@
}
.review-document-meta-chip.confidence {
background: rgba(236, 253, 245, 0.92);
color: #047857;
border-color: rgba(167, 243, 208, 0.92);
background: var(--success-soft);
color: var(--success);
border-color: color-mix(in srgb, var(--success) 28%, transparent);
}
.review-document-scroll {
@@ -1404,8 +1404,8 @@
.review-document-edit-field input:focus,
.review-document-edit-field textarea:focus {
outline: none;
border-color: rgba(16, 185, 129, 0.36);
box-shadow: 0 0 0 4px rgba(16, 185, 129, 0.08);
border-color: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.36);
box-shadow: 0 0 0 4px rgba(var(--theme-primary-rgb, 58, 124, 165), 0.08);
}
.review-document-edit-field textarea {
@@ -1482,8 +1482,8 @@
}
.knowledge-question-btn:hover:not(:disabled) {
border-color: rgba(16, 185, 129, 0.3);
background: rgba(240, 253, 244, 0.9);
border-color: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.3);
background: var(--theme-primary-soft);
transform: translateY(-1px);
}
@@ -1495,7 +1495,7 @@
.knowledge-question-btn i {
justify-self: end;
color: #059669;
color: var(--theme-primary);
font-size: 16px;
}

View File

@@ -24,8 +24,8 @@
}
.status-pill.success {
background: #ecfdf5;
color: #059669;
background: var(--success-soft);
color: var(--success);
}
.status-pill.warning {
@@ -94,9 +94,12 @@
background: #cbd5e1;
}
.timeline-list li.done .timeline-dot,
.timeline-list li.done .timeline-dot {
background: var(--success);
}
.timeline-list li.current .timeline-dot {
background: #10b981;
background: var(--theme-primary);
}
.timeline-list strong {
@@ -347,8 +350,8 @@
background: linear-gradient(180deg, rgba(255, 255, 255, 0.82) 0%, rgba(251, 248, 243, 0.82) 100%);
}
/* 已删除review-alert-chip-row 相关样式(冗余气泡) */
/* 已删除主对话框中的风险提示与右侧栏重复,已移除) */
/* 已删除 review-alert-chip-row 相关冗余样式 */
/* 已删除主对话框中的风险提示,避免与右侧栏重复 */
/* 风险提示样式已统一到 review-pending-item */
.review-risk-brief-list {
@@ -402,12 +405,12 @@
display: grid;
place-items: center;
border-radius: 10px;
background: rgba(236, 253, 245, 0.95);
color: #059669;
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
font-size: 16px;
}
/* 风险级别图标样式(已删除主对话框中的风险提示,保留样式备用 */
/* 风险级别图标样式保留备用 */
.review-pending-icon.high {
background: rgba(254, 226, 226, 0.95);
color: #dc2626;
@@ -473,9 +476,9 @@
}
.review-pending-status.ready {
background: rgba(240, 253, 244, 0.96);
color: #059669;
border: 1px solid #86efac;
background: var(--success-soft);
color: var(--success);
border: 1px solid color-mix(in srgb, var(--success) 28%, transparent);
}
.review-footer-actions {
@@ -507,10 +510,10 @@
}
.review-footer-btn.primary {
border-color: rgba(16, 185, 129, 0.26);
background: linear-gradient(135deg, #10b981, #059669);
border-color: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.26);
background: linear-gradient(135deg, var(--theme-primary), var(--theme-primary-active));
color: #fff;
box-shadow: 0 6px 14px rgba(16, 185, 129, 0.16);
box-shadow: 0 6px 14px rgba(var(--theme-primary-rgb, 58, 124, 165), 0.16);
}
.review-footer-btn:disabled {
@@ -555,10 +558,10 @@
.review-inline-btn.primary,
.primary-dialog-btn {
border: 1px solid rgba(16, 185, 129, 0.22);
background: linear-gradient(135deg, #10b981, #059669);
border: 1px solid rgba(var(--theme-primary-rgb, 58, 124, 165), 0.22);
background: linear-gradient(135deg, var(--theme-primary), var(--theme-primary-active));
color: #fff;
box-shadow: 0 10px 22px rgba(16, 185, 129, 0.18);
box-shadow: 0 10px 22px rgba(var(--theme-primary-rgb, 58, 124, 165), 0.18);
}
.review-inline-btn.secondary,
@@ -593,7 +596,7 @@
.review-inline-guidance {
margin: 0;
color: #0f766e;
color: var(--theme-primary-active);
font-size: 12px;
line-height: 1.7;
}
@@ -608,8 +611,8 @@
}
.review-status-banner.ready {
border-color: #bbf7d0;
background: linear-gradient(180deg, #f5fffa 0%, #ecfdf5 100%);
border-color: color-mix(in srgb, var(--success) 28%, transparent);
background: linear-gradient(180deg, #ffffff 0%, var(--success-soft) 100%);
}
.review-status-banner.pending {
@@ -921,8 +924,8 @@
}
.action-card.primary {
border-color: #bbf7d0;
background: #f0fdf4;
border-color: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.28);
background: var(--theme-primary-soft);
}
.action-card.secondary {
@@ -987,7 +990,7 @@
}
.recognition-bubble-label {
color: #0f766e;
color: var(--theme-primary-active);
font-size: 11px;
font-weight: 850;
letter-spacing: 0.02em;
@@ -1064,7 +1067,7 @@
width: min(720px, calc(100vw - 40px));
border-radius: 24px;
background:
radial-gradient(circle at top right, rgba(16, 185, 129, 0.08), transparent 28%),
radial-gradient(circle at top right, rgba(var(--theme-primary-rgb, 58, 124, 165), 0.08), transparent 28%),
linear-gradient(180deg, #fbfdff 0%, #f6f9fc 100%);
box-shadow:
0 24px 80px rgba(15, 23, 42, 0.22),

View File

@@ -14,7 +14,7 @@
overflow: hidden;
border-radius: 24px;
background:
radial-gradient(circle at top right, rgba(16, 185, 129, 0.08), transparent 28%),
radial-gradient(circle at top right, rgba(var(--theme-primary-rgb, 58, 124, 165), 0.08), transparent 28%),
linear-gradient(180deg, #fbfdff 0%, #f6f9fc 100%);
box-shadow:
0 24px 80px rgba(15, 23, 42, 0.22),
@@ -109,14 +109,14 @@
.welcome-quick-action-btn:hover:not(:disabled) {
transform: translateY(-1px);
border-color: #10b981;
background: #ecfdf5;
color: #059669;
box-shadow: 0 4px 12px rgba(16, 185, 129, 0.1);
border-color: var(--theme-primary);
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
box-shadow: 0 4px 12px rgba(var(--theme-primary-rgb, 58, 124, 165), 0.1);
}
.welcome-quick-action-btn:hover:not(:disabled) i {
color: #059669;
color: var(--theme-primary-active);
}
.welcome-quick-action-btn:disabled {
@@ -136,7 +136,7 @@
}
.welcome-card i {
color: #10b981;
color: var(--theme-primary);
font-size: var(--wb-fs-welcome);
}
@@ -177,7 +177,7 @@
transform: translateY(8px);
}
/* 笔记本 / 中等屏:工作台正文字号整体下调一档 */
/* 笔记本中等屏:工作台正文字号整体下调一档 */
@media (max-width: 1680px) {
.assistant-modal-stage {
--wb-fs-title: 19px;
@@ -226,7 +226,7 @@
}
}
/* 大屏:左右分栏右侧详情区宽度随视口收缩 */
/* 大屏:左右分栏右侧详情区宽度随视口收缩 */
@media (min-width: 1441px) and (max-width: 1680px) {
.insight-panel-shell {
width: clamp(280px, 26vw, 360px);
@@ -272,7 +272,7 @@
}
}
/* 矮屏笔记本(如 1366×768:压缩顶栏与间距,把高度留给对话列表 */
/* 矮屏笔记本:压缩顶栏与间距,把高度留给对话列表 */
@media (max-height: 820px) {
.assistant-modal-stage {
--wb-fs-title: 17px;
@@ -386,24 +386,6 @@
padding: 16px;
}
.message-row,
.message-row.user {
grid-template-columns: 34px minmax(0, 1fr);
}
.message-row.user .message-avatar {
order: 0;
}
.message-row.user .message-bubble {
order: 0;
justify-self: stretch;
}
.message-suggested-actions {
grid-template-columns: 1fr;
}
.composer {
padding: 0 16px 16px;
}

View File

@@ -1,5 +1,5 @@
.assistant-overlay {
/* 距屏幕边 1018px随视口微调高度用 dvh 适配笔记本浏览器工具栏 */
/* 距屏幕边 10-18px随视口微调高度使用 dvh 适配浏览器工具栏 */
--assistant-viewport-inset: clamp(10px, 1.25vmin, 18px);
position: fixed;
inset: 0;
@@ -13,11 +13,10 @@
padding: var(--assistant-viewport-inset);
box-sizing: border-box;
background:
radial-gradient(circle at 18% 14%, rgba(16, 185, 129, 0.18), transparent 24%),
radial-gradient(circle at 82% 12%, rgba(59, 130, 246, 0.12), transparent 28%),
rgba(97, 110, 131, 0.34);
backdrop-filter: blur(18px) saturate(1.02);
-webkit-backdrop-filter: blur(18px) saturate(1.02);
linear-gradient(180deg, rgba(239, 246, 255, 0.98) 0%, rgba(248, 250, 252, 0.98) 100%),
rgba(241, 245, 249, 0.98);
backdrop-filter: none;
-webkit-backdrop-filter: none;
}
.assistant-modal {
@@ -39,7 +38,7 @@
}
.assistant-modal-stage {
/* 工作台字号令牌:笔记本断点见文末 @media */
/* 工作台字号令牌:笔记本断点见后续 media */
--wb-fs-title: 22px;
--wb-fs-desc: 13px;
--wb-fs-badge: 12px;
@@ -70,14 +69,12 @@
transform: none;
border-radius: 24px;
background:
radial-gradient(circle at top right, rgba(16, 185, 129, 0.14), transparent 26%),
radial-gradient(circle at top left, rgba(59, 130, 246, 0.10), transparent 24%),
linear-gradient(180deg, rgba(241, 246, 245, 0.92) 0%, rgba(230, 237, 235, 0.88) 100%);
linear-gradient(180deg, #f8fbff 0%, #edf5ff 100%);
box-shadow:
0 28px 72px rgba(15, 23, 42, 0.22),
0 10px 28px rgba(15, 23, 42, 0.09),
inset 0 1px 0 rgba(255, 255, 255, 0.42);
border: 1px solid rgba(255, 255, 255, 0.44);
border: 1px solid rgba(191, 219, 254, 0.88);
background-clip: padding-box;
overflow: hidden;
isolation: isolate;
@@ -108,11 +105,11 @@
justify-content: center;
padding: 0 14px;
border-radius: 999px;
background: linear-gradient(135deg, #22c55e, #10b981);
background: linear-gradient(135deg, var(--theme-primary), var(--theme-primary-active));
color: #fff;
font-size: var(--wb-fs-badge);
font-weight: 800;
box-shadow: 0 8px 16px rgba(16, 185, 129, 0.14);
box-shadow: 0 8px 16px rgba(var(--theme-primary-rgb, 58, 124, 165), 0.14);
white-space: nowrap;
}
@@ -160,16 +157,16 @@
}
.assistant-toggle-btn {
border-color: rgba(16, 185, 129, 0.18);
background: rgba(245, 252, 249, 0.96);
color: #166534;
border-color: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.18);
background: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.08);
color: var(--theme-primary-active);
font-size: 16px;
box-shadow: 0 8px 18px rgba(16, 185, 129, 0.1);
box-shadow: 0 8px 18px rgba(var(--theme-primary-rgb, 58, 124, 165), 0.1);
}
.assistant-toggle-btn:hover:not(:disabled) {
background: rgba(236, 253, 245, 0.98);
border-color: rgba(16, 185, 129, 0.28);
background: var(--theme-primary-soft);
border-color: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.28);
}
.assistant-toggle-btn:disabled,
@@ -254,8 +251,8 @@
}
.flow-status-chip.completed {
background: #ecfdf5;
color: #059669;
background: var(--success-soft);
color: var(--success);
}
.flow-status-chip.failed {
@@ -337,7 +334,7 @@
}
.flow-step-item.completed .flow-step-rail span {
background: #10b981;
background: var(--success);
color: #fff;
}
@@ -405,13 +402,13 @@
}
.flow-step-status.completed {
background: #ecfdf5;
color: #059669;
background: var(--success-soft);
color: var(--success);
}
.flow-step-status.running {
background: #eff6ff;
color: #2563eb;
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
}
.flow-step-status.failed {
@@ -497,13 +494,13 @@
min-width: 0;
min-height: 0;
border: 1px solid rgba(189, 201, 214, 0.74);
border-radius: 24px;
background: rgba(248, 251, 251, 0.84);
border-radius: 16px;
background: #ffffff;
box-shadow:
0 14px 32px rgba(148, 163, 184, 0.16),
0 2px 6px rgba(15, 23, 42, 0.05);
backdrop-filter: blur(22px);
-webkit-backdrop-filter: blur(22px);
backdrop-filter: none;
-webkit-backdrop-filter: none;
}
.dialog-panel {
@@ -512,8 +509,7 @@
grid-template-rows: auto minmax(0, 1fr) auto;
overflow: hidden;
background:
radial-gradient(circle at top right, rgba(59, 130, 246, 0.07), transparent 22%),
linear-gradient(180deg, rgba(252, 253, 253, 0.88) 0%, rgba(243, 247, 248, 0.84) 100%);
linear-gradient(180deg, #ffffff 0%, #f8fbff 100%);
transition:
transform 320ms cubic-bezier(0.22, 1, 0.36, 1),
box-shadow 320ms cubic-bezier(0.22, 1, 0.36, 1);
@@ -564,18 +560,18 @@
}
.shortcut-chip i {
color: #059669;
color: var(--theme-primary);
}
.shortcut-chip.active {
border-color: rgba(5, 150, 105, 0.38);
background: rgba(16, 185, 129, 0.1);
color: #047857;
border-color: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.38);
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
box-shadow: none;
}
.shortcut-chip.active i {
color: #047857;
color: var(--theme-primary-active);
}
.shortcut-chip:disabled {
@@ -593,17 +589,6 @@
overflow-y: auto;
}
.message-row {
display: grid;
grid-template-columns: 38px minmax(0, 1fr);
align-items: start;
gap: 12px;
}
.message-row.user {
grid-template-columns: minmax(0, 1fr) 38px;
}
.message-row.user .message-avatar {
order: 2;
background: transparent;
@@ -850,13 +835,13 @@
}
.application-preview-row.highlight .application-preview-label {
background: #f0fdf4;
color: #047857;
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
}
.application-preview-row.highlight .application-preview-value {
background: #f7fee7;
color: #166534;
background: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.08);
color: var(--theme-primary-active);
font-weight: 780;
}
@@ -1138,9 +1123,9 @@
place-items: center;
border-radius: 10px;
background: #f1f5f9;
color: #0f766e;
color: var(--theme-primary-active);
font-size: 18px;
box-shadow: inset 0 0 0 1px rgba(15, 118, 110, 0.08);
box-shadow: inset 0 0 0 1px rgba(var(--theme-primary-rgb), 0.08);
}
.message-suggested-action-copy {
@@ -1171,7 +1156,7 @@
}
.message-suggested-action-btn:hover:not(:disabled) {
border-color: rgba(20, 184, 166, 0.72);
border-color: rgba(var(--theme-primary-rgb), 0.42);
background: #ffffff;
box-shadow: 0 10px 24px rgba(15, 23, 42, 0.09);
transform: translateY(-1px);
@@ -1179,35 +1164,35 @@
.message-suggested-action-btn:hover:not(:disabled) .message-suggested-action-icon,
.message-suggested-action-btn:focus-visible .message-suggested-action-icon {
background: #ccfbf1;
color: #0f766e;
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
}
.message-suggested-action-btn:hover:not(:disabled) .message-suggested-action-arrow,
.message-suggested-action-btn:focus-visible .message-suggested-action-arrow {
color: #0f766e;
color: var(--theme-primary-active);
transform: translateX(2px);
}
.message-suggested-action-btn:focus-visible {
outline: 3px solid rgba(20, 184, 166, 0.18);
outline: 3px solid var(--theme-focus-ring);
outline-offset: 2px;
border-color: #14b8a6;
border-color: var(--theme-primary);
}
.message-suggested-action-btn.selected {
border-color: rgba(13, 148, 136, 0.78);
background: #f0fdfa;
box-shadow: inset 0 0 0 1px rgba(13, 148, 136, 0.18);
border-color: rgba(var(--theme-primary-rgb), 0.52);
background: var(--theme-primary-light-9);
box-shadow: inset 0 0 0 1px var(--theme-primary-shadow);
}
.message-suggested-action-btn.selected .message-suggested-action-icon {
background: #99f6e4;
color: #115e59;
background: var(--theme-primary-soft-strong);
color: var(--theme-primary-active);
}
.message-suggested-action-btn.selected .message-suggested-action-arrow {
color: #0f766e;
color: var(--theme-primary-active);
}
.message-suggested-action-btn.locked:not(.selected) {
@@ -1268,8 +1253,8 @@
}
.message-action-chip {
background: #ecfdf5;
color: #059669;
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
}
.message-detail-block {
@@ -1376,8 +1361,8 @@
}
.expense-query-summary-chip.completed {
background: #dcfce7;
color: #15803d;
background: var(--success-soft);
color: var(--success);
}
.expense-query-summary-chip.other {
@@ -1491,8 +1476,8 @@
}
.expense-query-record-status.completed {
background: #dcfce7;
color: #15803d;
background: var(--success-soft);
color: var(--success);
}
.expense-query-record-card p {
@@ -1801,10 +1786,10 @@
}
.tool-btn.composer-side-btn.active {
border-color: rgba(59, 130, 246, 0.42);
background: rgba(239, 246, 255, 0.96);
color: #2563eb;
box-shadow: 0 6px 14px rgba(59, 130, 246, 0.14);
border-color: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.42);
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
box-shadow: 0 6px 14px rgba(var(--theme-primary-rgb, 58, 124, 165), 0.14);
}
.composer-date-popover {

View File

@@ -51,8 +51,8 @@
width: 6px;
height: 6px;
border-radius: 999px;
background: #10b981;
box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.12);
background: var(--success);
box-shadow: 0 0 0 3px rgba(var(--success-rgb), 0.12);
}
.validation-section--risk .validation-section-title {
@@ -69,13 +69,13 @@
gap: 6px;
margin: 0;
padding: 0 0 0 18px;
color: #0f766e;
color: var(--success-hover);
font-size: 13px;
line-height: 1.55;
}
.validation-list li::marker {
color: #14b8a6;
color: var(--success);
}
.validation-section--risk .risk-advice-list {
@@ -383,7 +383,7 @@
padding: 18px;
overflow: auto;
background:
linear-gradient(180deg, rgba(240, 253, 244, .5) 0%, rgba(255, 255, 255, 0) 140px),
linear-gradient(180deg, color-mix(in srgb, var(--theme-primary-soft) 55%, transparent) 0%, rgba(255, 255, 255, 0) 140px),
#fff;
}
@@ -415,8 +415,8 @@
display: grid;
place-items: center;
border-radius: 999px;
background: #ecfdf5;
color: #059669;
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
font-size: 18px;
}
@@ -471,9 +471,9 @@
}
.ai-composer-surface:focus-within {
border-color: rgba(16, 185, 129, .58);
border-color: rgba(var(--theme-primary-rgb), .58);
background: #fff;
box-shadow: 0 0 0 3px rgba(16, 185, 129, .11), 0 10px 24px rgba(15, 23, 42, .06);
box-shadow: 0 0 0 3px var(--theme-focus-ring), 0 10px 24px rgba(15, 23, 42, .06);
}
.ai-composer textarea {
@@ -552,14 +552,14 @@
.ai-send-btn {
border: 0;
background: #10b981;
background: var(--theme-primary);
color: #fff;
box-shadow: 0 8px 18px rgba(16, 185, 129, .20);
box-shadow: 0 8px 18px var(--theme-primary-shadow);
}
.ai-send-btn:hover {
background: #0ea672;
box-shadow: 0 10px 22px rgba(16, 185, 129, .24);
background: var(--theme-primary-active);
box-shadow: 0 10px 22px rgba(var(--theme-primary-rgb), .24);
}
.ai-upload-btn:active,
@@ -650,7 +650,7 @@
.ai-preview-empty i {
font-size: 32px;
color: #10b981;
color: var(--theme-primary);
}
.ai-preview-actions {
@@ -681,10 +681,10 @@
}
.ai-preview-primary {
border: 1px solid #059669;
background: #059669;
border: 1px solid var(--theme-primary);
background: var(--theme-primary);
color: #fff;
box-shadow: 0 8px 20px rgba(5, 150, 105, .18);
box-shadow: 0 8px 20px var(--theme-primary-shadow);
}
.ai-preview-secondary:hover {
@@ -692,7 +692,7 @@
}
.ai-preview-primary:hover {
background: #047857;
background: var(--theme-primary-active);
}
.ai-preview-secondary:disabled,
@@ -957,7 +957,7 @@
}
.validation-pill.ready {
background: #f0fdf4;
border-color: #bbf7d0;
color: #166534;
background: var(--success-soft);
border-color: var(--success-line);
color: var(--success-active);
}

View File

@@ -112,9 +112,9 @@
min-height: 24px;
padding: 0 9px;
border-radius: 999px;
background: #ecfdf5;
border: 1px solid rgba(16, 185, 129, .16);
color: #10b981;
background: var(--theme-primary-soft);
border: 1px solid rgba(var(--theme-primary-rgb), .16);
color: var(--theme-primary-active);
font-size: 11px;
font-weight: 800;
}
@@ -265,8 +265,8 @@
width: 8px;
height: 8px;
border-radius: 999px;
background: #10b981;
box-shadow: 0 0 0 4px rgba(16, 185, 129, .12);
background: var(--theme-primary);
box-shadow: 0 0 0 4px var(--theme-focus-ring);
}
.progress-line {
@@ -309,7 +309,7 @@
right: 0;
z-index: 0;
height: 2px;
background: #10b981;
background: var(--theme-primary);
opacity: 0;
}
@@ -346,20 +346,20 @@
position: absolute;
inset: -4px;
z-index: -1;
border: 2px solid rgba(16, 185, 129, .42);
border: 2px solid rgba(var(--theme-primary-rgb), .42);
border-radius: 999px;
pointer-events: none;
}
.progress-step.active span {
background: #059669;
background: var(--theme-primary-active);
color: #fff;
}
.progress-step.current span {
background: #10b981 !important;
background: var(--theme-primary) !important;
color: #fff !important;
box-shadow: 0 0 0 4px rgba(16, 185, 129, .15) !important;
box-shadow: 0 0 0 4px var(--theme-focus-ring) !important;
animation: breathe-dot 3.2s ease-in-out infinite !important;
transform-origin: center !important;
}
@@ -367,11 +367,11 @@
@keyframes breathe-dot {
0%, 100% {
transform: scale(1);
box-shadow: 0 4px 12px rgba(16, 185, 129, .3), 0 0 0 4px rgba(16, 185, 129, .15);
box-shadow: 0 4px 12px rgba(var(--theme-primary-rgb), .3), 0 0 0 4px var(--theme-focus-ring);
}
50% {
transform: scale(1.12);
box-shadow: 0 4px 20px rgba(16, 185, 129, .5), 0 0 0 10px rgba(16, 185, 129, .08);
box-shadow: 0 4px 20px rgba(var(--theme-primary-rgb), .5), 0 0 0 10px rgba(var(--theme-primary-rgb), .08);
}
}
@@ -382,7 +382,7 @@
text-align: center;
}
.progress-step.current strong { color: #059669; }
.progress-step.current strong { color: var(--theme-primary-active); }
.progress-step-copy {
width: 100%;
@@ -414,16 +414,16 @@
}
.progress-step.done .progress-step-status {
border-color: rgba(16, 185, 129, .2);
background: #ecfdf5;
color: #047857;
border-color: rgba(var(--theme-primary-rgb), .2);
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
}
.progress-step.current .progress-step-status {
border-color: rgba(5, 150, 105, .22);
background: #059669;
border-color: rgba(var(--theme-primary-rgb), .22);
background: var(--theme-primary);
color: #fff;
box-shadow: 0 8px 18px rgba(5, 150, 105, .14);
box-shadow: 0 8px 18px var(--theme-primary-shadow);
}
.progress-step:not(.done):not(.current) .progress-step-status {
@@ -432,7 +432,7 @@
}
.progress-step.current small {
color: #059669;
color: var(--theme-primary-active);
}
.progress-step-meta {
@@ -535,14 +535,14 @@
.smart-entry-btn.secondary {
background: #fff;
color: #0f766e;
border-color: rgba(16, 185, 129, .24);
color: var(--theme-primary-active);
border-color: rgba(var(--theme-primary-rgb), .24);
}
.smart-entry-btn:hover {
border-color: rgba(16, 185, 129, .36);
background: #f0fdf4;
color: #047857;
border-color: rgba(var(--theme-primary-rgb), .36);
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
}
.detail-total {
@@ -553,8 +553,8 @@
min-height: 34px;
padding: 0 12px;
border-radius: 999px;
background: #ecfdf5;
color: #047857;
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
font-size: 14px;
font-weight: 900;
}
@@ -643,16 +643,16 @@
}
.application-detail-fact.highlight span {
background: #eefcf6;
color: #047857;
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
}
.application-detail-fact.highlight strong {
background: #f6fef9;
background: color-mix(in srgb, var(--theme-primary-soft) 55%, #ffffff);
}
.application-detail-fact.emphasis strong {
color: #047857;
color: var(--theme-primary-active);
font-weight: 850;
}
@@ -663,13 +663,13 @@
.detail-note-editor textarea {
min-height: 92px;
border-color: rgba(16, 185, 129, .28);
border-color: rgba(var(--theme-primary-rgb), .28);
background: #fff;
}
.detail-note-editor textarea:focus {
border-color: #10b981;
box-shadow: 0 0 0 3px rgba(16, 185, 129, .12);
border-color: var(--theme-primary);
box-shadow: 0 0 0 3px var(--theme-focus-ring);
outline: none;
}
@@ -692,8 +692,8 @@
}
.leader-approval-card {
border-color: rgba(5, 150, 105, .18);
background: linear-gradient(180deg, #ffffff 0%, #f7fdfb 100%);
border-color: rgba(var(--theme-primary-rgb), .18);
background: linear-gradient(180deg, #ffffff 0%, var(--theme-primary-soft) 100%);
}
.leader-approval-card textarea {
@@ -704,8 +704,8 @@
.leader-approval-card textarea:focus {
outline: 0;
border-color: rgba(5, 150, 105, .5);
box-shadow: 0 0 0 3px rgba(5, 150, 105, .1);
border-color: rgba(var(--theme-primary-rgb), .5);
box-shadow: 0 0 0 3px var(--theme-focus-ring);
}
.leader-opinion-meta {
@@ -721,7 +721,7 @@
.leader-opinion-meta strong {
flex: 0 0 auto;
color: #047857;
color: var(--theme-primary-active);
font-weight: 850;
}
@@ -758,7 +758,7 @@
}
.application-leader-opinion-head strong {
color: #047857;
color: var(--theme-primary-active);
font-weight: 800;
font-size: 14px;
}
@@ -812,12 +812,12 @@
}
.detail-expense-table tbody tr.system-generated-row td {
background: #f0fdf4;
border-bottom-color: #bbf7d0;
background: var(--success-soft);
border-bottom-color: var(--success-line);
}
.detail-expense-table tbody tr.system-generated-row:hover td {
background: #ecfdf5;
background: var(--success-soft);
}
.detail-expense-table .col-time { width: 11%; }
@@ -893,8 +893,8 @@
.editor-input:focus,
.editor-select:focus {
border-color: #10b981;
box-shadow: 0 0 0 3px rgba(16, 185, 129, .12);
border-color: var(--theme-primary);
box-shadow: 0 0 0 3px var(--theme-focus-ring);
outline: none;
}
@@ -966,8 +966,8 @@
}
.over-tag.system {
background: #dcfce7;
color: #047857;
background: var(--success-soft);
color: var(--success-hover);
}
.expense-total-under-table {
@@ -977,10 +977,10 @@
gap: 12px;
margin-top: 12px;
padding: 12px 14px;
border: 1px solid #d1fae5;
border: 1px solid rgba(var(--theme-primary-rgb), .22);
border-radius: 8px;
background: #f0fdf4;
color: #0f766e;
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
}
.expense-total-under-table span {
@@ -990,7 +990,7 @@
}
.expense-total-under-table strong {
color: #047857;
color: var(--theme-primary-active);
font-size: 17px;
font-weight: 900;
}
@@ -1050,8 +1050,8 @@
}
.attachment-recognition-pill.pass {
background: #ecfdf5;
color: #047857;
background: var(--success-soft);
color: var(--success-hover);
}
.attachment-recognition-pill.medium {
@@ -1101,9 +1101,9 @@
}
.icon-action.upload {
background: #ecfdf5;
border-color: rgba(16, 185, 129, .24);
color: #047857;
background: var(--theme-primary-soft);
border-color: rgba(var(--theme-primary-rgb), .24);
color: var(--theme-primary-active);
}
.icon-action.preview {
@@ -1133,14 +1133,14 @@
}
.inline-action.accent {
border-color: rgba(16, 185, 129, .24);
background: #ecfdf5;
color: #047857;
border-color: rgba(var(--theme-primary-rgb), .24);
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
}
.inline-action.primary {
border-color: #10b981;
background: #10b981;
border-color: var(--theme-primary);
background: var(--theme-primary);
color: #fff;
}
@@ -1151,13 +1151,13 @@
}
.inline-action:hover {
border-color: rgba(16, 185, 129, .36);
color: #047857;
border-color: rgba(var(--theme-primary-rgb), .36);
color: var(--theme-primary-active);
}
.inline-action.primary:hover {
border-color: #059669;
background: #059669;
border-color: var(--theme-primary-active);
background: var(--theme-primary-active);
color: #fff;
}
@@ -1179,8 +1179,8 @@
min-height: 28px;
padding: 0 9px;
border-radius: 8px;
background: #dcfce7;
color: #047857;
background: var(--success-soft);
color: var(--success-hover);
font-size: 11px;
font-weight: 850;
white-space: nowrap;
@@ -1194,8 +1194,8 @@
min-height: 28px;
padding: 0 9px;
border-radius: 8px;
background: #ecfdf5;
color: #047857;
background: var(--success-soft);
color: var(--success-hover);
font-size: 11px;
font-weight: 850;
white-space: nowrap;
@@ -1217,7 +1217,7 @@
}
.risk-inline-tag.pass,
.risk-inline-tag.low { background: #ecfdf5; color: #059669; }
.risk-inline-tag.low { background: var(--success-soft); color: var(--success-hover); }
.risk-inline-tag.medium { background: #fff7ed; color: #ea580c; }
.risk-inline-tag.high { background: #fef2f2; color: #dc2626; }
@@ -1248,7 +1248,7 @@
}
.risk-suggestion {
color: #0f766e;
color: var(--success-hover);
font-weight: 700;
}
@@ -1284,10 +1284,10 @@
grid-template-rows: auto minmax(0, 1fr);
gap: 14px;
padding: 22px;
border: 1px solid rgba(16, 185, 129, .14);
border: 1px solid rgba(var(--theme-primary-rgb), .14);
border-radius: 24px;
background:
radial-gradient(circle at top left, rgba(16, 185, 129, .12), transparent 36%),
radial-gradient(circle at top left, rgba(var(--theme-primary-rgb), .12), transparent 36%),
linear-gradient(180deg, rgba(255, 255, 255, .98), rgba(247, 250, 252, .98));
box-shadow: 0 28px 56px rgba(15, 23, 42, .2);
}
@@ -1338,8 +1338,8 @@
min-height: 28px;
padding: 0 10px;
border-radius: 999px;
background: rgba(16, 185, 129, .12);
color: #047857;
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
font-size: 12px;
font-weight: 800;
}
@@ -1547,10 +1547,10 @@
.approve-action {
min-width: 92px;
border: 1px solid #059669;
background: #059669;
border: 1px solid var(--success);
background: var(--success);
color: #fff;
box-shadow: 0 4px 10px rgba(5, 150, 105, .14);
box-shadow: 0 4px 10px rgba(var(--success-rgb), .14);
}
.reject-action {

View File

@@ -0,0 +1,334 @@
<template>
<article class="skill-list panel">
<nav class="status-tabs" aria-label="能力类型">
<button
v-for="tab in tabs"
:key="tab.id"
type="button"
:class="{ active: activeType === tab.id }"
@click="emit('update:activeType', tab.id)"
>
{{ tab.label }}
</button>
</nav>
<div class="list-toolbar">
<div class="filter-set">
<label class="search-filter">
<i class="mdi mdi-magnify"></i>
<input
:value="keyword"
type="search"
:placeholder="searchPlaceholder"
@input="emit('update:keyword', $event.target.value)"
/>
</label>
<AuditPickerFilter
id="domain"
title="选择业务域"
close-label="关闭业务域选择"
:active-filter-popover="activeFilterPopover"
:label="selectedDomainLabel"
:options="domainOptions"
:selected-value="selectedDomain"
@toggle="emit('toggle-filter-popover', $event)"
@close="emit('close-filter-popover')"
@select="selectFilter('domain', $event)"
/>
<AuditPickerFilter
v-if="showOwnerFilter"
id="owner"
title="选择负责人"
close-label="关闭负责人选择"
:active-filter-popover="activeFilterPopover"
:label="selectedOwnerLabel"
:options="ownerOptions"
:selected-value="selectedOwner"
@toggle="emit('toggle-filter-popover', $event)"
@close="emit('close-filter-popover')"
@select="selectFilter('owner', $event)"
/>
<AuditPickerFilter
v-if="showRiskLevelFilter"
id="riskLevel"
title="选择风险等级"
close-label="关闭风险等级选择"
:active-filter-popover="activeFilterPopover"
:label="selectedRiskLevelLabel"
:options="riskLevelOptions"
:selected-value="selectedRiskLevel"
@toggle="emit('toggle-filter-popover', $event)"
@close="emit('close-filter-popover')"
@select="selectFilter('riskLevel', $event)"
/>
<AuditPickerFilter
v-if="showRiskScenarioFilter"
id="riskScenario"
title="选择使用场景"
close-label="关闭使用场景选择"
:active-filter-popover="activeFilterPopover"
:label="selectedRiskScenarioLabel"
:options="riskScenarioOptions"
:selected-value="selectedRiskScenario"
@toggle="emit('toggle-filter-popover', $event)"
@close="emit('close-filter-popover')"
@select="selectFilter('riskScenario', $event)"
/>
<AuditPickerFilter
v-if="showOnlineFilter"
id="online"
title="选择上线状态"
close-label="关闭上线状态选择"
:active-filter-popover="activeFilterPopover"
:label="selectedOnlineStateLabel"
:options="onlineStateOptions"
:selected-value="selectedOnlineState"
@toggle="emit('toggle-filter-popover', $event)"
@close="emit('close-filter-popover')"
@select="selectFilter('online', $event)"
/>
<AuditPickerFilter
v-if="showEnabledFilter"
id="enabled"
title="选择启用状态"
close-label="关闭启用状态选择"
:active-filter-popover="activeFilterPopover"
:label="selectedEnabledStateLabel"
:options="enabledStateOptions"
:selected-value="selectedEnabledState"
@toggle="emit('toggle-filter-popover', $event)"
@close="emit('close-filter-popover')"
@select="selectFilter('enabled', $event)"
/>
<AuditPickerFilter
v-if="showStatusFilter"
id="status"
title="选择状态"
close-label="关闭状态选择"
:active-filter-popover="activeFilterPopover"
:label="selectedStatusLabel"
:options="statusOptions"
:selected-value="selectedStatus"
@toggle="emit('toggle-filter-popover', $event)"
@close="emit('close-filter-popover')"
@select="selectFilter('status', $event)"
/>
</div>
<div class="toolbar-actions">
<button
v-if="activeFilterTokens.length"
class="ghost-filter-btn"
type="button"
@click="emit('reset-filters')"
>
<i class="mdi mdi-filter-remove-outline"></i>
<span>清空筛选</span>
</button>
<button
class="create-btn"
type="button"
:disabled="!canCreateRiskRule"
@click="emit('create-risk-rule')"
>
<i class="mdi mdi-plus"></i>
<span>{{ createButtonLabel }}</span>
</button>
</div>
</div>
<p class="hint"><i class="mdi mdi-information-outline"></i> {{ hintText }}</p>
<div v-if="activeFilterTokens.length" class="active-filter-strip">
<span v-for="token in activeFilterTokens" :key="token" class="active-filter-chip">
{{ token }}
</span>
</div>
<div
class="table-wrap"
:class="{ 'is-empty': !loading && !errorMessage && !visibleSkills.length }"
>
<div v-if="loading" class="table-state">
<TableLoadingState
variant="panel"
:title="`${activeTabLabel}资产同步中`"
:message="`正在加载${activeTabLabel}资产`"
icon="mdi mdi-view-list-outline"
/>
</div>
<div v-else-if="errorMessage" class="table-state error">
<i class="mdi mdi-alert-circle-outline"></i>
<p>{{ errorMessage }}</p>
</div>
<TableEmptyState
v-else-if="!visibleSkills.length"
:eyebrow="auditEmptyState.eyebrow"
:title="auditEmptyState.title"
:description="auditEmptyState.desc"
:icon="auditEmptyState.icon"
:action-label="auditEmptyState.actionLabel"
:action-icon="auditEmptyState.actionIcon"
:tone="auditEmptyState.tone"
:art-label="auditEmptyState.artLabel"
:tips="auditEmptyState.tips"
@action="emit('empty-action')"
/>
<table v-else>
<thead>
<tr>
<th>{{ tableColumns.name }}</th>
<th>{{ tableColumns.category }}</th>
<th>{{ tableColumns.owner }}</th>
<th>{{ tableColumns.scope }}</th>
<th v-if="showRuntimeColumn">{{ tableColumns.runtime }}</th>
<th v-if="showVersionColumn">{{ tableColumns.version }}</th>
<th v-if="showStatusColumn">{{ tableColumns.status || '状态' }}</th>
<th v-if="showMetricColumn">{{ tableColumns.metric }}</th>
<th v-if="showOnlineColumn">是否上线</th>
<th v-if="showEnabledColumn">是否启用</th>
<th>{{ tableColumns.updatedAt || '最近更新' }}</th>
</tr>
</thead>
<tbody>
<tr
v-for="skill in visibleSkills"
:key="skill.id"
:class="{ 'is-disabled': skill.usesJsonRiskRule && skill.statusValue === 'generating' }"
@click="emit('open-asset-detail', skill)"
>
<td>
<div class="skill-name-cell">
<span class="skill-avatar" :class="skill.badgeTone">{{ skill.short }}</span>
<div>
<strong>{{ skill.name }}</strong>
<span class="skill-list-subtitle">{{ skill.listSubtitle || skill.summary }}</span>
</div>
</div>
</td>
<td>{{ skill.category }}</td>
<td>
<span
v-if="skill.usesJsonRiskRule"
class="json-risk-meta-badge"
:class="skill.riskLevelTone"
>
{{ skill.riskLevelLabel || '-' }}
</span>
<template v-else>{{ skill.owner }}</template>
</td>
<td><span class="scope-pill">{{ skill.scope }}</span></td>
<td v-if="showRuntimeColumn">{{ skill.model }}</td>
<td v-if="showVersionColumn">{{ skill.versionDisplay || skill.version }}</td>
<td v-if="showStatusColumn">
<span class="status-pill" :class="skill.statusTone">{{ skill.status }}</span>
</td>
<td v-if="showMetricColumn">{{ skill.hitRate }}</td>
<td v-if="showOnlineColumn">
<span class="status-pill" :class="skill.isOnlineTone">{{ skill.isOnlineLabel }}</span>
</td>
<td v-if="showEnabledColumn">
<span class="status-pill" :class="skill.isEnabledTone">{{ skill.isEnabledLabel }}</span>
</td>
<td>{{ skill.updatedAt }}</td>
</tr>
</tbody>
</table>
</div>
<footer v-if="!loading && !errorMessage && visibleSkills.length" class="list-foot">
<span class="page-summary">当前展示 {{ visibleSkills.length }} 条资产</span>
</footer>
</article>
</template>
<script setup>
import AuditPickerFilter from './AuditPickerFilter.vue'
import TableEmptyState from '../shared/TableEmptyState.vue'
import TableLoadingState from '../shared/TableLoadingState.vue'
defineOptions({
name: 'AuditAssetList'
})
defineProps({
tabs: { type: Array, default: () => [] },
activeType: { type: String, default: '' },
activeTabLabel: { type: String, default: '' },
keyword: { type: String, default: '' },
searchPlaceholder: { type: String, default: '' },
createButtonLabel: { type: String, default: '' },
hintText: { type: String, default: '' },
tableColumns: { type: Object, default: () => ({}) },
showRuntimeColumn: { type: Boolean, default: false },
showVersionColumn: { type: Boolean, default: false },
showMetricColumn: { type: Boolean, default: false },
showStatusColumn: { type: Boolean, default: false },
showOnlineColumn: { type: Boolean, default: false },
showEnabledColumn: { type: Boolean, default: false },
visibleSkills: { type: Array, default: () => [] },
auditEmptyState: { type: Object, default: () => ({}) },
loading: { type: Boolean, default: false },
errorMessage: { type: String, default: '' },
selectedDomain: { type: String, default: '' },
selectedOwner: { type: String, default: '' },
selectedRiskLevel: { type: String, default: '' },
selectedStatus: { type: String, default: '' },
selectedRiskScenario: { type: String, default: '' },
selectedOnlineState: { type: String, default: '' },
selectedEnabledState: { type: String, default: '' },
selectedDomainLabel: { type: String, default: '' },
selectedOwnerLabel: { type: String, default: '' },
selectedRiskLevelLabel: { type: String, default: '' },
selectedStatusLabel: { type: String, default: '' },
selectedRiskScenarioLabel: { type: String, default: '' },
selectedOnlineStateLabel: { type: String, default: '' },
selectedEnabledStateLabel: { type: String, default: '' },
showRiskScenarioFilter: { type: Boolean, default: false },
showOwnerFilter: { type: Boolean, default: false },
showRiskLevelFilter: { type: Boolean, default: false },
showStatusFilter: { type: Boolean, default: false },
showOnlineFilter: { type: Boolean, default: false },
showEnabledFilter: { type: Boolean, default: false },
domainOptions: { type: Array, default: () => [] },
ownerOptions: { type: Array, default: () => [] },
riskLevelOptions: { type: Array, default: () => [] },
statusOptions: { type: Array, default: () => [] },
riskScenarioOptions: { type: Array, default: () => [] },
onlineStateOptions: { type: Array, default: () => [] },
enabledStateOptions: { type: Array, default: () => [] },
activeFilterPopover: { type: String, default: '' },
activeFilterTokens: { type: Array, default: () => [] },
canCreateRiskRule: { type: Boolean, default: false }
})
const emit = defineEmits([
'update:activeType',
'update:keyword',
'toggle-filter-popover',
'close-filter-popover',
'select-filter',
'reset-filters',
'create-risk-rule',
'empty-action',
'open-asset-detail'
])
function selectFilter(type, value) {
emit('select-filter', type, value)
}
</script>
<style scoped src="../../assets/styles/views/audit-view.css"></style>
<style scoped src="../../assets/styles/views/audit-view-part2.css"></style>

View File

@@ -0,0 +1,173 @@
<template>
<section class="json-risk-editor-shell panel">
<header class="json-risk-editor-head asset-detail-topbar list-toolbar">
<div class="json-risk-editor-title asset-detail-topbar-main filter-set">
<div class="json-risk-head-copy">
<div class="json-risk-head-title-row">
<h2>{{ selectedSkill.name }}</h2>
</div>
<p class="json-risk-head-subtitle">
{{ selectedSkill.riskRuleSubtitle || '平台通用风险规则' }}
</p>
<div class="json-risk-head-meta">
<span v-if="selectedSkill.riskCategory">适用场景{{ selectedSkill.riskCategory }}</span>
<span>业务域{{ selectedSkill.category || '-' }}</span>
<span>最近更新{{ selectedSkill.updatedAt || '-' }}</span>
</div>
</div>
</div>
<div
class="json-risk-score-ring"
:class="selectedSkill.riskRuleScoreLevel || selectedSkill.riskRuleSeverity"
>
<strong>{{ selectedSkill.riskRuleScore ?? '--' }}</strong>
<span>风险分</span>
<em>{{ selectedSkill.riskRuleScoreLabel || selectedSkill.riskRuleSeverityLabel }}</em>
</div>
</header>
<div
v-if="selectedSkill.riskRuleGenerationFailed"
class="json-risk-generation-failure"
>
<i class="mdi mdi-alert-circle-outline"></i>
<div>
<h3>风险规则生成失败</h3>
<p>这条规则没有生成出可执行的 JSON 模板和流程图管理员可以删除后重新创建</p>
<small v-if="selectedSkill.riskRuleGenerationError">
失败原因{{ selectedSkill.riskRuleGenerationError }}
</small>
</div>
</div>
<div v-else class="json-risk-editor-body">
<section class="json-risk-main-stage">
<article class="detail-card panel json-risk-summary-card">
<div class="card-head">
<div>
<h3>基本信息</h3>
<p>这条规则的业务域风险等级创建时间上线状态和最近操作</p>
</div>
</div>
<div class="json-risk-meta-grid">
<div class="json-risk-meta-item">
<span class="json-risk-meta-label">业务域</span>
<span class="json-risk-meta-value">{{ selectedSkill.category || '-' }}</span>
</div>
<div class="json-risk-meta-item">
<span class="json-risk-meta-label">适用场景</span>
<span class="json-risk-meta-value">{{ selectedSkill.riskCategory || selectedSkill.scope || '-' }}</span>
</div>
<div class="json-risk-meta-item">
<span class="json-risk-meta-label">业务环节</span>
<span class="json-risk-meta-value">{{ selectedSkill.businessStageLabel || '-' }}</span>
</div>
<div class="json-risk-meta-item">
<span class="json-risk-meta-label">风险等级</span>
<span class="json-risk-meta-value">
<span class="json-risk-meta-badge" :class="selectedSkill.riskRuleSeverity">
{{ selectedSkill.riskRuleSeverityLabel || '-' }}
</span>
</span>
</div>
<div class="json-risk-meta-item">
<span class="json-risk-meta-label">是否上线</span>
<span class="json-risk-meta-value">
<span class="json-risk-meta-badge" :class="selectedSkill.isOnlineValue ? 'test-passed' : 'test-pending'">
{{ selectedSkill.isOnlineLabel || '待上线' }}
</span>
</span>
</div>
<div class="json-risk-meta-item">
<span class="json-risk-meta-label">测试状态</span>
<span class="json-risk-meta-value">
<span class="json-risk-meta-badge" :class="riskRuleTestPassed ? 'test-passed' : 'test-pending'">
{{ riskRuleTestPassed ? '已确认通过' : '待测试确认' }}
</span>
</span>
</div>
<div class="json-risk-meta-item">
<span class="json-risk-meta-label">创建者</span>
<span class="json-risk-meta-value">{{ selectedSkill.creator || selectedSkill.publisher || '-' }}</span>
</div>
<div class="json-risk-meta-item">
<span class="json-risk-meta-label">创建时间</span>
<span class="json-risk-meta-value">
{{ selectedSkill.riskRuleCreatedAt || selectedSkill.updatedAt }}
<span v-if="selectedSkill.riskRuleAgeLabel" class="meta-value-hint">
({{ selectedSkill.riskRuleAgeLabel }})
</span>
</span>
</div>
<div class="json-risk-meta-item">
<span class="json-risk-meta-label">上线时间</span>
<span class="json-risk-meta-value">{{ selectedSkill.publishedAt || '-' }}</span>
</div>
<div class="json-risk-meta-item">
<span class="json-risk-meta-label">最后操作</span>
<span class="json-risk-meta-value">{{ selectedSkill.lastOperationLabel || '-' }}</span>
</div>
<div class="json-risk-meta-item full-width">
<span class="json-risk-meta-label">使用字段</span>
<span class="json-risk-meta-value">{{ selectedSkill.riskRuleFieldSummary || '-' }}</span>
</div>
</div>
</article>
<article
v-if="selectedSkill.riskRuleBusinessDescription"
class="detail-card panel json-risk-description-card"
>
<div class="card-head">
<div>
<h3>业务说明</h3>
<p>面向规则制定者和审核人的自然语言说明</p>
</div>
</div>
<p class="json-risk-description-text">{{ selectedSkill.riskRuleBusinessDescription }}</p>
<p
v-if="selectedSkill.riskRuleSourceRef"
class="json-risk-description-source"
>
来源{{ selectedSkill.riskRuleSourceRef }}
</p>
</article>
<article class="detail-card panel json-risk-flow-card">
<div class="card-head">
<div>
<h3>判断流程</h3>
<p>规则从业务单据开始读取字段证据后按判断依据决定是否进入复核</p>
</div>
</div>
<RiskRuleFlowDiagram
:svg="selectedSkill.riskRuleFlowDiagramSvg"
:flow="selectedSkill.riskRuleFlow"
:fields="selectedSkill.riskRuleFields"
:severity="selectedSkill.riskRuleSeverity"
:severity-label="selectedSkill.riskRuleSeverityLabel"
/>
</article>
</section>
</div>
</section>
</template>
<script setup>
import RiskRuleFlowDiagram from '../shared/RiskRuleFlowDiagram.vue'
defineOptions({
name: 'AuditJsonRiskRuleDetail'
})
defineProps({
selectedSkill: { type: Object, required: true },
riskRuleTestPassed: { type: Boolean, default: false }
})
</script>
<style scoped src="../../assets/styles/views/audit-view.css"></style>
<style scoped src="../../assets/styles/views/audit-view-part2.css"></style>

View File

@@ -0,0 +1,60 @@
<template>
<div class="picker-filter" :class="{ open: activeFilterPopover === id }">
<button
class="picker-trigger"
type="button"
:aria-expanded="activeFilterPopover === id"
aria-haspopup="dialog"
@click="emit('toggle', id)"
>
<span class="picker-label">{{ label }}</span>
<i class="mdi mdi-chevron-down"></i>
</button>
<div
v-if="activeFilterPopover === id"
class="picker-popover"
role="dialog"
:aria-label="title"
>
<header>
<strong>{{ title }}</strong>
<button type="button" :aria-label="closeLabel" @click="emit('close')">
<i class="mdi mdi-close"></i>
</button>
</header>
<div class="picker-option-list">
<button
v-for="option in options"
:key="option.value || `all-${id}`"
type="button"
class="picker-option"
:class="{ active: selectedValue === option.value }"
@click="emit('select', option.value)"
>
{{ option.label }}
</button>
</div>
</div>
</div>
</template>
<script setup>
defineOptions({
name: 'AuditPickerFilter'
})
defineProps({
id: { type: String, required: true },
title: { type: String, required: true },
closeLabel: { type: String, required: true },
activeFilterPopover: { type: String, default: '' },
label: { type: String, default: '' },
options: { type: Array, default: () => [] },
selectedValue: { type: [String, Number, Boolean], default: '' }
})
const emit = defineEmits(['toggle', 'close', 'select'])
</script>
<style scoped src="../../assets/styles/views/audit-view.css"></style>
<style scoped src="../../assets/styles/views/audit-view-part2.css"></style>

View File

@@ -0,0 +1,293 @@
<template>
<ConfirmDialog
:open="riskRuleCreateOpen"
badge="自然语言规则"
badge-tone="info"
title="新建风险规则"
description="默认创建费用类风险规则。选择业务环节和费用领域后填写规则标题与自然语言描述,系统会根据评分模型自动计算风险分数和等级。"
cancel-text="取消"
confirm-text="开始生成"
busy-text="生成中..."
confirm-tone="primary"
confirm-icon="mdi mdi-auto-fix"
:busy="riskRuleCreateBusy"
:close-on-mask="!riskRuleCreateBusy"
@close="emit('close-risk-rule-create')"
@confirm="emit('submit-risk-rule-create')"
>
<div class="risk-rule-create-form">
<label>
<span>业务环节</span>
<EnterpriseSelect
v-model="riskRuleCreateForm.business_stage"
:options="riskRuleBusinessStageOptions"
:disabled="riskRuleCreateBusy"
/>
</label>
<label>
<span>费用领域</span>
<EnterpriseSelect
v-model="riskRuleCreateForm.expense_category"
:options="riskRuleExpenseCategoryOptions"
:disabled="riskRuleCreateBusy"
/>
</label>
<label>
<span>是否上传附件</span>
<EnterpriseSelect
v-model="riskRuleCreateForm.requires_attachment"
:options="riskRuleAttachmentOptions"
:disabled="riskRuleCreateBusy"
/>
</label>
<label class="span-2">
<span>规则标题</span>
<input
v-model="riskRuleCreateForm.rule_title"
:disabled="riskRuleCreateBusy"
maxlength="80"
placeholder="例如:差旅目的地与票据城市一致性校验"
/>
</label>
<label class="span-2">
<span>自然语言规则</span>
<textarea
v-model="riskRuleCreateForm.natural_language"
:disabled="riskRuleCreateBusy"
placeholder="例如:住宿城市必须出现在本次差旅行程城市中,否则提示高风险并要求补充说明。"
></textarea>
</label>
</div>
</ConfirmDialog>
<RiskRuleTestDialog
:open="riskRuleTestOpen"
:rule="selectedSkill"
@close="emit('close-risk-rule-test')"
@report-saved="emit('report-saved', $event)"
/>
<ConfirmDialog
:open="riskRuleDeleteOpen"
badge="删除规则"
badge-tone="danger"
title="删除未发布风险规则"
description="该操作会删除规则草稿、版本记录和关联 JSON 文件。只有从未发布过的规则允许删除。"
cancel-text="取消"
confirm-text="确认删除"
busy-text="删除中..."
confirm-tone="danger"
confirm-icon="mdi mdi-delete-outline"
:busy="actionState === 'delete-risk-rule'"
@close="emit('close-delete-risk-rule')"
@confirm="emit('delete-selected-risk-rule')"
>
<div class="risk-rule-action-confirm">
<span>规则名称</span>
<strong>{{ selectedSkill?.name }}</strong>
</div>
</ConfirmDialog>
<ConfirmDialog
:open="riskRuleReturnOpen"
badge="回退规则"
badge-tone="warning"
title="回退风险规则"
description="回退后规则会回到草稿状态,编写人需要根据原因重新调整并测试。"
cancel-text="取消"
confirm-text="确认回退"
busy-text="回退中..."
confirm-tone="warning"
confirm-icon="mdi mdi-keyboard-return"
:busy="actionState === 'return-risk-rule'"
@close="emit('close-return-risk-rule')"
@confirm="emit('return-selected-risk-rule')"
>
<label class="risk-rule-action-note">
<span>回退原因</span>
<textarea
v-model="returnNoteModel"
rows="4"
:disabled="actionState === 'return-risk-rule'"
placeholder="请说明需要编写人调整的规则问题"
></textarea>
</label>
</ConfirmDialog>
<ConfirmDialog
:open="riskRulePublishOpen"
badge="发布上线"
badge-tone="info"
title="发布风险规则"
description="发布后该规则会进入真实业务风险扫描,只加载正式上线规则。"
cancel-text="取消"
confirm-text="确认发布"
busy-text="发布中..."
confirm-tone="primary"
confirm-icon="mdi mdi-rocket-launch-outline"
:busy="actionState === 'publish-risk-rule'"
@close="emit('close-publish-risk-rule')"
@confirm="emit('publish-selected-risk-rule')"
>
<div class="risk-rule-action-confirm">
<span>测试状态</span>
<strong>{{ riskRuleTestPassed ? '已确认通过' : '未确认通过' }}</strong>
</div>
</ConfirmDialog>
<ConfirmDialog
:open="Boolean(versionSwitchTarget)"
badge="切换版本"
badge-tone="info"
title="切换规则版本"
description="切换后编辑器只会替换当前展示内容,不会直接回滚后端当前版本。"
cancel-text="取消"
confirm-text="确认切换"
busy-text="切换中..."
confirm-tone="primary"
confirm-icon="mdi mdi-swap-horizontal"
@close="emit('cancel-version-switch')"
@confirm="emit('confirm-version-switch')"
>
<div class="version-modal-summary">
<div>
<span>当前展示版本</span>
<strong>{{ selectedSkill?.displayVersion }}</strong>
</div>
<i class="mdi mdi-arrow-right"></i>
<div>
<span>目标版本</span>
<strong>{{ versionSwitchTarget?.version }}</strong>
</div>
</div>
<div v-if="versionSwitchTarget" class="version-modal-note">
<strong>{{ versionSwitchTarget.note }}</strong>
<span>{{ versionSwitchTarget.time }}</span>
</div>
</ConfirmDialog>
<ConfirmDialog
:open="reviewSubmitOpen"
badge="提交审核"
badge-tone="info"
title="提交规则版本审核"
description="请先确认本次送审采用的版本号,并选择负责审核的高级管理员。若填写新的版本号,系统会将当前工作稿固化为该版本后再送审。"
cancel-text="取消"
confirm-text="确认提交"
busy-text="提交中..."
confirm-tone="primary"
confirm-icon="mdi mdi-send-outline"
:busy="actionState === 'review-pending'"
@close="emit('close-submit-review')"
@confirm="emit('submit-selected-rule-for-review')"
>
<div class="review-submit-form">
<label>
<span>送审版本号</span>
<input
v-model="reviewSubmitVersionModel"
type="text"
placeholder="例如v1.1.0"
:disabled="actionState === 'review-pending'"
/>
</label>
<label>
<span>审核人</span>
<EnterpriseSelect
v-model="reviewSubmitReviewerModel"
:options="reviewSubmitReviewerOptions"
:placeholder="reviewSubmitReviewerLoading ? '加载审核人中...' : '请选择高级管理员'"
:disabled="reviewSubmitReviewerLoading || actionState === 'review-pending'"
/>
</label>
<p
v-if="!reviewSubmitReviewerLoading && !hasReviewSubmitReviewers"
class="review-submit-hint"
>
当前没有可选的高级管理员请先在员工管理中配置具备管理员角色的员工
</p>
<div v-if="selectedSkillUsesJsonRisk" class="review-submit-test-state">
<span>测试确认</span>
<strong :class="{ passed: riskRuleTestPassed }">
{{ riskRuleTestPassed ? '当前版本已通过测试确认' : '当前版本尚未确认测试通过' }}
</strong>
<p>只有保存测试报告的风险规则才能提交给高级财务人员审核</p>
</div>
</div>
</ConfirmDialog>
</template>
<script setup>
import { computed } from 'vue'
import ConfirmDialog from '../shared/ConfirmDialog.vue'
import EnterpriseSelect from '../shared/EnterpriseSelect.vue'
import RiskRuleTestDialog from '../shared/RiskRuleTestDialog.vue'
defineOptions({
name: 'AuditRuleDialogs'
})
const props = defineProps({
selectedSkill: { type: Object, default: null },
versionSwitchTarget: { type: Object, default: null },
actionState: { type: String, default: '' },
riskRuleCreateOpen: { type: Boolean, default: false },
riskRuleCreateBusy: { type: Boolean, default: false },
riskRuleCreateForm: { type: Object, required: true },
riskRuleBusinessStageOptions: { type: Array, default: () => [] },
riskRuleExpenseCategoryOptions: { type: Array, default: () => [] },
riskRuleAttachmentOptions: { type: Array, default: () => [] },
riskRuleTestOpen: { type: Boolean, default: false },
riskRuleDeleteOpen: { type: Boolean, default: false },
riskRuleReturnOpen: { type: Boolean, default: false },
riskRulePublishOpen: { type: Boolean, default: false },
riskRuleReturnNote: { type: String, default: '' },
riskRuleTestPassed: { type: Boolean, default: false },
reviewSubmitOpen: { type: Boolean, default: false },
reviewSubmitVersion: { type: String, default: '' },
reviewSubmitReviewer: { type: String, default: '' },
reviewSubmitReviewerLoading: { type: Boolean, default: false },
reviewSubmitReviewerOptions: { type: Array, default: () => [] },
hasReviewSubmitReviewers: { type: Boolean, default: false },
selectedSkillUsesJsonRisk: { type: Boolean, default: false }
})
const emit = defineEmits([
'update:riskRuleReturnNote',
'update:reviewSubmitVersion',
'update:reviewSubmitReviewer',
'close-risk-rule-create',
'submit-risk-rule-create',
'close-risk-rule-test',
'report-saved',
'close-delete-risk-rule',
'delete-selected-risk-rule',
'close-return-risk-rule',
'return-selected-risk-rule',
'close-publish-risk-rule',
'publish-selected-risk-rule',
'cancel-version-switch',
'confirm-version-switch',
'close-submit-review',
'submit-selected-rule-for-review'
])
const returnNoteModel = computed({
get: () => props.riskRuleReturnNote,
set: (value) => emit('update:riskRuleReturnNote', value)
})
const reviewSubmitVersionModel = computed({
get: () => props.reviewSubmitVersion,
set: (value) => emit('update:reviewSubmitVersion', value)
})
const reviewSubmitReviewerModel = computed({
get: () => props.reviewSubmitReviewer,
set: (value) => emit('update:reviewSubmitReviewer', value)
})
</script>
<style scoped src="../../assets/styles/views/audit-view.css"></style>
<style scoped src="../../assets/styles/views/audit-view-part2.css"></style>

View File

@@ -0,0 +1,112 @@
<template>
<Transition name="drawer-fade">
<div v-if="open" class="rule-drawer-backdrop" @click.self="emit('close')">
<aside class="rule-drawer compare-drawer change-detail-drawer">
<header class="rule-drawer-head">
<div>
<span>最近修改</span>
<h3>修改详情</h3>
</div>
<button type="button" @click="emit('close')">
<i class="mdi mdi-close"></i>
</button>
</header>
<div v-if="record" class="compare-content change-detail-content">
<section class="change-detail-meta">
<article>
<span>修改人</span>
<strong>{{ record.actor }}</strong>
</article>
<article>
<span>修改时间</span>
<strong>{{ record.time }}</strong>
</article>
<article>
<span>修改工作表</span>
<strong>{{ record.changed_sheet_count }}</strong>
</article>
<article>
<span>变更单元格</span>
<strong>{{ record.changed_cell_count }}</strong>
</article>
</section>
<section class="compare-panel">
<header>
<strong>本次修改摘要</strong>
</header>
<p>{{ record.summary }}</p>
</section>
<section class="compare-panel">
<header>
<strong>工作表变化</strong>
</header>
<div v-if="sheetRows.length" class="compare-sheet-list">
<span
v-for="item in sheetRows"
:key="`${item.sheet_name}-${item.change_type}`"
:class="item.meta.tone"
>
{{ item.sheet_name }} · {{ item.meta.label }}
</span>
</div>
<p v-else>本次没有工作表级变化</p>
</section>
<section class="compare-panel compare-cell-panel">
<header>
<strong>单元格差异</strong>
<small>最多展示前 500 </small>
</header>
<div v-if="cellRows.length" class="compare-table-wrap">
<table>
<thead>
<tr>
<th>工作表</th>
<th>位置</th>
<th>类型</th>
<th>旧值</th>
<th>新值</th>
</tr>
</thead>
<tbody>
<tr
v-for="item in cellRows"
:key="`${item.sheet_name}-${item.cell}`"
>
<td>{{ item.sheet_name }}</td>
<td>{{ item.cell }}</td>
<td><b :class="item.meta.tone">{{ item.meta.label }}</b></td>
<td>{{ item.before_value ?? '-' }}</td>
<td>{{ item.after_value ?? '-' }}</td>
</tr>
</tbody>
</table>
</div>
<p v-else>本次没有发现单元格级差异</p>
</section>
</div>
</aside>
</div>
</Transition>
</template>
<script setup>
defineOptions({
name: 'AuditSpreadsheetChangeDrawer'
})
defineProps({
open: { type: Boolean, default: false },
record: { type: Object, default: null },
sheetRows: { type: Array, default: () => [] },
cellRows: { type: Array, default: () => [] }
})
const emit = defineEmits(['close'])
</script>
<style scoped src="../../assets/styles/views/audit-view.css"></style>
<style scoped src="../../assets/styles/views/audit-view-part2.css"></style>

View File

@@ -0,0 +1,159 @@
<template>
<section class="spreadsheet-editor-shell panel">
<header class="spreadsheet-editor-head asset-detail-topbar list-toolbar">
<div class="spreadsheet-editor-title asset-detail-topbar-main filter-set">
<div class="skill-badge" :class="selectedSkill.badgeTone">{{ selectedSkill.typeLabel }}</div>
<div>
<h2>{{ selectedSkill.name }}</h2>
<p>{{ selectedSkill.summary || '当前资产尚未补充说明。' }}</p>
</div>
</div>
<div class="spreadsheet-editor-actions asset-detail-topbar-meta toolbar-actions">
<span class="spreadsheet-mode-pill">
{{ selectedSpreadsheetModeLabel }}
</span>
</div>
</header>
<input
ref="fileInput"
class="spreadsheet-upload-input"
type="file"
accept=".xlsx,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
@change="emit('file-change', $event)"
/>
<div class="spreadsheet-editor-body">
<section class="spreadsheet-main-stage">
<div class="spreadsheet-editor-meta">
<span><strong>文件</strong>{{ selectedSpreadsheetFileName }}</span>
<span><strong>负责人</strong>{{ selectedSkill.owner }}</span>
<span><strong>最近更新</strong>{{ selectedSkill.updatedAt }}</span>
</div>
<div class="spreadsheet-workbench">
<div
:key="spreadsheetOnlyOfficeHostId"
:id="spreadsheetOnlyOfficeHostId"
class="rule-spreadsheet-host"
:class="{ hidden: !spreadsheetOnlyOfficeReady && !spreadsheetOnlyOfficeError }"
></div>
<TableLoadingState
v-if="spreadsheetOnlyOfficeLoading"
class="rule-spreadsheet-state"
variant="overlay"
tone="sky"
message="正在加载 Excel 规则表"
icon="mdi mdi-table-large"
:show-skeleton="false"
/>
<div v-else-if="spreadsheetOnlyOfficeError" class="rule-spreadsheet-state error">
<i class="mdi mdi-alert-circle-outline"></i>
<span>{{ spreadsheetOnlyOfficeError }}</span>
</div>
</div>
<footer class="spreadsheet-editor-foot">
<span>
{{
canEditSpreadsheetInline
? '可直接在线编辑;保存后,右侧会自动记录本次修改内容。'
: '当前为只读预览模式。'
}}
</span>
<span>右侧仅展示最近 30 次修改操作</span>
</footer>
</section>
<aside class="spreadsheet-change-center">
<header class="change-center-head">
<div>
<h3>最近修改</h3>
<p>展示最近 30 次保存后的具体改动</p>
</div>
</header>
<section class="change-center-section change-history-section">
<div v-if="selectedSpreadsheetChangeRecords.length" class="change-center-list">
<button
v-for="item in selectedSpreadsheetChangeRecords"
:key="`spreadsheet-change-${item.id || item.changed_at}-${item.actor}`"
type="button"
class="change-center-item change-record-item"
@click="emit('open-spreadsheet-change-detail', item)"
>
<div class="change-record-head">
<div>
<strong>{{ item.actor }}</strong>
<span>{{ item.time }}</span>
</div>
<b>{{ item.changeCountLabel }}</b>
</div>
<p>{{ item.summary }}</p>
<small v-if="item.sheetPreview.length">
涉及工作表{{ item.sheetPreview.join('') }}
<template v-if="item.remainingSheetCount"> {{ item.changedSheetNames.length }} </template>
</small>
<div v-if="item.previewChanges.length" class="change-record-preview">
<span
v-for="change in item.previewChanges"
:key="`${change.sheet_name}-${change.cell}`"
>
{{ change.sheet_name }}!{{ change.cell }}
{{ change.before_value ?? '-' }} {{ change.after_value ?? '-' }}
</span>
</div>
<small v-if="item.remainingChangeCount" class="change-record-more">
另有 {{ item.remainingChangeCount }} 处改动
</small>
</button>
</div>
<p v-else class="change-flow-empty">暂无修改记录</p>
</section>
</aside>
</div>
</section>
</template>
<script setup>
import { ref } from 'vue'
import TableLoadingState from '../shared/TableLoadingState.vue'
defineOptions({
name: 'AuditSpreadsheetRuleDetail'
})
defineProps({
selectedSkill: { type: Object, required: true },
selectedSpreadsheetModeLabel: { type: String, default: '' },
selectedSpreadsheetFileName: { type: String, default: '' },
selectedSpreadsheetChangeRecords: { type: Array, default: () => [] },
spreadsheetOnlyOfficeHostId: { type: String, required: true },
spreadsheetOnlyOfficeReady: { type: Boolean, default: false },
spreadsheetOnlyOfficeError: { type: String, default: '' },
spreadsheetOnlyOfficeLoading: { type: Boolean, default: false },
canEditSpreadsheetInline: { type: Boolean, default: false }
})
const emit = defineEmits(['file-change', 'open-spreadsheet-change-detail'])
const fileInput = ref(null)
function click() {
fileInput.value?.click()
}
function reset() {
if (fileInput.value) {
fileInput.value.value = ''
}
}
defineExpose({
click,
reset
})
</script>
<style scoped src="../../assets/styles/views/audit-view.css"></style>
<style scoped src="../../assets/styles/views/audit-view-part2.css"></style>

View File

@@ -0,0 +1,71 @@
<template>
<Transition name="drawer-fade">
<div v-if="open" class="rule-drawer-backdrop" @click.self="emit('close')">
<aside class="rule-drawer timeline-drawer">
<header class="rule-drawer-head">
<div>
<span>修改记录</span>
<h3>文档操作记录</h3>
</div>
<button type="button" @click="emit('close')">
<i class="mdi mdi-close"></i>
</button>
</header>
<TableLoadingState
v-if="loading"
class="rule-drawer-state"
variant="drawer"
message="正在加载操作记录"
icon="mdi mdi-history"
:show-skeleton="false"
/>
<div v-else-if="error" class="rule-drawer-state error">
<i class="mdi mdi-alert-circle-outline"></i>
<span>{{ error }}</span>
</div>
<div v-else-if="items.length" class="rule-timeline-list">
<article
v-for="item in items"
:key="`${item.event_type}-${item.version}-${item.event_time}`"
class="rule-timeline-item"
>
<i :class="[item.meta.icon, item.meta.tone]"></i>
<div>
<header>
<strong>{{ item.meta.label }}</strong>
<span>{{ item.timeLabel }}</span>
</header>
<p>{{ item.description || item.note || '暂无补充说明' }}</p>
<small>操作人{{ item.actor }}</small>
</div>
</article>
</div>
<div v-else class="rule-drawer-state">
<i class="mdi mdi-history"></i>
<span>暂无操作记录</span>
</div>
</aside>
</div>
</Transition>
</template>
<script setup>
import TableLoadingState from '../shared/TableLoadingState.vue'
defineOptions({
name: 'AuditVersionTimelineDrawer'
})
defineProps({
open: { type: Boolean, default: false },
loading: { type: Boolean, default: false },
error: { type: String, default: '' },
items: { type: Array, default: () => [] }
})
const emit = defineEmits(['close'])
</script>
<style scoped src="../../assets/styles/views/audit-view.css"></style>
<style scoped src="../../assets/styles/views/audit-view-part2.css"></style>

View File

@@ -347,8 +347,8 @@ const todoItems = [
suggestion: '补充客户单位、客户人数、我方陪同人员',
action: '去补充',
iconKey: 'hospitality',
color: '#0d9668',
accent: '#6ee7b7'
color: 'var(--theme-primary-active)',
accent: 'var(--theme-primary-soft-strong)'
},
{
title: '差旅报销单待提交',
@@ -356,8 +356,8 @@ const todoItems = [
suggestion: '补齐出发交通,可直接生成报销单',
action: '继续填写',
iconKey: 'travelDraft',
color: '#15803d',
accent: '#86efac'
color: 'var(--success-hover)',
accent: 'var(--success-line)'
},
{
title: '有 5 张票据未关联报销单',
@@ -365,8 +365,8 @@ const todoItems = [
suggestion: '其中 3 张疑似交通费,可合并生成交通报销',
action: '去整理',
iconKey: 'receipts',
color: '#2563eb',
accent: '#93c5fd'
color: 'var(--chart-blue)',
accent: 'var(--theme-primary-soft-strong)'
}
]
@@ -381,8 +381,8 @@ const progressItems = [
status: '主管审批中',
tone: 'success',
iconKey: 'flight',
color: '#0d9668',
accent: '#6ee7b7'
color: 'var(--theme-primary-active)',
accent: 'var(--theme-primary-soft-strong)'
},
{
id: 'transport',
@@ -392,8 +392,8 @@ const progressItems = [
status: '财务复核中',
tone: 'info',
iconKey: 'transport',
color: '#2563eb',
accent: '#93c5fd'
color: 'var(--chart-blue)',
accent: 'var(--theme-primary-soft-strong)'
},
{
id: 'office',
@@ -403,8 +403,8 @@ const progressItems = [
status: '已到账',
tone: 'mint',
iconKey: 'procurement',
color: '#059669',
accent: '#a7f3d0'
color: 'var(--success)',
accent: 'var(--success-line)'
}
]
@@ -453,710 +453,4 @@ watch(
)
</script>
<style scoped>
.workbench {
min-width: 0;
display: grid;
gap: 16px;
padding-bottom: 10px;
}
.assistant-hero {
position: relative;
overflow: hidden;
display: grid;
grid-template-columns: 228px minmax(0, 1fr);
gap: 18px;
padding: 20px 24px 20px 18px;
border: 1px solid rgba(16, 185, 129, 0.12);
background:
radial-gradient(circle at top left, rgba(16, 185, 129, 0.12), transparent 34%),
radial-gradient(circle at right 20%, rgba(59, 130, 246, 0.07), transparent 28%),
linear-gradient(135deg, #f7fffb 0%, #ffffff 48%, #f5fbff 100%);
}
.assistant-hero::before,
.assistant-hero::after {
content: "";
position: absolute;
border-radius: 999px;
background: rgba(16, 185, 129, 0.06);
pointer-events: none;
}
.assistant-hero::before {
right: -48px;
bottom: -58px;
width: 220px;
height: 220px;
}
.assistant-hero::after {
right: 92px;
top: -44px;
width: 140px;
height: 140px;
}
.assistant-visual {
position: relative;
min-height: 196px;
display: flex;
align-items: flex-end;
justify-content: flex-start;
padding: 0 0 10px 8px;
}
.assistant-visual::before {
content: "";
position: absolute;
inset: auto auto -78px -58px;
width: 264px;
height: 228px;
border-radius: 50%;
background: radial-gradient(circle at 48% 38%, rgba(255, 255, 255, 0.92) 0%, rgba(220, 252, 231, 0.84) 58%, rgba(220, 252, 231, 0) 100%);
pointer-events: none;
}
.assistant-visual::after {
content: "";
position: absolute;
left: 52px;
bottom: 18px;
width: 132px;
height: 18px;
border-radius: 999px;
background: rgba(16, 185, 129, 0.14);
filter: blur(12px);
pointer-events: none;
}
.assistant-glow {
position: absolute;
left: 24px;
bottom: 22px;
width: 176px;
height: 176px;
border-radius: 50%;
background: radial-gradient(circle, rgba(255, 255, 255, 0.98) 0%, rgba(236, 253, 245, 0.9) 58%, rgba(236, 253, 245, 0) 100%);
box-shadow: 0 24px 48px rgba(16, 185, 129, 0.12);
pointer-events: none;
}
.assistant-image {
position: relative;
z-index: 1;
width: 184px;
max-width: 100%;
height: auto;
object-fit: contain;
object-position: left bottom;
filter: drop-shadow(0 22px 28px rgba(15, 23, 42, 0.16));
}
.assistant-copy {
position: relative;
z-index: 1;
display: grid;
gap: 10px;
align-content: center;
}
.assistant-copy h3 {
color: #0f172a;
font-size: 26px;
line-height: 1.25;
font-weight: 800;
}
.assistant-copy p {
max-width: 760px;
color: #5b6b83;
font-size: 14px;
line-height: 1.6;
}
.assistant-input {
display: flex;
align-items: center;
min-height: 48px;
padding: 4px 14px;
border: 1px solid rgba(148, 163, 184, 0.28);
border-radius: 12px;
background: rgba(255, 255, 255, 0.92);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.7);
}
.assistant-file-input {
display: none;
}
.assistant-input textarea {
min-width: 0;
flex: 1;
height: 22px;
min-height: 22px;
max-height: 22px;
resize: none;
border: 0;
padding: 1px 0;
background: transparent;
color: #0f172a;
font-size: 15px;
line-height: 22px;
overflow: hidden;
}
.assistant-input textarea::placeholder {
color: #94a3b8;
}
.assistant-input textarea:focus {
outline: none;
}
.hero-action,
.secondary-action,
.ghost-action,
.row-action,
.link-action,
.row-link {
border: 0;
background: transparent;
}
.hero-action {
height: 40px;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 0 16px;
border-radius: 10px;
background: linear-gradient(135deg, #10b981, #059669);
color: #fff;
font-size: 14px;
font-weight: 800;
white-space: nowrap;
box-shadow: 0 10px 22px rgba(16, 185, 129, 0.18);
}
.hero-action .mdi,
.secondary-action .mdi,
.ghost-action .mdi {
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 14px;
line-height: 1;
}
.hero-action span,
.secondary-action span,
.ghost-action span {
display: inline-flex;
align-items: center;
line-height: 1;
}
.assistant-tools {
display: flex;
align-items: center;
gap: 10px;
flex-wrap: wrap;
}
.assistant-file-strip {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.assistant-file-note,
.assistant-file-chip {
display: inline-flex;
align-items: center;
min-height: 30px;
padding: 0 12px;
border-radius: 999px;
font-size: 12px;
font-weight: 700;
}
.assistant-file-note {
background: rgba(16, 185, 129, 0.1);
color: #047857;
}
.assistant-file-chip {
max-width: 220px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
border: 1px solid rgba(148, 163, 184, 0.24);
background: rgba(255, 255, 255, 0.9);
color: #475569;
}
.assistant-file-clear {
border: 0;
background: transparent;
color: #64748b;
font-size: 12px;
font-weight: 700;
cursor: pointer;
}
.ghost-action {
height: 40px;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 0 16px;
border: 1px solid rgba(15, 118, 110, 0.18);
border-radius: 10px;
background: linear-gradient(180deg, rgba(255, 255, 255, 0.94), rgba(244, 250, 247, 0.88));
color: #0f766e;
font-size: 14px;
font-weight: 700;
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.9),
0 6px 14px rgba(15, 118, 110, 0.06);
}
.ghost-action .mdi {
color: #10b981;
}
.secondary-action {
height: 40px;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 0 16px;
border: 1px solid rgba(59, 130, 246, 0.18);
border-radius: 10px;
background: linear-gradient(180deg, rgba(244, 249, 255, 0.96), rgba(234, 244, 255, 0.9));
color: #1d4ed8;
font-size: 14px;
font-weight: 700;
white-space: nowrap;
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.92),
0 6px 14px rgba(37, 99, 235, 0.08);
}
.secondary-action .mdi {
color: #2563eb;
}
.hero-action:disabled,
.secondary-action:disabled,
.ghost-action:disabled {
cursor: not-allowed;
opacity: 0.68;
box-shadow: none;
}
.workbench-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 20px;
}
.list-panel,
.policy-panel {
padding: 20px 22px;
}
.section-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-bottom: 16px;
}
.section-head h3 {
color: #0f172a;
font-size: 17px;
font-weight: 700;
}
.title-with-badge {
display: inline-flex;
align-items: center;
gap: 8px;
min-width: 0;
}
.alert-badge {
min-width: 22px;
height: 22px;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0 7px;
border-radius: 999px;
background: #ef4444;
color: #fff;
font-size: 12px;
font-weight: 800;
line-height: 1;
box-shadow: 0 6px 14px rgba(239, 68, 68, 0.22);
}
.link-action {
display: inline-flex;
align-items: center;
gap: 4px;
color: #10b981;
font-size: 14px;
font-weight: 700;
}
.list-body {
display: grid;
}
.todo-row,
.progress-row {
display: grid;
grid-template-columns: 56px minmax(0, 1fr) auto;
gap: 14px;
align-items: center;
padding: 14px 0;
border-top: 1px solid #edf2f7;
}
.todo-row:first-child,
.progress-row:first-child {
padding-top: 4px;
border-top: 0;
}
.todo-copy {
min-width: 0;
}
.todo-copy strong {
display: block;
color: #0f172a;
font-size: 15px;
font-weight: 700;
line-height: 1.4;
}
.todo-copy p {
margin-top: 4px;
color: #6b7280;
font-size: 14px;
line-height: 1.5;
}
.todo-advice {
display: flex;
align-items: flex-start;
gap: 8px;
flex-wrap: wrap;
}
.todo-advice-label {
display: inline-flex;
align-items: center;
min-height: 22px;
padding: 0 8px;
border-radius: 999px;
background: #ecfdf5;
color: #059669;
font-size: 12px;
font-weight: 800;
white-space: nowrap;
}
.todo-advice-text {
color: #64748b;
}
.row-action {
height: 38px;
padding: 0 16px;
border: 1px solid rgba(16, 185, 129, 0.36);
border-radius: 10px;
color: #10b981;
font-size: 14px;
font-weight: 700;
white-space: nowrap;
}
.progress-row {
grid-template-columns: 56px minmax(0, 1fr) minmax(84px, auto) minmax(104px, auto);
gap: 14px 16px;
}
.progress-copy strong {
margin-bottom: 2px;
}
.progress-amount {
color: #0f172a;
font-size: 20px;
font-weight: 800;
line-height: 1;
text-align: right;
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
.progress-status {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 104px;
min-height: 34px;
padding: 6px 14px;
border-radius: 999px;
font-size: 13px;
font-weight: 800;
white-space: nowrap;
justify-self: end;
}
.progress-status.success,
.policy-status.success {
background: #eafaf2;
color: #16935f;
}
.progress-status.info,
.policy-status.info {
background: #eff6ff;
color: #3b82f6;
}
.progress-status.mint {
background: #edfdf5;
color: #10b981;
}
.policy-table {
border: 1px solid #e7edf5;
border-radius: 12px;
overflow: hidden;
}
.policy-row {
display: grid;
grid-template-columns: 2.2fr 2.4fr 1fr;
gap: 16px;
align-items: center;
min-height: 56px;
padding: 0 18px;
border-top: 1px solid #edf2f7;
}
.policy-head {
min-height: 44px;
background: #f8fbff;
color: #64748b;
font-size: 12px;
font-weight: 800;
border-top: 0;
}
.policy-row strong,
.policy-row span {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.policy-row strong {
color: #0f172a;
font-size: 14px;
font-weight: 700;
}
.policy-row span {
color: #64748b;
font-size: 14px;
}
.policy-title-cell,
.policy-summary-cell {
justify-self: stretch;
text-align: left;
}
.policy-date-cell {
justify-self: center;
text-align: center;
}
@media (max-width: 1320px) {
.assistant-copy h3 {
font-size: 24px;
}
.policy-row {
grid-template-columns: 1.8fr 1.8fr 1fr;
}
}
@media (max-width: 1440px) {
.workbench {
gap: 14px;
}
.assistant-hero {
gap: 16px;
padding: 18px 20px 18px 16px;
}
.assistant-copy h3 {
font-size: 24px;
}
.assistant-visual {
min-height: 184px;
}
.assistant-image {
width: 172px;
}
.workbench-grid {
gap: 16px;
}
.list-panel,
.policy-panel {
padding: 18px 20px;
}
.policy-row {
min-height: 52px;
padding: 0 16px;
}
}
@media (max-width: 1080px) {
.assistant-hero {
grid-template-columns: 1fr;
gap: 8px;
}
.assistant-visual {
min-height: 188px;
justify-content: center;
padding: 0 0 8px;
}
.assistant-visual::before,
.assistant-visual::after,
.assistant-glow {
left: 50%;
transform: translateX(-50%);
}
.assistant-visual::before {
inset: auto auto -82px 50%;
}
.assistant-image {
width: 176px;
}
.workbench-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 860px) {
.assistant-hero,
.list-panel,
.policy-panel {
padding: 18px;
}
.assistant-input {
flex-direction: column;
align-items: stretch;
padding: 14px;
}
.assistant-visual {
min-height: 160px;
}
.assistant-glow {
width: 148px;
height: 148px;
}
.assistant-image {
width: 150px;
}
.assistant-input textarea {
height: 40px;
min-height: 40px;
max-height: 40px;
line-height: 1.5;
}
.hero-action,
.secondary-action,
.ghost-action,
.row-action {
width: 100%;
justify-content: center;
}
.assistant-file-chip {
max-width: 100%;
}
.todo-row,
.progress-row {
grid-template-columns: 56px minmax(0, 1fr);
}
.progress-amount {
grid-column: 2;
text-align: left;
font-size: 18px;
}
.row-action,
.progress-status {
grid-column: 2;
justify-self: start;
}
.policy-table {
border: 0;
border-radius: 0;
}
.policy-head {
display: none;
}
.policy-row {
grid-template-columns: 1fr;
gap: 8px;
padding: 16px 0;
border-top: 1px solid #edf2f7;
}
.policy-row strong,
.policy-row span {
white-space: normal;
}
}
</style>
<style scoped src="../../assets/styles/components/personal-workbench.css"></style>

View File

@@ -28,12 +28,22 @@ import {
BarElement,
Tooltip
} from 'chart.js'
import { resolveCssColor, useThemeColors } from '../../composables/useThemeColors.js'
ChartJS.register(CategoryScale, LinearScale, BarElement, Tooltip)
const props = defineProps({
items: { type: Array, required: true }
})
const themeColors = useThemeColors()
const resolvedItems = computed(() => {
const fallback = themeColors.value.chartPrimary
return props.items.map((item) => ({
...item,
resolvedColor: resolveCssColor(item.color, fallback)
}))
})
const medalClass = (idx) => {
if (idx === 0) return 'gold'
@@ -56,10 +66,10 @@ const formatValue = (value) => {
}
const chartData = computed(() => ({
labels: props.items.map((i) => i.name || i.shortName),
labels: resolvedItems.value.map((i) => i.name || i.shortName),
datasets: [{
data: props.items.map((i) => i.value || i.amount),
backgroundColor: props.items.map((i) => i.color),
data: resolvedItems.value.map((i) => i.value || i.amount),
backgroundColor: resolvedItems.value.map((i) => i.resolvedColor),
borderRadius: 6,
borderSkipped: false,
barPercentage: 0.7,

View File

@@ -16,6 +16,7 @@ import {
Tooltip
} from 'chart.js'
import { useAnimationProgress } from '../../composables/useAnimationProgress.js'
import { useThemeColors } from '../../composables/useThemeColors.js'
ChartJS.register(CategoryScale, LinearScale, BarElement, Tooltip, Legend)
@@ -34,6 +35,7 @@ const progress = useAnimationProgress([
() => props.occupied,
() => props.available
], 1000)
const themeColors = useThemeColors()
const currency = (value) =>
Number(value || 0).toLocaleString('zh-CN', {
@@ -77,7 +79,7 @@ const chartData = computed(() => ({
{
label: '已使用',
data: scaleSeries(usedPercent.value),
backgroundColor: '#13a66b',
backgroundColor: themeColors.value.chartPrimary,
borderRadius: 5,
borderSkipped: false,
stack: 'budgetUsage',
@@ -86,7 +88,7 @@ const chartData = computed(() => ({
{
label: '已占用',
data: scaleSeries(occupiedPercent.value),
backgroundColor: '#f59e0b',
backgroundColor: themeColors.value.warning,
borderRadius: 5,
borderSkipped: false,
stack: 'budgetUsage',

View File

@@ -9,7 +9,7 @@
</div>
<div class="donut-legend">
<div v-for="item in items" :key="item.name" class="legend-row">
<i :style="{ background: item.color }"></i>
<i :style="{ background: item.resolvedColor }"></i>
<span class="legend-name">{{ item.name }}</span>
<span class="legend-val">{{ item.display }}</span>
</div>
@@ -27,6 +27,7 @@ import {
Legend
} from 'chart.js'
import { useAnimationProgress } from '../../composables/useAnimationProgress.js'
import { resolveCssColor, useThemeColors } from '../../composables/useThemeColors.js'
ChartJS.register(ArcElement, Tooltip, Legend)
@@ -37,12 +38,21 @@ const props = defineProps({
})
const progress = useAnimationProgress([() => props.items], 1150)
const themeColors = useThemeColors()
const resolvedItems = computed(() => {
const fallback = themeColors.value.chartPrimary
return props.items.map((item) => ({
...item,
resolvedColor: resolveCssColor(item.color, fallback)
}))
})
const chartData = computed(() => ({
labels: props.items.map((i) => i.name),
labels: resolvedItems.value.map((i) => i.name),
datasets: [{
data: props.items.map((i) => Math.max(Number((i.value * progress.value).toFixed(1)), 0.001)),
backgroundColor: props.items.map((i) => i.color),
data: resolvedItems.value.map((i) => Math.max(Number((i.value * progress.value).toFixed(1)), 0.001)),
backgroundColor: resolvedItems.value.map((i) => i.resolvedColor),
borderWidth: 0,
cutout: '68%',
spacing: 3,

View File

@@ -33,6 +33,7 @@ import {
Tooltip
} from 'chart.js'
import { useAnimationProgress } from '../../composables/useAnimationProgress.js'
import { useThemeColors } from '../../composables/useThemeColors.js'
ChartJS.register(ArcElement, Tooltip)
@@ -46,12 +47,13 @@ const props = defineProps({
const ratioValue = computed(() => Number(props.ratio))
const progress = useAnimationProgress([() => props.ratio], 1150)
const animatedRatio = computed(() => Number((ratioValue.value * progress.value).toFixed(0)))
const themeColors = useThemeColors()
const chartData = computed(() => ({
labels: ['已执行', '剩余'],
datasets: [{
data: [animatedRatio.value, 100 - animatedRatio.value],
backgroundColor: ['#10b981', '#e2e8f0'],
backgroundColor: [themeColors.value.chartPrimary, '#e2e8f0'],
borderWidth: 0
}]
}))
@@ -101,7 +103,7 @@ const chartOptions = {
}
.gauge-center strong {
color: #10b981;
color: var(--chart-primary);
font-size: 22px;
font-weight: 700;
line-height: 1;

View File

@@ -1,8 +1,8 @@
<template>
<div class="log-trend-chart">
<div class="chart-legend">
<span><i style="background:#10b981"></i>日志总量</span>
<span><i style="background:#ef4444"></i>失败数</span>
<span><i :style="{ background: chartColors.primary }"></i>日志总量</span>
<span><i :style="{ background: chartColors.danger }"></i>失败数</span>
</div>
<div class="chart-body">
<Bar :data="chartData" :options="chartOptions" />
@@ -24,6 +24,7 @@ import {
Legend
} from 'chart.js'
import { useAnimationProgress } from '../../composables/useAnimationProgress.js'
import { useThemeColors } from '../../composables/useThemeColors.js'
ChartJS.register(CategoryScale, LinearScale, BarElement, PointElement, LineElement, Tooltip, Legend)
@@ -38,6 +39,11 @@ const progress = useAnimationProgress([
() => props.totals,
() => props.failures
], 1000)
const themeColors = useThemeColors()
const chartColors = computed(() => ({
primary: themeColors.value.chartPrimary,
danger: themeColors.value.chartDanger
}))
const scaleSeries = (series) =>
series.map((value) => Math.round(Number(value || 0) * progress.value))
@@ -50,7 +56,7 @@ const chartData = computed(() => ({
{
label: '日志总量',
data: scaleSeries(props.totals),
backgroundColor: '#10b981',
backgroundColor: chartColors.value.primary,
borderRadius: 4,
barPercentage: 0.58,
categoryPercentage: 0.56,
@@ -59,11 +65,11 @@ const chartData = computed(() => ({
{
label: '失败数',
data: scaleSeries(props.failures),
borderColor: '#ef4444',
borderColor: chartColors.value.danger,
backgroundColor: 'transparent',
borderWidth: 2,
pointBackgroundColor: '#ffffff',
pointBorderColor: '#ef4444',
pointBorderColor: chartColors.value.danger,
pointBorderWidth: 2,
pointRadius: 3,
pointHoverRadius: 5,

View File

@@ -1,9 +1,9 @@
<template>
<div class="trend-chart">
<div class="chart-legend">
<span><i style="background:#10b981"></i>申请量</span>
<span><i style="background:#3b82f6"></i>审批完成量</span>
<span><i style="background:#8b5cf6"></i>平均审批时长小时</span>
<span><i :style="{ background: chartColors.primary }"></i>申请量</span>
<span><i :style="{ background: chartColors.blue }"></i>审批完成量</span>
<span><i :style="{ background: chartColors.purple }"></i>平均审批时长小时</span>
</div>
<div class="chart-body">
<Bar :data="chartData" :options="chartOptions" />
@@ -26,6 +26,7 @@ import {
Legend
} from 'chart.js'
import { useAnimationProgress } from '../../composables/useAnimationProgress.js'
import { useThemeColors } from '../../composables/useThemeColors.js'
ChartJS.register(CategoryScale, LinearScale, BarElement, PointElement, LineElement, Filler, Tooltip, Legend)
@@ -42,6 +43,12 @@ const progress = useAnimationProgress([
() => props.approved,
() => props.avgHours
], 1200)
const themeColors = useThemeColors()
const chartColors = computed(() => ({
primary: themeColors.value.chartPrimary,
blue: themeColors.value.chartBlue,
purple: themeColors.value.chartPurple
}))
const scaleSeries = (series, decimals = 0) =>
series.map((value) => Number((Number(value) * progress.value).toFixed(decimals)))
@@ -52,7 +59,7 @@ const chartData = computed(() => ({
{
label: '申请量(单)',
data: scaleSeries(props.applications),
backgroundColor: '#10b981',
backgroundColor: chartColors.value.primary,
borderRadius: 4,
barPercentage: 0.6,
categoryPercentage: 0.5,
@@ -61,7 +68,7 @@ const chartData = computed(() => ({
{
label: '审批完成量(单)',
data: scaleSeries(props.approved),
backgroundColor: '#3b82f6',
backgroundColor: chartColors.value.blue,
borderRadius: 4,
barPercentage: 0.6,
categoryPercentage: 0.5,
@@ -70,11 +77,11 @@ const chartData = computed(() => ({
{
label: '平均审批时长(小时)',
data: scaleSeries(props.avgHours, 1),
borderColor: '#8b5cf6',
borderColor: chartColors.value.purple,
backgroundColor: 'transparent',
borderWidth: 2,
pointBackgroundColor: '#ffffff',
pointBorderColor: '#8b5cf6',
pointBorderColor: chartColors.value.purple,
pointBorderWidth: 2,
pointRadius: 3,
pointHoverRadius: 5,

View File

@@ -1,41 +1,57 @@
<template>
<section class="doc-filters" aria-label="单据筛选">
<div class="filter-item">
<label class="filter-item">
<span class="filter-label">申请月份</span>
<Dropdown v-model="docFilters.month" :options="docMonths" placeholder="选择月份" appendTo="body" class="filter-dropdown">
<template #option="{ option }">
{{ formatMonth(option) }}
</template>
<template #value="{ value }">
{{ formatMonth(value) }}
</template>
</Dropdown>
</div>
<div class="filter-item">
<EnterpriseSelect
v-model="docFilters.month"
:options="monthOptions"
placeholder="选择月份"
/>
</label>
<label class="filter-item">
<span class="filter-label">申请类型</span>
<Dropdown v-model="docFilters.type" :options="docTypes" placeholder="选择类型" appendTo="body" class="filter-dropdown" />
</div>
<div class="filter-item">
<EnterpriseSelect
v-model="docFilters.type"
:options="docTypes"
placeholder="选择类型"
/>
</label>
<label class="filter-item">
<span class="filter-label">单据状态</span>
<Dropdown v-model="docFilters.status" :options="docStatuses" placeholder="选择状态" appendTo="body" class="filter-dropdown" />
</div>
<EnterpriseSelect
v-model="docFilters.status"
:options="docStatuses"
placeholder="选择状态"
/>
</label>
</section>
</template>
<script setup>
import Dropdown from 'primevue/dropdown'
import { computed } from 'vue'
defineProps({
import EnterpriseSelect from '../shared/EnterpriseSelect.vue'
const props = defineProps({
docFilters: { type: Object, required: true },
docMonths: { type: Array, required: true },
docTypes: { type: Array, required: true },
docStatuses: { type: Array, required: true }
})
function formatMonth(m) {
if (!m) return ''
const [y, mm] = m.split('-')
return `${y}${parseInt(mm)}`
const monthOptions = computed(() =>
props.docMonths.map((month) => ({
label: formatMonth(month),
value: month
}))
)
function formatMonth(month) {
if (!month) return ''
const [year, rawMonth] = String(month).split('-')
return `${year}${Number.parseInt(rawMonth, 10)}`
}
</script>
@@ -43,12 +59,27 @@ function formatMonth(m) {
.doc-filters {
display: grid;
grid-template-columns: repeat(3, minmax(180px, 1fr));
gap: 14px;
padding: 16px 28px;
gap: 12px;
padding: 14px 24px;
border-bottom: 1px solid var(--line);
background: var(--surface);
}
.filter-item { display: grid; gap: 6px; }
.filter-label { color: var(--muted); font-size: 12px; font-weight: 700; }
.filter-dropdown { width: 100%; }
.filter-item {
display: grid;
gap: 6px;
}
.filter-label {
color: var(--muted);
font-size: 12px;
font-weight: 700;
}
@media (max-width: 760px) {
.doc-filters {
grid-template-columns: 1fr;
padding-inline: 16px;
}
}
</style>

View File

@@ -1,35 +1,34 @@
<template>
<section class="filters" :class="{ compact }" aria-label="筛选条件">
<template v-if="!compact">
<label>
<label class="filter-field">
<span>法人主体</span>
<select v-model="filters.entity">
<option>全部主体</option>
<option>Northstar China Ltd.</option>
<option>Northstar Singapore Pte.</option>
<option>Northstar US Inc.</option>
</select>
<EnterpriseSelect
v-model="filters.entity"
:options="entityOptions"
placeholder="选择主体"
/>
</label>
<label>
<label class="filter-field">
<span>费用类型</span>
<select v-model="filters.category">
<option>全部费用</option>
<option>机票</option>
<option>酒店</option>
<option>火车/用车</option>
<option>餐补及杂费</option>
</select>
<EnterpriseSelect
v-model="filters.category"
:options="categoryOptions"
placeholder="选择费用"
/>
</label>
<label>
<label class="filter-field">
<span>风险等级</span>
<select v-model="filters.risk">
<option>全部风险</option>
<option>高风险</option>
<option>需解释</option>
<option>低风险</option>
</select>
<EnterpriseSelect
v-model="filters.risk"
:options="riskOptions"
placeholder="选择风险"
/>
</label>
</template>
<div class="segmented-wrap" :class="{ compact }">
<span v-if="!compact">时间范围</span>
<div class="segmented" role="tablist" aria-label="处理视图">
@@ -48,6 +47,8 @@
</template>
<script setup>
import EnterpriseSelect from '../shared/EnterpriseSelect.vue'
defineProps({
filters: { type: Object, required: true },
ranges: { type: Array, required: true },
@@ -56,13 +57,35 @@ defineProps({
})
const emit = defineEmits(['update:activeRange'])
const entityOptions = [
'全部主体',
'Northstar China Ltd.',
'Northstar Singapore Pte.',
'Northstar US Inc.'
]
const categoryOptions = [
'全部费用',
'机票',
'酒店',
'火车/用车',
'餐补及杂费'
]
const riskOptions = [
'全部风险',
'高风险',
'需解释',
'低风险'
]
</script>
<style scoped>
.filters {
display: grid;
grid-template-columns: repeat(3, minmax(160px, 1fr)) auto;
gap: 14px;
gap: 12px;
padding: 0 16px 12px;
border-bottom: 1px solid var(--line);
background: #fff;
@@ -74,7 +97,7 @@ const emit = defineEmits(['update:activeRange'])
padding: 8px 16px;
}
.filters label,
.filter-field,
.segmented-wrap {
display: grid;
gap: 6px;
@@ -83,15 +106,6 @@ const emit = defineEmits(['update:activeRange'])
font-weight: 700;
}
.filters select {
height: 40px;
padding: 0 12px;
border: 1px solid #cbd5e1;
border-radius: 6px;
background: #fff;
color: var(--ink);
}
.segmented-wrap {
justify-self: end;
}
@@ -99,38 +113,40 @@ const emit = defineEmits(['update:activeRange'])
.segmented {
align-self: end;
display: inline-flex;
gap: 0;
min-height: 40px;
padding: 3px;
border-radius: 10px;
background: #f1f5f9;
min-height: 36px;
padding: 2px;
border: 1px solid #d8dee8;
border-radius: 4px;
background: #f8fafc;
}
.segmented button {
position: relative;
min-height: 34px;
padding: 0 20px;
min-height: 30px;
padding: 0 16px;
border: none;
border-radius: 8px;
border-radius: 3px;
background: transparent;
color: #64748b;
font-size: 14px;
font-weight: 500;
font-size: 13px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s ease;
transition:
background 160ms var(--ease),
color 160ms var(--ease),
box-shadow 160ms var(--ease);
white-space: nowrap;
}
.segmented button:hover:not(.active) {
color: #334155;
background: rgba(255, 255, 255, 0.5);
background: #fff;
}
.segmented button.active {
background: #fff;
color: #1e293b;
font-weight: 600;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.06);
color: var(--primary-active);
box-shadow: 0 1px 3px rgba(15, 23, 42, 0.08);
}
@media (max-width: 980px) {

View File

@@ -244,514 +244,4 @@ onBeforeUnmount(() => {
</script>
<style scoped>
.rail {
--rail-motion-duration: 320ms;
--rail-motion-ease: cubic-bezier(0.22, 1, 0.36, 1);
--rail-fade-duration: 160ms;
position: sticky;
top: 0;
width: 100%;
height: var(--desktop-stage-height, 100dvh);
min-height: var(--desktop-stage-height, 100dvh);
min-width: 0;
display: flex;
flex-direction: column;
overflow: hidden;
background: linear-gradient(180deg, rgba(255, 255, 255, 0.98), rgba(248, 251, 250, 0.96)), #fff;
border-right: 1px solid #dbe4ee;
box-shadow: 1px 0 0 rgba(15, 23, 42, 0.02);
z-index: 20;
}
.rail-brand {
position: relative;
min-height: 92px;
display: flex;
align-items: center;
justify-content: flex-start;
gap: 12px;
padding: 22px 16px 18px;
overflow: hidden;
transition:
padding var(--rail-motion-duration) var(--rail-motion-ease),
gap var(--rail-motion-duration) var(--rail-motion-ease);
}
.brand-mark {
flex: 0 0 auto;
width: 30px;
height: 30px;
display: grid;
place-items: center;
color: #07936f;
border-radius: 6px;
overflow: hidden;
transition:
width var(--rail-motion-duration) var(--rail-motion-ease),
height var(--rail-motion-duration) var(--rail-motion-ease),
margin var(--rail-motion-duration) var(--rail-motion-ease);
}
.custom-logo {
width: 100%;
height: 100%;
object-fit: contain;
}
.brand-mark svg {
width: 30px;
height: 30px;
fill: currentColor;
}
.brand-name {
flex: 1 1 auto;
min-width: 0;
max-width: 124px;
color: #0f172a;
font-size: 16px;
font-weight: 800;
white-space: nowrap;
overflow: hidden;
transition:
max-width var(--rail-motion-duration) var(--rail-motion-ease),
opacity var(--rail-fade-duration) var(--rail-motion-ease),
transform var(--rail-fade-duration) var(--rail-motion-ease);
}
.rail-collapse-btn {
position: absolute;
right: 16px;
top: 31px;
z-index: 2;
width: 30px;
height: 30px;
display: inline-grid;
place-items: center;
cursor: pointer;
border: 1px solid rgba(148, 163, 184, 0.22);
border-radius: 9px;
background: rgba(255, 255, 255, 0.86);
color: #64748b;
box-shadow: 0 6px 14px rgba(15, 23, 42, 0.06);
transition:
top var(--rail-motion-duration) var(--rail-motion-ease),
right var(--rail-motion-duration) var(--rail-motion-ease),
transform var(--rail-motion-duration) var(--rail-motion-ease),
background 180ms var(--ease),
border-color 180ms var(--ease),
color 180ms var(--ease),
box-shadow 180ms var(--ease);
}
.rail-collapse-btn:hover {
border-color: rgba(16, 185, 129, 0.28);
background: #ecfdf5;
color: #059669;
}
.rail-collapse-btn .mdi {
font-size: 18px;
line-height: 1;
}
.rail-nav {
display: flex;
flex-direction: column;
gap: 8px;
padding: 16px 20px;
overflow-y: auto;
overflow-x: hidden;
flex: 1;
transition:
padding var(--rail-motion-duration) var(--rail-motion-ease),
gap var(--rail-motion-duration) var(--rail-motion-ease);
}
.nav-btn {
width: 100%;
min-height: 48px;
position: relative;
display: flex;
align-items: center;
gap: 12px;
padding: 0 12px;
border: 1px solid transparent;
border-radius: 8px;
background: transparent;
color: #64748b;
text-align: left;
overflow: hidden;
transition:
padding var(--rail-motion-duration) var(--rail-motion-ease),
gap var(--rail-motion-duration) var(--rail-motion-ease),
background 180ms var(--ease),
border-color 180ms var(--ease),
color 180ms var(--ease);
}
.nav-btn:hover {
background: rgba(16, 185, 129, 0.07);
color: #0f9f78;
}
.nav-btn.active {
background: #ecfdf5;
border-color: rgba(16, 185, 129, 0.12);
color: #059669;
}
.nav-icon {
flex: 0 0 28px;
width: 28px;
height: 28px;
display: grid;
place-items: center;
border-radius: 7px;
color: currentColor;
transition:
flex-basis var(--rail-motion-duration) var(--rail-motion-ease),
width var(--rail-motion-duration) var(--rail-motion-ease),
height var(--rail-motion-duration) var(--rail-motion-ease);
}
.nav-btn :deep(svg) {
width: 19px;
height: 19px;
stroke: currentColor;
stroke-width: 2;
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
}
.nav-label {
flex: 1;
min-width: 0;
max-width: 128px;
color: currentColor;
font-size: 14px;
font-weight: 700;
white-space: nowrap;
overflow: hidden;
opacity: 1;
transition:
max-width var(--rail-motion-duration) var(--rail-motion-ease),
opacity var(--rail-fade-duration) var(--rail-motion-ease),
transform var(--rail-fade-duration) var(--rail-motion-ease);
}
.nav-badge {
flex: 0 0 auto;
min-width: 34px;
height: 22px;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0 8px;
border-radius: 999px;
background: #ff5b67;
color: #fff;
font-size: 12px;
font-weight: 800;
transition:
min-width var(--rail-motion-duration) var(--rail-motion-ease),
max-width var(--rail-motion-duration) var(--rail-motion-ease),
padding var(--rail-motion-duration) var(--rail-motion-ease),
opacity var(--rail-fade-duration) var(--rail-motion-ease);
}
.nav-unread-dot {
flex: 0 0 auto;
width: 8px;
height: 8px;
border: 2px solid #fff;
border-radius: 999px;
background: #ef4444;
box-shadow: 0 6px 14px rgba(239, 68, 68, 0.26);
}
.rail-user {
position: relative;
min-width: 0;
min-height: 78px;
margin: 0;
padding: 16px 20px 18px;
border-top: 1px solid #edf2f7;
transition: padding var(--rail-motion-duration) var(--rail-motion-ease);
}
.user-summary {
position: relative;
min-width: 0;
min-height: 42px;
display: flex;
align-items: center;
gap: 10px;
padding: 4px;
color: #64748b;
border-radius: 12px;
cursor: pointer;
transition:
gap var(--rail-motion-duration) var(--rail-motion-ease),
padding var(--rail-motion-duration) var(--rail-motion-ease),
background 180ms var(--ease);
}
.rail-user:hover .user-summary {
background: rgba(255, 255, 255, 0.72);
}
.user-avatar {
flex: 0 0 36px;
width: 36px;
height: 36px;
display: grid;
place-items: center;
border: 2px solid #fff;
border-radius: 999px;
background: linear-gradient(135deg, #0f9f78, #65d6b4);
box-shadow: 0 6px 14px rgba(15, 159, 120, 0.18);
color: #fff;
font-size: 14px;
font-weight: 800;
transition:
flex-basis var(--rail-motion-duration) var(--rail-motion-ease),
width var(--rail-motion-duration) var(--rail-motion-ease),
height var(--rail-motion-duration) var(--rail-motion-ease);
}
.user-copy {
flex: 1;
min-width: 0;
max-width: 116px;
display: flex;
flex-direction: column;
gap: 2px;
opacity: 1;
transition:
max-width var(--rail-motion-duration) var(--rail-motion-ease),
opacity var(--rail-fade-duration) var(--rail-motion-ease),
transform var(--rail-fade-duration) var(--rail-motion-ease);
}
.user-copy strong {
color: #334155;
font-size: 14px;
font-weight: 750;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.user-copy span {
color: #64748b;
font-size: 12px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.user-summary .mdi {
flex: 0 0 18px;
font-size: 18px;
transition:
max-width var(--rail-motion-duration) var(--rail-motion-ease),
opacity var(--rail-fade-duration) var(--rail-motion-ease);
}
.user-menu {
position: absolute;
right: 20px;
bottom: calc(100% - 6px);
min-width: 132px;
padding: 8px;
border: 1px solid rgba(226, 232, 240, 0.96);
border-radius: 12px;
background: rgba(255, 255, 255, 0.98);
box-shadow: 0 16px 32px rgba(15, 23, 42, 0.1);
opacity: 0;
transform: translateY(8px);
pointer-events: none;
transition: all 180ms var(--ease);
z-index: 4;
}
.rail-user:hover .user-menu {
opacity: 1;
transform: translateY(0);
pointer-events: auto;
}
.user-menu-item {
width: 100%;
height: 38px;
display: flex;
align-items: center;
gap: 8px;
padding: 0 12px;
border: 0;
border-radius: 9px;
background: transparent;
color: #dc2626;
font-size: 13px;
font-weight: 700;
transition: all 180ms var(--ease);
}
/* ========================================= */
/* COLLAPSED STATE */
/* ========================================= */
.rail-collapsed .rail-brand {
flex-direction: column;
align-items: center;
justify-content: flex-start;
gap: 8px;
min-height: 0;
padding: 24px 8px 14px;
}
.rail-collapsed .brand-mark {
width: 36px;
height: 36px;
margin: 0;
}
.rail-collapsed .brand-mark svg {
width: 34px;
height: 34px;
}
.rail-collapsed .brand-name {
position: absolute;
width: 1px;
height: 1px;
max-width: 0;
margin: 0;
opacity: 0;
overflow: hidden;
clip: rect(0 0 0 0);
pointer-events: none;
}
.rail-collapsed .rail-collapse-btn {
position: static;
top: auto;
right: auto;
align-self: center;
width: 36px;
height: 32px;
transform: none;
}
.rail-collapsed .rail-nav {
gap: 10px;
padding: 12px 8px;
}
.rail-collapsed .nav-btn {
justify-content: center;
padding: 0;
gap: 0;
}
.rail-collapsed .nav-icon {
width: 32px;
height: 32px;
flex: 0 0 32px;
}
.rail-collapsed .nav-label {
max-width: 0;
opacity: 0;
transform: translateX(-6px);
}
.rail-collapsed .nav-badge {
max-width: 0;
min-width: 0;
padding: 0;
opacity: 0;
overflow: hidden;
}
.rail-collapsed .nav-unread-dot {
position: absolute;
top: 10px;
right: 11px;
width: 9px;
height: 9px;
}
.rail-collapsed {
overflow: visible;
}
.rail-collapsed .rail-user {
position: relative;
z-index: 6;
padding: 14px 8px;
overflow: visible;
}
.rail-collapsed .user-summary {
justify-content: center;
padding: 4px;
gap: 0;
}
.rail-user-menu-floating {
position: fixed;
z-index: 12000;
min-width: 132px;
padding: 8px;
border: 1px solid rgba(226, 232, 240, 0.96);
border-radius: 12px;
background: rgba(255, 255, 255, 0.98);
box-shadow: 0 16px 32px rgba(15, 23, 42, 0.14);
transform: translateY(-50%);
animation: railUserMenuIn 180ms var(--rail-motion-ease) both;
}
.rail-user-menu-floating .user-menu-item {
width: 100%;
}
@keyframes railUserMenuIn {
from {
opacity: 0;
transform: translateY(-50%) translateX(-6px);
}
to {
opacity: 1;
transform: translateY(-50%) translateX(0);
}
}
.rail-collapsed .user-copy,
.rail-collapsed .user-summary .mdi {
max-width: 0;
opacity: 0;
overflow: hidden;
transform: translateX(-6px);
}
@media (max-width: 980px) {
.rail {
position: relative;
height: auto;
}
}
@media (prefers-reduced-motion: reduce) {
.rail *,
.rail *::before,
.rail *::after {
transition: none !important;
}
}
</style>
<style scoped src="../../assets/styles/components/sidebar-rail.css"></style>

View File

@@ -262,7 +262,7 @@ const workbenchKpis = computed(() => {
unit: '笔',
meta: '本月累计',
trend: monthlyCount > 0 ? 'up' : 'down',
color: '#10b981'
color: 'var(--theme-primary)'
},
{
label: '本月报销总金额',
@@ -299,10 +299,10 @@ const requestKpis = computed(() => {
const completed = Number(summary.completed ?? 0)
return [
{ label: '全部单据', value: total, delta: '当前', trend: 'up', arrow: 'mdi mdi-minus', color: '#10b981' },
{ label: '全部单据', value: total, delta: '当前', trend: 'up', arrow: 'mdi mdi-minus', color: 'var(--theme-primary)' },
{ label: '草稿', value: draft, delta: '待提交', trend: draft > 0 ? 'down' : 'up', arrow: draft > 0 ? 'mdi mdi-arrow-down' : 'mdi mdi-minus', color: '#f59e0b' },
{ label: '审批中', value: inProgress, delta: '处理中', trend: inProgress > 0 ? 'up' : 'down', arrow: inProgress > 0 ? 'mdi mdi-arrow-up' : 'mdi mdi-minus', color: '#3b82f6' },
{ label: '已完成', value: completed, delta: '已归档', trend: 'up', arrow: 'mdi mdi-arrow-up' , color: '#10b981' }
{ label: '已完成', value: completed, delta: '已归档', trend: 'up', arrow: 'mdi mdi-arrow-up' , color: 'var(--success)' }
]
})
@@ -314,10 +314,10 @@ const documentKpis = computed(() => {
const archived = Number(summary.archived ?? 0)
return [
{ label: '全部单据', value: total, delta: '当前', trend: 'up', arrow: 'mdi mdi-minus', color: '#10b981' },
{ label: '全部单据', value: total, delta: '当前', trend: 'up', arrow: 'mdi mdi-minus', color: 'var(--theme-primary)' },
{ label: '待提交', value: toSubmit, delta: '草稿待办', trend: toSubmit > 0 ? 'down' : 'up', arrow: toSubmit > 0 ? 'mdi mdi-arrow-down' : 'mdi mdi-minus', color: '#f59e0b' },
{ label: '待我处理', value: toProcess, delta: '审批待办', trend: toProcess > 0 ? 'down' : 'up', arrow: toProcess > 0 ? 'mdi mdi-arrow-down' : 'mdi mdi-minus', color: '#3b82f6' },
{ label: '已归档', value: archived, delta: '归档入账', trend: 'up', arrow: 'mdi mdi-arrow-up', color: '#10b981' }
{ label: '已归档', value: archived, delta: '归档入账', trend: 'up', arrow: 'mdi mdi-arrow-up', color: 'var(--success)' }
]
})
@@ -329,25 +329,25 @@ const logsKpis = computed(() => {
const failed = Number(summary.failed ?? 0)
return [
{ label: 'Hermes 总任务', value: total, unit: '条', meta: '当前', trend: 'up', color: '#10b981' },
{ label: 'Hermes 总任务', value: total, unit: '条', meta: '当前', trend: 'up', color: 'var(--theme-primary)' },
{ label: '运行中', value: running, unit: '条', meta: running > 0 ? '实时执行' : '暂无执行', trend: running > 0 ? 'up' : 'down', color: '#3b82f6' },
{ label: '已完成', value: completed, unit: '条', meta: total ? `占比 ${Math.round((completed / total) * 100)}%` : '等待数据', trend: 'up', color: '#10b981' },
{ label: '已完成', value: completed, unit: '条', meta: total ? `占比 ${Math.round((completed / total) * 100)}%` : '等待数据', trend: 'up', color: 'var(--success)' },
{ label: '失败数', value: failed, unit: '条', meta: failed > 0 ? '需要关注' : '运行正常', trend: failed > 0 ? 'down' : 'up', color: '#ef4444' }
]
})
const chatKpis = [
{ label: '今日已问数', value: 86, unit: '次', meta: '较昨日 +18', trend: 'up', color: '#10b981' },
{ label: '今日已问数', value: 86, unit: '次', meta: '较昨日 +18', trend: 'up', color: 'var(--theme-primary)' },
{ label: '已解决问题', value: 72, unit: '条', meta: '解决率 83.7%', trend: 'up', color: '#3b82f6' },
{ label: '知识命中率', value: '92.3', unit: '%', meta: '较昨日 +2.6%', trend: 'up', color: '#8b5cf6' },
{ label: '平均响应时长', value: 2.1, unit: 's', meta: '较昨日 -0.3s', trend: 'down', color: '#f59e0b' }
]
const approvalKpis = [
{ label: '待审批单据', value: 12, unit: '单', meta: '较昨日 +3', trend: 'up', color: '#059669' },
{ label: '待审批单据', value: 12, unit: '单', meta: '较昨日 +3', trend: 'up', color: 'var(--theme-primary)' },
{ label: '高风险单据', value: 4, unit: '单', meta: '较昨日 +1', trend: 'up', color: '#ef4444' },
{ label: '即将超时', value: 3, unit: '单', meta: '30 分钟内', trend: 'down', color: '#f59e0b' },
{ label: '今日已处理', value: 28, unit: '单', meta: '通过率 86%', trend: 'up', color: '#10b981' }
{ label: '今日已处理', value: 28, unit: '单', meta: '通过率 86%', trend: 'up', color: 'var(--success)' }
]
const knowledgeKpis = computed(() => {
@@ -360,7 +360,7 @@ const knowledgeKpis = computed(() => {
value: String(totalDocuments),
meta: '',
trend: 'up',
color: '#10b981'
color: 'var(--theme-primary)'
}
]
})
@@ -381,7 +381,7 @@ const employeeKpis = computed(() => {
unit: '人',
meta: `覆盖 ${departments} 个部门`,
trend: 'up',
color: '#10b981'
color: 'var(--theme-primary)'
},
{
label: '在职账号',
@@ -499,438 +499,4 @@ function buildPresetRangeLabel(label) {
}
</script>
<style scoped>
.topbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 24px;
padding: 18px 24px 20px;
background: #fff;
}
.topbar.chat-mode {
padding-bottom: 16px;
border-bottom: 1px solid #eef2f7;
}
.title-group {
min-width: 0;
}
.eyebrow {
display: inline-block;
margin-bottom: 8px;
padding: 3px 10px;
border-radius: 6px;
background: linear-gradient(135deg, rgba(16, 185, 129, 0.10), rgba(59, 130, 246, 0.10));
color: #0d9668;
font-size: 11px;
font-weight: 800;
letter-spacing: 1.2px;
text-transform: uppercase;
}
.topbar h1 {
color: #0f172a;
font-size: 26px;
font-weight: 800;
letter-spacing: 0;
line-height: 1.2;
}
.topbar p {
margin-top: 6px;
max-width: 720px;
color: #64748b;
font-size: 14px;
line-height: 1.5;
}
.top-actions {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 14px;
flex-wrap: wrap;
}
.range-combo {
position: relative;
display: inline-flex;
align-items: center;
gap: 8px;
}
.range-shell {
height: 42px;
display: inline-flex;
align-items: center;
padding: 3px;
border: 1px solid #d7e0ea;
border-radius: 10px;
background: #fff;
box-shadow: 0 1px 2px rgba(15, 23, 42, .04);
}
.range-meta {
height: 34px;
display: inline-flex;
align-items: center;
gap: 8px;
padding: 0 12px;
border-right: 1px solid #e2e8f0;
color: #334155;
font-size: 13px;
font-weight: 650;
white-space: nowrap;
}
.range-meta .mdi {
color: #10b981;
}
.range-tabs {
display: inline-flex;
align-items: center;
gap: 2px;
padding-left: 3px;
}
.range-tabs button {
height: 34px;
min-width: 54px;
padding: 0 14px;
border: 0;
border-radius: 8px;
background: transparent;
color: #64748b;
font-size: 13px;
font-weight: 700;
white-space: nowrap;
transition: background 160ms ease, color 160ms ease, box-shadow 160ms ease;
}
.range-tabs button:hover:not(.active) {
background: #f1f5f9;
color: #334155;
}
.range-tabs button.active {
background: #10b981;
color: #fff;
box-shadow: 0 6px 14px rgba(16, 185, 129, .18);
}
.custom-range-wrap {
position: relative;
}
.custom-range-btn {
height: 42px;
display: inline-flex;
align-items: center;
gap: 8px;
padding: 0 13px;
border: 1px solid #d7e0ea;
border-radius: 10px;
background: #fff;
color: #334155;
font-size: 13px;
font-weight: 750;
white-space: nowrap;
}
.custom-range-btn:hover,
.custom-range-btn.active {
border-color: rgba(16, 185, 129, .34);
background: #f6fffb;
color: #0f9f78;
}
.calendar-popover {
position: absolute;
top: calc(100% + 10px);
right: 0;
width: 336px;
z-index: 40;
display: grid;
gap: 14px;
padding: 16px;
border: 1px solid #d7e0ea;
border-radius: 12px;
background: #fff;
box-shadow: 0 18px 42px rgba(15, 23, 42, .16);
}
.calendar-popover header,
.calendar-popover footer {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.calendar-popover header strong {
color: #0f172a;
font-size: 15px;
}
.calendar-popover header button {
width: 30px;
height: 30px;
display: grid;
place-items: center;
border: 0;
border-radius: 8px;
background: transparent;
color: #64748b;
}
.date-fields {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
}
.date-fields label {
display: grid;
gap: 6px;
}
.date-fields span {
color: #64748b;
font-size: 12px;
font-weight: 700;
}
.date-fields input {
width: 100%;
height: 38px;
padding: 0 9px;
border: 1px solid #d7e0ea;
border-radius: 8px;
color: #0f172a;
font-size: 13px;
}
.ghost-btn,
.apply-btn {
height: 36px;
padding: 0 14px;
border-radius: 8px;
font-size: 13px;
font-weight: 750;
}
.ghost-btn {
border: 1px solid #d7e0ea;
background: #fff;
color: #334155;
}
.apply-btn {
border: 0;
background: #10b981;
color: #fff;
}
.apply-btn:disabled {
cursor: not-allowed;
background: #cbd5e1;
}
.kpi-chips {
display: flex;
gap: 10px;
}
.detail-alert-strip {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 10px;
flex-wrap: wrap;
}
.detail-alert-pill {
display: inline-flex;
align-items: center;
gap: 6px;
min-height: 32px;
padding: 0 12px;
border: 1px solid #fed7aa;
border-radius: 999px;
background: #fff7ed;
color: #ea580c;
font-size: 12px;
font-weight: 800;
white-space: nowrap;
}
.detail-alert-pill i {
font-size: 14px;
}
.detail-alert-pill.success {
border-color: #bbf7d0;
background: #f0fdf4;
color: #059669;
}
.detail-alert-pill.danger {
border-color: #fecaca;
background: #fff1f2;
color: #dc2626;
}
.kpi-chip {
display: grid;
grid-template-columns: auto auto;
grid-template-rows: auto auto;
gap: 2px 10px;
padding: 8px 16px;
border-radius: 10px;
background: linear-gradient(135deg, color-mix(in srgb, var(--chip-color) 8%, #fff), color-mix(in srgb, var(--chip-color) 3%, #f8fafc));
border: 1px solid color-mix(in srgb, var(--chip-color) 18%, #e2e8f0);
}
.chip-value {
grid-row: 1 / 3;
align-self: center;
color: #0f172a;
font-size: 22px;
font-weight: 850;
line-height: 1;
}
.chip-value small {
font-size: 13px;
font-weight: 700;
margin-left: 2px;
}
.chip-label {
color: #64748b;
font-size: 12px;
font-weight: 700;
}
.chip-delta {
color: #94a3b8;
font-size: 11px;
font-weight: 700;
}
.chip-delta.up { color: #059669; }
.chip-delta.down { color: #f59e0b; }
.topbar-spacer {
flex: 1;
min-width: 0;
}
.create-top-btn {
height: 40px;
display: inline-flex;
align-items: center;
gap: 8px;
padding: 0 18px;
border: 0;
border-radius: 8px;
background: #059669;
color: #fff;
font-size: 14px;
font-weight: 750;
box-shadow: 0 8px 18px rgba(5, 150, 105, 0.18);
white-space: nowrap;
}
.create-top-btn:hover {
background: #047857;
}
@media (max-width: 1120px) {
.range-combo {
width: 100%;
justify-content: flex-end;
}
}
@media (max-width: 960px) {
.topbar {
flex-direction: column;
align-items: stretch;
}
.top-actions,
.search-wrap,
.search-wrap.wide,
.detail-alert-strip,
.month-chip,
.qa-filter,
.new-question-btn {
width: 100%;
}
.range-combo {
justify-content: stretch;
}
.range-shell {
flex: 1;
}
.range-meta {
flex: 1;
}
.custom-range-btn {
width: 100%;
justify-content: center;
}
.calendar-popover {
right: auto;
left: 0;
}
}
@media (max-width: 640px) {
.range-combo {
display: grid;
gap: 8px;
}
.range-shell {
height: auto;
display: grid;
gap: 4px;
}
.range-meta {
width: 100%;
border-right: 0;
border-bottom: 1px solid #e2e8f0;
justify-content: center;
}
.range-tabs {
width: 100%;
padding-left: 0;
}
.range-tabs button {
flex: 1;
padding: 0 8px;
}
.calendar-popover {
width: min(336px, calc(100vw - 32px));
}
.date-fields {
grid-template-columns: 1fr;
}
}
</style>
<style scoped src="../../assets/styles/components/top-bar.css"></style>

View File

@@ -357,7 +357,7 @@ function observeResize() {
}
.graph-eyebrow {
color: #0f766e;
color: var(--theme-primary-active);
font-size: 12px;
font-weight: 850;
}
@@ -515,7 +515,7 @@ function observeResize() {
}
.inspector-title span {
color: #0f766e;
color: var(--theme-primary-active);
font-size: 12px;
font-weight: 850;
}

View File

@@ -2,8 +2,8 @@
<div class="node-detail-panel">
<section class="detail-section">
<div class="detail-section-head">
<strong>节点说明</strong>
<span>{{ safeNode.type || '实体' }}</span>
<strong>节点摘要</strong>
<span>{{ safeNode.type || '未知类型' }}</span>
</div>
<div v-if="descriptionItems.length" class="description-list">
<p v-for="(description, index) in descriptionItems" :key="index">
@@ -11,7 +11,7 @@
</p>
</div>
<div v-else class="detail-empty compact">
当前节点暂无 LightRAG 描述完成新的归集后会从图谱属性中补充
暂无描述LightRAG 还没有为该节点生成可展示的摘要
</div>
</section>
@@ -22,11 +22,11 @@
</div>
<dl class="property-grid">
<div>
<dt>类型</dt>
<dd>{{ safeNode.type || '实体' }}</dd>
<dt>节点类型</dt>
<dd>{{ safeNode.type || '未知类型' }}</dd>
</div>
<div>
<dt>系数</dt>
<dt>联度</dt>
<dd>{{ safeNode.degree || 0 }}</dd>
</div>
<div>
@@ -53,7 +53,7 @@
<section class="detail-section relation-section">
<div class="detail-section-head">
<strong>关系语境</strong>
<strong>联关</strong>
<span>{{ relationRows.length }} </span>
</div>
<div v-if="relationRows.length" class="relation-detail-list">
@@ -72,7 +72,7 @@
</div>
</button>
</div>
<div v-else class="detail-empty compact">暂无关联关系</div>
<div v-else class="detail-empty compact">暂无关联关系</div>
</section>
</div>
</template>
@@ -139,7 +139,7 @@ const relationRows = computed(() => {
raw: relation,
peerName,
type: String(relation.type || '关联').trim(),
directionLabel: isOutgoing ? '指向' : '来',
directionLabel: isOutgoing ? '指向' : '来',
description: String(relation.description || '').trim(),
keywords: dedupeTextItems(relation.keywords).slice(0, 6)
}
@@ -224,7 +224,7 @@ function formatPropertyKey(key) {
.description-list p {
margin: 0;
padding: 9px;
border: 1px solid #dbeafe;
border: 1px solid color-mix(in srgb, var(--theme-primary) 18%, #ffffff);
border-radius: 8px;
background: #fff;
color: #1e293b;
@@ -288,8 +288,8 @@ function formatPropertyKey(key) {
align-items: center;
padding: 0 7px;
border-radius: 999px;
background: #e0f2fe;
color: #075985;
background: var(--theme-primary-light-9);
color: var(--theme-primary-active);
font-size: 11px;
font-weight: 800;
}
@@ -317,7 +317,7 @@ function formatPropertyKey(key) {
grid-template-columns: auto minmax(0, 1fr) auto;
gap: 7px;
padding: 9px;
border: 1px solid #dbeafe;
border: 1px solid color-mix(in srgb, var(--theme-primary) 18%, #ffffff);
border-radius: 8px;
background: #fff;
cursor: pointer;
@@ -326,8 +326,8 @@ function formatPropertyKey(key) {
}
.relation-detail-list button:hover {
border-color: #60a5fa;
background: #eff6ff;
border-color: color-mix(in srgb, var(--theme-primary) 38%, #ffffff);
background: var(--theme-primary-light-9);
transform: translateY(-1px);
}
@@ -342,7 +342,7 @@ function formatPropertyKey(key) {
}
.relation-detail-list strong {
color: #1d4ed8;
color: var(--theme-primary-active);
}
.relation-detail-list p,
@@ -359,8 +359,8 @@ function formatPropertyKey(key) {
}
.keyword-row span {
background: #ecfdf5;
color: #047857;
background: var(--theme-primary-light-9);
color: var(--theme-primary-active);
}
.detail-empty {

View File

@@ -107,7 +107,7 @@ function formatElapsed(startedAt, finishedAt) {
}
.info-title span {
color: #0f766e;
color: var(--theme-primary-active);
font-size: 12px;
font-weight: 850;
}
@@ -129,8 +129,8 @@ function formatElapsed(startedAt, finishedAt) {
}
.info-title > strong.success {
background: #dcfce7;
color: #166534;
background: var(--success-soft);
color: var(--success-active);
}
.info-title > strong.warning {

View File

@@ -57,7 +57,7 @@ const model = computed(() => buildKnowledgeIngestLogModel(props.run))
}
.eyebrow {
color: #0f766e;
color: var(--theme-primary-active);
font-size: 12px;
font-weight: 800;
}
@@ -113,7 +113,7 @@ const model = computed(() => buildKnowledgeIngestLogModel(props.run))
display: block;
height: 100%;
border-radius: inherit;
background: linear-gradient(90deg, #0f766e, #2563eb);
background: linear-gradient(90deg, var(--theme-primary-active), var(--theme-secondary));
transition: width 0.24s ease;
}

View File

@@ -1,10 +1,12 @@
import { computed, ref, watch } from 'vue'
import { useThemeColors } from '../../composables/useThemeColors.js'
const MAX_VISIBLE_NODES = 72
const MAX_VISIBLE_EDGES = 180
const MAX_RELATION_PREVIEW = 40
const NODE_TONES = {
const BASE_NODE_TONES = {
hub: {
fill: '#2563eb',
stroke: '#dbeafe',
@@ -12,10 +14,10 @@ const NODE_TONES = {
shadow: 'rgba(37, 99, 235, 0.20)'
},
strong: {
fill: '#0f766e',
stroke: '#ccfbf1',
halo: '#5eead4',
shadow: 'rgba(15, 118, 110, 0.18)'
fill: '#3a7ca5',
stroke: '#eaf4fa',
halo: '#d4e8f3',
shadow: 'rgba(58, 124, 165, 0.18)'
},
accent: {
fill: '#d97706',
@@ -40,8 +42,18 @@ const NODE_TONES = {
export function useKnowledgeIngestGraph(props) {
const graphQuery = ref('')
const activeNodeId = ref('')
const themeColors = useThemeColors()
const allRelations = computed(() => normalizeRelations(props.graph?.relations))
const nodeTones = computed(() => ({
...BASE_NODE_TONES,
strong: {
fill: themeColors.value.chartPrimary,
stroke: themeColors.value.primarySoft,
halo: themeColors.value.primarySoft,
shadow: toRgba(themeColors.value.chartPrimary, 0.18)
}
}))
const rankedNodes = computed(() => buildRankedNodes(props.graph, allRelations.value))
const visibleNodes = computed(() => {
const query = graphQuery.value.toLowerCase()
@@ -83,7 +95,7 @@ export function useKnowledgeIngestGraph(props) {
selectedNodeRelations.value.filter((relation) => relation.source === selectedNode.value?.name)
)
const graphData = computed(() => ({
nodes: visibleNodes.value.map((node) => toG6Node(node)),
nodes: visibleNodes.value.map((node) => toG6Node(node, nodeTones.value)),
edges: visibleRelations.value
.map((relation, index) => toG6Edge(relation, index, nodeIdByName.value))
.filter(Boolean)
@@ -179,9 +191,9 @@ function buildRankedNodes(graph, relations) {
})
}
function toG6Node(node) {
function toG6Node(node, nodeTones) {
const tone = resolveNodeTone(node)
const palette = NODE_TONES[tone]
const palette = nodeTones[tone] || nodeTones.normal || BASE_NODE_TONES.normal
const size = clamp(34 + Math.sqrt(Math.max(node.degree, 1)) * 13, 38, node.rank === 1 ? 82 : 70)
const opacity = node.matchesQuery ? 1 : 0.24
@@ -241,6 +253,20 @@ function toG6Node(node) {
}
}
function toRgba(hexColor, alpha) {
const normalized = String(hexColor || '').trim()
const matched = normalized.match(/^#([0-9a-f]{6})$/i)
if (!matched) {
return `rgba(58, 124, 165, ${alpha})`
}
const value = matched[1]
const red = parseInt(value.slice(0, 2), 16)
const middle = parseInt(value.slice(2, 4), 16)
const blue = parseInt(value.slice(4, 6), 16)
return `rgba(${red}, ${middle}, ${blue}, ${alpha})`
}
function toG6Edge(relation, index, nodeIdByName) {
const sourceId = nodeIdByName.get(relation.source)
const targetId = nodeIdByName.get(relation.target)

View File

@@ -116,10 +116,10 @@ function handleCancel() {
display: grid;
gap: 14px;
padding: 24px;
border: 1px solid rgba(16, 185, 129, 0.14);
border-radius: 24px;
border: 1px solid rgba(var(--theme-primary-rgb, 58, 124, 165), 0.14);
border-radius: 8px;
background:
radial-gradient(circle at top left, rgba(16, 185, 129, 0.12), transparent 36%),
radial-gradient(circle at top left, rgba(var(--theme-primary-rgb, 58, 124, 165), 0.10), transparent 36%),
linear-gradient(180deg, rgba(255, 255, 255, 0.98), rgba(247, 250, 252, 0.98));
box-shadow: 0 28px 56px rgba(15, 23, 42, 0.18);
}
@@ -136,18 +136,18 @@ function handleCancel() {
}
.shared-confirm-badge.info {
background: rgba(59, 130, 246, 0.12);
color: #1d4ed8;
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
}
.shared-confirm-badge.warning {
background: rgba(245, 158, 11, 0.14);
color: #b45309;
background: var(--warning-soft);
color: var(--warning-active);
}
.shared-confirm-badge.danger {
background: rgba(239, 68, 68, 0.12);
color: #dc2626;
background: var(--danger-soft);
color: var(--danger-hover);
}
.shared-confirm-card h4 {
@@ -197,7 +197,7 @@ function handleCancel() {
justify-content: center;
gap: 8px;
padding: 0 16px;
border-radius: 14px;
border-radius: 6px;
font-size: 14px;
font-weight: 800;
transition: transform 160ms ease, box-shadow 160ms ease, border-color 160ms ease, background 160ms ease;
@@ -215,18 +215,18 @@ function handleCancel() {
}
.shared-confirm-btn.confirm.primary {
background: linear-gradient(135deg, #10b981, #059669);
box-shadow: 0 12px 24px rgba(5, 150, 105, 0.22);
background: linear-gradient(135deg, var(--theme-primary), var(--theme-primary-active));
box-shadow: 0 12px 24px rgba(var(--theme-primary-rgb, 58, 124, 165), 0.20);
}
.shared-confirm-btn.confirm.danger {
background: linear-gradient(135deg, #ef4444, #dc2626);
box-shadow: 0 12px 24px rgba(220, 38, 38, 0.22);
background: linear-gradient(135deg, var(--danger), var(--danger-hover));
box-shadow: 0 12px 24px rgba(var(--danger-rgb), 0.22);
}
.shared-confirm-btn.cancel:hover:not(:disabled) {
border-color: rgba(16, 185, 129, 0.3);
color: #047857;
border-color: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.3);
color: var(--theme-primary-active);
}
.shared-confirm-btn.confirm:hover:not(:disabled) {
@@ -259,7 +259,7 @@ function handleCancel() {
width: min(360px, 100%);
gap: 8px;
padding: 16px;
border-radius: 12px;
border-radius: 6px;
background: #fff;
}
@@ -281,7 +281,7 @@ function handleCancel() {
min-width: 76px;
min-height: 30px;
padding: 0 10px;
border-radius: 7px;
border-radius: 4px;
font-size: 12px;
}
@@ -292,7 +292,7 @@ function handleCancel() {
.shared-confirm-card {
padding: 20px;
border-radius: 20px;
border-radius: 8px;
}
.shared-confirm-card h4 {

View File

@@ -0,0 +1,72 @@
<template>
<ElSelect
class="enterprise-select"
popper-class="enterprise-select-popper"
:model-value="modelValue"
:placeholder="placeholder"
:disabled="disabled"
:clearable="clearable"
:filterable="filterable"
:size="size"
:teleported="teleported"
@change="handleChange"
>
<ElOption
v-for="option in normalizedOptions"
:key="option.key"
:label="option.label"
:value="option.value"
:disabled="option.disabled"
/>
</ElSelect>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({
modelValue: { type: [String, Number, Boolean], default: '' },
options: { type: Array, required: true },
placeholder: { type: String, default: '请选择' },
disabled: { type: Boolean, default: false },
clearable: { type: Boolean, default: false },
filterable: { type: Boolean, default: false },
size: { type: String, default: 'default' },
teleported: { type: Boolean, default: true }
})
const emit = defineEmits(['update:modelValue', 'change'])
const normalizedOptions = computed(() =>
props.options.map((option, index) => {
if (option && typeof option === 'object') {
const value = option.value ?? option.label ?? ''
return {
key: `${value}-${index}`,
label: String(option.label ?? value),
value,
disabled: Boolean(option.disabled)
}
}
return {
key: `${option}-${index}`,
label: String(option),
value: option,
disabled: false
}
})
)
function handleChange(value) {
emit('update:modelValue', value)
emit('change', value)
}
</script>
<style scoped>
.enterprise-select {
width: 100%;
}
</style>

View File

@@ -54,7 +54,7 @@ const props = defineProps({
artLabel: { type: String, default: 'EMPTY' },
actionLabel: { type: String, default: '' },
actionIcon: { type: String, default: '' },
tone: { type: String, default: 'emerald' },
tone: { type: String, default: 'theme' },
tips: {
type: Array,
default: () => []
@@ -66,11 +66,11 @@ defineEmits(['action'])
<style scoped>
.table-empty-state {
--accent: #10b981;
--accent-deep: #059669;
--accent-soft: rgba(16, 185, 129, 0.16);
--accent-line: rgba(16, 185, 129, 0.24);
--accent-mist: rgba(16, 185, 129, 0.08);
--accent: var(--theme-primary);
--accent-deep: var(--theme-primary-active);
--accent-soft: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.16);
--accent-line: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.24);
--accent-mist: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.08);
display: flex;
flex-direction: column;
align-items: center;
@@ -82,20 +82,21 @@ defineEmits(['action'])
width: 100%;
}
.table-empty-state.theme,
.table-empty-state.sky {
--accent: #0ea5e9;
--accent-deep: #0284c7;
--accent-soft: rgba(14, 165, 233, 0.16);
--accent-line: rgba(14, 165, 233, 0.24);
--accent-mist: rgba(14, 165, 233, 0.08);
--accent: var(--theme-primary);
--accent-deep: var(--theme-primary-active);
--accent-soft: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.16);
--accent-line: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.24);
--accent-mist: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.08);
}
.table-empty-state.amber {
--accent: #f59e0b;
--accent-deep: #d97706;
--accent-soft: rgba(245, 158, 11, 0.16);
--accent-line: rgba(245, 158, 11, 0.24);
--accent-mist: rgba(245, 158, 11, 0.08);
--accent: var(--warning);
--accent-deep: var(--warning-hover);
--accent-soft: rgba(var(--warning-rgb), 0.16);
--accent-line: rgba(var(--warning-rgb), 0.24);
--accent-mist: rgba(var(--warning-rgb), 0.08);
}
.table-empty-state.slate {
@@ -106,6 +107,14 @@ defineEmits(['action'])
--accent-mist: rgba(100, 116, 139, 0.08);
}
.table-empty-state.success {
--accent: var(--success);
--accent-deep: var(--success-hover);
--accent-soft: rgba(var(--success-rgb), 0.16);
--accent-line: rgba(var(--success-rgb), 0.24);
--accent-mist: rgba(var(--success-rgb), 0.08);
}
.table-empty-state__art {
position: relative;
width: min(240px, 100%);
@@ -144,7 +153,7 @@ defineEmits(['action'])
height: 82px;
padding: 14px 14px 12px;
border: 1px solid rgba(255, 255, 255, 0.88);
border-radius: 18px;
border-radius: 8px;
background:
linear-gradient(180deg, rgba(255, 255, 255, 0.98), rgba(245, 248, 250, 0.94));
box-shadow: 0 18px 30px rgba(15, 23, 42, 0.08);
@@ -208,7 +217,7 @@ defineEmits(['action'])
display: grid;
place-items: center;
border: 1px solid rgba(255, 255, 255, 0.86);
border-radius: 24px;
border-radius: 10px;
background:
radial-gradient(circle at 30% 20%, rgba(255, 255, 255, 0.88), rgba(255, 255, 255, 0.3) 42%),
linear-gradient(135deg, var(--accent), var(--accent-deep));
@@ -287,7 +296,7 @@ defineEmits(['action'])
gap: 8px;
padding: 0 16px;
border: 1px solid transparent;
border-radius: 12px;
border-radius: 6px;
background: linear-gradient(135deg, var(--accent), var(--accent-deep));
color: #fff;
font-size: 13px;
@@ -316,13 +325,13 @@ defineEmits(['action'])
.table-empty-state__sheet {
width: 110px;
height: 74px;
border-radius: 16px;
border-radius: 8px;
}
.table-empty-state__badge {
width: 60px;
height: 60px;
border-radius: 20px;
border-radius: 10px;
}
.table-empty-state__badge i {

View File

@@ -28,8 +28,8 @@ const props = defineProps({
},
tone: {
type: String,
default: 'emerald',
validator: (value) => ['emerald', 'sky'].includes(value)
default: 'theme',
validator: (value) => ['theme', 'sky', 'success'].includes(value)
},
title: { type: String, default: '' },
message: { type: String, default: '' },
@@ -45,15 +45,21 @@ const ariaLabel = computed(() => [props.title, props.message].filter(Boolean).jo
<style scoped>
.table-loading {
--accent: #10b981;
--accent-deep: #059669;
--accent: var(--theme-primary);
--accent-deep: var(--theme-primary-active);
width: 100%;
color: #64748b;
}
.table-loading.theme,
.table-loading.sky {
--accent: #0ea5e9;
--accent-deep: #0284c7;
--accent: var(--theme-primary);
--accent-deep: var(--theme-primary-active);
}
.table-loading.success {
--accent: var(--success);
--accent-deep: var(--success-hover);
}
.table-loading.panel {
@@ -96,7 +102,7 @@ const ariaLabel = computed(() => [props.title, props.message].filter(Boolean).jo
gap: 8px;
min-height: 0;
padding: 0;
color: #0369a1;
color: #255b7d;
}
.table-loading__spinner {

View File

@@ -19,7 +19,7 @@ import { workbenchIconMap } from '../../utils/workbenchIconAssets.js'
const props = defineProps({
iconKey: { type: String, required: true },
color: { type: String, default: '#10b981' },
color: { type: String, default: 'var(--theme-primary)' },
accent: { type: String, default: '' }
})

View File

@@ -0,0 +1,623 @@
<template>
<div
class="insight-panel-shell"
:class="{ collapsed: !ui.showInsightPanel }"
:aria-hidden="(!ui.showInsightPanel).toString()"
>
<aside class="insight-panel">
<div
v-if="!ui.isKnowledgeSession"
class="insight-head"
:class="{ 'review-mode': ui.activeReviewPayload || ui.isReviewFlowDrawer }"
>
<div>
<div v-if="!ui.activeReviewPayload && !ui.isReviewFlowDrawer" class="insight-head-eyebrow">
<span class="intent-pill" :class="ui.currentInsight.intent">{{ ui.currentIntentLabel }}</span>
</div>
<div v-else class="review-insight-title-row">
<div class="review-insight-title-copy">
<h3>{{ ui.reviewDrawerTitle }}</h3>
</div>
</div>
<h3 v-if="!ui.activeReviewPayload && !ui.isReviewFlowDrawer">{{ ui.currentInsight.title }}</h3>
<p v-if="!ui.activeReviewPayload && !ui.isReviewFlowDrawer">{{ ui.currentInsight.summary }}</p>
</div>
<div v-if="ui.activeReviewPayload || ui.isReviewFlowDrawer" class="review-insight-tools">
<button
v-if="ui.activeReviewPayload && ui.reviewOverviewDrawerAvailable"
type="button"
class="review-insight-switch-icon-btn"
:class="{
available: true,
active: ui.isReviewOverviewDrawer
}"
:disabled="ui.submitting || ui.reviewActionBusy"
title="报销识别核对"
aria-label="报销识别核对"
@click="ui.switchToReviewOverviewDrawer"
>
<i :class="ui.isReviewOverviewDrawer ? 'mdi mdi-clipboard-check' : 'mdi mdi-clipboard-check-outline'"></i>
</button>
<button
v-if="ui.activeReviewPayload && ui.reviewDocumentDrawerAvailable"
type="button"
class="review-insight-switch-icon-btn"
:class="{
available: ui.reviewDocumentDrawerAvailable,
active: ui.reviewDocumentDrawerAvailable && ui.isReviewDocumentDrawer
}"
:disabled="ui.submitting || ui.reviewActionBusy"
title="单据识别"
aria-label="单据识别"
@click="ui.toggleReviewDocumentDrawer"
>
<i :class="ui.reviewDocumentDrawerIcon"></i>
</button>
<button
v-if="ui.activeReviewPayload && ui.reviewRiskDrawerAvailable"
type="button"
class="review-insight-switch-icon-btn risk"
:class="{
available: ui.reviewRiskDrawerAvailable,
active: ui.reviewRiskDrawerAvailable && ui.isReviewRiskDrawer
}"
:disabled="ui.submitting || ui.reviewActionBusy"
title="显示风险"
aria-label="显示风险"
@click="ui.toggleReviewRiskDrawer"
>
<i :class="ui.reviewRiskDrawerIcon"></i>
</button>
<button
type="button"
class="review-insight-switch-icon-btn flow"
:class="{
available: ui.reviewFlowDrawerAvailable,
active: ui.reviewFlowDrawerAvailable && ui.isReviewFlowDrawer,
running: ui.flowOverallStatusTone === 'running'
}"
:disabled="!ui.reviewFlowDrawerAvailable || ui.submitting || ui.reviewActionBusy"
title="调用流程"
aria-label="调用流程"
@click="ui.toggleReviewFlowDrawer"
>
<i :class="ui.reviewFlowDrawerIcon"></i>
</button>
</div>
<div class="confidence-card" v-if="!ui.activeReviewPayload && !ui.isReviewFlowDrawer">
<span>{{ ui.currentInsight.metricLabel }}</span>
<strong>{{ ui.currentInsight.metricValue }}</strong>
</div>
</div>
<Transition name="insight-switch" mode="out-in">
<div
:key="`${ui.activeSessionType}-${ui.currentInsight.intent}-${ui.currentInsight.title}-${ui.reviewDrawerMode}`"
class="insight-body"
:class="{ 'document-review-body': ui.isReviewDocumentDrawer }"
>
<template v-if="ui.isKnowledgeSession">
<section class="insight-card knowledge-hot-card">
<div class="card-head">
<h4>热门问题 Top 10</h4>
</div>
<div class="knowledge-question-list">
<button
v-for="(item, index) in ui.hotKnowledgeQuestions"
:key="item"
type="button"
class="knowledge-question-btn"
:disabled="ui.submitting || ui.reviewActionBusy || ui.deleteSessionBusy || ui.sessionSwitchBusy"
@click="ui.askHotKnowledgeQuestion(item)"
>
<span
class="knowledge-question-index"
:class="ui.resolveKnowledgeRankTone(index)"
>
{{ ui.resolveKnowledgeRankLabel(index) }}
</span>
<span class="knowledge-question-copy">{{ item }}</span>
<i class="mdi mdi-arrow-top-right"></i>
</button>
</div>
</section>
</template>
<template v-else-if="ui.isReviewFlowDrawer">
<section class="review-flow-panel">
<div class="review-flow-summary">
<span class="flow-status-chip" :class="ui.flowOverallStatusTone">{{ ui.flowOverallStatusText }}</span>
<span>总耗时 {{ ui.flowTotalDurationText }}</span>
<button
type="button"
class="flow-icon-btn"
:disabled="!ui.flowRunId || ui.flowRefreshBusy"
title="刷新流程"
aria-label="刷新流程"
@click="ui.refreshFlowRunDetail"
>
<i :class="ui.flowRefreshBusy ? 'mdi mdi-loading mdi-spin' : 'mdi mdi-refresh'"></i>
</button>
</div>
<div v-if="ui.flowSteps.length" class="review-flow-list">
<article
v-for="(step, index) in ui.flowSteps"
:key="step.key"
class="flow-step-item"
:class="step.status"
>
<div class="flow-step-rail">
<span>{{ index + 1 }}</span>
</div>
<div class="flow-step-card">
<header>
<strong>{{ step.title }}</strong>
<div class="flow-step-side">
<span class="flow-step-status" :class="step.status">{{ ui.resolveFlowStepStatusLabel(step) }}</span>
<time>{{ ui.formatFlowStepDuration(step) }}</time>
</div>
</header>
<p class="flow-step-tool">工具{{ step.tool }}</p>
<p class="flow-step-detail">{{ ui.resolveFlowStepDetail(step) }}</p>
<p v-if="step.error" class="flow-step-error">{{ step.error }}</p>
</div>
</article>
</div>
<div v-else class="flow-empty-state compact">
<i class="mdi mdi-timeline-question-outline"></i>
<strong>暂无识别流程</strong>
<p>发起识别后这里会显示调用步骤和耗时</p>
</div>
</section>
</template>
<template v-else-if="ui.currentInsight.intent === 'agent' && ui.currentInsight.agent">
<template v-if="ui.activeReviewPayload">
<template v-if="ui.reviewOverviewDrawerAvailable && !ui.isReviewDocumentDrawer && !ui.isReviewRiskDrawer && !ui.isReviewFlowDrawer">
<section class="review-side-card review-side-overview-card">
<div class="review-side-intent-row">
<i class="mdi mdi-account-outline"></i>
<span>用户意图</span>
<strong>{{ ui.reviewIntentText }}</strong>
</div>
<section class="review-side-grid compact">
<article
v-for="item in ui.reviewFactCards"
:key="item.key"
class="review-side-metric-card"
:class="{
editable: item.editor,
editing: ui.reviewInlineEditorKey === item.key,
invalid: Boolean(ui.reviewInlineErrors[item.key]),
wide: item.wide
}"
@click="ui.openInlineReviewEditor(item.key)"
>
<span class="review-side-metric-icon">
<i :class="item.icon"></i>
</span>
<div class="review-side-metric-copy">
<small>{{ item.label }}</small>
<template v-if="ui.reviewInlineEditorKey === item.key && item.editor === 'date'">
<input
v-model="ui.reviewInlineForm[item.modelKey]"
class="review-inline-input"
:class="{ invalid: Boolean(ui.reviewInlineErrors[item.key]) }"
type="text"
:placeholder="`仅支持 ${ui.DATE_INPUT_FORMAT}`"
@click.stop
@input="ui.clearInlineReviewFieldError(item.key)"
@blur="ui.commitInlineReviewEditor"
@keydown.enter.prevent="ui.commitInlineReviewEditor"
/>
</template>
<template v-else-if="ui.reviewInlineEditorKey === item.key && item.editor === 'amount'">
<input
v-model="ui.reviewInlineForm[item.modelKey]"
class="review-inline-input"
:class="{ invalid: Boolean(ui.reviewInlineErrors[item.key]) }"
type="text"
:placeholder="item.placeholder"
@click.stop
@input="ui.clearInlineReviewFieldError(item.key)"
@blur="ui.commitInlineReviewEditor"
@keydown.enter.prevent="ui.commitInlineReviewEditor"
/>
</template>
<template v-else-if="ui.reviewInlineEditorKey === item.key && item.editor === 'text'">
<input
v-model="ui.reviewInlineForm[item.modelKey]"
class="review-inline-input"
:class="{ invalid: Boolean(ui.reviewInlineErrors[item.key]) }"
type="text"
:placeholder="item.placeholder"
@click.stop
@input="ui.clearInlineReviewFieldError(item.key)"
@blur="ui.commitInlineReviewEditor"
@keydown.enter.prevent="ui.commitInlineReviewEditor"
/>
</template>
<template v-else-if="ui.reviewInlineEditorKey === item.key && item.editor === 'textarea'">
<textarea
v-model="ui.reviewInlineForm[item.modelKey]"
class="review-inline-input review-inline-textarea"
:class="{ invalid: Boolean(ui.reviewInlineErrors[item.key]) }"
:placeholder="item.placeholder"
rows="3"
@click.stop
@input="ui.clearInlineReviewFieldError(item.key)"
@blur="ui.commitInlineReviewEditor"
@keydown.enter.stop
></textarea>
</template>
<template v-else-if="ui.reviewInlineEditorKey === item.key && item.editor === 'select'">
<div class="review-inline-select-list" @click.stop>
<button
v-for="scene in ui.REVIEW_SCENE_OPTIONS"
:key="scene"
type="button"
class="review-inline-select-option"
:class="{ active: ui.reviewInlineForm.scene_label === scene }"
@click.stop="ui.selectInlineScene(scene)"
>
{{ scene }}
</button>
<input
v-if="ui.reviewInlineForm.scene_label === ui.REVIEW_SCENE_OTHER_OPTION"
v-model="ui.reviewInlineForm.reason_value"
class="review-inline-input review-inline-select-custom"
:class="{ invalid: Boolean(ui.reviewInlineErrors[item.key]) }"
type="text"
placeholder="请输入具体事由"
@click.stop
@input="ui.clearInlineReviewFieldError(item.key)"
@blur="ui.commitInlineReviewEditor"
@keydown.enter.prevent="ui.commitInlineReviewEditor"
/>
</div>
</template>
<strong v-else :title="item.value">{{ item.value }}</strong>
<span v-if="ui.reviewInlineErrors[item.key]" class="review-inline-error">
{{ ui.reviewInlineErrors[item.key] }}
</span>
</div>
<span v-if="item.key !== 'attachments'" class="review-side-edit-hint">修改</span>
<span v-else class="review-side-edit-hint upload">{{ ui.reviewInlinePendingFiles.length ? '已选择' : '上传' }}</span>
</article>
</section>
</section>
<section class="review-side-card">
<div class="review-side-head">
<strong>报销分类</strong>
<span class="review-side-confidence">置信度 {{ ui.reviewPanelConfidence }}</span>
</div>
<div class="review-side-category-grid">
<button
v-for="item in ui.reviewCategoryOptions"
:key="item.key"
type="button"
class="review-side-category-card"
:class="{ active: item.active }"
@click="ui.selectReviewCategory(item)"
>
<div class="review-side-category-copy">
<strong>{{ item.label }}</strong>
<p>{{ item.is_other && ui.reviewSelectedOtherCategory ? ui.reviewSelectedOtherCategory : item.caption }}</p>
</div>
<i v-if="item.active" class="mdi mdi-check-circle review-side-group-check"></i>
</button>
</div>
<div v-if="ui.reviewOtherCategoryOpen" class="review-other-category-popover">
<button
v-for="item in ui.reviewOtherCategoryOptions"
:key="item.key"
type="button"
class="review-other-category-option"
:class="{ active: ui.reviewSelectedOtherCategory === item.label }"
@click="ui.selectReviewOtherCategory(item)"
>
{{ item.label }} · {{ item.confidenceLabel }}
</button>
</div>
</section>
</template>
<template v-else-if="ui.isReviewFlowDrawer">
<section class="review-flow-panel">
<div class="review-flow-summary">
<span class="flow-status-chip" :class="ui.flowOverallStatusTone">{{ ui.flowOverallStatusText }}</span>
<span>总耗时 {{ ui.flowTotalDurationText }}</span>
<button
type="button"
class="flow-icon-btn"
:disabled="!ui.flowRunId || ui.flowRefreshBusy"
title="刷新流程"
aria-label="刷新流程"
@click="ui.refreshFlowRunDetail"
>
<i :class="ui.flowRefreshBusy ? 'mdi mdi-loading mdi-spin' : 'mdi mdi-refresh'"></i>
</button>
</div>
<div v-if="ui.flowSteps.length" class="review-flow-list">
<article
v-for="(step, index) in ui.flowSteps"
:key="step.key"
class="flow-step-item"
:class="step.status"
>
<div class="flow-step-rail">
<span>{{ index + 1 }}</span>
</div>
<div class="flow-step-card">
<header>
<strong>{{ step.title }}</strong>
<div class="flow-step-side">
<span class="flow-step-status" :class="step.status">{{ ui.resolveFlowStepStatusLabel(step) }}</span>
<time>{{ ui.formatFlowStepDuration(step) }}</time>
</div>
</header>
<p class="flow-step-tool">工具{{ step.tool }}</p>
<p class="flow-step-detail">{{ ui.resolveFlowStepDetail(step) }}</p>
<p v-if="step.error" class="flow-step-error">{{ step.error }}</p>
</div>
</article>
</div>
<div v-else class="flow-empty-state compact">
<i class="mdi mdi-timeline-question-outline"></i>
<strong>暂无识别流程</strong>
<p>发起识别后这里会显示调用步骤和耗时</p>
</div>
</section>
</template>
<template v-else-if="ui.isReviewDocumentDrawer">
<section class="review-side-card review-document-switch-card review-ticket-drawer">
<div class="review-side-head review-document-switch-head">
<div class="review-side-head-copy">
<strong>票据识别结果卡片</strong>
<p>逐张查看 OCR 结果可直接修正后再切回核对滑窗</p>
</div>
<div class="review-document-nav">
<button
type="button"
class="review-document-nav-btn"
:disabled="ui.activeReviewDocumentIndex === 0"
aria-label="上一张票据"
@click="ui.goReviewDocument(-1)"
>
<i class="mdi mdi-chevron-left"></i>
</button>
<span>{{ ui.activeReviewDocumentIndex + 1 }} / {{ ui.reviewDocumentCount }}</span>
<button
type="button"
class="review-document-nav-btn"
:disabled="ui.activeReviewDocumentIndex >= ui.reviewDocumentCount - 1"
aria-label="下一张票据"
@click="ui.goReviewDocument(1)"
>
<i class="mdi mdi-chevron-right"></i>
</button>
</div>
</div>
<div v-if="ui.activeReviewDocument" class="review-document-stage">
<div class="review-document-stage-head">
<div class="review-document-stage-copy">
<strong :title="ui.activeReviewDocument.filename">{{ ui.activeReviewDocument.filename }}</strong>
</div>
</div>
<div class="review-document-meta-chip-row">
<span class="review-document-meta-chip">{{ ui.activeReviewDocument.documentTypeLabel }}</span>
<span class="review-document-meta-chip">{{ ui.activeReviewDocument.expenseTypeLabel }}</span>
<span class="review-document-meta-chip confidence">{{ ui.activeReviewDocument.confidenceLabel }}</span>
</div>
<div class="review-document-scroll">
<div
class="review-document-preview-card"
:class="[
ui.activeReviewDocumentPreview?.kind || 'file',
{ clickable: ui.canPreviewActiveReviewDocument }
]"
:role="ui.canPreviewActiveReviewDocument ? 'button' : null"
:tabindex="ui.canPreviewActiveReviewDocument ? 0 : null"
@click="ui.canPreviewActiveReviewDocument ? ui.openActiveReviewDocumentPreview() : null"
@keydown.enter.prevent="ui.canPreviewActiveReviewDocument ? ui.openActiveReviewDocumentPreview() : null"
@keydown.space.prevent="ui.canPreviewActiveReviewDocument ? ui.openActiveReviewDocumentPreview() : null"
>
<img
v-if="ui.activeReviewDocumentPreview?.kind === 'image' && ui.activeReviewDocumentPreview?.url"
:src="ui.activeReviewDocumentPreview.url"
:alt="ui.activeReviewDocument.filename"
/>
<div v-else-if="ui.activeReviewDocumentPreview?.kind === 'pdf'" class="review-document-preview-placeholder">
<i class="mdi mdi-file-pdf-box"></i>
<strong>PDF 票据文件</strong>
<p>当前文件还没有生成图片预览可先核对下方识别字段</p>
</div>
<div v-else class="review-document-preview-placeholder">
<i class="mdi mdi-file-search-outline"></i>
<strong>当前无可预览票据</strong>
<p>这张票据还没有可用预览可先核对下方识别字段</p>
</div>
</div>
<label class="review-document-edit-field summary">
<span>票据摘要</span>
<textarea
v-model="ui.activeReviewDocument.summary"
rows="3"
:disabled="ui.submitting || ui.reviewActionBusy"
placeholder="可根据票据图片修正 OCR 摘要"
></textarea>
</label>
<label class="review-document-edit-field">
<span>票据场景</span>
<input
v-model="ui.activeReviewDocument.scene_label"
type="text"
:disabled="ui.submitting || ui.reviewActionBusy"
placeholder="例如:出租车/网约车票据 / 火车/高铁票"
/>
</label>
<div v-if="ui.activeReviewDocument.fields.length" class="review-document-edit-grid">
<label
v-for="field in ui.activeReviewDocument.fields"
:key="`${ui.activeReviewDocument.filename}-${field.label}`"
class="review-document-edit-field"
>
<span>{{ field.label }}</span>
<input
v-model="field.value"
type="text"
:disabled="ui.submitting || ui.reviewActionBusy"
:placeholder="`修正 ${field.label}`"
/>
</label>
</div>
<div v-else class="review-side-empty compact">
<span class="review-side-empty-icon">
<i class="mdi mdi-text-recognition"></i>
</span>
<strong>暂无结构化字段</strong>
<p>当前只返回了摘要信息你仍然可以直接修改上面的票据摘要</p>
</div>
<div v-if="ui.activeReviewDocument.warnings?.length" class="review-document-warning-list">
<article
v-for="warning in ui.activeReviewDocument.warnings"
:key="`${ui.activeReviewDocument.filename}-${warning}`"
class="review-document-warning-item"
>
<i class="mdi mdi-alert-circle-outline"></i>
<span>{{ warning }}</span>
</article>
</div>
</div>
</div>
</section>
</template>
<template v-else-if="ui.isReviewRiskDrawer">
<section class="review-side-card review-side-risk-card">
<div class="review-side-head">
<div class="review-side-head-copy">
<strong>差旅合规提示</strong>
<p>结合票据识别结果与差旅规则逐项查看需要处理的风险点</p>
</div>
</div>
<p class="review-side-risk-summary">{{ ui.reviewRiskSummary }}</p>
<div v-if="ui.reviewRiskItems.length" class="review-side-risk-list">
<button
v-for="item in ui.reviewRiskItems"
:key="item.key"
type="button"
class="review-side-risk-item"
:class="item.level"
@click="ui.appendReviewRiskBriefToConversation(item)"
>
<span class="review-side-risk-icon" :title="item.levelLabel">
<i :class="item.icon"></i>
</span>
<span class="review-side-risk-copy">
<strong>{{ item.title }}</strong>
<p>{{ item.summary }}</p>
</span>
<span class="review-side-risk-meta">
<i class="mdi mdi-chevron-right"></i>
</span>
</button>
</div>
<div v-else-if="ui.reviewRiskEmpty" class="review-side-empty">
<span class="review-side-empty-icon">
<i class="mdi mdi-shield-check-outline"></i>
</span>
<strong>暂无风险提示</strong>
<p>当前没有需要额外处理的结构化风险点</p>
</div>
</section>
</template>
<button
v-if="ui.reviewHasUnsavedChanges"
type="button"
class="review-side-save-pill"
:disabled="ui.reviewActionBusy || ui.submitting"
@click="ui.saveInlineReviewChanges"
>
<i class="mdi mdi-content-save-outline"></i>
保存右侧修改
</button>
</template>
<section v-if="ui.currentInsight.agent.citations?.length && !ui.currentInsight.agent.queryPayload && !ui.activeReviewPayload" class="insight-card">
<div class="card-head">
<h4>制度依据</h4>
</div>
<div class="citation-stack">
<article v-for="item in ui.currentInsight.agent.citations" :key="item.code" class="citation-card">
<header>
<strong>{{ item.title }}</strong>
<span>{{ item.version || item.source_type }}</span>
</header>
<p>{{ item.excerpt || item.code }}</p>
</article>
</div>
</section>
<template v-if="!ui.activeReviewPayload">
<section class="insight-card primary">
<div class="card-head">
<h4>识别结果</h4>
</div>
<div class="note-block">
<strong>{{ ui.currentInsight.title }}</strong>
<p>{{ ui.currentInsight.summary }}</p>
</div>
</section>
<section v-if="ui.currentInsight.agent.riskFlags?.length" class="insight-card">
<div class="card-head">
<h4>风险标签</h4>
</div>
<div class="capability-chip-row">
<span v-for="item in ui.currentInsight.agent.riskFlags" :key="item" class="risk-chip">{{ item }}</span>
</div>
</section>
</template>
</template>
</div>
</Transition>
</aside>
</div>
</template>
<script>
export default {
name: 'TravelReimbursementInsightPanel',
props: {
ui: {
type: Object,
required: true
}
}
}
</script>
<style scoped src="../../assets/styles/components/travel-reimbursement-insight-panel.css"></style>

View File

@@ -0,0 +1,430 @@
<template>
<article
class="message-row"
:class="message.role"
>
<span class="message-avatar">
<img
:src="message.role === 'assistant' ? ui.aiAvatar : ui.userAvatar"
:alt="message.role === 'assistant' ? '财务助手头像' : '用户头像'"
/>
</span>
<div class="message-bubble" :class="ui.buildMessageBubbleClass(message)">
<header class="message-meta">
<strong>{{ message.role === 'assistant' ? (message.assistantName || ui.ASSISTANT_DISPLAY_NAME) : '我' }}</strong>
<time>{{ message.time }}</time>
</header>
<div
v-if="message.text && message.role === 'assistant' && message.reviewPayload && ui.buildReviewMainMessageText(message)"
class="review-summary message-answer-content message-answer-markdown"
v-html="ui.renderMarkdown(ui.buildReviewMainMessageText(message))"
@click="ui.handleAssistantMarkdownClick($event, message)"
></div>
<div
v-else-if="message.text && message.role !== 'assistant'"
class="message-answer-content message-answer-markdown message-rich-text"
v-html="ui.renderMarkdown(message.text)"
></div>
<div
v-else-if="message.text && message.role === 'assistant'"
class="message-answer-content message-answer-markdown"
v-html="ui.renderMarkdown(message.text)"
@click="ui.handleAssistantMarkdownClick($event, message)"
></div>
<div
v-if="message.role === 'assistant' && message.applicationPreview"
class="application-preview-table"
role="table"
aria-label="申请信息核对表"
>
<div class="application-preview-row head" role="row">
<span role="columnheader">字段</span>
<span role="columnheader">内容</span>
</div>
<div
v-for="row in ui.resolveApplicationPreviewRows(message)"
:key="`${message.id}-${row.key}`"
class="application-preview-row"
:class="{
missing: row.missing,
editable: row.editable && !ui.submitting && !ui.reviewActionBusy && !ui.sessionSwitchBusy,
highlight: row.highlight
}"
role="row"
:tabindex="row.editable && !ui.submitting && !ui.reviewActionBusy && !ui.sessionSwitchBusy ? 0 : -1"
:aria-label="row.editable ? `编辑${row.label}` : row.label"
@click="row.editable && !ui.isApplicationPreviewEditing(message, row.key) && !ui.submitting && !ui.reviewActionBusy && !ui.sessionSwitchBusy && ui.openApplicationPreviewEditor(message, row.key, row.value)"
@keydown.enter.prevent="row.editable && !ui.isApplicationPreviewEditing(message, row.key) && !ui.submitting && !ui.reviewActionBusy && !ui.sessionSwitchBusy && ui.openApplicationPreviewEditor(message, row.key, row.value)"
@keydown.space.prevent="row.editable && !ui.isApplicationPreviewEditing(message, row.key) && !ui.submitting && !ui.reviewActionBusy && !ui.sessionSwitchBusy && ui.openApplicationPreviewEditor(message, row.key, row.value)"
>
<span class="application-preview-label" role="cell">{{ row.label }}</span>
<span class="application-preview-value" role="cell">
<input
v-if="ui.isApplicationPreviewEditing(message, row.key) && ui.resolveApplicationPreviewEditorControl(row.key) !== 'select'"
v-model="ui.applicationPreviewEditor.draftValue"
class="application-preview-input"
type="text"
autofocus
@click.stop
@keydown.stop="ui.handleApplicationPreviewEditorKeydown($event, message)"
@blur="ui.commitApplicationPreviewEditor(message)"
/>
<EnterpriseSelect
v-else-if="ui.isApplicationPreviewEditing(message, row.key)"
v-model="ui.applicationPreviewEditor.draftValue"
class="application-preview-input application-preview-select"
:options="ui.resolveApplicationPreviewEditorOptions(row.key)"
clearable
:teleported="false"
autofocus
@click.stop
@change="ui.commitApplicationPreviewEditor(message)"
@keydown.stop="ui.handleApplicationPreviewEditorKeydown($event, message)"
@blur="ui.commitApplicationPreviewEditor(message)"
/>
<template v-else>
<span class="application-preview-text">{{ row.value }}</span>
<button
v-if="row.editable"
type="button"
class="application-preview-edit-btn"
title="修改内容"
aria-label="修改内容"
:disabled="ui.submitting || ui.reviewActionBusy || ui.sessionSwitchBusy"
@click.stop="ui.openApplicationPreviewEditor(message, row.key, row.value)"
>
<i class="mdi mdi-pencil-outline"></i>
</button>
</template>
</span>
</div>
</div>
<div
v-if="message.role === 'assistant' && message.applicationPreview && ui.buildApplicationPreviewFooterText(message)"
class="application-preview-footer message-answer-content message-answer-markdown"
v-html="ui.renderMarkdown(ui.buildApplicationPreviewFooterText(message))"
@click="ui.handleAssistantMarkdownClick($event, message)"
></div>
<div
v-if="message.role === 'assistant' && message.welcomeQuickActions?.length"
class="welcome-quick-actions"
>
<p class="welcome-quick-actions-title">您可以对我进行以下操作</p>
<div class="welcome-quick-action-grid">
<button
v-for="action in message.welcomeQuickActions"
:key="`${message.id}-${action.label}`"
type="button"
class="welcome-quick-action-btn"
:disabled="ui.submitting || ui.reviewActionBusy || ui.sessionSwitchBusy"
@click="ui.runWelcomeQuickAction(action)"
>
<i :class="action.icon"></i>
<span>{{ action.label }}</span>
</button>
</div>
</div>
<div v-if="message.role === 'assistant' && !message.reviewPayload && !message.queryPayload && message.meta?.length" class="message-meta-row">
<span
v-for="item in message.meta"
:key="item"
class="message-meta-chip"
:class="message.metaTone"
>
{{ item }}
</span>
</div>
<div
v-if="message.role === 'assistant' && !message.reviewPayload && !message.queryPayload && message.suggestedActions?.length"
class="message-suggested-actions"
>
<button
v-for="action in message.suggestedActions"
:key="`${message.id}-${action.action_type}-${action.label}`"
type="button"
class="message-suggested-action-btn"
:class="{
selected: ui.isSuggestedActionSelected(message, action),
locked: message.suggestedActionsLocked
}"
:disabled="message.suggestedActionsLocked || ui.submitting || ui.reviewActionBusy || ui.sessionSwitchBusy"
@click="ui.handleSuggestedAction(message, action)"
>
<span class="message-suggested-action-icon" aria-hidden="true">
<i :class="action.icon || 'mdi mdi-shape-outline'"></i>
</span>
<span class="message-suggested-action-copy">
<span class="message-suggested-action-title">{{ action.label }}</span>
<small v-if="action.description">{{ action.description }}</small>
</span>
<i class="message-suggested-action-arrow mdi mdi-arrow-right" aria-hidden="true"></i>
</button>
</div>
<div v-if="message.role === 'assistant' && !message.reviewPayload && message.riskFlags?.length" class="message-detail-block">
<strong>风险标签</strong>
<div class="message-detail-chip-row">
<span v-for="item in message.riskFlags" :key="item" class="message-risk-chip">{{ item }}</span>
</div>
</div>
<details
v-if="message.role === 'assistant' && !message.reviewPayload && !message.queryPayload && message.citations?.length"
class="message-detail-block message-citation-disclosure"
>
<summary>
<strong>引用依据</strong>
<span>{{ message.citations.length }} </span>
<i class="mdi mdi-chevron-down"></i>
</summary>
<div class="message-citation-list">
<article v-for="item in message.citations" :key="`${message.id}-${item.code}`" class="message-citation-card">
<header>
<span>{{ item.title }}</span>
<small>{{ item.version || item.source_type }}</small>
</header>
<p>{{ item.excerpt || item.code }}</p>
</article>
</div>
</details>
<div
v-if="message.role === 'assistant' && !message.reviewPayload && message.queryPayload?.resultType === 'expense_claim_list'"
class="message-detail-block expense-query-block"
>
<strong>
{{ message.queryPayload.title || (message.queryPayload.selectionMode === 'draft_association' ? '选择关联草稿' : '最近 5 条筛选结果') }}
</strong>
<p v-if="ui.buildExpenseQueryWindowLabel(message.queryPayload)" class="expense-query-window-label">
{{ ui.buildExpenseQueryWindowLabel(message.queryPayload) }}
</p>
<div v-if="message.queryPayload.statusGroups?.length" class="expense-query-summary-row">
<span
v-for="item in message.queryPayload.statusGroups"
:key="`${message.id}-${item.key}`"
class="expense-query-summary-chip"
:class="item.key"
>
{{ item.label }} {{ item.count }}
</span>
</div>
<div v-if="message.queryPayload.records?.length" class="expense-query-record-list compact">
<button
v-for="record in ui.getExpenseQueryVisibleRecords(message.queryPayload)"
:key="`${message.id}-${record.claimId}`"
type="button"
class="expense-query-record-card"
:class="{
selectable: message.queryPayload.selectionMode === 'draft_association',
selected: message.selectedQueryRecordId === record.claimId || message.queryPayload.selectedClaimId === record.claimId,
locked: message.querySelectionLocked || message.queryPayload.selectionLocked
}"
:disabled="message.queryPayload.selectionMode === 'draft_association' && (message.querySelectionLocked || message.queryPayload.selectionLocked)"
@click="ui.handleExpenseQueryRecordClick(message, record)"
>
<div class="expense-query-record-main">
<div class="expense-query-record-top">
<strong>{{ record.claimNo }}</strong>
<span class="expense-query-record-status" :class="record.statusGroup || 'other'">
{{ record.statusLabel }}
</span>
</div>
<p>{{ record.summary }}</p>
<div class="expense-query-record-meta">
<span>{{ record.expenseTypeLabel }}</span>
<span>{{ record.dateDisplay }}</span>
<span>{{ record.amountDisplay }}</span>
</div>
<div v-if="record.riskItems?.length" class="expense-query-risk-row">
<button
v-for="risk in record.riskItems"
:key="`${message.id}-${record.claimId}-${risk.key}`"
type="button"
class="expense-query-risk-chip"
:class="risk.level"
@click.stop="ui.appendExpenseQueryRiskToConversation(record, risk)"
>
<span>{{ record.claimNo }}</span>
<strong>{{ risk.levelLabel }}</strong>
<em>{{ risk.title }}</em>
</button>
</div>
</div>
<i class="mdi mdi-chevron-right"></i>
</button>
<div
v-if="ui.getExpenseQueryTotalPages(message.queryPayload) > 1"
class="expense-query-pager"
>
<button
type="button"
class="expense-query-pager-btn"
:disabled="ui.getExpenseQueryActivePage(message.queryPayload) === 1"
aria-label="上一页"
@click="ui.shiftExpenseQueryPage(message, -1)"
>
<i class="mdi mdi-chevron-left"></i>
</button>
<div class="expense-query-pager-dots" aria-label="单据分页">
<button
v-for="page in ui.getExpenseQueryTotalPages(message.queryPayload)"
:key="`${message.id}-query-page-${page}`"
type="button"
class="expense-query-pager-dot"
:class="{ active: ui.getExpenseQueryActivePage(message.queryPayload) === page }"
:aria-label="` ${page} `"
@click="ui.setExpenseQueryPage(message, page)"
></button>
</div>
<button
type="button"
class="expense-query-pager-btn"
:disabled="ui.getExpenseQueryActivePage(message.queryPayload) === ui.getExpenseQueryTotalPages(message.queryPayload)"
aria-label="下一页"
@click="ui.shiftExpenseQueryPage(message, 1)"
>
<i class="mdi mdi-chevron-right"></i>
</button>
</div>
</div>
<div v-else class="expense-query-empty">
<i class="mdi mdi-file-search-outline"></i>
<span>{{ message.queryPayload.emptyText || '当前没有可直接展开的近期待办单据。' }}</span>
</div>
<p
v-if="ui.buildExpenseQueryHint(message.queryPayload)"
class="expense-query-hint message-answer-markdown"
v-html="ui.renderMarkdown(ui.buildExpenseQueryHint(message.queryPayload))"
@click="ui.handleAssistantMarkdownClick($event, message)"
>
</p>
</div>
<div v-if="message.role === 'assistant' && message.reviewPayload" class="message-detail-block review-message-block">
<div class="review-plain-followup">
<template
v-for="followup in [ui.buildReviewPlainFollowupForMessage(message)]"
:key="`${message.id}-review-followup`"
>
<h3
class="review-plain-lead"
:class="{ danger: followup.tone === 'danger' }"
>
{{ followup.lead }}
</h3>
<p v-if="followup.summary" class="review-plain-summary">
{{ followup.summary }}
</p>
<ul v-if="followup.items.length" class="review-plain-list">
<li
v-for="item in followup.items"
:key="`${message.id}-${item.key}`"
>
<span class="review-plain-label">{{ item.label }}</span>
<span>{{ item.text }}</span>
</li>
</ul>
<p
v-for="line in followup.notes"
:key="`${message.id}-note-${line}`"
class="review-plain-note"
>
{{ line }}
</p>
<p v-if="ui.canUseInlineSaveDraft(message)" class="review-inline-save-copy">
请核查上面的关键信息您也可以暂时不处理上述的这些内容我可以帮你先保存为
<button
type="button"
class="review-inline-draft-link"
:disabled="ui.submitting || ui.reviewActionBusy || ui.sessionSwitchBusy"
@click="ui.handleInlineSaveDraft(message)"
>
草稿
</button>
</p>
</template>
<div
v-if="ui.buildReviewNextStepRichCopyForMessage(message)"
class="review-next-step-rich-copy message-answer-markdown"
v-html="ui.renderMarkdown(ui.buildReviewNextStepRichCopyForMessage(message))"
@click="ui.handleAssistantMarkdownClick($event, message)"
></div>
<div
v-if="ui.resolveReviewFooterActions(message.reviewPayload).length"
class="review-footer-actions"
>
<div class="review-footer-btn-row">
<button
v-for="action in ui.resolveReviewFooterActions(message.reviewPayload)"
:key="`${message.id}-${action.action_type}`"
type="button"
:class="['review-footer-btn', action.emphasis === 'primary' ? 'primary' : '']"
:disabled="ui.submitting || ui.reviewActionBusy || ui.sessionSwitchBusy"
@click="ui.handleReviewAction(message, action)"
>
{{ action.label || ui.buildReviewPrimaryButtonLabel(message.reviewPayload, message.draftPayload) }}
</button>
</div>
</div>
</div>
</div>
<div v-if="message.role === 'assistant' && !message.reviewPayload && message.draftPayload" class="draft-preview">
<header>
<strong>{{ message.draftPayload.title }}</strong>
<span>待人工确认</span>
</header>
<pre>{{ message.draftPayload.body }}</pre>
</div>
<div v-if="message.attachments?.length" class="message-files">
<span v-for="file in message.attachments" :key="file" class="file-chip">
<i class="mdi mdi-paperclip"></i>
{{ file }}
</span>
</div>
</div>
</article>
</template>
<script>
import EnterpriseSelect from '../shared/EnterpriseSelect.vue'
export default {
name: 'TravelReimbursementMessageItem',
components: {
EnterpriseSelect
},
props: {
message: {
type: Object,
required: true
},
ui: {
type: Object,
required: true
}
}
}
</script>
<style scoped src="../../assets/styles/components/travel-reimbursement-message-item.css"></style>

View File

@@ -0,0 +1,62 @@
<template>
<ConfirmDialog
:open="open"
:badge="badge"
badge-tone="info"
:title="title"
:description="description"
cancel-text="返回核对"
:confirm-text="confirmText"
:busy-text="busyText"
confirm-tone="primary"
confirm-icon="mdi mdi-check-circle-outline"
:busy="busy"
@close="emit('close')"
@confirm="emit('confirm')"
>
<div class="submit-confirm-summary" aria-label="领导审批通过摘要">
<div class="submit-confirm-row">
<span>单据编号</span>
<strong>{{ documentNo }}</strong>
</div>
<div class="submit-confirm-row">
<span>当前节点</span>
<strong>{{ node }}</strong>
</div>
<div class="submit-confirm-row">
<span>{{ summaryLabel }}</span>
<strong>{{ nextStage }}</strong>
</div>
<div class="submit-confirm-row">
<span>{{ opinionTitle }}</span>
<strong>{{ normalizedOpinion }}</strong>
</div>
</div>
</ConfirmDialog>
</template>
<script setup>
import { computed } from 'vue'
import ConfirmDialog from '../shared/ConfirmDialog.vue'
const props = defineProps({
open: { type: Boolean, required: true },
badge: { type: String, required: true },
title: { type: String, required: true },
description: { type: String, required: true },
confirmText: { type: String, required: true },
busyText: { type: String, required: true },
busy: { type: Boolean, required: true },
documentNo: { type: [String, Number], required: true },
node: { type: String, default: '' },
summaryLabel: { type: String, required: true },
nextStage: { type: String, required: true },
opinionTitle: { type: String, required: true },
opinion: { type: String, default: '' }
})
const emit = defineEmits(['close', 'confirm'])
const normalizedOpinion = computed(() => props.opinion.trim() || '未填写')
</script>

View File

@@ -0,0 +1,31 @@
<template>
<ConfirmDialog
:open="open"
:badge="badge"
badge-tone="danger"
:title="title"
:description="description"
cancel-text="取消"
confirm-text="确认删除"
busy-text="删除中..."
confirm-tone="danger"
confirm-icon="mdi mdi-trash-can-outline"
:busy="busy"
@close="emit('close')"
@confirm="emit('confirm')"
/>
</template>
<script setup>
import ConfirmDialog from '../shared/ConfirmDialog.vue'
defineProps({
open: { type: Boolean, required: true },
badge: { type: String, required: true },
title: { type: String, required: true },
description: { type: String, required: true },
busy: { type: Boolean, required: true }
})
const emit = defineEmits(['close', 'confirm'])
</script>

View File

@@ -0,0 +1,23 @@
<template>
<ReturnReasonDialog
:open="open"
:title="title"
:description="description"
:busy="busy"
@close="emit('close')"
@confirm="emit('confirm', $event)"
/>
</template>
<script setup>
import ReturnReasonDialog from '../shared/ReturnReasonDialog.vue'
defineProps({
open: { type: Boolean, required: true },
title: { type: String, required: true },
description: { type: String, required: true },
busy: { type: Boolean, required: true }
})
const emit = defineEmits(['close', 'confirm'])
</script>

View File

@@ -25,11 +25,11 @@ export function useOverviewView() {
}
const demoDepartments = [
{ name: '销售部', amount: 182000, color: '#10b981' },
{ name: '研发中心', amount: 146000, color: '#3b82f6' },
{ name: '市场部', amount: 96000, color: '#f59e0b' },
{ name: '运营部', amount: 68600, color: '#8b5cf6' },
{ name: '行政部', amount: 48300, color: '#3b82f6' }
{ name: '销售部', amount: 182000, color: 'var(--theme-primary)' },
{ name: '研发中心', amount: 146000, color: 'var(--chart-blue)' },
{ name: '市场部', amount: 96000, color: 'var(--chart-amber)' },
{ name: '运营部', amount: 68600, color: 'var(--chart-purple)' },
{ name: '行政部', amount: 48300, color: 'var(--chart-blue)' }
]
const formatCompact = (value) => {

View File

@@ -1,6 +1,7 @@
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
import { useSystemState } from './useSystemState.js'
import { useThemeSkin } from './useThemeSkin.js'
import { fetchSettings, saveSettings } from '../services/settings.js'
import { useToast } from './useToast.js'
import {
@@ -28,6 +29,12 @@ import {
export function useSettings() {
const { toast } = useToast()
const { companyProfile, currentUser, updateCompanyProfilePreview } = useSystemState()
const {
activeThemeSkin,
activeThemeSkinId,
setThemeSkin,
themeSkinOptions
} = useThemeSkin()
const buildResolvedDefaults = () => buildDefaultState(companyProfile.value, currentUser.value)
const pageState = ref(mergeState(buildResolvedDefaults(), readStoredSettings()))
@@ -40,6 +47,11 @@ export function useSettings() {
const logLevels = LOG_LEVELS
const providerOptions = PROVIDER_OPTIONS
const sessionRetentionOptions = SESSION_RETENTION_OPTIONS
const archiveCycleOptions = [
{ label: '按天归档', value: 'daily' },
{ label: '按周归档', value: 'weekly' },
{ label: '按月归档', value: 'monthly' }
]
const sectionStatus = computed(() => computeSectionStatus(pageState.value))
const completedSectionCount = computed(() => Object.values(sectionStatus.value).filter(Boolean).length)
@@ -293,6 +305,14 @@ export function useSettings() {
})
}
function selectThemeSkin(skinId) {
setThemeSkin(skinId)
}
function saveAppearanceSection() {
toast('界面皮肤已应用到当前浏览器。')
}
async function saveLlmSection() {
const llmForm = pageState.value.llmForm
const modelConfigs = [
@@ -391,6 +411,11 @@ export function useSettings() {
return
}
if (activeSection.value === 'appearance') {
saveAppearanceSection()
return
}
if (activeSection.value === 'session') {
await saveSessionSection()
return
@@ -460,6 +485,9 @@ export function useSettings() {
return {
activeSection,
activeSectionConfig,
activeThemeSkin,
activeThemeSkinId,
archiveCycleOptions,
activateSection,
clearRenderSecretMask,
completedSectionCount,
@@ -473,7 +501,9 @@ export function useSettings() {
saveActiveSection,
sectionStatus,
sections,
selectThemeSkin,
selectSessionRetentionDays,
themeSkinOptions,
toggleSessionRetentionPicker,
closeSessionRetentionPicker,
toggleBoolean,

View File

@@ -0,0 +1,64 @@
import { computed } from 'vue'
import { useThemeSkin } from './useThemeSkin.js'
const DEFAULT_COLORS = {
primary: '#3a7ca5',
primaryActive: '#255b7d',
primarySoft: '#eaf4fa',
success: '#2f855a',
warning: '#b7791f',
danger: '#ef4444',
info: '#475569',
chartBlue: '#4f6f9f',
chartPurple: '#6e7fa6',
chartAmber: '#b58b4c'
}
function readCssVariable(name, fallback) {
if (typeof window === 'undefined') {
return fallback
}
const value = window.getComputedStyle(document.documentElement).getPropertyValue(name).trim()
return value || fallback
}
export function resolveCssColor(value, fallback = DEFAULT_COLORS.primary) {
if (typeof value !== 'string') {
return fallback
}
const cssVariableMatch = value.match(/^var\((--[^,\s)]+)(?:,\s*([^)]+))?\)$/)
if (!cssVariableMatch) {
return value
}
return readCssVariable(cssVariableMatch[1], cssVariableMatch[2]?.trim() || fallback)
}
export function useThemeColors() {
const { activeThemeSkinId } = useThemeSkin()
return computed(() => {
activeThemeSkinId.value
const primary = readCssVariable('--theme-primary', DEFAULT_COLORS.primary)
const primaryActive = readCssVariable('--theme-primary-active', DEFAULT_COLORS.primaryActive)
return {
primary,
primaryActive,
primarySoft: readCssVariable('--theme-primary-soft', DEFAULT_COLORS.primarySoft),
success: readCssVariable('--success', DEFAULT_COLORS.success),
warning: readCssVariable('--warning', DEFAULT_COLORS.warning),
danger: readCssVariable('--danger', DEFAULT_COLORS.danger),
info: readCssVariable('--info', DEFAULT_COLORS.info),
chartPrimary: resolveCssColor(readCssVariable('--chart-primary', primary), primary),
chartBlue: resolveCssColor(readCssVariable('--chart-blue', DEFAULT_COLORS.chartBlue), DEFAULT_COLORS.chartBlue),
chartPurple: resolveCssColor(readCssVariable('--chart-purple', DEFAULT_COLORS.chartPurple), DEFAULT_COLORS.chartPurple),
chartAmber: resolveCssColor(readCssVariable('--chart-amber', DEFAULT_COLORS.chartAmber), DEFAULT_COLORS.chartAmber),
chartDanger: resolveCssColor(readCssVariable('--chart-danger', DEFAULT_COLORS.danger), DEFAULT_COLORS.danger)
}
})
}

View File

@@ -0,0 +1,282 @@
import { computed, ref } from 'vue'
const THEME_SKIN_STORAGE_KEY = 'x-financial-theme-skin'
const DEFAULT_THEME_SKIN_ID = 'sky'
const DEFAULT_SEMANTIC_COLORS = {
success: '#2f855a',
successHover: '#276749',
successActive: '#22543d',
successSoft: '#f0f7f2',
successLine: '#cde6d5',
warning: '#b7791f',
warningHover: '#975a16',
warningActive: '#7b4514',
warningSoft: '#fff8eb',
warningLine: '#efd9af',
danger: '#dc2626',
dangerHover: '#b91c1c',
dangerActive: '#991b1b',
dangerSoft: '#fef2f2',
dangerLine: '#fecaca',
info: '#475569',
infoHover: '#334155',
infoActive: '#1e293b',
infoSoft: '#f1f5f9',
infoLine: '#cbd5e1'
}
export const THEME_SKIN_OPTIONS = [
{
id: 'sky',
label: '浅蓝企业',
desc: '默认皮肤,降低蓝色饱和度,适合财务 SaaS 和审批后台。',
primary: '#3a7ca5',
primaryHover: '#2f6d95',
primaryActive: '#255b7d',
primarySoft: '#eaf4fa',
primarySoftStrong: '#d4e8f3',
secondary: '#4f6f9f',
chartBlue: '#4f6f9f',
chartPurple: '#6e7fa6',
chartAmber: '#b58b4c'
},
{
id: 'blue',
label: '湖蓝灰',
desc: '偏灰的湖蓝色,弱化科技感,更适合高密度运营页面。',
primary: '#477c9e',
primaryHover: '#3a6a89',
primaryActive: '#305873',
primarySoft: '#edf5f8',
primarySoftStrong: '#d8e8ef',
secondary: '#5d7288',
chartBlue: '#477c9e',
chartPurple: '#77799c',
chartAmber: '#b28a54'
},
{
id: 'navy',
label: '稳健蓝',
desc: '偏金融和管理驾驶舱的稳重蓝,适合长时间办公查看。',
primary: '#4b6f95',
primaryHover: '#405f80',
primaryActive: '#354e69',
primarySoft: '#eef3f8',
primarySoftStrong: '#dbe6f0',
secondary: '#6b7280',
chartBlue: '#4b6f95',
chartPurple: '#69769d',
chartAmber: '#aa8a55'
},
{
id: 'teal',
label: '雾青',
desc: '保留绿色倾向但降低鲜艳度,比旧绿色更克制。',
primary: '#3f827c',
primaryHover: '#36706b',
primaryActive: '#2d5c58',
primarySoft: '#eef8f6',
primarySoftStrong: '#d8ebe8',
secondary: '#4f6f9f',
chartBlue: '#4f7f9f',
chartPurple: '#708099',
chartAmber: '#b18a53'
},
{
id: 'legacy-green',
label: '经典绿',
desc: '保留旧版系统绿色,适合继续沿用原有品牌记忆。',
primary: '#10b981',
primaryHover: '#059669',
primaryActive: '#047857',
primarySoft: '#ecfdf5',
primarySoftStrong: '#d1fae5',
secondary: '#2563eb',
chartBlue: '#2563eb',
chartPurple: '#6d6a9f',
chartAmber: '#b88a44'
},
{
id: 'sage',
label: '鼠尾草绿',
desc: '低饱和灰绿色,比经典绿更安静,适合企业内控场景。',
primary: '#5f8d72',
primaryHover: '#517b62',
primaryActive: '#436653',
primarySoft: '#f0f7f2',
primarySoftStrong: '#dcebe0',
secondary: '#4f6f9f',
chartBlue: '#4f748f',
chartPurple: '#7a7898',
chartAmber: '#a98753'
},
{
id: 'slate',
label: '石板灰蓝',
desc: '弱主色方案,适合审计、规则和报表密集页面。',
primary: '#64748b',
primaryHover: '#526174',
primaryActive: '#3f4a5a',
primarySoft: '#f1f5f9',
primarySoftStrong: '#e2e8f0',
secondary: '#3a7ca5',
chartBlue: '#5d7590',
chartPurple: '#77748f',
chartAmber: '#a88955'
},
{
id: 'soft-violet',
label: '灰紫蓝',
desc: '保留一点智能系统气质,但用灰度压低 AI 感和饱和度。',
primary: '#6d6a9f',
primaryHover: '#5f5b8c',
primaryActive: '#504c78',
primarySoft: '#f2f1f8',
primarySoftStrong: '#e2e0ef',
secondary: '#477c9e',
chartBlue: '#4f7495',
chartPurple: '#6d6a9f',
chartAmber: '#a98857'
}
]
const activeThemeSkinId = ref(DEFAULT_THEME_SKIN_ID)
function findThemeSkin(id) {
return THEME_SKIN_OPTIONS.find((skin) => skin.id === id) || THEME_SKIN_OPTIONS[0]
}
function hexToRgb(hex) {
const normalized = String(hex || '').replace('#', '')
const value = normalized.length === 3
? normalized.split('').map((item) => item + item).join('')
: normalized
const numberValue = Number.parseInt(value, 16)
if (!Number.isFinite(numberValue) || value.length !== 6) {
return '58, 124, 165'
}
return [
(numberValue >> 16) & 255,
(numberValue >> 8) & 255,
numberValue & 255
].join(', ')
}
function setVariables(root, variables) {
Object.entries(variables).forEach(([key, value]) => {
root.style.setProperty(key, value)
})
}
function applyThemeSkin(skin) {
if (typeof document === 'undefined') {
return
}
const root = document.documentElement
const primaryRgb = hexToRgb(skin.primary)
const secondaryRgb = hexToRgb(skin.secondary)
const successRgb = hexToRgb(DEFAULT_SEMANTIC_COLORS.success)
const warningRgb = hexToRgb(DEFAULT_SEMANTIC_COLORS.warning)
const dangerRgb = hexToRgb(DEFAULT_SEMANTIC_COLORS.danger)
const infoRgb = hexToRgb(DEFAULT_SEMANTIC_COLORS.info)
root.dataset.themeSkin = skin.id
setVariables(root, {
'--primary': skin.primary,
'--primary-hover': skin.primaryHover,
'--primary-active': skin.primaryActive,
'--primary-soft': skin.primarySoft,
'--primary-soft-strong': skin.primarySoftStrong,
'--primary-rgb': primaryRgb,
'--secondary': skin.secondary,
'--secondary-rgb': secondaryRgb,
'--theme-secondary': skin.secondary,
'--theme-secondary-rgb': secondaryRgb,
'--theme-primary': skin.primary,
'--theme-primary-hover': skin.primaryHover,
'--theme-primary-active': skin.primaryActive,
'--theme-primary-soft': skin.primarySoft,
'--theme-primary-soft-strong': skin.primarySoftStrong,
'--theme-primary-light-5': 'color-mix(in srgb, var(--theme-primary) 46%, white)',
'--theme-primary-light-9': 'color-mix(in srgb, var(--theme-primary) 8%, white)',
'--theme-primary-rgb': primaryRgb,
'--theme-primary-shadow': `rgba(${primaryRgb}, 0.16)`,
'--theme-focus-ring': `rgba(${primaryRgb}, 0.12)`,
'--theme-gradient-primary': `linear-gradient(135deg, ${skin.primary}, ${skin.primaryActive})`,
'--chart-primary': skin.primary,
'--chart-primary-rgb': primaryRgb,
'--chart-blue': skin.chartBlue,
'--chart-purple': skin.chartPurple,
'--chart-amber': skin.chartAmber,
'--success': DEFAULT_SEMANTIC_COLORS.success,
'--success-hover': DEFAULT_SEMANTIC_COLORS.successHover,
'--success-active': DEFAULT_SEMANTIC_COLORS.successActive,
'--success-soft': DEFAULT_SEMANTIC_COLORS.successSoft,
'--success-line': DEFAULT_SEMANTIC_COLORS.successLine,
'--success-rgb': successRgb,
'--warning': DEFAULT_SEMANTIC_COLORS.warning,
'--warning-hover': DEFAULT_SEMANTIC_COLORS.warningHover,
'--warning-active': DEFAULT_SEMANTIC_COLORS.warningActive,
'--warning-soft': DEFAULT_SEMANTIC_COLORS.warningSoft,
'--warning-line': DEFAULT_SEMANTIC_COLORS.warningLine,
'--warning-rgb': warningRgb,
'--danger': DEFAULT_SEMANTIC_COLORS.danger,
'--danger-hover': DEFAULT_SEMANTIC_COLORS.dangerHover,
'--danger-active': DEFAULT_SEMANTIC_COLORS.dangerActive,
'--danger-soft': DEFAULT_SEMANTIC_COLORS.dangerSoft,
'--danger-line': DEFAULT_SEMANTIC_COLORS.dangerLine,
'--danger-rgb': dangerRgb,
'--info': DEFAULT_SEMANTIC_COLORS.info,
'--info-hover': DEFAULT_SEMANTIC_COLORS.infoHover,
'--info-active': DEFAULT_SEMANTIC_COLORS.infoActive,
'--info-soft': DEFAULT_SEMANTIC_COLORS.infoSoft,
'--info-line': DEFAULT_SEMANTIC_COLORS.infoLine,
'--info-rgb': infoRgb,
'--el-color-primary': skin.primary,
'--el-color-primary-dark-2': skin.primaryActive,
'--el-color-primary-light-3': skin.primaryHover,
'--el-color-primary-light-5': 'var(--theme-primary-light-5)',
'--el-color-primary-light-7': skin.primarySoftStrong,
'--el-color-primary-light-8': skin.primarySoft,
'--el-color-primary-light-9': 'var(--theme-primary-light-9)'
})
}
function readStoredThemeSkinId() {
if (typeof window === 'undefined') {
return DEFAULT_THEME_SKIN_ID
}
const stored = window.localStorage.getItem(THEME_SKIN_STORAGE_KEY)
return findThemeSkin(stored).id
}
export function installThemeSkin() {
const skin = findThemeSkin(readStoredThemeSkinId())
activeThemeSkinId.value = skin.id
applyThemeSkin(skin)
}
export function setThemeSkin(id) {
const skin = findThemeSkin(id)
activeThemeSkinId.value = skin.id
applyThemeSkin(skin)
if (typeof window !== 'undefined') {
window.localStorage.setItem(THEME_SKIN_STORAGE_KEY, skin.id)
}
}
export function useThemeSkin() {
return {
activeThemeSkin: computed(() => findThemeSkin(activeThemeSkinId.value)),
activeThemeSkinId,
setThemeSkin,
themeSkinOptions: THEME_SKIN_OPTIONS
}
}

View File

@@ -3,7 +3,7 @@ export const metricBlueprints = [
key: 'pendingCount',
label: '待审批单据',
unit: '单',
accent: '#10b981',
accent: 'var(--theme-primary)',
icon: 'mdi mdi-file-document-outline',
trend: 'down',
change: '12.5%',
@@ -12,7 +12,7 @@ export const metricBlueprints = [
{
key: 'pendingAmount',
label: '待处理金额',
accent: '#3b82f6',
accent: 'var(--chart-blue)',
icon: 'mdi mdi-wallet',
trend: 'up',
change: '8.3%',
@@ -22,7 +22,7 @@ export const metricBlueprints = [
key: 'avgSla',
label: '平均审批时长',
unit: 'h',
accent: '#8b5cf6',
accent: 'var(--chart-purple)',
icon: 'mdi mdi-clock-outline',
trend: 'down',
change: '14.8%',
@@ -32,7 +32,7 @@ export const metricBlueprints = [
key: 'autoPassRate',
label: '自动审单通过率',
unit: '%',
accent: '#16a34a',
accent: 'var(--success)',
icon: 'mdi mdi-shield-outline',
trend: 'up',
change: '6.2%',
@@ -42,7 +42,7 @@ export const metricBlueprints = [
key: 'riskCount',
label: '异常预警单',
unit: '单',
accent: '#ef4444',
accent: 'var(--danger)',
icon: 'mdi mdi-alert',
trend: 'up',
change: '16.7%',
@@ -52,7 +52,7 @@ export const metricBlueprints = [
key: 'slaRate',
label: 'SLA 达成率',
unit: '%',
accent: '#10b981',
accent: 'var(--success)',
icon: 'mdi mdi-check-circle',
trend: 'up',
change: '3.1%',
@@ -84,17 +84,17 @@ export const trendSeries = {
}
export const spendByCategory = [
{ name: '机票', value: 182000, color: '#16a34a' },
{ name: '酒店', value: 146000, color: '#3b82f6' },
{ name: '火车/用车', value: 78600, color: '#f59e0b' },
{ name: '餐补及杂费', value: 55000, color: '#8b5cf6' }
{ name: '机票', value: 182000, color: 'var(--theme-primary)' },
{ name: '酒店', value: 146000, color: 'var(--chart-blue)' },
{ name: '火车/用车', value: 78600, color: 'var(--chart-amber)' },
{ name: '餐补及杂费', value: 55000, color: 'var(--chart-purple)' }
]
export const exceptionMix = [
{ name: '住宿超标', value: 5, color: '#ef4444' },
{ name: '重复报销', value: 3, color: '#f59e0b' },
{ name: '行程缺失', value: 3, color: '#8b5cf6' },
{ name: '发票异常', value: 3, color: '#3b82f6' }
{ name: '住宿超标', value: 5, color: 'var(--danger)' },
{ name: '重复报销', value: 3, color: 'var(--warning)' },
{ name: '行程缺失', value: 3, color: 'var(--chart-purple)' },
{ name: '发票异常', value: 3, color: 'var(--chart-blue)' }
]
export const departmentRangeOptions = ['本周', '本月', '本季度']

View File

@@ -1,26 +1,22 @@
import { createApp } from 'vue'
import { MotionPlugin } from '@vueuse/motion'
import PrimeVue from 'primevue/config'
import Aura from '@primevue/themes/aura'
import ElementPlus from 'element-plus'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
import 'element-plus/dist/index.css'
import 'primeicons/primeicons.css'
import App from './App.vue'
import router from './router/index.js'
import { installThemeSkin } from './composables/useThemeSkin.js'
import { installSessionNavigation } from './composables/useSystemState.js'
import './assets/styles/element-plus-theme.css'
const app = createApp(App)
installThemeSkin()
installSessionNavigation(router)
app.use(MotionPlugin)
app.use(router)
app.use(PrimeVue, {
theme: {
preset: Aura,
options: {
darkModeSelector: '.dark',
cssLayer: false
}
}
})
app.use(ElementPlus, { locale: zhCn })
app.mount('#app')

View File

@@ -19,6 +19,14 @@ export const SECTION_DEFINITIONS = [
longDesc: '统一维护企业名称、系统显示名称和版权信息,保存后会同步更新当前系统品牌展示。',
actionLabel: '保存企业信息'
},
{
id: 'appearance',
label: '界面皮肤',
title: '界面皮肤与主色',
desc: '整体主色与控件观感',
longDesc: '设置当前浏览器的界面主色。默认使用浅蓝企业主题,后续可扩展为企业级统一下发。',
actionLabel: '保存皮肤设置'
},
{
id: 'admin',
label: '管理员安全',
@@ -417,6 +425,7 @@ export function computeSectionStatus(state) {
normalizeValue(state.companyForm.displayName) &&
normalizeValue(state.companyForm.copyright)
),
appearance: true,
admin: Boolean(
normalizeValue(state.adminForm.adminAccount) &&
normalizeValue(state.adminForm.adminEmail) &&

View File

@@ -29,35 +29,35 @@
<input v-model="listKeyword" type="search" placeholder="搜索单号、申请人、部门、归档类型..." />
</div>
<div
v-for="dropdown in filterDropdowns"
:key="dropdown.key"
class="archive-dropdown-filter"
:class="{ open: openFilterKey === dropdown.key }"
<el-dropdown
v-for="menu in filterMenus"
:key="menu.key"
class="archive-filter-control"
trigger="click"
placement="bottom-start"
popper-class="archive-filter-menu"
@command="selectFilterValue(menu.key, $event)"
>
<button class="filter-btn" type="button" @click="toggleFilterDropdown(dropdown.key)">
<span>{{ dropdown.label }}</span>
<button class="filter-btn archive-filter-trigger" type="button">
<span>{{ menu.label }}</span>
<i class="mdi mdi-chevron-down"></i>
</button>
<div
v-if="openFilterKey === dropdown.key"
class="archive-dropdown-menu"
role="menu"
:aria-label="`${dropdown.label}筛选`"
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
v-for="option in menu.options"
:key="`${menu.key}-${option.value}`"
:command="option.value"
:class="{ 'is-active': menu.activeValue === option.value }"
class="archive-filter-option"
:aria-current="menu.activeValue === option.value ? 'true' : undefined"
>
<button
v-for="option in dropdown.options"
:key="`${dropdown.key}-${option.value}`"
type="button"
class="archive-dropdown-option"
:class="{ active: dropdown.activeValue === option.value }"
role="menuitem"
@click="selectFilterValue(dropdown.key, option.value)"
>
{{ option.label }}
</button>
</div>
</div>
<i v-if="menu.activeValue === option.value" class="mdi mdi-check"></i>
<span>{{ option.label }}</span>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>

File diff suppressed because it is too large Load Diff

View File

@@ -35,27 +35,19 @@
<div class="budget-filter-set">
<label>
<span>预算年度</span>
<select v-model="filters.year">
<option v-for="year in years" :key="year" :value="year">{{ year }}年度</option>
</select>
<EnterpriseSelect v-model="filters.year" :options="yearOptions" />
</label>
<label>
<span>预算季度</span>
<select v-model="filters.quarter">
<option v-for="quarter in quarters" :key="quarter" :value="quarter">{{ quarter }}</option>
</select>
<EnterpriseSelect v-model="filters.quarter" :options="quarters" />
</label>
<label>
<span>费用类型</span>
<select v-model="filters.expenseType">
<option v-for="type in expenseTypes" :key="type">{{ type }}</option>
</select>
<EnterpriseSelect v-model="filters.expenseType" :options="expenseTypes" />
</label>
<label>
<span>状态</span>
<select v-model="filters.status">
<option v-for="status in statuses" :key="status">{{ status }}</option>
</select>
<EnterpriseSelect v-model="filters.status" :options="statuses" />
</label>
</div>
<div class="budget-action-set">
@@ -158,14 +150,13 @@
<i class="mdi mdi-chevron-right"></i>
</button>
</div>
<label class="budget-page-size">
<select v-model.number="budgetPageSize" aria-label="每页条数">
<option v-for="size in budgetPageSizeOptions" :key="size" :value="size">
{{ size }} /
</option>
</select>
<i class="mdi mdi-chevron-down"></i>
</label>
<EnterpriseSelect
v-model="budgetPageSize"
class="budget-page-size-select"
:options="budgetPageSizeOptions"
aria-label="每页条数"
size="small"
/>
<span class="budget-page-summary">
{{ totalBudgetRows }} 当前第 {{ budgetPage }} / {{ totalBudgetPages }}
</span>
@@ -232,23 +223,15 @@
<div class="budget-edit-form-grid">
<label class="required">
<span>预算年度</span>
<select v-model="budgetEditForm.budgetYear">
<option v-for="year in years" :key="year" :value="year">{{ year }}年度</option>
</select>
<EnterpriseSelect v-model="budgetEditForm.budgetYear" :options="yearOptions" />
</label>
<label class="required">
<span>预算季度</span>
<select v-model="budgetEditForm.budgetQuarter">
<option v-for="quarter in quarters" :key="quarter" :value="quarter">{{ quarter }}</option>
</select>
<EnterpriseSelect v-model="budgetEditForm.budgetQuarter" :options="quarters" />
</label>
<label v-if="canSwitchDepartments" class="required">
<span>所属部门</span>
<select v-model="budgetEditForm.departmentCode">
<option v-for="department in departments" :key="department.code" :value="department.code">
{{ department.name }}
</option>
</select>
<EnterpriseSelect v-model="budgetEditForm.departmentCode" :options="departmentOptions" />
</label>
<label v-else class="required">
<span>所属部门</span>
@@ -279,26 +262,19 @@
<tbody>
<tr v-for="row in budgetEditRows" :key="row.id">
<td>
<select v-model="row.budgetSubjectCode" @change="syncBudgetRowSubject(row)">
<option
v-for="option in expenseTypeOptions"
:key="option.value"
:value="option.value"
>
{{ option.label }}
</option>
</select>
<EnterpriseSelect
v-model="row.budgetSubjectCode"
:options="expenseTypeOptions"
size="small"
@change="syncBudgetRowSubject(row)"
/>
</td>
<td><input v-model="row.budgetAmount" type="text" inputmode="decimal" /></td>
<td>
<select v-model="row.warningThreshold">
<option v-for="warning in warningOptions" :key="warning">{{ warning }}</option>
</select>
<EnterpriseSelect v-model="row.warningThreshold" :options="warningOptions" size="small" />
</td>
<td>
<select v-model="row.controlAction">
<option v-for="action in controlActionOptions" :key="action">{{ action }}</option>
</select>
<EnterpriseSelect v-model="row.controlAction" :options="controlActionOptions" size="small" />
</td>
<td><input v-model="row.budgetRemark" type="text" /></td>
<td>

View File

@@ -232,24 +232,7 @@
<i class="mdi mdi-chevron-right"></i>
</button>
</div>
<div class="page-size-wrap">
<button class="page-size" type="button" @click="pageSizeOpen = !pageSizeOpen">
{{ pageSize }} / <i class="mdi mdi-chevron-down"></i>
</button>
<div v-if="pageSizeOpen" class="page-size-dropdown" role="listbox">
<button
v-for="size in pageSizes"
:key="size"
type="button"
role="option"
:aria-selected="pageSize === size"
:class="{ active: pageSize === size }"
@click="changePageSize(size)"
>
{{ size }} /
</button>
</div>
</div>
<EnterpriseSelect v-model="pageSize" class="page-size-select" :options="pageSizeOptions" size="small" @change="changePageSize" />
</footer>
</article>
</section>
@@ -258,6 +241,7 @@
<script setup>
import { computed, onMounted, ref, watch } from 'vue'
import EnterpriseSelect from '../components/shared/EnterpriseSelect.vue'
import TableEmptyState from '../components/shared/TableEmptyState.vue'
import TableLoadingState from '../components/shared/TableLoadingState.vue'
import { mapExpenseClaimToRequest } from '../composables/useRequests.js'
@@ -321,7 +305,7 @@ const FILTER_CONFIG_BY_SCOPE = {
showDocumentType: false
}
}
const pageSizes = [10, 20, 50]
const pageSizeOptions = [10, 20, 50].map((size) => ({ label: `${size} 条/页`, value: size }))
const documentTypeOptions = [
{ value: DOCUMENT_TYPE_ALL, label: '单据类型' },
{ value: DOCUMENT_TYPE_APPLICATION, label: '申请单' },
@@ -356,7 +340,6 @@ const appliedStart = ref('')
const appliedEnd = ref('')
const currentPage = ref(1)
const pageSize = ref(20)
const pageSizeOpen = ref(false)
const archiveRows = ref([])
const approvalRows = ref([])
const supportingLoading = ref(false)
@@ -519,7 +502,7 @@ const emptyState = computed(() => {
icon: 'mdi mdi-file-sign-outline',
actionLabel: '',
actionIcon: '',
tone: 'emerald',
tone: 'theme',
artLabel: 'APPLY',
tips: ['申请、报销、审批与归档统一在此查看', '申请批准后可继续发起报销']
}
@@ -534,7 +517,7 @@ const emptyState = computed(() => {
icon: filtered ? 'mdi mdi-magnify-scan' : 'mdi mdi-file-document-multiple-outline',
actionLabel: '',
actionIcon: '',
tone: 'emerald',
tone: 'theme',
artLabel: filtered ? 'FILTER' : 'DOCS',
tips: ['单据中心已接入当前报销单据', '归档视角会同步已归档数据']
}
@@ -724,7 +707,6 @@ function handleEmptyAction() {
function changePageSize(size) {
pageSize.value = size
pageSizeOpen.value = false
currentPage.value = 1
}
@@ -782,14 +764,12 @@ watch(
[activeScopeTab, activeStatusTab, activeDocumentType, activeScene, listKeyword, appliedStart, appliedEnd],
() => {
currentPage.value = 1
pageSizeOpen.value = false
}
)
watch(activeFilterConfig, () => {
openFilterKey.value = ''
datePopover.value = false
pageSizeOpen.value = false
if (!showDocumentTypeFilter.value) {
activeDocumentType.value = DOCUMENT_TYPE_ALL

View File

@@ -665,24 +665,13 @@
<i class="mdi mdi-chevron-right"></i>
</button>
</div>
<div class="page-size-wrap">
<button class="page-size" type="button" @click="togglePageSizeOpen">
{{ pageSize }} / <i class="mdi mdi-chevron-down"></i>
</button>
<div v-if="pageSizeOpen" class="page-size-dropdown" role="listbox">
<button
v-for="size in pageSizes"
:key="size"
type="button"
role="option"
:aria-selected="pageSize === size"
:class="{ active: pageSize === size }"
@click="changePageSize(size)"
>
{{ size }} /
</button>
</div>
</div>
<EnterpriseSelect
v-model="pageSize"
class="page-size-select"
:options="pageSizeOptions"
size="small"
@change="changePageSize"
/>
</footer>
</article>
</Transition>

View File

@@ -28,9 +28,12 @@
<div class="form-grid">
<label class="field">
<span><em>*</em> 供应商</span>
<select v-model="llmForm.mainProvider" @change="applyProviderPreset('main')">
<option v-for="option in providerOptions" :key="option" :value="option">{{ option }}</option>
</select>
<EnterpriseSelect
v-model="llmForm.mainProvider"
:options="providerOptions"
placeholder="选择供应商"
@change="applyProviderPreset('main')"
/>
</label>
<label class="field">
@@ -101,9 +104,12 @@
<div class="form-grid">
<label class="field">
<span><em>*</em> 供应商</span>
<select v-model="llmForm.backupProvider" @change="applyProviderPreset('backup')">
<option v-for="option in providerOptions" :key="option" :value="option">{{ option }}</option>
</select>
<EnterpriseSelect
v-model="llmForm.backupProvider"
:options="providerOptions"
placeholder="选择供应商"
@change="applyProviderPreset('backup')"
/>
</label>
<label class="field">
@@ -174,9 +180,12 @@
<div class="form-grid">
<label class="field">
<span><em>*</em> 供应商</span>
<select v-model="llmForm.embeddingProvider" @change="applyProviderPreset('embedding')">
<option v-for="option in providerOptions" :key="option" :value="option">{{ option }}</option>
</select>
<EnterpriseSelect
v-model="llmForm.embeddingProvider"
:options="providerOptions"
placeholder="选择供应商"
@change="applyProviderPreset('embedding')"
/>
</label>
<label class="field">
@@ -251,9 +260,12 @@
<div class="form-grid">
<label class="field">
<span><em>*</em> 供应商</span>
<select v-model="llmForm.rerankerProvider" @change="applyProviderPreset('reranker')">
<option v-for="option in providerOptions" :key="option" :value="option">{{ option }}</option>
</select>
<EnterpriseSelect
v-model="llmForm.rerankerProvider"
:options="providerOptions"
placeholder="选择供应商"
@change="applyProviderPreset('reranker')"
/>
</label>
<label class="field">
@@ -306,4 +318,3 @@
<style scoped src="../assets/styles/views/settings-view-form.css"></style>
<style scoped src="../assets/styles/views/settings-view.css"></style>

View File

@@ -27,22 +27,22 @@
<label class="filter-field">
<span>日志级别</span>
<select v-model="systemLevelFilter">
<option value="">全部</option>
<option v-for="level in systemLevelOptions" :key="level" :value="level">
{{ level }}
</option>
</select>
<EnterpriseSelect
v-model="systemLevelFilter"
:options="systemLevelFilterOptions"
placeholder="全部"
size="small"
/>
</label>
<label class="filter-field">
<span>事件类型</span>
<select v-model="systemEventTypeFilter">
<option value="">全部</option>
<option v-for="eventType in systemEventTypeOptions" :key="eventType" :value="eventType">
{{ eventType }}
</option>
</select>
<EnterpriseSelect
v-model="systemEventTypeFilter"
:options="systemEventTypeFilterOptions"
placeholder="全部"
size="small"
/>
</label>
<button
@@ -221,24 +221,13 @@
<i class="mdi mdi-chevron-right"></i>
</button>
</div>
<div class="page-size-wrap">
<button class="page-size" type="button" @click="pageSizeOpen = !pageSizeOpen">
{{ pageSize }} / <i class="mdi mdi-chevron-down"></i>
</button>
<div v-if="pageSizeOpen" class="page-size-dropdown" role="listbox">
<button
v-for="size in pageSizes"
:key="size"
type="button"
role="option"
:aria-selected="pageSize === size"
:class="{ active: pageSize === size }"
@click="changePageSize(size)"
>
{{ size }} /
</button>
</div>
</div>
<EnterpriseSelect
v-model="pageSize"
class="page-size-select"
:options="pageSizeOptions"
size="small"
@change="changePageSize"
/>
</div>
</section>

View File

@@ -27,11 +27,11 @@
<label class="field">
<span>加密方式</span>
<select v-model="mailForm.encryption">
<option value="SSL/TLS">SSL/TLS</option>
<option value="STARTTLS">STARTTLS</option>
<option value="None"></option>
</select>
<EnterpriseSelect
v-model="mailForm.encryption"
:options="encryptionOptions"
placeholder="选择加密方式"
/>
</label>
<label class="field">

View File

@@ -26,9 +26,13 @@
<article class="panel dashboard-card trend-panel">
<div class="card-head">
<h3>报销申请与审批趋势 <i class="mdi mdi-information-outline"></i></h3>
<select v-model="activeTrendRange" class="card-select" aria-label="趋势时间范围">
<option v-for="range in trendRanges" :key="range">{{ range }}</option>
</select>
<EnterpriseSelect
v-model="activeTrendRange"
class="card-select"
:options="trendRanges"
aria-label="趋势时间范围"
size="small"
/>
</div>
<TrendChart
@@ -52,17 +56,21 @@
<h3>风险异常分布 <i class="mdi mdi-information-outline"></i></h3>
</div>
<DonutChart :items="riskLegend" :center-value="`${riskTotal}`" center-label="异常预警单" />
<p class="panel-note">* 30天数据</p>
<p class="panel-note">* 30 天数据</p>
</article>
</div>
<div class="content-grid bottom-grid">
<article class="panel dashboard-card rank-panel">
<div class="card-head">
<h3>部门报销排行待处理金额 <i class="mdi mdi-information-outline"></i></h3>
<select v-model="activeDepartmentRange" class="card-select" aria-label="部门排行时间范围">
<option v-for="range in departmentRangeOptions" :key="range">{{ range }}</option>
</select>
<h3>部门报销排行待处理金额<i class="mdi mdi-information-outline"></i></h3>
<EnterpriseSelect
v-model="activeDepartmentRange"
class="card-select"
:options="departmentRangeOptions"
aria-label="部门排行时间范围"
size="small"
/>
</div>
<BarChart :items="rankedDepartments" />
@@ -99,7 +107,7 @@
<article class="panel dashboard-card budget-panel">
<div class="card-head">
<h3>预算执行率本月 <i class="mdi mdi-information-outline"></i></h3>
<h3>预算执行率本月<i class="mdi mdi-information-outline"></i></h3>
</div>
<GaugeChart
@@ -112,7 +120,6 @@
<button type="button" class="text-link">查看详情 <i class="mdi mdi-chevron-right"></i></button>
</article>
</div>
</section>
</template>
@@ -121,6 +128,7 @@ import TrendChart from '../components/charts/TrendChart.vue'
import DonutChart from '../components/charts/DonutChart.vue'
import BarChart from '../components/charts/BarChart.vue'
import GaugeChart from '../components/charts/GaugeChart.vue'
import EnterpriseSelect from '../components/shared/EnterpriseSelect.vue'
import { useOverviewView } from '../composables/useOverviewView.js'
@@ -128,8 +136,6 @@ defineProps({
filteredRequests: { type: Array, required: true }
})
const emit = defineEmits(['ask'])
const {
activeDepartmentRange,
activeTrend,

View File

@@ -168,24 +168,13 @@
<i class="mdi mdi-chevron-right"></i>
</button>
</div>
<div class="page-size-wrap">
<button class="page-size" type="button" @click="pageSizeOpen = !pageSizeOpen">
{{ pageSize }} /<i class="mdi mdi-chevron-down"></i>
</button>
<div v-if="pageSizeOpen" class="page-size-dropdown" role="listbox">
<button
v-for="size in pageSizes"
:key="size"
type="button"
role="option"
:aria-selected="pageSize === size"
:class="{ active: pageSize === size }"
@click="changePageSize(size)"
>
{{ size }} /
</button>
</div>
</div>
<EnterpriseSelect
v-model="pageSize"
class="page-size-select"
:options="pageSizeOptions"
size="small"
@change="changePageSize"
/>
</footer>
</section>
</div>

View File

@@ -135,14 +135,13 @@
<button v-for="p in totalPages" :key="p" class="page-number" :class="{ active: currentPage === p }" type="button" :aria-current="currentPage === p ? 'page' : undefined" @click="currentPage = p">{{ p }}</button>
<button class="page-nav" type="button" :disabled="currentPage === totalPages" aria-label="下一页" @click="currentPage++"><i class="mdi mdi-chevron-right"></i></button>
</div>
<div class="page-size-wrap">
<button class="page-size" type="button" @click="pageSizeOpen = !pageSizeOpen">
{{ pageSize }} / <i class="mdi mdi-chevron-down"></i>
</button>
<div v-if="pageSizeOpen" class="page-size-dropdown" role="listbox">
<button v-for="size in pageSizes" :key="size" type="button" role="option" :aria-selected="pageSize === size" :class="{ active: pageSize === size }" @click="changePageSize(size)">{{ size }} /</button>
</div>
</div>
<EnterpriseSelect
v-model="pageSize"
class="page-size-select"
:options="pageSizeOptions"
size="small"
@change="changePageSize"
/>
</footer>
</article>
</section>

View File

@@ -101,6 +101,53 @@
</section>
</template>
<template v-else-if="activeSection === 'appearance'">
<section class="settings-card">
<div class="card-head">
<div class="card-title-with-icon">
<div class="model-icon-box slate">
<i class="mdi mdi-palette-outline"></i>
</div>
<div>
<h4>界面皮肤与企业主色</h4>
<p>只调整整体主色焦点态按钮和 Element Plus 控件颜色不改变业务布局</p>
</div>
</div>
</div>
<div class="skin-option-grid">
<button
v-for="skin in themeSkinOptions"
:key="skin.id"
class="skin-option"
:class="{ active: activeThemeSkinId === skin.id }"
type="button"
@click="selectThemeSkin(skin.id)"
>
<span class="skin-swatch" aria-hidden="true">
<i :style="{ background: skin.primary }"></i>
<i :style="{ background: skin.primarySoftStrong }"></i>
<i :style="{ background: skin.secondary }"></i>
<i :style="{ background: skin.chartAmber }"></i>
</span>
<span class="skin-copy">
<strong>{{ skin.label }}</strong>
<small>{{ skin.desc }}</small>
</span>
<span v-if="activeThemeSkinId === skin.id" class="skin-current">当前</span>
</button>
</div>
<div class="skin-preview-panel">
<div>
<strong>{{ activeThemeSkin.label }}</strong>
<span>当前主色会同步到全局按钮焦点环下拉浮层和表单控件</span>
</div>
<button class="skin-preview-action" type="button">主按钮</button>
</div>
</section>
</template>
<template v-else-if="activeSection === 'admin'">
<section class="settings-card">
<div class="card-head">
@@ -367,11 +414,11 @@
<label class="field">
<span>归档周期</span>
<select v-model="pageState.logForm.archiveCycle">
<option value="daily">按天归档</option>
<option value="weekly">按周归档</option>
<option value="monthly">按月归档</option>
</select>
<EnterpriseSelect
v-model="pageState.logForm.archiveCycle"
:options="archiveCycleOptions"
placeholder="选择归档周期"
/>
</label>
<label class="field field-full">

File diff suppressed because it is too large Load Diff

View File

@@ -241,11 +241,7 @@
<td class="expense-type col-type">
<template v-if="editingExpenseId === item.id">
<div class="cell-editor">
<select v-model="expenseEditor.itemType" class="editor-select">
<option v-for="option in expenseTypeOptions" :key="option.value" :value="option.value">
{{ option.label }}
</option>
</select>
<EnterpriseSelect v-model="expenseEditor.itemType" class="editor-select" :options="expenseTypeOptions" size="small" />
<span>编辑费用项目</span>
</div>
</template>
@@ -763,58 +759,27 @@
</div>
</ConfirmDialog>
<ConfirmDialog
:open="deleteDialogOpen"
:badge="deleteActionLabel"
badge-tone="danger"
:title="deleteDialogTitle"
:description="deleteDialogDescription"
cancel-text="取消"
confirm-text="确认删除"
busy-text="删除中..."
confirm-tone="danger"
confirm-icon="mdi mdi-trash-can-outline"
:busy="deleteBusy"
@close="closeDeleteDialog"
@confirm="confirmDeleteRequest"
/>
<TravelRequestDeleteDialog :open="deleteDialogOpen" :badge="deleteActionLabel" :title="deleteDialogTitle" :description="deleteDialogDescription" :busy="deleteBusy" @close="closeDeleteDialog" @confirm="confirmDeleteRequest" />
<ConfirmDialog
<TravelRequestApprovalDialog
:open="approveConfirmDialogOpen"
:badge="approvalConfirmBadge"
badge-tone="info"
:title="approveConfirmTitle"
:description="approvalConfirmDescription"
cancel-text="返回核对"
:confirm-text="approveConfirmText"
:busy-text="approveBusyText"
confirm-tone="primary"
confirm-icon="mdi mdi-check-circle-outline"
:busy="approveBusy"
:document-no="request.documentNo || request.id"
:node="request.node"
:summary-label="approvalConfirmSummaryLabel"
:next-stage="approvalNextStage"
:opinion-title="approvalOpinionTitle"
:opinion="leaderOpinion"
@close="closeApproveConfirmDialog"
@confirm="confirmApproveRequest"
>
<div class="submit-confirm-summary" aria-label="领导审批通过摘要">
<div class="submit-confirm-row">
<span>单据编号</span>
<strong>{{ request.documentNo || request.id }}</strong>
</div>
<div class="submit-confirm-row">
<span>当前节点</span>
<strong>{{ request.node }}</strong>
</div>
<div class="submit-confirm-row">
<span>{{ approvalConfirmSummaryLabel }}</span>
<strong>{{ approvalNextStage }}</strong>
</div>
<div class="submit-confirm-row">
<span>{{ approvalOpinionTitle }}</span>
<strong>{{ leaderOpinion.trim() || '未填写' }}</strong>
</div>
</div>
</ConfirmDialog>
/>
<ReturnReasonDialog
<TravelRequestReturnDialog
:open="returnDialogOpen"
:title="`确认退回 ${request.id} 吗?`"
:description="returnDialogDescription"

View File

@@ -1,4 +1,4 @@
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
import { computed, ref } from 'vue'
import TableLoadingState from '../../components/shared/TableLoadingState.vue'
import TableEmptyState from '../../components/shared/TableEmptyState.vue'
@@ -102,7 +102,6 @@ export default {
const activeTypeFilter = ref(ARCHIVE_FILTER_ALL)
const activeDepartmentFilter = ref(ARCHIVE_FILTER_ALL)
const activeArchiveMonthFilter = ref(ARCHIVE_FILTER_ALL)
const openFilterKey = ref('')
const selectedClaimId = ref('')
const listKeyword = ref('')
const rows = ref([])
@@ -118,7 +117,7 @@ export default {
const departmentFilterLabel = computed(() => resolveFilterLabel(departmentFilterOptions.value, activeDepartmentFilter.value, '所属部门'))
const archiveMonthFilterLabel = computed(() => resolveFilterLabel(archiveMonthFilterOptions.value, activeArchiveMonthFilter.value, '归档月份'))
const filterDropdowns = computed(() => [
const filterMenus = computed(() => [
{
key: 'risk',
label: riskFilterLabel.value,
@@ -210,7 +209,6 @@ export default {
activeDepartmentFilter.value = ARCHIVE_FILTER_ALL
activeArchiveMonthFilter.value = ARCHIVE_FILTER_ALL
listKeyword.value = ''
openFilterKey.value = ''
}
function handleEmptyAction() {
@@ -222,10 +220,6 @@ export default {
resetListFilters()
}
function toggleFilterDropdown(key) {
openFilterKey.value = openFilterKey.value === key ? '' : key
}
function selectFilterValue(key, value) {
if (key === 'risk') {
activeRiskFilter.value = value
@@ -237,18 +231,6 @@ export default {
activeArchiveMonthFilter.value = value
}
openFilterKey.value = ''
}
function handleDocumentClick(event) {
const target = event.target
if (!(target instanceof Element)) {
return
}
if (!target.closest('.archive-dropdown-filter')) {
openFilterKey.value = ''
}
}
function closeSelectedDetail() {
@@ -278,14 +260,6 @@ export default {
}
}
onMounted(() => {
document.addEventListener('click', handleDocumentClick)
})
onBeforeUnmount(() => {
document.removeEventListener('click', handleDocumentClick)
})
void reload()
return {
@@ -293,11 +267,10 @@ export default {
archiveEmptyState,
closeSelectedDetail,
error,
filterDropdowns,
filterMenus,
handleEmptyAction,
listKeyword,
loading,
openFilterKey,
reload,
resetListFilters,
rows,
@@ -305,7 +278,6 @@ export default {
selectedRow,
showEmpty,
tabs,
toggleFilterDropdown,
visibleRows
}
}

View File

@@ -1,11 +1,13 @@
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import ConfirmDialog from '../../components/shared/ConfirmDialog.vue'
import AuditAssetList from '../../components/audit/AuditAssetList.vue'
import AuditJsonRiskRuleDetail from '../../components/audit/AuditJsonRiskRuleDetail.vue'
import AuditRuleDialogs from '../../components/audit/AuditRuleDialogs.vue'
import AuditSpreadsheetChangeDrawer from '../../components/audit/AuditSpreadsheetChangeDrawer.vue'
import AuditSpreadsheetRuleDetail from '../../components/audit/AuditSpreadsheetRuleDetail.vue'
import AuditVersionTimelineDrawer from '../../components/audit/AuditVersionTimelineDrawer.vue'
import { fetchEmployees } from '../../services/employees.js'
import RiskRuleFlowDiagram from '../../components/shared/RiskRuleFlowDiagram.vue'
import RiskRuleTestDialog from '../../components/shared/RiskRuleTestDialog.vue'
import TableLoadingState from '../../components/shared/TableLoadingState.vue'
import TableEmptyState from '../../components/shared/TableEmptyState.vue'
import { useSystemState } from '../../composables/useSystemState.js'
import { useToast } from '../../composables/useToast.js'
import {
@@ -77,11 +79,13 @@ import {
export default {
name: 'AuditView',
components: {
ConfirmDialog,
RiskRuleFlowDiagram,
RiskRuleTestDialog,
TableLoadingState,
TableEmptyState
AuditAssetList,
AuditJsonRiskRuleDetail,
AuditRuleDialogs,
AuditSpreadsheetChangeDrawer,
AuditSpreadsheetRuleDetail,
AuditVersionTimelineDrawer,
TableLoadingState
},
emits: ['detail-open-change'],
setup(_, { emit }) {
@@ -115,6 +119,10 @@ export default {
const reviewSubmitReviewer = ref('')
const reviewSubmitReviewerLoading = ref(false)
const reviewSubmitReviewerOptions = ref([])
const riskRuleAttachmentOptions = [
{ label: '是', value: true },
{ label: '否', value: false }
]
const riskRuleCreateOpen = ref(false)
const riskRuleCreateForm = ref(createDefaultRiskRuleForm())
const riskRuleTestOpen = ref(false)
@@ -466,7 +474,7 @@ export default {
icon: hasFilters ? 'mdi mdi-tune-variant' : 'mdi mdi-view-grid-outline',
actionLabel: hasFilters ? '清空筛选' : '',
actionIcon: hasFilters ? 'mdi mdi-filter-remove-outline' : '',
tone: hasFilters ? 'emerald' : 'slate',
tone: hasFilters ? 'primary' : 'slate',
artLabel: hasFilters ? 'FILTER' : 'QUEUE',
tips: hasFilters
? [
@@ -956,7 +964,7 @@ export default {
if (!canUploadSpreadsheet.value) {
return
}
spreadsheetUploadInput.value?.click()
spreadsheetUploadInput.value?.click?.()
}
async function downloadSpreadsheetFile() {
@@ -1003,9 +1011,7 @@ export default {
toast(error?.message || '规则表内容导入失败,请稍后重试。')
} finally {
actionState.value = ''
if (spreadsheetUploadInput.value) {
spreadsheetUploadInput.value.value = ''
}
spreadsheetUploadInput.value?.reset?.()
}
}
@@ -1916,6 +1922,7 @@ export default {
reviewSubmitReviewer,
reviewSubmitReviewerLoading,
reviewSubmitReviewerOptions,
riskRuleAttachmentOptions,
riskRuleCreateOpen,
riskRuleCreateForm,
riskRuleCreateBusy,

View File

@@ -2,6 +2,7 @@ import { computed, onMounted, ref, watch } from 'vue'
import BudgetTrendChart from '../../components/charts/BudgetTrendChart.vue'
import ConfirmDialog from '../../components/shared/ConfirmDialog.vue'
import EnterpriseSelect from '../../components/shared/EnterpriseSelect.vue'
import { createBudgetAllocation, fetchBudgetSummary } from '../../services/budgets.js'
import { fetchEmployeeMeta } from '../../services/employees.js'
import {
@@ -177,7 +178,8 @@ export default {
},
components: {
BudgetTrendChart,
ConfirmDialog
ConfirmDialog,
EnterpriseSelect
},
setup(props) {
const departments = ref(FALLBACK_DEPARTMENTS)
@@ -215,6 +217,14 @@ export default {
const isDepartmentBudgetMonitor = computed(
() => isBudgetMonitorUser(props.currentUser) && !canSwitchDepartments.value && !isExecutiveUser(props.currentUser)
)
const yearOptions = BUDGET_YEAR_OPTIONS.map((year) => ({ label: `${year}年度`, value: year }))
const budgetPageSizeOptions = BUDGET_PAGE_SIZE_OPTIONS.map((size) => ({ label: `${size} 条/页`, value: size }))
const departmentOptions = computed(() =>
departments.value.map((department) => ({
label: department.name,
value: department.code
}))
)
const activeDepartment = computed(() =>
departments.value.find((item) => item.code === activeDepartmentCode.value) || departments.value[0]
@@ -273,7 +283,7 @@ export default {
value: `¥${currency(totals.value.total)}`,
yoy: comparison('+8.42%', 'up'),
mom: comparison('+2.16%', 'up'),
tone: 'green',
tone: 'primary',
icon: 'mdi mdi-wallet-outline'
},
{
@@ -281,7 +291,7 @@ export default {
value: `¥${currency(totals.value.used)}`,
yoy: comparison('+12.68%', 'up'),
mom: comparison('+4.35%', 'up'),
tone: 'blue',
tone: 'info',
icon: 'mdi mdi-chart-line'
},
{
@@ -289,7 +299,7 @@ export default {
value: `¥${currency(totals.value.occupied)}`,
yoy: comparison('+6.37%', 'up'),
mom: comparison('-1.84%', 'down'),
tone: 'orange',
tone: 'warning',
icon: 'mdi mdi-briefcase-check-outline'
},
{
@@ -297,7 +307,7 @@ export default {
value: `¥${currency(totals.value.left)}`,
yoy: comparison('-3.26%', 'down'),
mom: comparison('-2.08%', 'down'),
tone: 'green',
tone: 'primary',
icon: 'mdi mdi-cash'
}
])
@@ -612,7 +622,7 @@ export default {
budgetPage: currentBudgetPage,
budgetPageNumbers,
budgetPageSize,
budgetPageSizeOptions: BUDGET_PAGE_SIZE_OPTIONS,
budgetPageSizeOptions,
canEditBudget,
canSwitchDepartments,
closeBudgetEditDialog,
@@ -633,6 +643,7 @@ export default {
confirmDeleteRow,
cancelDeleteRow,
cancelSaveBudget,
departmentOptions,
requestSaveBudget,
statusOptions: BUDGET_STATUS_OPTIONS,
statuses: ['全部', '正常', '预警', '管控'],
@@ -645,6 +656,7 @@ export default {
visibleDepartments,
warningOptions: BUDGET_WARNING_OPTIONS,
warnings,
yearOptions,
years: BUDGET_YEAR_OPTIONS
}
}

View File

@@ -1,6 +1,7 @@
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import ConfirmDialog from '../../components/shared/ConfirmDialog.vue'
import EnterpriseSelect from '../../components/shared/EnterpriseSelect.vue'
import TableLoadingState from '../../components/shared/TableLoadingState.vue'
import TableEmptyState from '../../components/shared/TableEmptyState.vue'
import { useToast } from '../../composables/useToast.js'
@@ -438,6 +439,7 @@ export default {
name: 'EmployeeManagementView',
components: {
ConfirmDialog,
EnterpriseSelect,
TableLoadingState,
TableEmptyState
},
@@ -457,7 +459,7 @@ export default {
const currentPage = ref(1)
const pageSize = ref(10)
const pageSizes = [10, 20, 50]
const pageSizeOpen = ref(false)
const pageSizeOptions = pageSizes.map((size) => ({ label: `${size} 条/页`, value: size }))
const actionState = ref('')
const loading = ref(false)
const errorMessage = ref('')
@@ -713,7 +715,7 @@ export default {
icon: hasEmployeeFilters.value ? 'mdi mdi-account-search-outline' : 'mdi mdi-badge-account-horizontal-outline',
actionLabel: hasEmployeeFilters.value ? '清空筛选' : '查看全部员工',
actionIcon: hasEmployeeFilters.value ? 'mdi mdi-filter-remove-outline' : 'mdi mdi-format-list-bulleted',
tone: hasEmployeeFilters.value ? 'emerald' : 'slate',
tone: hasEmployeeFilters.value ? 'primary' : 'slate',
artLabel: hasEmployeeFilters.value ? 'FILTER' : 'STATUS',
tips: hasEmployeeFilters.value
? ['关键词、部门、职级和角色条件会叠加生效', '也可以直接搜索姓名、工号或岗位']
@@ -791,7 +793,6 @@ export default {
watch(filteredEmployees, () => {
currentPage.value = 1
pageSizeOpen.value = false
})
function resetFilters() {
@@ -801,7 +802,6 @@ export default {
selectedRole.value = ''
activeTab.value = DEFAULT_STATUS_TABS[0]
activeFilterPopover.value = ''
pageSizeOpen.value = false
}
function handleEmployeeEmptyAction() {
@@ -815,14 +815,9 @@ export default {
function changePageSize(size) {
pageSize.value = size
pageSizeOpen.value = false
currentPage.value = 1
}
function togglePageSizeOpen() {
pageSizeOpen.value = !pageSizeOpen.value
}
function toggleFilterPopover(name) {
activeFilterPopover.value = activeFilterPopover.value === name ? '' : name
}
@@ -852,7 +847,6 @@ export default {
if (!(target instanceof Element)) {
closeFilterPopover()
pageSizeOpen.value = false
return
}
@@ -868,13 +862,8 @@ export default {
closeDepartmentPicker()
}
if (!target.closest('.page-size-wrap')) {
pageSizeOpen.value = false
}
if (
target.closest('.picker-filter') ||
target.closest('.page-size-wrap') ||
target.closest('.manager-picker') ||
target.closest('.department-picker')
) {
@@ -1437,7 +1426,7 @@ export default {
currentPage,
pageSize,
pageSizes,
pageSizeOpen,
pageSizeOptions,
departmentOptions,
gradeOptions,
roleFilterOptions,
@@ -1475,7 +1464,6 @@ export default {
disableDialogOpen,
disableEmployeeAccount,
changePageSize,
togglePageSizeOpen,
toggleFilterPopover,
closeFilterPopover,
selectFilter,

View File

@@ -1,6 +1,7 @@
import { ref } from 'vue'
import { testModelConnectivity } from '../../services/settings.js'
import { useToast } from '../../composables/useToast.js'
import EnterpriseSelect from '../../components/shared/EnterpriseSelect.vue'
const MODEL_SECRET_MASK = '********'
@@ -101,6 +102,9 @@ function isModelSecretMask(value) {
export default {
name: 'LlmSettingsPanel',
components: {
EnterpriseSelect
},
props: {
llmForm: {
type: Object,

Some files were not shown because too many files have changed in this diff Show More