feat: 前后端统一接口类型
This commit is contained in:
parent
dd522eb404
commit
847623e22a
|
@ -24,7 +24,7 @@ const api = new ChatGPTAPI({ apiKey })
|
|||
|
||||
async function chatReply(message: string) {
|
||||
if (!message)
|
||||
return sendResponse({ type: 'fail', message: 'Message is empty' })
|
||||
return sendResponse({ type: 'Fail', message: 'Message is empty' })
|
||||
|
||||
try {
|
||||
// Get the last context from the chat context
|
||||
|
@ -43,16 +43,17 @@ async function chatReply(message: string) {
|
|||
if (conversationId && id)
|
||||
chatContext.add({ conversationId, parentMessageId: id })
|
||||
|
||||
return sendResponse({ type: 'success', data: response })
|
||||
return sendResponse({ type: 'Success', data: response })
|
||||
}
|
||||
catch (error: any) {
|
||||
return sendResponse({ type: 'fail', message: error.message })
|
||||
global.console.log(error)
|
||||
return sendResponse({ type: 'Fail', message: error.message })
|
||||
}
|
||||
}
|
||||
|
||||
async function chatReplayOne(message: string, options?: ChatContext) {
|
||||
if (!message)
|
||||
return sendResponse({ type: 'fail', message: 'Message is empty' })
|
||||
return sendResponse({ type: 'Fail', message: 'Message is empty' })
|
||||
|
||||
try {
|
||||
let messageOptions: SendMessageOptions = {}
|
||||
|
@ -63,18 +64,18 @@ async function chatReplayOne(message: string, options?: ChatContext) {
|
|||
|
||||
const response = await api.sendMessage(message, { ...messageOptions })
|
||||
|
||||
return sendResponse({ type: 'success', data: response })
|
||||
return sendResponse({ type: 'Success', data: response })
|
||||
}
|
||||
}
|
||||
catch (error: any) {
|
||||
return sendResponse({ type: 'fail', message: error.message })
|
||||
return sendResponse({ type: 'Fail', message: error.message })
|
||||
}
|
||||
}
|
||||
|
||||
async function clearChatContext() {
|
||||
// Clear the chat context
|
||||
chatContext.clear()
|
||||
return sendResponse({ type: 'success', message: 'Chat context cleared' })
|
||||
return sendResponse({ type: 'Success', message: 'Chat context cleared' })
|
||||
}
|
||||
|
||||
export { chatReply, chatReplayOne, clearChatContext }
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
interface SendResponseOptions {
|
||||
type: 'success' | 'fail'
|
||||
type: 'Success' | 'Fail'
|
||||
message?: string
|
||||
data?: any
|
||||
}
|
||||
|
||||
export function sendResponse(options: SendResponseOptions) {
|
||||
if (options.type === 'success') {
|
||||
if (options.type === 'Success') {
|
||||
return Promise.resolve({
|
||||
message: options.message ?? 'Success',
|
||||
message: options.message ?? null,
|
||||
data: options.data ?? null,
|
||||
status: options.type,
|
||||
})
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
import { post } from '@/utils/request'
|
||||
|
||||
export function fetchChatAPI<T = any>(prompt: string) {
|
||||
return post<T>({
|
||||
url: '/chat',
|
||||
data: { prompt },
|
||||
})
|
||||
}
|
||||
|
||||
export function clearConversations<T = any>() {
|
||||
return post<T>({
|
||||
url: '/clear',
|
||||
})
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
<script lang="ts" setup>
|
||||
interface Props {
|
||||
reversal?: boolean
|
||||
error?: boolean
|
||||
}
|
||||
|
||||
defineProps<Props>()
|
||||
|
@ -8,7 +9,7 @@ defineProps<Props>()
|
|||
|
||||
<template>
|
||||
<div class="p-2 mt-2 rounded-md" :class="[reversal ? 'bg-[#d2f9d1]' : 'bg-[#f4f6f8]']">
|
||||
<span class="leading-relaxed whitespace-pre-wrap">
|
||||
<span class="leading-relaxed whitespace-pre-wrap" :class="[{ 'text-red-400': error }]">
|
||||
<slot />
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
@ -6,6 +6,7 @@ interface Props {
|
|||
message?: string
|
||||
dateTime?: string
|
||||
reversal?: boolean
|
||||
error?: boolean
|
||||
}
|
||||
|
||||
defineProps<Props>()
|
||||
|
@ -23,7 +24,7 @@ defineProps<Props>()
|
|||
<span class="text-xs text-[#b4bbc4]">
|
||||
{{ dateTime }}
|
||||
</span>
|
||||
<Text :reversal="reversal">
|
||||
<Text :reversal="reversal" :error="error">
|
||||
{{ message }}
|
||||
</Text>
|
||||
</div>
|
||||
|
|
|
@ -2,14 +2,15 @@
|
|||
import { nextTick, onMounted, ref } from 'vue'
|
||||
import { NButton, NInput, useMessage } from 'naive-ui'
|
||||
import { Message } from './components'
|
||||
import { clearChatContext, fetchChatAPI } from './request'
|
||||
import { Layout } from './layout'
|
||||
import { clearConversations, fetchChatAPI } from '@/api'
|
||||
import { HoverButton, SvgIcon } from '@/components/common'
|
||||
|
||||
interface ListProps {
|
||||
dateTime: string
|
||||
message: string
|
||||
reversal?: boolean
|
||||
error?: boolean
|
||||
}
|
||||
|
||||
const scrollRef = ref<HTMLDivElement>()
|
||||
|
@ -30,13 +31,13 @@ function initChat() {
|
|||
|
||||
async function handleClear() {
|
||||
try {
|
||||
const { message } = await clearChatContext()
|
||||
ms.success(message)
|
||||
list.value = []
|
||||
setTimeout(initChat, 100)
|
||||
const { message } = await clearConversations()
|
||||
ms.success(message ?? 'Success')
|
||||
}
|
||||
catch (error) {
|
||||
ms.error('Clear failed, please try again later.')
|
||||
list.value = []
|
||||
setTimeout(initChat, 100)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,6 +47,9 @@ function handleEnter(event: KeyboardEvent) {
|
|||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
if (loading.value)
|
||||
return
|
||||
|
||||
const message = prompt.value.trim()
|
||||
|
||||
if (!message || !message.length) {
|
||||
|
@ -58,19 +62,19 @@ async function handleSubmit() {
|
|||
|
||||
try {
|
||||
loading.value = true
|
||||
const { text } = await fetchChatAPI(message)
|
||||
addMessage(text, false)
|
||||
const { data } = await fetchChatAPI(message)
|
||||
addMessage(data?.text ?? '', false)
|
||||
}
|
||||
catch (error: any) {
|
||||
addMessage(error.message ?? 'Request failed, please try again later.', false)
|
||||
addMessage(`Error: ${error.message ?? 'Request failed, please try again later.'}`, false, true)
|
||||
}
|
||||
finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function addMessage(message: string, reversal = false) {
|
||||
list.value.push({ dateTime: new Date().toLocaleString(), message, reversal })
|
||||
function addMessage(message: string, reversal = false, error = false) {
|
||||
list.value.push({ dateTime: new Date().toLocaleString(), message, reversal, error })
|
||||
nextTick(() => scrollRef.value && (scrollRef.value.scrollTop = scrollRef.value.scrollHeight))
|
||||
}
|
||||
</script>
|
||||
|
@ -82,8 +86,12 @@ function addMessage(message: string, reversal = false) {
|
|||
<div ref="scrollRef" class="h-full p-4 overflow-hidden overflow-y-auto">
|
||||
<div>
|
||||
<Message
|
||||
v-for="(item, index) of list" :key="index" :date-time="item.dateTime" :message="item.message"
|
||||
v-for="(item, index) of list"
|
||||
:key="index"
|
||||
:date-time="item.dateTime"
|
||||
:message="item.message"
|
||||
:reversal="item.reversal"
|
||||
:error="item.error"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang='ts'>
|
||||
import { ref, watch } from 'vue'
|
||||
import { NButton, NLayoutSider } from 'naive-ui'
|
||||
import { NButton, NLayoutSider, useMessage } from 'naive-ui'
|
||||
import List from './List.vue'
|
||||
import Footer from './Footer.vue'
|
||||
|
||||
|
@ -18,6 +18,8 @@ const props = withDefaults(defineProps<Props>(), {
|
|||
|
||||
const emit = defineEmits<Emit>()
|
||||
|
||||
const ms = useMessage()
|
||||
|
||||
const collapsed = ref(props.collapsed)
|
||||
|
||||
watch(
|
||||
|
@ -28,6 +30,10 @@ watch(
|
|||
{ immediate: true },
|
||||
)
|
||||
|
||||
function handleAdd() {
|
||||
ms.info('Coming soon...')
|
||||
}
|
||||
|
||||
function handleCollapsed() {
|
||||
collapsed.value = !collapsed.value
|
||||
emit('update:collapsed', collapsed.value)
|
||||
|
@ -47,8 +53,8 @@ function handleCollapsed() {
|
|||
<div class="flex flex-col h-full">
|
||||
<main class="flex-1 min-h-0 overflow-hidden">
|
||||
<div class="p-4">
|
||||
<NButton dashed block>
|
||||
Add chat
|
||||
<NButton dashed block @click="handleAdd">
|
||||
New chat
|
||||
</NButton>
|
||||
</div>
|
||||
<List />
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
import axios from 'axios'
|
||||
|
||||
const BASE_URL = import.meta.env.VITE_GLOB_API_URL
|
||||
|
||||
async function fetchChatAPI(message: string) {
|
||||
if (!message || message.trim() === '')
|
||||
return
|
||||
|
||||
try {
|
||||
const { status, data } = await axios.post(`${BASE_URL}/chat`, { message })
|
||||
|
||||
if (status === 200) {
|
||||
if (data.text)
|
||||
return Promise.resolve(data)
|
||||
|
||||
if (data.statusText)
|
||||
return Promise.reject(new Error(data.statusText))
|
||||
}
|
||||
|
||||
return Promise.reject(new Error('Request failed'))
|
||||
}
|
||||
catch (error) {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
}
|
||||
|
||||
async function clearChatContext() {
|
||||
try {
|
||||
const { status, data } = await axios.post(`${BASE_URL}/clear`)
|
||||
|
||||
if (status === 200)
|
||||
return Promise.resolve(data)
|
||||
|
||||
return Promise.reject(new Error('Request failed'))
|
||||
}
|
||||
catch (error) {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
}
|
||||
|
||||
export { fetchChatAPI, clearChatContext }
|
|
@ -1,5 +1,5 @@
|
|||
<script setup lang='ts'>
|
||||
|
||||
import { GithubSite } from '@/components/custom'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -8,11 +8,11 @@
|
|||
<img class="object-cover" src="@/assets/avatar.jpg" alt="avatar">
|
||||
</div>
|
||||
<div class="ml-2">
|
||||
<h2 class="text-sm font-bold">
|
||||
Redon
|
||||
<h2 class="font-bold text-md">
|
||||
ChenZhaoYu
|
||||
</h2>
|
||||
<p class="text-xs text-gray-500">
|
||||
#0827
|
||||
<GithubSite />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
<template>
|
||||
<div class="fixed bottom-0 left-0 right-0 p-4 text-sm text-center text-neutral-500">
|
||||
<span class="text-center">
|
||||
❤️ Made By ChenZhaoYu -
|
||||
<div class="text-center text-neutral-500">
|
||||
<span>❤️ Star on</span>
|
||||
<a href="https://github.com/Chanzhaoyu/chatgpt-bot" target="_blank" class="text-blue-500">
|
||||
Github
|
||||
GitHub
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
import axios, { type AxiosResponse } from 'axios'
|
||||
|
||||
const service = axios.create({
|
||||
baseURL: import.meta.env.VITE_GLOB_API_URL,
|
||||
timeout: 10 * 1000,
|
||||
})
|
||||
|
||||
service.interceptors.request.use(
|
||||
(config) => {
|
||||
return config
|
||||
},
|
||||
(error) => {
|
||||
return Promise.reject(error.response)
|
||||
},
|
||||
)
|
||||
|
||||
service.interceptors.response.use(
|
||||
(response: AxiosResponse): AxiosResponse => {
|
||||
if (response.status === 200)
|
||||
return response
|
||||
|
||||
throw new Error(response.status.toString())
|
||||
},
|
||||
(error) => {
|
||||
return Promise.reject(error)
|
||||
},
|
||||
)
|
||||
|
||||
export default service
|
|
@ -0,0 +1,72 @@
|
|||
import type { AxiosResponse } from 'axios'
|
||||
import request from './axios'
|
||||
|
||||
export interface HttpOption {
|
||||
url: string
|
||||
data?: any
|
||||
method?: string
|
||||
headers?: any
|
||||
beforeRequest?: () => void
|
||||
afterRequest?: () => void
|
||||
}
|
||||
|
||||
export interface ExtraOption {
|
||||
notification?: boolean
|
||||
}
|
||||
|
||||
export interface Response<T = any> {
|
||||
data: T
|
||||
message: string | null
|
||||
status: string
|
||||
}
|
||||
|
||||
function http<T = any>({ url, data, method, headers, beforeRequest, afterRequest }: HttpOption) {
|
||||
const successHandler = (res: AxiosResponse<Response<T>>) => {
|
||||
if (res.data.status === 'Success')
|
||||
return res.data
|
||||
|
||||
return Promise.reject(res.data)
|
||||
}
|
||||
|
||||
const failHandler = (error: Response<Error>) => {
|
||||
afterRequest?.()
|
||||
throw new Error(error?.message || 'Error')
|
||||
}
|
||||
|
||||
beforeRequest?.()
|
||||
|
||||
method = method || 'GET'
|
||||
|
||||
const params = Object.assign(typeof data === 'function' ? data() : data ?? {}, {})
|
||||
|
||||
return method === 'GET'
|
||||
? request.get(url, { params }).then(successHandler, failHandler)
|
||||
: request.post(url, params, { headers }).then(successHandler, failHandler)
|
||||
}
|
||||
|
||||
export function get<T = any>(
|
||||
{ url, data, method = 'GET', beforeRequest, afterRequest }: HttpOption,
|
||||
): Promise<Response<T>> {
|
||||
return http<T>({
|
||||
url,
|
||||
method,
|
||||
data,
|
||||
beforeRequest,
|
||||
afterRequest,
|
||||
})
|
||||
}
|
||||
|
||||
export function post<T = any>(
|
||||
{ url, data, method = 'POST', headers, beforeRequest, afterRequest }: HttpOption,
|
||||
): Promise<Response<T>> {
|
||||
return http<T>({
|
||||
url,
|
||||
method,
|
||||
data,
|
||||
headers,
|
||||
beforeRequest,
|
||||
afterRequest,
|
||||
})
|
||||
}
|
||||
|
||||
export default post
|
Loading…
Reference in New Issue