李富豪 před 5 měsíci
rodič
revize
5ee2c37b00

+ 2 - 0
app/components/DeepSeekChat.tsx

@@ -1909,10 +1909,12 @@ function _Chat() {
 }
 
 export function Chat() {
+  const globalStore = useGlobalStore();
   const chatStore = useChatStore();
   const sessionIndex = chatStore.currentSessionIndex;
 
   useEffect(() => {
+    globalStore.setShowMenu(true);
     chatStore.setModel('DeepSeek');
     chatStore.setWebSearch(false);
   }, []);

+ 27 - 15
app/components/chat.tsx

@@ -8,7 +8,7 @@ import React, {
   Fragment,
   RefObject,
 } from "react";
-import { HomeOutlined } from '@ant-design/icons';
+import { HomeOutlined, MenuOutlined } from '@ant-design/icons';
 import SendWhiteIcon from "../icons/send-white.svg";
 import BrainIcon from "../icons/brain.svg";
 import RenameIcon from "../icons/rename.svg";
@@ -1762,25 +1762,25 @@ function _Chat() {
   return (
     <div className={styles.chat} key={session.id}>
       {
-        !location.search.includes('showMenu=false') &&
         <div className="window-header" data-tauri-drag-region>
           <div style={{ display: 'flex', alignItems: 'center' }}
             className={`window-header-title ${styles["chat-body-title"]}`}>
-            <div>
-              {
-                isMobileScreen &&
-                <IconButton
-                  style={{ padding: 0, marginRight: 20 }}
-                  icon={<LeftIcon />}
-                  text={Locale.NewChat.Return}
-                  onClick={() => navigate('/knowledgeChat')}
+            {
+              <div style={{ marginRight: 10 }}>
+                <Button
+                  type='text'
+                  icon={<MenuOutlined />}
+                  onClick={() => {
+                    globalStore.setShowMenu(!globalStore.showMenu);
+                  }}
                 />
-              }
-            </div>
-            <div>
+              </div>
+            }
+            {
+              false &&
               <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
                 <Select
-                  style={{ width: '100%', height: 38, marginRight: 5 }}
+                  style={{ width: '100%', height: 38, marginRight: 10 }}
                   value={selectedFruit}
                   onChange={(value: "ONLINE" | "LOCAL") => {
                     chatStore.clearSessions();
@@ -1825,7 +1825,7 @@ function _Chat() {
                     null
                 }
               </div>
-            </div>
+            }
           </div>
           <div className="window-actions">
             <div className="window-action-button">
@@ -2280,6 +2280,7 @@ function _Chat() {
 }
 
 export function Chat() {
+  const globalStore = useGlobalStore();
   const chatStore = useChatStore();
   const location = useLocation();
   const sessionIndex = chatStore.currentSessionIndex;
@@ -2288,8 +2289,19 @@ export function Chat() {
     chatStore.setModel('BigModel');
     const search = location.search;
     const params = new URLSearchParams(search);
+    const showMenu = params.get('showMenu');
     const chatMode = params.get('chatMode');
 
+    if (showMenu) {
+      if (showMenu === 'true') {
+        globalStore.setShowMenu(true);
+      } else if (showMenu === 'false') {
+        globalStore.setShowMenu(false);
+      }
+    } else {
+      globalStore.setShowMenu(true);
+    }
+
     if (chatMode) {
       chatStore.setChatMode(chatMode as "ONLINE" | "LOCAL");
     }

+ 1 - 1
app/components/home.tsx

@@ -266,7 +266,7 @@ function Screen() {
       <>
         {
           location.pathname !== '/' &&
-          <SideBar className={(location.pathname === '/knowledgeChat' || location.pathname === '/deepseekChat') ? styles["sidebar-show"] : ""} />
+          <SideBar className={styles["sidebar-show"]} />
         }
         <WindowContent>
           <Routes>

+ 226 - 5
app/components/sidebar.tsx

@@ -3,7 +3,7 @@ import styles from "./home.module.scss";
 import DragIcon from "../icons/drag.svg";
 import faviconSrc from "../icons/favicon.png";
 import deepSeekSrc from "../icons/deepSeek.png";
-import { EditOutlined } from '@ant-design/icons';
+import { AppstoreOutlined, EditOutlined, MenuOutlined } from '@ant-design/icons';
 import { useAppConfig, useChatStore, useGlobalStore } from "../store";
 import {
   DEFAULT_SIDEBAR_WIDTH,
@@ -14,8 +14,9 @@ import {
 import { useLocation, useNavigate } from "react-router-dom";
 import { isIOS, useMobileScreen } from "../utils";
 import api from "@/app/api/api";
-import { Button, Dropdown, Form, Input, Menu, Modal } from "antd";
+import { Button, Drawer, Dropdown, Empty, Form, Input, Menu, message, Modal, Rate, Tag } from "antd";
 import { downloadFile } from "../utils/index";
+import dayjs from "dayjs";
 
 const FormItem = Form.Item;
 
@@ -193,6 +194,172 @@ export function SideBarTail(props: {
   );
 }
 
+interface AppDrawerProps {
+  isMobileScreen: boolean,
+  selectedAppId: string,
+  type: 'all' | 'collect',
+  open: boolean,
+  onClose: () => void,
+}
+
+const AppDrawer: React.FC<AppDrawerProps> = (props) => {
+  const {
+    isMobileScreen,
+    selectedAppId,
+    type,
+    open,
+    onClose,
+  } = props;
+
+  const navigate = useNavigate();
+
+  const [listLoading, setListLoading] = useState(false);
+
+  type List = {
+    name: string,
+    chatMode: string,
+    appId: string,
+    desc: string,
+    createTime: string,
+    typeName: string,
+    isCollect: boolean,
+  }[];
+
+  const [list, setList] = useState<List>([]);
+
+  const fetchAppList = async () => {
+    setListLoading(true);
+    try {
+      const userId = '7';
+      const res = await api.get(`/deepseek/api/project/app/${userId}`);
+      if (type === 'all') {
+        setList(res.data);
+      } else {
+        setList(res.data.filter((item: any) => item.isCollect));
+      }
+    } catch (error) {
+      console.error(error);
+    } finally {
+      setListLoading(false);
+    }
+  };
+
+  // 收藏应用
+  const collectApp = async (appId: string) => {
+    try {
+      const userId = '7';
+      await api.post('/deepseek/api/app/collect', {
+        appId: appId,
+        userId: userId,
+      });
+      message.success('收藏成功');
+      await fetchAppList();
+    } catch (error: any) {
+      message.error(error.msg);
+    }
+  };
+
+  // 取消收藏应用
+  const cancelCollectApp = async (appId: string) => {
+    try {
+      const userId = '7';
+      await api.delete(`/deepseek/api/app/collect/${userId}/${appId}`);
+      message.success('操作成功');
+      await fetchAppList();
+    } catch (error: any) {
+      message.error(error.msg);
+    }
+  };
+
+  const init = async () => {
+    await fetchAppList();
+  }
+
+  useEffect(() => {
+    init();
+  }, [])
+
+  return (
+    <Drawer
+      width={isMobileScreen ? '100%' : 400}
+      title={type === 'all' ? '我的应用' : '我的收藏'}
+      open={open}
+      loading={listLoading}
+      onClose={onClose}
+    >
+      {
+        list.length > 0 ?
+          list.map((item, index) => {
+            return <div
+              style={{
+                padding: 20,
+                border: '1px solid #f0f0f0',
+                borderRadius: 4,
+                marginBottom: 20,
+                cursor: 'pointer',
+              }}
+              key={index}
+            >
+              <div style={{ display: 'flex', marginBottom: 10 }}>
+                <AppstoreOutlined style={{ fontSize: 40, color: '#3875f6', marginRight: 20 }} />
+                <div>
+                  <div style={{ fontSize: 16, fontWeight: 'bold', marginBottom: 5 }}>
+                    {item.name}
+                  </div>
+                  <div style={{ color: '#d4d7de' }}>
+                    ID:{item.appId}
+                  </div>
+                </div>
+              </div>
+              <div style={{ color: '#8f949e', marginBottom: 10 }}>
+                {item.desc}
+              </div>
+              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 10 }}>
+                <div style={{ color: '#747a86' }}>
+                  {dayjs(item.createTime).format('YYYY-MM-DD')} 发布
+                </div>
+                <div>
+                  <Tag style={{ margin: 0 }} color="blue">
+                    {item.typeName}
+                  </Tag>
+                </div>
+              </div>
+              <div
+                style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
+                <div onClick={async () => {
+                  if (item.isCollect) {
+                    await cancelCollectApp(item.appId);
+                  } else {
+                    await collectApp(item.appId);
+                  }
+                }}>
+                  {
+                    item.isCollect ?
+                      <Rate count={1} value={1} />
+                      :
+                      <Rate count={1} />
+                  }
+                </div>
+                <Button type='primary' size="small" onClick={() => {
+                  const search = `?showMenu=false&chatMode=${item.chatMode}&appId=${item.appId}`;
+                  navigate({
+                    pathname: '/knowledgeChat',
+                    search: search,
+                  })
+                  location.reload();
+                }}>
+                  使用
+                </Button>
+              </div>
+            </div>
+          })
+          :
+          <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
+      }
+    </Drawer>
+  )
+}
+
 export const SideBar = (props: { className?: string }) => {
   // useHotKey();
   const { onDragStart, shouldNarrow } = useDragSideBar();
@@ -355,10 +522,16 @@ export const SideBar = (props: { className?: string }) => {
     fetchChatList(chatStore.chatMode);
   }, [chatStore.chatMode]);
 
+  const isMobileScreen = useMobileScreen();
+
+  const [drawerOpen, setDrawerOpen] = useState(false);
+
+  const [drawerType, setDrawerType] = useState<'all' | 'collect'>('all');
+
   return (
     <>
       {
-        !location.search.includes('showMenu=false') &&
+        globalStore.showMenu &&
         <SideBarContainer
           onDragStart={onDragStart}
           shouldNarrow={shouldNarrow}
@@ -371,7 +544,22 @@ export const SideBar = (props: { className?: string }) => {
             </div>
           }
           <SideBarHeader
-            title={getType() === 'bigModel' ? '问答历史' : ''}
+            title={getType() === 'bigModel' ?
+              <div style={{ display: 'flex', alignItems: 'center' }}>
+                {isMobileScreen && <div>
+                  <Button
+                    type='text'
+                    icon={<MenuOutlined />}
+                    onClick={() => {
+                      globalStore.setShowMenu(!globalStore.showMenu);
+                    }}
+                  />
+                </div>}
+                问答历史
+              </div>
+              :
+              ''
+            }
             logo={getType() === 'bigModel' ? <img style={{ height: 40 }} src={faviconSrc.src} /> : ''}
           >
             <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 10 }}>
@@ -385,7 +573,6 @@ export const SideBar = (props: { className?: string }) => {
               </Button>
               <Button
                 style={{ width: '48%' }}
-                type="primary"
                 onClick={async () => {
                   chatStore.clearSessions();
                   chatStore.updateCurrentSession((value) => {
@@ -410,6 +597,28 @@ export const SideBar = (props: { className?: string }) => {
                 新建对话
               </Button>
             </div>
+            <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 10 }}>
+              <Button
+                style={{ width: '48%' }}
+                type="primary"
+                onClick={() => {
+                  setDrawerType('all');
+                  setDrawerOpen(true);
+                }}
+              >
+                我的应用
+              </Button>
+              <Button
+                style={{ width: '48%' }}
+                type="primary"
+                onClick={() => {
+                  setDrawerType('collect');
+                  setDrawerOpen(true);
+                }}
+              >
+                我的收藏
+              </Button>
+            </div>
           </SideBarHeader>
           <Menu
             style={{ border: 'none' }}
@@ -520,6 +729,18 @@ export const SideBar = (props: { className?: string }) => {
           </Modal>
         </SideBarContainer>
       }
+      {
+        drawerOpen &&
+        <AppDrawer
+          isMobileScreen={isMobileScreen}
+          selectedAppId={globalStore.selectedAppId}
+          type={drawerType}
+          open={drawerOpen}
+          onClose={() => {
+            setDrawerOpen(false);
+          }}
+        />
+      }
     </>
   );
 }

+ 4 - 0
app/store/global.ts

@@ -1,6 +1,7 @@
 import { createPersistStore } from "../utils/store";
 
 const state = {
+  showMenu: false,
   selectedAppId: "",
   currentSession: {
     appId: '',
@@ -13,6 +14,9 @@ const state = {
 export const useGlobalStore = createPersistStore(
   { ...state },
   (set, get) => ({
+    setShowMenu(status: boolean) {
+      set({ showMenu: status });
+    },
     setSelectedAppId(appId: string) {
       set({ selectedAppId: appId });
     },

+ 0 - 1
app/styles/window.scss

@@ -19,7 +19,6 @@
   color: #FFFFFF;
   max-width: calc(100% - 100px);
   width: 100%;
-  overflow: hidden;
 
   .window-header-main-title {
     font-size: 20px;

+ 1 - 1
next.config.mjs

@@ -92,7 +92,7 @@ if (mode !== "export") {
       },
       {
         source: "/bigmodel-api/:path*",
-        destination: "http://xia0miduo.gicp.net:8091/:path*",
+        destination: "http://192.168.3.27:8091/:path*",
       },
       {
         source: "/deepseek-api/:path*",