Selaa lähdekoodia

Enhance ImRecordPage with custom date range selection and improved state management

- Introduced custom date range selection using a RangePicker, allowing users to specify chat history retrieval periods.
- Updated state management to handle both week-based and custom date ranges for chat history.
- Refactored loadHistory functions to streamline data fetching based on selected date ranges.
- Improved user experience by differentiating between sent and received messages visually in the chat log.
- Replaced AutoComplete with Select for user search functionality, enhancing clarity and usability.
0es 1 kuukausi sitten
vanhempi
sitoutus
b30c81ac51
1 muutettua tiedostoa jossa 97 lisäystä ja 16 poistoa
  1. 97 16
      src/app/(dashboard)/user/im-record/page.tsx

+ 97 - 16
src/app/(dashboard)/user/im-record/page.tsx

@@ -8,15 +8,16 @@ import {
 } from "@ant-design/icons";
 import {
   App,
-  AutoComplete,
   Button,
   Card,
   Col,
+  DatePicker,
   Empty,
   Form,
   Image,
-  Input,
+  Popover,
   Row,
+  Select,
   Space,
   Spin,
   Table,
@@ -24,6 +25,7 @@ import {
   Typography,
 } from "antd";
 import type { ColumnsType } from "antd/es/table";
+import type { Dayjs } from "dayjs";
 import type React from "react";
 import { useMemo, useRef, useState } from "react";
 import UserBrief from "@/components/UserBrief";
@@ -32,6 +34,7 @@ import { getUserPage } from "@/services/user";
 import type { ImChatInfoVo, UserChatSessionItemVo } from "@/types/api";
 import dayjs from "@/utils/dayjs";
 
+const { RangePicker } = DatePicker;
 const { Text, Paragraph } = Typography;
 
 function formatUnixSeconds(sec?: number): string {
@@ -193,13 +196,11 @@ function getChatRowKey(c: ImChatInfoVo): string {
   );
 }
 
-function getWeekRangeByPage(
-  page: number,
-): [ReturnType<typeof dayjs>, ReturnType<typeof dayjs>] {
+function getWeekRangeByPage(page: number): [Dayjs, Dayjs] {
   const safe = Math.max(1, Math.floor(page || 1));
   const end = dayjs().subtract((safe - 1) * 7, "day");
   const start = end.subtract(7, "day");
-  return [start, end];
+  return [start, end] as [Dayjs, Dayjs];
 }
 
 const ImRecordPage: React.FC = () => {
@@ -231,6 +232,8 @@ const ImRecordPage: React.FC = () => {
   const [historyLoading, setHistoryLoading] = useState(false);
   // "Page" means week index: 1 => last 7 days, 2 => 7-14 days ago, ...
   const [historyWeekPage, setHistoryWeekPage] = useState(1);
+  const [historyMode, setHistoryMode] = useState<"week" | "custom">("week");
+  const [customRange, setCustomRange] = useState<[Dayjs, Dayjs] | null>(null);
   const [chatInfos, setChatInfos] = useState<ImChatInfoVo[]>([]);
 
   const sessionsReqId = useRef(0);
@@ -271,6 +274,8 @@ const ImRecordPage: React.FC = () => {
 
   const resetHistory = () => {
     setHistoryWeekPage(1);
+    setHistoryMode("week");
+    setCustomRange(null);
     setChatInfos([]);
   };
 
@@ -384,8 +389,8 @@ const ImRecordPage: React.FC = () => {
     await loadSessions(nextPage);
   };
 
-  const loadHistoryWeek = async (
-    weekPage: number,
+  const loadHistoryRange = async (
+    range: [Dayjs, Dayjs],
     fromAccountInput?: string,
     toAccountInput?: string,
     sessionTypeInput?: number,
@@ -403,7 +408,8 @@ const ImRecordPage: React.FC = () => {
     const reqId = ++historyReqId.current;
     setHistoryLoading(true);
     try {
-      const [start, end] = getWeekRangeByPage(weekPage);
+      const start = range[0];
+      const end = range[1];
       const minTime = start.unix();
       const maxTime = end.unix();
 
@@ -411,7 +417,7 @@ const ImRecordPage: React.FC = () => {
       let next: string | undefined;
       let guard = 0;
 
-      // Pull all cursor pages within the week window.
+      // Pull all cursor pages within the range window.
       while (guard < 200) {
         guard += 1;
         const res = await getChatHistory({
@@ -453,10 +459,27 @@ const ImRecordPage: React.FC = () => {
     }
   };
 
+  const loadHistoryWeek = async (
+    weekPage: number,
+    fromAccountInput?: string,
+    toAccountInput?: string,
+    sessionTypeInput?: number,
+  ) => {
+    const [start, end] = getWeekRangeByPage(weekPage);
+    await loadHistoryRange(
+      [start, end],
+      fromAccountInput,
+      toAccountInput,
+      sessionTypeInput,
+    );
+  };
+
   const selectSession = (session: UserChatSessionItemVo | null) => {
     setSelectedSession(session);
 
     setHistoryWeekPage(1);
+    setHistoryMode("week");
+    setCustomRange(null);
     setChatInfos([]);
 
     if (!session) {
@@ -482,6 +505,11 @@ const ImRecordPage: React.FC = () => {
 
     setChatInfos([]);
 
+    if (historyMode === "custom" && customRange) {
+      await loadHistoryRange(customRange);
+      return;
+    }
+
     await loadHistoryWeek(historyWeekPage);
   };
 
@@ -489,6 +517,8 @@ const ImRecordPage: React.FC = () => {
     if (!selectedSession) return;
     if (historyWeekPage <= 1) return;
     const nextPage = historyWeekPage - 1;
+    setHistoryMode("week");
+    setCustomRange(null);
     setHistoryWeekPage(nextPage);
     setChatInfos([]);
     await loadHistoryWeek(nextPage);
@@ -497,11 +527,22 @@ const ImRecordPage: React.FC = () => {
   const handleNextWeek = async () => {
     if (!selectedSession) return;
     const nextPage = historyWeekPage + 1;
+    setHistoryMode("week");
+    setCustomRange(null);
     setHistoryWeekPage(nextPage);
     setChatInfos([]);
     await loadHistoryWeek(nextPage);
   };
 
+  const handleSelectCustomRange = async (range: [Dayjs, Dayjs] | null) => {
+    if (!selectedSession) return;
+    if (!range) return;
+    setHistoryMode("custom");
+    setCustomRange(range);
+    setChatInfos([]);
+    await loadHistoryRange(range);
+  };
+
   const sessionColumns: ColumnsType<UserChatSessionItemVo> = [
     {
       title: "用户",
@@ -540,10 +581,16 @@ const ImRecordPage: React.FC = () => {
     {
       title: "消息",
       dataIndex: "msgBody",
-      render: (v: unknown) => {
+      render: (v: unknown, record) => {
         const rawText = safeJsonStringify(v);
+        const from = String(record?.from_Account || "").trim();
+        const me = String(currentUserNo || "").trim();
+        const isSentByMe = !!me && from === me;
         return (
           <Space size={8} style={{ width: "100%" }}>
+            <Tag color={isSentByMe ? "blue" : "default"}>
+              {isSentByMe ? "发送" : "接收"}
+            </Tag>
             {renderParsedMsgBody(v)}
             <Button
               type="link"
@@ -592,21 +639,31 @@ const ImRecordPage: React.FC = () => {
             label="用户"
             rules={[{ required: true, message: "请先搜索用户" }]}
           >
-            <AutoComplete
+            <Select
               style={{ width: 320 }}
               placeholder="输入用户编号 / 昵称 / 邮箱搜索"
+              showSearch
+              filterOption={false}
               options={userOptions}
               onSearch={handleUserNoSearch}
               allowClear
               notFoundContent={
                 userSearchLoading ? <Spin size="small" /> : <Empty />
               }
+              optionLabelProp="label"
               onSelect={(value) => {
                 void loadSessionsForUser(String(value || ""));
               }}
-            >
-              <Input />
-            </AutoComplete>
+              onClear={() => {
+                setCurrentUserNo("");
+                setSelectedSession(null);
+                resetHistory();
+                setSessionsPage(1);
+                setSessionsTokenByPage({ 1: undefined });
+                setSessionsHasNext(false);
+                setSessions([]);
+              }}
+            />
           </Form.Item>
           <Form.Item>
             <Space>
@@ -684,6 +741,27 @@ const ImRecordPage: React.FC = () => {
             title="聊天记录"
             extra={
               <Space>
+                <Popover
+                  trigger="click"
+                  placement="bottomRight"
+                  content={
+                    <RangePicker
+                      showTime
+                      style={{ width: 360 }}
+                      value={customRange}
+                      onChange={(dates) => {
+                        const d = dates as [Dayjs, Dayjs] | null;
+                        if (d?.[0] && d?.[1]) void handleSelectCustomRange(d);
+                      }}
+                      allowClear
+                      disabled={!selectedSession || historyLoading}
+                    />
+                  }
+                >
+                  <Button disabled={!selectedSession} loading={historyLoading}>
+                    自选范围
+                  </Button>
+                </Popover>
                 <Button
                   onClick={handlePrevWeek}
                   disabled={!selectedSession || historyWeekPage <= 1}
@@ -708,7 +786,10 @@ const ImRecordPage: React.FC = () => {
                   </Form.Item>
                   <Form.Item label="时间范围">
                     {(() => {
-                      const [start, end] = getWeekRangeByPage(historyWeekPage);
+                      const [start, end] =
+                        historyMode === "custom" && customRange
+                          ? customRange
+                          : getWeekRangeByPage(historyWeekPage);
                       return (
                         <Text type="secondary">
                           {start.format("YYYY-MM-DD")} ~{" "}