From 9a33801f124b005d99cf83a1e68581471b5b5b5b 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, 24 Jul 2025 16:22:39 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=9C=AC=E6=AC=A1=E6=8F=90=E4=BA=A4?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=86=85=E5=AE=B9=E5=A6=82=E4=B8=8B=20?= =?UTF-8?q?=E5=88=97=E8=A1=A8=E6=A0=B7=E5=BC=8F=E5=AD=98=E4=B8=80=E4=B8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../workspace/traffic-distribution/Detail.tsx | 8 - .../traffic-distribution/NewDistribution.tsx | 8 - .../TrafficDistribution.tsx | 10 - .../traffic-distribution/detail/api.ts | 0 .../traffic-distribution/detail/data.ts | 0 .../detail/index.module.scss | 0 .../traffic-distribution/detail/index.tsx | 3 + .../traffic-distribution/form/api.ts | 0 .../traffic-distribution/form/data.ts | 0 .../form/index.module.scss | 0 .../traffic-distribution/form/index.tsx | 3 + .../traffic-distribution/list/api.ts | 10 + .../traffic-distribution/list/data.ts | 133 +++++++++++ .../list/index.module.scss | 120 ++++++++++ .../traffic-distribution/list/index.tsx | 215 ++++++++++++++++++ nkebao/src/router/module/workspace.tsx | 6 +- 16 files changed, 487 insertions(+), 29 deletions(-) delete mode 100644 nkebao/src/pages/workspace/traffic-distribution/Detail.tsx delete mode 100644 nkebao/src/pages/workspace/traffic-distribution/NewDistribution.tsx delete mode 100644 nkebao/src/pages/workspace/traffic-distribution/TrafficDistribution.tsx create mode 100644 nkebao/src/pages/workspace/traffic-distribution/detail/api.ts create mode 100644 nkebao/src/pages/workspace/traffic-distribution/detail/data.ts create mode 100644 nkebao/src/pages/workspace/traffic-distribution/detail/index.module.scss create mode 100644 nkebao/src/pages/workspace/traffic-distribution/detail/index.tsx create mode 100644 nkebao/src/pages/workspace/traffic-distribution/form/api.ts create mode 100644 nkebao/src/pages/workspace/traffic-distribution/form/data.ts create mode 100644 nkebao/src/pages/workspace/traffic-distribution/form/index.module.scss create mode 100644 nkebao/src/pages/workspace/traffic-distribution/form/index.tsx create mode 100644 nkebao/src/pages/workspace/traffic-distribution/list/api.ts create mode 100644 nkebao/src/pages/workspace/traffic-distribution/list/data.ts create mode 100644 nkebao/src/pages/workspace/traffic-distribution/list/index.module.scss create mode 100644 nkebao/src/pages/workspace/traffic-distribution/list/index.tsx diff --git a/nkebao/src/pages/workspace/traffic-distribution/Detail.tsx b/nkebao/src/pages/workspace/traffic-distribution/Detail.tsx deleted file mode 100644 index f3469de15..000000000 --- a/nkebao/src/pages/workspace/traffic-distribution/Detail.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import React from "react"; -import PlaceholderPage from "@/components/PlaceholderPage"; - -const TrafficDistributionDetail: React.FC = () => { - return ; -}; - -export default TrafficDistributionDetail; diff --git a/nkebao/src/pages/workspace/traffic-distribution/NewDistribution.tsx b/nkebao/src/pages/workspace/traffic-distribution/NewDistribution.tsx deleted file mode 100644 index 06053b35f..000000000 --- a/nkebao/src/pages/workspace/traffic-distribution/NewDistribution.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import React from "react"; -import PlaceholderPage from "@/components/PlaceholderPage"; - -const NewDistribution: React.FC = () => { - return ; -}; - -export default NewDistribution; diff --git a/nkebao/src/pages/workspace/traffic-distribution/TrafficDistribution.tsx b/nkebao/src/pages/workspace/traffic-distribution/TrafficDistribution.tsx deleted file mode 100644 index 701f347b9..000000000 --- a/nkebao/src/pages/workspace/traffic-distribution/TrafficDistribution.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import React from "react"; -import PlaceholderPage from "@/components/PlaceholderPage"; - -const TrafficDistribution: React.FC = () => { - return ( - - ); -}; - -export default TrafficDistribution; diff --git a/nkebao/src/pages/workspace/traffic-distribution/detail/api.ts b/nkebao/src/pages/workspace/traffic-distribution/detail/api.ts new file mode 100644 index 000000000..e69de29bb diff --git a/nkebao/src/pages/workspace/traffic-distribution/detail/data.ts b/nkebao/src/pages/workspace/traffic-distribution/detail/data.ts new file mode 100644 index 000000000..e69de29bb diff --git a/nkebao/src/pages/workspace/traffic-distribution/detail/index.module.scss b/nkebao/src/pages/workspace/traffic-distribution/detail/index.module.scss new file mode 100644 index 000000000..e69de29bb diff --git a/nkebao/src/pages/workspace/traffic-distribution/detail/index.tsx b/nkebao/src/pages/workspace/traffic-distribution/detail/index.tsx new file mode 100644 index 000000000..010fef246 --- /dev/null +++ b/nkebao/src/pages/workspace/traffic-distribution/detail/index.tsx @@ -0,0 +1,3 @@ +export default function TrafficDistributionDetail() { + return
TrafficDistributionDetail
; +} diff --git a/nkebao/src/pages/workspace/traffic-distribution/form/api.ts b/nkebao/src/pages/workspace/traffic-distribution/form/api.ts new file mode 100644 index 000000000..e69de29bb diff --git a/nkebao/src/pages/workspace/traffic-distribution/form/data.ts b/nkebao/src/pages/workspace/traffic-distribution/form/data.ts new file mode 100644 index 000000000..e69de29bb diff --git a/nkebao/src/pages/workspace/traffic-distribution/form/index.module.scss b/nkebao/src/pages/workspace/traffic-distribution/form/index.module.scss new file mode 100644 index 000000000..e69de29bb diff --git a/nkebao/src/pages/workspace/traffic-distribution/form/index.tsx b/nkebao/src/pages/workspace/traffic-distribution/form/index.tsx new file mode 100644 index 000000000..52e3530a0 --- /dev/null +++ b/nkebao/src/pages/workspace/traffic-distribution/form/index.tsx @@ -0,0 +1,3 @@ +export default function TrafficDistributionForm() { + return
TrafficDistributionForm
; +} diff --git a/nkebao/src/pages/workspace/traffic-distribution/list/api.ts b/nkebao/src/pages/workspace/traffic-distribution/list/api.ts new file mode 100644 index 000000000..a607f2072 --- /dev/null +++ b/nkebao/src/pages/workspace/traffic-distribution/list/api.ts @@ -0,0 +1,10 @@ +import request from "@/api/request"; + +// 获取流量分发规则列表 +export function fetchDistributionRuleList(params: { + page?: number; + limit?: number; + keyword?: string; +}): Promise { + return request("/v1/workbench/list?type=5", params, "GET"); +} diff --git a/nkebao/src/pages/workspace/traffic-distribution/list/data.ts b/nkebao/src/pages/workspace/traffic-distribution/list/data.ts new file mode 100644 index 000000000..6f623dafa --- /dev/null +++ b/nkebao/src/pages/workspace/traffic-distribution/list/data.ts @@ -0,0 +1,133 @@ +// 流量分发相关类型定义 + +export interface Device { + id: string; + name: string; + status: "online" | "offline" | "busy"; + battery: number; + location: string; + wechatAccounts: number; + dailyAddLimit: number; + todayAdded: number; +} + +export interface WechatAccount { + id: string; + nickname: string; + wechatId: string; + avatar: string; + deviceId: string; + status: "normal" | "limited" | "blocked"; + friendCount: number; + dailyAddLimit: number; +} + +export interface CustomerService { + id: string; + name: string; + avatar: string; + status: "online" | "offline" | "busy"; + assignedUsers: number; +} + +export interface TrafficPool { + id: string; + name: string; + description?: string; + userCount?: number; + tags?: string[]; + createdAt?: string; + deviceIds?: string[]; +} + +export interface RFMScore { + recency: number; + frequency: number; + monetary: number; + total: number; + segment: string; + priority: "high" | "medium" | "low"; +} + +export interface UserTag { + id: string; + name: string; + color: string; + source: string; +} + +export interface UserInteraction { + id: string; + type: "message" | "purchase" | "view" | "click"; + content: string; + timestamp: string; + value?: number; +} + +export interface TrafficUser { + id: string; + avatar: string; + nickname: string; + wechatId: string; + phone: string; + region: string; + note: string; + status: "pending" | "added" | "failed" | "duplicate"; + addTime: string; + source: string; + scenario: string; + deviceId: string; + wechatAccountId: string; + customerServiceId: string; + poolIds: string[]; + tags: UserTag[]; + rfmScore: RFMScore; + lastInteraction: string; + totalSpent: number; + interactionCount: number; + conversionRate: number; + isDuplicate: boolean; + mergedAccounts: string[]; + addStatus: "not_added" | "adding" | "added" | "failed"; + interactions: UserInteraction[]; +} + +// 流量分发规则类型 +export interface DistributionRule { + id: number; + companyId: number; + name: string; + type: number; + status: number; + autoStart: number; + userId: number; + createTime: string; + updateTime: string; + config: { + id: number; + workbenchId: number; + distributeType: number; + maxPerDay: number; + timeType: number; + startTime: string; + endTime: string; + account: (string | number)[]; + devices: string[]; + pools: string[]; + exp: number; + createTime: string; + updateTime: string; + lastUpdated: string; + total: { + dailyAverage: number; + totalAccounts: number; + deviceCount: number; + poolCount: string | number; + totalUsers: number; + }; + }; + creatorName: string; + auto_like: any; + moments_sync: any; + group_push: any; +} diff --git a/nkebao/src/pages/workspace/traffic-distribution/list/index.module.scss b/nkebao/src/pages/workspace/traffic-distribution/list/index.module.scss new file mode 100644 index 000000000..df66cbda3 --- /dev/null +++ b/nkebao/src/pages/workspace/traffic-distribution/list/index.module.scss @@ -0,0 +1,120 @@ +.listToolbar { + display: flex; + align-items: center; + padding: 12px 0; + border-bottom: 1px solid #f0f0f0; + background: #fff; + font-size: 16px; + color: #222; +} + +.ruleList { + padding:0 16px; +} + +.ruleCard { + background: #fff; + border-radius: 12px; + box-shadow: 0 2px 8px rgba(0,0,0,0.04); + margin-bottom: 20px; + padding: 16px; + border: 1px solid #ececec; + transition: box-shadow 0.2s, border-color 0.2s; + position: relative; +} +.ruleCard:hover { + box-shadow: 0 4px 16px rgba(0,0,0,0.08); + border-color: #b3e5fc; +} + +.ruleHeader { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 8px; +} +.ruleName { + font-size: 17px; + font-weight: 600; + color: #222; +} + +.ruleStatus { + display: flex; + align-items: center; + gap: 8px; +} + +.ruleSwitch { + margin-left: 4px; +} + +.ruleMenu { + margin-left: 8px; + cursor: pointer; + color: #888; + font-size: 18px; +} + +.ruleMeta { + display: flex; + justify-content: space-between; + margin-bottom: 10px; + font-size: 15px; + color: #444; + font-weight: 500; +} +.ruleMetaItem { + flex: 1; + text-align: center; +} +.ruleMetaItem:not(:last-child) { + border-right: 1px solid #f0f0f0; +} + +.ruleDivider { + border-top: 1px solid #f0f0f0; + margin: 12px 0 10px 0; +} + +.ruleStats { + display: flex; + justify-content: space-between; + font-size: 16px; + color: #222; + font-weight: 600; + margin-bottom: 8px; +} +.ruleStatsItem { + flex: 1; + text-align: center; +} +.ruleStatsItem:not(:last-child) { + border-right: 1px solid #f0f0f0; +} + +.ruleFooter { + display: flex; + justify-content: space-between; + font-size: 13px; + color: #888; + margin-top: 6px; +} + +.ruleFooterIcon { + margin-right: 4px; + vertical-align: middle; +} + +.empty { + text-align: center; + color: #bbb; + padding: 40px 0; +} + +.pagination { + display: flex; + justify-content: center; + padding: 16px 0; + background: #fff; +} diff --git a/nkebao/src/pages/workspace/traffic-distribution/list/index.tsx b/nkebao/src/pages/workspace/traffic-distribution/list/index.tsx new file mode 100644 index 000000000..9edbf881c --- /dev/null +++ b/nkebao/src/pages/workspace/traffic-distribution/list/index.tsx @@ -0,0 +1,215 @@ +import React, { useEffect, useState } from "react"; +import Layout from "@/components/Layout/Layout"; +import { Input, Button } from "antd"; +import NavCommon from "@/components/NavCommon"; +import { fetchDistributionRuleList } from "./api"; +import type { DistributionRule } from "./data"; +import { Tag, Pagination, Spin, message, Switch } from "antd"; +import { + EllipsisOutlined, + UserOutlined, + ClusterOutlined, + AppstoreOutlined, + BarChartOutlined, + TeamOutlined, + ClockCircleOutlined, + SearchOutlined, + ReloadOutlined, + CalendarOutlined, +} from "@ant-design/icons"; +import style from "./index.module.scss"; + +const PAGE_SIZE = 10; + +const statusMap = { + 0: { text: "待处理", color: "default" }, + 1: { text: "进行中", color: "processing" }, + 2: { text: "已暂停", color: "warning" }, + 3: { text: "已完成", color: "success" }, + 4: { text: "失败", color: "error" }, +}; + +const TrafficDistributionList: React.FC = () => { + const [list, setList] = useState([]); + const [loading, setLoading] = useState(false); + const [total, setTotal] = useState(0); + const [page, setPage] = useState(1); + const [searchQuery, setSearchQuery] = useState(""); + + useEffect(() => { + fetchList(page, searchQuery); + // eslint-disable-next-line + }, []); + + const fetchList = async (pageNum = 1, keyword = "") => { + setLoading(true); + try { + const res = await fetchDistributionRuleList({ + page: pageNum, + limit: PAGE_SIZE, + keyword, + }); + setList(res?.list || []); + setTotal(Number(res?.total) || 0); + } catch (e) { + message.error("获取流量分发列表失败"); + } finally { + setLoading(false); + } + }; + + const handleSearch = (val: string) => { + setSearchQuery(val); + setPage(1); + fetchList(1, val); + }; + + const handlePageChange = (p: number) => { + setPage(p); + fetchList(p, searchQuery); + }; + + const handleRefresh = () => { + fetchList(page, searchQuery); + }; + + const renderCard = (item: DistributionRule) => ( +
+
+ {item.name} +
+ + {statusMap[item.status]?.text || "未知"} + + + +
+
+
+
+ +
+ {item.config?.account?.length || 0} +
+
分发账号
+
+
+ +
+ {item.config?.devices?.length || 0} +
+
分发设备
+
+
+ +
+ {item.config?.pools?.length || 0} +
+
流量池
+
+
+
+
+
+ + + {item.config?.total?.dailyAverage || 0} + +
+ 日均分发量 +
+
+
+ + + {item.config?.total?.totalUsers || 0} + +
+ 总流量池数量 +
+
+
+
+ + + 上次执行:{item.config?.lastUpdated || "-"} + + + + 创建时间:{item.createTime || "-"} + +
+
+ ); + + return ( + + + <> + + {/* 搜索栏 */} +
+
+ setSearchQuery(e.target.value)} + prefix={} + allowClear + size="large" + /> +
+ +
+ + } + loading={loading} + footer={ +
+ +
+ } + > +
+ {loading ? ( + + ) : list.length > 0 ? ( + list.map(renderCard) + ) : ( +
暂无数据
+ )} +
+
+ ); +}; + +export default TrafficDistributionList; diff --git a/nkebao/src/router/module/workspace.tsx b/nkebao/src/router/module/workspace.tsx index ee4ca0c51..7413452ea 100644 --- a/nkebao/src/router/module/workspace.tsx +++ b/nkebao/src/router/module/workspace.tsx @@ -12,9 +12,9 @@ import MomentsSync from "@/pages/workspace/moments-sync/MomentsSync"; import MomentsSyncDetail from "@/pages/workspace/moments-sync/Detail"; import NewMomentsSync from "@/pages/workspace/moments-sync/new/index"; import AIAssistant from "@/pages/workspace/ai-assistant/AIAssistant"; -import TrafficDistribution from "@/pages/workspace/traffic-distribution/TrafficDistribution"; -import TrafficDistributionDetail from "@/pages/workspace/traffic-distribution/Detail"; -import NewDistribution from "@/pages/workspace/traffic-distribution/NewDistribution"; +import TrafficDistribution from "@/pages/workspace/traffic-distribution/list/index"; +import TrafficDistributionDetail from "@/pages/workspace/traffic-distribution/detail/index"; +import NewDistribution from "@/pages/workspace/traffic-distribution/form/index"; import PlaceholderPage from "@/components/PlaceholderPage"; const workspaceRoutes = [