Pārlūkot izejas kodu

优化侧边栏样式

Ryuiso 1 nedēļu atpakaļ
vecāks
revīzija
1e3b7f3689

+ 3 - 1
.gitignore

@@ -46,4 +46,6 @@ dev
 *.key
 *.key.pub
 
-masks.json
+masks.json
+
+.history

+ 37 - 0
app/components/chat.module.scss

@@ -297,6 +297,43 @@
   padding-bottom: 40px;
 }
 
+.preset-questions-container {
+  flex: 1;
+  overflow-y: auto;
+  padding-right: 4px;
+  
+  /* 自定义滚动条样式 */
+  &::-webkit-scrollbar {
+    width: 4px;
+  }
+  
+  &::-webkit-scrollbar-track {
+    background: transparent;
+  }
+  
+  &::-webkit-scrollbar-thumb {
+    background: rgba(0, 0, 0, 0.2);
+    border-radius: 2px;
+  }
+  
+  &::-webkit-scrollbar-thumb:hover {
+    background: rgba(0, 0, 0, 0.3);
+  }
+  
+  /* 移动端优化 */
+  @media only screen and (max-width: 600px) {
+    max-height: 60vh;
+    
+    /* 隐藏滚动条但保持滚动功能 */
+    scrollbar-width: none;
+    -ms-overflow-style: none;
+    
+    &::-webkit-scrollbar {
+      display: none;
+    }
+  }
+}
+
 .chat-body-main-title {
   cursor: pointer;
 

+ 66 - 41
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";
@@ -953,6 +953,7 @@ function _Chat() {
   type RenderMessage = ChatMessage & { preview?: boolean };
 
   const chatStore = useChatStore();
+  const globalStore = useGlobalStore();
   const session = chatStore.currentSession();
   const config = useAppConfig();
   config.sendPreviewBubble = false;
@@ -1003,7 +1004,6 @@ function _Chat() {
 
   const [appList, setAppList] = useState<AppList>([]);
   const [appValue, setAppValue] = useState<string>();
-  const globalStore = useGlobalStore();
   type QuestionList = string[];
   const [questionList, setQuestionList] = useState<QuestionList>([]);
   const location = useLocation();
@@ -1890,7 +1890,19 @@ function _Chat() {
         // </div>
       }
       <div className="window-header" data-tauri-drag-region>
-        <div></div>
+        <div style={{ display: 'flex', alignItems: 'center' }}>
+          {
+            isMobileScreen && location.search.includes('showMenu=false') &&
+            <Button
+              type="text"
+              icon={<MenuOutlined />}
+              onClick={() => {
+                globalStore.setShowMenu(true);
+              }}
+              style={{ marginLeft: 8 }}
+            />
+          }
+        </div>
         <div className="window-actions">
           <div className="window-action-button">
             <Button type="primary" ghost onClick={() => {
@@ -2149,46 +2161,59 @@ function _Chat() {
             </>
             :
             <>
-              <div style={{ padding: '0 20px' }}>
-                <div style={{ display: 'flex', justifyContent: 'center' }}>
-                  <img style={{ width: 80, height: 80 }} src={avatarSrc.src} />
+              <div style={{ 
+                padding: '0 20px',
+                display: 'flex',
+                flexDirection: 'column',
+                height: '100%'
+              }}>
+                <div style={{ 
+                  flexShrink: 0,
+                  textAlign: 'center',
+                  marginBottom: '20px'
+                }}>
+                  <div style={{ display: 'flex', justifyContent: 'center' }}>
+                    <img style={{ width: 80, height: 80 }} src={avatarSrc.src} />
+                  </div>
+                  <h1 style={{ textAlign: 'center' }}>
+                    {getAppName()}
+                  </h1>
+                  <p style={{ textAlign: 'center' }}>
+                    {getDesc()}
+                  </p>
+                  <p style={{ textAlign: 'left', marginBottom: '16px' }}>我猜您可能想问:</p>
                 </div>
-                <h1 style={{ textAlign: 'center' }}>
-                  {getAppName()}
-                </h1>
-                <p style={{ textAlign: 'center' }}>
-                  {getDesc()}
-                </p>
-                <p>我猜您可能想问:</p>
-                {
-                  questionList.map((item, index) => {
-                    return (
-                      <div
-                        style={{
-                          padding: '10px',
-                          marginBottom: '10px',
-                          border: '1px solid #e6e8f1',
-                          borderRadius: '10px',
-                          fontSize: '16px',
-                          display: 'flex',
-                          justifyContent: 'space-between',
-                          alignItems: 'center',
-                          cursor: 'pointer'
-                        }}
-                        onClick={() => {
-                          setUserInput(item)
-                          doSubmit(item)
-                        }}
-                        key={index}
-                      >
-                        <div>
-                          {item}
+                <div className={styles['preset-questions-container']}>
+                  {
+                    questionList.map((item, index) => {
+                      return (
+                        <div
+                          style={{
+                            padding: '10px',
+                            marginBottom: '10px',
+                            border: '1px solid #e6e8f1',
+                            borderRadius: '10px',
+                            fontSize: '16px',
+                            display: 'flex',
+                            justifyContent: 'space-between',
+                            alignItems: 'center',
+                            cursor: 'pointer'
+                          }}
+                          onClick={() => {
+                            setUserInput(item)
+                            doSubmit(item)
+                          }}
+                          key={index}
+                        >
+                          <div>
+                            {item}
+                          </div>
+                          <RightOutlined />
                         </div>
-                        <RightOutlined />
-                      </div>
-                    )
-                  })
-                }
+                      )
+                    })
+                  }
+                </div>
               </div>
             </>
         }

+ 10 - 0
app/components/home.module.scss

@@ -128,6 +128,16 @@
     left: 0;
   }
 
+  .sidebar-overlay {
+    position: fixed;
+    top: 0;
+    left: 0;
+    width: 100vw;
+    height: 100vh;
+    background-color: rgba(0, 0, 0, 0.5);
+    z-index: 999;
+  }
+
   .mobile {
     display: block;
   }

+ 31 - 280
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 { MenuOutlined, PlusOutlined } from '@ant-design/icons';
 import { useAppConfig, useChatStore, useGlobalStore } from "../store";
 import {
   DEFAULT_SIDEBAR_WIDTH,
@@ -13,11 +13,7 @@ import {
 } from "../constant";
 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 { downloadFile } from "../utils/index";
-
-const FormItem = Form.Item;
+import { Button } from "antd";
 
 export function useHotKey() {
   const chatStore = useChatStore();
@@ -102,7 +98,7 @@ export function useDragSideBar() {
     const barWidth = shouldNarrow
       ? NARROW_SIDEBAR_WIDTH
       : limit(config.sidebarWidth ?? DEFAULT_SIDEBAR_WIDTH);
-    const sideBarWidth = isMobileScreen ? "100vw" : `${barWidth}px`;
+    const sideBarWidth = isMobileScreen ? "60vw" : `${barWidth}px`;
     document.documentElement.style.setProperty("--sidebar-width", sideBarWidth);
   }, [config.sidebarWidth, isMobileScreen, shouldNarrow]);
 
@@ -194,17 +190,13 @@ export function SideBarTail(props: {
 }
 
 export const SideBar = (props: { className?: string }) => {
-  // useHotKey();
   const { onDragStart, shouldNarrow } = useDragSideBar();
-  const [showPluginSelector, setShowPluginSelector] = useState(false);
   const navigate = useNavigate();
   const location = useLocation();
   const chatStore = useChatStore();
   const globalStore = useGlobalStore();
+  const isMobileScreen = useMobileScreen();
 
-  const [menuList, setMenuList] = useState([])
-  const [modalOpen, setModalOpen] = useState(false)
-  const [form] = Form.useForm();
   const getType = (): 'bigModel' | 'deepSeek' => {
     if (['/knowledgeChat', '/newChat'].includes(location.pathname)) {
       return 'bigModel';
@@ -215,150 +207,22 @@ export const SideBar = (props: { className?: string }) => {
     }
   }
 
-  // 获取聊天列表
-  const fetchChatList = async (chatMode?: 'ONLINE' | 'LOCAL') => {
-    try {
-      let url = '';
-      if (getType() === 'bigModel') {
-        const appId = globalStore.selectedAppId;
-        if (appId) {
-          if (chatMode === 'LOCAL') {
-            url = `/deepseek/api/dialog/list/${appId}`;
-          } else {
-            url = `/bigmodel/api/dialog/list/${appId}`;
-          }
-        }
-      } else {
-        const appId = '1881269958412521255';
-        url = `/bigmodel/api/dialog/list/${appId}`;
-      }
-
-      const res = await api.get(url);
-      const list = res.data.map((item: any) => {
-        return {
-          ...item,
-          children: item.children.map((child: any) => {
-            const items = [
-              {
-                key: '1',
-                label: (
-                  <a onClick={() => {
-                    setModalOpen(true);
-                    form.setFieldsValue({
-                      dialogId: child.key,
-                      dialogName: child.label
-                    });
-                  }}>
-                    重命名
-                  </a>
-                ),
-              },
-              {
-                key: '2',
-                label: (
-                  <a onClick={async () => {
-                    try {
-                      let blob = null;
-                      if (getType() === 'bigModel') {
-                        if (chatMode === 'LOCAL') {
-                          blob = await api.post(`/deepseek/api/dialog/export/${child.key}`, {}, { responseType: 'blob' });
-                        } else {
-                          blob = await api.post(`/bigmodel/api/dialog/export/${child.key}`, {}, { responseType: 'blob' });
-                        }
-                      } else {
-                        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: (
-                  <a onClick={async () => {
-                    try {
-                      if (getType() === 'bigModel') {
-                        if (chatMode === 'LOCAL') {
-                          await api.delete(`/deepseek/api/dialog/del/${child.key}`);
-                          await fetchChatList(chatMode);
-                        } else {
-                          await api.delete(`/bigmodel/api/dialog/del/${child.key}`);
-                          await fetchChatList();
-                        }
-                      } else {
-                        await api.delete(`/bigmodel/api/dialog/del/${child.key}`);
-                        await fetchChatList();
-                      }
-                      chatStore.clearSessions();
-                      useChatStore.setState({
-                        message: {
-                          content: '',
-                          role: 'assistant',
-                        }
-                      });
-                    } catch (error) {
-                      console.error(error);
-                    }
-                  }}>
-                    删除
-                  </a>
-                ),
-              },
-            ];
-            return {
-              ...child,
-              label: <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
-                <div style={{ flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', marginRight: 10 }}>
-                  {child.label}
-                </div>
-                <div style={{ width: 20 }}>
-                  <Dropdown menu={{ items }} trigger={['click']} placement="bottomRight">
-                    <EditOutlined onClick={(e) => e.stopPropagation()} />
-                  </Dropdown>
-                </div>
-              </div>
-            }
-          })
-        }
-      })
-      setMenuList(list);
-    } catch (error) {
-      console.error(error)
-    }
-  }
-
-  useEffect(() => {
-    if (getType() === 'bigModel') {
-      if (globalStore.selectedAppId) {
-        fetchChatList(chatStore.chatMode);
-      }
-    }
-  }, [globalStore.selectedAppId]);
-
-  useEffect(() => {
-    chatStore.clearSessions();
-    useChatStore.setState({
-      message: {
-        content: '',
-        role: 'assistant',
-      }
-    });
-  }, []);
-
-  useEffect(() => {
-    fetchChatList(chatStore.chatMode);
-  }, [chatStore.chatMode]);
+  const closeSidebar = () => {
+    globalStore.setShowMenu(false);
+  };
 
   return (
     <>
       {
-        !location.search.includes('showMenu=false') &&
+        isMobileScreen && globalStore.showMenu && (
+          <div
+            className={styles["sidebar-overlay"]}
+            onClick={closeSidebar}
+          />
+        )
+      }
+      {
+        (location.search.includes('showMenu=false') ? globalStore.showMenu : true) &&
         <SideBarContainer
           onDragStart={onDragStart}
           shouldNarrow={shouldNarrow}
@@ -371,153 +235,40 @@ export const SideBar = (props: { className?: string }) => {
             </div>
           }
           <SideBarHeader
-            title={getType() === 'bigModel' ? '问答历史' : ''}
-            logo={getType() === 'bigModel' ? <img style={{ height: 40 }} src={faviconSrc.src} /> : ''}
+            title={getType() === 'bigModel' ?
+              <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
+                <img style={{ height: 32 }} src={faviconSrc.src} />
+                <span>建科•小智</span>
+              </div>
+              :
+              ''
+            }
+            logo={getType() === 'bigModel' ? null : ''}
           >
-            <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 10 }}>
+            <div style={{ marginTop: 20 }}>
               <Button
-                style={{ width: '48%' }}
-                onClick={() => {
-                  navigate({ pathname: '/' });
-                }}
-              >
-                回到首页
-              </Button>
-              <Button
-                style={{ width: '48%' }}
+                style={{ width: '100%' }}
                 type="primary"
+                icon={<PlusOutlined />}
                 onClick={async () => {
                   chatStore.clearSessions();
                   chatStore.updateCurrentSession((value) => {
                     value.appId = globalStore.selectedAppId;
                   });
+                  if (isMobileScreen) {
+                    closeSidebar();
+                  }
                   if (getType() === 'bigModel') {
                     navigate({ pathname: '/newChat' });
                   } else {
                     navigate({ pathname: '/newDeepseekChat' });
                   }
-                  if (getType() === 'bigModel') {
-                    if (chatStore.chatMode === 'LOCAL') {
-                      await fetchChatList(chatStore.chatMode);
-                    } else {
-                      await fetchChatList();
-                    }
-                  } else {
-                    await fetchChatList();
-                  }
                 }}
               >
                 新建对话
               </Button>
             </div>
           </SideBarHeader>
-          <Menu
-            style={{ border: 'none' }}
-            onClick={async ({ key }) => {
-              let url = ``;
-              if (getType() === 'bigModel') {
-                if (chatStore.chatMode === 'LOCAL') {
-                  url = `/deepseek/api/dialog/detail/${key}`;
-                } else {
-                  url = `/bigmodel/api/dialog/detail/${key}`;
-                }
-              } else {
-                url = `/bigmodel/api/dialog/detail/${key}`;
-              }
-              const res = await api.get(url);
-              const list = res.data.map(((item: any) => {
-                return {
-                  id: item.did,
-                  role: item.type,
-                  date: item.create_time,
-                  content: item.content,
-                  document: item.document ? item.document : undefined,
-                  sliceInfo: item.sliceInfo ? item.sliceInfo : undefined,
-                  networkInfo: item.networkInfo ? item.networkInfo : undefined,
-                }
-              }))
-              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;
-              });
-              if (getType() === 'bigModel') {
-                navigate({ pathname: '/newChat' });
-              } else {
-                navigate({ pathname: '/newDeepseekChat' });
-              }
-            }}
-            mode="inline"
-            items={menuList}
-          />
-          <Modal
-            title="重命名"
-            open={modalOpen}
-            width={300}
-            maskClosable={false}
-            onOk={() => {
-              form.validateFields().then(async (values) => {
-                setModalOpen(false);
-                try {
-                  if (getType() === 'bigModel') {
-                    if (chatStore.chatMode === 'LOCAL') {
-                      await api.put(`/deepseek/api/dialog/update`, {
-                        id: values.dialogId,
-                        dialogName: values.dialogName
-                      });
-                      await fetchChatList(chatStore.chatMode);
-                    } else {
-                      await api.put(`/bigmodel/api/dialog/update`, {
-                        id: values.dialogId,
-                        dialogName: values.dialogName
-                      });
-                      await fetchChatList();
-                    }
-                  } else {
-                    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>
       }
     </>

+ 4 - 0
app/store/global.ts

@@ -2,6 +2,7 @@ import { createPersistStore } from "../utils/store";
 
 const state = {
   selectedAppId: "",
+  showMenu: false,
   currentSession: {
     appId: '',
     dialogName: '',
@@ -16,6 +17,9 @@ export const useGlobalStore = createPersistStore(
     setSelectedAppId(appId: string) {
       set({ selectedAppId: appId });
     },
+    setShowMenu(show: boolean) {
+      set({ showMenu: show });
+    },
     setCurrentSession(session: any) {
       set({ currentSession: session });
     },