Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6216d84ecd | ||
|
|
ea887ad755 | ||
|
|
c98a2a3cb8 | ||
|
|
06f41c3758 |
@@ -1,3 +1,11 @@
|
||||
## v2.4.1
|
||||
|
||||
`2023-02-18`
|
||||
|
||||
### Enhancement
|
||||
- 调整部份移动端上的样式
|
||||
- 输入框支持换行
|
||||
|
||||
## v2.4.0
|
||||
|
||||
`2023-02-17`
|
||||
@@ -6,6 +14,7 @@
|
||||
- 响应式支持移动端
|
||||
### Enhancement
|
||||
- 修改部份描述错误
|
||||
|
||||
## v2.3.3
|
||||
|
||||
`2023-02-16`
|
||||
|
||||
44
README.md
44
README.md
@@ -2,9 +2,7 @@
|
||||
|
||||
使用 express 和 vue3 搭建的 ChartGPT 演示网页
|
||||
|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
> 提示:目前 `OpenAI` 开放的模型最高只有 `GPT-3`,和现在网页所使用的 `GPT-3.5` 或 `GPT-4` 有很大差距,需要等官方开放最新的模型接口。
|
||||
|
||||
@@ -80,26 +78,7 @@ pnpm dev
|
||||
```
|
||||
|
||||
## 打包
|
||||
|
||||
### 后端服务
|
||||
> 如果你不需要本项目的 `node` 接口,可以省略如下操作
|
||||
|
||||
复制 `service` 文件夹到你有 `node` 服务环境的服务器上。(搜索关键字:`express部署`)
|
||||
|
||||
```shell
|
||||
# 安装
|
||||
pnpm install
|
||||
|
||||
# 打包
|
||||
pnpm build
|
||||
|
||||
# 运行
|
||||
pnpm prod
|
||||
```
|
||||
|
||||
PS: 不进行打包,直接在服务器上运行 `pnpm start` 也可
|
||||
|
||||
## Docker build & run
|
||||
## Docker build
|
||||
|
||||
[参考信息](https://github.com/Chanzhaoyu/chatgpt-web/pull/42)
|
||||
|
||||
@@ -123,8 +102,25 @@ services:
|
||||
OPENAI_API_KEY: xxxxxx
|
||||
```
|
||||
|
||||
### 后端服务
|
||||
> 如果你不需要本项目的 `node` 接口,可以省略如下操作
|
||||
|
||||
### 网页
|
||||
复制 `service` 文件夹到你有 `node` 服务环境的服务器上。(搜索关键字:`express部署`)
|
||||
|
||||
```shell
|
||||
# 安装
|
||||
pnpm install
|
||||
|
||||
# 打包
|
||||
pnpm build
|
||||
|
||||
# 运行
|
||||
pnpm prod
|
||||
```
|
||||
|
||||
PS: 不进行打包,直接在服务器上运行 `pnpm start` 也可
|
||||
|
||||
### 前端打包
|
||||
|
||||
根目录下运行以下命令,然后将 `dist` 文件夹复制到你的托管服务器上
|
||||
|
||||
|
||||
BIN
docs/c1.png
BIN
docs/c1.png
Binary file not shown.
|
Before Width: | Height: | Size: 188 KiB |
BIN
docs/c2.png
BIN
docs/c2.png
Binary file not shown.
|
Before Width: | Height: | Size: 136 KiB |
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",
|
||||
"version": "2.4.0",
|
||||
"version": "2.4.1",
|
||||
"private": false,
|
||||
"description": "ChatGPT Web",
|
||||
"author": "ChenZhaoYu <chenzhaoyu1994@gmail.com>",
|
||||
|
||||
@@ -8,6 +8,7 @@ import { useChat } from './hooks/useChat'
|
||||
import { fetchChatAPI } from '@/api'
|
||||
import { HoverButton, SvgIcon } from '@/components/common'
|
||||
import { useHistoryStore } from '@/store'
|
||||
import { useBasicLayout } from '@/hooks/useBasicLayout'
|
||||
|
||||
let controller = new AbortController()
|
||||
|
||||
@@ -15,6 +16,8 @@ const ms = useMessage()
|
||||
|
||||
const historyStore = useHistoryStore()
|
||||
|
||||
const { isMobile } = useBasicLayout()
|
||||
|
||||
let messageReactive: MessageReactive | null = null
|
||||
|
||||
const scrollRef = ref<HTMLDivElement>()
|
||||
@@ -30,6 +33,12 @@ const heartbeat = computed(() => historyStore.heartbeat)
|
||||
const list = computed<Chat.Chat[]>(() => historyStore.getCurrentChat)
|
||||
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() {
|
||||
if (loading.value)
|
||||
return
|
||||
@@ -69,8 +78,10 @@ async function handleSubmit() {
|
||||
}
|
||||
|
||||
function handleEnter(event: KeyboardEvent) {
|
||||
if (event.key === 'Enter')
|
||||
if (event.key === 'Enter' && !event.shiftKey) {
|
||||
event.preventDefault()
|
||||
handleSubmit()
|
||||
}
|
||||
}
|
||||
|
||||
function addMessage(
|
||||
@@ -143,7 +154,11 @@ watch(
|
||||
<Layout>
|
||||
<div class="flex flex-col h-full">
|
||||
<main class="flex-1 overflow-hidden">
|
||||
<div ref="scrollRef" class="h-full p-4 overflow-hidden overflow-y-auto">
|
||||
<div
|
||||
ref="scrollRef"
|
||||
class="h-full p-4 overflow-hidden overflow-y-auto"
|
||||
:class="[{ 'p-2': isMobile }]"
|
||||
>
|
||||
<template v-if="!list.length">
|
||||
<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" />
|
||||
@@ -153,21 +168,34 @@ watch(
|
||||
<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"
|
||||
v-for="(item, index) of list"
|
||||
:key="index"
|
||||
:date-time="item.dateTime"
|
||||
:message="item.message"
|
||||
:reversal="item.reversal"
|
||||
:error="item.error"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</main>
|
||||
<footer class="p-4">
|
||||
<footer
|
||||
class="p-4"
|
||||
:class="footerMobileStyle"
|
||||
>
|
||||
<div class="flex items-center justify-between space-x-2">
|
||||
<HoverButton tooltip="Clear conversations">
|
||||
<span class="text-xl text-[#4f555e]" @click="handleClear">
|
||||
<SvgIcon icon="ri:delete-bin-line" />
|
||||
</span>
|
||||
</HoverButton>
|
||||
<NInput v-model:value="prompt" placeholder="Type a message..." @keypress="handleEnter" />
|
||||
<NInput
|
||||
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>
|
||||
<SvgIcon icon="ri:send-plane-fill" />
|
||||
|
||||
@@ -22,13 +22,14 @@ const getContainerClass = computed(() => {
|
||||
return [
|
||||
'h-full',
|
||||
{ 'pt-14': isMobile.value },
|
||||
{ 'pb-[70px]': isMobile.value },
|
||||
{ 'pl-[260px]': !isMobile.value && !collapsed.value },
|
||||
]
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="h-screen p-4" :class="[{ 'p-0': isMobile }]">
|
||||
<div class="h-screen" :class="[isMobile ? 'p-0' : 'p-4']">
|
||||
<div class="h-full overflow-hidden" :class="getMobileClass">
|
||||
<NLayout class="z-40 transition" :class="getContainerClass" has-sider>
|
||||
<Sider />
|
||||
|
||||
@@ -23,7 +23,7 @@ function handleUpdateCollapsed() {
|
||||
|
||||
<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 px-4 h-14">
|
||||
<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" />
|
||||
|
||||
@@ -29,7 +29,10 @@ watch(
|
||||
(val) => {
|
||||
appStore.setSiderCollapsed(val)
|
||||
},
|
||||
{ flush: 'post' },
|
||||
{
|
||||
immediate: true,
|
||||
flush: 'post',
|
||||
},
|
||||
)
|
||||
</script>
|
||||
|
||||
@@ -42,6 +45,7 @@ watch(
|
||||
collapse-mode="transform"
|
||||
position="absolute"
|
||||
bordered
|
||||
style="z-index: 50;"
|
||||
@update-collapsed="handleUpdateCollapsed"
|
||||
>
|
||||
<div class="flex flex-col h-full" :class="[{ 'pt-14': isMobile }]">
|
||||
@@ -63,4 +67,7 @@ watch(
|
||||
</footer>
|
||||
</div>
|
||||
</NLayoutSider>
|
||||
<template v-if="isMobile">
|
||||
<div v-show="!collapsed" class="absolute inset-0 z-40 bg-black/40" @click="handleUpdateCollapsed" />
|
||||
</template>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user