diff --git a/client/src/components/ChatBox/index.tsx b/client/src/components/ChatBox/index.tsx index db6a6bde6..e10087ee9 100644 --- a/client/src/components/ChatBox/index.tsx +++ b/client/src/components/ChatBox/index.tsx @@ -27,19 +27,20 @@ import { useMarkdown } from '@/hooks/useMarkdown'; import { AppModuleItemType, VariableItemType } from '@/types/app'; import { SystemInputEnum, VariableInputEnum } from '@/constants/app'; import { useForm } from 'react-hook-form'; -import MySelect from '@/components/Select'; import { MessageItemType } from '@/pages/api/openapi/v1/chat/completions'; -import MyTooltip from '../MyTooltip'; import { fileDownload } from '@/utils/file'; import { htmlTemplate } from '@/constants/common'; import { useRouter } from 'next/router'; +import { useGlobalStore } from '@/store/global'; import dynamic from 'next/dynamic'; const QuoteModal = dynamic(() => import('./QuoteModal')); -import styles from './index.module.scss'; import { QuoteItemType } from '@/pages/api/app/modules/kb/search'; import { FlowModuleTypeEnum } from '@/constants/flow'; +import MyTooltip from '../MyTooltip'; +import MySelect from '@/components/Select'; +import styles from './index.module.scss'; const textareaMinH = '22px'; export type StartChatFnProps = { @@ -139,6 +140,7 @@ const ChatBox = ( const { copyData } = useCopyData(); const { toast } = useToast(); const { userInfo } = useUserStore(); + const { isPc } = useGlobalStore(); const TextareaDom = useRef(null); const controller = useRef(new AbortController()); @@ -312,7 +314,7 @@ const ChatBox = ( setTimeout(() => { generatingScroll(); - TextareaDom.current?.focus(); + isPc && TextareaDom.current?.focus(); }, 100); } catch (err: any) { toast({ @@ -342,13 +344,13 @@ const ChatBox = ( [ isChatting, chatHistory, - setChatHistory, resetInputVal, toast, scrollToBottom, onStartChat, generatingMessage, - generatingScroll + generatingScroll, + isPc ] ); diff --git a/client/src/constants/model.ts b/client/src/constants/model.ts index ef05efcbb..108a8376d 100644 --- a/client/src/constants/model.ts +++ b/client/src/constants/model.ts @@ -11,7 +11,7 @@ export enum OpenAiChatEnum { export const defaultApp: AppSchema = { _id: '', userId: 'userId', - name: '模型名称', + name: '模型加载中', avatar: '/icon/logo.png', intro: '', updateTime: Date.now(), diff --git a/client/src/pages/api/app/modules/kb/search.ts b/client/src/pages/api/app/modules/kb/search.ts index 5d3392c1c..e5f342e73 100644 --- a/client/src/pages/api/app/modules/kb/search.ts +++ b/client/src/pages/api/app/modules/kb/search.ts @@ -39,8 +39,12 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex const { kb_ids = [], userChatInput } = req.body as Props; - if (!userChatInput || !Array.isArray(kb_ids)) { - throw new Error('params is error'); + if (!userChatInput) { + throw new Error('用户输入为空'); + } + + if (!Array.isArray(kb_ids) || kb_ids.length === 0) { + throw new Error('没有选择知识库'); } const result = await kbSearch({ diff --git a/client/src/pages/api/chat/shareChat/list.ts b/client/src/pages/api/chat/shareChat/list.ts index 48338b138..80e36c00c 100644 --- a/client/src/pages/api/chat/shareChat/list.ts +++ b/client/src/pages/api/chat/shareChat/list.ts @@ -27,7 +27,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) _id: item._id, shareId: item.shareId, name: item.name, - tokens: item.tokens, + total: item.total, maxContext: item.maxContext, lastTime: item.lastTime })) diff --git a/client/src/pages/api/openapi/v1/chat/completions.ts b/client/src/pages/api/openapi/v1/chat/completions.ts index 127a09e3b..dfd5de2ea 100644 --- a/client/src/pages/api/openapi/v1/chat/completions.ts +++ b/client/src/pages/api/openapi/v1/chat/completions.ts @@ -188,7 +188,8 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex // bill finishTaskBill({ - billId + billId, + shareId }); } catch (err: any) { delTaskBill(billId); diff --git a/client/src/pages/app/detail/components/Settings.tsx b/client/src/pages/app/detail/components/Settings.tsx index 900f1c02f..166aae8bb 100644 --- a/client/src/pages/app/detail/components/Settings.tsx +++ b/client/src/pages/app/detail/components/Settings.tsx @@ -51,17 +51,8 @@ const Settings = ({ appId }: { appId: string }) => { }, [appDetail, setIsLoading, toast, router]); // load app data - const { isLoading, refetch } = useQuery([appId], () => loadAppDetail(appId, true), { - onError(err: any) { - toast({ - title: err?.message || '获取应用异常', - status: 'error' - }); - router.replace('/app/list'); - }, - onSettled() { - router.prefetch(`/chat?appId=${appId}`); - } + const { refetch } = useQuery([appId], () => loadAppDetail(appId, true), { + enabled: false }); return ( @@ -168,7 +159,7 @@ const Settings = ({ appId }: { appId: string }) => { )} - + ); }; diff --git a/client/src/pages/app/detail/components/Share.tsx b/client/src/pages/app/detail/components/Share.tsx index 71b8118d7..d3c1581a4 100644 --- a/client/src/pages/app/detail/components/Share.tsx +++ b/client/src/pages/app/detail/components/Share.tsx @@ -38,6 +38,7 @@ import { defaultShareChat } from '@/constants/model'; import type { ShareChatEditType } from '@/types/app'; import MyTooltip from '@/components/MyTooltip'; import { useRequest } from '@/hooks/useRequest'; +import { formatPrice } from '@/utils/user'; const Share = ({ appId }: { appId: string }) => { const { toast } = useToast(); @@ -82,12 +83,6 @@ const Share = ({ appId }: { appId: string }) => { } }); - // format share used token - const formatTokens = (tokens: number) => { - if (tokens < 10000) return tokens; - return `${(tokens / 10000).toFixed(2)}万`; - }; - return ( @@ -118,7 +113,7 @@ const Share = ({ appId }: { appId: string }) => { 名称 最大上下文 - tokens消耗 + 金额消耗 最后使用时间 操作 @@ -128,7 +123,7 @@ const Share = ({ appId }: { appId: string }) => { {item.name} {item.maxContext} - {formatTokens(item.tokens)} + {formatPrice(item.total)}元 {item.lastTime ? formatTimeToChatTime(item.lastTime) : '未使用'} diff --git a/client/src/pages/app/detail/index.tsx b/client/src/pages/app/detail/index.tsx index 6fe6d7e8f..7e10d9cec 100644 --- a/client/src/pages/app/detail/index.tsx +++ b/client/src/pages/app/detail/index.tsx @@ -4,6 +4,8 @@ import { Box, Flex, IconButton, useTheme } from '@chakra-ui/react'; import { useUserStore } from '@/store/user'; import dynamic from 'next/dynamic'; import { defaultApp } from '@/constants/model'; +import { useToast } from '@/hooks/useToast'; +import { useQuery } from '@tanstack/react-query'; import Tabs from '@/components/Tabs'; import SideTabs from '@/components/SideTabs'; @@ -28,8 +30,9 @@ enum TabEnum { const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => { const router = useRouter(); const theme = useTheme(); + const { toast } = useToast(); const { appId } = router.query as { appId: string }; - const { appDetail = defaultApp, clearAppModules } = useUserStore(); + const { appDetail = defaultApp, loadAppDetail, clearAppModules } = useUserStore(); const setCurrentTab = useCallback( (tab: `${TabEnum}`) => { @@ -65,6 +68,19 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => { }; }, []); + useQuery([appId], () => loadAppDetail(appId, true), { + onError(err: any) { + toast({ + title: err?.message || '获取应用异常', + status: 'error' + }); + router.replace('/app/list'); + }, + onSettled() { + router.prefetch(`/chat?appId=${appId}`); + } + }); + return ( diff --git a/client/src/service/events/pushBill.ts b/client/src/service/events/pushBill.ts index 611f2e385..e0f90b4ae 100644 --- a/client/src/service/events/pushBill.ts +++ b/client/src/service/events/pushBill.ts @@ -47,7 +47,7 @@ export const pushTaskBillListItem = async ({ }); } catch (error) {} }; -export const finishTaskBill = async ({ billId }: { billId: string }) => { +export const finishTaskBill = async ({ billId, shareId }: { billId: string; shareId?: string }) => { try { // update bill const res = await Bill.findByIdAndUpdate(billId, [ @@ -63,6 +63,13 @@ export const finishTaskBill = async ({ billId }: { billId: string }) => { if (!res) return; const total = res.list.reduce((sum, item) => sum + item.amount, 0) || 0; + if (shareId) { + updateShareChatBill({ + shareId, + total + }); + } + console.log('finish bill:', formatPrice(total)); // 账号扣费 @@ -85,16 +92,19 @@ export const delTaskBill = async (billId?: string) => { export const updateShareChatBill = async ({ shareId, - tokens + total }: { shareId: string; - tokens: number; + total: number; }) => { try { - await ShareChat.findByIdAndUpdate(shareId, { - $inc: { tokens }, - lastTime: new Date() - }); + await ShareChat.findOneAndUpdate( + { shareId }, + { + $inc: { total }, + lastTime: new Date() + } + ); } catch (error) { console.log('update shareChat error', error); } diff --git a/client/src/service/models/shareChat.ts b/client/src/service/models/shareChat.ts index 7f70efb0b..f3de937b0 100644 --- a/client/src/service/models/shareChat.ts +++ b/client/src/service/models/shareChat.ts @@ -20,7 +20,7 @@ const ShareChatSchema = new Schema({ type: String, required: true }, - tokens: { + total: { type: Number, default: 0 }, diff --git a/client/src/service/utils/tools.ts b/client/src/service/utils/tools.ts index ced563159..60daf8484 100644 --- a/client/src/service/utils/tools.ts +++ b/client/src/service/utils/tools.ts @@ -25,7 +25,10 @@ export const generateToken = (userId: string) => { /* set cookie */ export const setCookie = (res: NextApiResponse, userId: string) => { - res.setHeader('Set-Cookie', `token=${generateToken(userId)}; Path=/; HttpOnly; Max-Age=604800`); + res.setHeader( + 'Set-Cookie', + `token=${generateToken(userId)}; Path=/; HttpOnly; Max-Age=604800; Samesite=None; Secure;` + ); }; /* clear cookie */ export const clearCookie = (res: NextApiResponse) => { diff --git a/client/src/types/mongoSchema.d.ts b/client/src/types/mongoSchema.d.ts index a9eb5fbd2..a0df71627 100644 --- a/client/src/types/mongoSchema.d.ts +++ b/client/src/types/mongoSchema.d.ts @@ -137,7 +137,7 @@ export interface ShareChatSchema { userId: string; appId: string; name: string; - tokens: number; + total: number; maxContext: number; lastTime: Date; }