2025-08-12 09:27:50 +08:00
|
|
|
|
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;
|
2025-08-22 14:47:39 +08:00
|
|
|
|
deviceIds?: number[];
|
2025-08-12 09:27:50 +08:00
|
|
|
|
enableDeviceFilter?: boolean;
|
|
|
|
|
|
readonly?: boolean;
|
|
|
|
|
|
onConfirm?: (
|
|
|
|
|
|
selectedIds: number[],
|
|
|
|
|
|
selectedItems: FriendSelectionItem[],
|
|
|
|
|
|
) => void;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const SelectionPopup: React.FC<SelectionPopupProps> = ({
|
|
|
|
|
|
visible,
|
|
|
|
|
|
onVisibleChange,
|
|
|
|
|
|
selectedOptions,
|
|
|
|
|
|
onSelect,
|
|
|
|
|
|
deviceIds = [],
|
|
|
|
|
|
enableDeviceFilter = true,
|
|
|
|
|
|
readonly = false,
|
|
|
|
|
|
onConfirm,
|
|
|
|
|
|
}) => {
|
|
|
|
|
|
const [friends, setFriends] = useState<FriendSelectionItem[]>([]);
|
|
|
|
|
|
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 (
|
|
|
|
|
|
<Popup
|
|
|
|
|
|
visible={visible && !readonly}
|
|
|
|
|
|
onMaskClick={() => onVisibleChange(false)}
|
|
|
|
|
|
position="bottom"
|
|
|
|
|
|
bodyStyle={{ height: "100vh" }}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Layout
|
|
|
|
|
|
header={
|
|
|
|
|
|
<PopupHeader
|
|
|
|
|
|
title="选择微信好友"
|
|
|
|
|
|
searchQuery={searchQuery}
|
|
|
|
|
|
setSearchQuery={setSearchQuery}
|
|
|
|
|
|
searchPlaceholder="搜索好友"
|
|
|
|
|
|
loading={loading}
|
|
|
|
|
|
onRefresh={() => fetchFriends(currentPage, searchQuery)}
|
|
|
|
|
|
/>
|
|
|
|
|
|
}
|
|
|
|
|
|
footer={
|
|
|
|
|
|
<PopupFooter
|
|
|
|
|
|
total={totalFriends}
|
|
|
|
|
|
currentPage={currentPage}
|
|
|
|
|
|
totalPages={totalPages}
|
|
|
|
|
|
loading={loading}
|
|
|
|
|
|
selectedCount={selectedOptions.length}
|
|
|
|
|
|
onPageChange={setCurrentPage}
|
|
|
|
|
|
onCancel={() => onVisibleChange(false)}
|
|
|
|
|
|
onConfirm={handleConfirm}
|
|
|
|
|
|
/>
|
|
|
|
|
|
}
|
|
|
|
|
|
>
|
|
|
|
|
|
<div className={style.friendList}>
|
|
|
|
|
|
{loading ? (
|
|
|
|
|
|
<div className={style.loadingBox}>
|
|
|
|
|
|
<div className={style.loadingText}>加载中...</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
) : friends.length > 0 ? (
|
|
|
|
|
|
<div className={style.friendListInner}>
|
|
|
|
|
|
{friends.map(friend => (
|
|
|
|
|
|
<div key={friend.id} className={style.friendItem}>
|
|
|
|
|
|
<Checkbox
|
|
|
|
|
|
checked={selectedOptions.some(f => f.id === friend.id)}
|
|
|
|
|
|
onChange={() => !readonly && handleFriendToggle(friend)}
|
|
|
|
|
|
disabled={readonly}
|
|
|
|
|
|
style={{ marginRight: 12 }}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<div className={style.friendInfo}>
|
|
|
|
|
|
<div className={style.friendAvatar}>
|
|
|
|
|
|
{friend.avatar ? (
|
|
|
|
|
|
<img
|
|
|
|
|
|
src={friend.avatar}
|
|
|
|
|
|
alt={friend.nickname}
|
|
|
|
|
|
className={style.avatarImg}
|
|
|
|
|
|
/>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
friend.nickname.charAt(0)
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className={style.friendDetail}>
|
|
|
|
|
|
<div className={style.friendName}>{friend.nickname}</div>
|
|
|
|
|
|
<div className={style.friendId}>
|
|
|
|
|
|
微信ID: {friend.wechatId}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
{friend.customer && (
|
|
|
|
|
|
<div className={style.friendCustomer}>
|
|
|
|
|
|
归属客户: {friend.customer}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<div className={style.emptyBox}>
|
|
|
|
|
|
<div className={style.emptyText}>
|
|
|
|
|
|
{deviceIds.length === 0
|
|
|
|
|
|
? "请先选择设备"
|
|
|
|
|
|
: searchQuery
|
|
|
|
|
|
? `没有找到包含"${searchQuery}"的好友`
|
|
|
|
|
|
: "没有找到好友"}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</Layout>
|
|
|
|
|
|
</Popup>
|
|
|
|
|
|
);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export default SelectionPopup;
|