feat: 前后端统一接口类型
This commit is contained in:
parent
dd522eb404
commit
847623e22a
|
@ -24,7 +24,7 @@ const api = new ChatGPTAPI({ apiKey })
|
||||||
|
|
||||||
async function chatReply(message: string) {
|
async function chatReply(message: string) {
|
||||||
if (!message)
|
if (!message)
|
||||||
return sendResponse({ type: 'fail', message: 'Message is empty' })
|
return sendResponse({ type: 'Fail', message: 'Message is empty' })
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Get the last context from the chat context
|
// Get the last context from the chat context
|
||||||
|
@ -43,16 +43,17 @@ async function chatReply(message: string) {
|
||||||
if (conversationId && id)
|
if (conversationId && id)
|
||||||
chatContext.add({ conversationId, parentMessageId: id })
|
chatContext.add({ conversationId, parentMessageId: id })
|
||||||
|
|
||||||
return sendResponse({ type: 'success', data: response })
|
return sendResponse({ type: 'Success', data: response })
|
||||||
}
|
}
|
||||||
catch (error: any) {
|
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) {
|
async function chatReplayOne(message: string, options?: ChatContext) {
|
||||||
if (!message)
|
if (!message)
|
||||||
return sendResponse({ type: 'fail', message: 'Message is empty' })
|
return sendResponse({ type: 'Fail', message: 'Message is empty' })
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let messageOptions: SendMessageOptions = {}
|
let messageOptions: SendMessageOptions = {}
|
||||||
|
@ -63,18 +64,18 @@ async function chatReplayOne(message: string, options?: ChatContext) {
|
||||||
|
|
||||||
const response = await api.sendMessage(message, { ...messageOptions })
|
const response = await api.sendMessage(message, { ...messageOptions })
|
||||||
|
|
||||||
return sendResponse({ type: 'success', data: response })
|
return sendResponse({ type: 'Success', data: response })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (error: any) {
|
catch (error: any) {
|
||||||
return sendResponse({ type: 'fail', message: error.message })
|
return sendResponse({ type: 'Fail', message: error.message })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function clearChatContext() {
|
async function clearChatContext() {
|
||||||
// Clear the chat context
|
// Clear the chat context
|
||||||
chatContext.clear()
|
chatContext.clear()
|
||||||
return sendResponse({ type: 'success', message: 'Chat context cleared' })
|
return sendResponse({ type: 'Success', message: 'Chat context cleared' })
|
||||||
}
|
}
|
||||||
|
|
||||||
export { chatReply, chatReplayOne, clearChatContext }
|
export { chatReply, chatReplayOne, clearChatContext }
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
interface SendResponseOptions {
|
interface SendResponseOptions {
|
||||||
type: 'success' | 'fail'
|
type: 'Success' | 'Fail'
|
||||||
message?: string
|
message?: string
|
||||||
data?: any
|
data?: any
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sendResponse(options: SendResponseOptions) {
|
export function sendResponse(options: SendResponseOptions) {
|
||||||
if (options.type === 'success') {
|
if (options.type === 'Success') {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
message: options.message ?? 'Success',
|
message: options.message ?? null,
|
||||||
data: options.data ?? null,
|
data: options.data ?? null,
|
||||||
status: options.type,
|
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>
|
<script lang="ts" setup>
|
||||||
interface Props {
|
interface Props {
|
||||||
reversal?: boolean
|
reversal?: boolean
|
||||||
|
error?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
defineProps<Props>()
|
defineProps<Props>()
|
||||||
|
@ -8,7 +9,7 @@ defineProps<Props>()
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="p-2 mt-2 rounded-md" :class="[reversal ? 'bg-[#d2f9d1]' : 'bg-[#f4f6f8]']">
|
<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 />
|
<slot />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,6 +6,7 @@ interface Props {
|
||||||
message?: string
|
message?: string
|
||||||
dateTime?: string
|
dateTime?: string
|
||||||
reversal?: boolean
|
reversal?: boolean
|
||||||
|
error?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
defineProps<Props>()
|
defineProps<Props>()
|
||||||
|
@ -23,7 +24,7 @@ defineProps<Props>()
|
||||||
<span class="text-xs text-[#b4bbc4]">
|
<span class="text-xs text-[#b4bbc4]">
|
||||||
{{ dateTime }}
|
{{ dateTime }}
|
||||||
</span>
|
</span>
|
||||||
<Text :reversal="reversal">
|
<Text :reversal="reversal" :error="error">
|
||||||
{{ message }}
|
{{ message }}
|
||||||
</Text>
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,14 +2,15 @@
|
||||||
import { nextTick, onMounted, ref } from 'vue'
|
import { nextTick, onMounted, ref } from 'vue'
|
||||||
import { NButton, NInput, useMessage } from 'naive-ui'
|
import { NButton, NInput, useMessage } from 'naive-ui'
|
||||||
import { Message } from './components'
|
import { Message } from './components'
|
||||||
import { clearChatContext, fetchChatAPI } from './request'
|
|
||||||
import { Layout } from './layout'
|
import { Layout } from './layout'
|
||||||
|
import { clearConversations, fetchChatAPI } from '@/api'
|
||||||
import { HoverButton, SvgIcon } from '@/components/common'
|
import { HoverButton, SvgIcon } from '@/components/common'
|
||||||
|
|
||||||
interface ListProps {
|
interface ListProps {
|
||||||
dateTime: string
|
dateTime: string
|
||||||
message: string
|
message: string
|
||||||
reversal?: boolean
|
reversal?: boolean
|
||||||
|
error?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const scrollRef = ref<HTMLDivElement>()
|
const scrollRef = ref<HTMLDivElement>()
|
||||||
|
@ -30,13 +31,13 @@ function initChat() {
|
||||||
|
|
||||||
async function handleClear() {
|
async function handleClear() {
|
||||||
try {
|
try {
|
||||||
const { message } = await clearChatContext()
|
const { message } = await clearConversations()
|
||||||
ms.success(message)
|
ms.success(message ?? 'Success')
|
||||||
list.value = []
|
|
||||||
setTimeout(initChat, 100)
|
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
ms.error('Clear failed, please try again later.')
|
ms.error('Clear failed, please try again later.')
|
||||||
|
list.value = []
|
||||||
|
setTimeout(initChat, 100)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,6 +47,9 @@ function handleEnter(event: KeyboardEvent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleSubmit() {
|
async function handleSubmit() {
|
||||||
|
if (loading.value)
|
||||||
|
return
|
||||||
|
|
||||||
const message = prompt.value.trim()
|
const message = prompt.value.trim()
|
||||||
|
|
||||||
if (!message || !message.length) {
|
if (!message || !message.length) {
|
||||||
|
@ -58,19 +62,19 @@ async function handleSubmit() {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
const { text } = await fetchChatAPI(message)
|
const { data } = await fetchChatAPI(message)
|
||||||
addMessage(text, false)
|
addMessage(data?.text ?? '', false)
|
||||||
}
|
}
|
||||||
catch (error: any) {
|
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 {
|
finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addMessage(message: string, reversal = false) {
|
function addMessage(message: string, reversal = false, error = false) {
|
||||||
list.value.push({ dateTime: new Date().toLocaleString(), message, reversal })
|
list.value.push({ dateTime: new Date().toLocaleString(), message, reversal, error })
|
||||||
nextTick(() => scrollRef.value && (scrollRef.value.scrollTop = scrollRef.value.scrollHeight))
|
nextTick(() => scrollRef.value && (scrollRef.value.scrollTop = scrollRef.value.scrollHeight))
|
||||||
}
|
}
|
||||||
</script>
|
</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 ref="scrollRef" class="h-full p-4 overflow-hidden overflow-y-auto">
|
||||||
<div>
|
<div>
|
||||||
<Message
|
<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"
|
:reversal="item.reversal"
|
||||||
|
:error="item.error"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script setup lang='ts'>
|
<script setup lang='ts'>
|
||||||
import { ref, watch } from 'vue'
|
import { ref, watch } from 'vue'
|
||||||
import { NButton, NLayoutSider } from 'naive-ui'
|
import { NButton, NLayoutSider, useMessage } from 'naive-ui'
|
||||||
import List from './List.vue'
|
import List from './List.vue'
|
||||||
import Footer from './Footer.vue'
|
import Footer from './Footer.vue'
|
||||||
|
|
||||||
|
@ -18,6 +18,8 @@ const props = withDefaults(defineProps<Props>(), {
|
||||||
|
|
||||||
const emit = defineEmits<Emit>()
|
const emit = defineEmits<Emit>()
|
||||||
|
|
||||||
|
const ms = useMessage()
|
||||||
|
|
||||||
const collapsed = ref(props.collapsed)
|
const collapsed = ref(props.collapsed)
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
|
@ -28,6 +30,10 @@ watch(
|
||||||
{ immediate: true },
|
{ immediate: true },
|
||||||
)
|
)
|
||||||
|
|
||||||
|
function handleAdd() {
|
||||||
|
ms.info('Coming soon...')
|
||||||
|
}
|
||||||
|
|
||||||
function handleCollapsed() {
|
function handleCollapsed() {
|
||||||
collapsed.value = !collapsed.value
|
collapsed.value = !collapsed.value
|
||||||
emit('update:collapsed', collapsed.value)
|
emit('update:collapsed', collapsed.value)
|
||||||
|
@ -47,8 +53,8 @@ function handleCollapsed() {
|
||||||
<div class="flex flex-col h-full">
|
<div class="flex flex-col h-full">
|
||||||
<main class="flex-1 min-h-0 overflow-hidden">
|
<main class="flex-1 min-h-0 overflow-hidden">
|
||||||
<div class="p-4">
|
<div class="p-4">
|
||||||
<NButton dashed block>
|
<NButton dashed block @click="handleAdd">
|
||||||
Add chat
|
New chat
|
||||||
</NButton>
|
</NButton>
|
||||||
</div>
|
</div>
|
||||||
<List />
|
<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'>
|
<script setup lang='ts'>
|
||||||
|
import { GithubSite } from '@/components/custom'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -8,11 +8,11 @@
|
||||||
<img class="object-cover" src="@/assets/avatar.jpg" alt="avatar">
|
<img class="object-cover" src="@/assets/avatar.jpg" alt="avatar">
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-2">
|
<div class="ml-2">
|
||||||
<h2 class="text-sm font-bold">
|
<h2 class="font-bold text-md">
|
||||||
Redon
|
ChenZhaoYu
|
||||||
</h2>
|
</h2>
|
||||||
<p class="text-xs text-gray-500">
|
<p class="text-xs text-gray-500">
|
||||||
#0827
|
<GithubSite />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="fixed bottom-0 left-0 right-0 p-4 text-sm text-center text-neutral-500">
|
<div class="text-center text-neutral-500">
|
||||||
<span class="text-center">
|
<span>❤️ Star on</span>
|
||||||
❤️ Made By ChenZhaoYu -
|
<a href="https://github.com/Chanzhaoyu/chatgpt-bot" target="_blank" class="text-blue-500">
|
||||||
<a href="https://github.com/Chanzhaoyu/chatgpt-bot" target="_blank" class="text-blue-500">
|
GitHub
|
||||||
Github
|
</a>
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</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