diff --git a/index.html b/index.html
index 3c91bee..ac25c6e 100644
--- a/index.html
+++ b/index.html
@@ -5,7 +5,7 @@
-
Vite + Vue + TS
+ 优阅工作室
diff --git a/package-lock.json b/package-lock.json
index 0f67b88..f0c63ae 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,6 +8,7 @@
"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",
@@ -20,6 +21,8 @@
"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"
@@ -815,6 +818,25 @@
"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",
@@ -1108,6 +1130,16 @@
"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",
@@ -1156,6 +1188,11 @@
"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",
@@ -1180,6 +1217,19 @@
"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",
@@ -1375,6 +1425,14 @@
"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",
@@ -1386,6 +1444,13 @@
"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",
@@ -1430,11 +1495,45 @@
"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",
@@ -1446,6 +1545,76 @@
"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",
@@ -1459,6 +1628,39 @@
"@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",
@@ -1467,6 +1669,19 @@
"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",
@@ -1513,6 +1728,32 @@
"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",
@@ -1539,6 +1780,16 @@
"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",
@@ -1591,6 +1842,13 @@
"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",
@@ -1635,11 +1893,50 @@
"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",
@@ -1704,6 +2001,11 @@
"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",
@@ -1725,6 +2027,12 @@
"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",
diff --git a/package.json b/package.json
index 38e116b..0ef35cd 100644
--- a/package.json
+++ b/package.json
@@ -9,6 +9,7 @@
"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",
@@ -21,6 +22,8 @@
"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"
diff --git a/src/App.vue b/src/App.vue
index 6d57c26..6b00ab5 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -34,6 +34,8 @@ 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))
diff --git a/src/assets/ai_img.png b/src/assets/ai_img.png
new file mode 100644
index 0000000..0279fbe
Binary files /dev/null and b/src/assets/ai_img.png differ
diff --git a/src/components/chat/ChatAi.ts b/src/components/chat/ChatAi.ts
new file mode 100644
index 0000000..32383c4
--- /dev/null
+++ b/src/components/chat/ChatAi.ts
@@ -0,0 +1,220 @@
+const Api = {
+ CHAT_AI_URL: 'http://localhost:8180/chat/generateStreamFlex'
+};
+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;
+
+ private encoder = new TextEncoder();
+
+ private stream: ReadableStream;
+
+ private error: boolean;
+
+ private currentPhase: 'reasoning' | 'content' = 'reasoning';
+
+ constructor(
+ private data: {
+ reasoning: string; // 推理内容
+ content: string; // 正式内容
+ },
+ private delay: number = 100,
+ error = false,
+ ) {
+ 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 {
+ return new Promise((resolve) => {
+ resolve(getAi(_msg));
+ });
+ }
+
+ // 封装SSE连接
+ connectSSE = (url, params, onMessage, onError) => {
+ // 构建带参数的URL
+ const queryString = Object.keys(params)
+ .map((key, value) => `${encodeURIComponent(key)}=${params[key].message}`)
+ .join('&');
+
+ const API_BASE_URL = 'http://localhost:8180';
+ const fullUrl = `${API_BASE_URL}${url}?${queryString}`;
+
+ // 创建EventSource
+ const eventSource = new EventSource(fullUrl);
+
+ eventSource.onmessage = (event) => {
+ const { data } = event;
+
+ // 检查是否是特殊标记
+ if (data === '[DONE]') {
+ if (onMessage) onMessage('[DONE]');
+ } else {
+ // 处理普通消息
+ if (onMessage) onMessage(data);
+ }
+ };
+
+ eventSource.onerror = (error) => {
+ if (onError) onError(error);
+ eventSource.close();
+ };
+
+ // 返回eventSource实例,以便后续可以关闭连接
+ return eventSource;
+ };
+
+ // AI超级智能体聊天
+ chatWithManus = (message) => {
+ return this.connectSSE('/chat/generateStreamFlex', { message });
+ };
+}
+
+export const fetchSSE = async (options: FetchSSEOptions = {}) => {
+ const { success, fail, complete, url } = options;
+ // fetch请求流式接口url,需传入接口url和参数
+ const responsePromise = fetch(url).catch((e) => {
+ const msg = e.toString() || '流式接口异常';
+ complete?.(false, msg);
+ return Promise.reject(e); // 确保错误能够被后续的.catch()捕获
+ });
+
+ responsePromise
+ .then((response) => {
+ if (!response?.ok) {
+ complete?.(false, response.statusText);
+ fail?.();
+ throw new Error('Request failed'); // 抛出错误以便链式调用中的下一个.catch()处理
+ }
+ const reader = response.body.getReader();
+ const decoder = new TextDecoder();
+ if (!reader) throw new Error('No reader available');
+
+ const bufferArr: string[] = [];
+ let dataText = ''; // 记录数据
+ const event: SSEEvent = { type: null, data: null };
+
+ async function processText({ done, value }: ReadableStreamReadResult): Promise {
+ if (done) {
+ complete?.(true);
+ return Promise.resolve();
+ }
+ const chunk = decoder.decode(value);
+ const buffers = chunk.toString().replaceAll(' ', ' ').split(/\r?\n/);
+ bufferArr.push(...buffers);
+ const 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) {
+ const jsonData = JSON.parse(JSON.stringify(event));
+ console.log('流式数据解析结果:', jsonData);
+ // 回调更新数据
+ success(jsonData);
+ event.type = null;
+ event.data = null;
+ }
+ bufferArr.splice(i, 1);
+ }
+ return reader.read().then(processText);
+ }
+
+ return reader.read().then(processText);
+ })
+ .catch(() => {
+ // 处理整个链式调用过程中发生的任何错误
+ fail?.();
+ });
+};
\ No newline at end of file
diff --git a/src/components/chat/ChatAi.vue b/src/components/chat/ChatAi.vue
new file mode 100644
index 0000000..4108827
--- /dev/null
+++ b/src/components/chat/ChatAi.vue
@@ -0,0 +1,469 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// 第二种重写样式 定义一个啥也不带的 style
+
+
diff --git a/src/components/common/BackToTop.vue b/src/components/common/BackToTop.vue
index 4d8fd16..419f1e0 100644
--- a/src/components/common/BackToTop.vue
+++ b/src/components/common/BackToTop.vue
@@ -8,6 +8,7 @@ const isVisible = ref(false)
const handleScroll = () => {
// 当滚动超过300px时显示按钮
isVisible.value = window.scrollY > 300
+
}
// 回到顶部函数
diff --git a/src/main.ts b/src/main.ts
index bbce151..ae1f9c5 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -9,8 +9,10 @@ import router from './router/index.ts' // 确保路径正确
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)
// 必须先 use(router),再挂载
app.use(router)
@@ -20,6 +22,7 @@ app.use(createPinia())
// 使用TDesign
app.use(TDesign)
+app.use(TDesignChat)
app.mount('#app')
diff --git a/src/views/HomePage.vue b/src/views/HomePage.vue
index 76bdd6c..fda6df5 100644
--- a/src/views/HomePage.vue
+++ b/src/views/HomePage.vue
@@ -27,6 +27,8 @@
+
+
@@ -37,6 +39,8 @@ 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保持一致)