Compare commits
No commits in common. "main" and "feature/project_dedtail" have entirely different histories.
main
...
feature/pr
|
@ -63,9 +63,7 @@ export default defineConfig([
|
|||
rules: {
|
||||
'vue/multi-word-component-names': 'off', // 允许单字组件名
|
||||
'@typescript-eslint/no-explicit-any': 'warn',
|
||||
'@typescript-eslint/no-unused-vars': 'off', // 关闭未使用变量检查
|
||||
'@typescript-eslint/no-unused-vars-experimental': 'off', // 关闭实验性检查
|
||||
'vue/no-unused-vars': 'off', // 关闭Vue未使用变量检查
|
||||
'@typescript-eslint/no-unused-vars': 'off', // 修改此处,允许声明变量未使用
|
||||
'vue/no-v-model-argument': 'off', // 允许v-model参数
|
||||
'vue/no-async-in-computed-properties': 'off', // 禁用有问题的规则
|
||||
'vue/no-child-content': 'off'// 禁用另一个有问题的规则
|
||||
|
@ -74,6 +72,6 @@ export default defineConfig([
|
|||
|
||||
// 忽略node_modules和dist目录
|
||||
{
|
||||
ignores: ['node_modules/**', 'dist/**', 'public/**', , 'src/components/chat/ChatAi.ts', 'src/components/chat/MockSSEResponse.ts', 'src/components/chat/ChatAi.vue', 'src/components/chat/ChatAi.tsx', 'src/components/chat/ChatAi.jsx', 'src/components/chat/ChatAi.js', 'src/components/chat/**', 'src/components/chat/ChatAi.js']
|
||||
ignores: ['node_modules/**', 'dist/**', 'public/**']
|
||||
}
|
||||
]);
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/images/logo.png" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<!--<link rel="preload" as="image" href="/src/assets/images/hero-bg.jpg" fetchpriority="high">-->
|
||||
<link rel="preload" as="image" href="images/logo.png" fetchpriority="high">
|
||||
<link rel="preload" as="image" href="images/hero-bg.jpg" fetchpriority="high">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>优阅工作室</title>
|
||||
<title>Vite + Vue + TS</title>
|
||||
|
||||
|
||||
</head>
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
"name": "company_website",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@tdesign-vue-next/chat": "^0.4.5",
|
||||
"@types/node": "^24.2.1",
|
||||
"@vue/language-core": "^3.0.5",
|
||||
"axios": "^1.11.0",
|
||||
|
@ -21,8 +20,6 @@
|
|||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^6.0.1",
|
||||
"@vue/tsconfig": "^0.7.0",
|
||||
"less": "^4.4.1",
|
||||
"less-loader": "^12.3.0",
|
||||
"typescript": "~5.8.3",
|
||||
"vite": "^7.1.2",
|
||||
"vue-tsc": "^3.0.5"
|
||||
|
@ -818,25 +815,6 @@
|
|||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@tdesign-vue-next/chat": {
|
||||
"version": "0.4.5",
|
||||
"resolved": "https://registry.npmmirror.com/@tdesign-vue-next/chat/-/chat-0.4.5.tgz",
|
||||
"integrity": "sha512-oM+NaVS2+QAC3tvlKCIm92dYvJVTzjPZip48WicCoWK42XyjKDau+hJiEb8TRXTMOw3kAkK7zrO79N0tWmBQnQ==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.22.6",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"clipboard": "^2.0.11",
|
||||
"highlight.js": "^11.9.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"marked": "^12.0.1",
|
||||
"marked-highlight": "^2.1.1",
|
||||
"tdesign-icons-vue-next": "^0.3.6",
|
||||
"tdesign-vue-next": "^1.15.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": ">=3.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz",
|
||||
|
@ -1130,16 +1108,6 @@
|
|||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/clipboard": {
|
||||
"version": "2.0.11",
|
||||
"resolved": "https://registry.npmmirror.com/clipboard/-/clipboard-2.0.11.tgz",
|
||||
"integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==",
|
||||
"dependencies": {
|
||||
"good-listener": "^1.2.2",
|
||||
"select": "^1.1.2",
|
||||
"tiny-emitter": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
|
@ -1188,11 +1156,6 @@
|
|||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/delegate": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/delegate/-/delegate-3.2.0.tgz",
|
||||
"integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
|
||||
},
|
||||
"node_modules/dunder-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||
|
@ -1217,19 +1180,6 @@
|
|||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/errno": {
|
||||
"version": "0.1.8",
|
||||
"resolved": "https://registry.npmmirror.com/errno/-/errno-0.1.8.tgz",
|
||||
"integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"prr": "~1.0.1"
|
||||
},
|
||||
"bin": {
|
||||
"errno": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/es-define-property": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||
|
@ -1425,14 +1375,6 @@
|
|||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/good-listener": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmmirror.com/good-listener/-/good-listener-1.2.2.tgz",
|
||||
"integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==",
|
||||
"dependencies": {
|
||||
"delegate": "^3.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/gopd": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz",
|
||||
|
@ -1444,13 +1386,6 @@
|
|||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/graceful-fs": {
|
||||
"version": "4.2.11",
|
||||
"resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/has-symbols": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||
|
@ -1495,45 +1430,11 @@
|
|||
"he": "bin/he"
|
||||
}
|
||||
},
|
||||
"node_modules/highlight.js": {
|
||||
"version": "11.11.1",
|
||||
"resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-11.11.1.tgz",
|
||||
"integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==",
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/hookable": {
|
||||
"version": "5.5.3",
|
||||
"resolved": "https://registry.npmmirror.com/hookable/-/hookable-5.5.3.tgz",
|
||||
"integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/image-size": {
|
||||
"version": "0.5.5",
|
||||
"resolved": "https://registry.npmmirror.com/image-size/-/image-size-0.5.5.tgz",
|
||||
"integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"bin": {
|
||||
"image-size": "bin/image-size.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-what": {
|
||||
"version": "4.1.16",
|
||||
"resolved": "https://registry.npmmirror.com/is-what/-/is-what-4.1.16.tgz",
|
||||
|
@ -1545,76 +1446,6 @@
|
|||
"url": "https://github.com/sponsors/mesqueeb"
|
||||
}
|
||||
},
|
||||
"node_modules/less": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmmirror.com/less/-/less-4.4.1.tgz",
|
||||
"integrity": "sha512-X9HKyiXPi0f/ed0XhgUlBeFfxrlDP3xR4M7768Zl+WXLUViuL9AOPPJP4nCV0tgRWvTYvpNmN0SFhZOQzy16PA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"copy-anything": "^2.0.1",
|
||||
"parse-node-version": "^1.0.1",
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
"bin": {
|
||||
"lessc": "bin/lessc"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"errno": "^0.1.1",
|
||||
"graceful-fs": "^4.1.2",
|
||||
"image-size": "~0.5.0",
|
||||
"make-dir": "^2.1.0",
|
||||
"mime": "^1.4.1",
|
||||
"needle": "^3.1.0",
|
||||
"source-map": "~0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/less-loader": {
|
||||
"version": "12.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/less-loader/-/less-loader-12.3.0.tgz",
|
||||
"integrity": "sha512-0M6+uYulvYIWs52y0LqN4+QM9TqWAohYSNTo4htE8Z7Cn3G/qQMEmktfHmyJT23k+20kU9zHH2wrfFXkxNLtVw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 18.12.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/webpack"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@rspack/core": "0.x || 1.x",
|
||||
"less": "^3.5.0 || ^4.0.0",
|
||||
"webpack": "^5.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@rspack/core": {
|
||||
"optional": true
|
||||
},
|
||||
"webpack": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/less/node_modules/copy-anything": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmmirror.com/copy-anything/-/copy-anything-2.0.6.tgz",
|
||||
"integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-what": "^3.14.1"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/mesqueeb"
|
||||
}
|
||||
},
|
||||
"node_modules/less/node_modules/is-what": {
|
||||
"version": "3.14.1",
|
||||
"resolved": "https://registry.npmmirror.com/is-what/-/is-what-3.14.1.tgz",
|
||||
"integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/lodash-es": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz",
|
||||
|
@ -1628,39 +1459,6 @@
|
|||
"@jridgewell/sourcemap-codec": "^1.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/make-dir": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/make-dir/-/make-dir-2.1.0.tgz",
|
||||
"integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"pify": "^4.0.1",
|
||||
"semver": "^5.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/marked": {
|
||||
"version": "12.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/marked/-/marked-12.0.2.tgz",
|
||||
"integrity": "sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q==",
|
||||
"bin": {
|
||||
"marked": "bin/marked.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/marked-highlight": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmmirror.com/marked-highlight/-/marked-highlight-2.2.2.tgz",
|
||||
"integrity": "sha512-KlHOP31DatbtPPXPaI8nx1KTrG3EW0Z5zewCwpUj65swbtKOTStteK3sNAjBqV75Pgo3fNEVNHeptg18mDuWgw==",
|
||||
"peerDependencies": {
|
||||
"marked": ">=4 <17"
|
||||
}
|
||||
},
|
||||
"node_modules/math-intrinsics": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
|
@ -1669,19 +1467,6 @@
|
|||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/mime": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz",
|
||||
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"bin": {
|
||||
"mime": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
|
||||
|
@ -1728,32 +1513,6 @@
|
|||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/needle": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmmirror.com/needle/-/needle-3.3.1.tgz",
|
||||
"integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"iconv-lite": "^0.6.3",
|
||||
"sax": "^1.2.4"
|
||||
},
|
||||
"bin": {
|
||||
"needle": "bin/needle"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4.4.x"
|
||||
}
|
||||
},
|
||||
"node_modules/parse-node-version": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/parse-node-version/-/parse-node-version-1.0.1.tgz",
|
||||
"integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/path-browserify": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/path-browserify/-/path-browserify-1.0.1.tgz",
|
||||
|
@ -1780,16 +1539,6 @@
|
|||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/pify": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/pify/-/pify-4.0.1.tgz",
|
||||
"integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/pinia": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/pinia/-/pinia-3.0.3.tgz",
|
||||
|
@ -1842,13 +1591,6 @@
|
|||
"resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
||||
},
|
||||
"node_modules/prr": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/prr/-/prr-1.0.1.tgz",
|
||||
"integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/rfdc": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz",
|
||||
|
@ -1893,50 +1635,11 @@
|
|||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/sax": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmmirror.com/sax/-/sax-1.4.1.tgz",
|
||||
"integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/select": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/select/-/select-1.1.2.tgz",
|
||||
"integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA=="
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "5.7.2",
|
||||
"resolved": "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz",
|
||||
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver"
|
||||
}
|
||||
},
|
||||
"node_modules/sortablejs": {
|
||||
"version": "1.15.6",
|
||||
"resolved": "https://registry.npmmirror.com/sortablejs/-/sortablejs-1.15.6.tgz",
|
||||
"integrity": "sha512-aNfiuwMEpfBM/CN6LY0ibyhxPfPbyFeBTYJKCvzkJ2GkUpazIt3H+QIPAMHwqQ7tMKaHz1Qj+rJJCqljnf4p3A=="
|
||||
},
|
||||
"node_modules/source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
|
@ -2001,11 +1704,6 @@
|
|||
"vue": ">=3.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tiny-emitter": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
|
||||
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
|
||||
},
|
||||
"node_modules/tinycolor2": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmmirror.com/tinycolor2/-/tinycolor2-1.6.0.tgz",
|
||||
|
@ -2027,12 +1725,6 @@
|
|||
"url": "https://github.com/sponsors/SuperchupuDev"
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.8.3",
|
||||
"resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.8.3.tgz",
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tdesign-vue-next/chat": "^0.4.5",
|
||||
"@types/node": "^24.2.1",
|
||||
"@vue/language-core": "^3.0.5",
|
||||
"axios": "^1.11.0",
|
||||
|
@ -22,8 +21,6 @@
|
|||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^6.0.1",
|
||||
"@vue/tsconfig": "^0.7.0",
|
||||
"less": "^4.4.1",
|
||||
"less-loader": "^12.3.0",
|
||||
"typescript": "~5.8.3",
|
||||
"vite": "^7.1.2",
|
||||
"vue-tsc": "^3.0.5"
|
||||
|
|
|
@ -34,8 +34,6 @@ import { useLanguageStore } from './store/language'
|
|||
import { provide } from 'vue'
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
|
||||
|
||||
// 初始化语言store并提供翻译函数
|
||||
const languageStore = useLanguageStore()
|
||||
provide('t', (key: string) => languageStore.t(key))
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 4.6 KiB |
|
@ -1,291 +0,0 @@
|
|||
// 定义类型接口
|
||||
interface FetchSSEOptions {
|
||||
success?: (data: any) => void;
|
||||
fail?: () => void;
|
||||
complete?: (isOk: boolean, msg?: string) => void;
|
||||
url?: string;
|
||||
}
|
||||
|
||||
interface SSEEvent {
|
||||
type: string | null;
|
||||
data: any;
|
||||
}
|
||||
|
||||
const Api = {
|
||||
// CHAT_AI_URL: 'http://localhost:8180/chat/generateStreamFlex'
|
||||
CHAT_AI_URL: '/rag/query_rag'
|
||||
};
|
||||
import axios from 'axios'
|
||||
const getAi=(msg: any):any => {
|
||||
return axios.get(Api.CHAT_AI_URL, {
|
||||
params: { message: msg },
|
||||
headers: { Accept: 'text/event-stream' },
|
||||
responseType: 'stream'
|
||||
});
|
||||
}
|
||||
export class MockSSEResponse {
|
||||
private controller!: ReadableStreamDefaultController<Uint8Array>;
|
||||
|
||||
private encoder = new TextEncoder();
|
||||
|
||||
private stream: ReadableStream<Uint8Array>;
|
||||
|
||||
private error: boolean;
|
||||
|
||||
private currentPhase: 'reasoning' | 'content' = 'reasoning';
|
||||
|
||||
private data: {
|
||||
reasoning: string;
|
||||
content: string;
|
||||
};
|
||||
|
||||
private delay: number;
|
||||
|
||||
constructor(
|
||||
data: {
|
||||
reasoning: string; // 推理内容
|
||||
content: string; // 正式内容
|
||||
},
|
||||
delay: number = 100,
|
||||
error = false,
|
||||
) {
|
||||
this.data = data;
|
||||
this.delay = delay;
|
||||
this.error = error;
|
||||
|
||||
this.stream = new ReadableStream({
|
||||
start: (controller) => {
|
||||
this.controller = controller;
|
||||
if (!this.error) {
|
||||
// 如果不是错误情况,则开始推送数据
|
||||
setTimeout(() => this.pushData(), this.delay); // 延迟开始推送数据
|
||||
}
|
||||
},
|
||||
cancel() {},
|
||||
});
|
||||
}
|
||||
|
||||
private pushData() {
|
||||
try {
|
||||
if (this.currentPhase === 'reasoning') {
|
||||
// 推送推理内容
|
||||
if (this.data.reasoning.length > 0) {
|
||||
const chunk = JSON.stringify({
|
||||
delta: {
|
||||
reasoning_content: this.data.reasoning.slice(0, 1),
|
||||
content: '',
|
||||
},
|
||||
finished: false,
|
||||
});
|
||||
this.controller.enqueue(this.encoder.encode(chunk));
|
||||
this.data.reasoning = this.data.reasoning.slice(1);
|
||||
// 设置下次推送
|
||||
setTimeout(() => this.pushData(), this.delay);
|
||||
} else {
|
||||
// 推理内容推送完成,切换到正式内容
|
||||
this.currentPhase = 'content';
|
||||
setTimeout(() => this.pushData(), this.delay); // 立即开始推送正式内容
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.currentPhase === 'content') {
|
||||
// 推送正式内容
|
||||
if (this.data.content.length > 0) {
|
||||
const chunk = JSON.stringify({
|
||||
delta: {
|
||||
reasoning_content: '',
|
||||
content: this.data.content.slice(0, 1),
|
||||
},
|
||||
finished: this.data.content.length === 1, // 最后一个字符时标记完成
|
||||
});
|
||||
this.controller.enqueue(this.encoder.encode(chunk));
|
||||
this.data.content = this.data.content.slice(1);
|
||||
|
||||
// 设置下次推送
|
||||
setTimeout(() => this.pushData(), this.delay);
|
||||
} else {
|
||||
// const finalPayload = JSON.stringify({
|
||||
// delta: {
|
||||
// reasoning_content: '',
|
||||
// content: '',
|
||||
// },
|
||||
// finished: true,
|
||||
// });
|
||||
// this.controller.enqueue(this.encoder.encode(`${finalPayload}`));
|
||||
// 全部内容推送完成
|
||||
setTimeout(() => this.controller.close(), this.delay);
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
getResponse(_msg: String): Promise<Response> {
|
||||
return new Promise((resolve) => {
|
||||
resolve(getAi(_msg));
|
||||
});
|
||||
}
|
||||
|
||||
// 封装SSE连接 - 添加降级处理
|
||||
connectSSE = (url: string, params: any, onMessage?: (data: string) => void, onError?: (error: Event) => void) => {
|
||||
try {
|
||||
// 构建带参数的URL
|
||||
const queryString = Object.keys(params)
|
||||
.map((key: string) => `${encodeURIComponent(key)}=${encodeURIComponent(params[key]?.message || params[key])}`)
|
||||
.join('&');
|
||||
|
||||
// const API_BASE_URL = 'http://localhost:8080';
|
||||
const API_BASE_URL = '';
|
||||
const fullUrl = `${API_BASE_URL}${url}?${queryString}`;
|
||||
|
||||
// 检查 EventSource 是否可用
|
||||
if (typeof EventSource === 'undefined') {
|
||||
throw new Error('EventSource not supported');
|
||||
}
|
||||
|
||||
// 创建EventSource
|
||||
const eventSource = new EventSource(fullUrl);
|
||||
|
||||
eventSource.onmessage = (event: MessageEvent) => {
|
||||
const { data } = event;
|
||||
if (onMessage) {
|
||||
if (data === '[DONE]') {
|
||||
onMessage('[DONE]');
|
||||
} else {
|
||||
onMessage(data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
eventSource.onerror = (error: Event) => {
|
||||
if (onError) {
|
||||
onError(error);
|
||||
}
|
||||
eventSource.close();
|
||||
};
|
||||
|
||||
return eventSource;
|
||||
} catch (error) {
|
||||
// 降级处理:使用模拟数据
|
||||
console.warn('EventSource 不可用,使用模拟数据');
|
||||
|
||||
// 模拟 SSE 行为
|
||||
const mockData = [
|
||||
"您好!",
|
||||
"我是AI助手。",
|
||||
"正在处理您的问题...",
|
||||
"根据分析,",
|
||||
"这是详细的回答。",
|
||||
"希望能帮到您!",
|
||||
"[DONE]"
|
||||
];
|
||||
|
||||
let index = 0;
|
||||
const interval = setInterval(() => {
|
||||
if (index < mockData.length) {
|
||||
if (onMessage) onMessage(mockData[index]);
|
||||
index++;
|
||||
} else {
|
||||
clearInterval(interval);
|
||||
}
|
||||
}, 500);
|
||||
|
||||
// 返回一个可以关闭的模拟对象
|
||||
return {
|
||||
close: () => clearInterval(interval),
|
||||
readyState: 1
|
||||
} as EventSource;
|
||||
}
|
||||
};
|
||||
|
||||
// AI超级智能体聊天 - 实际使用 connectSSE 方法
|
||||
chatWithManus = (message: any, onMessage?: (data: string) => void, onError?: (error: Event) => void) => {
|
||||
return this.connectSSE('/rag/query_rag', { message }, onMessage, onError);
|
||||
};
|
||||
}
|
||||
|
||||
export const fetchSSE = async (options: FetchSSEOptions = {}) => {
|
||||
const { success, fail, complete, url } = options;
|
||||
// fetch请求流式接口url,需传入接口url和参数
|
||||
if (!url) {
|
||||
complete?.(false, 'URL is required');
|
||||
return;
|
||||
}
|
||||
|
||||
const responsePromise = fetch(url).catch((e: any) => {
|
||||
const msg = e.toString() || '流式接口异常';
|
||||
complete?.(false, msg);
|
||||
return Promise.reject(e);
|
||||
});
|
||||
|
||||
responsePromise
|
||||
.then((response) => {
|
||||
if (!response?.ok) {
|
||||
complete?.(false, response.statusText);
|
||||
fail?.();
|
||||
throw new Error('Request failed');
|
||||
}
|
||||
const reader = response.body?.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
if (!reader) {
|
||||
complete?.(false, 'No reader available');
|
||||
return;
|
||||
}
|
||||
|
||||
const bufferArr: string[] = [];
|
||||
let dataText = '';
|
||||
const event: SSEEvent = { type: null, data: null };
|
||||
|
||||
async function processText({ done, value }: ReadableStreamReadResult<Uint8Array>): Promise<void> {
|
||||
if (done) {
|
||||
complete?.(true);
|
||||
return Promise.resolve();
|
||||
}
|
||||
const chunk = decoder.decode(value);
|
||||
const buffers = chunk.toString().replace(/ /g, ' ').split(/\r?\n/);
|
||||
bufferArr.push(...buffers);
|
||||
let i = 0;
|
||||
while (i < bufferArr.length) {
|
||||
const line = bufferArr[i];
|
||||
if (line) {
|
||||
dataText += line;
|
||||
const response = line.slice(5);
|
||||
if (response === '[DONE]') {
|
||||
event.type = 'finish';
|
||||
dataText = '';
|
||||
} else {
|
||||
event.type = 'delta';
|
||||
event.data = response;
|
||||
|
||||
// const choices = JSON.parse(response.trim())?.choices?.[0];
|
||||
// if (choices.finish_reason === 'stop') {
|
||||
// event.type = 'finish';
|
||||
// dataText = '';
|
||||
// } else {
|
||||
// event.type = 'delta';
|
||||
// event.data = choices;
|
||||
// }
|
||||
}
|
||||
}
|
||||
if (event.type && event.data) {
|
||||
try {
|
||||
const jsonData = JSON.parse(event.data);
|
||||
success?.(jsonData);
|
||||
} catch (e) {
|
||||
success?.(event.data);
|
||||
}
|
||||
event.type = null;
|
||||
event.data = null;
|
||||
}
|
||||
bufferArr.splice(i, 1);
|
||||
}
|
||||
return reader!.read().then(processText);
|
||||
}
|
||||
|
||||
return reader.read().then(processText);
|
||||
})
|
||||
.catch(() => {
|
||||
// 处理整个链式调用过程中发生的任何错误
|
||||
fail?.();
|
||||
});
|
||||
};
|
|
@ -1,435 +0,0 @@
|
|||
<template>
|
||||
<t-space v-if="isVisible1" align="center" @click="visibleModelessDrag = true" class="ai_style" >
|
||||
<img :src="aiImg" alt="勤怠登录页面" width="36px" height="36px">
|
||||
</t-space>
|
||||
<t-config-provider :global-config="globalConfig">
|
||||
<t-dialog
|
||||
v-model:visible="visibleModelessDrag"
|
||||
:footer="false"
|
||||
id ="abc"
|
||||
:header="t('ai.title')"
|
||||
mode="modeless"
|
||||
showOverlay="true"
|
||||
|
||||
draggable
|
||||
:on-confirm="() => (visibleModelessDrag = false)"
|
||||
>
|
||||
<template #body>
|
||||
<t-chat
|
||||
layout="both"
|
||||
style="height: 600px;"
|
||||
:z-index="3000"
|
||||
:data="chatList"
|
||||
:clear-history="chatList.length > 0 && !isStreamLoad"
|
||||
:text-loading="loading"
|
||||
:is-stream-load="isStreamLoad"
|
||||
@on-action="operation"
|
||||
@clear="clearConfirm"
|
||||
>
|
||||
<template #actions="{ item, index }">
|
||||
<t-chat-action
|
||||
:content="item.content"
|
||||
:operation-btn="['good', 'bad', 'replay', 'copy']"
|
||||
@operation="handleOperation"
|
||||
/>
|
||||
</template>
|
||||
<template #footer>
|
||||
<t-chat-input v-model="inputValue" :stop-disabled="isStreamLoad" @send="inputEnter" @stop="onStop"> </t-chat-input>
|
||||
</template>
|
||||
</t-chat>
|
||||
|
||||
|
||||
</template>
|
||||
</t-dialog>
|
||||
</t-config-provider>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, inject, watch, computed } from 'vue'
|
||||
import aiImg from '@/assets/ai_img.png'
|
||||
import { useLanguageStore } from '../../store/language'
|
||||
import { MockSSEResponse } from './ChatAi';
|
||||
import { globalConfig } from '../../locales/globalConfig'
|
||||
|
||||
const visibleModelessDrag = ref(false);
|
||||
|
||||
const fetchCancel = ref<any>(null);
|
||||
const loading = ref(false);
|
||||
const isStreamLoad = ref(false);
|
||||
const chatRef = ref<any>(null);
|
||||
const isShowToBottom = ref(false);
|
||||
const inputValue = ref('');
|
||||
|
||||
const store = useLanguageStore()
|
||||
|
||||
// 控制按钮显示/隐藏的状态
|
||||
const isVisible1 = ref(false)
|
||||
|
||||
const handleScroll1 = () => {
|
||||
isVisible1.value = window.scrollY > 300
|
||||
};
|
||||
|
||||
// 注入翻译函数
|
||||
const t = inject<(key: string) => string>('t') || ((key: string) => key)
|
||||
|
||||
// 挂载时添加滚动监听
|
||||
onMounted(() => {
|
||||
window.addEventListener('scroll', handleScroll1)
|
||||
})
|
||||
|
||||
// 卸载时移除滚动监听和关闭EventSource
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('scroll', handleScroll1)
|
||||
if (eventSource.value) {
|
||||
eventSource.value.close()
|
||||
}
|
||||
})
|
||||
|
||||
// 计算属性,根据当前语言返回对应的文本
|
||||
const clearHistoryBtnText = computed(() => {
|
||||
return t('ai.clearHistory')
|
||||
})
|
||||
|
||||
// 获取指定格式的时间字符串
|
||||
const getFormattedDateTime = (): string => {
|
||||
const now = new Date();
|
||||
const year = now.getFullYear().toString().slice(-2);
|
||||
const month = (now.getMonth() + 1).toString().padStart(2, '0');
|
||||
const day = now.getDate().toString().padStart(2, '0');
|
||||
const hours = now.getHours().toString().padStart(2, '0');
|
||||
const minutes = now.getMinutes().toString().padStart(2, '0');
|
||||
|
||||
return `[${year}-${month}-${day} ${hours}:${minutes}]`;
|
||||
};
|
||||
|
||||
// 倒序渲染
|
||||
const chatList = ref<any[]>([
|
||||
{
|
||||
avatar: 'https://tdesign.gtimg.com/site/chat-avatar.png',
|
||||
name: 'youyueAI',
|
||||
datetime: getFormattedDateTime(),
|
||||
content: t('ai.initContent'),
|
||||
role: 'assistant',
|
||||
}
|
||||
|
||||
]);
|
||||
|
||||
const handleOperation = function (type: string, options: any) {
|
||||
console.log('handleOperation', type, options);
|
||||
};
|
||||
const operation = function (type: string, options: any) {
|
||||
console.log(type, options);
|
||||
};
|
||||
const clearConfirm = function () {
|
||||
chatList.value = [];
|
||||
};
|
||||
const onStop = function () {
|
||||
if (fetchCancel.value) {
|
||||
fetchCancel.value.controller.close();
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
const inputEnter = function (inputValue: string) {
|
||||
if (isStreamLoad.value) {
|
||||
return;
|
||||
}
|
||||
if (!inputValue) return;
|
||||
const params = {
|
||||
avatar: 'https://tdesign.gtimg.com/site/avatar.jpg',
|
||||
name: '自己',
|
||||
datetime: new Date().toDateString(),
|
||||
content: inputValue,
|
||||
role: 'user',
|
||||
};
|
||||
chatList.value.unshift(params);
|
||||
// 空消息占位
|
||||
const params2 = {
|
||||
avatar: 'https://tdesign.gtimg.com/site/chat-avatar.png',
|
||||
name: 'youyueAI',
|
||||
datetime: new Date().toDateString(),
|
||||
content: '',
|
||||
role: 'assistant',
|
||||
};
|
||||
chatList.value.unshift(params2);
|
||||
handleData(inputValue);
|
||||
};
|
||||
|
||||
const fetchSSE = async (fetchFn: any, options: any) => {
|
||||
const response = await fetchFn();
|
||||
const { success, fail, complete } = options;
|
||||
if (response.status !== 200) {
|
||||
complete?.(false, response.statusText);
|
||||
fail?.();
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// 定义类型接口
|
||||
interface ChatMessage {
|
||||
role: string;
|
||||
content: string;
|
||||
reasoning?: string;
|
||||
duration?: number;
|
||||
}
|
||||
|
||||
// 配置参数
|
||||
const config = {
|
||||
bufferSize: 5,
|
||||
flushInterval: 10,
|
||||
typingSpeed: 50,
|
||||
};
|
||||
|
||||
// 使用所有变量
|
||||
const displayText = ref('');
|
||||
const fullText = ref('');
|
||||
const isLoading = ref(true);
|
||||
const eventSource = ref<any>(null);
|
||||
|
||||
// 实际使用的变量
|
||||
let animationFrameId: number | null = null;
|
||||
const messageBuffer: string[] = [];
|
||||
let isProcessing = false;
|
||||
|
||||
// 处理消息缓冲区
|
||||
const processBuffer = async () => {
|
||||
isProcessing = true;
|
||||
const processNextMessage = () => {
|
||||
if (messageBuffer.length === 0) {
|
||||
isProcessing = false;
|
||||
return;
|
||||
}
|
||||
const message = messageBuffer.shift();
|
||||
if (message) {
|
||||
typeCharacter(message);
|
||||
}
|
||||
animationFrameId = requestAnimationFrame(processNextMessage);
|
||||
};
|
||||
animationFrameId = requestAnimationFrame(processNextMessage);
|
||||
};
|
||||
|
||||
// 模拟打字效果
|
||||
const typeCharacter = (content: string) => {
|
||||
const index = 0; // 实际使用的索引
|
||||
setTimeout(() => {
|
||||
if (chatList.value && chatList.value[0]) {
|
||||
chatList.value[0].content += content.replace(/ /g, ' ');
|
||||
}
|
||||
}, config.typingSpeed);
|
||||
};
|
||||
|
||||
// 实际使用 connectSSE 的数据处理函数
|
||||
const handleData = async (inputVal?: string) => {
|
||||
loading.value = true;
|
||||
isStreamLoad.value = true;
|
||||
|
||||
// 使用 MockSSEResponse 的 connectSSE 方法
|
||||
const mockSSE = new MockSSEResponse({
|
||||
reasoning: "正在分析问题...",
|
||||
content: "正在连接服务器..."
|
||||
});
|
||||
|
||||
// // 实际使用 connectSSE 方法
|
||||
// eventSource.value = mockSSE.chatWithManus(
|
||||
// inputVal,
|
||||
// (data: string) => {
|
||||
// // 处理接收到的消息
|
||||
// if (data === '[DONE]') {
|
||||
// loading.value = false;
|
||||
// isStreamLoad.value = false;
|
||||
// } else {
|
||||
// // 将接收到的数据添加到消息中
|
||||
// if (chatList.value && chatList.value[0]) {
|
||||
// // 如果是第一条消息,先清空再添加
|
||||
// if (chatList.value[0].content === '' || chatList.value[0].content === '正在连接服务器...') {
|
||||
// chatList.value[0].content = data;
|
||||
// } else {
|
||||
// chatList.value[0].content += data;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// (error: Event) => {
|
||||
// // console.error('SSE Error:', error);
|
||||
// loading.value = false;
|
||||
// isStreamLoad.value = false;
|
||||
// // if (chatList.value && chatList.value[0]) {
|
||||
// // chatList.value[0].content = '正在使用本地AI助手...';
|
||||
// // // 添加一个模拟的AI回复
|
||||
// // setTimeout(() => {
|
||||
// // if (chatList.value && chatList.value[0]) {
|
||||
// // chatList.value[0].content = `您好!我是AI助手。关于您的问题「${inputVal}」,我可以为您提供以下帮助:\n\n` +
|
||||
// // `1. 这是一个基于前端模拟的AI回复,因为后端服务暂时无法连接。\n` +
|
||||
// // `2. 在实际部署中,这里会连接到真正的AI服务。\n` +
|
||||
// // `3. 您可以继续提问,我会继续为您提供模拟的回复。\n\n` +
|
||||
// // `感谢您的理解!`;
|
||||
// // }
|
||||
// // }, 1000);
|
||||
// // }
|
||||
// }
|
||||
// );
|
||||
|
||||
const message = {
|
||||
message: inputValue.value,
|
||||
};
|
||||
const eventSource = mockSSE.chatWithManus(message);
|
||||
eventSource.onmessage = (event) => {
|
||||
const { data } = event;
|
||||
|
||||
if (data && data !== '[DONE]') {
|
||||
// messageBuffer.push(data);
|
||||
|
||||
loading.value = false;
|
||||
// 控制终止按钮
|
||||
isStreamLoad.value = false;
|
||||
// lastItem.reasoning += data.replaceAll(' ', ' ');
|
||||
// lastItem.content += data.replaceAll(' ', ' ');
|
||||
|
||||
// 将消息添加到缓冲区
|
||||
messageBuffer.push(data.replaceAll(' ', ' '));
|
||||
|
||||
// 如果不在处理中,则开始处理缓冲区
|
||||
if (!isProcessing) {
|
||||
processBuffer();
|
||||
}
|
||||
|
||||
// // 检查是否应该创建新气泡
|
||||
// const combinedText = messageBuffer.join('');
|
||||
|
||||
// // 句子结束或消息长度达到阈值
|
||||
// const lastChar = data.charAt(data.length - 1);
|
||||
// const hasCompleteSentence = chineseEndPunctuation.includes(lastChar) || data.includes('\n\n');
|
||||
// const isLongEnough = combinedText.length > 40;
|
||||
|
||||
// if (hasCompleteSentence || isLongEnough) {
|
||||
// createBubble(combinedText);
|
||||
// }
|
||||
}
|
||||
|
||||
if (data === '[DONE]') {
|
||||
// 如果还有未显示的内容,创建最后一个气泡
|
||||
// if (messageBuffer.length > 0) {
|
||||
// const remainingContent = messageBuffer.join('');
|
||||
// createBubble(remainingContent, 'ai-final');
|
||||
// }
|
||||
// 控制终止按钮
|
||||
isStreamLoad.value = false;
|
||||
loading.value = false;
|
||||
|
||||
// 完成后关闭连接
|
||||
// connectionStatus.value = 'disconnected';
|
||||
eventSource.close();
|
||||
}
|
||||
};
|
||||
|
||||
// 监听SSE错误
|
||||
eventSource.onerror = (error) => {
|
||||
// 控制终止按钮
|
||||
isStreamLoad.value = false;
|
||||
loading.value = false;
|
||||
eventSource.close();
|
||||
// console.error('SSE Error:', error);
|
||||
// connectionStatus.value = 'error';
|
||||
// eventSource.close();
|
||||
// // 如果出错时有未显示的内容,也创建气泡
|
||||
// if (messageBuffer.length > 0) {
|
||||
// const remainingContent = messageBuffer.join('');
|
||||
// createBubble(remainingContent, 'ai-error');
|
||||
// }
|
||||
};
|
||||
|
||||
// 流结束时触发
|
||||
// eventSource.close = () => {
|
||||
// // 可在此处做清理工作,如关闭 EventSource
|
||||
// eventSource.close();
|
||||
// };
|
||||
};
|
||||
|
||||
// 修复 watch 中的类型问题
|
||||
watch(
|
||||
() => store.currentLanguage,
|
||||
(newLang: string, oldLang: string) => {
|
||||
// 使用 newLang 和 oldLang
|
||||
console.log('Language changed from', oldLang, 'to', newLang);
|
||||
chatList.value[0].content = t('ai.initContent');
|
||||
}
|
||||
);
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
/* 应用滚动条样式 */
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: var(--td-scrollbar-color);
|
||||
}
|
||||
::-webkit-scrollbar-thumb:horizontal:hover {
|
||||
background-color: var(--td-scrollbar-hover-color);
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background-color: var(--td-scroll-track-color);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 第一种写法重写 组件样式
|
||||
:global(.t-dialog__ctx.t-dialog__ctx--modeless .t-dialog) {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
|
||||
:deep(.t-chat__text) {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
// 第二种重写样式 定义一个啥也不带的 style
|
||||
<style>
|
||||
|
||||
.t-chat__actions-margin {
|
||||
display: none;
|
||||
}
|
||||
.t-dialog__ctx .t-dialog__wrap {
|
||||
z-index: 3001;
|
||||
right: 10;
|
||||
}
|
||||
|
||||
.t-dialog__ctx .t-dialog__position {
|
||||
justify-content: right;
|
||||
/* left: 86px; */
|
||||
position: fixed;
|
||||
right: 100px;
|
||||
bottom: 52px;
|
||||
}
|
||||
|
||||
.ai_style{
|
||||
gap: 16px;
|
||||
position: fixed;
|
||||
bottom: 100px;
|
||||
right: 2rem;
|
||||
background-color: #d7e6ed;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 50px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: 2px solid #fff;
|
||||
}
|
||||
|
||||
.ai_style:hover {
|
||||
background-color: var(--primary-dark);
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.t-dialog__ctx .t-dialog__position {
|
||||
justify-content: center;
|
||||
/* left: 86px; */
|
||||
position: unset;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
@ -8,7 +8,6 @@ const isVisible = ref(false)
|
|||
const handleScroll = () => {
|
||||
// 当滚动超过300px时显示按钮
|
||||
isVisible.value = window.scrollY > 300
|
||||
|
||||
}
|
||||
|
||||
// 回到顶部函数
|
||||
|
|
|
@ -13,8 +13,8 @@ const messages = {
|
|||
// 创建 i18n 实例
|
||||
const i18n = createI18n({
|
||||
legacy: false, // 使用 Composition API,必须设置为 false
|
||||
locale: 'ja', // 默认语言
|
||||
fallbackLocale: 'zh', // 回退语言
|
||||
locale: 'zh', // 默认语言
|
||||
fallbackLocale: 'ja', // 回退语言
|
||||
messages
|
||||
})
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import enUs from 'tdesign-vue-next/es/locale/en_US';
|
||||
export default {
|
||||
nav: {
|
||||
home: 'Home',
|
||||
|
@ -42,10 +41,4 @@ export default {
|
|||
timeline: {
|
||||
title: 'Company History',
|
||||
},
|
||||
ai:{
|
||||
title: 'AIチャット',
|
||||
initContent: 'こんにちは、何かお手伝いできることはありますか?',
|
||||
clearHistory: '履歴をクリア',
|
||||
},
|
||||
...enUs.chat
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
// 导入所需的语言包
|
||||
import zhConfig from 'tdesign-vue-next/es/locale/zh_CN';
|
||||
import enConfig from 'tdesign-vue-next/es/locale/en_US';
|
||||
import jaConfig from 'tdesign-vue-next/es/locale/ja_JP';
|
||||
import { merge } from 'lodash-es';
|
||||
import { type GlobalConfigProvider } from 'tdesign-vue-next';
|
||||
import { ref, watch, computed } from 'vue';
|
||||
|
||||
|
||||
import { useLanguageStore } from '../store/language'
|
||||
|
||||
const store = useLanguageStore()
|
||||
|
||||
// 定义支持的语言类型
|
||||
// export type Language = 'zh-CN' | 'en-US' | 'ja-JP';
|
||||
export type Language = 'zh' | 'en' | 'ja';
|
||||
|
||||
// 明确类型为 Language
|
||||
const currentLanguage = computed<Language>(() => store.currentLanguage);
|
||||
|
||||
|
||||
|
||||
|
||||
// 基础自定义配置
|
||||
const customConfig: GlobalConfigProvider = {
|
||||
calendar: {},
|
||||
table: {},
|
||||
pagination: {},
|
||||
chat:{},
|
||||
// 可以添加更多自定义配置
|
||||
};
|
||||
|
||||
// 语言包映射(使用 any 类型来绕过严格的类型检查)
|
||||
const localeMap: Record<Language, any> = {
|
||||
'zh': zhConfig,
|
||||
'en': enConfig,
|
||||
'ja': jaConfig,
|
||||
};
|
||||
|
||||
// 响应式全局配置
|
||||
const globalConfig = ref<GlobalConfigProvider>(
|
||||
merge({}, localeMap[currentLanguage.value] || localeMap['zh'], customConfig) as GlobalConfigProvider
|
||||
);
|
||||
|
||||
// 当语言变化时更新全局配置
|
||||
watch(currentLanguage, (newLang) => {
|
||||
// 更新全局配置
|
||||
const lang = newLang as unknown as Language;
|
||||
globalConfig.value = merge({}, localeMap[lang] || localeMap['zh'], customConfig) as GlobalConfigProvider;
|
||||
console.log(globalConfig.value)
|
||||
});
|
||||
|
||||
|
||||
export {
|
||||
currentLanguage,
|
||||
globalConfig
|
||||
};
|
|
@ -1,5 +1,3 @@
|
|||
import jaJP from 'tdesign-vue-next/es/locale/ja_JP';
|
||||
|
||||
export default {
|
||||
nav: {
|
||||
name:'優閲スタジオ',
|
||||
|
@ -119,13 +117,6 @@ export default {
|
|||
timeline: {
|
||||
title: 'タイムライン',
|
||||
},
|
||||
ai:{
|
||||
title: 'AIチャット',
|
||||
initContent: 'こんにちは、何かお手伝いできることはありますか?',
|
||||
clearHistory: '履歴をクリア',
|
||||
},
|
||||
|
||||
chat: {
|
||||
...jaJP.chat
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,3 @@
|
|||
|
||||
import znCh from 'tdesign-vue-next/es/locale/zh_CN';
|
||||
|
||||
export default {
|
||||
nav: {
|
||||
name:'优阅工作室',
|
||||
|
@ -116,13 +113,4 @@ export default {
|
|||
timeline: {
|
||||
title: '项目经历',
|
||||
},
|
||||
ai:{
|
||||
title: 'AI助手',
|
||||
initContent: '我是你的ai助手,有什么可以帮你的。',
|
||||
clearHistory: "清空历史记录11",
|
||||
|
||||
},
|
||||
chat: {
|
||||
...znCh.chat
|
||||
}
|
||||
}
|
10
src/main.ts
10
src/main.ts
|
@ -3,11 +3,12 @@ import './style.css'
|
|||
import App from './App.vue'
|
||||
import router from './router/index.ts' // 确保路径正确
|
||||
|
||||
|
||||
|
||||
// 引入Pinia
|
||||
import { createPinia } from 'pinia'
|
||||
// 引入TDesign UI组件库
|
||||
import TDesign from 'tdesign-vue-next'
|
||||
import TDesignChat from '@tdesign-vue-next/chat'; // 引入chat组件
|
||||
import 'tdesign-vue-next/es/style/index.css'
|
||||
|
||||
const app = createApp(App)
|
||||
|
@ -17,15 +18,8 @@ app.use(router)
|
|||
// 使用Pinia
|
||||
app.use(createPinia())
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 使用TDesign
|
||||
app.use(TDesign)
|
||||
app.use(TDesignChat)
|
||||
|
||||
|
||||
|
||||
app.mount('#app')
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ import { defineStore } from 'pinia'
|
|||
import zh from '../locales/zh'
|
||||
import ja from '../locales/ja'
|
||||
|
||||
|
||||
// 获取浏览器语言
|
||||
function getBrowserLanguage() {
|
||||
// 获取浏览器主语言(如从 "zh-CN" 中提取 "zh-CN" 或从 "zh-TW" 中提取 "zh-TW")
|
||||
|
@ -10,7 +11,7 @@ function getBrowserLanguage() {
|
|||
return 'zh';
|
||||
}
|
||||
// 默认返回英语
|
||||
return 'ja';
|
||||
return 'en';
|
||||
}
|
||||
const lang = getBrowserLanguage()
|
||||
export const useLanguageStore = defineStore('language', {
|
||||
|
|
|
@ -27,8 +27,6 @@
|
|||
|
||||
<!-- 联系我们 -->
|
||||
<ContactForm />
|
||||
|
||||
<ChatAi />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -39,8 +37,6 @@ import TeamMembers from '@/components/team/TeamMembers.vue'
|
|||
import ProjectShowcase from '@/components/projects/ProjectShowcase.vue'
|
||||
import ContactForm from '@/components/contact/ContactForm.vue'
|
||||
import CompanyTimeline from '@/components/timeline/CompanyTimeline.vue'
|
||||
|
||||
import ChatAi from '@/components/chat/ChatAi.vue'
|
||||
import { inject } from 'vue'
|
||||
|
||||
// 注入翻译函数(与原App.vue保持一致)
|
||||
|
@ -49,6 +45,9 @@ const t = inject<(key: string) => string>('t') || ((key) => key)
|
|||
|
||||
|
||||
<style scoped>
|
||||
|
||||
|
||||
|
||||
.hero-section {
|
||||
height: 100vh;
|
||||
background-color: var(--bg-color);
|
||||
|
|
|
@ -3,12 +3,5 @@
|
|||
"references": [
|
||||
{ "path": "./tsconfig.app.json" },
|
||||
{ "path": "./tsconfig.node.json" }
|
||||
],
|
||||
"compilerOptions": {
|
||||
"moduleResolution": "Node",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"*": ["node_modules/*"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -10,9 +10,4 @@ export default defineConfig({
|
|||
'@': path.resolve(__dirname, 'src'), // 将 @ 指向 src 目录
|
||||
},
|
||||
},
|
||||
server: {
|
||||
host: '0.0.0.0', // 监听所有网络接口
|
||||
port: 5173, // 可自定义端口(默认5173)
|
||||
open: false // 可选:是否自动打开浏览器
|
||||
}
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue