mirror of
https://github.com/Chanzhaoyu/chatgpt-web.git
synced 2025-07-24 15:04:53 +00:00
Compare commits
17 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a6605e8a57 | ||
![]() |
3482565481 | ||
![]() |
a6f670101a | ||
![]() |
2683977e21 | ||
![]() |
e1d8f5ff56 | ||
![]() |
4f9232c130 | ||
![]() |
c226d07368 | ||
![]() |
2b2efe2f15 | ||
![]() |
c2ce7009e6 | ||
![]() |
b651ef8373 | ||
![]() |
b8f2a0e849 | ||
![]() |
c6e1663a39 | ||
![]() |
847a2d4d8c | ||
![]() |
6e272bb343 | ||
![]() |
bc390ef09d | ||
![]() |
1cb5393b91 | ||
![]() |
0f51e51827 |
13
CHANGELOG.md
13
CHANGELOG.md
@@ -1,3 +1,16 @@
|
||||
## v2.11.1
|
||||
|
||||
`2023-10-11`
|
||||
|
||||
## Enhancement
|
||||
- 优化打字机光标效果
|
||||
- 清空聊天历史按钮
|
||||
- 更新文档
|
||||
|
||||
## BugFix
|
||||
- 修复移动端上的问题
|
||||
- 修复不规范的引入导致的问题
|
||||
|
||||
## v2.11.0
|
||||
|
||||
`2023-04-26`
|
||||
|
11
README.md
11
README.md
@@ -2,8 +2,6 @@
|
||||
|
||||
> 声明:此项目只发布于 GitHub,基于 MIT 协议,免费且作为开源学习使用。并且不会有任何形式的卖号、付费服务、讨论群、讨论组等行为。谨防受骗。
|
||||
|
||||
更多功能:[chatgpt-web-plus](https://github.com/Chanzhaoyu/chatgpt-web-plus)
|
||||
|
||||

|
||||

|
||||
|
||||
@@ -29,6 +27,7 @@
|
||||
- [防止爬虫抓取](#防止爬虫抓取)
|
||||
- [使用 Railway 部署](#使用-railway-部署)
|
||||
- [Railway 环境变量](#railway-环境变量)
|
||||
- [使用 Sealos 部署](#使用-sealos-部署)
|
||||
- [手动打包](#手动打包)
|
||||
- [后端服务](#后端服务-1)
|
||||
- [前端网页](#前端网页-1)
|
||||
@@ -218,7 +217,7 @@ services:
|
||||
# API接口地址,可选,设置 OPENAI_API_KEY 时可用
|
||||
OPENAI_API_BASE_URL: xxx
|
||||
# API模型,可选,设置 OPENAI_API_KEY 时可用,https://platform.openai.com/docs/models
|
||||
# gpt-4, gpt-4-0314, gpt-4-32k, gpt-4-32k-0314, gpt-3.5-turbo, gpt-3.5-turbo-0301, text-davinci-003, text-davinci-002, code-davinci-002
|
||||
# gpt-4, gpt-4-0314, gpt-4-0613, gpt-4-32k, gpt-4-32k-0314, gpt-4-32k-0613, gpt-3.5-turbo-16k, gpt-3.5-turbo-16k-0613, gpt-3.5-turbo, gpt-3.5-turbo-0301, gpt-3.5-turbo-0613, text-davinci-003, text-davinci-002, code-davinci-002
|
||||
OPENAI_API_MODEL: xxx
|
||||
# 反向代理,可选
|
||||
API_REVERSE_PROXY: xxx
|
||||
@@ -278,6 +277,12 @@ services:
|
||||
|
||||
> 注意: `Railway` 修改环境变量会重新 `Deploy`
|
||||
|
||||
### 使用 Sealos 部署
|
||||
|
||||
[](https://cloud.sealos.io/?openapp=system-fastdeploy%3FtemplateName%3Dchatgpt-web)
|
||||
|
||||
> 环境变量与 Docker 环境变量一致
|
||||
|
||||
### 手动打包
|
||||
#### 后端服务
|
||||
> 如果你不需要本项目的 `node` 接口,可以省略如下操作
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "chatgpt-web",
|
||||
"version": "2.11.0",
|
||||
"version": "2.11.1",
|
||||
"private": false,
|
||||
"description": "ChatGPT Web",
|
||||
"author": "ChenZhaoYu <chenzhaoyu1994@gmail.com>",
|
||||
|
@@ -58,6 +58,12 @@ let api: ChatGPTAPI | ChatGPTUnofficialProxyAPI
|
||||
options.maxResponseTokens = 2048
|
||||
}
|
||||
}
|
||||
else if (model.toLowerCase().includes('gpt-3.5')) {
|
||||
if (model.toLowerCase().includes('16k')) {
|
||||
options.maxModelTokens = 16384
|
||||
options.maxResponseTokens = 4096
|
||||
}
|
||||
}
|
||||
|
||||
if (isNotEmptyString(OPENAI_API_BASE_URL))
|
||||
options.apiBaseUrl = `${OPENAI_API_BASE_URL}/v1`
|
||||
|
@@ -1,8 +1,8 @@
|
||||
<script setup lang='ts'>
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { NSpin } from 'naive-ui'
|
||||
import pkg from '../../../../package.json'
|
||||
import { fetchChatConfig } from '@/api'
|
||||
import pkg from '@/../package.json'
|
||||
import { useAuthStore } from '@/store'
|
||||
|
||||
interface ConfigState {
|
||||
|
@@ -42,7 +42,7 @@ function handleReset() {
|
||||
<div class="flex items-center space-x-4">
|
||||
<span class="flex-shrink-0 w-[120px]">{{ $t('setting.temperature') }} </span>
|
||||
<div class="flex-1">
|
||||
<NSlider v-model:value="temperature" :max="1" :min="0" :step="0.1" />
|
||||
<NSlider v-model:value="temperature" :max="2" :min="0" :step="0.1" />
|
||||
</div>
|
||||
<span>{{ temperature }}</span>
|
||||
<NButton size="tiny" text type="primary" @click="updateSettings({ temperature })">
|
||||
|
@@ -26,6 +26,7 @@ export default {
|
||||
failed: 'Failed',
|
||||
verify: 'Verify',
|
||||
unauthorizedTips: 'Unauthorized, please verify first.',
|
||||
stopResponding: 'Stop Responding',
|
||||
},
|
||||
chat: {
|
||||
newChatButton: 'New Chat',
|
||||
|
@@ -26,6 +26,7 @@ export default {
|
||||
failed: '실패',
|
||||
verify: '검증',
|
||||
unauthorizedTips: '인증되지 않았습니다. 먼저 확인하십시오.',
|
||||
stopResponding: '응답 중지',
|
||||
},
|
||||
chat: {
|
||||
newChatButton: '새로운 채팅',
|
||||
|
@@ -26,6 +26,7 @@ export default {
|
||||
failed: 'Не удалось',
|
||||
verify: 'Проверить',
|
||||
unauthorizedTips: 'Не авторизован, сначала подтвердите свою личность.',
|
||||
stopResponding: 'Прекращение отклика',
|
||||
},
|
||||
chat: {
|
||||
newChatButton: 'Новый чат',
|
||||
|
94
src/locales/vi-VN.ts
Normal file
94
src/locales/vi-VN.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
export default {
|
||||
common: {
|
||||
add: 'Thêm',
|
||||
addSuccess: 'Thêm thành công',
|
||||
edit: 'Sửa',
|
||||
editSuccess: 'Sửa thành công',
|
||||
delete: 'Xóa',
|
||||
deleteSuccess: 'Xóa thành công',
|
||||
save: 'Lưu',
|
||||
saveSuccess: 'Lưu thành công',
|
||||
reset: 'Đặt lại',
|
||||
action: 'Hành động',
|
||||
export: 'Xuất',
|
||||
exportSuccess: 'Xuất thành công',
|
||||
import: 'Nhập',
|
||||
importSuccess: 'Nhập thành công',
|
||||
clear: 'Dọn dẹp',
|
||||
clearSuccess: 'Dọn dẹp thành công',
|
||||
yes: 'Có',
|
||||
no: 'Không',
|
||||
confirm: 'Xác nhận',
|
||||
download: 'Tải xuống',
|
||||
noData: 'Không có dữ liệu',
|
||||
wrong: 'Đã xảy ra lỗi, vui lòng thử lại sau.',
|
||||
success: 'Thành công',
|
||||
failed: 'Thất bại',
|
||||
verify: 'Xác minh',
|
||||
unauthorizedTips: 'Không được ủy quyền, vui lòng xác minh trước.',
|
||||
},
|
||||
chat: {
|
||||
newChatButton: 'Tạo hội thoại',
|
||||
placeholder: 'Hỏi tôi bất cứ điều gì...(Shift + Enter = ngắt dòng, "/" to trigger prompts)',
|
||||
placeholderMobile: 'Hỏi tôi bất cứ điều gì...',
|
||||
copy: 'Sao chép',
|
||||
copied: 'Đã sao chép',
|
||||
copyCode: 'Sao chép Code',
|
||||
clearChat: 'Clear Chat',
|
||||
clearChatConfirm: 'Bạn có chắc chắn xóa cuộc trò chuyện này?',
|
||||
exportImage: 'Xuất hình ảnh',
|
||||
exportImageConfirm: 'Bạn có chắc chắn xuất cuộc trò chuyện này sang png không?',
|
||||
exportSuccess: 'Xuất thành công',
|
||||
exportFailed: 'Xuất thất bại',
|
||||
usingContext: 'Context Mode',
|
||||
turnOnContext: 'Ở chế độ hiện tại, việc gửi tin nhắn sẽ mang theo các bản ghi trò chuyện trước đó.',
|
||||
turnOffContext: 'Ở chế độ hiện tại, việc gửi tin nhắn sẽ không mang theo các bản ghi trò chuyện trước đó.',
|
||||
deleteMessage: 'Xóa tin nhắn',
|
||||
deleteMessageConfirm: 'Bạn có chắc chắn xóa tin nhắn này?',
|
||||
deleteHistoryConfirm: 'Bạn có chắc chắn để xóa lịch sử này?',
|
||||
clearHistoryConfirm: 'Bạn có chắc chắn để xóa lịch sử trò chuyện?',
|
||||
preview: 'Xem trước',
|
||||
showRawText: 'Hiển thị dưới dạng văn bản thô',
|
||||
},
|
||||
setting: {
|
||||
setting: 'Cài đặt',
|
||||
general: 'Chung',
|
||||
advanced: 'Nâng cao',
|
||||
config: 'Cấu hình',
|
||||
avatarLink: 'Avatar Link',
|
||||
name: 'Tên',
|
||||
description: 'Miêu tả',
|
||||
role: 'Vai trò',
|
||||
temperature: 'Nhiệt độ',
|
||||
top_p: 'Top_p',
|
||||
resetUserInfo: 'Đặt lại thông tin người dùng',
|
||||
chatHistory: 'Lịch sử trò chuyện',
|
||||
theme: 'Giao diện',
|
||||
language: 'Ngôn ngữ',
|
||||
api: 'API',
|
||||
reverseProxy: 'Reverse Proxy',
|
||||
timeout: 'Timeout',
|
||||
socks: 'Socks',
|
||||
httpsProxy: 'HTTPS Proxy',
|
||||
balance: 'API Balance',
|
||||
monthlyUsage: 'Sử dụng hàng tháng',
|
||||
},
|
||||
store: {
|
||||
siderButton: 'Prompt Store',
|
||||
local: 'Local',
|
||||
online: 'Online',
|
||||
title: 'Tiêu đề',
|
||||
description: 'Miêu tả',
|
||||
clearStoreConfirm: 'Cho dù để xóa dữ liệu?',
|
||||
importPlaceholder: 'Vui lòng dán dữ liệu JSON vào đây',
|
||||
addRepeatTitleTips: 'Tiêu đề trùng lặp, vui lòng nhập lại',
|
||||
addRepeatContentTips: 'Nội dung trùng lặp: {msg}, vui lòng nhập lại',
|
||||
editRepeatTitleTips: 'Xung đột tiêu đề, vui lòng sửa lại',
|
||||
editRepeatContentTips: 'Xung đột nội dung {msg} , vui lòng sửa đổi lại',
|
||||
importError: 'Key value mismatch',
|
||||
importRepeatTitle: 'Tiêu đề liên tục bị bỏ qua: {msg}',
|
||||
importRepeatContent: 'Nội dung liên tục bị bỏ qua: {msg}',
|
||||
onlineImportWarning: 'Lưu ý: Vui lòng kiểm tra nguồn tệp JSON!',
|
||||
downloadError: 'Vui lòng kiểm tra trạng thái mạng và tính hợp lệ của tệp JSON',
|
||||
},
|
||||
}
|
@@ -26,6 +26,7 @@ export default {
|
||||
failed: '操作失败',
|
||||
verify: '验证',
|
||||
unauthorizedTips: '未经授权,请先进行验证。',
|
||||
stopResponding: '停止响应',
|
||||
},
|
||||
chat: {
|
||||
newChatButton: '新建聊天',
|
||||
@@ -46,7 +47,7 @@ export default {
|
||||
deleteMessage: '删除消息',
|
||||
deleteMessageConfirm: '是否删除此消息?',
|
||||
deleteHistoryConfirm: '确定删除此记录?',
|
||||
clearHistoryConfirm: '确定清空聊天记录?',
|
||||
clearHistoryConfirm: '确定清空记录?',
|
||||
preview: '预览',
|
||||
showRawText: '显示原文',
|
||||
},
|
||||
|
@@ -26,6 +26,7 @@ export default {
|
||||
failed: '操作失敗',
|
||||
verify: '驗證',
|
||||
unauthorizedTips: '未經授權,請先進行驗證。',
|
||||
stopResponding: '停止回應',
|
||||
},
|
||||
chat: {
|
||||
newChatButton: '新增對話',
|
||||
|
3
src/store/helper.ts
Normal file
3
src/store/helper.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { createPinia } from 'pinia'
|
||||
|
||||
export const store = createPinia()
|
@@ -1,7 +1,5 @@
|
||||
import type { App } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
|
||||
export const store = createPinia()
|
||||
import { store } from './helper'
|
||||
|
||||
export function setupStore(app: App) {
|
||||
app.use(store)
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import type { AppState, Language, Theme } from './helper'
|
||||
import { getLocalSetting, setLocalSetting } from './helper'
|
||||
import { store } from '@/store'
|
||||
import { store } from '@/store/helper'
|
||||
|
||||
export const useAppStore = defineStore('app-store', {
|
||||
state: (): AppState => getLocalSetting(),
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { getToken, removeToken, setToken } from './helper'
|
||||
import { store } from '@/store'
|
||||
import { store } from '@/store/helper'
|
||||
import { fetchSession } from '@/api'
|
||||
|
||||
interface SessionResponse {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { getLocalState, setLocalState } from './helper'
|
||||
import { defaultState, getLocalState, setLocalState } from './helper'
|
||||
import { router } from '@/router'
|
||||
|
||||
export const useChatStore = defineStore('chat-store', {
|
||||
@@ -182,6 +182,11 @@ export const useChatStore = defineStore('chat-store', {
|
||||
}
|
||||
},
|
||||
|
||||
clearHistory() {
|
||||
this.$state = { ...defaultState() }
|
||||
this.recordState()
|
||||
},
|
||||
|
||||
async reloadRoute(uuid?: number) {
|
||||
this.recordState()
|
||||
await router.push({ name: 'Chat', params: { uuid } })
|
||||
|
@@ -9,7 +9,7 @@ interface Props {
|
||||
|
||||
interface Emit {
|
||||
(ev: 'export'): void
|
||||
(ev: 'toggleUsingContext'): void
|
||||
(ev: 'handleClear'): void
|
||||
}
|
||||
|
||||
defineProps<Props>()
|
||||
@@ -36,8 +36,8 @@ function handleExport() {
|
||||
emit('export')
|
||||
}
|
||||
|
||||
function toggleUsingContext() {
|
||||
emit('toggleUsingContext')
|
||||
function handleClear() {
|
||||
emit('handleClear')
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -62,16 +62,16 @@ function toggleUsingContext() {
|
||||
{{ currentChatHistory?.title ?? '' }}
|
||||
</h1>
|
||||
<div class="flex items-center space-x-2">
|
||||
<HoverButton @click="toggleUsingContext">
|
||||
<span class="text-xl" :class="{ 'text-[#4b9e5f]': usingContext, 'text-[#a8071a]': !usingContext }">
|
||||
<SvgIcon icon="ri:chat-history-line" />
|
||||
</span>
|
||||
</HoverButton>
|
||||
<HoverButton @click="handleExport">
|
||||
<span class="text-xl text-[#4f555e] dark:text-white">
|
||||
<SvgIcon icon="ri:download-2-line" />
|
||||
</span>
|
||||
</HoverButton>
|
||||
<HoverButton @click="handleClear">
|
||||
<span class="text-xl text-[#4f555e] dark:text-white">
|
||||
<SvgIcon icon="ri:delete-bin-line" />
|
||||
</span>
|
||||
</HoverButton>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
@@ -107,13 +107,10 @@ onUnmounted(() => {
|
||||
<div class="text-black" :class="wrapClass">
|
||||
<div ref="textRef" class="leading-relaxed break-words">
|
||||
<div v-if="!inversion">
|
||||
<div v-if="!asRawText" class="markdown-body" v-html="text" />
|
||||
<div v-if="!asRawText" class="markdown-body" :class="{ 'markdown-body-generate': loading }" v-html="text" />
|
||||
<div v-else class="whitespace-pre-wrap" v-text="text" />
|
||||
</div>
|
||||
<div v-else class="whitespace-pre-wrap" v-text="text" />
|
||||
<template v-if="loading">
|
||||
<span class="dark:text-white w-[4px] h-[20px] block animate-blink" />
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@@ -57,10 +57,60 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
&.markdown-body-generate>dd:last-child:after,
|
||||
&.markdown-body-generate>dl:last-child:after,
|
||||
&.markdown-body-generate>dt:last-child:after,
|
||||
&.markdown-body-generate>h1:last-child:after,
|
||||
&.markdown-body-generate>h2:last-child:after,
|
||||
&.markdown-body-generate>h3:last-child:after,
|
||||
&.markdown-body-generate>h4:last-child:after,
|
||||
&.markdown-body-generate>h5:last-child:after,
|
||||
&.markdown-body-generate>h6:last-child:after,
|
||||
&.markdown-body-generate>li:last-child:after,
|
||||
&.markdown-body-generate>ol:last-child li:last-child:after,
|
||||
&.markdown-body-generate>p:last-child:after,
|
||||
&.markdown-body-generate>pre:last-child code:after,
|
||||
&.markdown-body-generate>td:last-child:after,
|
||||
&.markdown-body-generate>ul:last-child li:last-child:after {
|
||||
animation: blink 1s steps(5, start) infinite;
|
||||
color: #000;
|
||||
content: '_';
|
||||
font-weight: 700;
|
||||
margin-left: 3px;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
to {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
html.dark {
|
||||
|
||||
.markdown-body {
|
||||
|
||||
&.markdown-body-generate>dd:last-child:after,
|
||||
&.markdown-body-generate>dl:last-child:after,
|
||||
&.markdown-body-generate>dt:last-child:after,
|
||||
&.markdown-body-generate>h1:last-child:after,
|
||||
&.markdown-body-generate>h2:last-child:after,
|
||||
&.markdown-body-generate>h3:last-child:after,
|
||||
&.markdown-body-generate>h4:last-child:after,
|
||||
&.markdown-body-generate>h5:last-child:after,
|
||||
&.markdown-body-generate>h6:last-child:after,
|
||||
&.markdown-body-generate>li:last-child:after,
|
||||
&.markdown-body-generate>ol:last-child li:last-child:after,
|
||||
&.markdown-body-generate>p:last-child:after,
|
||||
&.markdown-body-generate>pre:last-child code:after,
|
||||
&.markdown-body-generate>td:last-child:after,
|
||||
&.markdown-body-generate>ul:last-child li:last-child:after {
|
||||
color: #65a665;
|
||||
}
|
||||
}
|
||||
|
||||
.message-reply {
|
||||
.whitespace-pre-wrap {
|
||||
white-space: pre-wrap;
|
||||
@@ -73,3 +123,13 @@ html.dark {
|
||||
background-color: #282c34;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 533px) {
|
||||
.markdown-body .code-block-wrapper {
|
||||
padding: unset;
|
||||
|
||||
code {
|
||||
padding: 24px 16px 16px 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -93,7 +93,7 @@ async function onConversation() {
|
||||
+uuid,
|
||||
{
|
||||
dateTime: new Date().toLocaleString(),
|
||||
text: '',
|
||||
text: '思考中',
|
||||
loading: true,
|
||||
inversion: false,
|
||||
error: false,
|
||||
@@ -469,7 +469,7 @@ onUnmounted(() => {
|
||||
v-if="isMobile"
|
||||
:using-context="usingContext"
|
||||
@export="handleExport"
|
||||
@toggle-using-context="toggleUsingContext"
|
||||
@handle-clear="handleClear"
|
||||
/>
|
||||
<main class="flex-1 overflow-hidden">
|
||||
<div id="scrollRef" ref="scrollRef" class="h-full overflow-hidden overflow-y-auto">
|
||||
@@ -502,7 +502,7 @@ onUnmounted(() => {
|
||||
<template #icon>
|
||||
<SvgIcon icon="ri:stop-circle-line" />
|
||||
</template>
|
||||
Stop Responding
|
||||
{{ t('common.stopResponding') }}
|
||||
</NButton>
|
||||
</div>
|
||||
</div>
|
||||
@@ -513,7 +513,7 @@ onUnmounted(() => {
|
||||
<footer :class="footerClass">
|
||||
<div class="w-full max-w-screen-xl m-auto">
|
||||
<div class="flex items-center justify-between space-x-2">
|
||||
<HoverButton @click="handleClear">
|
||||
<HoverButton v-if="!isMobile" @click="handleClear">
|
||||
<span class="text-xl text-[#4f555e] dark:text-white">
|
||||
<SvgIcon icon="ri:delete-bin-line" />
|
||||
</span>
|
||||
@@ -523,7 +523,7 @@ onUnmounted(() => {
|
||||
<SvgIcon icon="ri:download-2-line" />
|
||||
</span>
|
||||
</HoverButton>
|
||||
<HoverButton v-if="!isMobile" @click="toggleUsingContext">
|
||||
<HoverButton @click="toggleUsingContext">
|
||||
<span class="text-xl" :class="{ 'text-[#4b9e5f]': usingContext, 'text-[#a8071a]': !usingContext }">
|
||||
<SvgIcon icon="ri:chat-history-line" />
|
||||
</span>
|
||||
|
@@ -1,16 +1,19 @@
|
||||
<script setup lang='ts'>
|
||||
import type { CSSProperties } from 'vue'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { NButton, NLayoutSider } from 'naive-ui'
|
||||
import { NButton, NLayoutSider, useDialog } from 'naive-ui'
|
||||
import List from './List.vue'
|
||||
import Footer from './Footer.vue'
|
||||
import { useAppStore, useChatStore } from '@/store'
|
||||
import { useBasicLayout } from '@/hooks/useBasicLayout'
|
||||
import { PromptStore } from '@/components/common'
|
||||
import { PromptStore, SvgIcon } from '@/components/common'
|
||||
import { t } from '@/locales'
|
||||
|
||||
const appStore = useAppStore()
|
||||
const chatStore = useChatStore()
|
||||
|
||||
const dialog = useDialog()
|
||||
|
||||
const { isMobile } = useBasicLayout()
|
||||
const show = ref(false)
|
||||
|
||||
@@ -26,6 +29,20 @@ function handleUpdateCollapsed() {
|
||||
appStore.setSiderCollapsed(!collapsed.value)
|
||||
}
|
||||
|
||||
function handleClearAll() {
|
||||
dialog.warning({
|
||||
title: t('chat.deleteMessage'),
|
||||
content: t('chat.clearHistoryConfirm'),
|
||||
positiveText: t('common.yes'),
|
||||
negativeText: t('common.no'),
|
||||
onPositiveClick: () => {
|
||||
chatStore.clearHistory()
|
||||
if (isMobile.value)
|
||||
appStore.setSiderCollapsed(true)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const getMobileClass = computed<CSSProperties>(() => {
|
||||
if (isMobile.value) {
|
||||
return {
|
||||
@@ -79,9 +96,14 @@ watch(
|
||||
<div class="flex-1 min-h-0 pb-4 overflow-hidden">
|
||||
<List />
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<NButton block @click="show = true">
|
||||
{{ $t('store.siderButton') }}
|
||||
<div class="flex items-center p-4 space-x-4">
|
||||
<div class="flex-1">
|
||||
<NButton block @click="show = true">
|
||||
{{ $t('store.siderButton') }}
|
||||
</NButton>
|
||||
</div>
|
||||
<NButton @click="handleClearAll">
|
||||
<SvgIcon icon="ri:close-circle-line" />
|
||||
</NButton>
|
||||
</div>
|
||||
</main>
|
||||
@@ -89,7 +111,7 @@ watch(
|
||||
</div>
|
||||
</NLayoutSider>
|
||||
<template v-if="isMobile">
|
||||
<div v-show="!collapsed" class="fixed inset-0 z-40 bg-black/40" @click="handleUpdateCollapsed" />
|
||||
<div v-show="!collapsed" class="fixed inset-0 z-40 w-full h-full bg-black/40" @click="handleUpdateCollapsed" />
|
||||
</template>
|
||||
<PromptStore v-model:visible="show" />
|
||||
</template>
|
||||
|
Reference in New Issue
Block a user