diff --git a/AGENTS.md b/AGENTS.md index 63476525..97543e83 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,15 +13,16 @@ Yuxi-Know 是一个基于知识图谱和向量数据库的智能知识库系统 前端开发规范: -- API 接口规范:所有的 API 接口都应该定义在 web/src/apis 下面,并继承自 apiGet/apiPost/apiRequest -- Icon 应该从 @ant-design/icons-vue 或者 lucide-vue-next -- Vue 中的样式使用 less,并尽量使用[base.css](web/src/assets/css/base.css) 中的颜色。 +- API 接口规范:所有的 API 接口都应该定义在 web/src/apis 下面,并继承自 apiGet/apiPost/apiRequest 以及其他。 +- Icon 应该从 @ant-design/icons-vue 或者 lucide-vue-next (推荐,但是需要注意尺寸) +- Vue 中的样式使用 less,非必要情况必须使用[base.css](web/src/assets/css/base.css) 中的颜色变量。 - UI风格要简洁,同时要保持一致性,颜色要尽量参考 不要悬停位移,不要过度使用阴影以及渐变色。 +- 绝对不要尝试使用 npm/pnpm 等等运行前端开发服务器。 后端开发规范: -- 项目使用 uv 来管理依赖,所以需要使用 uv run 来调试。 +- 项目使用 uv 来管理依赖,所以必须使用 uv run 来调试。 - Python 代码要符合 Python 的规范,符合 pythonic 风格,尽量使用较新的语法,避免使用旧版本的语法(版本兼容到 3.12+),使用 make lint 检查 lint。使用 make format 来格式化代码。 其他: diff --git a/web/package.json b/web/package.json index 128aad20..da1413fe 100644 --- a/web/package.json +++ b/web/package.json @@ -8,7 +8,7 @@ "server:prod": "vite serve --host --port 8080", "build": "vite build", "preview": "vite preview", - "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore", + "lint": "eslint . --fix", "format": "prettier --write src/" }, "dependencies": { diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 3708cb29..0d26237d 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -59,6 +59,12 @@ importers: marked-highlight: specifier: ^2.2.2 version: 2.2.2(marked@16.3.0) + markmap-lib: + specifier: ^0.18.12 + version: 0.18.12(markmap-common@0.18.9) + markmap-view: + specifier: ^0.18.12 + version: 0.18.12(markmap-common@0.18.9) md-editor-v3: specifier: ^5.8.4 version: 5.8.5(vue@3.5.21) @@ -80,7 +86,7 @@ importers: version: 1.12.0 '@vitejs/plugin-vue': specifier: ^6.0.1 - version: 6.0.1(vite@7.1.5(@types/node@24.5.2)(less@4.4.1)(terser@5.44.0))(vue@3.5.21) + version: 6.0.1(vite@7.1.5(@types/node@24.5.2)(less@4.4.1)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.21) '@vue/eslint-config-prettier': specifier: ^10.2.0 version: 10.2.0(@types/eslint@9.6.1)(eslint@9.36.0)(prettier@3.6.2) @@ -95,7 +101,7 @@ importers: version: 3.6.2 vite: specifier: ^7.1.5 - version: 7.1.5(@types/node@24.5.2)(less@4.4.1)(terser@5.44.0) + version: 7.1.5(@types/node@24.5.2)(less@4.4.1)(terser@5.44.0)(yaml@2.8.1) vue-eslint-parser: specifier: ^10.2.0 version: 10.2.0(eslint@9.36.0) @@ -506,6 +512,9 @@ packages: resolution: {integrity: sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@gera2ld/jsx-dom@2.2.2': + resolution: {integrity: sha512-EOqf31IATRE6zS1W1EoWmXZhGfLAoO9FIlwTtHduSrBdud4npYBxYAkv8dZ5hudDPwJeeSjn40kbCL4wAzr8dA==} + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -834,6 +843,9 @@ packages: vite: ^5.0.0 || ^6.0.0 || ^7.0.0 vue: ^3.2.25 + '@vscode/markdown-it-katex@1.1.2': + resolution: {integrity: sha512-+4IIv5PgrmhKvW/3LpkpkGg257OViEhXkOOgCyj5KMsjsOfnRXkni8XAuuF9Ui5p3B8WnUovlDXAQNb8RJ/RaQ==} + '@vue/compiler-core@3.5.21': resolution: {integrity: sha512-8i+LZ0vf6ZgII5Z9XmUvrCyEzocvWT+TeR2VBUVlzIH6Tyv57E20mPZ1bCS+tbejgUgmjrEh7q/0F0bibskAmw==} @@ -1037,6 +1049,13 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + cheerio-select@2.1.0: + resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} + + cheerio@1.0.0: + resolution: {integrity: sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==} + engines: {node: '>=18.17'} + chrome-trace-event@1.0.4: resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} engines: {node: '>=6.0'} @@ -1067,6 +1086,10 @@ packages: resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} engines: {node: '>= 10'} + commander@8.3.0: + resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} + engines: {node: '>= 12'} + compute-scroll-into-view@1.0.20: resolution: {integrity: sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==} @@ -1090,6 +1113,13 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} + css-select@5.2.2: + resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} + + css-what@6.2.2: + resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} + engines: {node: '>= 6'} + cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} @@ -1273,6 +1303,19 @@ packages: dom-scroll-into-view@2.0.1: resolution: {integrity: sha512-bvVTQe1lfaUr1oFzZX80ce9KLDlZ3iU+XGNE/bz9HnGdklTieqsbmsLHe+rT2XWqopvL0PckkYqN7ksmm5pe3w==} + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@3.2.2: + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + echarts-gl@2.0.9: resolution: {integrity: sha512-oKeMdkkkpJGWOzjgZUsF41DOh6cMsyrGGXimbjK2l6Xeq/dBQu4ShG2w2Dzrs/1bD27b2pLTGSaUzouY191gzA==} peerDependencies: @@ -1288,6 +1331,9 @@ packages: resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} engines: {node: '>= 4'} + encoding-sniffer@0.2.1: + resolution: {integrity: sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==} + enhanced-resolve@5.18.3: resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} engines: {node: '>=10.13.0'} @@ -1296,6 +1342,10 @@ packages: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + errno@0.1.8: resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} hasBin: true @@ -1521,6 +1571,9 @@ packages: hookable@5.5.3: resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} + htmlparser2@9.1.0: + resolution: {integrity: sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==} + iconv-lite@0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} @@ -1605,6 +1658,10 @@ packages: engines: {node: '>=6'} hasBin: true + katex@0.16.25: + resolution: {integrity: sha512-woHRUZ/iF23GBP1dkDQMh1QBad9dmr8/PAwNA54VrSOVYgI12MAcE14TqnDdQOdzyEonGzMepYnqBMYdsoAr8Q==} + hasBin: true + keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -1672,6 +1729,12 @@ packages: peerDependencies: markdown-it: '*' + markdown-it-ins@4.0.0: + resolution: {integrity: sha512-sWbjK2DprrkINE4oYDhHdCijGT+MIDhEupjSHLXe5UXeVr5qmVxs/nTUVtgi0Oh/qtF+QKV0tNWDhQBEPxiMew==} + + markdown-it-mark@4.0.0: + resolution: {integrity: sha512-YLhzaOsU9THO/cal0lUjfMjrqSMPjjyjChYM7oyj4DnyaXEzA8gnW6cVJeyCrCVeyesrY2PlEdUYJSPFYL4Nkg==} + markdown-it-sub@2.0.0: resolution: {integrity: sha512-iCBKgwCkfQBRg2vApy9vx1C1Tu6D8XYo8NvevI3OlwzBRmiMtsJ2sXupBgEA7PPxiDwNni3qIUkhZ6j5wofDUA==} @@ -1692,6 +1755,24 @@ packages: engines: {node: '>= 20'} hasBin: true + markmap-common@0.18.9: + resolution: {integrity: sha512-MV2HQO7IGIm3jWEJXSG8vmdpqf4WIDXcEyAEN52lrWR1qD53Zg5l81JwjXoZ2l0rY5mofKYqUFlmdM2fqTGMVg==} + + markmap-html-parser@0.18.11: + resolution: {integrity: sha512-+kC5C4sCGntGUhGvTa5VIb5rtM75cSy/VCy3tzZoNAcn2qZGdgYvljN0WvjsOzrEzp+V6XKgwzO0u2TdzNAiOg==} + peerDependencies: + markmap-common: '*' + + markmap-lib@0.18.12: + resolution: {integrity: sha512-WCA4OT+b71jYg0e4PS/6NRKqihod5OpPsvw1jEGHQwCtqQrY/yXXCeRyuL3axOS5cMy5pV8BSl4CwKfJU1LxJg==} + peerDependencies: + markmap-common: '*' + + markmap-view@0.18.12: + resolution: {integrity: sha512-D8bzT1YwIC/8rkbwm6WzigVUrpOAGv7ioEGTi1Lj+Oo8gO5sAm6hhli27jvTgUcZ9TwBeIWZ+dSUP+AupYUGlQ==} + peerDependencies: + markmap-common: '*' + md-editor-v3@5.8.5: resolution: {integrity: sha512-NsqAmmAx/ykA1AcwxcHH4Hkn4VAPkqMX7Hd6Lv4FcwQoMQ70wWmJfs/mokyPGkqr4oYqqn8LRMBTqFNfoP0O0A==} peerDependencies: @@ -1765,6 +1846,9 @@ packages: node-releases@2.0.21: resolution: {integrity: sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==} + npm2url@0.2.4: + resolution: {integrity: sha512-arzGp/hQz0Ey+ZGhF64XVH7Xqwd+1Q/po5uGiBbzph8ebX6T0uvt3N7c1nBHQNsQVykQgHhqoRTX7JFcHecGuw==} + nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} @@ -1794,6 +1878,15 @@ packages: resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==} engines: {node: '>= 0.10'} + parse5-htmlparser2-tree-adapter@7.1.0: + resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} + + parse5-parser-stream@7.1.2: + resolution: {integrity: sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==} + + parse5@7.3.0: + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -1846,6 +1939,10 @@ packages: engines: {node: '>=14'} hasBin: true + prismjs@1.30.0: + resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} + engines: {node: '>=6'} + prr@1.0.1: resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} @@ -2029,6 +2126,10 @@ packages: undici-types@7.12.0: resolution: {integrity: sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==} + undici@6.22.0: + resolution: {integrity: sha512-hU/10obOIu62MGYjdskASR3CUAiYaFTtC9Pa6vHyf//mAipSvSQg6od2CnJswq7fvzNS3zJhxoRkgNVaHurWKw==} + engines: {node: '>=18.17'} + update-browserslist-db@1.1.3: resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} hasBin: true @@ -2130,6 +2231,14 @@ packages: webpack-cli: optional: true + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -2153,6 +2262,11 @@ packages: engines: {node: '>= 0.10.0'} hasBin: true + yaml@2.8.1: + resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==} + engines: {node: '>= 14.6'} + hasBin: true + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -2796,6 +2910,10 @@ snapshots: '@eslint/core': 0.15.2 levn: 0.4.1 + '@gera2ld/jsx-dom@2.2.2': + dependencies: + '@babel/runtime': 7.28.3 + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.6': @@ -3095,12 +3213,16 @@ snapshots: '@vavt/util@2.1.0': {} - '@vitejs/plugin-vue@6.0.1(vite@7.1.5(@types/node@24.5.2)(less@4.4.1)(terser@5.44.0))(vue@3.5.21)': + '@vitejs/plugin-vue@6.0.1(vite@7.1.5(@types/node@24.5.2)(less@4.4.1)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.21)': dependencies: '@rolldown/pluginutils': 1.0.0-beta.29 - vite: 7.1.5(@types/node@24.5.2)(less@4.4.1)(terser@5.44.0) + vite: 7.1.5(@types/node@24.5.2)(less@4.4.1)(terser@5.44.0)(yaml@2.8.1) vue: 3.5.21 + '@vscode/markdown-it-katex@1.1.2': + dependencies: + katex: 0.16.25 + '@vue/compiler-core@3.5.21': dependencies: '@babel/parser': 7.28.3 @@ -3380,6 +3502,29 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + cheerio-select@2.1.0: + dependencies: + boolbase: 1.0.0 + css-select: 5.2.2 + css-what: 6.2.2 + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + + cheerio@1.0.0: + dependencies: + cheerio-select: 2.1.0 + dom-serializer: 2.0.0 + domhandler: 5.0.3 + domutils: 3.2.2 + encoding-sniffer: 0.2.1 + htmlparser2: 9.1.0 + parse5: 7.3.0 + parse5-htmlparser2-tree-adapter: 7.1.0 + parse5-parser-stream: 7.1.2 + undici: 6.22.0 + whatwg-mimetype: 4.0.0 + chrome-trace-event@1.0.4: {} claygl@1.3.0: {} @@ -3411,6 +3556,8 @@ snapshots: commander@7.2.0: {} + commander@8.3.0: {} + compute-scroll-into-view@1.0.20: {} concat-map@0.0.1: {} @@ -3433,6 +3580,16 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + css-select@5.2.2: + dependencies: + boolbase: 1.0.0 + css-what: 6.2.2 + domhandler: 5.0.3 + domutils: 3.2.2 + nth-check: 2.1.1 + + css-what@6.2.2: {} + cssesc@3.0.0: {} cssfilter@0.0.10: {} @@ -3632,6 +3789,24 @@ snapshots: dom-scroll-into-view@2.0.1: {} + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + domelementtype@2.3.0: {} + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + domutils@3.2.2: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + echarts-gl@2.0.9(echarts@6.0.0): dependencies: claygl: 1.3.0 @@ -3647,6 +3822,11 @@ snapshots: emojis-list@3.0.0: {} + encoding-sniffer@0.2.1: + dependencies: + iconv-lite: 0.6.3 + whatwg-encoding: 3.1.1 + enhanced-resolve@5.18.3: dependencies: graceful-fs: 4.2.11 @@ -3654,6 +3834,8 @@ snapshots: entities@4.5.0: {} + entities@6.0.1: {} + errno@0.1.8: dependencies: prr: 1.0.1 @@ -3896,6 +4078,13 @@ snapshots: hookable@5.5.3: {} + htmlparser2@9.1.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + entities: 4.5.0 + iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 @@ -3956,6 +4145,10 @@ snapshots: json5@2.2.3: {} + katex@0.16.25: + dependencies: + commander: 8.3.0 + keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -4029,6 +4222,10 @@ snapshots: dependencies: markdown-it: 14.1.0 + markdown-it-ins@4.0.0: {} + + markdown-it-mark@4.0.0: {} + markdown-it-sub@2.0.0: {} markdown-it-sup@2.0.0: {} @@ -4048,6 +4245,41 @@ snapshots: marked@16.3.0: {} + markmap-common@0.18.9: + dependencies: + '@babel/runtime': 7.28.3 + '@gera2ld/jsx-dom': 2.2.2 + npm2url: 0.2.4 + + markmap-html-parser@0.18.11(markmap-common@0.18.9): + dependencies: + '@babel/runtime': 7.28.3 + cheerio: 1.0.0 + markmap-common: 0.18.9 + + markmap-lib@0.18.12(markmap-common@0.18.9): + dependencies: + '@babel/runtime': 7.28.3 + '@vscode/markdown-it-katex': 1.1.2 + highlight.js: 11.11.1 + katex: 0.16.25 + markdown-it: 14.1.0 + markdown-it-ins: 4.0.0 + markdown-it-mark: 4.0.0 + markdown-it-sub: 2.0.0 + markdown-it-sup: 2.0.0 + markmap-common: 0.18.9 + markmap-html-parser: 0.18.11(markmap-common@0.18.9) + markmap-view: 0.18.12(markmap-common@0.18.9) + prismjs: 1.30.0 + yaml: 2.8.1 + + markmap-view@0.18.12(markmap-common@0.18.9): + dependencies: + '@babel/runtime': 7.28.3 + d3: 7.9.0 + markmap-common: 0.18.9 + md-editor-v3@5.8.5(vue@3.5.21): dependencies: '@codemirror/autocomplete': 6.18.6 @@ -4135,6 +4367,8 @@ snapshots: node-releases@2.0.21: {} + npm2url@0.2.4: {} + nth-check@2.1.1: dependencies: boolbase: 1.0.0 @@ -4168,6 +4402,19 @@ snapshots: parse-node-version@1.0.1: {} + parse5-htmlparser2-tree-adapter@7.1.0: + dependencies: + domhandler: 5.0.3 + parse5: 7.3.0 + + parse5-parser-stream@7.1.2: + dependencies: + parse5: 7.3.0 + + parse5@7.3.0: + dependencies: + entities: 6.0.1 + path-exists@4.0.0: {} path-key@3.1.1: {} @@ -4205,6 +4452,8 @@ snapshots: prettier@3.6.2: {} + prismjs@1.30.0: {} + prr@1.0.1: optional: true @@ -4380,6 +4629,8 @@ snapshots: undici-types@7.12.0: {} + undici@6.22.0: {} + update-browserslist-db@1.1.3(browserslist@4.26.2): dependencies: browserslist: 4.26.2 @@ -4392,7 +4643,7 @@ snapshots: util-deprecate@1.0.2: {} - vite@7.1.5(@types/node@24.5.2)(less@4.4.1)(terser@5.44.0): + vite@7.1.5(@types/node@24.5.2)(less@4.4.1)(terser@5.44.0)(yaml@2.8.1): dependencies: esbuild: 0.25.10 fdir: 6.5.0(picomatch@4.0.3) @@ -4405,6 +4656,7 @@ snapshots: fsevents: 2.3.3 less: 4.4.1 terser: 5.44.0 + yaml: 2.8.1 vue-eslint-parser@10.2.0(eslint@9.36.0): dependencies: @@ -4480,6 +4732,12 @@ snapshots: - esbuild - uglify-js + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + + whatwg-mimetype@4.0.0: {} + which@2.0.2: dependencies: isexe: 2.0.0 @@ -4498,6 +4756,8 @@ snapshots: commander: 2.20.3 cssfilter: 0.0.10 + yaml@2.8.1: {} + yocto-queue@0.1.0: {} zrender@5.6.1: diff --git a/web/src/App.vue b/web/src/App.vue index 3d705f8c..cc8ba07a 100644 --- a/web/src/App.vue +++ b/web/src/App.vue @@ -1,11 +1,12 @@ diff --git a/web/src/assets/css/base.css b/web/src/assets/css/base.css index 0b3d78cd..714c836f 100644 --- a/web/src/assets/css/base.css +++ b/web/src/assets/css/base.css @@ -37,7 +37,7 @@ --gray-100: #eff2f2; --gray-50: #f5f7f7; --gray-25: #f8fafa; - --gray-10: #fafcfc; + --gray-10: #fbfcfc; --gray-0: #ffffff; --main-color: #016179; @@ -48,9 +48,13 @@ --color-primary: #1c7796; --color-primary-bg: #d6eef1; --color-success: #45b30e; + --color-success-light: #f6ffed; --color-error: #c73234; + --color-error-light: #fff2f0; --color-info: #0058d4; + --color-info-light: #e6f7ff; --color-warning: #faad14; + --color-warning-light: #fffbe6; /* Chart Colors - 图表颜色系统 */ --chart-primary: var(--main-500); /* 主色调 - 用于主要数据 */ @@ -110,5 +114,59 @@ --bg-sider: var(--main-5); --color-text: var(--c-black); + /* Shadow System - 阴影系统 */ + --shadow-0: rgba(0, 0, 0, 0.02); + --shadow-1: rgba(0, 0, 0, 0.05); + --shadow-2: rgba(0, 0, 0, 0.08); + --shadow-3: rgba(0, 0, 0, 0.12); + --shadow-4: rgba(0, 0, 0, 0.16); + --shadow-5: rgba(0, 0, 0, 0.20); + + /* Extended Chart Variables - 扩展图表变量 */ + --chart-primary-lighter: var(--chart-primary-light); + --chart-info-lighter: var(--chart-info-light); + --chart-primary-shadow: rgba(28, 119, 150, 0.3); + --chart-info-shadow: rgba(24, 144, 255, 0.3); + --min-width: 400px; + + /* Ant Design 兼容变量 */ + --color-bg-container: var(--main-0); + --color-bg-elevated: var(--gray-10); + --color-text-secondary: rgba(0, 0, 0, 0.65); + --color-text-tertiary: rgba(0, 0, 0, 0.45); + --color-trans-light: rgba(255, 255, 255, 0.85); + --color-trans-dark: rgba(0, 0, 0, 0.85); +} + +:root { + --scrollbar-width: 4px; +} + +/* 全局滚动条样式 */ +::-webkit-scrollbar { + width: var(--scrollbar-width); + height: var(--scrollbar-width); +} + +::-webkit-scrollbar-track { + background: var(--gray-100); + border-radius: calc(var(--scrollbar-width) / 2); +} + +::-webkit-scrollbar-thumb { + background: var(--gray-200); + border-radius: calc(var(--scrollbar-width) / 2); + transition: background 0.2s ease; + cursor: pointer; +} + +::-webkit-scrollbar-thumb:hover { + background: var(--gray-100); +} + +/* Firefox 滚动条样式 */ +* { + scrollbar-width: thin; + scrollbar-color: var(--gray-200) transparent; } diff --git a/web/src/assets/css/base.dark.css b/web/src/assets/css/base.dark.css new file mode 100644 index 00000000..c7a99414 --- /dev/null +++ b/web/src/assets/css/base.dark.css @@ -0,0 +1,142 @@ + +/* 深色模式 */ +:root.dark { + --main-1000: #e1f6fb; + --main-900: #c4eaf5; + --main-800: #a3d8e8; + --main-700: #82c3d6; + --main-600: #5faec2; + --main-500: #4a9fb8; + --main-400: #3996ae; + --main-300: #24839a; + --main-200: #046a82; + --main-100: #035065; + --main-50: #023944; + --main-40: #01151f; + --main-30: #010d14; + --main-20: #010a0f; + --main-10: #00070a; + --main-5: #000405; + --main-1: #000102; + --main-0: #000000; + + --gray-10000: #ffffff; + --gray-2000: #f8fafa; + --gray-1000: #f5f7f7; + --gray-900: #eff2f2; + --gray-800: #eef0f0; + --gray-700: #e4e6e6; + --gray-600: #d7d9d9; + --gray-500: #bdbfbf; + --gray-400: #979999; + --gray-300: #697070; + --gray-200: #4c4d4d; + --gray-150: #323333; + --gray-100: #1e1f1f; + --gray-50: #151616; + --gray-25: #0c0d0d; + --gray-10: #080808; + --gray-0: #030303; + + --main-color: #4a9fb8; + --main-bright: #5faec2; + --secondry-color: #8fa5b0; + --error-color: #ff6b6b; + --color-primary: #4a9fb8; + --color-primary-bg: #1a3a42; + --color-success: #52c41a; + --color-success-light: #162312; + --color-error: #ff4d4f; + --color-error-light: #2a1215; + --color-info: #3d8fff; + --color-info-light: #111b26; + --color-warning: #faad14; + --color-warning-light: #2b2111; + + /* Chart Colors - 深色模式图表颜色 */ + --chart-primary: var(--main-500); + --chart-primary-light: var(--main-600); + --chart-primary-dark: var(--main-400); + + --chart-success: #52c41a; + --chart-success-light: #73d13d; + --chart-success-dark: #389e0d; + + --chart-warning: #faad14; + --chart-warning-light: #ffc53d; + --chart-warning-dark: #d48806; + + --chart-error: #ff4d4f; + --chart-error-light: #ff7875; + --chart-error-dark: #cf1322; + + --chart-info: #1890ff; + --chart-info-light: #40a9ff; + --chart-info-dark: #096dd9; + + --chart-secondary: #722ed1; + --chart-secondary-light: #9254de; + --chart-secondary-dark: #531dab; + + --chart-accent: #13c2c2; + --chart-accent-light: #36cfc9; + --chart-accent-dark: #08979c; + + /* Stats Card Colors - 深色模式统计卡片 */ + --stats-primary-bg: #1e3a8a; + --stats-primary-color: #93c5fd; + --stats-success-bg: #14532d; + --stats-success-color: #86efac; + --stats-info-bg: #0c4a6e; + --stats-info-color: #7dd3fc; + --stats-warning-bg: #78350f; + --stats-warning-color: #fcd34d; + --stats-secondary-bg: #581c87; + --stats-secondary-color: #d8b4fe; + --stats-error-bg: #7f1d1d; + --stats-error-color: #fca5a5; + + --bg-sider: #141414; + --color-text: #ffffff; + + /* Ant Design 兼容变量 - 深色模式 */ + --color-bg-container: #1f1f1f; + --color-bg-elevated: #262626; + --color-text-secondary: rgba(255, 255, 255, 0.65); + --color-text-tertiary: rgba(255, 255, 255, 0.45); + + /* Shadow System - 深色模式阴影系统 */ + --shadow-0: rgba(0, 0, 0, 0.1); + --shadow-1: rgba(0, 0, 0, 0.2); + --shadow-2: rgba(0, 0, 0, 0.3); + --shadow-3: rgba(0, 0, 0, 0.4); + --shadow-4: rgba(0, 0, 0, 0.5); + --shadow-5: rgba(0, 0, 0, 0.6); + + /* Extended Chart Variables - 扩展图表变量 */ + --chart-primary-lighter: var(--chart-primary-light); + --chart-info-lighter: var(--chart-info-light); + --chart-primary-shadow: rgba(74, 159, 184, 0.4); + --chart-info-shadow: rgba(24, 144, 255, 0.4); + + --color-trans-light: rgba(0, 0, 0, 0.85); + --color-trans-dark: rgba(255, 255, 255, 0.85); +} + +:root.dark .markmap { + --markmap-max-width: 9999px; + --markmap-a-color: var(--main-500); + --markmap-a-hover-color: var(--main-600); + --markmap-code-bg: var(--gray-50); + --markmap-code-color: var(--gray-700); + --markmap-highlight-bg: var(--color-warning-light); + --markmap-table-border: 1px solid currentColor; + --markmap-font: 300 16px / 20px sans-serif; + --markmap-circle-open-bg: var(--main-0); + --markmap-text-color: var(--gray-700); + --markmap-highlight-node-bg: var(--color-error-light); +} + +:root.dark .md-editor { + --md-bk-color: var(--gray-0); +} \ No newline at end of file diff --git a/web/src/assets/css/dashboard.css b/web/src/assets/css/dashboard.css index 7b5ea4b6..06bbc000 100644 --- a/web/src/assets/css/dashboard.css +++ b/web/src/assets/css/dashboard.css @@ -127,201 +127,6 @@ box-shadow: 0 4px 12px rgba(245, 34, 45, 0.15); } -/* Stats Overview Component - 统计概览组件样式 */ -.stats-overview-container { - margin-top: 8px; - - .stats-grid { - display: grid; - padding: 0 16px; - grid-template-columns: repeat(6, 1fr); - gap: 16px; - - .stat-card { - background: var(--gray-0); - border-radius: 8px; - padding: 20px; - border: 1px solid var(--gray-100); - transition: all 0.2s ease; - display: flex; - flex-direction: row; - align-items: center; - text-align: left; - min-height: 80px; - justify-content: flex-start; - - &:hover { - border-color: var(--gray-200); - box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.05); - } - - &.primary { - .stat-icon { - background-color: var(--stats-primary-bg); - color: var(--stats-primary-color); - } - } - - &.success { - .stat-icon { - background-color: var(--stats-success-bg); - color: var(--stats-success-color); - } - } - - &.info { - .stat-icon { - background-color: var(--stats-info-bg); - color: var(--stats-info-color); - } - } - - &.warning { - .stat-icon { - background-color: var(--stats-warning-bg); - color: var(--stats-warning-color); - } - } - - &.secondary { - .stat-icon { - background-color: var(--stats-secondary-bg); - color: var(--stats-secondary-color); - } - } - - &.satisfaction-high { - .stat-icon { - background-color: var(--stats-success-bg); - color: var(--stats-success-color); - } - } - - &.satisfaction-medium { - .stat-icon { - background-color: var(--stats-warning-bg); - color: var(--stats-warning-color); - } - } - - &.satisfaction-low { - .stat-icon { - background-color: var(--stats-error-bg); - color: var(--stats-error-color); - } - } - - .stat-icon { - width: 44px; - height: 44px; - border-radius: 8px; - display: flex; - align-items: center; - justify-content: center; - margin-right: 16px; - flex-shrink: 0; - - .icon { - width: 20px; - height: 20px; - } - } - - .stat-content { - flex: 1; - display: flex; - flex-direction: column; - align-items: flex-start; - - .stat-value { - font-size: 24px; - font-weight: 700; - color: var(--gray-1000); - line-height: 1.1; - margin-bottom: 4px; - } - - .stat-label { - font-size: 13px; - color: var(--gray-600); - font-weight: 500; - margin-bottom: 8px; - } - - .stat-trend { - display: flex; - align-items: center; - gap: 4px; - margin-top: 4px; - - .trend-icon { - width: 14px; - height: 14px; - - &.up { - color: var(--stats-success-color); - } - - &.down { - color: var(--stats-error-color); - } - } - - .trend-text { - font-size: 12px; - font-weight: 500; - color: var(--gray-600); - } - } - } - } - } -} - -/* Stats Overview 响应式设计 */ -@media (max-width: 1200px) { - .stats-overview-container { - .stats-grid { - grid-template-columns: repeat(3, 1fr); - gap: 16px; - } - } -} - -@media (max-width: 768px) { - .stats-overview-container { - .stats-grid { - grid-template-columns: repeat(2, 1fr); - gap: 12px; - - .stat-card { - padding: 16px; - min-height: 80px; - - .stat-icon { - width: 36px; - height: 36px; - margin-right: 12px; - - .icon { - width: 18px; - height: 18px; - } - } - - .stat-content { - .stat-value { - font-size: 20px; - } - - .stat-label { - font-size: 12px; - } - } - } - } - } -} /* 共享的卡片样式 */ .dashboard-card { @@ -440,10 +245,6 @@ color: var(--gray-1000); } -.dashboard-card .ant-divider { - border-color: var(--gray-200) !important; -} - .dashboard-card .ant-table { background-color: var(--gray-0); border-radius: 8px; @@ -489,179 +290,3 @@ } } -/* 紧凑型统计网格 */ -.compact-stats-grid { - display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 8px; - margin-bottom: 16px; - - .mini-stat-card { - background-color: var(--gray-0); - border: 1px solid var(--gray-200); - border-radius: 6px; - padding: 16px; - text-align: center; - transition: all 0.2s ease; - - &:hover { - border-color: var(--gray-300); - box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1); - } - - .mini-stat-value { - font-size: 20px; - font-weight: 600; - color: var(--gray-1000); - line-height: 1.2; - margin-bottom: 4px; - } - - .mini-stat-label { - font-size: 12px; - color: var(--gray-600); - font-weight: 500; - text-transform: uppercase; - letter-spacing: 0.025em; - } - } -} - -/* 紧凑型图表容器 */ -.compact-chart-container { - background-color: var(--gray-0); - border: 1px solid var(--gray-200); - border-radius: 6px; - padding: 16px; - height: 240px; - transition: all 0.2s ease; - - &:hover { - border-color: var(--gray-300); - box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1); - } - - .chart-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 12px; - - .chart-title { - font-size: 14px; - font-weight: 600; - color: var(--gray-1000); - } - - .chart-subtitle { - font-size: 11px; - color: var(--gray-600); - background-color: var(--gray-100); - padding: 2px 6px; - border-radius: 4px; - font-weight: 500; - text-transform: uppercase; - letter-spacing: 0.025em; - } - } - - .compact-chart { - height: 180px; - width: 100%; - } -} - -/* 排名显示样式 */ -.rank-display { - display: flex; - align-items: center; - justify-content: center; - - .rank-medal { - font-size: 20px; - } - - .rank-number { - display: inline-flex; - align-items: center; - justify-content: center; - width: 24px; - height: 24px; - background-color: var(--gray-100); - border-radius: 50%; - font-size: 12px; - font-weight: 600; - color: var(--gray-600); - border: 1px solid var(--gray-200); - } -} - -/* 指标值样式 */ -.metric-value { - font-weight: 500; - color: var(--gray-1000); - font-size: 14px; -} - -/* 排名样式 */ -.dashboard-card .ant-table-tbody > tr.top-rank.first { - background-color: #fef3c7; - border-radius: 4px; -} - -.dashboard-card .ant-table-tbody > tr.top-rank.second { - background-color: var(--gray-100); - border-radius: 4px; -} - -.dashboard-card .ant-table-tbody > tr.top-rank.third { - background-color: #fed7aa; - border-radius: 4px; -} - -/* 响应式设计 */ -@media (max-width: 1200px) { - .compact-stats-grid { - grid-template-columns: repeat(2, 1fr); - } -} - -@media (max-width: 768px) { - .compact-stats-grid { - grid-template-columns: repeat(2, 1fr); - gap: 6px; - - .mini-stat-card { - padding: 8px; - - .mini-stat-value { - font-size: 16px; - } - - .mini-stat-label { - font-size: 10px; - } - } - } - - .compact-chart-container { - height: 180px; - padding: 8px; - - .compact-chart { - height: 130px; - } - - .chart-header { - margin-bottom: 4px; - - .chart-title { - font-size: 11px; - } - - .chart-subtitle { - font-size: 9px; - } - } - } -} diff --git a/web/src/assets/css/main.css b/web/src/assets/css/main.css index 3100b0df..dc9dc0fe 100644 --- a/web/src/assets/css/main.css +++ b/web/src/assets/css/main.css @@ -1,4 +1,5 @@ @import './base.css'; +@import './base.dark.css'; @import './sigma.css'; @import './shorts.css'; @import './dashboard.css'; @@ -22,6 +23,7 @@ body { display: flow-root; min-height: 100vh; color: var(--gray-900); + background: var(--gray-0); line-height: 1.6; font-family: -apple-system, BlinkMacSystemFont, 'Noto Sans SC', 'Roboto', 'HarmonyOS Sans SC', 'Segoe UI', 'Helvetica Neue', Arial, sans-serif; font-size: 15px; diff --git a/web/src/assets/css/sigma.css b/web/src/assets/css/sigma.css index ec73888e..703cc4f9 100644 --- a/web/src/assets/css/sigma.css +++ b/web/src/assets/css/sigma.css @@ -3,7 +3,6 @@ position: relative; width: 100%; height: 100%; - background: #fafafa; } .sigma-container canvas { @@ -22,34 +21,34 @@ /* 高亮样式 */ .node-highlighted { - stroke: #ff0000 !important; + stroke: var(--color-error) !important; stroke-width: 3px !important; } .edge-highlighted { - stroke: #ff0000 !important; + stroke: var(--color-error) !important; stroke-width: 3px !important; } /* 选中样式 */ .node-selected { - stroke: #1890ff !important; + stroke: var(--color-info) !important; stroke-width: 4px !important; } .edge-selected { - stroke: #1890ff !important; + stroke: var(--color-info) !important; stroke-width: 4px !important; } /* 悬停样式 */ .node-focused { - stroke: #52c41a !important; + stroke: var(--color-success) !important; stroke-width: 2px !important; } .edge-focused { - stroke: #52c41a !important; + stroke: var(--color-success) !important; stroke-width: 2px !important; } @@ -65,8 +64,8 @@ /* 工具提示 */ .sigma-tooltip { position: absolute; - background: rgba(0, 0, 0, 0.8); - color: white; + background: var(--shadow-5); + color: var(--gray-0); padding: 8px 12px; border-radius: 4px; font-size: 12px; diff --git a/web/src/assets/theme.js b/web/src/assets/theme.js deleted file mode 100644 index fa0851f8..00000000 --- a/web/src/assets/theme.js +++ /dev/null @@ -1,14 +0,0 @@ -// https://www.antdv.com/docs/vue/customize-theme-cn -export const themeConfig = { - token: { - fontFamily: "'HarmonyOS Sans SC', Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;", - colorPrimary: '#1c7796', - colorPrimaryBg: '#d6eef1', - colorInfo: '#0058d4', - colorSuccess: '#45b30e', - colorError: '#c73234', - colorBgBase: '#ffffff', - borderRadius: 8, - wireframe: false, - }, -} \ No newline at end of file diff --git a/web/src/components/AgentChatComponent.vue b/web/src/components/AgentChatComponent.vue index 2f21726f..3d046942 100644 --- a/web/src/components/AgentChatComponent.vue +++ b/web/src/components/AgentChatComponent.vue @@ -1415,7 +1415,7 @@ watch(conversations, () => { display: flex; flex-direction: column; overflow-x: hidden; - background: white; + background: var(--gray-0); position: relative; box-sizing: border-box; overflow-y: scroll; @@ -1565,7 +1565,7 @@ watch(conversations, () => { width: 100%; margin: 0 auto; padding: 4px 2rem 0 2rem; - background: white; + background: var(--gray-0); z-index: 1000; .message-input-wrapper { @@ -1581,55 +1581,13 @@ watch(conversations, () => { .note { font-size: small; - color: #ccc; + color: var(--gray-300); margin: 4px 0; user-select: none; } } } -.conversation-list::-webkit-scrollbar { - position: absolute; - width: 4px; - height: 4px; -} - -.conversation-list::-webkit-scrollbar-track { - background: transparent; - border-radius: 4px; -} - -.conversation-list::-webkit-scrollbar-thumb { - background: var(--gray-400); - border-radius: 4px; -} - -.conversation-list::-webkit-scrollbar-thumb:hover { - background: rgb(100, 100, 100); - border-radius: 4px; -} - -.chat::-webkit-scrollbar { - position: absolute; - width: 4px; - height: 4px; -} - -.chat::-webkit-scrollbar-track { - background: transparent; - border-radius: 4px; -} - -.chat::-webkit-scrollbar-thumb { - background: var(--gray-400); - border-radius: 4px; -} - -.chat::-webkit-scrollbar-thumb:hover { - background: rgb(100, 100, 100); - border-radius: 4px; -} - .loading-dots { display: inline-flex; align-items: center; @@ -1643,7 +1601,6 @@ watch(conversations, () => { background: linear-gradient(135deg, var(--main-color), var(--main-700)); border-radius: 50%; animation: dotPulse 1.4s infinite ease-in-out both; - box-shadow: 0 1px 3px rgba(59, 130, 246, 0.3); } .loading-dots div:nth-child(1) { @@ -1767,7 +1724,7 @@ watch(conversations, () => { @media (max-width: 1800px) { .chat-header { - background-color: white; + background-color: var(--gray-0); border-bottom: 1px solid var(--gray-100); } } diff --git a/web/src/components/AgentConfigSidebar.vue b/web/src/components/AgentConfigSidebar.vue index d6157904..0af27ec2 100644 --- a/web/src/components/AgentConfigSidebar.vue +++ b/web/src/components/AgentConfigSidebar.vue @@ -574,8 +574,8 @@ watch(() => props.isOpen, (newVal) => { position: relative; width: 0; height: 100vh; - background: white; - border-left: 1px solid #e8e8e8; + background: var(--gray-0); + border-left: 1px solid var(--gray-200); transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1); overflow: hidden; display: flex; @@ -592,7 +592,7 @@ watch(() => props.isOpen, (newVal) => { align-items: center; padding: 10px 20px; border-bottom: 1px solid var(--gray-200); - background: #fff; + background: var(--gray-0); flex-shrink: 0; min-width: 400px; @@ -645,7 +645,7 @@ watch(() => props.isOpen, (newVal) => { bottom: 0px; padding: 12px 0; border-top: 1px solid var(--gray-100); - background: #fff; + background: var(--gray-0); // min-width: 400px; z-index: 10; @@ -664,7 +664,7 @@ watch(() => props.isOpen, (newVal) => { &.changed { background-color: var(--main-color); - color: #fff; + color: var(--gray-0); } &:hover { @@ -886,7 +886,7 @@ watch(() => props.isOpen, (newVal) => { padding: 10px 12px; cursor: pointer; transition: all 0.2s ease; - background: white; + background: var(--gray-0); &:hover { border-color: var(--main-color); @@ -946,7 +946,7 @@ watch(() => props.isOpen, (newVal) => { } :deep(.ant-modal-header) { - background: white; + background: var(--gray-0); border-bottom: 1px solid var(--gray-200); padding: 16px 20px; @@ -959,7 +959,7 @@ watch(() => props.isOpen, (newVal) => { :deep(.ant-modal-body) { padding: 20px; - background: white; + background: var(--gray-0); } .tools-modal-content { @@ -972,7 +972,7 @@ watch(() => props.isOpen, (newVal) => { height: 36px; font-size: 14px; transition: all 0.2s ease; - background: white; + background: var(--gray-0); .search-icon { color: var(--gray-500); @@ -1002,7 +1002,7 @@ watch(() => props.isOpen, (newVal) => { overflow-y: auto; border-radius: 8px; margin-bottom: 16px; - background: white; + background: var(--gray-0); // 在小屏幕下调整为单列布局 @media (max-width: 480px) { @@ -1034,7 +1034,7 @@ watch(() => props.isOpen, (newVal) => { transition: all 0.2s ease; border-radius: 8px; margin-bottom: 4px; - background: white; + background: var(--gray-0); border: 1px solid var(--gray-200); &:hover { @@ -1129,7 +1129,7 @@ watch(() => props.isOpen, (newVal) => { &.ant-btn-default { border: 1px solid var(--gray-300); color: var(--gray-700); - background: white; + background: var(--gray-0); &:hover { border-color: var(--main-color); @@ -1140,7 +1140,7 @@ watch(() => props.isOpen, (newVal) => { &.ant-btn-primary { background: var(--main-color); border: none; - color: white; + color: var(--gray-0); &:hover { background: var(--main-color); diff --git a/web/src/components/AgentMessageComponent.vue b/web/src/components/AgentMessageComponent.vue index 42adc209..a5df374c 100644 --- a/web/src/components/AgentMessageComponent.vue +++ b/web/src/components/AgentMessageComponent.vue @@ -24,6 +24,7 @@ themeStore.isDark ? 'dark' : 'light'); + // 工具相关方法 const getToolNameByToolCall = (toolCall) => { const toolId = toolCall.name || toolCall.function?.name; @@ -247,7 +253,7 @@ const toggleToolCall = (toolCallId) => { font-size: 15px; line-height: 24px; box-sizing: border-box; - color: black; + color: var(--gray-10000); max-width: 100%; position: relative; letter-spacing: .25px; @@ -291,12 +297,12 @@ const toggleToolCall = (toolCallId) => { } .err-msg { - color: #d15252; - border: 1px solid #f19999; + color: var(--color-error); + border: 1px solid currentColor; padding: 0.5rem 1rem; border-radius: 8px; text-align: left; - background: #fffbfb; + background: var(--color-error-light); margin-bottom: 10px; cursor: pointer; } @@ -310,7 +316,7 @@ const toggleToolCall = (toolCallId) => { margin-top: 10px; margin-bottom: 15px; border-radius: 8px; - border: 1px solid var(--gray-200); + border: 1px solid var(--gray-150); background-color: var(--gray-25); overflow: hidden; transition: all 0.2s ease; @@ -368,9 +374,9 @@ const toggleToolCall = (toolCallId) => { display: flex; align-items: center; gap: 8px; - background-color: #fef2f2; + background-color: var(--color-error-light); // border: 1px solid #f87171; - color: #991b1b; + color: var(--color-error); span { line-height: 1.5; } @@ -404,7 +410,7 @@ const toggleToolCall = (toolCallId) => { :deep(.tool-call-display) { background-color: var(--gray-25); - outline: 1px solid var(--gray-200); + outline: 1px solid var(--gray-150); border-radius: 8px; overflow: hidden; transition: all 0.2s ease; @@ -516,13 +522,13 @@ const toggleToolCall = (toolCallId) => { .retry-hint { margin-top: 8px; padding: 8px 16px; - color: #666; + color: var(--gray-600); font-size: 14px; text-align: left; } .retry-link { - color: #1890ff; + color: var(--color-info); cursor: pointer; margin-left: 4px; @@ -533,10 +539,10 @@ const toggleToolCall = (toolCallId) => { .ant-btn-icon-only { &:has(.anticon-stop) { - background-color: #ff4d4f !important; + background-color: var(--color-error) !important; &:hover { - background-color: #ff7875 !important; + background-color: var(--chart-error-light) !important; } } } diff --git a/web/src/components/AgentPopover.vue b/web/src/components/AgentPopover.vue index 719ae034..f64547b4 100644 --- a/web/src/components/AgentPopover.vue +++ b/web/src/components/AgentPopover.vue @@ -219,7 +219,6 @@ const emitRefresh = () => { diff --git a/web/src/components/FileTable.vue b/web/src/components/FileTable.vue index 58fc753f..60b8d94f 100644 --- a/web/src/components/FileTable.vue +++ b/web/src/components/FileTable.vue @@ -529,6 +529,7 @@ import ChunkParamsConfig from '@/components/ChunkParamsConfig.vue'; flex-grow: 1; flex-direction: column; max-height: 100%; + background: var(--gray-10); overflow: hidden; border-radius: 12px; border: 1px solid var(--gray-150); @@ -685,7 +686,7 @@ import ChunkParamsConfig from '@/components/ChunkParamsConfig.vue'; padding: 1px 5px; font-size: 10px; font-weight: bold; - color: white; + color: var(--gray-0); border-radius: 4px; text-transform: uppercase; opacity: 0.9; @@ -724,7 +725,7 @@ import ChunkParamsConfig from '@/components/ChunkParamsConfig.vue'; .panel-action-btn.auto-refresh-btn.ant-btn-primary { background-color: var(--main-color); border-color: var(--main-color); - color: #fff; + color: var(--gray-0); } .panel-action-btn:hover { diff --git a/web/src/components/FileUploadModal.vue b/web/src/components/FileUploadModal.vue index b5c79d64..582fa366 100644 --- a/web/src/components/FileUploadModal.vue +++ b/web/src/components/FileUploadModal.vue @@ -769,7 +769,7 @@ const chunkData = async () => { padding: 12px; background: #f0f7ff; border-radius: 4px; - color: #666; + color: var(--gray-500); font-size: 12px; } diff --git a/web/src/components/GraphCanvas.vue b/web/src/components/GraphCanvas.vue index 8baa2361..0997c478 100644 --- a/web/src/components/GraphCanvas.vue +++ b/web/src/components/GraphCanvas.vue @@ -5,7 +5,7 @@
-
+
@@ -18,6 +18,7 @@ - - diff --git a/web/src/components/SearchConfigTab.vue b/web/src/components/SearchConfigTab.vue index af15ce03..3d67047a 100644 --- a/web/src/components/SearchConfigTab.vue +++ b/web/src/components/SearchConfigTab.vue @@ -173,7 +173,7 @@ onMounted(() => { height: 100%; display: flex; flex-direction: column; - background: #fff; + background: var(--gray-0); } .config-header { diff --git a/web/src/components/StatusBar.vue b/web/src/components/StatusBar.vue index b39dbf3f..c2eb9b33 100644 --- a/web/src/components/StatusBar.vue +++ b/web/src/components/StatusBar.vue @@ -121,7 +121,7 @@ onUnmounted(() => { diff --git a/web/src/components/ThemeToggle.vue b/web/src/components/ThemeToggle.vue new file mode 100644 index 00000000..02c391f3 --- /dev/null +++ b/web/src/components/ThemeToggle.vue @@ -0,0 +1,33 @@ + + + + + diff --git a/web/src/components/UserInfoComponent.vue b/web/src/components/UserInfoComponent.vue index 9e2ffc47..ef77fe9e 100644 --- a/web/src/components/UserInfoComponent.vue +++ b/web/src/components/UserInfoComponent.vue @@ -10,22 +10,29 @@
{{ userStore.username }}
@@ -33,9 +40,6 @@ 登录 - - + {{ userStore.avatar ? '更换头像' : '上传头像' }} @@ -153,12 +157,17 @@ import { computed, ref } from 'vue'; import { useRouter } from 'vue-router'; import { useUserStore } from '@/stores/user'; -import { UserOutlined, LogoutOutlined, UploadOutlined } from '@ant-design/icons-vue'; +// +// +// +// import { message } from 'ant-design-vue'; -import { CircleUser, UserRoundCheck } from 'lucide-vue-next'; +import { CircleUser, UserRoundCheck, BookOpen, Sun, Moon, User, LogOut, Upload } from 'lucide-vue-next'; +import { useThemeStore } from '@/stores/theme' const router = useRouter(); const userStore = useUserStore(); +const themeStore = useThemeStore(); // 个人资料弹窗状态 const profileModalVisible = ref(false); @@ -222,6 +231,14 @@ const goToLogin = () => { router.push('/login'); }; +const openDocs = () => { + window.open('https://xerrors.github.io/Yuxi-Know/', '_blank', 'noopener,noreferrer') +} + +const toggleTheme = () => { + themeStore.toggleTheme() +} + // 打开个人资料页面 const openProfile = async () => { profileModalVisible.value = true; @@ -357,7 +374,7 @@ const handleAvatarChange = async (info) => { width: 100%; display: flex; align-items: center; - gap: 12px; + gap: 8px; &[data-align="center"] { justify-content: center; @@ -369,14 +386,14 @@ const handleAvatarChange = async (info) => { } .user-avatar { - width: 30px; - height: 30px; + width: 28px; + height: 28px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; - font-size: 18px; + font-size: 16px; cursor: pointer; position: relative; overflow: hidden; @@ -390,7 +407,7 @@ const handleAvatarChange = async (info) => { height: 100%; object-fit: contain; border-radius: 50%; - border: 2px solid #f0f0f0; + border: 2px solid var(--gray-150); } } @@ -401,33 +418,47 @@ const handleAvatarChange = async (info) => { border-radius: 50%; right: 0; bottom: 0; - border: 2px solid white; + border: 2px solid var(--gray-0); &.superadmin { - background-color: #c1bd00; // 红色,超管 + background-color: var(--color-warning); } &.admin { - background-color: #1890ff; // 蓝色,管理员 + background-color: var(--color-info); /* 蓝色,管理员 */ } &.user { - background-color: #52c41a; // 绿色,普通用户 + background-color: var(--color-success); /* 绿色,普通用户 */ } } +.user-info-display { + line-height: 1.4; +} + .user-menu-username { - font-weight: bold; + font-weight: 600; + color: var(--gray-900); + font-size: 14px; + display: block; + margin-bottom: 2px; +} + +.user-menu-details { + display: flex; + gap: 12px; + align-items: center; } .user-menu-info { font-size: 12px; - color: rgba(0, 0, 0, 0.65); + color: var(--gray-600); } .user-menu-role { font-size: 12px; - color: rgba(0, 0, 0, 0.45); + color: var(--gray-500); } .login-icon { @@ -438,22 +469,24 @@ const handleAvatarChange = async (info) => { justify-content: center; cursor: pointer; border-radius: 50%; - transition: background-color 0.3s; + transition: background-color 0.2s, color 0.2s; + color: var(--gray-900); &:hover { - background-color: rgba(0, 0, 0, 0.05); + background-color: var(--main-10); + color: var(--main-color); } } .profile-modal { :deep(.ant-modal-header) { padding: 20px 24px; - border-bottom: 1px solid #f0f0f0; + border-bottom: 1px solid var(--gray-150); .ant-modal-title { font-size: 18px; font-weight: 600; - color: #262626; + color: var(--gray-900); } } @@ -467,7 +500,7 @@ const handleAvatarChange = async (info) => { text-align: center; margin-bottom: 32px; padding-bottom: 24px; - border-bottom: 1px solid #f0f0f0; + border-bottom: 1px solid var(--gray-150); .avatar-container { display: inline-block; @@ -480,25 +513,25 @@ const handleAvatarChange = async (info) => { height: 80px; border-radius: 50%; object-fit: cover; - border: 3px solid #f0f0f0; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + border: 3px solid var(--gray-150); + box-shadow: 0 2px 8px var(--shadow-2); } .default-avatar { width: 80px; height: 80px; border-radius: 50%; - background: #f5f5f5; + background: var(--gray-50); display: flex; margin: 0 auto; align-items: center; justify-content: center; - border: 3px solid #f0f0f0; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + border: 3px solid var(--gray-150); + box-shadow: 0 2px 8px var(--shadow-2); // 确保图标居中 :deep(svg) { - color: #bfbfbf; + color: var(--gray-400); } } } @@ -507,7 +540,7 @@ const handleAvatarChange = async (info) => { .avatar-tips { margin-top: 8px; font-size: 12px; - color: #8c8c8c; + color: var(--gray-500); line-height: 1.4; } } @@ -521,7 +554,7 @@ const handleAvatarChange = async (info) => { display: flex; align-items: center; padding: 12px 0; - border-bottom: 1px solid #f5f5f5; + border-bottom: 1px solid var(--gray-50); &:last-child { border-bottom: none; @@ -530,18 +563,18 @@ const handleAvatarChange = async (info) => { .info-label { width: 80px; font-weight: 500; - color: #8c8c8c; + color: var(--gray-500); flex-shrink: 0; } .info-value { flex: 1; - color: #262626; + color: var(--gray-900); font-size: 14px; &.user-id { font-family: 'Monaco', 'Consolas', monospace; - // background: #f5f5f5; + // background: var(--gray-50); // padding: 4px 8px; border-radius: 4px; display: inline-block; @@ -559,7 +592,29 @@ const handleAvatarChange = async (info) => { .actions-section { text-align: center; padding-top: 16px; - border-top: 1px solid #f0f0f0; + border-top: 1px solid var(--gray-150); } } - \ No newline at end of file + +:deep(.ant-dropdown-menu) { + padding: 8px 0; +} + +:deep(.ant-dropdown-menu-title-content) { + display: flex; + align-items: center; + gap: 8px; + font-size: 13px; + color: var(--gray-900); +} + +:deep(.ant-dropdown-menu-item svg) { + margin-right: 4px; + color: var(--gray-900); + vertical-align: middle; +} + +.menu-text { + line-height: 20px; +} + diff --git a/web/src/components/UserManagementComponent.vue b/web/src/components/UserManagementComponent.vue index adc9af3d..b0af9281 100644 --- a/web/src/components/UserManagementComponent.vue +++ b/web/src/components/UserManagementComponent.vue @@ -481,7 +481,7 @@ onMounted(() => { .description { font-size: 14px; - color: #8c8c8c; + color: var(--gray-400); margin: 0; line-height: 1.4; margin-bottom: 16px; @@ -670,12 +670,12 @@ onMounted(() => { .user-modal { :deep(.ant-modal-header) { padding: 20px 24px; - border-bottom: 1px solid #f0f0f0; + border-bottom: 1px solid var(--gray-800); .ant-modal-title { font-size: 16px; font-weight: 600; - color: #262626; + color: var(--gray-900); } } @@ -692,20 +692,20 @@ onMounted(() => { label { font-weight: 500; - color: #262626; + color: var(--gray-900); } } } .error-text { - color: #ff4d4f; + color: var(--color-error); font-size: 12px; margin-top: 4px; line-height: 1.3; } .help-text { - color: #8c8c8c; + color: var(--gray-400); font-size: 12px; margin-top: 4px; line-height: 1.3; @@ -716,7 +716,7 @@ onMounted(() => { :deep(.ant-checkbox-wrapper) { font-weight: 500; - color: #495057; + color: var(--gray-600); } } } diff --git a/web/src/components/dashboard/AgentStatsComponent.vue b/web/src/components/dashboard/AgentStatsComponent.vue index 1ea607af..de869239 100644 --- a/web/src/components/dashboard/AgentStatsComponent.vue +++ b/web/src/components/dashboard/AgentStatsComponent.vue @@ -99,6 +99,15 @@ import { ref, onMounted, watch, nextTick, computed } from 'vue' import * as echarts from 'echarts' import { getColorByIndex, getChartColor } from '@/utils/chartColors' +import { useThemeStore } from '@/stores/theme' + +// CSS 变量解析工具函数 +function getCSSVariable(variableName, element = document.documentElement) { + return getComputedStyle(element).getPropertyValue(variableName).trim() +} + +// theme store +const themeStore = useThemeStore() // Props const props = defineProps({ @@ -177,6 +186,12 @@ const initConversationToolChart = () => { (!props.agentStats?.agent_conversation_counts?.length && !props.agentStats?.agent_tool_usage?.length)) return + // 如果已存在图表实例,先销毁 + if (conversationToolChart) { + conversationToolChart.dispose() + conversationToolChart = null + } + conversationToolChart = echarts.init(conversationToolChartRef.value) const conversationData = props.agentStats.agent_conversation_counts || [] @@ -211,11 +226,11 @@ const initConversationToolChart = () => { const option = { tooltip: { trigger: 'axis', - backgroundColor: 'rgba(255, 255, 255, 0.95)', - borderColor: '#e8e8e8', + backgroundColor: getCSSVariable('--gray-0'), + borderColor: getCSSVariable('--gray-200'), borderWidth: 1, textStyle: { - color: '#666' + color: getCSSVariable('--gray-600') } }, legend: { @@ -224,7 +239,7 @@ const initConversationToolChart = () => { top: '0%', orient: 'horizontal', textStyle: { - color: '#666' + color: getCSSVariable('--gray-500') } }, grid: { @@ -239,11 +254,11 @@ const initConversationToolChart = () => { data: topAgentIds, axisLine: { lineStyle: { - color: '#e8e8e8' + color: getCSSVariable('--gray-200') } }, axisLabel: { - color: '#666', + color: getCSSVariable('--gray-500'), interval: 0, // rotate: 45 } @@ -252,15 +267,15 @@ const initConversationToolChart = () => { type: 'value', axisLine: { lineStyle: { - color: '#e8e8e8' + color: getCSSVariable('--gray-200') } }, axisLabel: { - color: '#666' + color: getCSSVariable('--gray-500') }, splitLine: { lineStyle: { - color: '#f0f0f0' + color: getCSSVariable('--gray-150') } } }, @@ -280,7 +295,7 @@ const initConversationToolChart = () => { itemStyle: { color: getChartColor('primary'), shadowBlur: 10, - shadowColor: 'rgba(2, 142, 160, 0.3)' + shadowColor: getCSSVariable('--chart-info-shadow') } } }, @@ -299,7 +314,7 @@ const initConversationToolChart = () => { itemStyle: { color: getChartColor('primary'), shadowBlur: 10, - shadowColor: 'rgba(2, 142, 160, 0.3)' + shadowColor: getCSSVariable('--chart-info-shadow') } } } @@ -333,6 +348,15 @@ onMounted(() => { window.addEventListener('resize', handleResize) }) +// 监听主题变化,重新渲染图表 +watch(() => themeStore.isDark, () => { + if (props.agentStats && conversationToolChart) { + nextTick(() => { + updateCharts() + }) + } +}) + // 组件卸载时清理 const cleanup = () => { window.removeEventListener('resize', handleResize) @@ -350,6 +374,39 @@ defineExpose({ \ No newline at end of file diff --git a/web/src/components/dashboard/CallStatsComponent.vue b/web/src/components/dashboard/CallStatsComponent.vue index d0542977..1ae2dde6 100644 --- a/web/src/components/dashboard/CallStatsComponent.vue +++ b/web/src/components/dashboard/CallStatsComponent.vue @@ -43,11 +43,20 @@ import { ref, computed, onMounted, onUnmounted, nextTick, defineExpose, watch } import * as echarts from 'echarts' import { dashboardApi } from '@/apis/dashboard_api' import { getColorByIndex, truncateLegend } from '@/utils/chartColors' +import { useThemeStore } from '@/stores/theme' + +// CSS 变量解析工具函数 +function getCSSVariable(variableName, element = document.documentElement) { + return getComputedStyle(element).getPropertyValue(variableName).trim() +} const props = defineProps({ loading: { type: Boolean, default: false }, }) +// theme store +const themeStore = useThemeStore() + // state const callStatsData = ref(null) const callStatsLoading = ref(false) @@ -184,27 +193,27 @@ const renderCallStatsChart = () => { xAxis: { type: 'category', data: xAxisData, - axisLine: { lineStyle: { color: '#e5e7eb' } }, + axisLine: { lineStyle: { color: getCSSVariable('--gray-200') } }, axisTick: { show: false }, - axisLabel: { color: '#6b7280', fontSize: 12 } + axisLabel: { color: getCSSVariable('--gray-500'), fontSize: 12 } }, yAxis: { type: 'value', axisLine: { show: false }, axisTick: { show: false }, axisLabel: { - color: '#6b7280', + color: getCSSVariable('--gray-500'), fontSize: 12, formatter: (value) => (isTokenView.value ? formatTokenValue(value) : value), }, - splitLine: { lineStyle: { color: '#f3f4f6' } } + splitLine: { lineStyle: { color: getCSSVariable('--gray-100') } } }, tooltip: { trigger: 'axis', - backgroundColor: 'rgba(255, 255, 255, 0.95)', - borderColor: '#e5e7eb', + backgroundColor: getCSSVariable('--gray-0'), + borderColor: getCSSVariable('--gray-200'), borderWidth: 1, - textStyle: { color: '#374151', fontSize: 12 }, + textStyle: { color: getCSSVariable('--gray-600'), fontSize: 12 }, formatter: (params) => { if (!params?.length) return '' let total = 0 @@ -223,7 +232,7 @@ const renderCallStatsChart = () => { legend: { data: categories.map(cat => (cat === 'None' ? '未知模型' : cat)), bottom: 5, /* 调整图例位置,从0改为5 */ - textStyle: { color: '#6b7280', fontSize: 12 }, + textStyle: { color: getCSSVariable('--gray-500'), fontSize: 12 }, itemWidth: 14, itemHeight: 14, formatter: (name) => truncateLegend(name) @@ -278,6 +287,15 @@ watch(() => props.loading, (now) => { } }) +// 监听主题变化,重新渲染图表 +watch(() => themeStore.isDark, () => { + if (callStatsData.value && callStatsChart) { + nextTick(() => { + renderCallStatsChart() + }) + } +}) + onUnmounted(() => { cleanup() }) @@ -334,14 +352,14 @@ onUnmounted(() => { .simple-toggle-label { font-size: 12px; - color: #6b7280; + color: var(--gray-500); margin-right: 4px; } .simple-toggle { padding: 4px 8px; font-size: 12px; - color: #6b7280; + color: var(--gray-500); cursor: pointer; border-radius: 4px; transition: all 0.2s ease; @@ -349,20 +367,19 @@ onUnmounted(() => { } .simple-toggle:hover { - background-color: #f3f4f6; - color: #374151; + background-color: var(--gray-100); + color: var(--gray-700); } .simple-toggle.active { - background-color: #3996ae; - color: white; + background-color: var(--main-600); + color: var(--gray-0); } .divider { width: 1px; height: 16px; - background-color: #e5e7eb; + background-color: var(--gray-200); } - diff --git a/web/src/components/dashboard/FeedbackModalComponent.vue b/web/src/components/dashboard/FeedbackModalComponent.vue index 891f12f3..1441b242 100644 --- a/web/src/components/dashboard/FeedbackModalComponent.vue +++ b/web/src/components/dashboard/FeedbackModalComponent.vue @@ -182,23 +182,23 @@ watch(() => props.agentId, () => { } &::-webkit-scrollbar-track { - background: #f1f1f1; + background: var(--gray-100); border-radius: 3px; } &::-webkit-scrollbar-thumb { - background: #c1c1c1; + background: var(--gray-300); border-radius: 3px; &:hover { - background: #a8a8a8; + background: var(--gray-400); } } } // 反馈卡片 - 紧凑设计 .feedback-card { - background: white; + background: var(--gray-0); border: 1px solid var(--gray-100); border-radius: 8px; transition: all 0.2s ease; @@ -218,7 +218,7 @@ watch(() => props.agentId, () => { align-items: center; padding: 12px 16px; border-bottom: 1px solid var(--gray-100); - background: #fafafa; + background: var(--gray-25); border-radius: 8px 8px 0 0; } @@ -260,7 +260,7 @@ watch(() => props.agentId, () => { } .message-content { - background: #f8f9fa; + background: var(--gray-50); padding: 10px; border-radius: 6px; // border-left: 3px solid var(--main-color); @@ -304,10 +304,10 @@ watch(() => props.agentId, () => { } .reason-content { - background: #fff7e6; + background: var(--color-warning-light); padding: 10px; border-radius: 6px; - border-left: 3px solid #faad14; + border-left: 3px solid var(--color-warning); font-size: 13px; line-height: 1.4; color: var(--gray-800); @@ -318,7 +318,7 @@ watch(() => props.agentId, () => { .card-footer { padding: 8px 16px; border-top: 1px solid var(--gray-100); - background: #fafafa; + background: var(--gray-25); border-radius: 0 0 8px 8px; } diff --git a/web/src/components/dashboard/KnowledgeStatsComponent.vue b/web/src/components/dashboard/KnowledgeStatsComponent.vue index 2607303c..29ae681f 100644 --- a/web/src/components/dashboard/KnowledgeStatsComponent.vue +++ b/web/src/components/dashboard/KnowledgeStatsComponent.vue @@ -104,6 +104,15 @@ import { ref, onMounted, watch, nextTick, computed } from 'vue' import * as echarts from 'echarts' import { getColorByIndex, getChartColor, getColorPalette } from '@/utils/chartColors' +import { useThemeStore } from '@/stores/theme' + +// CSS 变量解析工具函数 +function getCSSVariable(variableName, element = document.documentElement) { + return getComputedStyle(element).getPropertyValue(variableName).trim() +} + +// theme store +const themeStore = useThemeStore() // Props const props = defineProps({ @@ -181,7 +190,7 @@ const initDbTypeChart = () => { data: [item.value], itemStyle: { color: getLegendColorByIndex(idx), - borderColor: '#ffffff', + borderColor: getCSSVariable('--gray-0'), borderWidth: 2, borderJoin: 'miter' }, @@ -196,10 +205,10 @@ const initDbTypeChart = () => { animation: false, tooltip: { trigger: 'item', - backgroundColor: 'rgba(255, 255, 255, 0.95)', - borderColor: '#e8e8e8', + backgroundColor: getCSSVariable('--gray-0'), + borderColor: getCSSVariable('--gray-200'), borderWidth: 1, - textStyle: { color: '#666' }, + textStyle: { color: getCSSVariable('--gray-600') }, formatter: (params) => { const value = params.value || 0 return `${params.seriesName}: ${value}/${total}` @@ -226,6 +235,12 @@ const initDbTypeChart = () => { const initFileTypeChart = () => { if (!fileTypeChartRef.value) return + // 如果已存在图表实例,先销毁 + if (fileTypeChart) { + fileTypeChart.dispose() + fileTypeChart = null + } + fileTypeChart = echarts.init(fileTypeChartRef.value) // 检查是否有文件类型数据 - 兼容旧字段名和新字段名 @@ -246,11 +261,11 @@ const initFileTypeChart = () => { const option = { tooltip: { trigger: 'item', - backgroundColor: 'rgba(255, 255, 255, 0.95)', - borderColor: '#e8e8e8', + backgroundColor: getCSSVariable('--gray-0'), + borderColor: getCSSVariable('--gray-200'), borderWidth: 1, textStyle: { - color: '#666' + color: getCSSVariable('--gray-600') }, formatter: '{a}
{b}: {c} ({d}%)' }, @@ -263,7 +278,7 @@ const initFileTypeChart = () => { itemHeight: 10, textStyle: { fontSize: 11, - color: '#666' + color: getCSSVariable('--gray-600') } }, series: [{ @@ -274,7 +289,7 @@ const initFileTypeChart = () => { avoidLabelOverlap: true, // 避免标签重叠 itemStyle: { borderRadius: 8, - borderColor: '#fff', + borderColor: getCSSVariable('--gray-0'), borderWidth: 2 }, label: { @@ -284,7 +299,7 @@ const initFileTypeChart = () => { itemStyle: { shadowBlur: 10, shadowOffsetX: 0, - shadowColor: 'rgba(0, 0, 0, 0.5)' + shadowColor: getCSSVariable('--shadow-3') } }, labelLine: { @@ -306,11 +321,11 @@ const initFileTypeChart = () => { const option = { tooltip: { trigger: 'item', - backgroundColor: 'rgba(255, 255, 255, 0.95)', - borderColor: '#e8e8e8', + backgroundColor: getCSSVariable('--gray-0'), + borderColor: getCSSVariable('--gray-200'), borderWidth: 1, textStyle: { - color: '#666' + color: getCSSVariable('--gray-600') }, formatter: '{a}
{b}: {c} ({d}%)' }, @@ -322,7 +337,7 @@ const initFileTypeChart = () => { avoidLabelOverlap: true, itemStyle: { borderRadius: 8, - borderColor: '#fff', + borderColor: getCSSVariable('--gray-0'), borderWidth: 2 }, label: { @@ -332,7 +347,7 @@ const initFileTypeChart = () => { itemStyle: { shadowBlur: 10, shadowOffsetX: 0, - shadowColor: 'rgba(0, 0, 0, 0.5)' + shadowColor: getCSSVariable('--shadow-3') } }, labelLine: { @@ -341,7 +356,7 @@ const initFileTypeChart = () => { data: [ { name: '暂无数据', value: 1 } ], - color: ['#e1f6fb'] + color: [getCSSVariable('--chart-info-lighter')] }] } @@ -394,6 +409,15 @@ onMounted(() => { window.addEventListener('resize', handleResize) }) +// 监听主题变化,重新渲染图表 +watch(() => themeStore.isDark, () => { + if (props.knowledgeStats && (dbTypeChart || fileTypeChart)) { + nextTick(() => { + updateCharts() + }) + } +}) + // 组件卸载时清理 const cleanup = () => { window.removeEventListener('resize', handleResize) @@ -437,7 +461,7 @@ defineExpose({ align-items: center; gap: 6px; font-size: 12px; - color: #666; + color: var(--gray-500); } .legend-color { @@ -486,21 +510,21 @@ defineExpose({ .carousel-label { font-size: 14px; font-weight: 600; - color: #666; + color: var(--gray-500); margin-bottom: 4px; } .carousel-value { font-size: 24px; font-weight: 700; - color: #333; + color: var(--gray-800); margin-bottom: 2px; line-height: 1; } .carousel-percent { font-size: 12px; - color: #999; + color: var(--gray-400); font-weight: 500; } } diff --git a/web/src/components/dashboard/StatsOverviewComponent.vue b/web/src/components/dashboard/StatsOverviewComponent.vue index 794e2a26..2fcfb179 100644 --- a/web/src/components/dashboard/StatsOverviewComponent.vue +++ b/web/src/components/dashboard/StatsOverviewComponent.vue @@ -92,4 +92,201 @@ const getSatisfactionClass = () => { diff --git a/web/src/components/dashboard/ToolStatsComponent.vue b/web/src/components/dashboard/ToolStatsComponent.vue index 813ba517..0970cd76 100644 --- a/web/src/components/dashboard/ToolStatsComponent.vue +++ b/web/src/components/dashboard/ToolStatsComponent.vue @@ -79,6 +79,15 @@ import { ref, onMounted, watch, nextTick, computed } from 'vue' import * as echarts from 'echarts' import { getColorByIndex, getChartColor, getColorPalette } from '@/utils/chartColors' +import { useThemeStore } from '@/stores/theme' + +// CSS 变量解析工具函数 +function getCSSVariable(variableName, element = document.documentElement) { + return getComputedStyle(element).getPropertyValue(variableName).trim() +} + +// theme store +const themeStore = useThemeStore() // Props const props = defineProps({ @@ -132,6 +141,12 @@ const errorData = computed(() => { const initToolsChart = () => { if (!toolsChartRef.value || !props.toolStats?.most_used_tools?.length) return + // 如果已存在图表实例,先销毁 + if (toolsChart) { + toolsChart.dispose() + toolsChart = null + } + toolsChart = echarts.init(toolsChartRef.value) const data = props.toolStats.most_used_tools.slice(0, 10) // 只显示前10个 @@ -142,11 +157,11 @@ const initToolsChart = () => { axisPointer: { type: 'shadow' }, - backgroundColor: 'rgba(255, 255, 255, 0.95)', - borderColor: '#e8e8e8', + backgroundColor: getCSSVariable('--gray-0'), + borderColor: getCSSVariable('--gray-200'), borderWidth: 1, textStyle: { - color: '#666' + color: getCSSVariable('--gray-600') } }, grid: { @@ -160,15 +175,15 @@ const initToolsChart = () => { type: 'value', axisLine: { lineStyle: { - color: '#e8e8e8' + color: getCSSVariable('--gray-200') } }, axisLabel: { - color: '#666' + color: getCSSVariable('--gray-500') }, splitLine: { lineStyle: { - color: '#f0f0f0' + color: getCSSVariable('--gray-150') } } }, @@ -177,11 +192,11 @@ const initToolsChart = () => { data: data.map(item => item.tool_name), axisLine: { lineStyle: { - color: '#e8e8e8' + color: getCSSVariable('--gray-200') } }, axisLabel: { - color: '#666', + color: getCSSVariable('--gray-500'), interval: 0 } }, @@ -197,7 +212,7 @@ const initToolsChart = () => { itemStyle: { color: getChartColor('primary'), shadowBlur: 10, - shadowColor: 'rgba(2, 142, 160, 0.3)' + shadowColor: getCSSVariable('--chart-info-shadow') } } }] @@ -210,6 +225,12 @@ const initToolsChart = () => { const initErrorChart = () => { if (!errorChartRef.value || !hasErrorData.value) return + // 如果已存在图表实例,先销毁 + if (errorChart) { + errorChart.dispose() + errorChart = null + } + errorChart = echarts.init(errorChartRef.value) const data = errorData.value.slice(0, 5) // 只显示前5个 @@ -217,11 +238,11 @@ const initErrorChart = () => { const option = { tooltip: { trigger: 'item', - backgroundColor: 'rgba(255, 255, 255, 0.95)', - borderColor: '#e8e8e8', + backgroundColor: getCSSVariable('--gray-0'), + borderColor: getCSSVariable('--gray-200'), borderWidth: 1, textStyle: { - color: '#666' + color: getCSSVariable('--gray-600') }, formatter: '{a}
{b}: {c} ({d}%)' }, @@ -236,7 +257,7 @@ const initErrorChart = () => { })), itemStyle: { borderRadius: 6, - borderColor: '#fff', + borderColor: getCSSVariable('--gray-0'), borderWidth: 2 }, label: { @@ -247,7 +268,7 @@ const initErrorChart = () => { itemStyle: { shadowBlur: 10, shadowOffsetX: 0, - shadowColor: 'rgba(0, 0, 0, 0.5)' + shadowColor: getCSSVariable('--shadow-300') } }, color: getColorPalette() @@ -283,6 +304,15 @@ onMounted(() => { window.addEventListener('resize', handleResize) }) +// 监听主题变化,重新渲染图表 +watch(() => themeStore.isDark, () => { + if (props.toolStats && (toolsChart || errorChart)) { + nextTick(() => { + updateCharts() + }) + } +}) + // 组件卸载时清理 const cleanup = () => { window.removeEventListener('resize', handleResize) diff --git a/web/src/components/dashboard/UserStatsComponent.vue b/web/src/components/dashboard/UserStatsComponent.vue index 58ec0129..5eef0673 100644 --- a/web/src/components/dashboard/UserStatsComponent.vue +++ b/web/src/components/dashboard/UserStatsComponent.vue @@ -32,6 +32,15 @@ import { ref, onMounted, watch, nextTick } from 'vue' import * as echarts from 'echarts' import { getChartColor } from '@/utils/chartColors' import { dashboardApi } from '@/apis/dashboard_api' +import { useThemeStore } from '@/stores/theme' + +// CSS 变量解析工具函数 +function getCSSVariable(variableName, element = document.documentElement) { + return getComputedStyle(element).getPropertyValue(variableName).trim() +} + +// theme store +const themeStore = useThemeStore() // Props const props = defineProps({ @@ -53,16 +62,22 @@ let activityChart = null const initActivityChart = () => { if (!activityChartRef.value || !props.userStats?.daily_active_users) return + // 如果已存在图表实例,先销毁 + if (activityChart) { + activityChart.dispose() + activityChart = null + } + activityChart = echarts.init(activityChartRef.value) const option = { tooltip: { trigger: 'axis', - backgroundColor: 'rgba(255, 255, 255, 0.95)', - borderColor: '#e8e8e8', + backgroundColor: getCSSVariable('--gray-0'), + borderColor: getCSSVariable('--gray-200'), borderWidth: 1, textStyle: { - color: '#666' + color: getCSSVariable('--gray-600') } }, grid: { @@ -77,26 +92,26 @@ const initActivityChart = () => { data: props.userStats.daily_active_users.map(item => item.date), axisLine: { lineStyle: { - color: '#e8e8e8' + color: getCSSVariable('--gray-200') } }, axisLabel: { - color: '#666' + color: getCSSVariable('--gray-500') } }, yAxis: { type: 'value', axisLine: { lineStyle: { - color: '#e8e8e8' + color: getCSSVariable('--gray-200') } }, axisLabel: { - color: '#666' + color: getCSSVariable('--gray-500') }, splitLine: { lineStyle: { - color: '#f0f0f0' + color: getCSSVariable('--gray-100') } } }, @@ -117,24 +132,24 @@ const initActivityChart = () => { x2: 0, y2: 1, colorStops: [{ - offset: 0, color: 'rgba(57, 150, 174, 0.3)' + offset: 0, color: getCSSVariable('--chart-primary-light') }, { - offset: 1, color: 'rgba(57, 150, 174, 0.05)' + offset: 1, color: getCSSVariable('--chart-primary-lighter') }] } }, itemStyle: { color: getChartColor('primary'), borderWidth: 2, - borderColor: '#fff' + borderColor: getCSSVariable('--gray-0') }, emphasis: { itemStyle: { color: getChartColor('primary'), borderWidth: 3, - borderColor: '#fff', + borderColor: getCSSVariable('--gray-0'), shadowBlur: 10, - shadowColor: 'rgba(57, 150, 174, 0.5)' + shadowColor: getCSSVariable('--chart-primary-shadow') } } }] @@ -166,6 +181,15 @@ onMounted(() => { window.addEventListener('resize', handleResize) }) +// 监听主题变化,重新渲染图表 +watch(() => themeStore.isDark, () => { + if (props.userStats?.daily_active_users && activityChart) { + nextTick(() => { + initActivityChart() + }) + } +}) + // 组件卸载时清理 const cleanup = () => { window.removeEventListener('resize', handleResize) @@ -183,12 +207,132 @@ defineExpose({ \ No newline at end of file diff --git a/web/src/layouts/AppLayout.vue b/web/src/layouts/AppLayout.vue index 22358cf2..fb137001 100644 --- a/web/src/layouts/AppLayout.vue +++ b/web/src/layouts/AppLayout.vue @@ -3,9 +3,8 @@ import { ref, reactive, onMounted, useTemplateRef, computed } from 'vue' import { RouterLink, RouterView, useRoute } from 'vue-router' import { GithubOutlined, - ExclamationCircleOutlined, } from '@ant-design/icons-vue' -import { Bot, Waypoints, LibraryBig, Settings, BarChart3, BookOpen, CircleCheck } from 'lucide-vue-next'; +import { Bot, Waypoints, LibraryBig, Settings, BarChart3, CircleCheck } from 'lucide-vue-next'; import { onLongPress } from '@vueuse/core' import { useConfigStore } from '@/stores/config' @@ -169,31 +168,20 @@ const mainList = [{ - + {{ (githubStars / 1000).toFixed(1) }}k
- - @@ -323,7 +311,7 @@ div.header, #app-router-view { text-decoration: none; font-size: 24px; font-weight: bold; - color: #333; + color: var(--gray-900); } } @@ -337,9 +325,9 @@ div.header, #app-router-view { border: 1px solid transparent; border-radius: 12px; background-color: transparent; - color: #222; + color: var(--gray-1000); font-size: 20px; - transition: background-color 0.2s ease-in-out; + transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out; margin: 0; text-decoration: none; cursor: pointer; @@ -353,13 +341,13 @@ div.header, #app-router-view { } &.active { - background-color: var(--gray-150); + background-color: var(--gray-100); font-weight: bold; color: var(--main-color); } &.warning { - color: red; + color: var(--color-error); } &:hover { @@ -388,7 +376,7 @@ div.header, #app-router-view { margin-top: 4px; .star-icon { - color: #f0a742; + color: var(--color-warning); font-size: 12px; margin-right: 2px; } @@ -402,16 +390,7 @@ div.header, #app-router-view { &.api-docs { padding: 10px 12px; } - &.docs { - padding: 10px 12px; - margin-bottom: 12px; - - .docs-link { - display: flex; - align-items: center; - color: inherit; - } - } + &.docs { display: none; } &.task-center { .task-center-badge { width: 100%; @@ -420,6 +399,23 @@ div.header, #app-router-view { } } + &.theme-toggle-nav { + .theme-toggle-icon { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + cursor: pointer; + color: var(--gray-1000); + transition: color 0.2s ease-in-out; + + &:hover { + color: var(--main-color); + } + } + } + &.setting { margin: 8px 0; @@ -465,7 +461,7 @@ div.header, #app-router-view { transition: color 0.1s ease-in-out, font-size 0.1s ease-in-out; &.active { - color: black; + color: var(--gray-10000); font-size: 1.1rem; } } @@ -566,12 +562,35 @@ div.header, #app-router-view { margin-left: 6px; .star-icon { - color: #f0a742; + color: var(--color-warning); font-size: 14px; margin-right: 2px; } } } + + &.theme-toggle-nav { + padding: 8px 12px; + + .theme-toggle-icon { + display: flex; + align-items: center; + justify-content: center; + color: var(--gray-1000); + transition: color 0.2s ease-in-out; + cursor: pointer; + + &:hover { + color: var(--main-color); + } + } + + &.active { + .theme-toggle-icon { + color: var(--main-color); + } + } + } } } diff --git a/web/src/stores/theme.js b/web/src/stores/theme.js new file mode 100644 index 00000000..ea798211 --- /dev/null +++ b/web/src/stores/theme.js @@ -0,0 +1,64 @@ +import { ref, watch } from 'vue' +import { defineStore } from 'pinia' +import { theme } from 'ant-design-vue' + +export const useThemeStore = defineStore('theme', () => { + // 从 localStorage 读取保存的主题,默认为浅色 + const isDark = ref(localStorage.getItem('theme') === 'dark') + + // 公共主题配置 + const commonTheme = { + token: { + fontFamily: "'HarmonyOS Sans SC', Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;", + colorPrimary: '#1c7796', + borderRadius: 8, + wireframe: false, + }, + } + + // 浅色主题配置 + const lightTheme = { + ...commonTheme, + } + + // 深色主题配置 + const darkTheme = { + ...commonTheme, + algorithm: theme.darkAlgorithm, + } + + // 当前主题配置 + const currentTheme = ref(isDark.value ? darkTheme : lightTheme) + + // 切换主题 + function toggleTheme() { + setTheme(!isDark.value) + } + + // 设置主题 + function setTheme(dark) { + isDark.value = dark + currentTheme.value = dark ? darkTheme : lightTheme + localStorage.setItem('theme', dark ? 'dark' : 'light') + updateDocumentTheme() + } + + // 更新 document 的主题类 + function updateDocumentTheme() { + if (isDark.value) { + document.documentElement.classList.add('dark') + } else { + document.documentElement.classList.remove('dark') + } + } + + // 初始化时设置主题 + updateDocumentTheme() + + return { + isDark, + currentTheme, + toggleTheme, + setTheme, + } +}) diff --git a/web/src/views/AgentSingleView.vue b/web/src/views/AgentSingleView.vue index 221de886..f048335b 100644 --- a/web/src/views/AgentSingleView.vue +++ b/web/src/views/AgentSingleView.vue @@ -80,7 +80,7 @@ const handleShareChat = async () => { top: 0; height: 100%; width: 240px; - background-color: #f5f5f5; + background-color: var(--gray-50); transition: all 0.3s ease; z-index: 20; display: flex; @@ -113,7 +113,7 @@ const handleShareChat = async () => { transform: translateY(-50%); width: 30px; height: 30px; - background-color: #fff; + background-color: var(--gray-0); border-radius: 50%; display: flex; align-items: center; diff --git a/web/src/views/AgentView.vue b/web/src/views/AgentView.vue index 786ec6cc..dcc896ee 100644 --- a/web/src/views/AgentView.vue +++ b/web/src/views/AgentView.vue @@ -49,7 +49,7 @@ 配置 -
+
@@ -198,6 +198,7 @@ const toggleConf = () => { // 更多菜单相关 const moreMenuRef = ref(null); +const moreButtonRef = ref(null); const toggleMoreMenu = (event) => { event.stopPropagation(); @@ -220,7 +221,7 @@ onClickOutside(moreMenuRef, () => { if (chatUIStore.moreMenuOpen) { closeMoreMenu(); } -}); +}, { ignore: [moreButtonRef] }); const handleShareChat = async () => { closeMoreMenu(); @@ -387,7 +388,7 @@ const handlePreview = () => { } .action-button { - background-color: white; + background-color: var(--gray-0); border: 1px solid var(--main-20); text-align: left; height: auto; @@ -436,7 +437,7 @@ const handlePreview = () => { } .default-icon { - color: #faad14; + color: var(--color-warning); font-size: 14px; margin-left: 4px; } @@ -469,7 +470,7 @@ const handlePreview = () => { .select-tools-btn { background: var(--main-color); border: none; - color: #fff; + color: var(--gray-0); border-radius: 6px; padding: 4px 12px; font-size: 13px; @@ -525,7 +526,7 @@ const handlePreview = () => { overflow: hidden; } :deep(.ant-modal-header) { - background: #fff; + background: var(--gray-0); border-bottom: 1px solid var(--gray-200); padding: 16px 20px; .ant-modal-title { @@ -536,7 +537,7 @@ const handlePreview = () => { } :deep(.ant-modal-body) { padding: 20px; - background: #fff; + background: var(--gray-0); } .tools-modal-content { .tools-search { @@ -558,7 +559,7 @@ const handlePreview = () => { border: 1px solid var(--gray-200); border-radius: 8px; margin-bottom: 16px; - background: #fff; + background: var(--gray-0); .tool-item { padding: 14px 16px; border-bottom: 1px solid var(--gray-100); @@ -626,7 +627,7 @@ const handlePreview = () => { &.ant-btn-default { border: 1px solid var(--gray-300); color: var(--gray-900); - background: #fff; + background: var(--gray-0); &:hover { border-color: var(--main-color); color: var(--main-color); @@ -636,7 +637,7 @@ const handlePreview = () => { &.ant-btn-primary { background: var(--main-color); border: none; - color: #fff; + color: var(--gray-0); &:hover { background: var(--main-color); } @@ -671,7 +672,7 @@ const handlePreview = () => { padding: 8px 12px; cursor: pointer; transition: all 0.2s ease; - background: white; + background: var(--gray-0); user-select: none; &:hover { @@ -737,12 +738,13 @@ const handlePreview = () => { } } + // 智能体选择器样式 .agent-selector { border: 1px solid var(--gray-300); border-radius: 8px; padding: 8px 12px; - background: white; + background: var(--gray-0); transition: border-color 0.2s ease; &:hover { @@ -761,7 +763,7 @@ const handlePreview = () => { } .default-icon { - color: #faad14; + color: var(--color-warning); font-size: 14px; } } @@ -775,7 +777,7 @@ const handlePreview = () => { } :deep(.ant-modal-header) { - background: #fff; + background: var(--gray-0); border-bottom: 1px solid var(--gray-200); padding: 16px 20px; @@ -788,7 +790,7 @@ const handlePreview = () => { :deep(.ant-modal-body) { padding: 20px; - background: #fff; + background: var(--gray-0); } .agent-modal-content { @@ -806,7 +808,7 @@ const handlePreview = () => { padding: 16px; cursor: pointer; transition: border-color 0.2s ease; - background: white; + background: var(--gray-0); &:hover { border-color: var(--main-color); @@ -832,7 +834,7 @@ const handlePreview = () => { } .default-icon { - color: #faad14; + color: var(--color-warning); font-size: 16px; flex-shrink: 0; } @@ -884,7 +886,7 @@ const handlePreview = () => { .more-popup-menu { position: fixed; min-width: 130px; - background: white; + background: var(--gray-0); border-radius: 10px; box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08), 0 2px 8px rgba(0, 0, 0, 0.04); border: 1px solid var(--gray-100); @@ -937,6 +939,7 @@ const handlePreview = () => { } } + // 菜单淡入淡出动画 .menu-fade-enter-active { animation: menuSlideIn 0.2s cubic-bezier(0.16, 1, 0.3, 1); diff --git a/web/src/views/DashboardView.vue b/web/src/views/DashboardView.vue index 9c852424..e9c98c59 100644 --- a/web/src/views/DashboardView.vue +++ b/web/src/views/DashboardView.vue @@ -384,7 +384,7 @@ onUnmounted(() => { .conversations-section, .call-stats-section { border-color: var(--gray-200); - box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1); + box-shadow: 0 1px 3px 0 var(--shadow-100); } } @@ -439,7 +439,7 @@ onUnmounted(() => { &:hover { background-color: var(--gray-25); border-color: var(--gray-200); - box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1); + box-shadow: 0 1px 3px 0 var(--shadow-100); } :deep(.ant-card-head) { @@ -537,8 +537,8 @@ onUnmounted(() => { margin-bottom: 24px; .summary-card { - background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%); - border: 1px solid #e2e8f0; + background: linear-gradient(135deg, var(--gray-50) 0%, var(--gray-100) 100%); + border: 1px solid var(--gray-200); border-radius: 8px; padding: 12px; text-align: center; @@ -546,13 +546,13 @@ onUnmounted(() => { .summary-value { font-size: 16px; font-weight: 600; - color: #1e293b; + color: var(--gray-800); margin-bottom: 4px; } .summary-label { font-size: 11px; - color: #64748b; + color: var(--gray-500); font-weight: 500; } } diff --git a/web/src/views/DataBaseInfoView.vue b/web/src/views/DataBaseInfoView.vue index 957ccd14..51fee778 100644 --- a/web/src/views/DataBaseInfoView.vue +++ b/web/src/views/DataBaseInfoView.vue @@ -155,7 +155,7 @@ const resetFileSelectionState = () => { watch(() => route.params.database_id, async (newId, oldId) => { // 切换知识库时,标记为初次加载 isInitialLoad.value = true; - + store.databaseId = newId; resetFileSelectionState(); resetGraphStats(); @@ -173,22 +173,22 @@ watch( () => database.value?.files, (newFiles, oldFiles) => { if (!newFiles) return; - + const newFileCount = Object.keys(newFiles).length; const oldFileCount = previousFileCount.value; - + // 首次加载时,只更新计数,不触发任何操作 if (isInitialLoad.value) { previousFileCount.value = newFileCount; isInitialLoad.value = false; return; } - + // 如果文件数量发生变化(增加或减少),都重新生成问题和思维导图 if (newFileCount !== oldFileCount) { const changeType = newFileCount > oldFileCount ? '增加' : '减少'; console.log(`文件数量从 ${oldFileCount} ${changeType}到 ${newFileCount},准备重新生成问题和思维导图`); - + // 只要有文件,就重新生成思维导图(无论增加还是减少) if (newFileCount > 0) { setTimeout(() => { @@ -208,7 +208,7 @@ watch( // 如果文件数量变为0,清空思维导图(如果需要的话) console.log('文件数量为0,思维导图将自动清空'); } - + // 只要有文件,就重新生成问题(无论之前是否有问题) if (newFileCount > 0) { setTimeout(async () => { @@ -238,7 +238,7 @@ watch( }, 1000); } } - + previousFileCount.value = newFileCount; }, { deep: true } @@ -332,11 +332,11 @@ const handleMouseUp = () => { .unified-layout { display: flex; height: 100vh; + background-color: var(--gray-0); gap: 0; .left-panel, .right-panel { - background-color: #fff; display: flex; flex-direction: column; overflow: hidden; @@ -347,7 +347,6 @@ const handleMouseUp = () => { display: flex; flex-shrink: 0; flex-grow: 1; - background-color: var(--gray-0); padding-right: 0; // max-height: calc(100% - 16px); } @@ -381,6 +380,9 @@ const handleMouseUp = () => { flex-direction: column; border: 1px solid var(--gray-200); border-radius: 12px; + background: var(--gray-10); + overflow: hidden; + :deep(.ant-tabs-content) { flex: 1; @@ -395,15 +397,11 @@ const handleMouseUp = () => { :deep(.ant-tabs-nav) { margin-bottom: 0; - // background-color: #fff; + // background-color: var(--gray-0); border-bottom: 1px solid var(--gray-200); } } -/* Simplify resize handle */ -.resize-handle { - opacity: 0.8; -} /* Responsive design for smaller screens */ @media (max-width: 768px) { @@ -484,8 +482,8 @@ const handleMouseUp = () => { justify-content: space-between; align-items: center; padding: 8px 12px; - border-bottom: 1px solid #f0f0f0; - background-color: #fafafa; + border-bottom: 1px solid var(--gray-150); + background-color: var(--gray-25); .header-left { display: flex; diff --git a/web/src/views/DataBaseView.vue b/web/src/views/DataBaseView.vue index dd7ffdd8..bd343ce5 100644 --- a/web/src/views/DataBaseView.vue +++ b/web/src/views/DataBaseView.vue @@ -604,7 +604,7 @@ onMounted(() => { border-radius: 12px; padding: 16px; margin-top: 16px; - background: #fafafa; + background: var(--gray-25); .reranker-row { display: flex; @@ -670,12 +670,12 @@ onMounted(() => { } .kb-type-card { - border: 2px solid #f0f0f0; + border: 2px solid var(--gray-150); border-radius: 12px; padding: 20px; cursor: pointer; transition: all 0.3s ease; - background: white; + background: var(--gray-0); position: relative; overflow: hidden; @@ -685,20 +685,20 @@ onMounted(() => { // 为不同知识库类型设置不同的悬停颜色 &:nth-child(1):hover { - border-color: #d3adf7; + border-color: var(--chart-secondary-light); } &:nth-child(2):hover { - border-color: #ffd591; + border-color: var(--chart-warning-light); } &:nth-child(3):hover { - border-color: #ffadd2; + border-color: var(--chart-error-light); } &.active { border-color: var(--main-color); - background: #f8faff; + background: rgba(24, 144, 255, 0.05); .type-icon { color: var(--main-color); @@ -713,48 +713,48 @@ onMounted(() => { // 为不同知识库类型设置不同的主题色 &:nth-child(1) { &.active { - border-color: #d3adf7; - background: #f9f0ff; + border-color: var(--chart-secondary-light); + background: rgba(114, 46, 209, 0.05); .type-icon { - color: #722ed1; + color: var(--chart-secondary); } .feature-tag { background: rgba(114, 46, 209, 0.1); - color: #722ed1; + color: var(--chart-secondary); } } } &:nth-child(2) { &.active { - border-color: #ffd591; - background: #fff7e6; + border-color: var(--chart-warning-light); + background: rgba(250, 140, 22, 0.05); .type-icon { - color: #fa8c16; + color: var(--chart-warning); } .feature-tag { background: rgba(250, 140, 22, 0.1); - color: #fa8c16; + color: var(--chart-warning); } } } &:nth-child(3) { &.active { - border-color: #ffadd2; - background: #fff1f0; + border-color: var(--chart-error-light); + background: rgba(245, 34, 45, 0.05); .type-icon { - color: #f5222d; + color: var(--chart-error); } .feature-tag { background: rgba(245, 34, 45, 0.1); - color: #f5222d; + color: var(--chart-error); } } } @@ -791,7 +791,7 @@ onMounted(() => { .feature-tag { display: inline-block; padding: 4px 8px; - background: rgba(24, 144, 255, 0.1); + background: rgba(24, 144, 255, 0.08); color: var(--main-color); border-radius: 6px; font-size: 12px; @@ -804,9 +804,9 @@ onMounted(() => { .chunk-config { margin-top: 16px; padding: 12px 16px; - background-color: #fafafa; + background-color: var(--gray-25); border-radius: 6px; - border: 1px solid #f0f0f0; + border: 1px solid var(--gray-150); h3 { margin-top: 0; @@ -871,11 +871,9 @@ onMounted(() => { } .database, .graphbase { - background: linear-gradient(145deg, #ffffff 0%, #fafbfc 100%); - box-shadow: - 0px 1px 2px 0px rgba(16,24,40,.06), - inset 0px 1px 0px 0px rgba(255,255,255,.6); - border: 1px solid rgba(233, 236, 239, 0.6); + background: linear-gradient(145deg, var(--gray-0) 0%, var(--gray-10) 100%); + box-shadow: 0px 1px 2px 0px var(--shadow-2); + border: 1px solid var(--gray-100); transition: none; position: relative; } @@ -896,16 +894,16 @@ onMounted(() => { top: 20px; right: 20px; color: var(--gray-600); - background: linear-gradient(135deg, rgba(255,255,255,0.9) 0%, rgba(248,250,252,0.9) 100%); + background: linear-gradient(135deg, var(--gray-0) 0%, var(--gray-100) 100%); font-size: 12px; border-radius: 8px; padding: 6px; z-index: 2; - box-shadow: 0px 2px 4px rgba(0,0,0,0.1); - border: 1px solid rgba(229, 231, 235, 0.5); + box-shadow: 0px 2px 4px var(--shadow-2); + border: 1px solid var(--gray-100); } - + .top { display: flex; align-items: center; @@ -922,7 +920,7 @@ onMounted(() => { align-items: center; background: var(--main-30); border-radius: 12px; - border: 1px solid rgba(1, 97, 121, 0.05); + border: 1px solid var(--gray-200); color: var(--main-color); position: relative; } @@ -930,7 +928,7 @@ onMounted(() => { .info { h3, p { margin: 0; - color: black; + color: var(--gray-10000); } h3 { diff --git a/web/src/views/GraphView.vue b/web/src/views/GraphView.vue index 568d9bcc..56a1d006 100644 --- a/web/src/views/GraphView.vue +++ b/web/src/views/GraphView.vue @@ -330,11 +330,6 @@ const indexNodes = () => { }); }; -const getAuthHeaders = () => { - const userStore = useUserStore(); - return userStore.getAuthHeaders(); -}; - const exportGraphData = () => { const dataStr = JSON.stringify({ nodes: graphData.nodes, @@ -356,9 +351,9 @@ const exportGraphData = () => { message.success('图谱数据已导出'); }; -const showQueryHistory = () => { - // 这里可以实现查询历史功能 - message.info('查询历史功能开发中...'); +const getAuthHeaders = () => { + const userStore = useUserStore(); + return userStore.getAuthHeaders(); }; const openLink = (url) => { @@ -372,6 +367,7 @@ const openLink = (url) => { .graph-container { padding: 0; + background-color: var(--gray-0); .header-container { height: @graph-header-height; @@ -383,7 +379,7 @@ const openLink = (url) => { align-items: center; margin-right: 16px; font-size: 14px; - color: rgba(0, 0, 0, 0.65); + color: var(--color-text-secondary); } .status-text { @@ -397,16 +393,16 @@ const openLink = (url) => { display: inline-block; &.loading { - background-color: #faad14; + background-color: var(--color-warning); animation: pulse 1.5s infinite ease-in-out; } &.open { - background-color: #52c41a; + background-color: var(--color-success); } &.closed { - background-color: #f5222d; + background-color: var(--color-error); } } @@ -448,6 +444,11 @@ const openLink = (url) => { padding: 0 24px; width: 100%; } + + .tags { + display: flex; + gap: 8px; + } } .actions { diff --git a/web/src/views/HomeView.vue b/web/src/views/HomeView.vue index 9f090f4b..a72b4121 100644 --- a/web/src/views/HomeView.vue +++ b/web/src/views/HomeView.vue @@ -232,7 +232,7 @@ const actionLinks = computed(() => { align-items: center; width: 100%; padding: 0.75rem 2.5rem; - background-color: rgba(255, 255, 255, 0.85); + background-color: var(--color-trans-light); backdrop-filter: blur(20px); // border-bottom: 1px solid var(--main-30); position: fixed; @@ -255,7 +255,7 @@ const actionLinks = computed(() => { gap: 0.5rem; padding: 0.6rem 1rem; text-decoration: none; - color: #555; + color: var(--gray-800); font-weight: 500; font-size: 0.95rem; transition: color 0.2s ease; @@ -263,7 +263,9 @@ const actionLinks = computed(() => { overflow: hidden; &:hover { - color: #333; + color: var(--gray-900); + + svg { transform: scale(1.1); @@ -272,7 +274,7 @@ const actionLinks = computed(() => { &.router-link-active { background: linear-gradient(135deg, var(--main-600), var(--main-500)); - color: white; + color: var(--gray-0); border-radius: 1.5rem; &:hover { @@ -313,14 +315,14 @@ const actionLinks = computed(() => { display: flex; align-items: center; text-decoration: none; - color: #555; + color: var(--gray-600); padding: 0.6rem 1rem; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); font-size: 0.9rem; font-weight: 500; &:hover { - color: #333; + color: var(--gray-700); svg { transform: scale(1.1); @@ -418,7 +420,7 @@ const actionLinks = computed(() => { .button-base.primary { background: linear-gradient(135deg, var(--main-600), var(--main-500)); - color: #fff; + color: var(--gray-0); border-color: transparent; &:hover { @@ -623,7 +625,7 @@ const actionLinks = computed(() => { transition: opacity 0.3s ease; .overlay-content { - color: white; + color: var(--gray-0); h3 { font-size: 1.5rem; diff --git a/web/src/views/LoginView.vue b/web/src/views/LoginView.vue index 1096ef08..71e12a5c 100644 --- a/web/src/views/LoginView.vue +++ b/web/src/views/LoginView.vue @@ -24,16 +24,6 @@ @@ -574,62 +564,6 @@ onUnmounted(() => { object-fit: cover; object-position: center; } - - .image-overlay { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(0, 0, 0, 0.35); - display: flex; - flex-direction: column; - justify-content: space-between; - padding: 72px 64px 36px; - } - - .brand-info { - text-align: left; - color: white; - max-width: 520px; - - .brand-title { - font-size: 52px; - font-weight: 700; - margin-bottom: 20px; - text-shadow: 0 3px 6px rgba(0, 0, 0, 0.35); - letter-spacing: -0.5px; - } - - .brand-subtitle { - font-size: 24px; - font-weight: 500; - margin-bottom: 24px; - opacity: 0.92; - text-shadow: 0 2px 4px rgba(0, 0, 0, 0.28); - line-height: 1.4; - } - - .brand-description { - font-size: 18px; - line-height: 1.6; - margin: 0; - opacity: 0.82; - text-shadow: 0 1px 3px rgba(0, 0, 0, 0.28); - } - } - - .brand-copyright { - align-self: flex-start; - - p { - margin: 0; - font-size: 14px; - color: rgba(255, 255, 255, 0.7); - text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5); - font-weight: 400; - } - } } .login-form-section { @@ -649,7 +583,7 @@ onUnmounted(() => { background: var(--gray-0); border-radius: 24px; border: 1px solid var(--gray-150); - box-shadow: 0 18px 36px rgba(66, 66, 66, 0.05); + box-shadow: 0 18px 36px var(--shadow-1); display: flex; flex-direction: column; gap: 32px; @@ -783,7 +717,7 @@ onUnmounted(() => { margin-top: 16px; padding: 10px 12px; background-color: var(--stats-error-bg); - border: 1px solid rgba(220, 38, 38, 0.25); + border: 1px solid color-mix(in srgb, var(--color-error) 25%, transparent); border-radius: 8px; color: var(--stats-error-color); font-size: 14px; @@ -874,10 +808,10 @@ onUnmounted(() => { left: 0; right: 0; padding: 12px 20px; - background: linear-gradient(135deg, #ff4d4f, #ff7875); - color: white; + background: linear-gradient(135deg, var(--color-error), var(--chart-error-light)); + color: var(--gray-0); z-index: 1000; - box-shadow: 0 2px 8px rgba(255, 77, 79, 0.3); + box-shadow: 0 2px 8px color-mix(in srgb, var(--color-error) 30%, transparent); .alert-content { display: flex; @@ -888,7 +822,7 @@ onUnmounted(() => { .alert-icon { font-size: 20px; margin-right: 12px; - color: white; + color: var(--gray-0); } .alert-text { @@ -907,12 +841,12 @@ onUnmounted(() => { } :deep(.ant-btn-link) { - color: white; - border-color: white; + color: var(--gray-0); + border-color: var(--gray-0); &:hover { - color: white; - background-color: rgba(255, 255, 255, 0.1); + color: var(--gray-0); + background-color: color-mix(in srgb, var(--gray-0) 10%, transparent); } } } diff --git a/web/src/views/SettingView.vue b/web/src/views/SettingView.vue index b2663d41..d675cb47 100644 --- a/web/src/views/SettingView.vue +++ b/web/src/views/SettingView.vue @@ -292,11 +292,11 @@ const getModelStatusIcon = (modelId) => { // 获取模型状态颜色 const getModelStatusColor = (modelId) => { const status = state.modelStatuses[modelId] - if (!status) return '#999' - if (status.status === 'available') return '#52c41a' - if (status.status === 'unavailable') return '#ff4d4f' - if (status.status === 'error') return '#faad14' - return '#999' + if (!status) return 'var(--gray-500)' + if (status.status === 'available') return 'var(--color-success)' + if (status.status === 'unavailable') return 'var(--color-error)' + if (status.status === 'error') return 'var(--color-warning)' + return 'var(--gray-500)' } // 获取模型状态提示文本 const getModelStatusTooltip = (modelId) => { @@ -446,7 +446,7 @@ const openLink = (url) => { padding: 12px 16px; border: 1px solid var(--gray-150); border-radius: 8px; - background: white; + background: var(--gray-0); transition: all 0.2s; min-height: 60px;