Преглед на файлове

feat: 添加会话列表分页功能

liaojiaxing преди 1 месец
родител
ревизия
b2e6db5d5d
променени са 5 файла, в които са добавени 124 реда и са изтрити 34 реда
  1. 1 0
      package.json
  2. 17 0
      pnpm-lock.yaml
  3. 12 0
      src/global.less
  4. 44 10
      src/hooks/useChat.ts
  5. 50 24
      src/pages/ai/Assistant.tsx

+ 1 - 0
package.json

@@ -21,6 +21,7 @@
     "antd": "^5.24.2",
     "dayjs": "^1.11.13",
     "emoji-mart": "^5.6.0",
+    "react-infinite-scroll-component": "^6.1.0",
     "react-markdown": "^10.1.0",
     "react-syntax-highlighter": "^15.6.1",
     "rehype-raw": "^7.0.0",

+ 17 - 0
pnpm-lock.yaml

@@ -41,6 +41,9 @@ dependencies:
   emoji-mart:
     specifier: ^5.6.0
     version: 5.6.0
+  react-infinite-scroll-component:
+    specifier: ^6.1.0
+    version: 6.1.0(react@18.3.1)
   react-markdown:
     specifier: ^10.1.0
     version: 10.1.0(@types/react@18.3.18)(react@18.3.1)
@@ -10851,6 +10854,15 @@ packages:
       shallowequal: 1.1.0
     dev: false
 
+  /react-infinite-scroll-component@6.1.0(react@18.3.1):
+    resolution: {integrity: sha512-SQu5nCqy8DxQWpnUVLx7V7b7LcA37aM7tvoWjTLZp1dk6EJibM5/4EJKzOnl07/BsM1Y40sKLuqjCwwH/xV0TQ==}
+    peerDependencies:
+      react: '>=16.0.0'
+    dependencies:
+      react: 18.3.1
+      throttle-debounce: 2.3.0
+    dev: false
+
   /react-intl@3.12.1(react@18.3.1):
     resolution: {integrity: sha512-cgumW29mwROIqyp8NXStYsoIm27+8FqnxykiLSawWjOxGIBeLuN/+p2srei5SRIumcJefOkOIHP+NDck05RgHg==}
     peerDependencies:
@@ -12224,6 +12236,11 @@ packages:
     dependencies:
       real-require: 0.1.0
 
+  /throttle-debounce@2.3.0:
+    resolution: {integrity: sha512-H7oLPV0P7+jgvrk+6mwwwBDmxTaxnu9HMXmloNLXwnNO0ZxZ31Orah2n8lU1eMPvsaowP2CX+USCgyovXfdOFQ==}
+    engines: {node: '>=8'}
+    dev: false
+
   /throttle-debounce@5.0.2:
     resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==}
     engines: {node: '>=12.22'}

+ 12 - 0
src/global.less

@@ -1,3 +1,15 @@
 body {
   background: #f2f4f7;
+}
+
+::-webkit-scrollbar {
+  width: 4px;
+}
+::-webkit-scrollbar-thumb {
+  background-color: rgba(0, 0, 0, 0.15);
+  border-radius: 2px;
+}
+
+::-webkit-scrollbar-track {
+  background-color: transparent;
 }

+ 44 - 10
src/hooks/useChat.ts

