From d785f11aff3239831e88661c1581644f26e3c0ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B6=85=E7=BA=A7=E8=80=81=E7=99=BD=E5=85=94?= Date: Fri, 8 Aug 2025 13:56:34 +0800 Subject: [PATCH] =?UTF-8?q?FEAT=20=3D>=20=E6=9C=AC=E6=AC=A1=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E9=A1=B9=E7=9B=AE=E4=B8=BA=EF=BC=9A=20=E5=A5=BD?= =?UTF-8?q?=E5=8F=8B=E9=80=89=E6=8B=A9=E6=9E=84=E5=BB=BA=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FriendSelection/index.module.scss | 15 + .../src/components/FriendSelection/index.tsx | 273 +++--------------- .../FriendSelection/selectionPopup.tsx | 213 ++++++++++++++ nkebao/src/pages/mobile/test/select.tsx | 6 +- 4 files changed, 278 insertions(+), 229 deletions(-) create mode 100644 nkebao/src/components/FriendSelection/selectionPopup.tsx diff --git a/nkebao/src/components/FriendSelection/index.module.scss b/nkebao/src/components/FriendSelection/index.module.scss index 51eb1af5..f450dde2 100644 --- a/nkebao/src/components/FriendSelection/index.module.scss +++ b/nkebao/src/components/FriendSelection/index.module.scss @@ -1,6 +1,21 @@ .inputWrapper { position: relative; } +.selectedListRow { + padding: 8px; + border-bottom: 1px solid #f0f0f0; + font-size: 14px; +} +.selectedListRowContent { + flex: 1; + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; +} +.selectedListRowContentText { + flex: 1; +} .inputIcon { position: absolute; left: 12px; diff --git a/nkebao/src/components/FriendSelection/index.tsx b/nkebao/src/components/FriendSelection/index.tsx index 51fcc382..efdcad64 100644 --- a/nkebao/src/components/FriendSelection/index.tsx +++ b/nkebao/src/components/FriendSelection/index.tsx @@ -1,13 +1,10 @@ -import React, { useState, useEffect } from "react"; +import React, { useState } from "react"; import { SearchOutlined, DeleteOutlined } from "@ant-design/icons"; -import { Popup } from "antd-mobile"; import { Button, Input } from "antd"; -import { getFriendList } from "./api"; +import { Avatar } from "antd-mobile"; import style from "./index.module.scss"; -import Layout from "@/components/Layout/Layout"; -import PopupHeader from "@/components/PopuLayout/header"; -import PopupFooter from "@/components/PopuLayout/footer"; -import { FriendSelectionProps, FriendSelectionItem } from "./data"; +import { FriendSelectionProps } from "./data"; +import SelectionPopup from "./selectionPopup"; export default function FriendSelection({ selectedOptions, @@ -25,12 +22,7 @@ export default function FriendSelection({ onConfirm, }: FriendSelectionProps) { const [popupVisible, setPopupVisible] = useState(false); - const [friends, setFriends] = useState([]); - const [searchQuery, setSearchQuery] = useState(""); - const [currentPage, setCurrentPage] = useState(1); - const [totalPages, setTotalPages] = useState(1); - const [totalFriends, setTotalFriends] = useState(0); - const [loading, setLoading] = useState(false); + // 内部弹窗交给 selectionPopup 处理 // 受控弹窗逻辑 const realVisible = visible !== undefined ? visible : popupVisible; @@ -39,75 +31,10 @@ export default function FriendSelection({ if (visible === undefined) setPopupVisible(v); }; - // 打开弹窗并请求第一页好友 + // 打开弹窗 const openPopup = () => { if (readonly) return; - setCurrentPage(1); - setSearchQuery(""); setRealVisible(true); - fetchFriends(1, ""); - }; - - // 当页码变化时,拉取对应页数据(弹窗已打开时) - useEffect(() => { - if (realVisible && currentPage !== 1) { - fetchFriends(currentPage, searchQuery); - } - }, [currentPage, realVisible, searchQuery]); - - // 搜索防抖 - useEffect(() => { - if (!realVisible) return; - - const timer = setTimeout(() => { - setCurrentPage(1); - fetchFriends(1, searchQuery); - }, 500); - - return () => clearTimeout(timer); - }, [searchQuery, realVisible]); - - // 获取好友列表API - 添加 keyword 参数 - const fetchFriends = async (page: number, keyword: string = "") => { - setLoading(true); - try { - const params: any = { - page, - limit: 20, - }; - - if (keyword.trim()) { - params.keyword = keyword.trim(); - } - - if (enableDeviceFilter && deviceIds.length > 0) { - params.deviceIds = deviceIds.join(","); - } - - const response = await getFriendList(params); - if (response && response.list) { - setFriends(response.list); - setTotalFriends(response.total || 0); - setTotalPages(Math.ceil((response.total || 0) / 20)); - } - } catch (error) { - console.error("获取好友列表失败:", error); - } finally { - setLoading(false); - } - }; - - // 处理好友选择 - const handleFriendToggle = (friend: FriendSelectionItem) => { - if (readonly) return; - - const newSelectedFriends = selectedOptions - .map(v => v.id) - .includes(friend.id) - ? selectedOptions.filter(v => v.id !== friend.id) - : selectedOptions.concat(friend); - - onSelect(newSelectedFriends); }; // 获取显示文本 @@ -122,14 +49,13 @@ export default function FriendSelection({ onSelect(selectedOptions.filter(v => v.id !== id)); }; - // 确认选择 - const handleConfirm = () => { - if (onConfirm) { - onConfirm( - selectedOptions.map(v => v.id), - selectedOptions, - ); - } + // 弹窗确认回调 + const handleConfirm = ( + selectedIds: number[], + selectedItems: typeof selectedOptions, + ) => { + onSelect(selectedItems); + if (onConfirm) onConfirm(selectedIds, selectedItems); setRealVisible(false); }; @@ -167,151 +93,48 @@ export default function FriendSelection({ }} > {selectedOptions.map(friend => ( -
-
- {friend.nickname || friend.wechatId || friend.id} +
+
+ +
+
{friend.nickname}
+
{friend.wechatId}
+
+ {!readonly && ( +
- {!readonly && ( -
))}
)} {/* 弹窗 */} - setRealVisible(false)} - position="bottom" - bodyStyle={{ height: "100vh" }} - > - fetchFriends(currentPage, searchQuery)} - /> - } - footer={ - setRealVisible(false)} - onConfirm={handleConfirm} - /> - } - > -
- {loading ? ( -
-
加载中...
-
- ) : friends.length > 0 ? ( -
- {friends.map(friend => ( - - ))} -
- ) : ( -
-
- {deviceIds.length === 0 - ? "请先选择设备" - : searchQuery - ? `没有找到包含"${searchQuery}"的好友` - : "没有找到好友"} -
-
- )} -
-
-
+ onVisibleChange={setRealVisible} + selectedOptions={selectedOptions} + onSelect={onSelect} + deviceIds={deviceIds} + enableDeviceFilter={enableDeviceFilter} + readonly={readonly} + onConfirm={handleConfirm} + /> ); } diff --git a/nkebao/src/components/FriendSelection/selectionPopup.tsx b/nkebao/src/components/FriendSelection/selectionPopup.tsx new file mode 100644 index 00000000..f5713c80 --- /dev/null +++ b/nkebao/src/components/FriendSelection/selectionPopup.tsx @@ -0,0 +1,213 @@ +import React, { useCallback, useEffect, useState } from "react"; +import { Popup, Checkbox } from "antd-mobile"; +import Layout from "@/components/Layout/Layout"; +import PopupHeader from "@/components/PopuLayout/header"; +import PopupFooter from "@/components/PopuLayout/footer"; +import { getFriendList } from "./api"; +import style from "./index.module.scss"; +import type { FriendSelectionItem } from "./data"; + +interface SelectionPopupProps { + visible: boolean; + onVisibleChange: (visible: boolean) => void; + selectedOptions: FriendSelectionItem[]; + onSelect: (friends: FriendSelectionItem[]) => void; + deviceIds?: string[]; + enableDeviceFilter?: boolean; + readonly?: boolean; + onConfirm?: ( + selectedIds: number[], + selectedItems: FriendSelectionItem[], + ) => void; +} + +const SelectionPopup: React.FC = ({ + visible, + onVisibleChange, + selectedOptions, + onSelect, + deviceIds = [], + enableDeviceFilter = true, + readonly = false, + onConfirm, +}) => { + const [friends, setFriends] = useState([]); + const [searchQuery, setSearchQuery] = useState(""); + const [currentPage, setCurrentPage] = useState(1); + const [totalPages, setTotalPages] = useState(1); + const [totalFriends, setTotalFriends] = useState(0); + const [loading, setLoading] = useState(false); + + // 获取好友列表API + const fetchFriends = useCallback( + async (page: number, keyword: string = "") => { + setLoading(true); + try { + const params: any = { + page, + limit: 20, + }; + + if (keyword.trim()) { + params.keyword = keyword.trim(); + } + + if (enableDeviceFilter && deviceIds.length > 0) { + params.deviceIds = deviceIds.join(","); + } + + const response = await getFriendList(params); + if (response && response.list) { + setFriends(response.list); + setTotalFriends(response.total || 0); + setTotalPages(Math.ceil((response.total || 0) / 20)); + } + } catch (error) { + console.error("获取好友列表失败:", error); + } finally { + setLoading(false); + } + }, + [deviceIds, enableDeviceFilter], + ); + + // 处理好友选择 + const handleFriendToggle = (friend: FriendSelectionItem) => { + if (readonly) return; + + const newSelectedFriends = selectedOptions.some(f => f.id === friend.id) + ? selectedOptions.filter(f => f.id !== friend.id) + : selectedOptions.concat(friend); + + onSelect(newSelectedFriends); + }; + + // 确认选择 + const handleConfirm = () => { + if (onConfirm) { + onConfirm( + selectedOptions.map(v => v.id), + selectedOptions, + ); + } + onVisibleChange(false); + }; + + // 弹窗打开时初始化 + useEffect(() => { + if (visible) { + setCurrentPage(1); + setSearchQuery(""); + fetchFriends(1, ""); + } + }, [visible]); // 只在弹窗开启时请求 + + // 搜索防抖(只在弹窗打开且搜索词变化时执行) + useEffect(() => { + if (!visible || searchQuery === "") return; // 弹窗关闭或搜索词为空时不请求 + + const timer = setTimeout(() => { + setCurrentPage(1); + fetchFriends(1, searchQuery); + }, 500); + + return () => clearTimeout(timer); + }, [searchQuery, visible]); + + // 页码变化时请求数据(只在弹窗打开且页码不是1时执行) + useEffect(() => { + if (!visible || currentPage === 1) return; // 弹窗关闭或第一页时不请求 + fetchFriends(currentPage, searchQuery); + }, [currentPage, visible, searchQuery]); + + return ( + onVisibleChange(false)} + position="bottom" + bodyStyle={{ height: "100vh" }} + > + fetchFriends(currentPage, searchQuery)} + /> + } + footer={ + onVisibleChange(false)} + onConfirm={handleConfirm} + /> + } + > +
+ {loading ? ( +
+
加载中...
+
+ ) : friends.length > 0 ? ( +
+ {friends.map(friend => ( +
+ f.id === friend.id)} + onChange={() => !readonly && handleFriendToggle(friend)} + disabled={readonly} + style={{ marginRight: 12 }} + /> +
+
+ {friend.avatar ? ( + {friend.nickname} + ) : ( + friend.nickname.charAt(0) + )} +
+
+
{friend.nickname}
+
+ 微信ID: {friend.wechatId} +
+ {friend.customer && ( +
+ 归属客户: {friend.customer} +
+ )} +
+
+
+ ))} +
+ ) : ( +
+
+ {deviceIds.length === 0 + ? "请先选择设备" + : searchQuery + ? `没有找到包含"${searchQuery}"的好友` + : "没有找到好友"} +
+
+ )} +
+
+
+ ); +}; + +export default SelectionPopup; diff --git a/nkebao/src/pages/mobile/test/select.tsx b/nkebao/src/pages/mobile/test/select.tsx index 65dc43e7..9dea5a6a 100644 --- a/nkebao/src/pages/mobile/test/select.tsx +++ b/nkebao/src/pages/mobile/test/select.tsx @@ -12,14 +12,11 @@ import { GroupSelectionItem } from "@/components/GroupSelection/data"; import { ContentItem } from "@/components/ContentSelection/data"; import { FriendSelectionItem } from "@/components/FriendSelection/data"; const ComponentTest: React.FC = () => { - const [activeTab, setActiveTab] = useState("libraries"); + const [activeTab, setActiveTab] = useState("friends"); // 设备选择状态 const [selectedDevices, setSelectedDevices] = useState([]); - // 好友选择状态 - const [selectedFriends, setSelectedFriends] = useState([]); - // 群组选择状态 const [selectedGroups, setSelectedGroups] = useState( [], @@ -29,6 +26,7 @@ const ComponentTest: React.FC = () => { const [selectedContent, setSelectedContent] = useState([]); const [selectedAccounts, setSelectedAccounts] = useState([]); + // 好友选择状态 const [selectedFriendsOptions, setSelectedFriendsOptions] = useState< FriendSelectionItem[] >([]);