
14.6 系统前端
本项目的前端功能是通过Vue实现的,建立和项目后端API的连接,实现ChatGPT聊天页面功能。在本节的内容中,将详细讲解本项目前端模块的实现过程。
14.6.1 设置代理
编写文件proxy.ts创建Vite开发服务器代理的函数,其作用是在开发环境中将来自前端应用程序的HTTP请求转发到后端API服务器,以便项目测试和调试。在文件文件proxy.ts中定义了一个名为 createViteProxy 的函数,该函数接受如下两个参数:
- isOpenProxy:一个布尔值,指示代理是否打开。当代理关闭时,函数将返回 undefined。
- viteEnv:一个包含当前Vite环境变量的对象,例如 VITE_APP_API_BASE_URL 等。
文件proxy.ts的主要实现代码如下所示。
import type { ProxyOptions } from 'vite'
export function createViteProxy(isOpenProxy: boolean, viteEnv: ImportMetaEnv) {
if (!isOpenProxy)
return
const proxy: Record<string, string | ProxyOptions> = {
'/api': {
target: viteEnv.VITE_APP_API_BASE_URL,
changeOrigin: true,
rewrite: path => path.replace('/api/', '/'),
},
}
return proxy
}
如果打开代理,函数createViteProxy()将创建一个名为 proxy 的对象,并使用 viteEnv.VITE_APP_API_BASE_URL 作为后端API服务器的基本URL。然后,它将设置 /api 路径的代理规则,以便将来自前端应用程序的HTTP请求重定向到后端API服务器。同时,还可以通过 rewrite 属性将URL中的路径重写为API服务器的实际路径,例如将 /api/users 重写为 /users。
最后,它将返回代理配置对象(proxy),供Vite开发服务器使用。
14.6.2 HTTP请求
编写文件frontend\src\api\index.ts,功能是在前台页面向后台服务器发送HTTP请求,主要实现代码如下所示。
export function fetchChatAPI<T = any>(
prompt: string,
options?: { conversationId?: string; parentMessageId?: string },
signal?: GenericAbortSignal,
) {
return post<T>({
url: '/chat',
data: { prompt, options },
signal,
})
}
export function fetchChatConfig<T = any>() {
return post<T>({
url: '/config',
})
}
export function fetchChatAPIProcess<T = any>(
params: {
prompt: string
options?: { conversationId?: string; parentMessageId?: string }
baseURI?: string
accessToken?: string
isGPT4?: boolean
signal?: GenericAbortSignal
onDownloadProgress?: (progressEvent: AxiosProgressEvent) => void },
) {
const settingStore = useSettingStore()
return post<T>({
url: '/chat-process',
data: { prompt: params.prompt, options: params.options, systemMessage: settingStore.systemMessage, baseURI: params.baseURI, accessToken: params.accessToken, isGPT4: params.isGPT4 },
signal: params.signal,
onDownloadProgress: params.onDownloadProgress,
})
}
export function fetchSession<T>() {
return post<T>({
url: '/session',
})
}
export function fetchVerify<T>(token: string) {
return post<T>({
url: '/verify',
data: { token },
})
}
在上述代码中实现了用于向服务器发送HTTP请求的方法,它们使用库axios来执行网络请求,并返回Promise对象以便在异步环境中使用。对上述方法的具体说明如下:
- 方法fetchChatAPI():向 /chat 接口发送POST请求,传递一个名为 prompt 的字符串参数和一个可选的包含 conversationId 和 parentMessageId 属性的对象,用于获取聊天机器人的回复。该函数返回一个泛型类型 T 的Promise对象,可以通过 then 方法处理响应结果。
- 方法fetchChatConfig<T>():向 /config 接口发送POST请求,用于获取聊天机器人的配置信息。该函数返回一个泛型类型 T 的Promise对象,可以通过 then 方法处理响应结果。
- 方法fetchChatAPIProcess<T>(params: {...}):向 /chat-process 接口发送POST请求,传递一个包含多个参数的对象,用于获取聊天机器人的回复和处理过程。该函数返回一个泛型类型 T 的Promise对象,可以通过 then 方法处理响应结果。
- 方法fetchSession<T>():向 /session 接口发送POST请求,获取当前用户的会话信息。该函数返回一个泛型类型 T 的Promise对象,可以通过 then 方法处理响应结果。
- 方法fetchVerify<T>(token: string):向 /verify 接口发送POST请求,传递一个名为 token 的字符串参数,用于验证用户的身份。该函数返回一个泛型类型 T 的Promise对象,可以通过 then 方法处理响应结果。
上述方法都是基于 post 方法封装而成的,其中使用了一些默认请求配置,例如基本URL、请求头、超时时间等。此外,方法fetchChatAPIProcess()还支持在下载过程中获取进度信息,并接受一个可取消的 signal 参数,以便在需要时取消请求。
在实际应用程序中,我们可能需要根据具体业务需求定义更多的HTTP请求函数,并针对不同的接口和数据类型进行适当的错误处理和数据转换。
14.6.3 ChatGPT聊天界面侧边栏
编写文件frontend/src/views/chat/layout/sider/index.vue实现ChatGPT聊天界面的左侧边栏功能,主要实现代码如下所示。
const appStore = useAppStore()
const chatStore = useChatStore()
const { isMobile } = useBasicLayout()
const show = ref(false)
const collapsed = computed(() => appStore.siderCollapsed)
function handleAdd() {
chatStore.addHistory({ title: 'New Chat', uuid: Date.now(), isEdit: false })
if (isMobile.value)
appStore.setSiderCollapsed(true)
}
function handleUpdateCollapsed() {
appStore.setSiderCollapsed(!collapsed.value)
}
const getMobileClass = computed<CSSProperties>(() => {
if (isMobile.value) {
return {
position: 'fixed',
zIndex: 50,
}
}
return {}
})
const mobileSafeArea = computed(() => {
if (isMobile.value) {
return {
paddingBottom: 'env(safe-area-inset-bottom)',
}
}
return {}
})
watch(
isMobile,
(val) => {
appStore.setSiderCollapsed(val)
},
{
immediate: true,
flush: 'post',
},
)
</script>
<template>
<NLayoutSider
:collapsed="collapsed"
:collapsed-width="0"
:width="260"
:show-trigger="isMobile ? false : 'arrow-circle'"
collapse-mode="transform"
position="absolute"
bordered
:style="getMobileClass"
@update-collapsed="handleUpdateCollapsed"
>
<div class="flex flex-col h-full" :style="mobileSafeArea">
<main class="flex flex-col flex-1 min-h-0">
<div class="p-4">
<NButton dashed block @click="handleAdd">
{{ $t('chat.newChatButton') }}
</NButton>
</div>
<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') }}
</NButton>
</div>
</main>
<Footer />
</div>
</NLayoutSider>
<template v-if="isMobile">
<div v-show="!collapsed" class="fixed inset-0 z-40 bg-black/40" @click="handleUpdateCollapsed" />
</template>
<PromptStore v-model:visible="show" />
</template>
上述代码使用了多个Vue.js实用工具,例如使用computed、ref、watch等来管理侧边栏的状态和显示内容。在侧边栏中还包括了一个“新建聊天”按钮,用于创建新的聊天会话,并在侧边栏中显示已有的聊天历史记录。同时,在移动设备上,该组件还会自动适应安全区域,并提供了一个可折叠的侧边栏菜单。此外,在侧边栏中还包括了一个Footer组件,用于显示底部的版权信息。最后,该组件还引用了PromptStore组件,并在需要时显示该组件的模态对话框。