@@ -91,27 +91,59 @@ export function useChat({ app_name, onSuccess, onUpdate, onError }: ChatProps) {
   // 当前智能体对象
   const [currentAgent, setCurrentAgent] = useSessionStorageState("agent-map");
 
-  useEffect(() => {
+  // 有更多对话
+  const [hasMoreConversation, setHasMoreConversation] = useState(false);
+
+  // 会话分页
+  const [pageIndex, setPageIndex] = useState(1);
+
+  const getSession = (page: number) => {
     setLoadingSession(true);
     GetSessionList({
       app_name,
-      page_index: 1,
+      page_index: page,
     })
       .then((res) => {
-        setConversationList([
-          { ...defaultConversation },
-          ...(res?.result?.model || []).map((item: any) => ({
-            ...item,
-            key: item.sessionId,
-            label: item.name,
-          })),
-        ]);
+        if(page === 1)  {
+          setConversationList([
+            { ...defaultConversation },
+            ...(res?.result?.model || []).map((item: any) => ({
+              ...item,
+              key: item.sessionId,
+              label: item.name,
+            })),
+          ]);
+        } else {
+          setConversationList([
+            ...(conversationList || []),
+            ...(res?.result?.model || []).map((item: any) => ({
+              ...item,
+              key: item.sessionId,
+              label: item.name,
+            })),
+          ]);
+        }
+        setHasMoreConversation(res?.result.totalPages > page);
       })
       .finally(() => {
         setLoadingSession(false);
       });
+  }
+
+  // 切换app时获取会话记录
+  useEffect(() => {
+    setPageIndex(1);
+    getSession(1);
   }, [app_name]);
 
+  /**
+   * 加载更多会话
+   */
+  const loadMoreConversation = () => {
+    getSession(pageIndex + 1);
+    setPageIndex(pageIndex + 1);
+  };
+
   /**
    * 切换会话
    * @param key 会话id
@@ -309,5 +341,7 @@ export function useChat({ app_name, onSuccess, onUpdate, onError }: ChatProps) {
     onRequest,
     addConversation,
     changeConversation,
+    loadMoreConversation,
+    hasMoreConversation
   };
 }

+ 50 - 24
src/pages/ai/Assistant.tsx

@@ -43,6 +43,7 @@ import type { ConversationsProps } from "@ant-design/x";
 import type { AgentItem } from "./data";
 import MarkdownViewer from "@/components/ai/MarkdownViewer";
 import { ChangeSessionName, DeleteSession } from "@/api/ai";
+import InfiniteScroll from "react-infinite-scroll-component";
 
 type AssistantProps = {
   agent?: AgentItem;
@@ -65,7 +66,7 @@ const roles: GetProp<typeof Bubble.List, "roles"> = {
     ),
     messageRender: (content) => {
       return typeof content === "string" ? (
-        <Typography className={content?.includes('```') ? 'w-full' : ''}>
+        <Typography className={content?.includes("```") ? "w-full" : ""}>
           <MarkdownViewer content={content} />
         </Typography>
       ) : (
@@ -78,7 +79,7 @@ const roles: GetProp<typeof Bubble.List, "roles"> = {
     placement: "end",
     avatar: { icon: <UserOutlined />, style: { background: "#87d068" } },
     messageRender: (content) => {
-      return <div style={{ whiteSpace: 'pre-wrap' }}>{content}</div>
+      return <div style={{ whiteSpace: "pre-wrap" }}>{content}</div>;
     },
   },
 };
@@ -97,6 +98,8 @@ export default (props: AssistantProps) => {
     loadingSession,
     addConversation,
     setConversationList,
+    loadMoreConversation,
+    hasMoreConversation,
   } = useChat({
     app_name: props.agent?.key || "",
     onSuccess: (msg) => {
@@ -361,7 +364,9 @@ export default (props: AssistantProps) => {
       arr[messages.length - 1].loading = false;
       arr[messages.length - 1].footer = (
         <div>
-          <div className="text-12px text-text-secondary pl-12px">(已停止思考)</div>
+          <div className="text-12px text-text-secondary pl-12px">
+            (已停止思考)
+          </div>
           <BubbleFooter
             content={arr[messages.length - 1].content as string}
             query={arr[messages.length - 2].content as string}
@@ -393,29 +398,50 @@ export default (props: AssistantProps) => {
       >
         <XProvider direction="ltr">
           <Flex style={{ height: "100%" }} gap={12}>
-            <Spin spinning={loadingSession}>
-              <div className="w-200px">
-                <div className="w-full px-12px">
-                  <Button
-                    type="primary"
-                    className="w-full"
-                    icon={<PlusOutlined />}
-                    onClick={addConversation}
-                  >
-                    新对话
-                  </Button>
-                </div>
-                <Conversations
-                  style={{ width: 200 }}
-                  activeKey={activeConversation}
-                  onActiveChange={changeConversation}
-                  menu={menuConfig}
-                  items={conversationList}
-                />
+            <div className="w-200px flex flex-col">
+              <div className="w-full px-12px mb-12px">
+                <Button
+                  type="primary"
+                  className="w-full"
+                  icon={<PlusOutlined />}
+                  onClick={addConversation}
+                >
+                  新对话
+                </Button>
               </div>
-            </Spin>
+              <div id="scrollableDiv" className="flex-1 overflow-auto">
+                <InfiniteScroll
+                  dataLength={conversationList?.length || 0}
+                  next={loadMoreConversation}
+                  hasMore={hasMoreConversation}
+                  loader={
+                    <div style={{ textAlign: "center" }}>
+                      <Spin indicator={<RedoOutlined spin />} size="small" />
+                    </div>
+                  }
+                  endMessage={<Divider plain><span className="text-12px text-text-secondary">无更多会话~</span></Divider>}
+                  scrollableTarget="scrollableDiv"
+                  style={{ overflow: "hidden" }}
+                >
+                  <Spin spinning={loadingSession} className="flex-col">
+                    <Conversations
+                      style={{ width: 200 }}
+                      activeKey={activeConversation}
+                      onActiveChange={changeConversation}
+                      menu={menuConfig}
+                      items={conversationList}
+                    />
+                  </Spin>
+                </InfiniteScroll>
+              </div>
+            </div>
+
             <Divider type="vertical" style={{ height: "100%" }} />
-            <Flex vertical style={{ flex: 1, overflow: 'hidden', height: '100%' }} gap={8}>
+            <Flex
+              vertical
+              style={{ flex: 1, overflow: "hidden", height: "100%" }}
+              gap={8}
+            >
               <div
                 className="flex-1 overflow-hidden"
                 ref={contentRef}