From 42ba2590f7a92e2b3506007dcc7db8ebb004ce3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AC=94=E8=AE=B0=E6=9C=AC=E9=87=8C=E7=9A=84=E6=B0=B8?= =?UTF-8?q?=E5=B9=B3?= Date: Thu, 17 Jul 2025 09:53:34 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E6=9C=AC=E6=AC=A1=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=E6=9B=B4=E6=96=B0=E5=86=85=E5=AE=B9=E5=A6=82=E4=B8=8B?= =?UTF-8?q?=20=E9=80=89=E6=8B=A9=E6=A0=8F=E5=81=9A=E4=BA=86=E4=BF=AE?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nkebao/src/components/FriendSelection.tsx | 96 +++++++++++++++-------- nkebao/src/components/GroupSelection.tsx | 79 +++++++++++++------ nkebao/src/pages/scenarios/new/page.tsx | 6 +- 3 files changed, 116 insertions(+), 65 deletions(-) diff --git a/nkebao/src/components/FriendSelection.tsx b/nkebao/src/components/FriendSelection.tsx index a23584f7..979b2f8b 100644 --- a/nkebao/src/components/FriendSelection.tsx +++ b/nkebao/src/components/FriendSelection.tsx @@ -3,12 +3,7 @@ import { Search, X } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { ScrollArea } from "@/components/ui/scroll-area"; -import { - Dialog, - DialogContent, - DialogHeader, - DialogTitle, -} from "@/components/ui/dialog"; +import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog"; import { get } from "@/api/request"; // 微信好友接口类型 @@ -38,11 +33,12 @@ interface FriendsResponse { }; } -// 获取好友列表API函数 +// 获取好友列表API函数 - 添加 keyword 参数 const fetchFriendsList = async (params: { page: number; limit: number; deviceIds?: string[]; + keyword?: string; }): Promise => { if (params.deviceIds && params.deviceIds.length === 0) { return { @@ -58,8 +54,12 @@ const fetchFriendsList = async (params: { } const deviceIdsParam = params?.deviceIds?.join(",") || ""; + const keywordParam = params?.keyword + ? `&keyword=${encodeURIComponent(params.keyword)}` + : ""; + return get( - `/v1/friend?page=${params.page}&limit=${params.limit}&deviceIds=${deviceIdsParam}` + `/v1/friend?page=${params.page}&limit=${params.limit}&deviceIds=${deviceIdsParam}${keywordParam}` ); }; @@ -94,20 +94,33 @@ export default function FriendSelection({ // 打开弹窗并请求第一页好友 const openDialog = () => { setCurrentPage(1); + setSearchQuery(""); // 重置搜索关键词 setDialogOpen(true); - fetchFriends(1); + fetchFriends(1, ""); }; // 当页码变化时,拉取对应页数据(弹窗已打开时) useEffect(() => { if (dialogOpen && currentPage !== 1) { - fetchFriends(currentPage); + fetchFriends(currentPage, searchQuery); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [currentPage]); - // 获取好友列表API - const fetchFriends = async (page: number) => { + // 搜索防抖 + useEffect(() => { + if (!dialogOpen) return; + + const timer = setTimeout(() => { + setCurrentPage(1); // 重置到第一页 + fetchFriends(1, searchQuery); + }, 500); // 500 防抖 + + return () => clearTimeout(timer); + }, [searchQuery, dialogOpen]); + + // 获取好友列表API - 添加 keyword 参数 + const fetchFriends = async (page: number, keyword: string = "") => { setLoading(true); try { let res; @@ -119,9 +132,18 @@ export default function FriendSelection({ setLoading(false); return; } - res = await fetchFriendsList({ page, limit: 20, deviceIds: deviceIds }); + res = await fetchFriendsList({ + page, + limit: 20, + deviceIds: deviceIds, + keyword: keyword.trim() || undefined, + }); } else { - res = await fetchFriendsList({ page, limit: 20 }); + res = await fetchFriendsList({ + page, + limit: 20, + keyword: keyword.trim() || undefined, + }); } if (res && res.code === 200 && res.data) { @@ -144,13 +166,6 @@ export default function FriendSelection({ } }; - // 过滤好友 - const filteredFriends = friends.filter( - (friend) => - friend.nickname.toLowerCase().includes(searchQuery.toLowerCase()) || - friend.wechatId.toLowerCase().includes(searchQuery.toLowerCase()) - ); - // 处理好友选择 const handleFriendToggle = (friendId: string) => { let newIds: string[]; @@ -176,6 +191,13 @@ export default function FriendSelection({ setDialogOpen(false); }; + // 清空搜索 + const handleClearSearch = () => { + setSearchQuery(""); + setCurrentPage(1); + fetchFriends(1, ""); + }; + return ( <> {/* 输入框 */} @@ -221,25 +243,27 @@ export default function FriendSelection({ className="pl-10 py-2 rounded-full border-gray-200" /> - + {searchQuery && ( + + )} - +
{loading ? (
加载中...
- ) : filteredFriends.length > 0 ? ( + ) : friends.length > 0 ? (
- {filteredFriends.map((friend) => ( + {friends.map((friend) => (
)} - +
diff --git a/nkebao/src/components/GroupSelection.tsx b/nkebao/src/components/GroupSelection.tsx index 534cf146..0e1841b3 100644 --- a/nkebao/src/components/GroupSelection.tsx +++ b/nkebao/src/components/GroupSelection.tsx @@ -36,12 +36,17 @@ interface GroupsResponse { }; } +// 修改:支持keyword参数 const fetchGroupsList = async (params: { page: number; limit: number; + keyword?: string; }): Promise => { + const keywordParam = params.keyword + ? `&keyword=${encodeURIComponent(params.keyword)}` + : ""; return get( - `/v1/chatroom?page=${params.page}&limit=${params.limit}` + `/v1/chatroom?page=${params.page}&limit=${params.limit}${keywordParam}` ); }; @@ -71,23 +76,38 @@ export default function GroupSelection({ // 打开弹窗并请求第一页群组 const openDialog = () => { setCurrentPage(1); + setSearchQuery(""); // 重置搜索关键词 setDialogOpen(true); - fetchGroups(1); + fetchGroups(1, ""); }; // 当页码变化时,拉取对应页数据(弹窗已打开时) useEffect(() => { if (dialogOpen && currentPage !== 1) { - fetchGroups(currentPage); + fetchGroups(currentPage, searchQuery); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [currentPage]); - // 获取群组列表API - const fetchGroups = async (page: number) => { + // 搜索防抖 + useEffect(() => { + if (!dialogOpen) return; + const timer = setTimeout(() => { + setCurrentPage(1); + fetchGroups(1, searchQuery); + }, 500); + return () => clearTimeout(timer); + }, [searchQuery, dialogOpen]); + + // 获取群组列表API - 支持keyword + const fetchGroups = async (page: number, keyword: string = "") => { setLoading(true); try { - const res = await fetchGroupsList({ page, limit: 20 }); + const res = await fetchGroupsList({ + page, + limit: 20, + keyword: keyword.trim() || undefined, + }); if (res && res.code === 200 && res.data) { setGroups( res.data.list.map((group) => ({ @@ -110,13 +130,6 @@ export default function GroupSelection({ } }; - // 过滤群组 - const filteredGroups = groups.filter( - (group) => - group.name.toLowerCase().includes(searchQuery.toLowerCase()) || - group.chatroomId.toLowerCase().includes(searchQuery.toLowerCase()) - ); - // 处理群组选择 const handleGroupToggle = (groupId: string) => { let newIds: string[]; @@ -142,6 +155,13 @@ export default function GroupSelection({ setDialogOpen(false); }; + // 清空搜索 + const handleClearSearch = () => { + setSearchQuery(""); + setCurrentPage(1); + fetchGroups(1, ""); + }; + return ( <> {/* 输入框 */} @@ -186,25 +206,27 @@ export default function GroupSelection({ className="pl-10 py-2 rounded-full border-gray-200" /> - + {searchQuery && ( + + )}
- +
{loading ? (
加载中...
- ) : filteredGroups.length > 0 ? ( + ) : groups.length > 0 ? (
- {filteredGroups.map((group) => ( + {groups.map((group) => (
总计 {totalGroups} 个群聊 + {searchQuery && ` (搜索: "${searchQuery}")`}
From 43e873f83a029eca1e66ac4730d2d3fbe4b04468 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AC=94=E8=AE=B0=E6=9C=AC=E9=87=8C=E7=9A=84=E6=B0=B8?= =?UTF-8?q?=E5=B9=B3?= Date: Thu, 17 Jul 2025 09:59:54 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E9=80=89=E6=8B=A9=E7=9A=84=E5=A4=84?= =?UTF-8?q?=E7=90=86=E4=B8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nkebao/src/components/DeviceSelection.tsx | 41 +- .../src/components/DeviceSelectionDialog.tsx | 94 +++-- nkebao/src/pages/content/NewContent.tsx | 372 +++++++++++++----- 3 files changed, 341 insertions(+), 166 deletions(-) diff --git a/nkebao/src/components/DeviceSelection.tsx b/nkebao/src/components/DeviceSelection.tsx index 79fcdca8..4c670af0 100644 --- a/nkebao/src/components/DeviceSelection.tsx +++ b/nkebao/src/components/DeviceSelection.tsx @@ -41,18 +41,11 @@ export default function DeviceSelection({ const [statusFilter, setStatusFilter] = useState("all"); const [loading, setLoading] = useState(false); - // 当弹窗打开时获取设备列表 - useEffect(() => { - if (dialogOpen) { - fetchDevices(); - } - }, [dialogOpen]); - - // 获取设备列表 - const fetchDevices = async () => { + // 获取设备列表,支持keyword + const fetchDevices = async (keyword: string = "") => { setLoading(true); try { - const res = await fetchDeviceList(1, 100); + const res = await fetchDeviceList(1, 100, keyword.trim() || undefined); if (res && res.data && Array.isArray(res.data.list)) { setDevices( res.data.list.map((d) => ({ @@ -71,19 +64,29 @@ export default function DeviceSelection({ } }; - // 过滤设备 - const filteredDevices = devices.filter((device) => { - const matchesSearch = - device.name.toLowerCase().includes(searchQuery.toLowerCase()) || - device.imei.toLowerCase().includes(searchQuery.toLowerCase()) || - device.wechatId.toLowerCase().includes(searchQuery.toLowerCase()); + // 打开弹窗时获取设备列表 + const openDialog = () => { + setSearchQuery(""); + setDialogOpen(true); + fetchDevices(""); + }; + // 搜索防抖 + useEffect(() => { + if (!dialogOpen) return; + const timer = setTimeout(() => { + fetchDevices(searchQuery); + }, 500); + return () => clearTimeout(timer); + }, [searchQuery, dialogOpen]); + + // 过滤设备(只保留状态过滤) + const filteredDevices = devices.filter((device) => { const matchesStatus = statusFilter === "all" || (statusFilter === "online" && device.status === "online") || (statusFilter === "offline" && device.status === "offline"); - - return matchesSearch && matchesStatus; + return matchesStatus; }); // 处理设备选择 @@ -110,7 +113,7 @@ export default function DeviceSelection({ placeholder={placeholder} className="pl-10 h-14 rounded-xl border-gray-200 text-base" readOnly - onClick={() => setDialogOpen(true)} + onClick={openDialog} value={getDisplayText()} />
diff --git a/nkebao/src/components/DeviceSelectionDialog.tsx b/nkebao/src/components/DeviceSelectionDialog.tsx index 2ecc2950..67f4d1d0 100644 --- a/nkebao/src/components/DeviceSelectionDialog.tsx +++ b/nkebao/src/components/DeviceSelectionDialog.tsx @@ -42,62 +42,74 @@ export function DeviceSelectionDialog({ const [loading, setLoading] = useState(false); const [devices, setDevices] = useState([]); - // 获取设备列表 - const fetchDevices = useCallback(async () => { - setLoading(true); - try { - const response = await fetchDeviceList(1, 100, searchQuery); - if (response.code === 200 && response.data) { - // 转换服务端数据格式为组件需要的格式 - const convertedDevices: Device[] = response.data.list.map( - (serverDevice: ServerDevice) => ({ - id: serverDevice.id.toString(), - name: serverDevice.memo || `设备 ${serverDevice.id}`, - imei: serverDevice.imei, - wxid: serverDevice.wechatId || "", - status: serverDevice.alive === 1 ? "online" : "offline", - usedInPlans: 0, // 这个字段需要从其他API获取 - nickname: serverDevice.nickname || "", - }) + // 获取设备列表,支持keyword + const fetchDevices = useCallback( + async (keyword: string = "") => { + setLoading(true); + try { + const response = await fetchDeviceList( + 1, + 100, + keyword.trim() || undefined ); - setDevices(convertedDevices); - } else { + if (response.code === 200 && response.data) { + // 转换服务端数据格式为组件需要的格式 + const convertedDevices: Device[] = response.data.list.map( + (serverDevice: ServerDevice) => ({ + id: serverDevice.id.toString(), + name: serverDevice.memo || `设备 ${serverDevice.id}`, + imei: serverDevice.imei, + wxid: serverDevice.wechatId || "", + status: serverDevice.alive === 1 ? "online" : "offline", + usedInPlans: 0, // 这个字段需要从其他API获取 + nickname: serverDevice.nickname || "", + }) + ); + setDevices(convertedDevices); + } else { + toast({ + title: "获取设备列表失败", + description: response.msg, + variant: "destructive", + }); + } + } catch (error) { + console.error("获取设备列表失败:", error); toast({ title: "获取设备列表失败", - description: response.msg, + description: "请检查网络连接", variant: "destructive", }); + } finally { + setLoading(false); } - } catch (error) { - console.error("获取设备列表失败:", error); - toast({ - title: "获取设备列表失败", - description: "请检查网络连接", - variant: "destructive", - }); - } finally { - setLoading(false); - } - }, [searchQuery, toast]); + }, + [toast] + ); + // 打开弹窗时获取设备列表 useEffect(() => { if (open) { - fetchDevices(); + fetchDevices(""); } - }, [open, searchQuery, fetchDevices]); + }, [open, fetchDevices]); + // 搜索防抖 + useEffect(() => { + if (!open) return; + const timer = setTimeout(() => { + fetchDevices(searchQuery); + }, 500); + return () => clearTimeout(timer); + }, [searchQuery, open, fetchDevices]); + + // 过滤设备(只保留状态过滤) const filteredDevices = devices.filter((device) => { - const matchesSearch = - device.name.toLowerCase().includes(searchQuery.toLowerCase()) || - device.imei.toLowerCase().includes(searchQuery.toLowerCase()) || - device.wxid.toLowerCase().includes(searchQuery.toLowerCase()); - const matchesStatus = statusFilter === "all" || (statusFilter === "online" && device.status === "online") || (statusFilter === "offline" && device.status === "offline"); - - return matchesSearch && matchesStatus; + return matchesStatus; }); const handleDeviceSelect = (deviceId: string) => { @@ -137,7 +149,7 @@ export function DeviceSelectionDialog({ -
+
+ +
} > -
+
- + setForm(f => ({ ...f, name: e.target.value }))} + onChange={(e) => + setForm((f) => ({ ...f, name: e.target.value })) + } placeholder="请输入内容库名称" required />
- setForm(f => ({ ...f, sourceType: val as 'friends' | 'groups' }))}> + + setForm((f) => ({ + ...f, + sourceType: val as "friends" | "groups", + })) + } + > 选择微信好友 选择聊天群 f.id)} - onSelect={ids => setForm(f => ({ - ...f, - selectedFriends: ids.map(id => ({ id, nickname: id, avatar: '' })) - }))} + selectedFriends={form.selectedFriends.map((f) => f.id)} + onSelect={(ids) => + setForm((f) => ({ + ...f, + selectedFriends: ids.map((id) => ({ + id, + nickname: id, + avatar: "", + })), + })) + } onSelectDetail={setSelectedFriendObjs} enableDeviceFilter={false} placeholder="选择微信好友" /> {selectedFriendObjs.length > 0 && (
- {selectedFriendObjs.map(friend => ( -
+ {selectedFriendObjs.map((friend) => ( +
{friend.avatar ? ( - {friend.nickname} + {friend.nickname} ) : ( -
{friend.nickname?.charAt(0) || '友'}
+
+ {friend.nickname?.charAt(0) || "友"} +
)} {friend.nickname}