mirror of
https://github.com/Chanzhaoyu/chatgpt-web.git
synced 2025-07-29 09:53:44 +00:00
Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
6216d84ecd | ||
![]() |
ea887ad755 | ||
![]() |
c98a2a3cb8 | ||
![]() |
06f41c3758 | ||
![]() |
3f4cb5c900 |
17
CHANGELOG.md
17
CHANGELOG.md
@@ -1,3 +1,20 @@
|
|||||||
|
## v2.4.1
|
||||||
|
|
||||||
|
`2023-02-18`
|
||||||
|
|
||||||
|
### Enhancement
|
||||||
|
- 调整部份移动端上的样式
|
||||||
|
- 输入框支持换行
|
||||||
|
|
||||||
|
## v2.4.0
|
||||||
|
|
||||||
|
`2023-02-17`
|
||||||
|
|
||||||
|
### Feature
|
||||||
|
- 响应式支持移动端
|
||||||
|
### Enhancement
|
||||||
|
- 修改部份描述错误
|
||||||
|
|
||||||
## v2.3.3
|
## v2.3.3
|
||||||
|
|
||||||
`2023-02-16`
|
`2023-02-16`
|
||||||
|
52
README.md
52
README.md
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
使用 express 和 vue3 搭建的 ChartGPT 演示网页
|
使用 express 和 vue3 搭建的 ChartGPT 演示网页
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
> 提示:目前 `OpenAI` 开放的模型最高只有 `GPT-3`,和现在网页所使用的 `GPT-3.5` 或 `GPT-4` 有很大差距,需要等官方开放最新的模型接口。
|
> 提示:目前 `OpenAI` 开放的模型最高只有 `GPT-3`,和现在网页所使用的 `GPT-3.5` 或 `GPT-4` 有很大差距,需要等官方开放最新的模型接口。
|
||||||
|
|
||||||
@@ -78,6 +78,29 @@ pnpm dev
|
|||||||
```
|
```
|
||||||
|
|
||||||
## 打包
|
## 打包
|
||||||
|
## Docker build
|
||||||
|
|
||||||
|
[参考信息](https://github.com/Chanzhaoyu/chatgpt-web/pull/42)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build -t chatgpt-web .
|
||||||
|
```
|
||||||
|
|
||||||
|
## Docker compose
|
||||||
|
|
||||||
|
[Hub 地址](https://hub.docker.com/repository/docker/chenzhaoyu94/chatgpt-web/general)
|
||||||
|
|
||||||
|
```yml
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
image: chenzhaoyu94/chatgpt-web:main
|
||||||
|
ports:
|
||||||
|
- 3002:3002
|
||||||
|
environment:
|
||||||
|
OPENAI_API_KEY: xxxxxx
|
||||||
|
```
|
||||||
|
|
||||||
### 后端服务
|
### 后端服务
|
||||||
> 如果你不需要本项目的 `node` 接口,可以省略如下操作
|
> 如果你不需要本项目的 `node` 接口,可以省略如下操作
|
||||||
@@ -97,32 +120,7 @@ pnpm prod
|
|||||||
|
|
||||||
PS: 不进行打包,直接在服务器上运行 `pnpm start` 也可
|
PS: 不进行打包,直接在服务器上运行 `pnpm start` 也可
|
||||||
|
|
||||||
## Docker build & run
|
### 前端打包
|
||||||
|
|
||||||
[参考信息](https://github.com/Chanzhaoyu/chatgpt-web/pull/42)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker build -t chatgpt-web .
|
|
||||||
```
|
|
||||||
|
|
||||||
## Docker compose
|
|
||||||
|
|
||||||
[Hub 地址](https://hub.docker.com/repository/docker/chenzhaoyu94/chatgpt-web/general)
|
|
||||||
|
|
||||||
```yml
|
|
||||||
version: '3'
|
|
||||||
|
|
||||||
service:
|
|
||||||
app:
|
|
||||||
image: chenzhaoyu94/chatgpt-web
|
|
||||||
ports:
|
|
||||||
- 3002:3002
|
|
||||||
environment:
|
|
||||||
OPENAI_API_KEY: xxxxxx
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### 网页
|
|
||||||
|
|
||||||
根目录下运行以下命令,然后将 `dist` 文件夹复制到你的托管服务器上
|
根目录下运行以下命令,然后将 `dist` 文件夹复制到你的托管服务器上
|
||||||
|
|
||||||
|
BIN
docs/cover-2.png
BIN
docs/cover-2.png
Binary file not shown.
Before Width: | Height: | Size: 200 KiB |
BIN
docs/cover.png
Normal file
BIN
docs/cover.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 62 KiB |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "chatgpt-web",
|
"name": "chatgpt-web",
|
||||||
"version": "2.3.3",
|
"version": "2.4.1",
|
||||||
"private": false,
|
"private": false,
|
||||||
"description": "ChatGPT Web",
|
"description": "ChatGPT Web",
|
||||||
"author": "ChenZhaoYu <chenzhaoyu1994@gmail.com>",
|
"author": "ChenZhaoYu <chenzhaoyu1994@gmail.com>",
|
||||||
@@ -23,6 +23,7 @@
|
|||||||
"common:prepare": "husky install"
|
"common:prepare": "husky install"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@vueuse/core": "^9.12.0",
|
||||||
"highlight.js": "^11.7.0",
|
"highlight.js": "^11.7.0",
|
||||||
"naive-ui": "^2.34.3",
|
"naive-ui": "^2.34.3",
|
||||||
"pinia": "^2.0.30",
|
"pinia": "^2.0.30",
|
||||||
@@ -36,12 +37,14 @@
|
|||||||
"@iconify/vue": "^4.1.0",
|
"@iconify/vue": "^4.1.0",
|
||||||
"@types/crypto-js": "^4.1.1",
|
"@types/crypto-js": "^4.1.1",
|
||||||
"@types/node": "^18.13.0",
|
"@types/node": "^18.13.0",
|
||||||
|
"@types/web-bluetooth": "^0.0.16",
|
||||||
"@vitejs/plugin-vue": "^4.0.0",
|
"@vitejs/plugin-vue": "^4.0.0",
|
||||||
"autoprefixer": "^10.4.13",
|
"autoprefixer": "^10.4.13",
|
||||||
"axios": "^1.3.3",
|
"axios": "^1.3.3",
|
||||||
"crypto-js": "^4.1.1",
|
"crypto-js": "^4.1.1",
|
||||||
"eslint": "^8.34.0",
|
"eslint": "^8.34.0",
|
||||||
"husky": "^8.0.3",
|
"husky": "^8.0.3",
|
||||||
|
"less": "^4.1.3",
|
||||||
"lint-staged": "^13.1.2",
|
"lint-staged": "^13.1.2",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"postcss": "^8.4.21",
|
"postcss": "^8.4.21",
|
||||||
|
154
pnpm-lock.yaml
generated
154
pnpm-lock.yaml
generated
@@ -7,13 +7,16 @@ specifiers:
|
|||||||
'@iconify/vue': ^4.1.0
|
'@iconify/vue': ^4.1.0
|
||||||
'@types/crypto-js': ^4.1.1
|
'@types/crypto-js': ^4.1.1
|
||||||
'@types/node': ^18.13.0
|
'@types/node': ^18.13.0
|
||||||
|
'@types/web-bluetooth': ^0.0.16
|
||||||
'@vitejs/plugin-vue': ^4.0.0
|
'@vitejs/plugin-vue': ^4.0.0
|
||||||
|
'@vueuse/core': ^9.12.0
|
||||||
autoprefixer: ^10.4.13
|
autoprefixer: ^10.4.13
|
||||||
axios: ^1.3.3
|
axios: ^1.3.3
|
||||||
crypto-js: ^4.1.1
|
crypto-js: ^4.1.1
|
||||||
eslint: ^8.34.0
|
eslint: ^8.34.0
|
||||||
highlight.js: ^11.7.0
|
highlight.js: ^11.7.0
|
||||||
husky: ^8.0.3
|
husky: ^8.0.3
|
||||||
|
less: ^4.1.3
|
||||||
lint-staged: ^13.1.2
|
lint-staged: ^13.1.2
|
||||||
naive-ui: ^2.34.3
|
naive-ui: ^2.34.3
|
||||||
npm-run-all: ^4.1.5
|
npm-run-all: ^4.1.5
|
||||||
@@ -28,6 +31,7 @@ specifiers:
|
|||||||
vue-tsc: ^1.1.0
|
vue-tsc: ^1.1.0
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@vueuse/core': 9.12.0_vue@3.2.47
|
||||||
highlight.js: 11.7.0
|
highlight.js: 11.7.0
|
||||||
naive-ui: 2.34.3_vue@3.2.47
|
naive-ui: 2.34.3_vue@3.2.47
|
||||||
pinia: 2.0.30_hmuptsblhheur2tugfgucj7gc4
|
pinia: 2.0.30_hmuptsblhheur2tugfgucj7gc4
|
||||||
@@ -41,19 +45,21 @@ devDependencies:
|
|||||||
'@iconify/vue': 4.1.0_vue@3.2.47
|
'@iconify/vue': 4.1.0_vue@3.2.47
|
||||||
'@types/crypto-js': 4.1.1
|
'@types/crypto-js': 4.1.1
|
||||||
'@types/node': 18.13.0
|
'@types/node': 18.13.0
|
||||||
|
'@types/web-bluetooth': 0.0.16
|
||||||
'@vitejs/plugin-vue': 4.0.0_vite@4.1.1+vue@3.2.47
|
'@vitejs/plugin-vue': 4.0.0_vite@4.1.1+vue@3.2.47
|
||||||
autoprefixer: 10.4.13_postcss@8.4.21
|
autoprefixer: 10.4.13_postcss@8.4.21
|
||||||
axios: 1.3.3
|
axios: 1.3.3
|
||||||
crypto-js: 4.1.1
|
crypto-js: 4.1.1
|
||||||
eslint: 8.34.0
|
eslint: 8.34.0
|
||||||
husky: 8.0.3
|
husky: 8.0.3
|
||||||
|
less: 4.1.3
|
||||||
lint-staged: 13.1.2
|
lint-staged: 13.1.2
|
||||||
npm-run-all: 4.1.5
|
npm-run-all: 4.1.5
|
||||||
postcss: 8.4.21
|
postcss: 8.4.21
|
||||||
rimraf: 4.1.2
|
rimraf: 4.1.2
|
||||||
tailwindcss: 3.2.6_postcss@8.4.21
|
tailwindcss: 3.2.6_postcss@8.4.21
|
||||||
typescript: 4.9.5
|
typescript: 4.9.5
|
||||||
vite: 4.1.1_@types+node@18.13.0
|
vite: 4.1.1_edl3ajnhen4c5iqs57t4gqrzly
|
||||||
vue-tsc: 1.1.0_typescript@4.9.5
|
vue-tsc: 1.1.0_typescript@4.9.5
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
@@ -755,6 +761,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==}
|
resolution: {integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@types/web-bluetooth/0.0.16:
|
||||||
|
resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==}
|
||||||
|
|
||||||
/@typescript-eslint/eslint-plugin/5.52.0_6cfvjsbua5ptj65675bqcn6oza:
|
/@typescript-eslint/eslint-plugin/5.52.0_6cfvjsbua5ptj65675bqcn6oza:
|
||||||
resolution: {integrity: sha512-lHazYdvYVsBokwCdKOppvYJKaJ4S41CgKBcPvyd0xjZNbvQdhn/pnJlGtQksQ/NhInzdaeaSarlBjDXHuclEbg==}
|
resolution: {integrity: sha512-lHazYdvYVsBokwCdKOppvYJKaJ4S41CgKBcPvyd0xjZNbvQdhn/pnJlGtQksQ/NhInzdaeaSarlBjDXHuclEbg==}
|
||||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||||
@@ -892,7 +901,7 @@ packages:
|
|||||||
vite: ^4.0.0
|
vite: ^4.0.0
|
||||||
vue: ^3.2.25
|
vue: ^3.2.25
|
||||||
dependencies:
|
dependencies:
|
||||||
vite: 4.1.1_@types+node@18.13.0
|
vite: 4.1.1_edl3ajnhen4c5iqs57t4gqrzly
|
||||||
vue: 3.2.47
|
vue: 3.2.47
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
@@ -1012,6 +1021,31 @@ packages:
|
|||||||
/@vue/shared/3.2.47:
|
/@vue/shared/3.2.47:
|
||||||
resolution: {integrity: sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ==}
|
resolution: {integrity: sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ==}
|
||||||
|
|
||||||
|
/@vueuse/core/9.12.0_vue@3.2.47:
|
||||||
|
resolution: {integrity: sha512-h/Di8Bvf6xRcvS/PvUVheiMYYz3U0tH3X25YxONSaAUBa841ayMwxkuzx/DGUMCW/wHWzD8tRy2zYmOC36r4sg==}
|
||||||
|
dependencies:
|
||||||
|
'@types/web-bluetooth': 0.0.16
|
||||||
|
'@vueuse/metadata': 9.12.0
|
||||||
|
'@vueuse/shared': 9.12.0_vue@3.2.47
|
||||||
|
vue-demi: 0.13.11_vue@3.2.47
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@vue/composition-api'
|
||||||
|
- vue
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@vueuse/metadata/9.12.0:
|
||||||
|
resolution: {integrity: sha512-9oJ9MM9lFLlmvxXUqsR1wLt1uF7EVbP5iYaHJYqk+G2PbMjY6EXvZeTjbdO89HgoF5cI6z49o2zT/jD9SVoNpQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@vueuse/shared/9.12.0_vue@3.2.47:
|
||||||
|
resolution: {integrity: sha512-TWuJLACQ0BVithVTRbex4Wf1a1VaRuSpVeyEd4vMUWl54PzlE0ciFUshKCXnlLuD0lxIaLK4Ypj3NXYzZh4+SQ==}
|
||||||
|
dependencies:
|
||||||
|
vue-demi: 0.13.11_vue@3.2.47
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@vue/composition-api'
|
||||||
|
- vue
|
||||||
|
dev: false
|
||||||
|
|
||||||
/JSONStream/1.3.5:
|
/JSONStream/1.3.5:
|
||||||
resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==}
|
resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@@ -1490,6 +1524,12 @@ packages:
|
|||||||
through2: 4.0.2
|
through2: 4.0.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/copy-anything/2.0.6:
|
||||||
|
resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==}
|
||||||
|
dependencies:
|
||||||
|
is-what: 3.14.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
/cosmiconfig-typescript-loader/4.3.0_p7cp6dsfhdrlk7mvuxd3wodbsu:
|
/cosmiconfig-typescript-loader/4.3.0_p7cp6dsfhdrlk7mvuxd3wodbsu:
|
||||||
resolution: {integrity: sha512-NTxV1MFfZDLPiBMjxbHRwSh5LaLcPMwNdCutmnHJCKoVnlvldPWlllonKwrsRJ5pYZBIBGRWWU2tfvzxgeSW5Q==}
|
resolution: {integrity: sha512-NTxV1MFfZDLPiBMjxbHRwSh5LaLcPMwNdCutmnHJCKoVnlvldPWlllonKwrsRJ5pYZBIBGRWWU2tfvzxgeSW5Q==}
|
||||||
engines: {node: '>=12', npm: '>=6'}
|
engines: {node: '>=12', npm: '>=6'}
|
||||||
@@ -1741,6 +1781,15 @@ packages:
|
|||||||
engines: {node: '>=0.12'}
|
engines: {node: '>=0.12'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/errno/0.1.8:
|
||||||
|
resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==}
|
||||||
|
hasBin: true
|
||||||
|
requiresBuild: true
|
||||||
|
dependencies:
|
||||||
|
prr: 1.0.1
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
/error-ex/1.3.2:
|
/error-ex/1.3.2:
|
||||||
resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==}
|
resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -2634,11 +2683,27 @@ packages:
|
|||||||
hasBin: true
|
hasBin: true
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/iconv-lite/0.6.3:
|
||||||
|
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
dependencies:
|
||||||
|
safer-buffer: 2.1.2
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
/ignore/5.2.4:
|
/ignore/5.2.4:
|
||||||
resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==}
|
resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==}
|
||||||
engines: {node: '>= 4'}
|
engines: {node: '>= 4'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/image-size/0.5.5:
|
||||||
|
resolution: {integrity: sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
hasBin: true
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
/import-fresh/3.3.0:
|
/import-fresh/3.3.0:
|
||||||
resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
|
resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
@@ -2874,6 +2939,10 @@ packages:
|
|||||||
call-bind: 1.0.2
|
call-bind: 1.0.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/is-what/3.14.1:
|
||||||
|
resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/isexe/2.0.0:
|
/isexe/2.0.0:
|
||||||
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
||||||
dev: true
|
dev: true
|
||||||
@@ -2959,6 +3028,26 @@ packages:
|
|||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/less/4.1.3:
|
||||||
|
resolution: {integrity: sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
hasBin: true
|
||||||
|
dependencies:
|
||||||
|
copy-anything: 2.0.6
|
||||||
|
parse-node-version: 1.0.1
|
||||||
|
tslib: 2.5.0
|
||||||
|
optionalDependencies:
|
||||||
|
errno: 0.1.8
|
||||||
|
graceful-fs: 4.2.10
|
||||||
|
image-size: 0.5.5
|
||||||
|
make-dir: 2.1.0
|
||||||
|
mime: 1.6.0
|
||||||
|
needle: 3.2.0
|
||||||
|
source-map: 0.6.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
dev: true
|
||||||
|
|
||||||
/levn/0.4.1:
|
/levn/0.4.1:
|
||||||
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
|
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
|
||||||
engines: {node: '>= 0.8.0'}
|
engines: {node: '>= 0.8.0'}
|
||||||
@@ -3116,6 +3205,16 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
sourcemap-codec: 1.4.8
|
sourcemap-codec: 1.4.8
|
||||||
|
|
||||||
|
/make-dir/2.1.0:
|
||||||
|
resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
requiresBuild: true
|
||||||
|
dependencies:
|
||||||
|
pify: 4.0.1
|
||||||
|
semver: 5.7.1
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
/make-error/1.3.6:
|
/make-error/1.3.6:
|
||||||
resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==}
|
resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==}
|
||||||
dev: true
|
dev: true
|
||||||
@@ -3206,6 +3305,14 @@ packages:
|
|||||||
mime-db: 1.52.0
|
mime-db: 1.52.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/mime/1.6.0:
|
||||||
|
resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
hasBin: true
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
/mimic-fn/2.1.0:
|
/mimic-fn/2.1.0:
|
||||||
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
|
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
@@ -3298,6 +3405,20 @@ packages:
|
|||||||
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
|
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/needle/3.2.0:
|
||||||
|
resolution: {integrity: sha512-oUvzXnyLiVyVGoianLijF9O/RecZUf7TkBfimjGrLM4eQhXyeJwM6GeAWccwfQ9aa4gMCZKqhAOuLaMIcQxajQ==}
|
||||||
|
engines: {node: '>= 4.4.x'}
|
||||||
|
hasBin: true
|
||||||
|
requiresBuild: true
|
||||||
|
dependencies:
|
||||||
|
debug: 3.2.7
|
||||||
|
iconv-lite: 0.6.3
|
||||||
|
sax: 1.2.4
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
/nice-try/1.0.5:
|
/nice-try/1.0.5:
|
||||||
resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==}
|
resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==}
|
||||||
dev: true
|
dev: true
|
||||||
@@ -3512,6 +3633,11 @@ packages:
|
|||||||
lines-and-columns: 1.2.4
|
lines-and-columns: 1.2.4
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/parse-node-version/1.0.1:
|
||||||
|
resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==}
|
||||||
|
engines: {node: '>= 0.10'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/path-exists/4.0.0:
|
/path-exists/4.0.0:
|
||||||
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
|
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -3583,6 +3709,12 @@ packages:
|
|||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/pify/4.0.1:
|
||||||
|
resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
/pinia/2.0.30_hmuptsblhheur2tugfgucj7gc4:
|
/pinia/2.0.30_hmuptsblhheur2tugfgucj7gc4:
|
||||||
resolution: {integrity: sha512-q6DUmxWwe/mQgg+55QQjykpKC+aGeGdaJV3niminl19V08dE+LRTvSEuqi6/NLSGCKHI49KGL6tMNEOssFiMyA==}
|
resolution: {integrity: sha512-q6DUmxWwe/mQgg+55QQjykpKC+aGeGdaJV3niminl19V08dE+LRTvSEuqi6/NLSGCKHI49KGL6tMNEOssFiMyA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -3684,6 +3816,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
|
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/prr/1.0.1:
|
||||||
|
resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==}
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
/punycode/2.3.0:
|
/punycode/2.3.0:
|
||||||
resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==}
|
resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
@@ -3896,6 +4033,16 @@ packages:
|
|||||||
regexp-tree: 0.1.24
|
regexp-tree: 0.1.24
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/safer-buffer/2.1.2:
|
||||||
|
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/sax/1.2.4:
|
||||||
|
resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==}
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
/seemly/0.3.6:
|
/seemly/0.3.6:
|
||||||
resolution: {integrity: sha512-lEV5VB8BUKTo/AfktXJcy+JeXns26ylbMkIUco8CYREsQijuz4mrXres2Q+vMLdwkuLxJdIPQ8IlCIxLYm71Yw==}
|
resolution: {integrity: sha512-lEV5VB8BUKTo/AfktXJcy+JeXns26ylbMkIUco8CYREsQijuz4mrXres2Q+vMLdwkuLxJdIPQ8IlCIxLYm71Yw==}
|
||||||
dev: false
|
dev: false
|
||||||
@@ -4380,7 +4527,7 @@ packages:
|
|||||||
vue: 3.2.47
|
vue: 3.2.47
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/vite/4.1.1_@types+node@18.13.0:
|
/vite/4.1.1_edl3ajnhen4c5iqs57t4gqrzly:
|
||||||
resolution: {integrity: sha512-LM9WWea8vsxhr782r9ntg+bhSFS06FJgCvvB0+8hf8UWtvaiDagKYWXndjfX6kGl74keHJUcpzrQliDXZlF5yg==}
|
resolution: {integrity: sha512-LM9WWea8vsxhr782r9ntg+bhSFS06FJgCvvB0+8hf8UWtvaiDagKYWXndjfX6kGl74keHJUcpzrQliDXZlF5yg==}
|
||||||
engines: {node: ^14.18.0 || >=16.0.0}
|
engines: {node: ^14.18.0 || >=16.0.0}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@@ -4407,6 +4554,7 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 18.13.0
|
'@types/node': 18.13.0
|
||||||
esbuild: 0.16.17
|
esbuild: 0.16.17
|
||||||
|
less: 4.1.3
|
||||||
postcss: 8.4.21
|
postcss: 8.4.21
|
||||||
resolve: 1.22.1
|
resolve: 1.22.1
|
||||||
rollup: 3.15.0
|
rollup: 3.15.0
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
<script setup lang='ts'>
|
<script setup lang='ts'>
|
||||||
import { computed, nextTick, onMounted, ref, watch } from 'vue'
|
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
||||||
|
import type { MessageReactive } from 'naive-ui'
|
||||||
import { NButton, NInput, useMessage } from 'naive-ui'
|
import { NButton, NInput, useMessage } from 'naive-ui'
|
||||||
import { Message } from './components'
|
import { Message } from './components'
|
||||||
import { Layout } from './layout'
|
import { Layout } from './layout'
|
||||||
@@ -7,6 +8,7 @@ import { useChat } from './hooks/useChat'
|
|||||||
import { fetchChatAPI } from '@/api'
|
import { fetchChatAPI } from '@/api'
|
||||||
import { HoverButton, SvgIcon } from '@/components/common'
|
import { HoverButton, SvgIcon } from '@/components/common'
|
||||||
import { useHistoryStore } from '@/store'
|
import { useHistoryStore } from '@/store'
|
||||||
|
import { useBasicLayout } from '@/hooks/useBasicLayout'
|
||||||
|
|
||||||
let controller = new AbortController()
|
let controller = new AbortController()
|
||||||
|
|
||||||
@@ -14,6 +16,10 @@ const ms = useMessage()
|
|||||||
|
|
||||||
const historyStore = useHistoryStore()
|
const historyStore = useHistoryStore()
|
||||||
|
|
||||||
|
const { isMobile } = useBasicLayout()
|
||||||
|
|
||||||
|
let messageReactive: MessageReactive | null = null
|
||||||
|
|
||||||
const scrollRef = ref<HTMLDivElement>()
|
const scrollRef = ref<HTMLDivElement>()
|
||||||
|
|
||||||
const { addChat, clearChat } = useChat()
|
const { addChat, clearChat } = useChat()
|
||||||
@@ -27,6 +33,12 @@ const heartbeat = computed(() => historyStore.heartbeat)
|
|||||||
const list = computed<Chat.Chat[]>(() => historyStore.getCurrentChat)
|
const list = computed<Chat.Chat[]>(() => historyStore.getCurrentChat)
|
||||||
const chatList = computed<Chat.Chat[]>(() => list.value.filter(item => (!item.reversal && !item.error)))
|
const chatList = computed<Chat.Chat[]>(() => list.value.filter(item => (!item.reversal && !item.error)))
|
||||||
|
|
||||||
|
const footerMobileStyle = computed(() => {
|
||||||
|
if (isMobile.value)
|
||||||
|
return ['pl-2', 'pt-2', 'pb-6', 'fixed', 'bottom-0', 'left-0', 'right-0', 'z-30']
|
||||||
|
return []
|
||||||
|
})
|
||||||
|
|
||||||
async function handleSubmit() {
|
async function handleSubmit() {
|
||||||
if (loading.value)
|
if (loading.value)
|
||||||
return
|
return
|
||||||
@@ -51,6 +63,7 @@ async function handleSubmit() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
|
createMessage()
|
||||||
const { data } = await fetchChatAPI(message, options, controller.signal)
|
const { data } = await fetchChatAPI(message, options, controller.signal)
|
||||||
addMessage(data?.text ?? '', { options: { conversationId: data.conversationId, parentMessageId: data.id } })
|
addMessage(data?.text ?? '', { options: { conversationId: data.conversationId, parentMessageId: data.id } })
|
||||||
}
|
}
|
||||||
@@ -60,12 +73,15 @@ async function handleSubmit() {
|
|||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
|
removeMessage()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleEnter(event: KeyboardEvent) {
|
function handleEnter(event: KeyboardEvent) {
|
||||||
if (event.key === 'Enter')
|
if (event.key === 'Enter' && !event.shiftKey) {
|
||||||
|
event.preventDefault()
|
||||||
handleSubmit()
|
handleSubmit()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addMessage(
|
function addMessage(
|
||||||
@@ -89,12 +105,32 @@ function handleCancel() {
|
|||||||
controller.abort()
|
controller.abort()
|
||||||
controller = new AbortController()
|
controller = new AbortController()
|
||||||
loading.value = false
|
loading.value = false
|
||||||
|
removeMessage()
|
||||||
|
}
|
||||||
|
|
||||||
|
function createMessage() {
|
||||||
|
if (!messageReactive) {
|
||||||
|
messageReactive = ms.loading('Thinking...', {
|
||||||
|
duration: 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeMessage() {
|
||||||
|
if (messageReactive) {
|
||||||
|
messageReactive.destroy()
|
||||||
|
messageReactive = null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
scrollToBottom()
|
scrollToBottom()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
handleCancel()
|
||||||
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
heartbeat,
|
heartbeat,
|
||||||
() => {
|
() => {
|
||||||
@@ -118,24 +154,49 @@ watch(
|
|||||||
<Layout>
|
<Layout>
|
||||||
<div class="flex flex-col h-full">
|
<div class="flex flex-col h-full">
|
||||||
<main class="flex-1 overflow-hidden">
|
<main class="flex-1 overflow-hidden">
|
||||||
<div ref="scrollRef" class="h-full p-4 overflow-hidden overflow-y-auto">
|
<div
|
||||||
<div>
|
ref="scrollRef"
|
||||||
<Message
|
class="h-full p-4 overflow-hidden overflow-y-auto"
|
||||||
v-for="(item, index) of list" :key="index" :date-time="item.dateTime" :message="item.message"
|
:class="[{ 'p-2': isMobile }]"
|
||||||
:reversal="item.reversal" :error="item.error"
|
>
|
||||||
/>
|
<template v-if="!list.length">
|
||||||
</div>
|
<div class="flex items-center justify-center mt-4 text-center text-neutral-300">
|
||||||
|
<SvgIcon icon="ri:bubble-chart-fill" class="mr-2 text-3xl" />
|
||||||
|
<span>Aha~</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<div>
|
||||||
|
<Message
|
||||||
|
v-for="(item, index) of list"
|
||||||
|
:key="index"
|
||||||
|
:date-time="item.dateTime"
|
||||||
|
:message="item.message"
|
||||||
|
:reversal="item.reversal"
|
||||||
|
:error="item.error"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
<footer class="p-4">
|
<footer
|
||||||
|
class="p-4"
|
||||||
|
:class="footerMobileStyle"
|
||||||
|
>
|
||||||
<div class="flex items-center justify-between space-x-2">
|
<div class="flex items-center justify-between space-x-2">
|
||||||
<HoverButton tooltip="Clear conversations">
|
<HoverButton tooltip="Clear conversations">
|
||||||
<span class="text-xl text-[#4f555e]" @click="handleClear">
|
<span class="text-xl text-[#4f555e]" @click="handleClear">
|
||||||
<SvgIcon icon="ri:delete-bin-line" />
|
<SvgIcon icon="ri:delete-bin-line" />
|
||||||
</span>
|
</span>
|
||||||
</HoverButton>
|
</HoverButton>
|
||||||
<NInput v-model:value="prompt" placeholder="Type a message..." @keypress="handleEnter" />
|
<NInput
|
||||||
<NButton type="primary" :loading="loading" @click="handleSubmit">
|
v-model:value="prompt"
|
||||||
|
type="textarea"
|
||||||
|
:autosize="{ minRows: 1, maxRows: 2 }"
|
||||||
|
placeholder="Ask me anything..."
|
||||||
|
@keypress="handleEnter"
|
||||||
|
/>
|
||||||
|
<NButton type="primary" :disabled="loading" @click="handleSubmit">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<SvgIcon icon="ri:send-plane-fill" />
|
<SvgIcon icon="ri:send-plane-fill" />
|
||||||
</template>
|
</template>
|
||||||
|
@@ -1,15 +1,43 @@
|
|||||||
<script setup lang='ts'>
|
<script setup lang='ts'>
|
||||||
|
import { computed } from 'vue'
|
||||||
import { NLayout, NLayoutContent } from 'naive-ui'
|
import { NLayout, NLayoutContent } from 'naive-ui'
|
||||||
import Sider from './sider/index.vue'
|
import Sider from './sider/index.vue'
|
||||||
|
import Header from './header/index.vue'
|
||||||
|
import { useBasicLayout } from '@/hooks/useBasicLayout'
|
||||||
|
import { useAppStore } from '@/store'
|
||||||
|
|
||||||
|
const appStore = useAppStore()
|
||||||
|
|
||||||
|
const { isMobile } = useBasicLayout()
|
||||||
|
|
||||||
|
const collapsed = computed(() => appStore.siderCollapsed)
|
||||||
|
|
||||||
|
const getMobileClass = computed(() => {
|
||||||
|
if (isMobile.value)
|
||||||
|
return ['rounded-none', 'shadow-none']
|
||||||
|
return ['border', 'rounded-md', 'shadow-md']
|
||||||
|
})
|
||||||
|
|
||||||
|
const getContainerClass = computed(() => {
|
||||||
|
return [
|
||||||
|
'h-full',
|
||||||
|
{ 'pt-14': isMobile.value },
|
||||||
|
{ 'pb-[70px]': isMobile.value },
|
||||||
|
{ 'pl-[260px]': !isMobile.value && !collapsed.value },
|
||||||
|
]
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="h-full overflow-hidden border rounded-md shadow-md">
|
<div class="h-screen" :class="[isMobile ? 'p-0' : 'p-4']">
|
||||||
<NLayout class="h-full" has-sider>
|
<div class="h-full overflow-hidden" :class="getMobileClass">
|
||||||
<Sider />
|
<NLayout class="z-40 transition" :class="getContainerClass" has-sider>
|
||||||
<NLayoutContent class="h-full">
|
<Sider />
|
||||||
<slot />
|
<Header v-if="isMobile" />
|
||||||
</NLayoutContent>
|
<NLayoutContent class="h-full">
|
||||||
</NLayout>
|
<slot />
|
||||||
|
</NLayoutContent>
|
||||||
|
</NLayout>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
36
src/components/business/Chat/layout/header/index.vue
Normal file
36
src/components/business/Chat/layout/header/index.vue
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import { SvgIcon } from '@/components/common'
|
||||||
|
import { useAppStore, useHistoryStore } from '@/store'
|
||||||
|
|
||||||
|
const appStore = useAppStore()
|
||||||
|
const historyStore = useHistoryStore()
|
||||||
|
|
||||||
|
const collapsed = computed(() => appStore.siderCollapsed)
|
||||||
|
|
||||||
|
function handleAdd() {
|
||||||
|
historyStore.addHistory({
|
||||||
|
title: 'New Chat',
|
||||||
|
isEdit: false,
|
||||||
|
data: [],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleUpdateCollapsed() {
|
||||||
|
appStore.setSiderCollapsed(!collapsed.value)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<header class="fixed top-0 left-0 right-0 z-50 border-b bg-white/80 backdrop-blur">
|
||||||
|
<div class="relative flex items-center justify-between h-14">
|
||||||
|
<button class="flex items-center justify-center w-11 h-11" @click="handleUpdateCollapsed">
|
||||||
|
<SvgIcon v-if="collapsed" class="text-2xl" icon="ri:align-justify" />
|
||||||
|
<SvgIcon v-else class="text-2xl" icon="ri:align-right" />
|
||||||
|
</button>
|
||||||
|
<button class="flex items-center justify-center w-11 h-11" @click="handleAdd">
|
||||||
|
<SvgIcon class="text-2xl" icon="ri:add-fill" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
</template>
|
@@ -1,14 +0,0 @@
|
|||||||
<script setup lang='ts'>
|
|
||||||
import { HoverButton, SvgIcon, UserAvatar } from '@/components/common'
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<footer class="flex items-center justify-between min-w-0 p-4 overflow-hidden border-t h-[70px]">
|
|
||||||
<UserAvatar class="flex-1" />
|
|
||||||
<HoverButton tooltip="Setting">
|
|
||||||
<span class="text-xl text-[#4f555e]">
|
|
||||||
<SvgIcon icon="ri:settings-4-line" />
|
|
||||||
</span>
|
|
||||||
</HoverButton>
|
|
||||||
</footer>
|
|
||||||
</template>
|
|
@@ -33,39 +33,47 @@ function handleEnter(index: number, isEdit: boolean, event: KeyboardEvent) {
|
|||||||
<template>
|
<template>
|
||||||
<NScrollbar class="px-4">
|
<NScrollbar class="px-4">
|
||||||
<div class="flex flex-col gap-2 text-sm">
|
<div class="flex flex-col gap-2 text-sm">
|
||||||
<div v-for="(item, index) of dataSources" :key="index">
|
<template v-if="!dataSources.length">
|
||||||
<a
|
<div class="flex flex-col items-center mt-4 text-center text-neutral-300">
|
||||||
class="relative flex items-center gap-3 px-3 py-3 break-all border rounded-md cursor-pointer hover:bg-neutral-100 group"
|
<SvgIcon icon="ri:inbox-line" class="mb-2 text-3xl" />
|
||||||
:class="historyStore.active === index && ['border-[#4b9e5f]', 'bg-neutral-100', 'text-[#4b9e5f]', 'pr-14']"
|
<span>No history</span>
|
||||||
@click="handleSelect(index)"
|
</div>
|
||||||
>
|
</template>
|
||||||
<span>
|
<template v-else>
|
||||||
<SvgIcon icon="ri:message-3-line" />
|
<div v-for="(item, index) of dataSources" :key="index">
|
||||||
</span>
|
<a
|
||||||
<div class="relative flex-1 overflow-hidden break-all text-ellipsis whitespace-nowrap">
|
class="relative flex items-center gap-3 px-3 py-3 break-all border rounded-md cursor-pointer hover:bg-neutral-100 group"
|
||||||
<NInput
|
:class="historyStore.active === index && ['border-[#4b9e5f]', 'bg-neutral-100', 'text-[#4b9e5f]', 'pr-14']"
|
||||||
v-if="item.isEdit" v-model:value="item.title" size="tiny"
|
@click="handleSelect(index)"
|
||||||
@keypress="handleEnter(index, false, $event)"
|
>
|
||||||
/>
|
<span>
|
||||||
<span v-else>{{ item.title }}</span>
|
<SvgIcon icon="ri:message-3-line" />
|
||||||
</div>
|
</span>
|
||||||
<div v-if="historyStore.active === index" class="absolute z-10 flex visible right-1">
|
<div class="relative flex-1 overflow-hidden break-all text-ellipsis whitespace-nowrap">
|
||||||
<template v-if="item.isEdit">
|
<NInput
|
||||||
<button class="p-1" @click="handleEdit(index, false, $event)">
|
v-if="item.isEdit" v-model:value="item.title" size="tiny"
|
||||||
<SvgIcon icon="ri:save-line" />
|
@keypress="handleEnter(index, false, $event)"
|
||||||
</button>
|
/>
|
||||||
</template>
|
<span v-else>{{ item.title }}</span>
|
||||||
<template v-else>
|
</div>
|
||||||
<button class="p-1">
|
<div v-if="historyStore.active === index" class="absolute z-10 flex visible right-1">
|
||||||
<SvgIcon icon="ri:edit-line" @click="handleEdit(index, true, $event)" />
|
<template v-if="item.isEdit">
|
||||||
</button>
|
<button class="p-1" @click="handleEdit(index, false, $event)">
|
||||||
<button class="p-1" @click="handleRemove(index, $event)">
|
<SvgIcon icon="ri:save-line" />
|
||||||
<SvgIcon icon="ri:delete-bin-line" />
|
</button>
|
||||||
</button>
|
</template>
|
||||||
</template>
|
<template v-else>
|
||||||
</div>
|
<button class="p-1">
|
||||||
</a>
|
<SvgIcon icon="ri:edit-line" @click="handleEdit(index, true, $event)" />
|
||||||
</div>
|
</button>
|
||||||
|
<button class="p-1" @click="handleRemove(index, $event)">
|
||||||
|
<SvgIcon icon="ri:delete-bin-line" />
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</NScrollbar>
|
</NScrollbar>
|
||||||
</template>
|
</template>
|
||||||
|
@@ -1,14 +1,16 @@
|
|||||||
<script setup lang='ts'>
|
<script setup lang='ts'>
|
||||||
import { ref } from 'vue'
|
import { computed, watch } from 'vue'
|
||||||
import { NButton, NLayoutSider } from 'naive-ui'
|
import { NButton, NLayoutSider } from 'naive-ui'
|
||||||
import List from './List.vue'
|
import List from './List.vue'
|
||||||
import Footer from './Footer.vue'
|
import { HoverButton, SvgIcon, UserAvatar } from '@/components/common'
|
||||||
import { useAppStore, useHistoryStore } from '@/store'
|
import { useAppStore, useHistoryStore } from '@/store'
|
||||||
|
import { useBasicLayout } from '@/hooks/useBasicLayout'
|
||||||
|
|
||||||
const appStore = useAppStore()
|
const appStore = useAppStore()
|
||||||
const historyStore = useHistoryStore()
|
const historyStore = useHistoryStore()
|
||||||
|
const { isMobile } = useBasicLayout()
|
||||||
|
|
||||||
const collapsed = ref(appStore.siderCollapsed ?? false)
|
const collapsed = computed(() => appStore.siderCollapsed)
|
||||||
|
|
||||||
function handleAdd() {
|
function handleAdd() {
|
||||||
historyStore.addHistory({
|
historyStore.addHistory({
|
||||||
@@ -18,10 +20,20 @@ function handleAdd() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleCollapsed() {
|
function handleUpdateCollapsed() {
|
||||||
collapsed.value = !collapsed.value
|
appStore.setSiderCollapsed(!collapsed.value)
|
||||||
appStore.setSiderCollapsed(collapsed.value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
isMobile,
|
||||||
|
(val) => {
|
||||||
|
appStore.setSiderCollapsed(val)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
flush: 'post',
|
||||||
|
},
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -29,12 +41,14 @@ function handleCollapsed() {
|
|||||||
:collapsed="collapsed"
|
:collapsed="collapsed"
|
||||||
:collapsed-width="0"
|
:collapsed-width="0"
|
||||||
:width="260"
|
:width="260"
|
||||||
|
:show-trigger="isMobile ? false : 'arrow-circle'"
|
||||||
collapse-mode="transform"
|
collapse-mode="transform"
|
||||||
show-trigger="arrow-circle"
|
position="absolute"
|
||||||
bordered
|
bordered
|
||||||
@update:collapsed="handleCollapsed"
|
style="z-index: 50;"
|
||||||
|
@update-collapsed="handleUpdateCollapsed"
|
||||||
>
|
>
|
||||||
<div class="flex flex-col h-full">
|
<div class="flex flex-col h-full" :class="[{ 'pt-14': isMobile }]">
|
||||||
<main class="flex-1 min-h-0 overflow-hidden">
|
<main class="flex-1 min-h-0 overflow-hidden">
|
||||||
<div class="p-4">
|
<div class="p-4">
|
||||||
<NButton dashed block @click="handleAdd">
|
<NButton dashed block @click="handleAdd">
|
||||||
@@ -43,7 +57,17 @@ function handleCollapsed() {
|
|||||||
</div>
|
</div>
|
||||||
<List />
|
<List />
|
||||||
</main>
|
</main>
|
||||||
<Footer />
|
<footer class="flex items-center justify-between min-w-0 p-4 overflow-hidden border-t h-[70px]">
|
||||||
|
<UserAvatar />
|
||||||
|
<HoverButton tooltip="Setting">
|
||||||
|
<span class="text-xl text-[#4f555e]">
|
||||||
|
<SvgIcon icon="ri:settings-4-line" />
|
||||||
|
</span>
|
||||||
|
</HoverButton>
|
||||||
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</NLayoutSider>
|
</NLayoutSider>
|
||||||
|
<template v-if="isMobile">
|
||||||
|
<div v-show="!collapsed" class="absolute inset-0 z-40 bg-black/40" @click="handleUpdateCollapsed" />
|
||||||
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
@@ -17,7 +17,3 @@ import { GithubSite } from '@/components/custom'
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
8
src/hooks/useBasicLayout.ts
Normal file
8
src/hooks/useBasicLayout.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { breakpointsTailwind, useBreakpoints } from '@vueuse/core'
|
||||||
|
|
||||||
|
export function useBasicLayout() {
|
||||||
|
const breakpoints = useBreakpoints(breakpointsTailwind)
|
||||||
|
const isMobile = breakpoints.smaller('sm')
|
||||||
|
|
||||||
|
return { isMobile }
|
||||||
|
}
|
@@ -3,7 +3,5 @@ import { Chat } from '@/components/business'
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="h-full p-4 overflow-hidden">
|
<Chat />
|
||||||
<Chat />
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
@@ -16,7 +16,8 @@
|
|||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./src/*"]
|
"@/*": ["./src/*"]
|
||||||
},
|
},
|
||||||
"types": ["vite/client", "node", "naive-ui/volar"]
|
// @vueuse/core 不能通过 vue-tsc 检查,所以这里需要忽略,以后将移除
|
||||||
|
"types": ["vite/client", "node", "naive-ui/volar", "web-bluetooth"]
|
||||||
},
|
},
|
||||||
"exclude": ["node_modules", "dist", "service"]
|
"exclude": ["node_modules", "dist", "service"]
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user