Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
26de5359ef | ||
|
|
0423b87530 | ||
|
|
58464b2cee | ||
|
|
e23e1df3ce | ||
|
|
2c3bc77eb2 | ||
|
|
52b43868eb | ||
|
|
7930ec5dab |
3
.env
3
.env
@@ -1,3 +1,4 @@
|
|||||||
# Glob API URL
|
# Glob API URL
|
||||||
VITE_GLOB_API_URL=/api
|
VITE_GLOB_API_URL=/api
|
||||||
VITE_APP_API_BASE_URL = http://localhost:3002/
|
|
||||||
|
VITE_APP_API_BASE_URL=http://localhost:3002/
|
||||||
|
|||||||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -37,6 +37,7 @@
|
|||||||
"Typecheck",
|
"Typecheck",
|
||||||
"unplugin",
|
"unplugin",
|
||||||
"VITE",
|
"VITE",
|
||||||
"vueuse"
|
"vueuse",
|
||||||
|
"Zhao"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
## v2.3.1
|
||||||
|
|
||||||
|
`2023-02-15`
|
||||||
|
|
||||||
|
### BugFix
|
||||||
|
- 修复多会话状态下一些意想不到的问题
|
||||||
|
|
||||||
## v2.3.0
|
## v2.3.0
|
||||||
|
|
||||||
`2023-02-15`
|
`2023-02-15`
|
||||||
|
|||||||
@@ -1,4 +1,22 @@
|
|||||||
# 指南
|
# 贡献指南
|
||||||
|
感谢你的宝贵时间。你的贡献将使这个项目变得更好!在提交贡献之前,请务必花点时间阅读下面的入门指南。
|
||||||
|
|
||||||
|
## 语义化版本
|
||||||
|
该项目遵循语义化版本。我们对重要的漏洞修复发布修订号,对新特性或不重要的变更发布次版本号,对重大且不兼容的变更发布主版本号。
|
||||||
|
|
||||||
|
每个重大更改都将记录在 `changelog` 中。
|
||||||
|
|
||||||
|
## 提交 Pull Request
|
||||||
|
1. Fork [此仓库](https://github.com/Chanzhaoyu/chatgpt-web),从 `main` 创建分支。新功能实现请发 pull request 到 `feature` 分支。其他更改发到 `main` 分支。
|
||||||
|
2. 使用 `npm install pnpm -g` 安装 `pnpm` 工具。
|
||||||
|
3. `vscode` 安装了 `Eslint` 插件,其它编辑器如 `webStorm` 打开了 `eslint` 功能。
|
||||||
|
4. 根目录下执行 `pnpm bootstrap`。
|
||||||
|
5. `/service/` 目录下执行 `pnpm install`。
|
||||||
|
6. 对代码库进行更改。如果适用的话,请确保进行了相应的测试。
|
||||||
|
7. 请在根目录下执行 `pnpm lint:fix` 进行代码格式检查。
|
||||||
|
8. 请在根目录下执行 `pnpm type-check` 进行类型检查。
|
||||||
|
9. 提交 git commit, 请同时遵守 [Commit 规范](#commit-指南)
|
||||||
|
10. 提交 `pull request`, 如果有对应的 `issue`,请进行[关联](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword)。
|
||||||
|
|
||||||
## Commit 指南
|
## Commit 指南
|
||||||
|
|
||||||
@@ -24,3 +42,8 @@ Commit messages 请遵循[conventional-changelog 标准](https://www.conventiona
|
|||||||
- perf: 性能优化
|
- perf: 性能优化
|
||||||
- test: 单元测试
|
- test: 单元测试
|
||||||
- chore: 其他不修改 src 或测试文件的提交
|
- chore: 其他不修改 src 或测试文件的提交
|
||||||
|
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[MIT](./license)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "chatgpt-web",
|
"name": "chatgpt-web",
|
||||||
"version": "2.3.0",
|
"version": "2.3.1",
|
||||||
"private": false,
|
"private": false,
|
||||||
"description": "ChatGPT Web",
|
"description": "ChatGPT Web",
|
||||||
"author": "ChenZhaoYu <chenzhaoyu1994@gmail.com>",
|
"author": "ChenZhaoYu <chenzhaoyu1994@gmail.com>",
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ export function useChat() {
|
|||||||
function addChat(
|
function addChat(
|
||||||
message: string,
|
message: string,
|
||||||
args?: { reversal?: boolean; error?: boolean; options?: Chat.ChatOptions },
|
args?: { reversal?: boolean; error?: boolean; options?: Chat.ChatOptions },
|
||||||
uuid?: number | null,
|
|
||||||
) {
|
) {
|
||||||
historyStore.addChat(
|
historyStore.addChat(
|
||||||
{
|
{
|
||||||
@@ -16,7 +15,6 @@ export function useChat() {
|
|||||||
error: args?.error ?? false,
|
error: args?.error ?? false,
|
||||||
options: args?.options ?? undefined,
|
options: args?.options ?? undefined,
|
||||||
},
|
},
|
||||||
uuid,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ 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 { isNumber } from '@/utils/is'
|
|
||||||
|
|
||||||
let controller = new AbortController()
|
let controller = new AbortController()
|
||||||
|
|
||||||
@@ -23,6 +22,7 @@ const prompt = ref('')
|
|||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
|
||||||
const currentActive = computed(() => historyStore.active)
|
const currentActive = computed(() => historyStore.active)
|
||||||
|
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)))
|
||||||
@@ -71,9 +71,8 @@ function handleEnter(event: KeyboardEvent) {
|
|||||||
function addMessage(
|
function addMessage(
|
||||||
message: string,
|
message: string,
|
||||||
args?: { reversal?: boolean; error?: boolean; options?: Chat.ChatOptions },
|
args?: { reversal?: boolean; error?: boolean; options?: Chat.ChatOptions },
|
||||||
uuid?: number | null,
|
|
||||||
) {
|
) {
|
||||||
addChat(message, args, uuid)
|
addChat(message, args)
|
||||||
scrollToBottom()
|
scrollToBottom()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,10 +95,18 @@ onMounted(() => {
|
|||||||
scrollToBottom()
|
scrollToBottom()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
heartbeat,
|
||||||
|
() => {
|
||||||
|
handleCancel()
|
||||||
|
scrollToBottom()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
currentActive,
|
currentActive,
|
||||||
(active) => {
|
(_, oldActive) => {
|
||||||
if (isNumber(active)) {
|
if (oldActive !== null) {
|
||||||
handleCancel()
|
handleCancel()
|
||||||
scrollToBottom()
|
scrollToBottom()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,17 +12,21 @@ function handleSelect(index: number) {
|
|||||||
historyStore.chooseHistory(index)
|
historyStore.chooseHistory(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleEdit(index: number, isEdit: boolean) {
|
function handleEdit(index: number, isEdit: boolean, event?: MouseEvent) {
|
||||||
historyStore.editHistory(index, isEdit)
|
historyStore.editHistory(index, isEdit)
|
||||||
|
event?.stopPropagation()
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleRemove(index: number) {
|
function handleRemove(index: number, event?: MouseEvent) {
|
||||||
historyStore.removeHistory(index)
|
historyStore.removeHistory(index)
|
||||||
|
event?.stopPropagation()
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleEnter(index: number, isEdit: boolean, event: KeyboardEvent) {
|
function handleEnter(index: number, isEdit: boolean, event: KeyboardEvent) {
|
||||||
if (event.key === 'Enter')
|
if (event.key === 'Enter') {
|
||||||
handleEdit(index, isEdit)
|
handleEdit(index, isEdit)
|
||||||
|
event.stopPropagation()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -31,8 +35,8 @@ function handleEnter(index: number, isEdit: boolean, event: KeyboardEvent) {
|
|||||||
<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">
|
<div v-for="(item, index) of dataSources" :key="index">
|
||||||
<a
|
<a
|
||||||
class="relative flex items-center gap-3 px-3 py-3 break-all border rounded-md cursor-pointer pr-14 hover:bg-neutral-100 group"
|
class="relative flex items-center gap-3 px-3 py-3 break-all border rounded-md cursor-pointer hover:bg-neutral-100 group"
|
||||||
:class="historyStore.active === index && ['border-[#4b9e5f]', 'bg-neutral-100', 'text-[#4b9e5f]']"
|
:class="historyStore.active === index && ['border-[#4b9e5f]', 'bg-neutral-100', 'text-[#4b9e5f]', 'pr-14']"
|
||||||
@click="handleSelect(index)"
|
@click="handleSelect(index)"
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
@@ -45,17 +49,17 @@ function handleEnter(index: number, isEdit: boolean, event: KeyboardEvent) {
|
|||||||
/>
|
/>
|
||||||
<span v-else>{{ item.title }}</span>
|
<span v-else>{{ item.title }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="absolute z-10 flex visible right-1">
|
<div v-if="historyStore.active === index" class="absolute z-10 flex visible right-1">
|
||||||
<template v-if="item.isEdit">
|
<template v-if="item.isEdit">
|
||||||
<button class="p-1" @click="handleEdit(index, false)">
|
<button class="p-1" @click="handleEdit(index, false, $event)">
|
||||||
<SvgIcon icon="ri:save-line" />
|
<SvgIcon icon="ri:save-line" />
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<button class="p-1">
|
<button class="p-1">
|
||||||
<SvgIcon icon="ri:edit-line" @click="handleEdit(index, true)" />
|
<SvgIcon icon="ri:edit-line" @click="handleEdit(index, true, $event)" />
|
||||||
</button>
|
</button>
|
||||||
<button class="p-1" @click="handleRemove(index)">
|
<button class="p-1" @click="handleRemove(index, $event)">
|
||||||
<SvgIcon icon="ri:delete-bin-line" />
|
<SvgIcon icon="ri:delete-bin-line" />
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -5,10 +5,11 @@ const LOCAL_NAME = 'historyChat'
|
|||||||
export interface HistoryState {
|
export interface HistoryState {
|
||||||
historyChat: Chat.HistoryChat[]
|
historyChat: Chat.HistoryChat[]
|
||||||
active: number | null
|
active: number | null
|
||||||
|
heartbeat: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export function defaultSetting() {
|
export function defaultSetting() {
|
||||||
return { historyChat: [], active: null }
|
return { historyChat: [], active: null, heartbeat: false }
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getLocalHistory() {
|
export function getLocalHistory() {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import type { HistoryState } from './helper'
|
import type { HistoryState } from './helper'
|
||||||
import { getLocalHistory, setLocalHistory } from './helper'
|
import { getLocalHistory, setLocalHistory } from './helper'
|
||||||
|
|
||||||
export const useHistoryStore = defineStore('history-store', {
|
export const useHistoryStore = defineStore('history-store', {
|
||||||
state: (): HistoryState => getLocalHistory(),
|
state: (): HistoryState => getLocalHistory(),
|
||||||
getters: {
|
getters: {
|
||||||
@@ -18,16 +19,16 @@ export const useHistoryStore = defineStore('history-store', {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
addChat(data: Chat.Chat, uuid: number | null = null) {
|
addChat(data: Chat.Chat) {
|
||||||
if (this.active === null) {
|
if (this.active === null) {
|
||||||
this.historyChat.push({ title: data.message, isEdit: false, data: [data] })
|
this.historyChat.push({ title: data.message, isEdit: false, data: [data] })
|
||||||
this.active = this.historyChat.length - 1
|
this.active = this.historyChat.length - 1
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const active = uuid !== null ? uuid : this.active
|
if (this.historyChat[this.active].title === 'New Chat')
|
||||||
if (this.historyChat[active].title === 'New Chat')
|
this.historyChat[this.active].title = data.message
|
||||||
this.historyChat[active].title = data.message
|
|
||||||
this.historyChat[active].data.push(data)
|
this.historyChat[this.active].data.push(data)
|
||||||
}
|
}
|
||||||
setLocalHistory(this.$state)
|
setLocalHistory(this.$state)
|
||||||
},
|
},
|
||||||
@@ -52,14 +53,19 @@ export const useHistoryStore = defineStore('history-store', {
|
|||||||
|
|
||||||
removeHistory(index: number) {
|
removeHistory(index: number) {
|
||||||
this.historyChat.splice(index, 1)
|
this.historyChat.splice(index, 1)
|
||||||
|
|
||||||
if (this.active === index) {
|
if (this.active === index) {
|
||||||
if (this.historyChat.length === 0)
|
if (this.historyChat.length === 0)
|
||||||
this.active = null
|
this.active = null
|
||||||
else if (this.active === this.historyChat.length)
|
else if (this.active === this.historyChat.length)
|
||||||
this.active--
|
this.active = this.historyChat.length - 1
|
||||||
else
|
|
||||||
this.active = 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.historyChat.length === 0)
|
||||||
|
this.active = null
|
||||||
|
|
||||||
|
this.toggleHeartbeat()
|
||||||
|
|
||||||
setLocalHistory(this.$state)
|
setLocalHistory(this.$state)
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -69,5 +75,9 @@ export const useHistoryStore = defineStore('history-store', {
|
|||||||
this.active = index
|
this.active = index
|
||||||
setLocalHistory(this.$state)
|
setLocalHistory(this.$state)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
toggleHeartbeat() {
|
||||||
|
this.heartbeat = !this.heartbeat
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
2
src/typings/env.d.ts
vendored
2
src/typings/env.d.ts
vendored
@@ -1,6 +1,6 @@
|
|||||||
/// <reference types="vite/client" />
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
interface ImportMetaEnv {
|
interface ImportMetaEnv {
|
||||||
/** api url */
|
|
||||||
readonly VITE_GLOB_API_URL: string;
|
readonly VITE_GLOB_API_URL: string;
|
||||||
|
readonly VITE_APP_API_BASE_URL: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ import { defineConfig, loadEnv } from 'vite'
|
|||||||
import vue from '@vitejs/plugin-vue'
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
|
||||||
export default defineConfig((env) => {
|
export default defineConfig((env) => {
|
||||||
const viteEnv = loadEnv(env.mode, process.cwd())
|
const viteEnv = loadEnv(env.mode, process.cwd()) as unknown as ImportMetaEnv
|
||||||
|
|
||||||
return {
|
return {
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
|
|||||||
Reference in New Issue
Block a user