Files
ckb-SuperAdmin/nkebao/src/pages/wechat-accounts/list/index.tsx
笔记本里的永平 f3f2e8db41 feat: 本次提交更新内容如下
存一下进度
2025-07-23 15:14:01 +08:00

310 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState, useEffect } from "react";
import {
NavBar,
List,
Card,
Button,
SpinLoading,
Popup,
Toast,
} from "antd-mobile";
import { Pagination, Input, Tooltip } from "antd";
import {
ArrowLeftOutlined,
SearchOutlined,
ReloadOutlined,
} from "@ant-design/icons";
import { useNavigate } from "react-router-dom";
import Layout from "@/components/Layout/Layout";
import style from "./index.module.scss";
import { getWechatAccounts } from "./api";
interface WechatAccount {
id: number;
nickname: string;
avatar: string;
wechatId: string;
wechatAccount: string;
deviceId: number;
times: number; // 今日可添加
addedCount: number; // 今日新增
wechatStatus: number; // 1正常 0异常
totalFriend: number;
deviceMemo: string; // 设备名
activeTime: string; // 最后活跃
}
const PAGE_SIZE = 10;
const WechatAccounts: React.FC = () => {
const navigate = useNavigate();
const [accounts, setAccounts] = useState<WechatAccount[]>([]);
const [searchTerm, setSearchTerm] = useState("");
const [currentPage, setCurrentPage] = useState(1);
const [totalAccounts, setTotalAccounts] = useState(0);
const [isLoading, setIsLoading] = useState(true);
const [isRefreshing, setIsRefreshing] = useState(false);
const [popupVisible, setPopupVisible] = useState(false);
const [selectedAccount, setSelectedAccount] = useState<WechatAccount | null>(
null
);
const fetchAccounts = async (page = 1, keyword = "") => {
setIsLoading(true);
try {
const res = await getWechatAccounts({
page,
page_size: PAGE_SIZE,
keyword,
});
if (res && res.list) {
setAccounts(res.list);
setTotalAccounts(res.total || 0);
} else {
setAccounts([]);
setTotalAccounts(0);
}
} catch (e) {
Toast.show({ content: "获取微信号失败", position: "top" });
setAccounts([]);
setTotalAccounts(0);
} finally {
setIsLoading(false);
}
};
useEffect(() => {
fetchAccounts(currentPage, searchTerm);
// eslint-disable-next-line
}, [currentPage]);
const handleSearch = () => {
setCurrentPage(1);
fetchAccounts(1, searchTerm);
};
const handleRefresh = async () => {
setIsRefreshing(true);
await fetchAccounts(currentPage, searchTerm);
setIsRefreshing(false);
Toast.show({ content: "刷新成功", position: "top" });
};
const handleAccountClick = (account: WechatAccount) => {
setSelectedAccount(account);
setPopupVisible(true);
};
const handleTransferFriends = (account: WechatAccount) => {
// TODO: 实现好友转移弹窗或跳转
Toast.show({ content: `好友转移:${account.nickname}` });
};
return (
<Layout
header={
<>
<NavBar
back={null}
style={{ background: "#fff" }}
left={
<div className={style["nav-title"]}>
<ArrowLeftOutlined
twoToneColor="#1677ff"
onClick={() => navigate(-1)}
/>
</div>
}
>
<span className={style["nav-title"]}></span>
</NavBar>
<div className="search-bar">
<div className="search-input-wrapper">
<Input
placeholder="搜索微信号/昵称"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
prefix={<SearchOutlined />}
allowClear
size="large"
onPressEnter={handleSearch}
/>
</div>
<Button
size="small"
onClick={handleRefresh}
loading={isRefreshing}
className="refresh-btn"
>
<ReloadOutlined />
</Button>
</div>
</>
}
>
<div className={style["wechat-accounts-page"]}>
{isLoading ? (
<div className={style["loading"]}>
<SpinLoading color="primary" style={{ fontSize: 32 }} />
</div>
) : accounts.length === 0 ? (
<div className={style["empty"]}></div>
) : (
<div className={style["card-list"]}>
{accounts.map((account) => {
const percent =
account.times > 0
? Math.min((account.addedCount / account.times) * 100, 100)
: 0;
return (
<div
key={account.id}
className={style["account-card"]}
onClick={() => handleAccountClick(account)}
>
<div className={style["card-header"]}>
<div className={style["avatar-wrapper"]}>
<img
src={account.avatar}
alt={account.nickname}
className={style["avatar"]}
/>
<span
className={
account.wechatStatus === 1
? style["status-dot-normal"]
: style["status-dot-abnormal"]
}
/>
</div>
<div className={style["header-info"]}>
<div className={style["nickname-row"]}>
<span className={style["nickname"]}>
{account.nickname}
</span>
<span
className={
account.wechatStatus === 1
? style["status-label-normal"]
: style["status-label-abnormal"]
}
>
{account.wechatStatus === 1 ? "正常" : "异常"}
</span>
</div>
<div className={style["wechat-id"]}>
{account.wechatAccount}
</div>
</div>
</div>
<div className={style["card-body"]}>
<div className={style["row-group"]}>
<div className={style["row-item"]}>
<span></span>
<span className={style["strong"]}>
{account.totalFriend}
</span>
</div>
<div className={style["row-item"]}>
<span></span>
<span className={style["strong-green"]}>
+{account.addedCount}
</span>
</div>
</div>
<div className={style["row-group"]}>
<div className={style["row-item"]}>
<span></span>
<span>{account.times}</span>
</div>
<div className={style["row-item"]}>
<Tooltip title={`每日最多添加 ${account.times} 个好友`}>
<span></span>
<span>
{account.addedCount}/{account.times}
</span>
</Tooltip>
</div>
</div>
<div className={style["progress-bar"]}>
<div className={style["progress-bg"]}>
<div
className={style["progress-fill"]}
style={{ width: `${percent}%` }}
/>
</div>
</div>
<div className={style["row-group"]}>
<div className={style["row-item"]}>
<span></span>
<span>{account.deviceMemo || "-"}</span>
</div>
<div className={style["row-item"]}>
<span></span>
<span>{account.activeTime}</span>
</div>
</div>
</div>
</div>
);
})}
</div>
)}
<div className={style["pagination"]}>
{totalAccounts > PAGE_SIZE && (
<Pagination
total={Math.ceil(totalAccounts / PAGE_SIZE)}
current={currentPage}
onChange={setCurrentPage}
/>
)}
</div>
<Popup
visible={popupVisible}
onMaskClick={() => setPopupVisible(false)}
bodyStyle={{ borderRadius: "16px 16px 0 0" }}
>
{selectedAccount && (
<div className={style["popup-content"]}>
<div style={{ textAlign: "center", margin: 16 }}>
<img
src={selectedAccount.avatar}
alt="avatar"
style={{ width: 60, height: 60, borderRadius: 30 }}
/>
<div style={{ fontWeight: 600, marginTop: 8 }}>
{selectedAccount.nickname}
</div>
<div style={{ color: "#888", fontSize: 12 }}>
{selectedAccount.wechatAccount}
</div>
</div>
<div style={{ margin: 16 }}>
<Button
block
color="primary"
onClick={() => {
navigate(`/wechat-accounts/detail/${selectedAccount.id}`);
}}
>
</Button>
</div>
<Button
block
color="danger"
fill="outline"
onClick={() => setPopupVisible(false)}
>
</Button>
</div>
)}
</Popup>
</div>
</Layout>
);
};
export default WechatAccounts;