Compare commits
16 Commits
main
...
release-xg
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5f0a21f96 | ||
|
|
1be7759250 | ||
|
|
36cd9aeaf0 | ||
|
|
e3f81d4f7b | ||
|
|
bfde0250e3 | ||
|
|
3b280eb579 | ||
|
|
4bf7369f92 | ||
|
|
beba6b81ce | ||
|
|
89c261e13f | ||
|
|
afdcae1e8e | ||
|
|
b198d73f9e | ||
|
|
9360d598b0 | ||
|
|
7a47b4d7ab | ||
|
|
55502295ca | ||
|
|
bb7fea71f1 | ||
|
|
006aa04a7f |
@ -3,7 +3,7 @@ run-name: ${{ gitea.actor }} is testing out Gitea Actions 🚀
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main**'
|
||||
- 'release-xg'
|
||||
|
||||
env:
|
||||
BUILD: staging
|
||||
@ -35,12 +35,12 @@ jobs:
|
||||
uses: https://gitea.yantootech.com/neil/build-push-action@v6
|
||||
with:
|
||||
push: true
|
||||
tags: hub.kce.ksyun.com/yantoo/yantoo-repository/baidu-digital-human-app:${{ gitea.run_id }}
|
||||
tags: hub.kce.ksyun.com/yantoo/yantoo-repository/xg-digital-human-app:${{ gitea.run_id }}
|
||||
- name: Install
|
||||
run: |
|
||||
helm upgrade --install baidu-digital-human-app ./.gitea/charts \
|
||||
helm upgrade --install xg-digital-human-app ./.gitea/charts \
|
||||
--namespace digital-human-prod \
|
||||
--create-namespace \
|
||||
--set image.repository=hub.kce.ksyun.com/yantoo/yantoo-repository/baidu-digital-human-app \
|
||||
--set image.repository=hub.kce.ksyun.com/yantoo/yantoo-repository/xg-digital-human-app \
|
||||
--set image.tag=${{ gitea.run_id }}
|
||||
- run: echo "🍏 This job's status is ${{ job.status }}."
|
||||
|
||||
519
package-lock.json
generated
519
package-lock.json
generated
@ -11,6 +11,7 @@
|
||||
"@bddh/starling-realtime-client": "^2.0.9",
|
||||
"crypto-js": "^4.2.0",
|
||||
"element-plus": "^2.9.7",
|
||||
"opencc-js": "^1.4.0",
|
||||
"uuid": "^9.0.1",
|
||||
"vue": "^3.5.13"
|
||||
},
|
||||
@ -2010,24 +2011,6 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-arm64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz",
|
||||
"integrity": "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
|
||||
@ -2045,24 +2028,6 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-arm64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz",
|
||||
"integrity": "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
|
||||
@ -2080,24 +2045,6 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openharmony-arm64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz",
|
||||
"integrity": "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openharmony"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
|
||||
@ -4546,6 +4493,12 @@
|
||||
"node": ">=12.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/opencc-js": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmmirror.com/opencc-js/-/opencc-js-1.4.0.tgz",
|
||||
"integrity": "sha512-SEb4huI5+lT4CFPA4sgs+NAOOgpcmdHZ+vYxhRLR1bQWIt6VRwpFPjWgTJAWAg1MviDcXTBe5zVoUOVxVxNK8w==",
|
||||
"license": "MIT AND Apache-2.0"
|
||||
},
|
||||
"node_modules/path-parse": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz",
|
||||
@ -5258,420 +5211,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vitest/node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz",
|
||||
"integrity": "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"aix"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/vitest/node_modules/@esbuild/android-arm": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.28.0.tgz",
|
||||
"integrity": "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/vitest/node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz",
|
||||
"integrity": "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/vitest/node_modules/@esbuild/android-x64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.28.0.tgz",
|
||||
"integrity": "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/vitest/node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz",
|
||||
"integrity": "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/vitest/node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz",
|
||||
"integrity": "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/vitest/node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz",
|
||||
"integrity": "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/vitest/node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz",
|
||||
"integrity": "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/vitest/node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz",
|
||||
"integrity": "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/vitest/node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz",
|
||||
"integrity": "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/vitest/node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz",
|
||||
"integrity": "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/vitest/node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz",
|
||||
"integrity": "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/vitest/node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz",
|
||||
"integrity": "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/vitest/node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz",
|
||||
"integrity": "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/vitest/node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz",
|
||||
"integrity": "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/vitest/node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz",
|
||||
"integrity": "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/vitest/node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz",
|
||||
"integrity": "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/vitest/node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz",
|
||||
"integrity": "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/vitest/node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz",
|
||||
"integrity": "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/vitest/node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz",
|
||||
"integrity": "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"sunos"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/vitest/node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz",
|
||||
"integrity": "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/vitest/node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz",
|
||||
"integrity": "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/vitest/node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz",
|
||||
"integrity": "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/vitest/node_modules/@vitest/mocker": {
|
||||
"version": "4.1.8",
|
||||
"resolved": "https://registry.npmmirror.com/@vitest/mocker/-/mocker-4.1.8.tgz",
|
||||
@ -5699,50 +5238,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vitest/node_modules/esbuild": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.28.0.tgz",
|
||||
"integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.28.0",
|
||||
"@esbuild/android-arm": "0.28.0",
|
||||
"@esbuild/android-arm64": "0.28.0",
|
||||
"@esbuild/android-x64": "0.28.0",
|
||||
"@esbuild/darwin-arm64": "0.28.0",
|
||||
"@esbuild/darwin-x64": "0.28.0",
|
||||
"@esbuild/freebsd-arm64": "0.28.0",
|
||||
"@esbuild/freebsd-x64": "0.28.0",
|
||||
"@esbuild/linux-arm": "0.28.0",
|
||||
"@esbuild/linux-arm64": "0.28.0",
|
||||
"@esbuild/linux-ia32": "0.28.0",
|
||||
"@esbuild/linux-loong64": "0.28.0",
|
||||
"@esbuild/linux-mips64el": "0.28.0",
|
||||
"@esbuild/linux-ppc64": "0.28.0",
|
||||
"@esbuild/linux-riscv64": "0.28.0",
|
||||
"@esbuild/linux-s390x": "0.28.0",
|
||||
"@esbuild/linux-x64": "0.28.0",
|
||||
"@esbuild/netbsd-arm64": "0.28.0",
|
||||
"@esbuild/netbsd-x64": "0.28.0",
|
||||
"@esbuild/openbsd-arm64": "0.28.0",
|
||||
"@esbuild/openbsd-x64": "0.28.0",
|
||||
"@esbuild/openharmony-arm64": "0.28.0",
|
||||
"@esbuild/sunos-x64": "0.28.0",
|
||||
"@esbuild/win32-arm64": "0.28.0",
|
||||
"@esbuild/win32-ia32": "0.28.0",
|
||||
"@esbuild/win32-x64": "0.28.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vitest/node_modules/estree-walker": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-3.0.3.tgz",
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
"@bddh/starling-realtime-client": "^2.0.9",
|
||||
"crypto-js": "^4.2.0",
|
||||
"element-plus": "^2.9.7",
|
||||
"opencc-js": "^1.4.0",
|
||||
"uuid": "^9.0.1",
|
||||
"vue": "^3.5.13"
|
||||
},
|
||||
|
||||
1787
src/App.vue
1787
src/App.vue
File diff suppressed because it is too large
Load Diff
BIN
src/GP.webp
Normal file
BIN
src/GP.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.5 KiB |
BIN
src/assets/avatar.png
Normal file
BIN
src/assets/avatar.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 281 KiB |
BIN
src/assets/background.jpg
Normal file
BIN
src/assets/background.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.6 MiB |
@ -1,60 +1,7 @@
|
||||
<template>
|
||||
<div class="subtitle-panel">
|
||||
<!-- 字幕模式下拉框 -->
|
||||
<div class="subtitle-header">
|
||||
<div class="dropdown-wrapper" :class="{ open: dropdownOpen }">
|
||||
<button class="dropdown-trigger" @click="dropdownOpen = !dropdownOpen">
|
||||
<span class="dropdown-label">{{ modeLabel }}</span>
|
||||
<svg
|
||||
class="dropdown-arrow"
|
||||
viewBox="0 0 24 24"
|
||||
width="14"
|
||||
height="14"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<polyline points="6 9 12 15 18 9" />
|
||||
</svg>
|
||||
</button>
|
||||
<Transition name="dropdown">
|
||||
<div v-if="dropdownOpen" class="dropdown-menu">
|
||||
<button
|
||||
v-for="opt in modeOptions"
|
||||
:key="opt.value"
|
||||
class="dropdown-item"
|
||||
:class="{ active: currentMode === opt.value }"
|
||||
@click="selectMode(opt.value)"
|
||||
>
|
||||
<span class="item-icon">{{ opt.icon }}</span>
|
||||
<span class="item-label">{{ opt.label }}</span>
|
||||
<svg
|
||||
v-if="currentMode === opt.value"
|
||||
viewBox="0 0 24 24"
|
||||
width="14"
|
||||
height="14"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="3"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<polyline points="20 6 9 17 4 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 模式1:仅AI字幕 -->
|
||||
<div
|
||||
v-if="currentMode === 'ai-only'"
|
||||
class="subtitle-content ai-only-mode"
|
||||
ref="scrollContainerRef"
|
||||
>
|
||||
<!-- AI字幕 -->
|
||||
<div class="subtitle-content ai-only-mode" ref="scrollContainerRef">
|
||||
<div v-if="aiSubtitle" class="ai-subtitle-bar" ref="aiSubtitleBarRef">
|
||||
<div
|
||||
class="ai-subtitle-text"
|
||||
@ -96,217 +43,19 @@
|
||||
d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"
|
||||
/>
|
||||
</svg>
|
||||
等待AI回复...
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 模式2:对话气泡 -->
|
||||
<div
|
||||
v-if="currentMode === 'bubble'"
|
||||
class="subtitle-content bubble-mode"
|
||||
ref="scrollContainerRef"
|
||||
>
|
||||
<div v-if="messages.length === 0" class="subtitle-placeholder">
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
width="20"
|
||||
height="20"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
>
|
||||
<path
|
||||
d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"
|
||||
/>
|
||||
</svg>
|
||||
等待对话开始...
|
||||
</div>
|
||||
<div class="chat-list" ref="chatListRef">
|
||||
<div
|
||||
v-for="msg in messages"
|
||||
:key="msg.id"
|
||||
:class="[
|
||||
'chat-bubble',
|
||||
msg.role === 'user' ? 'bubble-user' : 'bubble-ai',
|
||||
]"
|
||||
>
|
||||
<div class="bubble-body">
|
||||
<div
|
||||
class="bubble-text"
|
||||
:class="{
|
||||
collapsed: isCollapsed(msg.id) && isLongText(msg.content),
|
||||
'text-user': msg.role === 'user',
|
||||
'text-ai': msg.role === 'ai',
|
||||
}"
|
||||
:ref="(el) => setMsgRef(msg.id, el)"
|
||||
>
|
||||
{{ msg.content }}
|
||||
</div>
|
||||
<button
|
||||
v-if="isLongText(msg.content)"
|
||||
class="expand-btn"
|
||||
@click="toggleCollapse(msg.id)"
|
||||
>
|
||||
{{ isCollapsed(msg.id) ? "展开全部" : "收起" }}
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
width="12"
|
||||
height="12"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
:style="{
|
||||
transform: isCollapsed(msg.id)
|
||||
? 'rotate(0deg)'
|
||||
: 'rotate(180deg)',
|
||||
transition: 'transform 0.2s',
|
||||
}"
|
||||
>
|
||||
<polyline points="6 9 12 15 18 9" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 模式3:合并气泡 -->
|
||||
<div
|
||||
v-if="currentMode === 'merged'"
|
||||
class="subtitle-content merged-mode"
|
||||
ref="scrollContainerRef"
|
||||
>
|
||||
<div v-if="messages.length === 0" class="subtitle-placeholder">
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
width="20"
|
||||
height="20"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
>
|
||||
<path
|
||||
d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"
|
||||
/>
|
||||
</svg>
|
||||
等待对话开始...
|
||||
</div>
|
||||
<div class="merged-list" ref="mergedListRef">
|
||||
<div
|
||||
v-for="(pair, idx) in pairedMessages"
|
||||
:key="idx"
|
||||
class="merged-bubble"
|
||||
>
|
||||
<div v-if="pair.user" class="merged-user-section">
|
||||
<div
|
||||
class="merged-text text-user"
|
||||
:class="{
|
||||
collapsed:
|
||||
isCollapsed(pair.user.id) && isLongText(pair.user.content),
|
||||
}"
|
||||
>
|
||||
{{ pair.user.content }}
|
||||
</div>
|
||||
<button
|
||||
v-if="isLongText(pair.user.content)"
|
||||
class="expand-btn"
|
||||
@click="toggleCollapse(pair.user.id)"
|
||||
>
|
||||
{{ isCollapsed(pair.user.id) ? "展开全部" : "收起" }}
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
width="12"
|
||||
height="12"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
:style="{
|
||||
transform: isCollapsed(pair.user.id)
|
||||
? 'rotate(0deg)'
|
||||
: 'rotate(180deg)',
|
||||
transition: 'transform 0.2s',
|
||||
}"
|
||||
>
|
||||
<polyline points="6 9 12 15 18 9" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="pair.ai" class="merged-ai-section">
|
||||
<div
|
||||
class="merged-text text-ai"
|
||||
:class="{
|
||||
collapsed:
|
||||
isCollapsed(pair.ai.id) && isLongText(pair.ai.content),
|
||||
}"
|
||||
>
|
||||
{{ pair.ai.content }}
|
||||
</div>
|
||||
<button
|
||||
v-if="isLongText(pair.ai.content)"
|
||||
class="expand-btn"
|
||||
@click="toggleCollapse(pair.ai.id)"
|
||||
>
|
||||
{{ isCollapsed(pair.ai.id) ? "展开全部" : "收起" }}
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
width="12"
|
||||
height="12"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
:style="{
|
||||
transform: isCollapsed(pair.ai.id)
|
||||
? 'rotate(0deg)'
|
||||
: 'rotate(180deg)',
|
||||
transition: 'transform 0.2s',
|
||||
}"
|
||||
>
|
||||
<polyline points="6 9 12 15 18 9" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
等待AI回覆...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
ref,
|
||||
computed,
|
||||
watch,
|
||||
nextTick,
|
||||
onMounted,
|
||||
onBeforeUnmount,
|
||||
} from "vue";
|
||||
import type { ChatMessage, SubtitleMode } from "@/types";
|
||||
import { ref, watch, nextTick, onMounted, onBeforeUnmount } from "vue";
|
||||
|
||||
const props = defineProps<{
|
||||
mode: SubtitleMode;
|
||||
aiSubtitle: string;
|
||||
userSubtitle: string;
|
||||
messages: ChatMessage[];
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
"update:mode": [value: SubtitleMode];
|
||||
}>();
|
||||
|
||||
const currentMode = computed({
|
||||
get: () => props.mode,
|
||||
set: (val: SubtitleMode) => emit("update:mode", val),
|
||||
});
|
||||
|
||||
const dropdownOpen = ref(false);
|
||||
// 滚动容器ref:指向 .subtitle-content(实际可滚动容器)
|
||||
const scrollContainerRef = ref<HTMLElement | null>(null);
|
||||
const aiSubtitleBarRef = ref<HTMLElement | null>(null);
|
||||
@ -316,20 +65,59 @@ const aiSubtitleTextRef = ref<HTMLElement | null>(null);
|
||||
const aiFontSize = ref(18);
|
||||
const aiTextOverflow = ref(false);
|
||||
|
||||
// 计算AI字幕字号:根据文本长度动态调整
|
||||
// 检测是否为 1440×2560 目标分辨率
|
||||
const isTargetResolution = ref(false);
|
||||
|
||||
/**
|
||||
* 检测当前视口是否为 1440×2560 分辨率
|
||||
* 使用 window.innerWidth 和 innerHeight 精确匹配
|
||||
*/
|
||||
const checkResolution = () => {
|
||||
const TARGET_WIDTH = 1440;
|
||||
const TARGET_HEIGHT = 2560;
|
||||
console.log("------------", window.innerWidth, window.innerHeight);
|
||||
isTargetResolution.value =
|
||||
(window.innerWidth === TARGET_WIDTH &&
|
||||
window.innerHeight === TARGET_HEIGHT) ||
|
||||
(window.innerWidth > 1000 && window.innerHeight > 1680);
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取目标分辨率下的字号基准值
|
||||
* 1440×2560 下使用更大的字号以确保清晰可读
|
||||
*/
|
||||
const getTargetFontSizeBase = (textLength: number): number => {
|
||||
if (textLength <= 50) return 36;
|
||||
if (textLength <= 100) return 32;
|
||||
if (textLength <= 200) return 28;
|
||||
if (textLength <= 400) return 24;
|
||||
return 22;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取普通分辨率下的字号基准值
|
||||
*/
|
||||
const getNormalFontSizeBase = (textLength: number): number => {
|
||||
if (textLength <= 50) return 18;
|
||||
if (textLength <= 100) return 16;
|
||||
if (textLength <= 200) return 14;
|
||||
if (textLength <= 400) return 13;
|
||||
return 12;
|
||||
};
|
||||
|
||||
// 计算AI字幕字号:根据分辨率和文本长度动态调整
|
||||
const computeAIFontSize = () => {
|
||||
const text = props.aiSubtitle;
|
||||
console.log("------------", text);
|
||||
if (!text) {
|
||||
aiFontSize.value = 18;
|
||||
aiFontSize.value = isTargetResolution.value ? 36 : 18;
|
||||
return;
|
||||
}
|
||||
|
||||
const len = text.length;
|
||||
if (len <= 50) aiFontSize.value = 18;
|
||||
else if (len <= 100) aiFontSize.value = 16;
|
||||
else if (len <= 200) aiFontSize.value = 14;
|
||||
else if (len <= 400) aiFontSize.value = 13;
|
||||
else aiFontSize.value = 12;
|
||||
aiFontSize.value = isTargetResolution.value
|
||||
? getTargetFontSizeBase(len)
|
||||
: getNormalFontSizeBase(len);
|
||||
};
|
||||
|
||||
// 检测AI字幕是否溢出
|
||||
@ -352,111 +140,32 @@ const scrollAIToBottom = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// ===== 长文本折叠/展开 =====
|
||||
const COLLAPSE_THRESHOLD = 120; // 超过120字符视为长文本
|
||||
const collapsedIds = ref<Set<string>>(new Set());
|
||||
const msgRefs = new Map<string, HTMLElement>();
|
||||
|
||||
const isLongText = (text: string): boolean => text.length > COLLAPSE_THRESHOLD;
|
||||
|
||||
const isCollapsed = (id: string): boolean => collapsedIds.value.has(id);
|
||||
|
||||
const toggleCollapse = (id: string) => {
|
||||
if (collapsedIds.value.has(id)) {
|
||||
collapsedIds.value.delete(id);
|
||||
} else {
|
||||
collapsedIds.value.add(id);
|
||||
}
|
||||
// 触发响应式更新
|
||||
collapsedIds.value = new Set(collapsedIds.value);
|
||||
};
|
||||
|
||||
const setMsgRef = (id: string, el: any) => {
|
||||
if (el) msgRefs.set(id, el as HTMLElement);
|
||||
};
|
||||
|
||||
// ===== 下拉框 =====
|
||||
const modeOptions = [
|
||||
{ value: "ai-only" as SubtitleMode, label: "AI字幕", icon: "💬" },
|
||||
{ value: "bubble" as SubtitleMode, label: "对话气泡", icon: "💭" },
|
||||
{ value: "merged" as SubtitleMode, label: "合并气泡", icon: "📋" },
|
||||
];
|
||||
|
||||
const modeLabel = computed(() => {
|
||||
return (
|
||||
modeOptions.find((o) => o.value === currentMode.value)?.label || "AI字幕"
|
||||
);
|
||||
});
|
||||
|
||||
const selectMode = (val: SubtitleMode) => {
|
||||
currentMode.value = val;
|
||||
dropdownOpen.value = false;
|
||||
};
|
||||
|
||||
const handleClickOutside = (e: MouseEvent) => {
|
||||
const target = e.target as HTMLElement;
|
||||
if (!target.closest(".dropdown-wrapper")) {
|
||||
dropdownOpen.value = false;
|
||||
/**
|
||||
* 处理视口尺寸变化:重新检测分辨率并更新字号
|
||||
*/
|
||||
const handleResize = () => {
|
||||
const wasTarget = isTargetResolution.value;
|
||||
checkResolution();
|
||||
// 如果分辨率状态变化,重新计算字号
|
||||
if (wasTarget !== isTargetResolution.value) {
|
||||
computeAIFontSize();
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener("click", handleClickOutside);
|
||||
// 初始检测分辨率
|
||||
checkResolution();
|
||||
computeAIFontSize();
|
||||
// 监听视口尺寸变化
|
||||
window.addEventListener("resize", handleResize);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
document.removeEventListener("click", handleClickOutside);
|
||||
});
|
||||
|
||||
// ===== 消息配对 =====
|
||||
const pairedMessages = computed(() => {
|
||||
const pairs: { user: ChatMessage | null; ai: ChatMessage | null }[] = [];
|
||||
let currentPair: { user: ChatMessage | null; ai: ChatMessage | null } = {
|
||||
user: null,
|
||||
ai: null,
|
||||
};
|
||||
|
||||
for (const msg of props.messages) {
|
||||
if (msg.role === "user") {
|
||||
if (currentPair.user) {
|
||||
pairs.push(currentPair);
|
||||
currentPair = { user: msg, ai: null };
|
||||
} else {
|
||||
currentPair.user = msg;
|
||||
}
|
||||
} else {
|
||||
currentPair.ai = msg;
|
||||
pairs.push(currentPair);
|
||||
currentPair = { user: null, ai: null };
|
||||
}
|
||||
}
|
||||
if (currentPair.user || currentPair.ai) {
|
||||
pairs.push(currentPair);
|
||||
}
|
||||
return pairs;
|
||||
window.removeEventListener("resize", handleResize);
|
||||
});
|
||||
|
||||
// ===== 监听变化 =====
|
||||
|
||||
// 模式切换时:立即刷新内容并滚动到底部
|
||||
watch(
|
||||
() => props.mode,
|
||||
() => {
|
||||
computeAIFontSize();
|
||||
nextTick(() => {
|
||||
checkAIOverflow();
|
||||
scrollToBottom();
|
||||
// AI模式切换后确保字幕滚动到底部
|
||||
if (props.mode === "ai-only" && aiSubtitleBarRef.value) {
|
||||
aiSubtitleBarRef.value.scrollTo({
|
||||
top: aiSubtitleBarRef.value.scrollHeight,
|
||||
behavior: "instant" as ScrollBehavior,
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
// AI字幕变化时:自适应字号 + 检测溢出 + 自动滚动
|
||||
watch(
|
||||
() => props.aiSubtitle,
|
||||
@ -474,55 +183,6 @@ watch(
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
// 消息变化时:自动滚动到底部 + 新消息默认折叠
|
||||
watch(
|
||||
() => props.messages.length,
|
||||
(newLen, oldLen) => {
|
||||
// 新增的消息默认折叠
|
||||
if (newLen > (oldLen ?? 0)) {
|
||||
for (let i = oldLen ?? 0; i < newLen; i++) {
|
||||
const msg = props.messages[i];
|
||||
if (msg && isLongText(msg.content)) {
|
||||
collapsedIds.value.add(msg.id);
|
||||
}
|
||||
}
|
||||
collapsedIds.value = new Set(collapsedIds.value);
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
scrollToBottom();
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
// 流式内容更新时:自动滚动到底部(消息数量不变但内容变化)
|
||||
watch(
|
||||
() => {
|
||||
const msgs = props.messages;
|
||||
if (msgs.length === 0) return "";
|
||||
// 监听最后一条消息的内容变化,用于流式更新时自动滚动
|
||||
return msgs[msgs.length - 1].content;
|
||||
},
|
||||
() => {
|
||||
nextTick(() => {
|
||||
scrollToBottom();
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* 将对话列表滚动到底部
|
||||
* 兼容不同内容量:内容少时无需滚动,内容超出可见区域时平滑滚动到底部
|
||||
*/
|
||||
const scrollToBottom = () => {
|
||||
const el = scrollContainerRef.value;
|
||||
if (!el) return;
|
||||
el.scrollTo({
|
||||
top: el.scrollHeight,
|
||||
behavior: "smooth",
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@ -532,108 +192,6 @@ const scrollToBottom = () => {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* ===== 下拉框 ===== */
|
||||
.subtitle-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
|
||||
margin-bottom: 8px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.dropdown-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dropdown-trigger {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 14px;
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 10px;
|
||||
color: rgba(255, 255, 255, 0.75);
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-color: rgba(255, 255, 255, 0.18);
|
||||
}
|
||||
|
||||
.dropdown-wrapper.open & {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-color: rgba(108, 140, 255, 0.4);
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-arrow {
|
||||
transition: transform 0.2s ease;
|
||||
.dropdown-wrapper.open & {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
position: absolute;
|
||||
bottom: calc(100% + 8px);
|
||||
left: 0;
|
||||
min-width: 180px;
|
||||
background: rgba(20, 25, 50, 0.97);
|
||||
backdrop-filter: blur(12px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||
border-radius: 12px;
|
||||
padding: 6px;
|
||||
z-index: 100;
|
||||
box-shadow: 0 -8px 32px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
width: 100%;
|
||||
padding: 10px 14px;
|
||||
border-radius: 8px;
|
||||
border: none;
|
||||
background: none;
|
||||
color: rgba(255, 255, 255, 0.65);
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
transition: all 0.15s;
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
color: #fff;
|
||||
}
|
||||
&.active {
|
||||
background: rgba(108, 140, 255, 0.15);
|
||||
color: #6c8cff;
|
||||
}
|
||||
.item-icon {
|
||||
font-size: 14px;
|
||||
}
|
||||
.item-label {
|
||||
flex: 1;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-enter-active,
|
||||
.dropdown-leave-active {
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.dropdown-enter-from,
|
||||
.dropdown-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(6px) scale(0.96);
|
||||
}
|
||||
|
||||
/* ===== 字幕内容 ===== */
|
||||
.subtitle-content {
|
||||
flex: 1;
|
||||
@ -672,7 +230,7 @@ const scrollToBottom = () => {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* ===== 模式1:AI字幕 ===== */
|
||||
/* ===== AI字幕 ===== */
|
||||
.ai-only-mode {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
@ -738,170 +296,40 @@ const scrollToBottom = () => {
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== 模式2:对话气泡 ===== */
|
||||
.bubble-mode {
|
||||
.chat-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 2px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-bubble {
|
||||
animation: bubbleSlide 0.35s ease;
|
||||
|
||||
&.bubble-user {
|
||||
.bubble-body {
|
||||
background: rgba(240, 242, 255, 0.92);
|
||||
border-color: rgba(79, 110, 247, 0.12);
|
||||
border-radius: 14px 14px 4px 14px;
|
||||
margin-left: 24px;
|
||||
}
|
||||
/* ===== 1440×2560 分辨率专用样式 ===== */
|
||||
@media (min-width: 1000px) and (min-height: 1680px) {
|
||||
.subtitle-placeholder {
|
||||
font-size: 24px;
|
||||
line-height: 1.75;
|
||||
letter-spacing: 0.5px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
&.bubble-ai {
|
||||
.bubble-body {
|
||||
background: rgba(20, 30, 50, 0.85);
|
||||
border-color: rgba(52, 211, 153, 0.12);
|
||||
border-radius: 14px 14px 14px 4px;
|
||||
margin-right: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes bubbleSlide {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(10px) scale(0.96);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.bubble-body {
|
||||
padding: 8px 12px;
|
||||
border: 1px solid;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.bubble-text {
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
word-break: break-word;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.35s ease;
|
||||
|
||||
&.text-user {
|
||||
color: #1a1a1a;
|
||||
text-shadow: none;
|
||||
/* AI字幕:字号由 JavaScript 动态控制,此处仅设置辅助样式 */
|
||||
.ai-subtitle-text {
|
||||
line-height: 1.75;
|
||||
letter-spacing: 0.6px;
|
||||
font-weight: 400;
|
||||
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
|
||||
&.text-ai {
|
||||
color: #ffffff;
|
||||
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
|
||||
/* 字幕容器:背景与边框适配大字号 */
|
||||
.ai-subtitle-bar {
|
||||
padding: 18px 24px;
|
||||
border-radius: 12px;
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(108, 140, 255, 0.12) 0%,
|
||||
rgba(79, 110, 247, 0.06) 100%
|
||||
);
|
||||
border: 1px solid rgba(108, 140, 255, 0.15);
|
||||
}
|
||||
|
||||
&.collapsed {
|
||||
max-height: 4.8em;
|
||||
position: relative;
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 2em;
|
||||
background: linear-gradient(transparent, rgba(10, 14, 30, 0.5));
|
||||
pointer-events: none;
|
||||
border-radius: 0 0 8px 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== 展开/收起按钮 ===== */
|
||||
.expand-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
margin-top: 6px;
|
||||
padding: 3px 10px;
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 6px;
|
||||
color: rgba(255, 255, 255, 0.55);
|
||||
font-size: 11px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
border-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== 模式3:合并气泡 ===== */
|
||||
.merged-mode {
|
||||
.merged-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 2px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.merged-bubble {
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
border: 1px solid rgba(255, 255, 255, 0.04);
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
animation: bubbleSlide 0.35s ease;
|
||||
}
|
||||
|
||||
.merged-user-section {
|
||||
padding: 8px 12px;
|
||||
border-bottom: 1px solid rgba(79, 110, 247, 0.08);
|
||||
}
|
||||
|
||||
.merged-ai-section {
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
.merged-text {
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
word-break: break-word;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.35s ease;
|
||||
|
||||
&.text-user {
|
||||
color: #303030ab;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
&.text-ai {
|
||||
color: #ffffff;
|
||||
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
&.collapsed {
|
||||
max-height: 4.8em;
|
||||
position: relative;
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 2em;
|
||||
background: linear-gradient(transparent, rgba(10, 14, 30, 0.5));
|
||||
pointer-events: none;
|
||||
}
|
||||
/* 滚动指示器:尺寸适配 */
|
||||
.scroll-hint {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -4,18 +4,27 @@ import RealTimeHuman from "@bddh/starling-realtime-client";
|
||||
import { checkPlayUnMute } from "@/utils/audioUtils";
|
||||
import { SilenceDetector } from "@/utils/SilenceDetector";
|
||||
import { TokenManager } from "@/utils/tokenManager";
|
||||
import type { ChatMessage, SubtitleMode } from "@/types";
|
||||
import type { ChatMessage } from "@/types";
|
||||
|
||||
const DEFAULT_TEXT =
|
||||
"你好,我是东航小云,请问有什么需要帮忙的吗?";
|
||||
const DEFAULT_TEXT = "你好!我是皮特教授,歡迎向我提問任何有關電池的問題。";
|
||||
const OPENING_TTS_DELAY_MS = 200;
|
||||
const SILENCE_TIMEOUT_MS = 5000;
|
||||
// 最短有效语音时长(秒),低于此时长视为无效输入
|
||||
const MIN_VALID_DURATION_SEC = 0.5;
|
||||
// 播报超时保护(毫秒):如果FINISHED回调未触发,自动恢复状态
|
||||
const BROADCAST_TIMEOUT_MS = 60000;
|
||||
const APPID = "i-sfkmd7h8ex6nd";
|
||||
const APP_KEY = "hs6b9nu9b080ap9hswgp";
|
||||
const APPID = "i-sfyrkdr0ubepa";
|
||||
const APP_KEY = "72vpdansbpg8nxjbxzhp";
|
||||
// 打断词列表:识别到这些词时自动打断播报
|
||||
const INTERRUPT_KEYWORDS = [
|
||||
"稍等一下",
|
||||
"停一下",
|
||||
"抱歉打断一下",
|
||||
"不好意思",
|
||||
"对不起",
|
||||
"停",
|
||||
"闭嘴",
|
||||
];
|
||||
|
||||
/**
|
||||
* 检测文本是否包含实质内容(去除空白和标点后仍有字符)
|
||||
@ -29,6 +38,17 @@ const hasSubstantialContent = (text: string): boolean => {
|
||||
return cleaned.length > 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* 检测文本是否包含打断词
|
||||
*/
|
||||
const containsInterruptKeyword = (text: string): boolean => {
|
||||
const cleaned = text.replace(
|
||||
/[\s\u3000.,!?;:,。!?;:、…—\-_·""''()()\[\]【】《》<>]/g,
|
||||
"",
|
||||
);
|
||||
return INTERRUPT_KEYWORDS.some((keyword) => cleaned.includes(keyword));
|
||||
};
|
||||
|
||||
enum ReadyState {
|
||||
UNINSTANTIATED = -1,
|
||||
CONNECTING = 0,
|
||||
@ -114,7 +134,6 @@ export function useDigitalHuman(options?: {
|
||||
const isBroadcasting = ref(false);
|
||||
|
||||
// 字幕相关
|
||||
const subtitleMode = ref<SubtitleMode>("ai-only");
|
||||
const currentAISubtitle = ref("");
|
||||
const currentUserSubtitle = ref("");
|
||||
const chatMessages = ref<ChatMessage[]>([]);
|
||||
@ -141,6 +160,8 @@ export function useDigitalHuman(options?: {
|
||||
|
||||
// 播报超时保护计时器
|
||||
let broadcastTimeoutId: ReturnType<typeof setTimeout> | null = null;
|
||||
// 打断词检测时间戳:用于忽略打断后延迟到达的ASR结果
|
||||
let lastInterruptTime = 0;
|
||||
|
||||
const clearBroadcastTimeout = () => {
|
||||
if (broadcastTimeoutId !== null) {
|
||||
@ -164,19 +185,23 @@ export function useDigitalHuman(options?: {
|
||||
};
|
||||
|
||||
/**
|
||||
* 进入播报状态:静音麦克风 + 暂停静音检测器(互斥锁)
|
||||
* 进入播报状态
|
||||
* RTC模式下保持麦克风开启,允许ASR识别打断词
|
||||
*/
|
||||
const enterBroadcasting = () => {
|
||||
isBroadcasting.value = true;
|
||||
setPickPhase("broadcasting", "数字人开始播报");
|
||||
startBroadcastTimeout();
|
||||
// RTC模式:播报期间禁用拾音
|
||||
// RTC模式:播报期间保持麦克风开启,允许ASR识别打断词
|
||||
if (audioMode.value === "rtc") {
|
||||
humanInstanceRef.value?.muteMicrophone?.(true);
|
||||
isMuted.value = true;
|
||||
canTransfer.value = false;
|
||||
silenceDetector.pause();
|
||||
console.info("[播报] RTC模式:麦克风已静音,静音检测器已暂停");
|
||||
humanInstanceRef.value?.muteMicrophone?.(false);
|
||||
isMuted.value = false;
|
||||
canTransfer.value = true;
|
||||
const resumed = silenceDetector.resume();
|
||||
if (!resumed) {
|
||||
startSilenceDetection();
|
||||
}
|
||||
console.info("[播报] RTC模式:麦克风保持开启,允许ASR识别打断词");
|
||||
}
|
||||
console.info("[播报] 开始播报,等待FINISHED信号");
|
||||
};
|
||||
@ -306,9 +331,19 @@ export function useDigitalHuman(options?: {
|
||||
type,
|
||||
} = JSON.parse(content.body);
|
||||
if (type === "QUERY") {
|
||||
// 播报期间忽略ASR结果(双重守卫:麦克风已静音,但防止残余回调)
|
||||
// 播报期间检测打断词:识别到打断词后仅中断播报并恢复拾音,不调用LLM接口
|
||||
if (pickPhase.value === "broadcasting") {
|
||||
console.info("[状态机] 播报中收到ASR结果,忽略");
|
||||
const hasInterrupt = containsInterruptKeyword(subtitleContent);
|
||||
if (hasInterrupt) {
|
||||
console.info(
|
||||
`[打断词检测] 播报中识别到打断词: "${subtitleContent}",自动打断(仅中断播报,不调用LLM)`,
|
||||
);
|
||||
interrupt();
|
||||
} else {
|
||||
console.info(
|
||||
`[打断词检测] 播报中收到ASR结果: "${subtitleContent}",非打断词,忽略`,
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -321,6 +356,11 @@ export function useDigitalHuman(options?: {
|
||||
// idle 状态下收到 QUERY:必须验证内容有实质才允许状态转换
|
||||
// 环境噪音可能触发 ASR 产生空白/标点内容的 QUERY,不应进入识别
|
||||
if (pickPhase.value === "idle") {
|
||||
// 忽略打断后3秒内到达的延迟ASR结果(避免打断词被当作新输入)
|
||||
if (Date.now() - lastInterruptTime < 3000) {
|
||||
console.info("[状态机] 打断后延迟到达的ASR结果,忽略");
|
||||
break;
|
||||
}
|
||||
const hasSubstance = hasSubstantialContent(subtitleContent);
|
||||
if (!hasSubstance) {
|
||||
console.info(
|
||||
@ -496,8 +536,8 @@ export function useDigitalHuman(options?: {
|
||||
token: token.value,
|
||||
wrapperId: "human-wrapper",
|
||||
connectParams: {
|
||||
ttsPer: "CAP_4189",
|
||||
figureId: "3999826",
|
||||
ttsPer: "20007751",
|
||||
figureId: "4000821",
|
||||
resolutionHeight: 1920,
|
||||
resolutionWidth: 1080,
|
||||
inactiveDisconnectSec: 300,
|
||||
@ -514,7 +554,7 @@ export function useDigitalHuman(options?: {
|
||||
config.connectParams.pullAudioFromRtc = mode === "rtc";
|
||||
config.connectParams.pickAudioMode =
|
||||
mode === "btn" ? "pressButton" : "free";
|
||||
config.connectParams.enableInterrupt = enableInterruptRef.value;
|
||||
config.connectParams.enableInterrupt = false;
|
||||
}
|
||||
|
||||
humanInstanceRef.value = new RealTimeHuman(config);
|
||||
@ -590,6 +630,8 @@ export function useDigitalHuman(options?: {
|
||||
console.info("发送打断");
|
||||
clearBroadcastTimeout();
|
||||
await humanInstanceRef.value?.interrupt?.();
|
||||
// 记录打断时间戳,用于忽略打断后延迟到达的ASR结果
|
||||
lastInterruptTime = Date.now();
|
||||
// 打断后播报结束,恢复状态
|
||||
if (isBroadcasting.value) {
|
||||
isBroadcasting.value = false;
|
||||
@ -812,7 +854,6 @@ export function useDigitalHuman(options?: {
|
||||
// 播报状态
|
||||
isBroadcasting,
|
||||
// 字幕
|
||||
subtitleMode,
|
||||
currentAISubtitle,
|
||||
currentUserSubtitle,
|
||||
chatMessages,
|
||||
|
||||
@ -1,15 +1,26 @@
|
||||
import { ref } from "vue";
|
||||
import OpenCC from "opencc-js";
|
||||
|
||||
// 火山方舟 Chat Completions API 配置
|
||||
const LLM_API_URL =
|
||||
"https://ai.yantootech.com/polyhedron/api/ark/chat/completions";
|
||||
const DEFAULT_MODEL = "bot-20260605135355-68pth";
|
||||
|
||||
// ChatBase API 配置
|
||||
const CHATBASE_API_URL = "https://www.chatbase.co/api/v1/chat";
|
||||
const CHATBASE_API_KEY = "ca4616db-417c-4dce-8c65-b9909621f53e";
|
||||
const CHATBASE_CHATBOT_ID = "TRIe58TuVExBm6K0HKTfz";
|
||||
|
||||
interface LLMMessage {
|
||||
role: "system" | "user" | "assistant";
|
||||
content: string;
|
||||
}
|
||||
|
||||
interface ChatBaseMessage {
|
||||
content: string;
|
||||
role: "user";
|
||||
}
|
||||
|
||||
interface LLMChoice {
|
||||
index: number;
|
||||
message: {
|
||||
@ -41,7 +52,7 @@ export function useLLM() {
|
||||
|
||||
const SYSTEM_PROMPT: LLMMessage = {
|
||||
role: "system",
|
||||
content: "你是一个友好的数字人助手,请用简洁自然的语言回答用户的问题。",
|
||||
content: "你是一個友善的數位人助手,請用簡潔自然的語言回答使用者的問題。",
|
||||
};
|
||||
|
||||
const sendToLLM = async (userMessage: string): Promise<string> => {
|
||||
@ -92,7 +103,7 @@ export function useLLM() {
|
||||
|
||||
return aiReply;
|
||||
} catch (err: any) {
|
||||
const errorMsg = err.message || "请求失败";
|
||||
const errorMsg = err.message || "請求失敗";
|
||||
error.value = errorMsg;
|
||||
console.error("LLM请求错误:", err);
|
||||
throw err;
|
||||
@ -105,11 +116,65 @@ export function useLLM() {
|
||||
conversationHistory.value = [];
|
||||
};
|
||||
|
||||
const sendToChatBase = async (userMessage: string): Promise<string> => {
|
||||
if (!userMessage.trim()) return "";
|
||||
|
||||
isLoading.value = true;
|
||||
error.value = "";
|
||||
|
||||
try {
|
||||
const messages: ChatBaseMessage[] = [
|
||||
{ content: userMessage, role: "user" },
|
||||
];
|
||||
|
||||
const response = await fetch(CHATBASE_API_URL, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${CHATBASE_API_KEY}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
messages,
|
||||
chatbotId: CHATBASE_CHATBOT_ID,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP错误: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
// 将简体中文回复转换为繁体中文
|
||||
const rawText = data.text || "";
|
||||
const aiReply = OpenCC.Converter({ from: "cn", to: "tw" })(rawText);
|
||||
|
||||
conversationHistory.value.push({
|
||||
role: "user",
|
||||
content: userMessage,
|
||||
});
|
||||
|
||||
conversationHistory.value.push({
|
||||
role: "assistant",
|
||||
content: aiReply,
|
||||
});
|
||||
|
||||
return aiReply;
|
||||
} catch (err: any) {
|
||||
const errorMsg = err.message || "請求失敗";
|
||||
error.value = errorMsg;
|
||||
console.error("ChatBase请求错误:", err);
|
||||
throw err;
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
isLoading,
|
||||
error,
|
||||
conversationHistory,
|
||||
sendToLLM,
|
||||
sendToChatBase,
|
||||
clearHistory,
|
||||
};
|
||||
}
|
||||
|
||||
@ -5,11 +5,3 @@ export interface ChatMessage {
|
||||
timestamp: number;
|
||||
completed: boolean;
|
||||
}
|
||||
|
||||
export type SubtitleMode = 'ai-only' | 'bubble' | 'merged';
|
||||
|
||||
export const SUBTITLE_MODE_LABELS: Record<SubtitleMode, string> = {
|
||||
'ai-only': 'AI字幕',
|
||||
'bubble': '对话气泡',
|
||||
'merged': '合并气泡',
|
||||
};
|
||||
|
||||
@ -4,7 +4,7 @@ import legacy from "@vitejs/plugin-legacy";
|
||||
import path from "path";
|
||||
|
||||
export default defineConfig({
|
||||
base: "/baidu-digital-human-app",
|
||||
base: "/xg-digital-human-app",
|
||||
plugins: [
|
||||
vue(),
|
||||
legacy({
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user