From 566ca54fcf6517b60a8a56096e37ba5f540a75ff 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: Thu, 14 Aug 2025 15:04:03 +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?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cunkebao/dist/.vite/manifest.json | 4 +- Cunkebao/dist/index.html | 4 +- .../plan/new/steps/FriendRequestSettings.tsx | 20 +- .../list/AccountListModal.tsx | 126 +++++++ .../list/DeviceListModal.tsx | 141 ++++++++ .../list/index.module.scss | 315 ++++++++++++++++++ .../traffic-distribution/list/index.tsx | 76 ++++- 7 files changed, 668 insertions(+), 18 deletions(-) create mode 100644 Cunkebao/src/pages/mobile/workspace/traffic-distribution/list/AccountListModal.tsx create mode 100644 Cunkebao/src/pages/mobile/workspace/traffic-distribution/list/DeviceListModal.tsx diff --git a/Cunkebao/dist/.vite/manifest.json b/Cunkebao/dist/.vite/manifest.json index 70d5d6b2..70988547 100644 --- a/Cunkebao/dist/.vite/manifest.json +++ b/Cunkebao/dist/.vite/manifest.json @@ -33,7 +33,7 @@ "name": "vendor" }, "index.html": { - "file": "assets/index-Cp05akVy.js", + "file": "assets/index-AbKWTcUz.js", "name": "index", "src": "index.html", "isEntry": true, @@ -44,7 +44,7 @@ "_charts-D0fT04H8.js" ], "css": [ - "assets/index-Eg_DAu9e.css" + "assets/index-V1Q-fxX3.css" ] } } \ No newline at end of file diff --git a/Cunkebao/dist/index.html b/Cunkebao/dist/index.html index 6261e31e..16cb8970 100644 --- a/Cunkebao/dist/index.html +++ b/Cunkebao/dist/index.html @@ -11,13 +11,13 @@ - + - +
diff --git a/Cunkebao/src/pages/mobile/scenarios/plan/new/steps/FriendRequestSettings.tsx b/Cunkebao/src/pages/mobile/scenarios/plan/new/steps/FriendRequestSettings.tsx index 4734c615..5aa73aed 100644 --- a/Cunkebao/src/pages/mobile/scenarios/plan/new/steps/FriendRequestSettings.tsx +++ b/Cunkebao/src/pages/mobile/scenarios/plan/new/steps/FriendRequestSettings.tsx @@ -87,18 +87,14 @@ const FriendRequestSettings: React.FC = ({ return (
{/* 选择设备区块 */} - {![7].includes(formData.scenario) && ( - <> -
选择设备
-
- -
- - )} +
选择设备
+
+ +
{/* 好友备注区块 */}
好友备注
diff --git a/Cunkebao/src/pages/mobile/workspace/traffic-distribution/list/AccountListModal.tsx b/Cunkebao/src/pages/mobile/workspace/traffic-distribution/list/AccountListModal.tsx new file mode 100644 index 00000000..79bc23a7 --- /dev/null +++ b/Cunkebao/src/pages/mobile/workspace/traffic-distribution/list/AccountListModal.tsx @@ -0,0 +1,126 @@ +import React from "react"; +import { Popup, Avatar } from "antd-mobile"; +import { Button } from "antd"; +import { CloseOutlined } from "@ant-design/icons"; +import style from "./index.module.scss"; + +interface AccountItem { + id: string | number; + nickname?: string; + wechatId?: string; + avatar?: string; + status?: string; +} + +interface AccountListModalProps { + visible: boolean; + onClose: () => void; + accounts: AccountItem[]; + title?: string; +} + +const AccountListModal: React.FC = ({ + visible, + onClose, + accounts, + title = "分发账号列表", +}) => { + const getStatusColor = (status?: string) => { + switch (status) { + case "normal": + return "#52c41a"; + case "limited": + return "#faad14"; + case "blocked": + return "#ff4d4f"; + default: + return "#d9d9d9"; + } + }; + + const getStatusText = (status?: string) => { + switch (status) { + case "normal": + return "正常"; + case "limited": + return "受限"; + case "blocked": + return "封禁"; + default: + return "未知"; + } + }; + + return ( + +
+ {/* 头部 */} +
+

{title}

+
+ + {/* 账号列表 */} +
+ {accounts.length > 0 ? ( + accounts.map((account, index) => ( +
+
+ + {(account.nickname || account.wechatId || "账号")[0]} + +
+
+
+ {account.nickname || + account.wechatId || + `账号${account.id}`} +
+
+ {account.wechatId || "未绑定微信号"} +
+
+
+ + + {getStatusText(account.status)} + +
+
+ )) + ) : ( +
+
暂无账号数据
+
+ )} +
+ + {/* 底部统计 */} +
+
+ 共 {accounts.length} 个账号 +
+
+
+
+ ); +}; + +export default AccountListModal; diff --git a/Cunkebao/src/pages/mobile/workspace/traffic-distribution/list/DeviceListModal.tsx b/Cunkebao/src/pages/mobile/workspace/traffic-distribution/list/DeviceListModal.tsx new file mode 100644 index 00000000..ea26f58e --- /dev/null +++ b/Cunkebao/src/pages/mobile/workspace/traffic-distribution/list/DeviceListModal.tsx @@ -0,0 +1,141 @@ +import React from "react"; +import { Popup, Avatar } from "antd-mobile"; +import { Button } from "antd"; +import { CloseOutlined } from "@ant-design/icons"; +import style from "./index.module.scss"; + +interface DeviceItem { + id: string | number; + memo?: string; + imei?: string; + wechatId?: string; + status?: "online" | "offline"; + avatar?: string; + totalFriend?: number; +} + +interface DeviceListModalProps { + visible: boolean; + onClose: () => void; + devices: DeviceItem[]; + title?: string; +} + +const DeviceListModal: React.FC = ({ + visible, + onClose, + devices, + title = "分发设备列表", +}) => { + const getStatusColor = (status?: string) => { + return status === "online" ? "#52c41a" : "#ff4d4f"; + }; + + const getStatusText = (status?: string) => { + return status === "online" ? "在线" : "离线"; + }; + + return ( + +
+ {/* 头部 */} +
+

{title}

+
+ + {/* 设备列表 */} +
+ {devices.length > 0 ? ( + devices.map((device, index) => ( +
+ {/* 顶部行:IMEI */} +
+ + IMEI: {device.imei?.toUpperCase() || "-"} + +
+ + {/* 主要内容区域:头像和详细信息 */} +
+ {/* 头像 */} +
+ {device.avatar ? ( + 头像 + ) : ( + + {(device.memo || device.wechatId || "设")[0]} + + )} +
+ + {/* 设备信息 */} +
+
+

+ {device.memo || "未命名设备"} +

+ + {getStatusText(device.status)} + +
+ +
+
+ 微信号: + + {device.wechatId || "未绑定"} + +
+
+ 好友数: + + {device.totalFriend ?? "-"} + +
+
+
+
+
+ )) + ) : ( +
+
暂无设备数据
+
+ )} +
+ + {/* 底部统计 */} +
+
+ 共 {devices.length} 个设备 +
+
+
+
+ ); +}; + +export default DeviceListModal; diff --git a/Cunkebao/src/pages/mobile/workspace/traffic-distribution/list/index.module.scss b/Cunkebao/src/pages/mobile/workspace/traffic-distribution/list/index.module.scss index a9770b88..f30dfa28 100644 --- a/Cunkebao/src/pages/mobile/workspace/traffic-distribution/list/index.module.scss +++ b/Cunkebao/src/pages/mobile/workspace/traffic-distribution/list/index.module.scss @@ -69,10 +69,15 @@ .ruleMetaItem { flex: 1; text-align: center; + transition: background-color 0.2s ease; } .ruleMetaItem:not(:last-child) { border-right: 1px solid #f0f0f0; } +.ruleMetaItem:hover { + background-color: #f8f9fa; + border-radius: 6px; +} .ruleDivider { border-top: 1px solid #f0f0f0; @@ -124,3 +129,313 @@ padding: 16px 0; background: #fff; } + +// 账号列表弹窗样式 +.accountModal { + height: 100%; + display: flex; + flex-direction: column; +} + +.accountModalHeader { + display: flex; + align-items: center; + justify-content: space-between; + padding: 16px 20px; + border-bottom: 1px solid #f0f0f0; + background: #fff; +} + +.accountModalTitle { + margin: 0; + font-size: 18px; + font-weight: 600; + color: #222; +} + +.accountModalClose { + border: none; + background: none; + color: #888; + font-size: 16px; +} + +.accountList { + flex: 1; + overflow-y: auto; + padding: 0 20px; +} + +.accountItem { + display: flex; + align-items: center; + padding: 12px 0; + border-bottom: 1px solid #f5f5f5; +} + +.accountItem:last-child { + border-bottom: none; +} + +.accountAvatar { + margin-right: 12px; + flex-shrink: 0; +} + +.accountInfo { + flex: 1; + min-width: 0; +} + +.accountName { + font-size: 16px; + font-weight: 500; + color: #222; + margin-bottom: 4px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.accountWechatId { + font-size: 14px; + color: #888; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.accountStatus { + display: flex; + align-items: center; + gap: 6px; + flex-shrink: 0; +} + +.statusDot { + width: 8px; + height: 8px; + border-radius: 50%; +} + +.statusText { + font-size: 13px; + color: #666; +} + +.accountEmpty { + display: flex; + align-items: center; + justify-content: center; + height: 200px; + color: #888; +} + +.accountEmptyText { + font-size: 16px; +} + +.accountModalFooter { + padding: 16px 20px; + border-top: 1px solid #f0f0f0; + background: #fff; +} + +.accountStats { + text-align: center; + font-size: 14px; + color: #666; +} + +// 设备列表弹窗样式 +.deviceModal { + height: 100%; + display: flex; + flex-direction: column; +} + +.deviceModalHeader { + display: flex; + align-items: center; + justify-content: space-between; + padding: 16px 20px; + border-bottom: 1px solid #f0f0f0; + background: #fff; +} + +.deviceModalTitle { + margin: 0; + font-size: 18px; + font-weight: 600; + color: #222; +} + +.deviceModalClose { + border: none; + background: none; + color: #888; + font-size: 16px; +} + +.deviceList { + flex: 1; + overflow-y: auto; + padding: 0 20px; +} + +.deviceItem { + background: #fff; + border-radius: 12px; + padding: 12px; + margin-bottom: 12px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04); + border: 1px solid #ececec; + transition: all 0.2s ease; +} + +.deviceItem:hover { + transform: translateY(-1px); + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1); +} + +.deviceHeaderRow { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 8px; +} + +.deviceImeiText { + font-size: 13px; + color: #888; + font-weight: 500; +} + +.deviceMainContent { + display: flex; + align-items: center; +} + +.deviceAvatar { + width: 48px; + height: 48px; + border-radius: 12px; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + display: flex; + align-items: center; + justify-content: center; + margin-right: 12px; + flex-shrink: 0; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + overflow: hidden; +} + +.deviceAvatar img { + width: 100%; + height: 100%; + object-fit: cover; + border-radius: 12px; +} + +.deviceAvatarText { + color: #fff; + font-size: 18px; + font-weight: 600; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3); +} + +.deviceInfo { + flex: 1; + min-width: 0; +} + +.deviceInfoHeader { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 6px; +} + +.deviceName { + margin: 0; + font-size: 16px; + font-weight: 600; + color: #222; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + flex: 1; + margin-right: 8px; +} + +.deviceStatusBadge { + padding: 2px 8px; + border-radius: 10px; + font-size: 12px; + font-weight: 500; + flex-shrink: 0; +} + +.deviceStatusOnline { + background: rgba(82, 196, 26, 0.1); + color: #52c41a; +} + +.deviceStatusOffline { + background: rgba(255, 77, 79, 0.1); + color: #ff4d4f; +} + +.deviceInfoList { + display: flex; + flex-direction: column; + gap: 4px; +} + +.deviceInfoItem { + display: flex; + align-items: center; + font-size: 13px; +} + +.deviceInfoLabel { + color: #888; + margin-right: 6px; + min-width: 50px; +} + +.deviceInfoValue { + color: #444; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.deviceFriendCount { + color: #1890ff; + font-weight: 500; +} + +.deviceEmpty { + display: flex; + align-items: center; + justify-content: center; + height: 200px; + color: #888; +} + +.deviceEmptyText { + font-size: 16px; +} + +.deviceModalFooter { + padding: 16px 20px; + border-top: 1px solid #f0f0f0; + background: #fff; +} + +.deviceStats { + text-align: center; + font-size: 14px; + color: #666; +} diff --git a/Cunkebao/src/pages/mobile/workspace/traffic-distribution/list/index.tsx b/Cunkebao/src/pages/mobile/workspace/traffic-distribution/list/index.tsx index 940622a8..0b571ecf 100644 --- a/Cunkebao/src/pages/mobile/workspace/traffic-distribution/list/index.tsx +++ b/Cunkebao/src/pages/mobile/workspace/traffic-distribution/list/index.tsx @@ -32,6 +32,8 @@ import { } from "@ant-design/icons"; import style from "./index.module.scss"; import { useNavigate } from "react-router-dom"; +import AccountListModal from "./AccountListModal"; +import DeviceListModal from "./DeviceListModal"; const PAGE_SIZE = 10; @@ -51,6 +53,14 @@ const TrafficDistributionList: React.FC = () => { const [searchQuery, setSearchQuery] = useState(""); // 优化:用menuLoadingId标记当前操作的item const [menuLoadingId, setMenuLoadingId] = useState(null); + // 账号列表弹窗 + const [accountModalVisible, setAccountModalVisible] = useState(false); + const [currentAccounts, setCurrentAccounts] = useState([]); + const [currentRuleName, setCurrentRuleName] = useState(""); + // 设备列表弹窗 + const [deviceModalVisible, setDeviceModalVisible] = useState(false); + const [currentDevices, setCurrentDevices] = useState([]); + const [currentDeviceRuleName, setCurrentDeviceRuleName] = useState(""); const navigate = useNavigate(); useEffect(() => { @@ -128,6 +138,44 @@ const TrafficDistributionList: React.FC = () => { } }; + // 显示账号列表弹窗 + const showAccountList = (item: DistributionRule) => { + // 这里需要根据实际的账号数据结构来转换 + // 假设 item.config.account 是账号ID数组,需要转换为账号对象数组 + const accounts = (item.config?.account || []).map( + (accountId: string | number) => ({ + id: accountId, + nickname: `账号${accountId}`, + wechatId: `wx_${accountId}`, + avatar: "", + status: "normal", + }), + ); + + setCurrentAccounts(accounts); + setCurrentRuleName(item.name); + setAccountModalVisible(true); + }; + + // 显示设备列表弹窗 + const showDeviceList = (item: DistributionRule) => { + // 这里需要根据实际的设备数据结构来转换 + // 假设 item.config.devices 是设备ID数组,需要转换为设备对象数组 + const devices = (item.config?.devices || []).map((deviceId: string) => ({ + id: deviceId, + memo: `设备${deviceId}`, + imei: `IMEI${deviceId}`, + wechatId: `wx_${deviceId}`, + status: "online" as const, + avatar: "", + totalFriend: Math.floor(Math.random() * 1000) + 100, + })); + + setCurrentDevices(devices); + setCurrentDeviceRuleName(item.name); + setDeviceModalVisible(true); + }; + const renderCard = (item: DistributionRule) => { const menu = ( handleMenuClick(key, item)}> @@ -213,13 +261,21 @@ const TrafficDistributionList: React.FC = () => {
-
+
showAccountList(item)} + >
{item.config?.account?.length || 0}
分发账号
-
+
showDeviceList(item)} + >
{item.config?.devices?.length || 0}
@@ -325,6 +381,22 @@ const TrafficDistributionList: React.FC = () => {
暂无数据
)}
+ + {/* 账号列表弹窗 */} + setAccountModalVisible(false)} + accounts={currentAccounts} + title={`${currentRuleName} - 分发账号列表`} + /> + + {/* 设备列表弹窗 */} + setDeviceModalVisible(false)} + devices={currentDevices} + title={`${currentDeviceRuleName} - 分发设备列表`} + /> ); };