Compare commits

..

No commits in common. "main" and "feature/project_dedtail" have entirely different histories.

38 changed files with 3523 additions and 4668 deletions

View File

@ -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/**']
}
]);

View File

@ -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>

308
package-lock.json generated
View File

@ -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",

View File

@ -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"

View File

@ -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

View File

@ -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(/&nbsp;/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?.();
});
};

View File

@ -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(/&nbsp;/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('&nbsp;', ' ');
// lastItem.content += data.replaceAll('&nbsp;', ' ');
//
messageBuffer.push(data.replaceAll('&nbsp;', ' '));
//
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>

View File

@ -8,7 +8,6 @@ const isVisible = ref(false)
const handleScroll = () => {
// 300px
isVisible.value = window.scrollY > 300
}
//

View File

@ -13,8 +13,8 @@ const messages = {
// 创建 i18n 实例
const i18n = createI18n({
legacy: false, // 使用 Composition API必须设置为 false
locale: 'ja', // 默认语言
fallbackLocale: 'zh', // 回退语言
locale: 'zh', // 默认语言
fallbackLocale: 'ja', // 回退语言
messages
})

View File

@ -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
}

View File

@ -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
};

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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')

View File

@ -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', {

View File

@ -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);

View File

@ -3,12 +3,5 @@
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
],
"compilerOptions": {
"moduleResolution": "Node",
"baseUrl": ".",
"paths": {
"*": ["node_modules/*"]
}
}
]
}

View File

@ -10,9 +10,4 @@ export default defineConfig({
'@': path.resolve(__dirname, 'src'), // 将 @ 指向 src 目录
},
},
server: {
host: '0.0.0.0', // 监听所有网络接口
port: 5173, // 可自定义端口默认5173
open: false // 可选:是否自动打开浏览器
}
})