Jelajahi Sumber

问答历史

李富豪 11 bulan lalu
induk
melakukan
861c702015

+ 8 - 4
app/api/api.ts

@@ -16,11 +16,15 @@ axiosInstance.interceptors.request.use(
 // 响应拦截器
 axiosInstance.interceptors.response.use(
     (response: AxiosResponse) => {// 成功信息
-        const { data } = response;
-        if (data.code === 200) {// 成功
+        const { config, data } = response;
+        if (config.responseType === 'blob') {
             return Promise.resolve(data);
-        } else {// 失败
-            return Promise.reject(data);
+        } else {
+            if (data.code === 200) {// 成功
+                return Promise.resolve(data);
+            } else {// 失败
+                return Promise.reject(data);
+            }
         }
     },
     (error) => {// 错误信息

+ 3 - 0
app/client/platforms/bigmodel.ts

@@ -154,6 +154,9 @@ export class BigModelApi implements LLMApi {
           const session = useChatStore.getState().sessions[0];
           const data = {
             id: session.id,
+            appId: session.appId,
+            userId: undefined,
+            dialogName: session.topic,
             messages: session.messages.map(item => ({
               id: item.id,
               date: item.date,

+ 13 - 14
app/components/chat.tsx

@@ -75,6 +75,7 @@ import dynamic from "next/dynamic";
 import { ChatControllerPool } from "../client/controller";
 import { DalleSize } from "../typing";
 import { Prompt, usePromptStore } from "../store/prompt";
+import { useGlobalStore } from "../store";
 import Locale from "../locales";
 
 import { IconButton } from "./button";
@@ -910,27 +911,23 @@ function _Chat() {
 
   const [appList, setAppList] = useState<AppList>([]);
   const [appValue, setAppValue] = useState<string>();
+  const globalStore = useGlobalStore();
 
   const init = async () => {
     setLoading(true);
     try {
       const res = await api.get('/bigmodel/api/application/list');
-      const list = res.data.map((item: any) => {
+      const list = res.data.filter((item: any) => item.appId !== '1234567890123456789').map((item: any) => {
         return {
           label: item.name,
           value: item.appId,
         }
       })
       setAppList(list);
-      const appId = localStorage.getItem('appId');
       let newAppValue = '';
-      if (appId) {
-        setAppValue(appId);
-        newAppValue = appId;
-      } else {
-        setAppValue(list[0]?.value);
-        newAppValue = list[0]?.value;
-      }
+      setAppValue(list[0]?.value);
+      newAppValue = list[0]?.value;
+      globalStore.setSelectedAppId(newAppValue);
       chatStore.updateCurrentSession((session) => (session.appId = newAppValue));
     } catch (error) {
       console.error(error);
@@ -1472,10 +1469,12 @@ function _Chat() {
                   options={appList}
                   value={appValue}
                   onChange={(value) => {
-                    // setAppValue(value);
-                    // localStorage.clear();
-                    // localStorage.setItem('appId', value as string);
-                    // location.reload()
+                    setAppValue(value);
+                    globalStore.setSelectedAppId(value);
+                    chatStore.clearSessions();
+                    chatStore.updateCurrentSession((values) => {
+                      values.appId = value;
+                    });
                   }}
                 />
                 :
@@ -1835,7 +1834,7 @@ function _Chat() {
           )}
           <IconButton
             icon={<SendWhiteIcon />}
-            text={Locale.Chat.Send}
+            text='发送'
             className={styles["chat-input-send"]}
             type="primary"
             onClick={() => doSubmit(userInput)}

+ 131 - 14
app/components/sidebar.tsx

@@ -16,7 +16,7 @@ import faviconSrc from "../icons/favicon.png";
 import { EditOutlined } from '@ant-design/icons';
 import Locale from "../locales";
 
-import { useAppConfig, useChatStore } from "../store";
+import { useAppConfig, useChatStore, useGlobalStore } from "../store";
 
 import {
   DEFAULT_SIDEBAR_WIDTH,
@@ -32,7 +32,10 @@ import { Link, useNavigate } from "react-router-dom";
 import { isIOS, useMobileScreen } from "../utils";
 import dynamic from "next/dynamic";
 import api from "@/app/api/api";
-import { Button, Dropdown, Menu } from "antd";
+import { Button, Dropdown, Form, Input, Menu, Modal } from "antd";
+import { downloadFile } from "../utils/index";
+
+const FormItem = Form.Item;
 
 const ChatList = dynamic(async () => (await import("./chat-list")).ChatList, {
   loading: () => null,
@@ -147,9 +150,9 @@ export function SideBarContainer(props: {
       className={`${styles.sidebar} ${className} ${shouldNarrow && styles["narrow-sidebar"]
         }`}
       style={{
-        // #3016 disable transition on ios mobile screen
         transition: isMobileScreen && isIOSMobile ? "none" : undefined,
-        background: '#FFFFFF'
+        background: '#FFFFFF',
+        overflowY: "auto",
       }}
     >
       {children}
@@ -212,20 +215,23 @@ export function SideBarTail(props: {
   );
 }
 
-export function SideBar(props: { className?: string }) {
+export const SideBar = (props: { className?: string }) => {
   useHotKey();
   const { onDragStart, shouldNarrow } = useDragSideBar();
   const [showPluginSelector, setShowPluginSelector] = useState(false);
   const navigate = useNavigate();
   const config = useAppConfig();
   const chatStore = useChatStore();
+  const globalStore = useGlobalStore();
 
   const [menuList, setMenuList] = useState([])
+  const [modalOpen, setModalOpen] = useState(false)
+  const [form] = Form.useForm();
 
   // 获取聊天列表
   const fetchChatList = async () => {
     try {
-      const res = await api.get('/bigmodel/api/dialog/list');
+      const res = await api.get(`/bigmodel/api/dialog/list/${globalStore.selectedAppId}`);
       const list = res.data.map((item: any) => {
         return {
           ...item,
@@ -233,18 +239,54 @@ export function SideBar(props: { className?: string }) {
             const items = [
               {
                 key: '1',
-                label: '重命名',
+                label: (
+                  <a onClick={() => {
+                    setModalOpen(true);
+                    form.setFieldsValue({
+                      dialogId: child.key,
+                      dialogName: child.label
+                    });
+                  }}>
+                    重命名
+                  </a>
+                ),
               },
               {
                 key: '2',
-                label: '导出',
+                label: (
+                  <a onClick={async () => {
+                    try {
+                      const blob = await api.post(`/bigmodel/api/dialog/export/${child.key}`, {}, { responseType: 'blob' });
+                      const fileName = `${child.label}.xlsx`;
+                      downloadFile(blob, fileName);
+                    } catch (error) {
+                      console.error(error);
+                    }
+                  }}>
+                    导出
+                  </a>
+                ),
               },
               {
                 key: '3',
-                label: '删除',
+                label: (
+                  <a onClick={async () => {
+                    try {
+                      await api.delete(`/bigmodel/api/dialog/del/${child.key}`);
+                      await fetchChatList()
+                      chatStore.clearSessions();
+                      chatStore.updateCurrentSession((value) => {
+                        value.appId = globalStore.selectedAppId;
+                      });
+                    } catch (error) {
+                      console.error(error);
+                    }
+                  }}>
+                    删除
+                  </a>
+                ),
               },
             ];
-
             return {
               ...child,
               label: <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
@@ -253,7 +295,7 @@ export function SideBar(props: { className?: string }) {
                 </div>
                 <div style={{ width: 20 }}>
                   <Dropdown menu={{ items }} trigger={['click']} placement="bottomRight">
-                    <EditOutlined />
+                    <EditOutlined onClick={(e) => e.stopPropagation()} />
                   </Dropdown>
                 </div>
               </div>
@@ -269,6 +311,10 @@ export function SideBar(props: { className?: string }) {
 
   useEffect(() => {
     fetchChatList();
+  }, [globalStore.selectedAppId]);
+
+  useEffect(() => {
+    chatStore.clearSessions();
   }, []);
 
   return (
@@ -307,7 +353,10 @@ export function SideBar(props: { className?: string }) {
           type="primary"
           style={{ marginBottom: 20 }}
           onClick={() => {
-            chatStore.newSession();
+            chatStore.clearSessions();
+            chatStore.updateCurrentSession((value) => {
+              value.appId = globalStore.selectedAppId;
+            });
             navigate(Path.Chat);
           }}
         >
@@ -395,10 +444,78 @@ export function SideBar(props: { className?: string }) {
       /> */}
       <Menu
         style={{ border: 'none' }}
-        // onClick={onClick}
+        onClick={async ({ key }) => {
+          const res = await api.get(`/bigmodel/api/dialog/detail/${key}`);
+          const list = res.data.map(((item: any) => {
+            return {
+              content: item.content,
+              date: item.create_time,
+              id: item.did,
+              role: item.type,
+            }
+          }))
+          const session = {
+            appId: res.data.length ? res.data[0].appId : '',
+            dialogName: res.data.length ? res.data[0].dialog_name : '',
+            id: res.data.length ? res.data[0].id : '',
+            messages: list,
+          }
+          globalStore.setCurrentSession(session);
+          chatStore.clearSessions();
+          chatStore.updateCurrentSession((value) => {
+            value.appId = session.appId;
+            value.topic = session.dialogName;
+            value.id = session.id;
+            value.messages = list;
+          });
+          navigate('/chat', { state: { fromHome: true } });
+        }}
         mode="inline"
         items={menuList}
       />
+      <Modal
+        title="重命名"
+        open={modalOpen}
+        width={300}
+        maskClosable={false}
+        onOk={() => {
+          form.validateFields().then(async (values) => {
+            setModalOpen(false);
+            try {
+              await api.put('/bigmodel/api/dialog/update', {
+                id: values.dialogId,
+                dialogName: values.dialogName
+              });
+              await fetchChatList()
+              chatStore.updateCurrentSession((value) => {
+                value.topic = values.dialogName;
+              });
+            } catch (error) {
+              console.error(error);
+            }
+          }).catch((error) => {
+            console.error(error);
+          });
+        }}
+        onCancel={() => {
+          setModalOpen(false);
+        }}
+      >
+        <Form form={form} layout='inline'>
+          <FormItem name='dialogId' noStyle />
+          <FormItem
+            label='名称'
+            name='dialogName'
+            rules={[{ required: true, message: '名称不能为空', whitespace: true }]}
+          >
+            <Input
+              style={{ width: 300 }}
+              placeholder='请输入'
+              maxLength={20}
+            />
+          </FormItem>
+        </Form>
+      </Modal>
     </SideBarContainer>
   );
-}
+}

+ 15 - 15
app/masks/index.ts

@@ -24,18 +24,18 @@ export const BUILTIN_MASK_STORE = {
 
 export const BUILTIN_MASKS: BuiltinMask[] = [];
 
-if (typeof window != "undefined") {
-  // run in browser skip in next server
-  fetch("/masks.json")
-    .then((res) => res.json())
-    .catch((error) => {
-      console.error("[Fetch] failed to fetch masks", error);
-      return { cn: [], tw: [], en: [] };
-    })
-    .then((masks) => {
-      const { cn = [], tw = [], en = [] } = masks;
-      return [...cn, ...tw, ...en].map((m) => {
-        BUILTIN_MASKS.push(BUILTIN_MASK_STORE.add(m));
-      });
-    });
-}
+// if (typeof window != "undefined") {
+//   // run in browser skip in next server
+//   fetch("/masks.json")
+//     .then((res) => res.json())
+//     .catch((error) => {
+//       console.error("[Fetch] failed to fetch masks", error);
+//       return { cn: [], tw: [], en: [] };
+//     })
+//     .then((masks) => {
+//       const { cn = [], tw = [], en = [] } = masks;
+//       return [...cn, ...tw, ...en].map((m) => {
+//         BUILTIN_MASKS.push(BUILTIN_MASK_STORE.add(m));
+//       });
+//     });
+// }

+ 27 - 0
app/store/global.ts

@@ -0,0 +1,27 @@
+import { createPersistStore } from "../utils/store";
+
+const state = {
+  selectedAppId: "",
+  currentSession: {
+    appId: '',
+    dialogName: '',
+    id: '',
+    messages: [],
+  },
+};
+
+export const useGlobalStore = createPersistStore(
+  { ...state },
+  (set, get) => ({
+    setSelectedAppId(appId: string) {
+      set({ selectedAppId: appId });
+    },
+    setCurrentSession(session: any) {
+      set({ currentSession: session });
+    },
+  }),
+  {
+    name: "Global",
+    version: 1,
+  },
+);

+ 1 - 0
app/store/index.ts

@@ -2,3 +2,4 @@ export * from "./chat";
 export * from "./update";
 export * from "./access";
 export * from "./config";
+export * from "./global";

+ 10 - 0
app/utils/index.ts

@@ -0,0 +1,10 @@
+export const downloadFile = (blob: any, fileName: string) => {
+    const downUrl = window.URL.createObjectURL(new Blob([blob]));
+    const elementA = document.createElement('a');
+    elementA.href = downUrl;
+    elementA.download = fileName;
+    elementA.style.display = 'none';
+    document.body.appendChild(elementA);
+    elementA.click();
+    document.body.removeChild(elementA);
+};