From 9facfe2ff8fe564643df5e6b33f6bc4c928239ce 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: Mon, 28 Jul 2025 14:43:49 +0800 Subject: [PATCH 01/12] =?UTF-8?q?feat:=20=E6=9C=AC=E6=AC=A1=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=E6=9B=B4=E6=96=B0=E5=86=85=E5=AE=B9=E5=A6=82=E4=B8=8B?= =?UTF-8?q?=20=E6=8E=A5=E5=8F=A3=E5=AF=B9=E6=8E=A5=E6=88=90=E5=8A=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mobile/mine/traffic-pool/detail/api.ts | 4 +- .../traffic-pool/detail/index.module.scss | 309 +++++++++++ .../mobile/mine/traffic-pool/detail/index.tsx | 488 +++++++++--------- .../mobile/mine/traffic-pool/list/index.tsx | 4 +- .../src/pages/mobile/workspace/main/index.tsx | 15 +- 5 files changed, 553 insertions(+), 267 deletions(-) diff --git a/nkebao/src/pages/mobile/mine/traffic-pool/detail/api.ts b/nkebao/src/pages/mobile/mine/traffic-pool/detail/api.ts index 13e3d5358..dc8b6e92e 100644 --- a/nkebao/src/pages/mobile/mine/traffic-pool/detail/api.ts +++ b/nkebao/src/pages/mobile/mine/traffic-pool/detail/api.ts @@ -1,5 +1,5 @@ import request from "@/api/request"; -export function getTrafficPoolDetail(id: string): Promise { - return request("/v1/workbench/detail", { id }, "GET"); +export function getTrafficPoolDetail(wechatId: string): Promise { + return request("/v1/wechats/getWechatInfo", { wechatId }, "GET"); } diff --git a/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.module.scss b/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.module.scss index e69de29bb..aff534bb2 100644 --- a/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.module.scss +++ b/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.module.scss @@ -0,0 +1,309 @@ +.container { + padding: 12px; + background: #f5f5f5; + min-height: 100vh; +} + +.userCard { + margin-bottom: 12px; + border-radius: 12px; + overflow: hidden; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); +} + +.userInfo { + display: flex; + align-items: center; + gap: 16px; + padding: 16px; +} + +.avatar { + width: 64px; + height: 64px; + border-radius: 50%; + flex-shrink: 0; +} + +.userDetails { + flex: 1; + min-width: 0; +} + +.nickname { + font-size: 18px; + font-weight: 600; + color: #333; + margin-bottom: 4px; + line-height: 1.2; +} + +.wechatId { + font-size: 14px; + color: #1677ff; + margin-bottom: 8px; + line-height: 1.2; +} + +.tags { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.packageTag { + font-size: 12px; + padding: 2px 8px; + border-radius: 12px; +} + +.tabs { + background: transparent; + + :global(.adm-tabs-header) { + background: white; + border-radius: 12px 12px 0 0; + margin-bottom: 0; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); + } + + :global(.adm-tabs-tab) { + font-size: 14px; + font-weight: 500; + } + + :global(.adm-tabs-tab-active) { + color: #1677ff; + } + + :global(.adm-tabs-tab-line) { + background: #1677ff; + } + + :global(.adm-tabs-content) { + background: white; + border-radius: 0 0 12px 12px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); + } +} + +.tabContent { + padding: 16px; +} + +.infoCard { + margin-bottom: 12px; + border-radius: 8px; + overflow: hidden; + + &:last-child { + margin-bottom: 0; + } + + :global(.adm-card-header) { + padding: 12px 16px; + border-bottom: 1px solid #f0f0f0; + font-size: 14px; + font-weight: 600; + color: #333; + } + + :global(.adm-card-body) { + padding: 0; + } +} + +.rfmGrid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 16px; + padding: 16px; +} + +.rfmItem { + text-align: center; +} + +.rfmValue { + font-size: 20px; + font-weight: 700; + line-height: 1.2; + margin-bottom: 4px; +} + +.rfmLabel { + font-size: 12px; + color: #666; + line-height: 1.2; +} + +.statsGrid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 16px; + padding: 16px; +} + +.statItem { + text-align: center; +} + +.statValue { + font-size: 18px; + font-weight: 700; + line-height: 1.2; + margin-bottom: 4px; +} + +.statLabel { + font-size: 12px; + color: #666; + line-height: 1.2; +} + +.interactionContent { + display: flex; + align-items: center; + gap: 8px; + font-size: 13px; + color: #666; + line-height: 1.4; +} + +.purchaseValue { + color: #22c55e; + font-weight: 600; +} + +.tagsContainer { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-bottom: 16px; + padding: 16px; +} + +.userTag { + font-size: 12px; + padding: 4px 12px; + border-radius: 16px; +} + +.addTagButton { + margin: 0 16px 16px; + height: 40px; + border-radius: 8px; + font-size: 14px; + + :global(.antd-mobile-icon) { + margin-right: 4px; + } +} + +.emptyState { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 48px 16px; + text-align: center; +} + +.emptyText { + color: #999; + font-size: 14px; + line-height: 1.4; +} + +// 响应式设计 +@media (max-width: 375px) { + .container { + padding: 8px; + } + + .userInfo { + padding: 12px; + gap: 12px; + } + + .avatar { + width: 56px; + height: 56px; + } + + .nickname { + font-size: 16px; + } + + .wechatId { + font-size: 13px; + } + + .tabContent { + padding: 12px; + } + + .rfmGrid { + gap: 12px; + padding: 12px; + } + + .rfmValue { + font-size: 18px; + } + + .statsGrid { + gap: 12px; + padding: 12px; + } + + .statValue { + font-size: 16px; + } + + .tagsContainer { + padding: 12px; + } + + .addTagButton { + margin: 0 12px 12px; + height: 36px; + font-size: 13px; + } +} + +// 暗色模式支持 +@media (prefers-color-scheme: dark) { + .container { + background: #1a1a1a; + } + + .userCard, + .tabs :global(.adm-tabs-header), + .tabs :global(.adm-tabs-content) { + background: #2a2a2a; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); + } + + .nickname { + color: #fff; + } + + .wechatId { + color: #4a9eff; + } + + .infoCard :global(.adm-card-header) { + color: #fff; + border-bottom-color: #3a3a3a; + } + + .rfmLabel, + .statLabel { + color: #999; + } + + .emptyText { + color: #666; + } +} diff --git a/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.tsx b/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.tsx index 6a3b983c1..25bd986d5 100644 --- a/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.tsx +++ b/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.tsx @@ -1,24 +1,24 @@ import React, { useEffect, useState } from "react"; import { useParams, useNavigate } from "react-router-dom"; +import { Card, Button, Avatar, Tag, Tabs, List, Badge } from "antd-mobile"; +import { + UserOutlined, + MessageOutlined, + ShoppingOutlined, + EyeOutlined, + PlusOutlined, +} from "@ant-design/icons"; import Layout from "@/components/Layout/Layout"; +import NavCommon from "@/components/NavCommon"; import { getTrafficPoolDetail } from "./api"; import type { TrafficPoolUserDetail } from "./data"; -import { Card, Button, Avatar, Tag, Spin } from "antd"; - -const tabList = [ - { key: "base", label: "基本信息" }, - { key: "journey", label: "用户旅程" }, - { key: "tags", label: "用户标签" }, -]; +import styles from "./index.module.scss"; const TrafficPoolDetail: React.FC = () => { const { id } = useParams(); const navigate = useNavigate(); const [loading, setLoading] = useState(true); const [user, setUser] = useState(null); - const [activeTab, setActiveTab] = useState<"base" | "journey" | "tags">( - "base" - ); useEffect(() => { if (!id) return; @@ -28,270 +28,256 @@ const TrafficPoolDetail: React.FC = () => { .finally(() => setLoading(false)); }, [id]); - if (loading) { - return ( - -
- -
-
- ); - } + const getInteractionIcon = (type: string) => { + switch (type) { + case "click": + return ; + case "message": + return ; + case "purchase": + return ; + case "view": + return ; + default: + return ; + } + }; + + const getInteractionTitle = (type: string) => { + switch (type) { + case "click": + return "点击行为"; + case "message": + return "消息互动"; + case "purchase": + return "购买行为"; + case "view": + return "页面浏览"; + default: + return "未知行为"; + } + }; + + const getStatusText = (status: string | number) => { + if (status === "failed" || status === 0) return "添加失败"; + if (status === "added" || status === 1) return "添加成功"; + return "未添加"; + }; + + const getStatusColor = (status: string | number) => { + if (status === "failed" || status === 0) return "danger"; + if (status === "added" || status === 1) return "success"; + return "default"; + }; + if (!user) { return ( - -
- 未找到该用户 + } loading={loading}> +
+
未找到该用户
); } return ( - - -
用户详情
-
- } - > -
- {/* 顶部信息 */} -
- -
-
{user.nickname}
-
- {user.wechatId} + } loading={loading}> +
+ {/* 用户基本信息 */} + +
+ } + /> +
+
{user.nickname}
+
{user.wechatId}
+
+ {user.packages?.map((pkg) => ( + + {pkg} + + ))} +
- {user.packages && - user.packages.length > 0 && - user.packages.map((pkg) => ( - - {pkg} - - ))}
-
- {/* Tab栏 */} -
- {tabList.map((tab) => ( -
setActiveTab(tab.key as any)} - > - {tab.label} -
- ))} -
+ + {/* Tab内容 */} - {activeTab === "base" && ( - <> - -
-
设备:{user.deviceName || "--"}
-
微信号:{user.wechatAccountName || "--"}
-
客服:{user.customerServiceName || "--"}
-
添加时间:{user.addTime || "--"}
-
最近互动:{user.lastInteraction || "--"}
-
-
- -
-
-
- {user.rfmScore?.recency ?? "-"} + + +
+ {/* 关键信息 */} + + + 设备 + + 微信号 + + + 客服 + + 添加时间 + + 最近互动 + + + + + {/* RFM评分 */} + +
+
+
+ {user.rfmScore?.recency ?? "-"} +
+
最近性(R)
-
最近性(R)
-
-
-
- {user.rfmScore?.frequency ?? "-"} +
+
+ {user.rfmScore?.frequency ?? "-"} +
+
频率(F)
-
频率(F)
-
-
-
- {user.rfmScore?.monetary ?? "-"} +
+
+ {user.rfmScore?.monetary ?? "-"} +
+
金额(M)
-
金额(M)
-
- - -
-
-
- ¥{user.totalSpent ?? "-"} + + + {/* 统计数据 */} + +
+
+
+ ¥{user.totalSpent ?? "-"} +
+
总消费
-
总消费
-
-
-
- {user.interactionCount ?? "-"} +
+
+ {user.interactionCount ?? "-"} +
+
互动次数
-
互动次数
-
-
-
- {user.conversionRate ?? "-"} +
+
+ {user.conversionRate ?? "-"} +
+
转化率
-
转化率
-
-
-
- {user.status === "failed" - ? "添加失败" - : user.status === "added" - ? "添加成功" - : "未添加"} +
+
+ {getStatusText(user.status)} +
+
添加状态
-
添加状态
-
- - - )} - {activeTab === "journey" && ( - - {user.interactions && user.interactions.length > 0 ? ( - user.interactions.slice(0, 4).map((it) => ( -
+
+ + + +
+ + {user.interactions && user.interactions.length > 0 ? ( + + {user.interactions.slice(0, 4).map((interaction) => ( + + {interaction.content} + {interaction.type === "purchase" && + interaction.value && ( + + ¥{interaction.value} + + )} +
+ } + extra={interaction.timestamp} + /> + ))} + + ) : ( +
+
暂无互动记录
+
+ )} +
+
+ + + +
+ +
+ {user.tags && user.tags.length > 0 ? ( + user.tags.map((tag) => ( + + {tag} + + )) + ) : ( +
暂无标签
+ )} +
+
- )) - ) : ( -
- 暂无互动记录 -
- )} - - )} - {activeTab === "tags" && ( - -
- {user.tags && user.tags.length > 0 ? ( - user.tags.map((tag) => ( - - {tag} - - )) - ) : ( - 暂无标签 - )} + + 添加新标签 + +
- -
- )} +
+
); diff --git a/nkebao/src/pages/mobile/mine/traffic-pool/list/index.tsx b/nkebao/src/pages/mobile/mine/traffic-pool/list/index.tsx index afa631e46..a0c963938 100644 --- a/nkebao/src/pages/mobile/mine/traffic-pool/list/index.tsx +++ b/nkebao/src/pages/mobile/mine/traffic-pool/list/index.tsx @@ -191,7 +191,9 @@ const TrafficPoolList: React.FC = () => {
navigate(`/traffic-pool/detail/${item.id}`)} + onClick={() => + navigate(`/traffic-pool/detail/${item.sourceId}`) + } >
{ path: "/workspace/traffic-distribution", bgColor: "#e6f7ff", }, - { - id: "ai-assistant", - name: "AI对话助手", - description: "智能回复,提高互动质量", - icon: ( - - ), - path: "/workspace/ai-assistant", - bgColor: "#e6f7ff", - isNew: true, - }, ]; // AI智能助手 @@ -176,7 +165,7 @@ const Workspace: React.FC = () => {
{/* AI智能助手 */} -
+ {/*

AI 智能助手

{aiFeatures.map((feature) => ( @@ -205,7 +194,7 @@ const Workspace: React.FC = () => { ))}
-
+
*/}
); From 3a444a29b93d7d6f328ba0b783dc2888a7f36754 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: Tue, 29 Jul 2025 10:07:09 +0800 Subject: [PATCH 02/12] =?UTF-8?q?=E5=AD=98=E8=BF=9B=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mobile/mine/traffic-pool/detail/data.ts | 68 ++-- .../traffic-pool/detail/index.module.scss | 124 +++--- .../mobile/mine/traffic-pool/detail/index.tsx | 357 +++++++++++------- 3 files changed, 319 insertions(+), 230 deletions(-) diff --git a/nkebao/src/pages/mobile/mine/traffic-pool/detail/data.ts b/nkebao/src/pages/mobile/mine/traffic-pool/detail/data.ts index f1c30d00e..3e9cfa579 100644 --- a/nkebao/src/pages/mobile/mine/traffic-pool/detail/data.ts +++ b/nkebao/src/pages/mobile/mine/traffic-pool/detail/data.ts @@ -1,32 +1,46 @@ // 用户详情类型 export interface TrafficPoolUserDetail { - id: number; - nickname: string; - avatar: string; - wechatId: string; - status: number | string; - addTime: string; - lastInteraction: string; - deviceName?: string; - wechatAccountName?: string; - customerServiceName?: string; - poolNames?: string[]; - rfmScore?: { - recency: number; - frequency: number; - monetary: number; - segment?: string; + userInfo: { + wechatId: string; + weight: number | null; + activity: { + totalMsgCount: number; + sevenDayMsgCount: number; + thirtyDayMsgCount: number; + yesterdayMsgCount: number; + }; + friendShip: { + maleFriend: number; + groupNumber: number; + totalFriend: number; + femaleFriend: number; + unknowFriend: number; + }; + nickname: string; + alias: string; + avatar: string; + gender: number; // 0-未知, 1-男, 2-女 }; - totalSpent?: number; - interactionCount?: number; - conversionRate?: number; - tags?: string[]; - packages?: string[]; - interactions?: Array<{ - id: string; - type: string; - content: string; - timestamp: string; - value?: number; + accountAge: string; + activityLevel: { + allTimes: number; + dayTimes: number; + }; + accountWeight: { + ageWeight: number; + activityWeigth: number; + restrictWeight: number; + realNameWeight: number; + scope: number; + }; + statistics: { + todayAdded: number; + addLimit: number; + }; + restrictions: Array<{ + id: number; + date: number | null; + level: number; // 1-轻微, 2-中等, 3-严重 + reason: string; }>; } diff --git a/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.module.scss b/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.module.scss index aff534bb2..a3833543c 100644 --- a/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.module.scss +++ b/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.module.scss @@ -41,6 +41,13 @@ .wechatId { font-size: 14px; color: #1677ff; + margin-bottom: 4px; + line-height: 1.2; +} + +.alias { + font-size: 12px; + color: #666; margin-bottom: 8px; line-height: 1.2; } @@ -51,7 +58,13 @@ gap: 6px; } -.packageTag { +.genderTag { + font-size: 12px; + padding: 2px 8px; + border-radius: 12px; +} + +.weightTag { font-size: 12px; padding: 2px 8px; border-radius: 12px; @@ -113,30 +126,6 @@ } } -.rfmGrid { - display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 16px; - padding: 16px; -} - -.rfmItem { - text-align: center; -} - -.rfmValue { - font-size: 20px; - font-weight: 700; - line-height: 1.2; - margin-bottom: 4px; -} - -.rfmLabel { - font-size: 12px; - color: #666; - line-height: 1.2; -} - .statsGrid { display: grid; grid-template-columns: repeat(2, 1fr); @@ -161,43 +150,32 @@ line-height: 1.2; } -.interactionContent { +.restrictionTitle { display: flex; align-items: center; + justify-content: space-between; gap: 8px; - font-size: 13px; - color: #666; + font-size: 14px; + font-weight: 500; + color: #333; line-height: 1.4; } -.purchaseValue { - color: #22c55e; - font-weight: 600; -} - -.tagsContainer { - display: flex; - flex-wrap: wrap; - gap: 8px; - margin-bottom: 16px; - padding: 16px; -} - -.userTag { - font-size: 12px; - padding: 4px 12px; - border-radius: 16px; -} - -.addTagButton { - margin: 0 16px 16px; - height: 40px; +.restrictionLevel { + font-size: 10px; + padding: 2px 6px; border-radius: 8px; - font-size: 14px; - - :global(.antd-mobile-icon) { - margin-right: 4px; - } + flex-shrink: 0; +} + +.restrictionContent { + display: flex; + flex-direction: column; + gap: 4px; + font-size: 12px; + color: #666; + line-height: 1.4; + margin-top: 4px; } .emptyState { @@ -239,19 +217,14 @@ font-size: 13px; } + .alias { + font-size: 11px; + } + .tabContent { padding: 12px; } - .rfmGrid { - gap: 12px; - padding: 12px; - } - - .rfmValue { - font-size: 18px; - } - .statsGrid { gap: 12px; padding: 12px; @@ -261,14 +234,12 @@ font-size: 16px; } - .tagsContainer { - padding: 12px; + .restrictionTitle { + font-size: 13px; } - .addTagButton { - margin: 0 12px 12px; - height: 36px; - font-size: 13px; + .restrictionContent { + font-size: 11px; } } @@ -293,16 +264,27 @@ color: #4a9eff; } + .alias { + color: #999; + } + .infoCard :global(.adm-card-header) { color: #fff; border-bottom-color: #3a3a3a; } - .rfmLabel, .statLabel { color: #999; } + .restrictionTitle { + color: #fff; + } + + .restrictionContent { + color: #999; + } + .emptyText { color: #666; } diff --git a/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.tsx b/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.tsx index 25bd986d5..48cbe7a15 100644 --- a/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.tsx +++ b/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.tsx @@ -4,8 +4,9 @@ import { Card, Button, Avatar, Tag, Tabs, List, Badge } from "antd-mobile"; import { UserOutlined, MessageOutlined, - ShoppingOutlined, - EyeOutlined, + TeamOutlined, + ClockCircleOutlined, + ExclamationCircleOutlined, PlusOutlined, } from "@ant-design/icons"; import Layout from "@/components/Layout/Layout"; @@ -28,46 +29,72 @@ const TrafficPoolDetail: React.FC = () => { .finally(() => setLoading(false)); }, [id]); - const getInteractionIcon = (type: string) => { - switch (type) { - case "click": - return ; - case "message": - return ; - case "purchase": - return ; - case "view": - return ; + const getGenderText = (gender: number) => { + switch (gender) { + case 1: + return "男"; + case 2: + return "女"; default: - return ; + return "未知"; } }; - const getInteractionTitle = (type: string) => { - switch (type) { - case "click": - return "点击行为"; - case "message": - return "消息互动"; - case "purchase": - return "购买行为"; - case "view": - return "页面浏览"; + const getGenderColor = (gender: number) => { + switch (gender) { + case 1: + return "#1677ff"; + case 2: + return "#eb2f96"; default: - return "未知行为"; + return "#999"; } }; - const getStatusText = (status: string | number) => { - if (status === "failed" || status === 0) return "添加失败"; - if (status === "added" || status === 1) return "添加成功"; - return "未添加"; + const getRestrictionLevelText = (level: number) => { + switch (level) { + case 1: + return "轻微"; + case 2: + return "中等"; + case 3: + return "严重"; + default: + return "未知"; + } }; - const getStatusColor = (status: string | number) => { - if (status === "failed" || status === 0) return "danger"; - if (status === "added" || status === 1) return "success"; - return "default"; + const getRestrictionLevelColor = (level: number) => { + switch (level) { + case 1: + return "warning"; + case 2: + return "danger"; + case 3: + return "danger"; + default: + return "default"; + } + }; + + const formatDate = (timestamp: number | null) => { + if (!timestamp) return "--"; + try { + const date = new Date(timestamp * 1000); + return date.toLocaleDateString("zh-CN"); + } catch (error) { + return "--"; + } + }; + + const formatAccountAge = (dateString: string) => { + if (!dateString) return "--"; + try { + const date = new Date(dateString); + return date.toLocaleDateString("zh-CN"); + } catch (error) { + return dateString; + } }; if (!user) { @@ -87,24 +114,32 @@ const TrafficPoolDetail: React.FC = () => {
} />
-
{user.nickname}
-
{user.wechatId}
+
{user.userInfo.nickname}
+
{user.userInfo.wechatId}
+
别名:{user.userInfo.alias}
- {user.packages?.map((pkg) => ( + + {getGenderText(user.userInfo.gender)} + + {user.userInfo.weight && ( - {pkg} + 权重: {user.userInfo.weight} - ))} + )}
@@ -114,166 +149,224 @@ const TrafficPoolDetail: React.FC = () => {
- {/* 关键信息 */} - + {/* 账户信息 */} + - 设备 - - 微信号 + + 注册时间 - - 客服 + + 今日添加 - 添加时间 - - 最近互动 + + 总消息数 + + + 今日消息 - {/* RFM评分 */} - -
-
+ {/* 好友统计 */} + +
+
- {user.rfmScore?.recency ?? "-"} + {user.userInfo.friendShip.totalFriend}
-
最近性(R)
+
总好友
-
+
- {user.rfmScore?.frequency ?? "-"} + {user.userInfo.friendShip.maleFriend}
-
频率(F)
+
男性好友
-
+
- {user.rfmScore?.monetary ?? "-"} + {user.userInfo.friendShip.femaleFriend}
-
金额(M)
+
女性好友
+
+
+
+ {user.userInfo.friendShip.unknowFriend} +
+
未知性别
+
+
+
+ {user.userInfo.friendShip.groupNumber} +
+
群聊数量
- {/* 统计数据 */} - + {/* 活跃度统计 */} +
- ¥{user.totalSpent ?? "-"} + {user.userInfo.activity.totalMsgCount}
-
总消费
-
-
-
- {user.interactionCount ?? "-"} -
-
互动次数
+
总消息数
- {user.conversionRate ?? "-"} + {user.userInfo.activity.sevenDayMsgCount}
-
转化率
+
7天消息
- {getStatusText(user.status)} + {user.userInfo.activity.thirtyDayMsgCount}
-
添加状态
+
30天消息
+
+
+
+ {user.userInfo.activity.yesterdayMsgCount} +
+
昨日消息
+
+
+
+ + {/* 账户权重 */} + +
+
+
+ {user.accountWeight.ageWeight} +
+
年龄权重
+
+
+
+ {user.accountWeight.activityWeigth} +
+
活跃权重
+
+
+
+ {user.accountWeight.restrictWeight} +
+
限制权重
+
+
+
+ {user.accountWeight.realNameWeight} +
+
实名权重
+
+
+
+ {user.accountWeight.scope} +
+
综合评分
- +
- - {user.interactions && user.interactions.length > 0 ? ( + + {user.restrictions && user.restrictions.length > 0 ? ( - {user.interactions.slice(0, 4).map((interaction) => ( + {user.restrictions.map((restriction) => ( - {interaction.content} - {interaction.type === "purchase" && - interaction.value && ( - - ¥{interaction.value} - + key={restriction.id} + prefix={ + + } + title={ +
+ {restriction.reason || "未知原因"} + + {getRestrictionLevelText(restriction.level)} + +
+ } + description={ +
+ 限制ID: {restriction.id} + {restriction.date && ( + + 限制时间: {formatDate(restriction.date)} + + )}
} - extra={interaction.timestamp} /> ))}
) : (
-
暂无互动记录
+
暂无限制记录
)}
- +
- -
- {user.tags && user.tags.length > 0 ? ( - user.tags.map((tag) => ( - - {tag} - - )) - ) : ( -
暂无标签
- )} + +
+
暂无操作记录
-
From da11868d484fe042c3a642ae1b5bf1e0d9d3cfbf 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: Tue, 29 Jul 2025 11:40:47 +0800 Subject: [PATCH 03/12] =?UTF-8?q?=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nkebao/tsconfig.json | 48 ++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/nkebao/tsconfig.json b/nkebao/tsconfig.json index d5830e183..9d100420a 100644 --- a/nkebao/tsconfig.json +++ b/nkebao/tsconfig.json @@ -1,24 +1,24 @@ -{ - "compilerOptions": { - "target": "ESNext", - "lib": ["DOM", "DOM.Iterable", "ESNext"], - "allowJs": true, - "skipLibCheck": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "strict": false, - "noImplicitAny": false, - "forceConsistentCasingInFileNames": true, - "noFallthroughCasesInSwitch": true, - "module": "ESNext", - "moduleResolution": "Node", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "react-jsx", - "baseUrl": "./", - "paths": { - "@/*": ["src/*"] - } - }, - "include": ["src"] -} +{ + "compilerOptions": { + "target": "ESNext", + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": false, + "noImplicitAny": false, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "module": "ESNext", + "moduleResolution": "Node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "baseUrl": "./", + "paths": { + "@/*": ["src/*"] + } + }, + "include": ["src"] +} From dce691037166f309deca321e6f3241e372567069 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: Tue, 29 Jul 2025 19:08:50 +0800 Subject: [PATCH 04/12] =?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=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E8=AF=A6=E6=83=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nkebao/.env.development | 5 +- .../mobile/mine/traffic-pool/detail/api.ts | 24 +- .../mobile/mine/traffic-pool/detail/data.ts | 66 +- .../traffic-pool/detail/index.module.scss | 711 +++++++++++------- .../mobile/mine/traffic-pool/detail/index.tsx | 662 +++++++++++----- .../mobile/mine/traffic-pool/list/index.tsx | 2 +- nkebao/src/router/module/mine.tsx | 2 +- nkebao/tsconfig.json | 48 +- 8 files changed, 1018 insertions(+), 502 deletions(-) diff --git a/nkebao/.env.development b/nkebao/.env.development index 3b39062c9..616d7ebb3 100644 --- a/nkebao/.env.development +++ b/nkebao/.env.development @@ -1,6 +1,5 @@ # 基础环境变量示例 -VITE_API_BASE_URL=http://www.yishi.com -# VITE_API_BASE_URL=https://ckbapi.quwanzhi.com +# VITE_API_BASE_URL=http://www.yishi.com +VITE_API_BASE_URL=https://ckbapi.quwanzhi.com VITE_APP_TITLE=Nkebao Base - diff --git a/nkebao/src/pages/mobile/mine/traffic-pool/detail/api.ts b/nkebao/src/pages/mobile/mine/traffic-pool/detail/api.ts index 845742355..0bbaec1f0 100644 --- a/nkebao/src/pages/mobile/mine/traffic-pool/detail/api.ts +++ b/nkebao/src/pages/mobile/mine/traffic-pool/detail/api.ts @@ -1,5 +1,27 @@ import request from "@/api/request"; +import type { TrafficPoolUserDetail, UserJourneyResponse } from "./data"; -export function getTrafficPoolDetail(wechatId: string): Promise { +export function getTrafficPoolDetail( + wechatId: string +): Promise { return request("/v1/wechats/getWechatInfo", { wechatId }, "GET"); } + +// 获取用户旅程记录 +export function getUserJourney(params: { + page: number; + pageSize: number; + userId: string; +}): Promise { + return request("/v1/traffic/pool/getUserJourney", params, "GET"); +} + +// 获取用户标签 +export function getUserTags(userId: string): Promise { + return request("/v1/user/tags", { userId }, "GET"); +} + +// 添加用户标签 +export function addUserTag(userId: string, tagData: any): Promise { + return request("/v1/user/tags", { userId, ...tagData }, "POST"); +} diff --git a/nkebao/src/pages/mobile/mine/traffic-pool/detail/data.ts b/nkebao/src/pages/mobile/mine/traffic-pool/detail/data.ts index fb149007a..1e274f63e 100644 --- a/nkebao/src/pages/mobile/mine/traffic-pool/detail/data.ts +++ b/nkebao/src/pages/mobile/mine/traffic-pool/detail/data.ts @@ -1,4 +1,4 @@ -// 用户详情类型 +// 用户详情类型 - 基于实际API返回数据 export interface TrafficPoolUserDetail { userInfo: { wechatId: string; @@ -44,3 +44,67 @@ export interface TrafficPoolUserDetail { reason: string; }>; } + +// 用户旅程记录类型 +export interface UserJourneyRecord { + id: number; + type: number; // 0-浏览, 2-提交订单, 3-注册 + trafficPoolId: number; + remark: string; + count: number; + createTime: string; + updateTime: string; +} + +// 用户旅程响应类型 +export interface UserJourneyResponse { + list: UserJourneyRecord[]; + total: number; +} + +// 扩展的用户详情类型 - 用于前端展示 +export interface ExtendedUserDetail extends TrafficPoolUserDetail { + // 前端计算或模拟的数据 + rfmScore?: { + recency: number; + frequency: number; + monetary: number; + totalScore: number; + }; + trafficPools?: { + currentPool: string; + availablePools: string[]; + }; + userJourney?: InteractionRecord[]; + userTags?: UserTag[]; + valueTags?: ValueTag[]; +} + +// 用户标签 +export interface UserTag { + id: string; + name: string; + color: string; + icon?: string; + type: "user" | "value"; +} + +// 价值标签 +export interface ValueTag { + id: string; + name: string; + color: string; + icon?: string; + rfmScore: number; + valueLevel: string; +} + +// 互动记录 +export interface InteractionRecord { + id: string; + type: "click" | "view" | "purchase"; + action: string; + description: string; + timestamp: string; + icon: string; +} diff --git a/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.module.scss b/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.module.scss index a3833543c..60165acbb 100644 --- a/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.module.scss +++ b/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.module.scss @@ -1,291 +1,420 @@ -.container { - padding: 12px; - background: #f5f5f5; - min-height: 100vh; -} - -.userCard { - margin-bottom: 12px; - border-radius: 12px; - overflow: hidden; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); -} - -.userInfo { - display: flex; - align-items: center; - gap: 16px; - padding: 16px; -} - -.avatar { - width: 64px; - height: 64px; - border-radius: 50%; - flex-shrink: 0; -} - -.userDetails { - flex: 1; - min-width: 0; -} - -.nickname { - font-size: 18px; - font-weight: 600; - color: #333; - margin-bottom: 4px; - line-height: 1.2; -} - -.wechatId { - font-size: 14px; - color: #1677ff; - margin-bottom: 4px; - line-height: 1.2; -} - -.alias { - font-size: 12px; - color: #666; - margin-bottom: 8px; - line-height: 1.2; -} - -.tags { - display: flex; - flex-wrap: wrap; - gap: 6px; -} - -.genderTag { - font-size: 12px; - padding: 2px 8px; - border-radius: 12px; -} - -.weightTag { - font-size: 12px; - padding: 2px 8px; - border-radius: 12px; -} - -.tabs { - background: transparent; - - :global(.adm-tabs-header) { - background: white; - border-radius: 12px 12px 0 0; - margin-bottom: 0; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); - } - - :global(.adm-tabs-tab) { - font-size: 14px; - font-weight: 500; - } - - :global(.adm-tabs-tab-active) { - color: #1677ff; - } - - :global(.adm-tabs-tab-line) { - background: #1677ff; - } - - :global(.adm-tabs-content) { - background: white; - border-radius: 0 0 12px 12px; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); - } -} - -.tabContent { - padding: 16px; -} - -.infoCard { - margin-bottom: 12px; - border-radius: 8px; - overflow: hidden; - - &:last-child { - margin-bottom: 0; - } - - :global(.adm-card-header) { - padding: 12px 16px; - border-bottom: 1px solid #f0f0f0; - font-size: 14px; - font-weight: 600; - color: #333; - } - - :global(.adm-card-body) { - padding: 0; - } -} - -.statsGrid { - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: 16px; - padding: 16px; -} - -.statItem { - text-align: center; -} - -.statValue { - font-size: 18px; - font-weight: 700; - line-height: 1.2; - margin-bottom: 4px; -} - -.statLabel { - font-size: 12px; - color: #666; - line-height: 1.2; -} - -.restrictionTitle { - display: flex; - align-items: center; - justify-content: space-between; - gap: 8px; - font-size: 14px; - font-weight: 500; - color: #333; - line-height: 1.4; -} - -.restrictionLevel { - font-size: 10px; - padding: 2px 6px; - border-radius: 8px; - flex-shrink: 0; -} - -.restrictionContent { - display: flex; - flex-direction: column; - gap: 4px; - font-size: 12px; - color: #666; - line-height: 1.4; - margin-top: 4px; -} - -.emptyState { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - padding: 48px 16px; - text-align: center; -} - -.emptyText { - color: #999; - font-size: 14px; - line-height: 1.4; -} - -// 响应式设计 -@media (max-width: 375px) { - .container { - padding: 8px; - } - - .userInfo { - padding: 12px; - gap: 12px; - } - - .avatar { - width: 56px; - height: 56px; - } - - .nickname { - font-size: 16px; - } - - .wechatId { - font-size: 13px; - } - - .alias { - font-size: 11px; - } - - .tabContent { - padding: 12px; - } - - .statsGrid { - gap: 12px; - padding: 12px; - } - - .statValue { - font-size: 16px; - } - - .restrictionTitle { - font-size: 13px; - } - - .restrictionContent { - font-size: 11px; - } -} - -// 暗色模式支持 -@media (prefers-color-scheme: dark) { - .container { - background: #1a1a1a; - } - - .userCard, - .tabs :global(.adm-tabs-header), - .tabs :global(.adm-tabs-content) { - background: #2a2a2a; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); - } - - .nickname { - color: #fff; - } - - .wechatId { - color: #4a9eff; - } - - .alias { - color: #999; - } - - .infoCard :global(.adm-card-header) { - color: #fff; - border-bottom-color: #3a3a3a; - } - - .statLabel { - color: #999; - } - - .restrictionTitle { - color: #fff; - } - - .restrictionContent { - color: #999; - } - - .emptyText { - color: #666; - } -} +.container { + padding: 0; + background: #f5f5f5; + min-height: 100vh; +} + +// 头部样式 +.header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 16px; + background: #fff; + border-bottom: 1px solid #f0f0f0; + + .title { + font-size: 18px; + font-weight: 600; + color: #333; + } + + .closeBtn { + padding: 8px; + border: none; + background: transparent; + color: #999; + font-size: 16px; + } +} + +// 用户卡片 +.userCard { + margin: 16px; + border-radius: 12px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + + .userInfo { + display: flex; + align-items: flex-start; + gap: 16px; + } + + .avatar { + width: 60px; + height: 60px; + border-radius: 50%; + flex-shrink: 0; + } + + .userDetails { + flex: 1; + min-width: 0; + } + + .nickname { + font-size: 18px; + font-weight: 600; + color: #333; + margin-bottom: 4px; + } + + .wechatId { + font-size: 14px; + color: #666; + margin-bottom: 8px; + } + + .tags { + display: flex; + flex-wrap: wrap; + gap: 8px; + } + + .userTag { + font-size: 12px; + padding: 4px 8px; + border-radius: 12px; + } +} + +// 标签导航 +.tabNav { + display: flex; + background: #fff; + margin: 0 16px; + border-radius: 8px; + overflow: hidden; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + + .tabItem { + flex: 1; + padding: 12px 16px; + text-align: center; + font-size: 14px; + color: #666; + cursor: pointer; + transition: all 0.3s ease; + border-bottom: 2px solid transparent; + + &.active { + color: var(--primary-color); + border-bottom-color: var(--primary-color); + background: rgba(24, 142, 238, 0.05); + } + + &:hover { + background: rgba(24, 142, 238, 0.05); + } + } +} + +// 内容区域 +.content { + padding: 16px; +} + +.tabContent { + display: flex; + flex-direction: column; + gap: 16px; +} + +// 信息卡片 +.infoCard { + border-radius: 12px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + overflow: hidden; + + :global(.adm-card-header) { + padding: 16px; + border-bottom: 1px solid #f0f0f0; + font-weight: 600; + color: #333; + } + + :global(.adm-card-body) { + padding: 0; + } +} + +// RFM评分网格 +.rfmGrid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 16px; + padding: 16px; +} + +.rfmItem { + text-align: center; + padding: 12px; + background: #f8f9fa; + border-radius: 8px; +} + +.rfmLabel { + font-size: 12px; + color: #666; + margin-bottom: 4px; +} + +.rfmValue { + font-size: 18px; + font-weight: 600; +} + +// 流量池区域 +.poolSection { + padding: 16px; + display: flex; + flex-direction: column; + gap: 12px; +} + +.currentPool, +.availablePools { + display: flex; + align-items: center; + gap: 8px; + flex-wrap: wrap; +} + +.poolLabel { + font-size: 14px; + color: #666; + white-space: nowrap; +} + +// 统计数据网格 +.statsGrid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 16px; + padding: 16px; +} + +.statItem { + text-align: center; + padding: 12px; + background: #f8f9fa; + border-radius: 8px; +} + +.statValue { + font-size: 18px; + font-weight: 600; + margin-bottom: 4px; +} + +.statLabel { + font-size: 12px; + color: #666; +} + +// 用户旅程 +.journeyItem { + display: flex; + justify-content: space-between; + align-items: center; + font-size: 12px; + color: #666; + margin-top: 4px; +} + +.timestamp { + color: #999; +} + +// 加载状态 +.loadingContainer { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 40px 16px; + text-align: center; +} + +.loadingText { + font-size: 14px; + color: #999; + margin-top: 8px; +} + +.loadingMore { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + padding: 16px; + color: #666; + font-size: 14px; +} + +.loadMoreBtn { + display: flex; + justify-content: center; + padding: 16px; +} + +// 标签区域 +.tagsSection { + padding: 16px; + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.valueTagsSection { + padding: 16px; + display: flex; + flex-direction: column; + gap: 12px; +} + +.tagItem { + font-size: 12px; + padding: 6px 12px; + border-radius: 16px; +} + +.valueTagContainer { + display: flex; + flex-direction: column; + gap: 8px; +} + +.valueTagRow { + display: flex; + justify-content: space-between; + align-items: center; + gap: 8px; +} + +.rfmScoreText { + font-size: 12px; + color: #666; + white-space: nowrap; +} + +.valueLevelLabel { + font-size: 12px; + color: #666; + white-space: nowrap; +} + +.valueTagItem { + display: flex; + justify-content: space-between; + align-items: center; + padding: 12px 0; + border-bottom: 1px solid #f0f0f0; + + &:last-child { + border-bottom: none; + } +} + +.valueInfo { + display: flex; + align-items: center; + gap: 8px; + font-size: 12px; + color: #666; +} + +// 添加标签按钮 +.addTagBtn { + margin-top: 16px; + border-radius: 8px; + height: 48px; + font-size: 16px; + font-weight: 500; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; +} + +// 空状态 +.emptyState { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 60px 16px; + text-align: center; +} + +.emptyIcon { + margin-bottom: 16px; + opacity: 0.6; +} + +.emptyText { + font-size: 16px; + color: #666; + margin-bottom: 8px; + font-weight: 500; +} + +.emptyDesc { + font-size: 14px; + color: #999; + line-height: 1.4; +} + +// 限制记录样式 +.restrictionTitle { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + font-size: 14px; + font-weight: 500; + color: #333; + line-height: 1.4; +} + +.restrictionLevel { + font-size: 10px; + padding: 2px 6px; + border-radius: 8px; + flex-shrink: 0; +} + +.restrictionContent { + display: flex; + flex-direction: column; + gap: 4px; + font-size: 12px; + color: #666; + line-height: 1.4; + margin-top: 4px; +} + +// 响应式设计 +@media (max-width: 375px) { + .rfmGrid, + .statsGrid { + grid-template-columns: 1fr; + } + + .userInfo { + flex-direction: column; + text-align: center; + } + + .avatar { + align-self: center; + } + + .restrictionTitle { + font-size: 13px; + } + + .restrictionContent { + font-size: 11px; + } +} diff --git a/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.tsx b/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.tsx index c10a74efa..afe795fa2 100644 --- a/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.tsx +++ b/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.tsx @@ -1,33 +1,191 @@ import React, { useEffect, useState } from "react"; import { useParams, useNavigate } from "react-router-dom"; -import { Card, Button, Avatar, Tag, Tabs, List, Badge } from "antd-mobile"; +import { + Card, + Button, + Avatar, + Tag, + Tabs, + List, + Badge, + SpinLoading, +} from "antd-mobile"; import { UserOutlined, - MessageOutlined, - TeamOutlined, - ClockCircleOutlined, - ExclamationCircleOutlined, + CrownOutlined, PlusOutlined, + CloseOutlined, + EyeOutlined, + DollarOutlined, + MobileOutlined, + TagOutlined, + FileTextOutlined, + UserAddOutlined, } from "@ant-design/icons"; import Layout from "@/components/Layout/Layout"; import NavCommon from "@/components/NavCommon"; -import { getTrafficPoolDetail } from "./api"; -import type { TrafficPoolUserDetail } from "./data"; +import { getTrafficPoolDetail, getUserJourney } from "./api"; +import type { + TrafficPoolUserDetail, + ExtendedUserDetail, + InteractionRecord, + UserJourneyRecord, +} from "./data"; import styles from "./index.module.scss"; const TrafficPoolDetail: React.FC = () => { - const { id } = useParams(); + const { wxid, userId } = useParams(); const navigate = useNavigate(); const [loading, setLoading] = useState(true); - const [user, setUser] = useState(null); + const [user, setUser] = useState(null); + const [activeTab, setActiveTab] = useState("basic"); + + // 用户旅程相关状态 + const [journeyLoading, setJourneyLoading] = useState(false); + const [journeyList, setJourneyList] = useState([]); + const [journeyPage, setJourneyPage] = useState(1); + const [journeyTotal, setJourneyTotal] = useState(0); + const pageSize = 10; useEffect(() => { - if (!id) return; + if (!wxid) return; setLoading(true); - getTrafficPoolDetail(id as string) - .then((res) => setUser(res)) + getTrafficPoolDetail(wxid as string) + .then(res => { + // 将API数据转换为扩展的用户详情数据 + const extendedUser: ExtendedUserDetail = { + ...res, + // 模拟RFM评分数据 + rfmScore: { + recency: 5, + frequency: 5, + monetary: 5, + totalScore: 15, + }, + // 模拟流量池数据 + trafficPools: { + currentPool: "新用户池", + availablePools: ["高价值客户池", "活跃用户池"], + }, + // 模拟用户标签数据 + userTags: [ + { id: "1", name: "近期活跃", color: "success", type: "user" }, + { id: "2", name: "高频互动", color: "primary", type: "user" }, + { id: "3", name: "高消费", color: "warning", type: "user" }, + { id: "4", name: "老客户", color: "danger", type: "user" }, + ], + // 模拟价值标签数据 + valueTags: [ + { + id: "1", + name: "重要保持客户", + color: "primary", + icon: "crown", + rfmScore: 14, + valueLevel: "高价值", + }, + ], + }; + setUser(extendedUser); + }) .finally(() => setLoading(false)); - }, [id]); + }, [wxid]); + + // 获取用户旅程数据 + const fetchUserJourney = async (page: number = 1) => { + if (!userId) return; + + setJourneyLoading(true); + try { + const response = await getUserJourney({ + page, + pageSize, + userId: userId, + }); + + if (page === 1) { + setJourneyList(response.list); + } else { + setJourneyList(prev => [...prev, ...response.list]); + } + setJourneyTotal(response.total); + setJourneyPage(page); + } catch (error) { + console.error("获取用户旅程失败:", error); + } finally { + setJourneyLoading(false); + } + }; + + // 标签切换处理 + const handleTabChange = (tab: string) => { + setActiveTab(tab); + if (tab === "journey" && journeyList.length === 0) { + fetchUserJourney(1); + } + }; + + const handleClose = () => { + navigate(-1); + }; + + const getJourneyTypeIcon = (type: number) => { + switch (type) { + case 0: // 浏览 + return ; + case 2: // 提交订单 + return ; + case 3: // 注册 + return ; + default: + return ; + } + }; + + const getJourneyTypeText = (type: number) => { + switch (type) { + case 0: + return "浏览行为"; + case 2: + return "提交订单"; + case 3: + return "注册行为"; + default: + return "其他行为"; + } + }; + + const formatDateTime = (dateTime: string) => { + try { + const date = new Date(dateTime); + return date.toLocaleString("zh-CN", { + year: "numeric", + month: "2-digit", + day: "2-digit", + hour: "2-digit", + minute: "2-digit", + }); + } catch (error) { + return dateTime; + } + }; + + const getActionIcon = (type: string) => { + switch (type) { + case "click": + return ; + case "view": + return ; + case "purchase": + return ; + default: + return ; + } + }; + + const formatCurrency = (amount: number) => { + return `¥${amount.toLocaleString()}`; + }; const getGenderText = (gender: number) => { switch (gender) { @@ -87,16 +245,6 @@ const TrafficPoolDetail: React.FC = () => { } }; - const formatAccountAge = (dateString: string) => { - if (!dateString) return "--"; - try { - const date = new Date(dateString); - return date.toLocaleDateString("zh-CN"); - } catch (error) { - return dateString; - } - }; - if (!user) { return ( } loading={loading}> @@ -108,8 +256,21 @@ const TrafficPoolDetail: React.FC = () => { } return ( - } loading={loading}> +
+ {/* 头部 */} +
+
用户详情
+ +
+ {/* 用户基本信息 */}
@@ -121,54 +282,167 @@ const TrafficPoolDetail: React.FC = () => {
{user.userInfo.nickname}
{user.userInfo.wechatId}
-
别名:{user.userInfo.alias}
- - {getGenderText(user.userInfo.gender)} + + + 重要价值客户 + + + 优先添加 - {user.userInfo.weight && ( - - 权重: {user.userInfo.weight} - - )}
- {/* Tab内容 */} - - + {/* 导航标签 */} +
+
handleTabChange("basic")} + > + 基本信息 +
+
handleTabChange("journey")} + > + 用户旅程 +
+
handleTabChange("tags")} + > + 用户标签 +
+
+ + {/* 内容区域 */} +
+ {activeTab === "basic" && (
- {/* 账户信息 */} - + {/* 关联信息 */} + - - 注册时间 - - - 今日添加 - - - 总消息数 - - - 今日消息 - + 设备 + 微信号 + 客服 + 添加时间 + 最近互动 + {/* RFM评分 */} + {user.rfmScore && ( + +
+
+
最近性(R)
+
+ {user.rfmScore.recency} +
+
+
+
频率(F)
+
+ {user.rfmScore.frequency} +
+
+
+
金额(M)
+
+ {user.rfmScore.monetary} +
+
+
+
总分
+
+ {user.rfmScore.totalScore}/15 +
+
+
+
+ )} + + {/* 流量池 */} + {user.trafficPools && ( + +
+
+ 当前池: + + {user.trafficPools.currentPool} + +
+
+ 可选池: + {user.trafficPools.availablePools.map((pool, index) => ( + + {pool} + + ))} +
+
+
+ )} + + {/* 统计数据 */} + +
+
+
+ ¥9561 +
+
总消费
+
+
+
+ 6 +
+
互动次数
+
+
+
+ 3% +
+
转化率
+
+
+
+ 未添加 +
+
添加状态
+
+
+
+ {/* 好友统计 */}
@@ -205,126 +479,16 @@ const TrafficPoolDetail: React.FC = () => {
未知性别
-
-
- {user.userInfo.friendShip.groupNumber} -
-
群聊数量
-
- {/* 活跃度统计 */} - -
-
-
- {user.userInfo.activity.totalMsgCount} -
-
总消息数
-
-
-
- {user.userInfo.activity.sevenDayMsgCount} -
-
7天消息
-
-
-
- {user.userInfo.activity.thirtyDayMsgCount} -
-
30天消息
-
-
-
- {user.userInfo.activity.yesterdayMsgCount} -
-
昨日消息
-
-
-
- - {/* 账户权重 */} - -
-
-
- {user.accountWeight.ageWeight} -
-
年龄权重
-
-
-
- {user.accountWeight.activityWeigth} -
-
活跃权重
-
-
-
- {user.accountWeight.restrictWeight} -
-
限制权重
-
-
-
- {user.accountWeight.realNameWeight} -
-
实名权重
-
-
-
- {user.accountWeight.scope} -
-
综合评分
-
-
-
-
- - - -
+ {/* 限制记录 */} {user.restrictions && user.restrictions.length > 0 ? ( - {user.restrictions.map((restriction) => ( + {user.restrictions.map(restriction => ( - } title={
{restriction.reason || "未知原因"} @@ -354,23 +518,161 @@ const TrafficPoolDetail: React.FC = () => { ) : (
+
+ +
暂无限制记录
+
+ 该用户没有任何限制记录 +
)}
- + )} - + {activeTab === "journey" && (
- -
-
暂无操作记录
-
+ + {journeyLoading && journeyList.length === 0 ? ( +
+ +
加载中...
+
+ ) : journeyList.length === 0 ? ( +
+
+ +
+
暂无互动记录
+
+ 该用户还没有任何互动行为 +
+
+ ) : ( + + {journeyList.map(record => ( + + {record.remark} + + {formatDateTime(record.createTime)} + +
+ } + /> + ))} + {journeyLoading && journeyList.length > 0 && ( +
+ + 加载更多... +
+ )} + {!journeyLoading && journeyList.length < journeyTotal && ( +
+ +
+ )} +
+ )}
-
- + )} + + {activeTab === "tags" && ( +
+ {/* 用户标签 */} + + {user.userTags && user.userTags.length > 0 ? ( +
+ {user.userTags.map(tag => ( + + {tag.name} + + ))} +
+ ) : ( +
+
+ +
+
暂无用户标签
+
该用户还没有任何标签
+
+ )} +
+ + {/* 价值标签 */} + + {user.valueTags && user.valueTags.length > 0 ? ( +
+ {user.valueTags.map(tag => ( +
+
+ + {tag.icon === "crown" && } + {tag.name} + + + RFM总分: {tag.rfmScore}/15 + +
+
+ + 价值等级: + + + {tag.valueLevel} + +
+
+ ))} +
+ ) : ( +
+
+ +
+
暂无价值标签
+
+ 该用户还没有任何价值标签 +
+
+ )} +
+ + {/* 添加新标签按钮 */} + +
+ )} +
); diff --git a/nkebao/src/pages/mobile/mine/traffic-pool/list/index.tsx b/nkebao/src/pages/mobile/mine/traffic-pool/list/index.tsx index a802394d9..7e0005240 100644 --- a/nkebao/src/pages/mobile/mine/traffic-pool/list/index.tsx +++ b/nkebao/src/pages/mobile/mine/traffic-pool/list/index.tsx @@ -192,7 +192,7 @@ const TrafficPoolList: React.FC = () => { className={styles.card} style={{ cursor: "pointer" }} onClick={() => - navigate(`/traffic-pool/detail/${item.sourceId}`) + navigate(`/traffic-pool/detail/${item.sourceId}/${item.id}`) } >
diff --git a/nkebao/src/router/module/mine.tsx b/nkebao/src/router/module/mine.tsx index 7a190068b..46e852e8b 100644 --- a/nkebao/src/router/module/mine.tsx +++ b/nkebao/src/router/module/mine.tsx @@ -29,7 +29,7 @@ const routes = [ auth: true, }, { - path: "/traffic-pool/detail/:id", + path: "/traffic-pool/detail/:wxid/:userId", element: , auth: true, }, diff --git a/nkebao/tsconfig.json b/nkebao/tsconfig.json index d5830e183..9d100420a 100644 --- a/nkebao/tsconfig.json +++ b/nkebao/tsconfig.json @@ -1,24 +1,24 @@ -{ - "compilerOptions": { - "target": "ESNext", - "lib": ["DOM", "DOM.Iterable", "ESNext"], - "allowJs": true, - "skipLibCheck": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "strict": false, - "noImplicitAny": false, - "forceConsistentCasingInFileNames": true, - "noFallthroughCasesInSwitch": true, - "module": "ESNext", - "moduleResolution": "Node", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "react-jsx", - "baseUrl": "./", - "paths": { - "@/*": ["src/*"] - } - }, - "include": ["src"] -} +{ + "compilerOptions": { + "target": "ESNext", + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": false, + "noImplicitAny": false, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "module": "ESNext", + "moduleResolution": "Node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "baseUrl": "./", + "paths": { + "@/*": ["src/*"] + } + }, + "include": ["src"] +} From c1cc74c2f3d50ccbc399f08e1f61b7a1edd9dfe0 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: Tue, 29 Jul 2025 19:14:56 +0800 Subject: [PATCH 05/12] =?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 --- .../mobile/mine/traffic-pool/detail/api.ts | 10 +- .../mobile/mine/traffic-pool/detail/data.ts | 14 ++ .../mobile/mine/traffic-pool/detail/index.tsx | 196 ++++++++++-------- 3 files changed, 132 insertions(+), 88 deletions(-) diff --git a/nkebao/src/pages/mobile/mine/traffic-pool/detail/api.ts b/nkebao/src/pages/mobile/mine/traffic-pool/detail/api.ts index 0bbaec1f0..dfad51951 100644 --- a/nkebao/src/pages/mobile/mine/traffic-pool/detail/api.ts +++ b/nkebao/src/pages/mobile/mine/traffic-pool/detail/api.ts @@ -1,5 +1,9 @@ import request from "@/api/request"; -import type { TrafficPoolUserDetail, UserJourneyResponse } from "./data"; +import type { + TrafficPoolUserDetail, + UserJourneyResponse, + UserTagsResponse, +} from "./data"; export function getTrafficPoolDetail( wechatId: string @@ -17,8 +21,8 @@ export function getUserJourney(params: { } // 获取用户标签 -export function getUserTags(userId: string): Promise { - return request("/v1/user/tags", { userId }, "GET"); +export function getUserTags(userId: string): Promise { + return request("/v1/traffic/pool/getUserTags", { userId }, "GET"); } // 添加用户标签 diff --git a/nkebao/src/pages/mobile/mine/traffic-pool/detail/data.ts b/nkebao/src/pages/mobile/mine/traffic-pool/detail/data.ts index 1e274f63e..0657ab5e9 100644 --- a/nkebao/src/pages/mobile/mine/traffic-pool/detail/data.ts +++ b/nkebao/src/pages/mobile/mine/traffic-pool/detail/data.ts @@ -62,6 +62,20 @@ export interface UserJourneyResponse { total: number; } +// 用户标签类型 +export interface UserTagItem { + id: string; + name: string; + color?: string; + type?: string; +} + +// 用户标签响应类型 +export interface UserTagsResponse { + wechat: UserTagItem[]; + siteLabels: UserTagItem[]; +} + // 扩展的用户详情类型 - 用于前端展示 export interface ExtendedUserDetail extends TrafficPoolUserDetail { // 前端计算或模拟的数据 diff --git a/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.tsx b/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.tsx index afe795fa2..f478fdfc4 100644 --- a/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.tsx +++ b/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.tsx @@ -24,12 +24,14 @@ import { } from "@ant-design/icons"; import Layout from "@/components/Layout/Layout"; import NavCommon from "@/components/NavCommon"; -import { getTrafficPoolDetail, getUserJourney } from "./api"; +import { getTrafficPoolDetail, getUserJourney, getUserTags } from "./api"; import type { TrafficPoolUserDetail, ExtendedUserDetail, InteractionRecord, UserJourneyRecord, + UserTagsResponse, + UserTagItem, } from "./data"; import styles from "./index.module.scss"; @@ -47,6 +49,10 @@ const TrafficPoolDetail: React.FC = () => { const [journeyTotal, setJourneyTotal] = useState(0); const pageSize = 10; + // 用户标签相关状态 + const [tagsLoading, setTagsLoading] = useState(false); + const [userTagsList, setUserTagsList] = useState([]); + useEffect(() => { if (!wxid) return; setLoading(true); @@ -117,12 +123,30 @@ const TrafficPoolDetail: React.FC = () => { } }; + // 获取用户标签数据 + const fetchUserTags = async () => { + if (!userId) return; + + setTagsLoading(true); + try { + const response: UserTagsResponse = await getUserTags(userId); + setUserTagsList(response.siteLabels || []); + } catch (error) { + console.error("获取用户标签失败:", error); + } finally { + setTagsLoading(false); + } + }; + // 标签切换处理 const handleTabChange = (tab: string) => { setActiveTab(tab); if (tab === "journey" && journeyList.length === 0) { fetchUserJourney(1); } + if (tab === "tags" && userTagsList.length === 0) { + fetchUserTags(); + } }; const handleClose = () => { @@ -245,6 +269,12 @@ const TrafficPoolDetail: React.FC = () => { } }; + // 获取标签颜色 + const getTagColor = (index: number): string => { + const colors = ["primary", "success", "warning", "danger", "default"]; + return colors[index % colors.length]; + }; + if (!user) { return ( } loading={loading}> @@ -256,73 +286,69 @@ const TrafficPoolDetail: React.FC = () => { } return ( - -
- {/* 头部 */} -
-
用户详情
- -
- - {/* 用户基本信息 */} - -
- } - /> -
-
{user.userInfo.nickname}
-
{user.userInfo.wechatId}
-
- - - 重要价值客户 - - - 优先添加 - + + + {/* 用户基本信息 */} + +
+ } + /> +
+
{user.userInfo.nickname}
+
{user.userInfo.wechatId}
+
+ + + 重要价值客户 + + + 优先添加 + +
+
+ {/* 导航标签 */} +
+
handleTabChange("basic")} + > + 基本信息 +
+
handleTabChange("journey")} + > + 用户旅程 +
+
handleTabChange("tags")} + > + 用户标签 +
- - - {/* 导航标签 */} -
-
handleTabChange("basic")} - > - 基本信息 -
-
handleTabChange("journey")} - > - 用户旅程 -
-
handleTabChange("tags")} - > - 用户标签 -
-
- + + } + > +
{/* 内容区域 */}
{activeTab === "basic" && ( @@ -593,20 +619,12 @@ const TrafficPoolDetail: React.FC = () => {
{/* 用户标签 */} - {user.userTags && user.userTags.length > 0 ? ( -
- {user.userTags.map(tag => ( - - {tag.name} - - ))} + {tagsLoading && userTagsList.length === 0 ? ( +
+ +
加载中...
- ) : ( + ) : userTagsList.length === 0 ? (
@@ -614,6 +632,19 @@ const TrafficPoolDetail: React.FC = () => {
暂无用户标签
该用户还没有任何标签
+ ) : ( +
+ {userTagsList.map((tag, index) => ( + + {tag.name} + + ))} +
)} @@ -661,14 +692,9 @@ const TrafficPoolDetail: React.FC = () => { {/* 添加新标签按钮 */} -
)} From e45344a1c37e43b1d57d388cf2900951fd143ecb 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: Tue, 29 Jul 2025 19:26:39 +0800 Subject: [PATCH 06/12] =?UTF-8?q?Revert=20"=20feat:=20=E6=9C=AC=E6=AC=A1?= =?UTF-8?q?=E6=9B=B4=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 This reverts commit 3995ef55e118aa251ccc231bb67f13e851b357aa. --- nkebao/tsconfig.json | 48 ++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/nkebao/tsconfig.json b/nkebao/tsconfig.json index 9d100420a..d5830e183 100644 --- a/nkebao/tsconfig.json +++ b/nkebao/tsconfig.json @@ -1,24 +1,24 @@ -{ - "compilerOptions": { - "target": "ESNext", - "lib": ["DOM", "DOM.Iterable", "ESNext"], - "allowJs": true, - "skipLibCheck": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "strict": false, - "noImplicitAny": false, - "forceConsistentCasingInFileNames": true, - "noFallthroughCasesInSwitch": true, - "module": "ESNext", - "moduleResolution": "Node", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "react-jsx", - "baseUrl": "./", - "paths": { - "@/*": ["src/*"] - } - }, - "include": ["src"] -} +{ + "compilerOptions": { + "target": "ESNext", + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": false, + "noImplicitAny": false, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "module": "ESNext", + "moduleResolution": "Node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "baseUrl": "./", + "paths": { + "@/*": ["src/*"] + } + }, + "include": ["src"] +} From 54b84f8bf6ef08ef75b47dfebe549265d0da9a85 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: Tue, 29 Jul 2025 19:27:24 +0800 Subject: [PATCH 07/12] =?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=AD=98?= =?UTF-8?q?=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nkebao/tsconfig.json | 48 ++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/nkebao/tsconfig.json b/nkebao/tsconfig.json index d5830e183..9d100420a 100644 --- a/nkebao/tsconfig.json +++ b/nkebao/tsconfig.json @@ -1,24 +1,24 @@ -{ - "compilerOptions": { - "target": "ESNext", - "lib": ["DOM", "DOM.Iterable", "ESNext"], - "allowJs": true, - "skipLibCheck": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "strict": false, - "noImplicitAny": false, - "forceConsistentCasingInFileNames": true, - "noFallthroughCasesInSwitch": true, - "module": "ESNext", - "moduleResolution": "Node", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "react-jsx", - "baseUrl": "./", - "paths": { - "@/*": ["src/*"] - } - }, - "include": ["src"] -} +{ + "compilerOptions": { + "target": "ESNext", + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": false, + "noImplicitAny": false, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "module": "ESNext", + "moduleResolution": "Node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "baseUrl": "./", + "paths": { + "@/*": ["src/*"] + } + }, + "include": ["src"] +} From d2d9e536555aca662fa7154e517ef2269d97af33 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: Tue, 29 Jul 2025 19:27:48 +0800 Subject: [PATCH 08/12] =?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=E6=9D=91?= =?UTF-8?q?=E9=87=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nkebao/vite.config.ts | 54 +++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/nkebao/vite.config.ts b/nkebao/vite.config.ts index 612025186..f1a085ec2 100644 --- a/nkebao/vite.config.ts +++ b/nkebao/vite.config.ts @@ -1,27 +1,27 @@ -import { defineConfig } from "vite"; -import react from "@vitejs/plugin-react"; -import path from "path"; - -export default defineConfig({ - plugins: [react()], - resolve: { - alias: { - "@": path.resolve("src"), - }, - }, - server: { - open: true, - }, - build: { - chunkSizeWarningLimit: 1500, // 提高警告阈值,减少无关警告 - rollupOptions: { - output: { - manualChunks: { - "react-vendor": ["react", "react-dom"], - "antd-vendor": ["antd", "@ant-design/icons", "antd-mobile"], - "echarts-vendor": ["echarts", "echarts-for-react"], - }, - }, - }, - }, -}); +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import path from "path"; + +export default defineConfig({ + plugins: [react()], + resolve: { + alias: { + "@": path.resolve("src"), + }, + }, + server: { + open: true, + }, + build: { + chunkSizeWarningLimit: 1500, // 提高警告阈值,减少无关警告 + rollupOptions: { + output: { + manualChunks: { + "react-vendor": ["react", "react-dom"], + "antd-vendor": ["antd", "@ant-design/icons", "antd-mobile"], + "echarts-vendor": ["echarts", "echarts-for-react"], + }, + }, + }, + }, +}); From 89a7c12fa0be55f2e0748ea827fb1c085e20ba35 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: Tue, 29 Jul 2025 19:31:23 +0800 Subject: [PATCH 09/12] =?UTF-8?q?=20feat:=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 --- nkebao/vite.config.ts | 54 +++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/nkebao/vite.config.ts b/nkebao/vite.config.ts index 612025186..f1a085ec2 100644 --- a/nkebao/vite.config.ts +++ b/nkebao/vite.config.ts @@ -1,27 +1,27 @@ -import { defineConfig } from "vite"; -import react from "@vitejs/plugin-react"; -import path from "path"; - -export default defineConfig({ - plugins: [react()], - resolve: { - alias: { - "@": path.resolve("src"), - }, - }, - server: { - open: true, - }, - build: { - chunkSizeWarningLimit: 1500, // 提高警告阈值,减少无关警告 - rollupOptions: { - output: { - manualChunks: { - "react-vendor": ["react", "react-dom"], - "antd-vendor": ["antd", "@ant-design/icons", "antd-mobile"], - "echarts-vendor": ["echarts", "echarts-for-react"], - }, - }, - }, - }, -}); +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import path from "path"; + +export default defineConfig({ + plugins: [react()], + resolve: { + alias: { + "@": path.resolve("src"), + }, + }, + server: { + open: true, + }, + build: { + chunkSizeWarningLimit: 1500, // 提高警告阈值,减少无关警告 + rollupOptions: { + output: { + manualChunks: { + "react-vendor": ["react", "react-dom"], + "antd-vendor": ["antd", "@ant-design/icons", "antd-mobile"], + "echarts-vendor": ["echarts", "echarts-for-react"], + }, + }, + }, + }, +}); From e4b1cf4a1568df72f0eae3a8f600bca787ea4764 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: Tue, 29 Jul 2025 19:49:09 +0800 Subject: [PATCH 10/12] =?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-=E8=AE=BE?= =?UTF-8?q?=E5=A4=87=E6=89=AB=E6=8F=8F=E4=BA=8C=E7=BB=B4=E7=A0=81=E5=BC=B9?= =?UTF-8?q?=E7=AA=97=E4=BC=98=E5=8C=96=E2=9C=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/pages/mobile/mine/devices/index.tsx | 27 ++++++++++--------- nkebao/src/pages/mobile/mine/main/index.tsx | 25 ----------------- 2 files changed, 15 insertions(+), 37 deletions(-) diff --git a/nkebao/src/pages/mobile/mine/devices/index.tsx b/nkebao/src/pages/mobile/mine/devices/index.tsx index 84464d2f1..467b65d55 100644 --- a/nkebao/src/pages/mobile/mine/devices/index.tsx +++ b/nkebao/src/pages/mobile/mine/devices/index.tsx @@ -10,7 +10,6 @@ import { ArrowLeftOutlined, } from "@ant-design/icons"; import Layout from "@/components/Layout/Layout"; -import MeauMobile from "@/components/MeauMobile/MeauMoible"; import { fetchDeviceList, fetchDeviceQRCode, @@ -19,6 +18,7 @@ import { } from "@/api/devices"; import type { Device } from "@/types/device"; import { comfirm } from "@/utils/common"; +import { useUserStore } from "@/store/module/user"; const Devices: React.FC = () => { // 设备列表相关 @@ -47,6 +47,7 @@ const Devices: React.FC = () => { const [delLoading, setDelLoading] = useState(false); const navigate = useNavigate(); + const { user } = useUserStore(); // 加载设备列表 const loadDevices = useCallback( async (reset = false) => { @@ -68,7 +69,7 @@ const Devices: React.FC = () => { setLoading(false); } }, - [loading, search, page] + [loading, search, page], ); // 首次加载和搜索 @@ -86,7 +87,7 @@ const Devices: React.FC = () => { setPage(p => p + 1); } }, - { threshold: 0.5 } + { threshold: 0.5 }, ); if (observerRef.current) observer.observe(observerRef.current); return () => observer.disconnect(); @@ -112,7 +113,7 @@ const Devices: React.FC = () => { setQrLoading(true); setQrCode(null); try { - const accountId = localStorage.getItem("s2_accountId") || ""; + const accountId = user.s2_accountId; if (!accountId) throw new Error("未获取到用户信息"); const res = await fetchDeviceQRCode(accountId); setQrCode(res.qrCode); @@ -123,6 +124,11 @@ const Devices: React.FC = () => { } }; + const addDevice = async () => { + await handleGetQr(); + setAddVisible(true); + }; + // 手动添加设备 const handleAddDevice = async () => { if (!imei.trim() || !name.trim()) { @@ -166,7 +172,7 @@ const Devices: React.FC = () => { try { await comfirm( `将删除${selected.length}个设备,删除后本设备配置的计划任务操作也将失效。确认删除?`, - { title: "确认删除", confirmText: "确认删除", cancelText: "取消" } + { title: "确认删除", confirmText: "确认删除", cancelText: "取消" }, ); handleDelete(); } catch { @@ -201,11 +207,7 @@ const Devices: React.FC = () => { } style={{ background: "#fff" }} right={ - @@ -301,7 +303,7 @@ const Devices: React.FC = () => { setSelected(prev => e.target.checked ? [...prev, device.id!] - : prev.filter(id => id !== device.id) + : prev.filter(id => id !== device.id), ); }} onClick={e => e.stopPropagation()} @@ -373,7 +375,7 @@ const Devices: React.FC = () => { {addTab === "scan" && (
} - > - - 设备管理 - - - + />
{/* 搜索栏 */}
From 0059c3ba3b57c7e36c65b25d2b95b708bc4e9fe5 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: Wed, 30 Jul 2025 09:29:32 +0800 Subject: [PATCH 12/12] fix: Add missing commas in various TypeScript files to ensure proper syntax and prevent potential runtime errors. --- nkebao/src/api/common.ts | 2 +- nkebao/src/api/devices.ts | 2 +- nkebao/src/api/request.ts | 4 +- .../src/components/AccountSelection/index.tsx | 2 +- .../ContentLibrarySelection/index.tsx | 6 +- .../DeviceSelection/selectionPopup.tsx | 4 +- .../src/components/FriendSelection/index.tsx | 2 +- .../src/components/GroupSelection/index.tsx | 4 +- nkebao/src/components/Layout/Layout.tsx | 2 +- nkebao/src/components/Upload/index.tsx | 2 +- nkebao/src/pages/mobile/content/form/api.ts | 4 +- nkebao/src/pages/mobile/content/list/api.ts | 6 +- .../src/pages/mobile/content/list/index.tsx | 2 +- nkebao/src/pages/mobile/home/index.tsx | 4 +- .../mobile/mine/consumption-records/api.ts | 17 + .../mobile/mine/consumption-records/data.ts | 26 ++ .../consumption-records/index.module.scss | 141 +++++++ .../mobile/mine/consumption-records/index.tsx | 212 ++++++++++ .../mobile/mine/devices/DeviceDetail.tsx | 10 +- .../mobile/mine/recharge/index.module.scss | 329 ++++++++++++++- .../src/pages/mobile/mine/recharge/index.tsx | 382 +++++++++++++++--- .../mobile/mine/traffic-pool/detail/api.ts | 2 +- .../mobile/mine/traffic-pool/detail/index.tsx | 2 +- .../mine/traffic-pool/list/dataAnyx.tsx | 2 +- .../mobile/mine/wechat-accounts/detail/api.ts | 2 +- .../mine/wechat-accounts/detail/index.tsx | 4 +- .../src/pages/mobile/scenarios/list/index.tsx | 2 +- .../mobile/scenarios/plan/list/index.tsx | 6 +- .../mobile/scenarios/plan/list/planApi.tsx | 2 +- .../pages/mobile/scenarios/plan/new/index.tsx | 2 +- .../plan/new/steps/BasicSettings.tsx | 20 +- .../plan/new/steps/FriendRequestSettings.tsx | 2 +- .../plan/new/steps/MessageSettings.tsx | 8 +- .../scenarios/plan/new/steps/step.api.ts | 12 +- .../workspace/auto-group/detail/index.tsx | 8 +- .../workspace/auto-group/list/index.tsx | 6 +- .../mobile/workspace/auto-like/list/api.ts | 4 +- .../mobile/workspace/auto-like/list/index.tsx | 2 +- .../mobile/workspace/auto-like/new/index.tsx | 2 +- .../mobile/workspace/auto-like/record/api.ts | 4 +- .../workspace/group-push/detail/groupPush.ts | 10 +- .../workspace/group-push/detail/index.tsx | 2 +- .../form/components/BasicSettings.tsx | 2 +- .../form/components/ContentSelector.tsx | 2 +- .../form/components/GroupSelector.tsx | 4 +- .../workspace/group-push/form/index.api.ts | 2 +- .../workspace/group-push/list/index.api.ts | 10 +- .../workspace/group-push/list/index.tsx | 4 +- .../mobile/workspace/moments-sync/Detail.tsx | 2 +- .../workspace/moments-sync/MomentsSync.tsx | 8 +- .../traffic-distribution/form/api.ts | 4 +- .../traffic-distribution/form/index.tsx | 2 +- .../traffic-distribution/list/api.ts | 2 +- .../traffic-distribution/list/index.tsx | 6 +- nkebao/src/router/config.ts | 2 +- nkebao/src/router/index.tsx | 2 +- nkebao/src/store/createPersistStore.ts | 4 +- nkebao/src/store/module/user.ts | 2 +- nkebao/src/utils/common.ts | 2 +- 59 files changed, 1154 insertions(+), 173 deletions(-) create mode 100644 nkebao/src/pages/mobile/mine/consumption-records/api.ts create mode 100644 nkebao/src/pages/mobile/mine/consumption-records/data.ts create mode 100644 nkebao/src/pages/mobile/mine/consumption-records/index.module.scss create mode 100644 nkebao/src/pages/mobile/mine/consumption-records/index.tsx diff --git a/nkebao/src/api/common.ts b/nkebao/src/api/common.ts index ebe966eb5..77a0e7dea 100644 --- a/nkebao/src/api/common.ts +++ b/nkebao/src/api/common.ts @@ -7,7 +7,7 @@ import request from "./request"; */ export async function uploadFile( file: File, - uploadUrl: string = "/v1/attachment/upload" + uploadUrl: string = "/v1/attachment/upload", ): Promise { try { // 创建 FormData 对象用于文件上传 diff --git a/nkebao/src/api/devices.ts b/nkebao/src/api/devices.ts index efadbc918..1a4534ab4 100644 --- a/nkebao/src/api/devices.ts +++ b/nkebao/src/api/devices.ts @@ -19,7 +19,7 @@ export const fetchDeviceRelatedAccounts = (id: string | number) => export const fetchDeviceHandleLogs = ( id: string | number, page = 1, - limit = 10 + limit = 10, ) => request(`/v1/devices/${id}/handle-logs`, { page, limit }, "GET"); // 更新设备任务配置 diff --git a/nkebao/src/api/request.ts b/nkebao/src/api/request.ts index 18173f8da..2235a5529 100644 --- a/nkebao/src/api/request.ts +++ b/nkebao/src/api/request.ts @@ -47,7 +47,7 @@ instance.interceptors.response.use( err => { Toast.show({ content: err.message || "网络异常", position: "top" }); return Promise.reject(err); - } + }, ); export function request( @@ -55,7 +55,7 @@ export function request( data?: any, method: Method = "GET", config?: AxiosRequestConfig, - debounceGap?: number + debounceGap?: number, ): Promise { const gap = typeof debounceGap === "number" ? debounceGap : DEFAULT_DEBOUNCE_GAP; diff --git a/nkebao/src/components/AccountSelection/index.tsx b/nkebao/src/components/AccountSelection/index.tsx index be8023b89..3f8aace6c 100644 --- a/nkebao/src/components/AccountSelection/index.tsx +++ b/nkebao/src/components/AccountSelection/index.tsx @@ -117,7 +117,7 @@ export default function AccountSelection({ acc => acc.userName.includes(searchQuery) || acc.realName.includes(searchQuery) || - acc.departmentName.includes(searchQuery) + acc.departmentName.includes(searchQuery), ); // 处理账号选择 diff --git a/nkebao/src/components/ContentLibrarySelection/index.tsx b/nkebao/src/components/ContentLibrarySelection/index.tsx index a7e8507c7..b053f1d98 100644 --- a/nkebao/src/components/ContentLibrarySelection/index.tsx +++ b/nkebao/src/components/ContentLibrarySelection/index.tsx @@ -58,7 +58,7 @@ interface ContentLibrarySelectionProps { readonly?: boolean; onConfirm?: ( selectedIds: string[], - selectedItems: ContentLibraryItem[] + selectedItems: ContentLibraryItem[], ) => void; } @@ -86,7 +86,7 @@ export default function ContentLibrarySelection({ // 获取已选内容库详细信息 const selectedLibraryObjs = libraries.filter(item => - selectedLibraries.includes(item.id) + selectedLibraries.includes(item.id), ); // 删除已选内容库 @@ -161,7 +161,7 @@ export default function ContentLibrarySelection({ onSelect(newSelected); if (onSelectDetail) { const selectedObjs = libraries.filter(item => - newSelected.includes(item.id) + newSelected.includes(item.id), ); onSelectDetail(selectedObjs); } diff --git a/nkebao/src/components/DeviceSelection/selectionPopup.tsx b/nkebao/src/components/DeviceSelection/selectionPopup.tsx index d1e002f05..fc3ea3c21 100644 --- a/nkebao/src/components/DeviceSelection/selectionPopup.tsx +++ b/nkebao/src/components/DeviceSelection/selectionPopup.tsx @@ -61,7 +61,7 @@ const SelectionPopup: React.FC = ({ wxid: d.wechatId || "", nickname: d.nickname || "", usedInPlans: d.usedInPlans || 0, - })) + })), ); setTotal(res.total || 0); } @@ -71,7 +71,7 @@ const SelectionPopup: React.FC = ({ setLoading(false); } }, - [] + [], ); // 打开弹窗时获取第一页 diff --git a/nkebao/src/components/FriendSelection/index.tsx b/nkebao/src/components/FriendSelection/index.tsx index e2f37c363..c416d0114 100644 --- a/nkebao/src/components/FriendSelection/index.tsx +++ b/nkebao/src/components/FriendSelection/index.tsx @@ -137,7 +137,7 @@ export default function FriendSelection({ // 如果有 onSelectDetail 回调,传递完整的好友对象 if (onSelectDetail) { const selectedFriendObjs = friends.filter(friend => - newSelectedFriends.includes(friend.id) + newSelectedFriends.includes(friend.id), ); onSelectDetail(selectedFriendObjs); } diff --git a/nkebao/src/components/GroupSelection/index.tsx b/nkebao/src/components/GroupSelection/index.tsx index fe027edfa..423cf142b 100644 --- a/nkebao/src/components/GroupSelection/index.tsx +++ b/nkebao/src/components/GroupSelection/index.tsx @@ -59,7 +59,7 @@ export default function GroupSelection({ // 获取已选群聊详细信息 const selectedGroupObjs = groups.filter(group => - selectedGroups.includes(group.id) + selectedGroups.includes(group.id), ); // 删除已选群聊 @@ -141,7 +141,7 @@ export default function GroupSelection({ // 如果有 onSelectDetail 回调,传递完整的群聊对象 if (onSelectDetail) { const selectedGroupObjs = groups.filter(group => - newSelectedGroups.includes(group.id) + newSelectedGroups.includes(group.id), ); onSelectDetail(selectedGroupObjs); } diff --git a/nkebao/src/components/Layout/Layout.tsx b/nkebao/src/components/Layout/Layout.tsx index 43c51f1fc..160d993e4 100644 --- a/nkebao/src/components/Layout/Layout.tsx +++ b/nkebao/src/components/Layout/Layout.tsx @@ -20,7 +20,7 @@ const Layout: React.FC = ({ const setRealHeight = () => { document.documentElement.style.setProperty( "--real-vh", - `${window.innerHeight * 0.01}px` + `${window.innerHeight * 0.01}px`, ); }; setRealHeight(); diff --git a/nkebao/src/components/Upload/index.tsx b/nkebao/src/components/Upload/index.tsx index 73ec84a92..da1a2a211 100644 --- a/nkebao/src/components/Upload/index.tsx +++ b/nkebao/src/components/Upload/index.tsx @@ -68,7 +68,7 @@ const UploadComponent: React.FC = ({ Authorization: `Bearer ${localStorage.getItem("token")}`, }, body: formData, - } + }, ); if (!response.ok) { diff --git a/nkebao/src/pages/mobile/content/form/api.ts b/nkebao/src/pages/mobile/content/form/api.ts index 4cce9f260..bc136d885 100644 --- a/nkebao/src/pages/mobile/content/form/api.ts +++ b/nkebao/src/pages/mobile/content/form/api.ts @@ -12,14 +12,14 @@ export function getContentLibraryDetail(id: string): Promise { // 创建内容库 export function createContentLibrary( - params: CreateContentLibraryParams + params: CreateContentLibraryParams, ): Promise { return request("/v1/content/library/create", params, "POST"); } // 更新内容库 export function updateContentLibrary( - params: UpdateContentLibraryParams + params: UpdateContentLibraryParams, ): Promise { const { id, ...data } = params; return request(`/v1/content/library/update`, { id, ...data }, "POST"); diff --git a/nkebao/src/pages/mobile/content/list/api.ts b/nkebao/src/pages/mobile/content/list/api.ts index 7bb985ee2..51821b6f7 100644 --- a/nkebao/src/pages/mobile/content/list/api.ts +++ b/nkebao/src/pages/mobile/content/list/api.ts @@ -22,14 +22,14 @@ export function getContentLibraryDetail(id: string): Promise { // 创建内容库 export function createContentLibrary( - params: CreateContentLibraryParams + params: CreateContentLibraryParams, ): Promise { return request("/v1/content/library/create", params, "POST"); } // 更新内容库 export function updateContentLibrary( - params: UpdateContentLibraryParams + params: UpdateContentLibraryParams, ): Promise { const { id, ...data } = params; return request(`/v1/content/library/update`, { id, ...data }, "POST"); @@ -43,7 +43,7 @@ export function deleteContentLibrary(id: string): Promise { // 切换内容库状态 export function toggleContentLibraryStatus( id: string, - status: number + status: number, ): Promise { return request("/v1/content/library/update-status", { id, status }, "POST"); } diff --git a/nkebao/src/pages/mobile/content/list/index.tsx b/nkebao/src/pages/mobile/content/list/index.tsx index f3b0eb134..a601710cf 100644 --- a/nkebao/src/pages/mobile/content/list/index.tsx +++ b/nkebao/src/pages/mobile/content/list/index.tsx @@ -182,7 +182,7 @@ const ContentLibraryList: React.FC = () => { const filteredLibraries = libraries.filter( library => library.name.toLowerCase().includes(searchQuery.toLowerCase()) || - library.creatorName?.toLowerCase().includes(searchQuery.toLowerCase()) + library.creatorName?.toLowerCase().includes(searchQuery.toLowerCase()), ); return ( diff --git a/nkebao/src/pages/mobile/home/index.tsx b/nkebao/src/pages/mobile/home/index.tsx index 5ff2672a9..013374a37 100644 --- a/nkebao/src/pages/mobile/home/index.tsx +++ b/nkebao/src/pages/mobile/home/index.tsx @@ -206,8 +206,8 @@ const Home: React.FC = () => { onClick={() => navigate( `/scenarios/list/${scenario.id}/${encodeURIComponent( - scenario.name - )}` + scenario.name, + )}`, ) } > diff --git a/nkebao/src/pages/mobile/mine/consumption-records/api.ts b/nkebao/src/pages/mobile/mine/consumption-records/api.ts new file mode 100644 index 000000000..52f953377 --- /dev/null +++ b/nkebao/src/pages/mobile/mine/consumption-records/api.ts @@ -0,0 +1,17 @@ +import request from "@/api/request"; +import { ConsumptionRecordsResponse, ConsumptionRecordDetail } from "./data"; + +// 获取消费记录列表 +export function getConsumptionRecords(params: { + page: number; + limit: number; +}): Promise { + return request("/v1/consumption-records", params, "GET"); +} + +// 获取消费记录详情 +export function getConsumptionRecordDetail( + id: string, +): Promise { + return request(`/v1/consumption-records/${id}`, {}, "GET"); +} diff --git a/nkebao/src/pages/mobile/mine/consumption-records/data.ts b/nkebao/src/pages/mobile/mine/consumption-records/data.ts new file mode 100644 index 000000000..391b40dda --- /dev/null +++ b/nkebao/src/pages/mobile/mine/consumption-records/data.ts @@ -0,0 +1,26 @@ +// 消费记录类型定义 +export interface ConsumptionRecord { + id: string; + type: "recharge" | "ai_service" | "version_upgrade"; + amount: number; + description: string; + createTime: string; + status: "success" | "pending" | "failed"; + balance?: number; +} + +// API响应类型 +export interface ConsumptionRecordsResponse { + list: ConsumptionRecord[]; + total: number; + page: number; + limit: number; +} + +// 消费记录详情 +export interface ConsumptionRecordDetail extends ConsumptionRecord { + orderNo?: string; + paymentMethod?: string; + remark?: string; + operator?: string; +} diff --git a/nkebao/src/pages/mobile/mine/consumption-records/index.module.scss b/nkebao/src/pages/mobile/mine/consumption-records/index.module.scss new file mode 100644 index 000000000..f244cccce --- /dev/null +++ b/nkebao/src/pages/mobile/mine/consumption-records/index.module.scss @@ -0,0 +1,141 @@ +.records-page { + padding: 16px; + background: #f7f8fa; + min-height: 100vh; +} + +.records-list { + display: flex; + flex-direction: column; + gap: 12px; +} + +.record-card { + border-radius: 12px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); + background: #fff; +} + +.record-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 12px; +} + +.record-info { + display: flex; + align-items: flex-start; + gap: 12px; + flex: 1; +} + +.type-icon-wrapper { + width: 40px; + height: 40px; + border-radius: 8px; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.type-icon { + font-size: 20px; + color: #666; +} + +.record-details { + flex: 1; + min-width: 0; +} + +.record-description { + font-size: 16px; + font-weight: 500; + color: #222; + margin-bottom: 4px; + line-height: 1.4; +} + +.record-time { + display: flex; + align-items: center; + gap: 4px; + font-size: 12px; + color: #999; +} + +.time-icon { + font-size: 12px; +} + +.record-amount { + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 4px; + flex-shrink: 0; +} + +.amount-text { + font-size: 16px; + font-weight: 600; +} + +.status-tag { + font-size: 11px; + padding: 2px 6px; + border-radius: 8px; +} + +.balance-info { + margin-top: 8px; + padding-top: 8px; + border-top: 1px solid #f0f0f0; + font-size: 12px; + color: #666; +} + +.loading-container { + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; + padding: 20px; +} + +.loading-text { + font-size: 14px; + color: #666; +} + +.load-more { + text-align: center; + padding: 16px; + color: var(--primary-color); + font-size: 14px; + font-weight: 500; + cursor: pointer; + background: #fff; + border-radius: 8px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); + transition: background-color 0.2s ease; + + &:hover { + background-color: #f8f9fa; + } + + &:active { + background-color: #e9ecef; + } +} + +.empty-state { + margin-top: 60px; +} + +.empty-icon { + font-size: 48px; + color: #ccc; +} diff --git a/nkebao/src/pages/mobile/mine/consumption-records/index.tsx b/nkebao/src/pages/mobile/mine/consumption-records/index.tsx new file mode 100644 index 000000000..64e20ac58 --- /dev/null +++ b/nkebao/src/pages/mobile/mine/consumption-records/index.tsx @@ -0,0 +1,212 @@ +import React, { useState, useEffect } from "react"; +import { useNavigate } from "react-router-dom"; +import { Card, List, Tag, SpinLoading, Empty } from "antd-mobile"; +import { useUserStore } from "@/store/module/user"; +import style from "./index.module.scss"; +import { + WalletOutlined, + RobotOutlined, + CrownOutlined, + ClockCircleOutlined, +} from "@ant-design/icons"; +import NavCommon from "@/components/NavCommon"; +import Layout from "@/components/Layout/Layout"; +import { getConsumptionRecords } from "./api"; +import { ConsumptionRecord } from "./data"; + +const ConsumptionRecords: React.FC = () => { + const navigate = useNavigate(); + const { user } = useUserStore(); + const [records, setRecords] = useState([]); + const [loading, setLoading] = useState(true); + const [hasMore, setHasMore] = useState(true); + const [page, setPage] = useState(1); + + useEffect(() => { + loadRecords(); + }, []); + + const loadRecords = async (reset = false) => { + if (loading) return; + setLoading(true); + try { + const currentPage = reset ? 1 : page; + const response = await getConsumptionRecords({ + page: currentPage, + limit: 20, + }); + + const newRecords = response.list || []; + setRecords(prev => (reset ? newRecords : [...prev, ...newRecords])); + setHasMore(newRecords.length === 20); + if (reset) setPage(1); + else setPage(currentPage + 1); + } catch (error) { + console.error("加载消费记录失败:", error); + } finally { + setLoading(false); + } + }; + + const getTypeIcon = (type: string) => { + switch (type) { + case "recharge": + return ; + case "ai_service": + return ; + case "version_upgrade": + return ; + default: + return ; + } + }; + + const getTypeColor = (type: string) => { + switch (type) { + case "recharge": + return "#52c41a"; + case "ai_service": + return "#1890ff"; + case "version_upgrade": + return "#722ed1"; + default: + return "#666"; + } + }; + + const getStatusText = (status: string) => { + switch (status) { + case "success": + return "成功"; + case "pending": + return "处理中"; + case "failed": + return "失败"; + default: + return "未知"; + } + }; + + const getStatusColor = (status: string) => { + switch (status) { + case "success": + return "#52c41a"; + case "pending": + return "#faad14"; + case "failed": + return "#ff4d4f"; + default: + return "#666"; + } + }; + + const formatAmount = (amount: number, type: string) => { + if (type === "recharge") { + return `+¥${amount.toFixed(2)}`; + } else { + return `-¥${amount.toFixed(2)}`; + } + }; + + const formatTime = (timeStr: string) => { + const date = new Date(timeStr); + const now = new Date(); + const diff = now.getTime() - date.getTime(); + const days = Math.floor(diff / (1000 * 60 * 60 * 24)); + + if (days === 0) { + return date.toLocaleTimeString("zh-CN", { + hour: "2-digit", + minute: "2-digit", + }); + } else if (days === 1) { + return ( + "昨天 " + + date.toLocaleTimeString("zh-CN", { + hour: "2-digit", + minute: "2-digit", + }) + ); + } else if (days < 7) { + return `${days}天前`; + } else { + return date.toLocaleDateString("zh-CN"); + } + }; + + const renderRecordItem = (record: ConsumptionRecord) => ( + +
+
+
+ {getTypeIcon(record.type)} +
+
+
+ {record.description} +
+
+ + {formatTime(record.createTime)} +
+
+
+
+
+ {formatAmount(record.amount, record.type)} +
+ + {getStatusText(record.status)} + +
+
+ {record.balance !== undefined && ( +
+ 余额: ¥{record.balance.toFixed(2)} +
+ )} +
+ ); + + return ( + }> +
+ {records.length === 0 && !loading ? ( + } + /> + ) : ( +
+ {records.map(renderRecordItem)} + {loading && ( +
+ +
加载中...
+
+ )} + {!loading && hasMore && ( +
loadRecords()}> + 加载更多 +
+ )} +
+ )} +
+
+ ); +}; + +export default ConsumptionRecords; diff --git a/nkebao/src/pages/mobile/mine/devices/DeviceDetail.tsx b/nkebao/src/pages/mobile/mine/devices/DeviceDetail.tsx index 0c8add5dd..b39217b5e 100644 --- a/nkebao/src/pages/mobile/mine/devices/DeviceDetail.tsx +++ b/nkebao/src/pages/mobile/mine/devices/DeviceDetail.tsx @@ -23,7 +23,7 @@ const DeviceDetail: React.FC = () => { const [logs, setLogs] = useState([]); const [logsLoading, setLogsLoading] = useState(false); const [featureSaving, setFeatureSaving] = useState<{ [k: string]: boolean }>( - {} + {}, ); // 获取设备详情 @@ -82,7 +82,7 @@ const DeviceDetail: React.FC = () => { // 功能开关 const handleFeatureChange = async ( feature: keyof Device["features"], - checked: boolean + checked: boolean, ) => { if (!id) return; setFeatureSaving(prev => ({ ...prev, [feature]: true })); @@ -94,7 +94,7 @@ const DeviceDetail: React.FC = () => { ...prev, features: { ...prev.features, [feature]: checked }, } - : prev + : prev, ); Toast.show({ content: `${getFeatureName(feature)}已${checked ? "开启" : "关闭"}`, @@ -219,12 +219,12 @@ const DeviceDetail: React.FC = () => { onChange={checked => handleFeatureChange( f as keyof Device["features"], - checked + checked, ) } />
- ) + ), )}
)} diff --git a/nkebao/src/pages/mobile/mine/recharge/index.module.scss b/nkebao/src/pages/mobile/mine/recharge/index.module.scss index b92276d96..c7e3615a1 100644 --- a/nkebao/src/pages/mobile/mine/recharge/index.module.scss +++ b/nkebao/src/pages/mobile/mine/recharge/index.module.scss @@ -1,11 +1,52 @@ .recharge-page { - padding: 16px 0 60px 0; - background: #f7f8fa; - min-height: 100vh; +} + +.record-btn { + color: var(--primary-color); + font-size: 14px; + font-weight: 500; + cursor: pointer; + padding: 4px 8px; + border-radius: 4px; + transition: background-color 0.2s ease; + + &:hover { + background-color: rgba(24, 142, 238, 0.1); + } + + &:active { + background-color: rgba(24, 142, 238, 0.2); + } +} + +.recharge-tabs { + :global(.adm-tabs-header) { + background: #fff; + border-bottom: 1px solid #f0f0f0; + position: sticky; + top: 0; + z-index: 10; + } + + :global(.adm-tabs-tab) { + font-size: 16px; + font-weight: 500; + } + + :global(.adm-tabs-tab-active) { + color: var(--primary-color); + } + + :global(.adm-tabs-tab-line) { + background: var(--primary-color); + } +} + +.tab-content { } .balance-card { - margin: 16px; + margin-bottom: 16px; background: #f6ffed; border: 1px solid #b7eb8f; border-radius: 12px; @@ -43,24 +84,24 @@ } .quick-card { - margin: 16px; + margin-bottom: 16px; .quick-list { display: flex; flex-wrap: wrap; - gap: 8px; + gap: 6px; justify-content: flex-start; margin-bottom: 8px; } } .desc-card { - margin: 16px; + margin: 16px 0px; background: #fffbe6; border: 1px solid #ffe58f; } .warn-card { - margin: 16px; + margin: 16px 0; background: #fff2e8; border: 1px solid #ffbb96; } @@ -125,3 +166,275 @@ color: #faad14; font-size: 14px; } + +// AI服务样式 +.ai-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 8px; +} + +.ai-title { + display: flex; + align-items: center; + font-size: 20px; + font-weight: 700; + color: #222; +} + +.ai-icon { + font-size: 24px; + color: var(--primary-color); + margin-right: 8px; +} + +.ai-tag { + background: #ff6b35; + color: #fff; + font-size: 12px; + padding: 4px 8px; + border-radius: 12px; + font-weight: 500; +} + +.ai-description { + color: #666; + font-size: 14px; + margin-bottom: 20px; +} + +.ai-services { + display: flex; + flex-direction: column; + gap: 16px; +} + +.ai-service-card { + border-radius: 12px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); +} + +.service-header { + margin-bottom: 12px; +} + +.service-info { + display: flex; + align-items: center; + gap: 12px; +} + +.service-icon { + font-size: 24px; + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + background: #f0f0f0; + border-radius: 8px; +} + +.service-details { + flex: 1; + display: flex; + justify-content: space-between; + align-items: center; +} + +.service-name { + font-size: 16px; + font-weight: 600; + color: #222; +} + +.service-price { + font-size: 16px; + font-weight: 700; + color: #ff4d4f; +} + +.service-description { + color: #666; + font-size: 14px; + margin-bottom: 12px; + line-height: 1.5; +} + +.service-features { + margin-bottom: 16px; +} + +.feature-item { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 6px; + font-size: 14px; + color: #333; +} + +.feature-check { + color: #52c41a; + font-weight: bold; + font-size: 16px; +} + +.usage-progress { + margin-top: 12px; +} + +.usage-label { + font-size: 14px; + color: #666; + margin-bottom: 8px; +} + +.progress-bar { + width: 100%; + height: 6px; + background: #f0f0f0; + border-radius: 3px; + overflow: hidden; + margin-bottom: 6px; +} + +.progress-fill { + height: 100%; + background: var(--primary-color); + border-radius: 3px; + transition: width 0.3s ease; +} + +.usage-text { + font-size: 12px; + color: #999; + text-align: right; +} + +// 版本套餐样式 +.version-header { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 8px; + font-size: 20px; + font-weight: 700; + color: #222; +} + +.version-icon { + font-size: 24px; + color: #722ed1; +} + +.version-description { + color: #666; + font-size: 14px; + margin-bottom: 20px; +} + +.version-packages { + display: flex; + flex-direction: column; + gap: 16px; +} + +.version-card { + border-radius: 12px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); + position: relative; +} + +.package-header { + margin-bottom: 12px; +} + +.package-info { + display: flex; + align-items: center; + gap: 12px; +} + +.package-icon { + font-size: 24px; + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + background: #f0f0f0; + border-radius: 8px; +} + +.package-details { + flex: 1; + display: flex; + justify-content: space-between; + align-items: center; +} + +.package-name { + display: flex; + align-items: center; + gap: 8px; + font-size: 16px; + font-weight: 600; + color: #222; +} + +.package-tag { + font-size: 12px; + padding: 2px 8px; + border-radius: 10px; + font-weight: 500; +} + +.tag-blue { + background: #e6f7ff; + color: #1890ff; +} + +.tag-green { + background: #f6ffed; + color: #52c41a; +} + +.package-price { + font-size: 18px; + font-weight: 700; + color: var(--primary-color); +} + +.package-description { + color: #666; + font-size: 14px; + margin-bottom: 12px; + line-height: 1.5; +} + +.package-features { + margin-bottom: 16px; +} + +.features-title { + font-size: 14px; + font-weight: 600; + color: #333; + margin-bottom: 8px; +} + +.package-status { + text-align: center; + color: #52c41a; + font-size: 14px; + font-weight: 500; + margin-bottom: 12px; +} + +.upgrade-btn { + border-radius: 8px; + font-size: 16px; + font-weight: 600; +} diff --git a/nkebao/src/pages/mobile/mine/recharge/index.tsx b/nkebao/src/pages/mobile/mine/recharge/index.tsx index 52ea5325a..e322aca24 100644 --- a/nkebao/src/pages/mobile/mine/recharge/index.tsx +++ b/nkebao/src/pages/mobile/mine/recharge/index.tsx @@ -1,14 +1,123 @@ import React, { useState } from "react"; import { useNavigate } from "react-router-dom"; -import { Card, Button, Toast, NavBar } from "antd-mobile"; +import { Card, Button, Toast, NavBar, Tabs } from "antd-mobile"; import { useUserStore } from "@/store/module/user"; import style from "./index.module.scss"; -import { WalletOutlined, WarningOutlined } from "@ant-design/icons"; +import { + WalletOutlined, + WarningOutlined, + ClockCircleOutlined, + RobotOutlined, + CrownOutlined, +} from "@ant-design/icons"; import NavCommon from "@/components/NavCommon"; import Layout from "@/components/Layout/Layout"; const quickAmounts = [50, 100, 200, 500, 1000]; +// AI服务套餐数据 +const aiServicePackages = [ + { + id: 1, + name: "入门套餐", + tag: "推荐", + tagColor: "blue", + description: "适合个人用户体验AI服务", + usage: "可使用AI服务约110次", + price: 100, + originalPrice: 110, + gift: 10, + actualAmount: 110, + }, + { + id: 2, + name: "标准套餐", + tag: "热门", + tagColor: "green", + description: "适合小团队日常使用", + usage: "可使用AI服务约580次", + price: 500, + originalPrice: 580, + gift: 80, + actualAmount: 580, + }, +]; + +// AI服务列表数据 +const aiServices = [ + { + id: 1, + name: "添加好友及打招呼", + icon: "💬", + price: 1, + description: "AI智能添加好友并发送个性化打招呼消息", + features: ["智能筛选目标用户", "发送个性化打招呼消息", "自动记录添加结果"], + usage: { current: 15, total: 450 }, + }, + { + id: 2, + name: "小室AI内容生产", + icon: "⚡", + price: 1, + description: "AI智能创建朋友圈内容,智能配文与朋友圈内容", + features: ["智能生成朋友圈文案", "AI配文智能文案", "内容智能排版优化"], + usage: { current: 28, total: 680 }, + }, + { + id: 3, + name: "智能分发服务", + icon: "📤", + price: 1, + description: "AI智能分发内容到多个平台", + features: ["多平台智能分发", "内容智能优化", "分发效果分析"], + usage: { current: 12, total: 300 }, + }, +]; + +// 版本套餐数据 +const versionPackages = [ + { + id: 1, + name: "普通版本", + icon: "📦", + price: "免费", + description: "充值即可使用,包含基础AI功能", + features: ["基础AI服务", "标准客服支持", "基础数据统计"], + status: "当前使用中", + buttonText: null, + tagColor: undefined, + }, + { + id: 2, + name: "标准版本", + icon: "👑", + price: "¥98/月", + tag: "推荐", + tagColor: "blue", + description: "适合中小企业,AI功能更丰富", + features: ["高级AI服务", "优先客服支持", "详细数据分析", "API接口访问"], + status: null, + buttonText: "立即升级", + }, + { + id: 3, + name: "企业版本", + icon: "🏢", + price: "¥1980/月", + description: "适合大型企业,提供专属服务", + features: [ + "专属AI服务", + "24小时专属客服", + "高级数据分析", + "API接口访问", + "专属技术支持", + ], + status: null, + buttonText: "立即升级", + tagColor: undefined, + }, +]; + const Recharge: React.FC = () => { const navigate = useNavigate(); const { user } = useUserStore(); @@ -16,6 +125,7 @@ const Recharge: React.FC = () => { const [balance, setBalance] = useState(0); const [selected, setSelected] = useState(null); const [loading, setLoading] = useState(false); + const [activeTab, setActiveTab] = useState("account"); // 充值操作 const handleRecharge = async () => { @@ -31,68 +141,228 @@ const Recharge: React.FC = () => { }, 1200); }; - return ( - }> -
- -
- -
-
当前余额
-
- ¥{balance.toFixed(2)} + // 渲染账户充值tab内容 + const renderAccountRecharge = () => ( +
+ +
+ +
+
当前余额
+
+ ¥{balance.toFixed(2)} +
+
+
+
+ +
快捷充值
+
+ {quickAmounts.map(amt => ( + + ))} +
+ +
+ +
服务消耗
+
+ 使用以下服务将从余额中扣除相应费用。 +
+
+ {balance < 10 && ( + +
+ +
+
余额不足提醒
+
+ 当前余额较低,建议及时充值以免影响服务使用
- -
快捷充值
-
- {quickAmounts.map(amt => ( - - ))} -
- -
- -
服务消耗
-
- 使用以下服务将从余额中扣除相应费用。 -
-
- {balance < 10 && ( - -
- -
-
余额不足提醒
-
- 当前余额较低,建议及时充值以免影响服务使用 + )} +
+ ); + + // 渲染AI服务tab内容 + const renderAiServices = () => ( +
+
+
+ + AI智能服务收费 +
+
统一按次收费
+
+
+ 三项核心AI服务,按使用次数收费,每次1元 +
+ +
+ {aiServices.map(service => ( + +
+
+
{service.icon}
+
+
{service.name}
+
+ ¥{service.price}/次 +
+
+ {service.description} +
+
+ {service.features.map((feature, index) => ( +
+ + {feature} +
+ ))} +
+
+
今日使用进度
+
+
+
+
+ {service.usage.current} / {service.usage.total} +
+
- )} + ))} +
+
+ ); + + // 渲染版本套餐tab内容 + const renderVersionPackages = () => ( +
+
+ + 存客宝版本套餐 +
+
+ 选择适合的版本,享受不同级别的AI服务 +
+ +
+ {versionPackages.map(pkg => ( + +
+
+
{pkg.icon}
+
+
+ {pkg.name} + {pkg.tag && ( + + {pkg.tag} + + )} +
+
{pkg.price}
+
+
+
+
+ {pkg.description} +
+
+
包含功能:
+ {pkg.features.map((feature, index) => ( +
+ + {feature} +
+ ))} +
+ {pkg.status && ( +
{pkg.status}
+ )} + {pkg.buttonText && ( + + )} +
+ ))} +
+
+ ); + + return ( + navigate("/mine/consumption-records")} + > + +  记录 +
+ } + /> + } + > +
+ + + {renderAccountRecharge()} + + + {renderAiServices()} + + + {renderVersionPackages()} + +
); diff --git a/nkebao/src/pages/mobile/mine/traffic-pool/detail/api.ts b/nkebao/src/pages/mobile/mine/traffic-pool/detail/api.ts index dfad51951..0a0a1e92c 100644 --- a/nkebao/src/pages/mobile/mine/traffic-pool/detail/api.ts +++ b/nkebao/src/pages/mobile/mine/traffic-pool/detail/api.ts @@ -6,7 +6,7 @@ import type { } from "./data"; export function getTrafficPoolDetail( - wechatId: string + wechatId: string, ): Promise { return request("/v1/wechats/getWechatInfo", { wechatId }, "GET"); } diff --git a/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.tsx b/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.tsx index f478fdfc4..88fd1c415 100644 --- a/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.tsx +++ b/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.tsx @@ -520,7 +520,7 @@ const TrafficPoolDetail: React.FC = () => { {restriction.reason || "未知原因"} { setSelectedIds(prev => - checked ? [...prev, id] : prev.filter(i => i !== id) + checked ? [...prev, id] : prev.filter(i => i !== id), ); }; diff --git a/nkebao/src/pages/mobile/mine/wechat-accounts/detail/api.ts b/nkebao/src/pages/mobile/mine/wechat-accounts/detail/api.ts index 0cb792811..d98ec6d2f 100644 --- a/nkebao/src/pages/mobile/mine/wechat-accounts/detail/api.ts +++ b/nkebao/src/pages/mobile/mine/wechat-accounts/detail/api.ts @@ -19,7 +19,7 @@ export function getWechatFriends(params: { limit: params.limit, keyword: params.keyword, }, - "GET" + "GET", ); } diff --git a/nkebao/src/pages/mobile/mine/wechat-accounts/detail/index.tsx b/nkebao/src/pages/mobile/mine/wechat-accounts/detail/index.tsx index 83666dbcd..25271b237 100644 --- a/nkebao/src/pages/mobile/mine/wechat-accounts/detail/index.tsx +++ b/nkebao/src/pages/mobile/mine/wechat-accounts/detail/index.tsx @@ -133,7 +133,7 @@ const WechatAccountDetail: React.FC = () => { setIsFetchingFriends(false); } }, - [id] + [id], ); // 搜索好友 @@ -153,7 +153,7 @@ const WechatAccountDetail: React.FC = () => { setFriendsPage(page); fetchFriendsList(page, searchQuery); }, - [searchQuery, fetchFriendsList] + [searchQuery, fetchFriendsList], ); // 初始化数据 diff --git a/nkebao/src/pages/mobile/scenarios/list/index.tsx b/nkebao/src/pages/mobile/scenarios/list/index.tsx index eb21d3e28..6fc5d8da0 100644 --- a/nkebao/src/pages/mobile/scenarios/list/index.tsx +++ b/nkebao/src/pages/mobile/scenarios/list/index.tsx @@ -69,7 +69,7 @@ const Scene: React.FC = () => { const handleScenarioClick = (scenarioId: string, scenarioName: string) => { navigate( - `/scenarios/list/${scenarioId}/${encodeURIComponent(scenarioName)}` + `/scenarios/list/${scenarioId}/${encodeURIComponent(scenarioName)}`, ); }; diff --git a/nkebao/src/pages/mobile/scenarios/plan/list/index.tsx b/nkebao/src/pages/mobile/scenarios/plan/list/index.tsx index 68db5ee5b..f1bd371c3 100644 --- a/nkebao/src/pages/mobile/scenarios/plan/list/index.tsx +++ b/nkebao/src/pages/mobile/scenarios/plan/list/index.tsx @@ -211,7 +211,7 @@ const ScenarioList: React.FC = () => { if (response) { // 处理webhook URL,使用工具函数构建完整地址 const webhookUrl = buildApiUrl( - response.textUrl?.fullUrl || `webhook/${taskId}` + response.textUrl?.fullUrl || `webhook/${taskId}`, ); setCurrentApiSettings({ @@ -286,7 +286,7 @@ const ScenarioList: React.FC = () => { }; const filteredTasks = tasks.filter(task => - task.name.toLowerCase().includes(searchTerm.toLowerCase()) + task.name.toLowerCase().includes(searchTerm.toLowerCase()), ); // 生成操作菜单 @@ -537,7 +537,7 @@ const ScenarioList: React.FC = () => { {item.icon} {item.text}
- ) + ), )}
diff --git a/nkebao/src/pages/mobile/scenarios/plan/list/planApi.tsx b/nkebao/src/pages/mobile/scenarios/plan/list/planApi.tsx index 386cf58a3..ee67b59c3 100644 --- a/nkebao/src/pages/mobile/scenarios/plan/list/planApi.tsx +++ b/nkebao/src/pages/mobile/scenarios/plan/list/planApi.tsx @@ -317,7 +317,7 @@ HttpResponse response = client.send(request, HttpResponse.BodyHandlers.o onClick={() => handleCopy( codeExamples[activeLanguage as keyof typeof codeExamples], - "代码" + "代码", ) } className={style["copy-code-btn"]} diff --git a/nkebao/src/pages/mobile/scenarios/plan/new/index.tsx b/nkebao/src/pages/mobile/scenarios/plan/new/index.tsx index 013b0218b..f75c6cc7c 100644 --- a/nkebao/src/pages/mobile/scenarios/plan/new/index.tsx +++ b/nkebao/src/pages/mobile/scenarios/plan/new/index.tsx @@ -146,7 +146,7 @@ export default function NewPlan() { ? error : isEdit ? "更新计划失败,请重试" - : "创建计划失败,请重试" + : "创建计划失败,请重试", ); } }; diff --git a/nkebao/src/pages/mobile/scenarios/plan/new/steps/BasicSettings.tsx b/nkebao/src/pages/mobile/scenarios/plan/new/steps/BasicSettings.tsx index 40431a5e7..eaec26cea 100644 --- a/nkebao/src/pages/mobile/scenarios/plan/new/steps/BasicSettings.tsx +++ b/nkebao/src/pages/mobile/scenarios/plan/new/steps/BasicSettings.tsx @@ -105,10 +105,10 @@ const BasicSettings: React.FC = ({ const [accounts] = useState(generateRandomAccounts(50)); const [materials] = useState(generatePosterMaterials()); const [selectedAccounts, setSelectedAccounts] = useState( - formData.accounts?.length > 0 ? formData.accounts : [] + formData.accounts?.length > 0 ? formData.accounts : [], ); const [selectedMaterials, setSelectedMaterials] = useState( - formData.materials?.length > 0 ? formData.materials : [] + formData.materials?.length > 0 ? formData.materials : [], ); // showAllScenarios 默认为 true const [showAllScenarios, setShowAllScenarios] = useState(true); @@ -128,7 +128,7 @@ const BasicSettings: React.FC = ({ const [customTags, setCustomTags] = useState(formData.customTags || []); const [tips, setTips] = useState(formData.tips || ""); const [selectedScenarioTags, setSelectedScenarioTags] = useState( - formData.scenarioTags || [] + formData.scenarioTags || [], ); // 电话获客相关状态 @@ -140,10 +140,10 @@ const BasicSettings: React.FC = ({ // 群设置相关状态 const [weixinqunName, setWeixinqunName] = useState( - formData.weixinqunName || "" + formData.weixinqunName || "", ); const [weixinqunNotice, setWeixinqunNotice] = useState( - formData.weixinqunNotice || "" + formData.weixinqunNotice || "", ); // 新增:自定义海报相关状态 @@ -232,7 +232,7 @@ const BasicSettings: React.FC = ({ onChange({ ...formData, customTags: updatedCustomTags }); // 同时从选中标签中移除 const updatedSelectedTags = selectedScenarioTags.filter( - (t: string) => t !== tagId + (t: string) => t !== tagId, ); setSelectedScenarioTags(updatedSelectedTags); onChange({ @@ -292,12 +292,12 @@ const BasicSettings: React.FC = ({ // 账号多选切换 const handleAccountToggle = (account: Account) => { const isSelected = selectedAccounts.some( - (a: Account) => a.id === account.id + (a: Account) => a.id === account.id, ); let newSelected; if (isSelected) { newSelected = selectedAccounts.filter( - (a: Account) => a.id !== account.id + (a: Account) => a.id !== account.id, ); } else { newSelected = [...selectedAccounts, account]; @@ -362,7 +362,7 @@ const BasicSettings: React.FC = ({ const [orderUploaded, setOrderUploaded] = useState(false); const handleOrderFileUpload = async ( - event: React.ChangeEvent + event: React.ChangeEvent, ) => { const file = event.target.files?.[0]; if (file) { @@ -518,7 +518,7 @@ const BasicSettings: React.FC = ({
{[...materials, ...customPosters].map(material => { const isSelected = selectedMaterials.some( - m => m.id === material.id + m => m.id === material.id, ); const isCustom = material.id.startsWith("custom-"); return ( diff --git a/nkebao/src/pages/mobile/scenarios/plan/new/steps/FriendRequestSettings.tsx b/nkebao/src/pages/mobile/scenarios/plan/new/steps/FriendRequestSettings.tsx index 42b89d4ff..01775fcc6 100644 --- a/nkebao/src/pages/mobile/scenarios/plan/new/steps/FriendRequestSettings.tsx +++ b/nkebao/src/pages/mobile/scenarios/plan/new/steps/FriendRequestSettings.tsx @@ -38,7 +38,7 @@ const FriendRequestSettings: React.FC = ({ const [isTemplateDialogOpen, setIsTemplateDialogOpen] = useState(false); const [hasWarnings, setHasWarnings] = useState(false); const [selectedDevices, setSelectedDevices] = useState( - formData.device || [] + formData.device || [], ); const [showRemarkTip, setShowRemarkTip] = useState(false); diff --git a/nkebao/src/pages/mobile/scenarios/plan/new/steps/MessageSettings.tsx b/nkebao/src/pages/mobile/scenarios/plan/new/steps/MessageSettings.tsx index 29d7d1207..6ce86565f 100644 --- a/nkebao/src/pages/mobile/scenarios/plan/new/steps/MessageSettings.tsx +++ b/nkebao/src/pages/mobile/scenarios/plan/new/steps/MessageSettings.tsx @@ -125,7 +125,7 @@ const MessageSettings: React.FC = ({ const handleUpdateMessage = ( dayIndex: number, messageIndex: number, - updates: Partial + updates: Partial, ) => { const updatedPlans = [...dayPlans]; updatedPlans[dayIndex].messages[messageIndex] = { @@ -181,7 +181,7 @@ const MessageSettings: React.FC = ({ setSelectedGroupId(groupId); setIsGroupSelectOpen(false); message.success( - `已选择群组:${mockGroups.find(g => g.id === groupId)?.name}` + `已选择群组:${mockGroups.find(g => g.id === groupId)?.name}`, ); }; @@ -189,7 +189,7 @@ const MessageSettings: React.FC = ({ const triggerUpload = ( dayIdx: number, msgIdx: number, - type: "miniprogram" | "link" + type: "miniprogram" | "link", ) => { setUploadingDay(dayIdx); setUploadingMsgIdx(msgIdx); @@ -539,7 +539,7 @@ const MessageSettings: React.FC = ({ handleFileUpload( dayIndex, messageIndex, - message.type as any + message.type as any, ) } > diff --git a/nkebao/src/pages/mobile/scenarios/plan/new/steps/step.api.ts b/nkebao/src/pages/mobile/scenarios/plan/new/steps/step.api.ts index 1f5a5150d..8c68b53fc 100644 --- a/nkebao/src/pages/mobile/scenarios/plan/new/steps/step.api.ts +++ b/nkebao/src/pages/mobile/scenarios/plan/new/steps/step.api.ts @@ -33,7 +33,7 @@ export function deleteScenario(id: string) { export function getPlanList( scenarioId: string, page: number = 1, - limit: number = 20 + limit: number = 20, ) { return request(`/api/scenarios/${scenarioId}/plans`, { page, limit }, "GET"); } @@ -214,7 +214,7 @@ export function deleteAutoLikeTask(taskId: string) { return request( `/api/workspace/auto-like/tasks/${taskId}`, undefined, - "DELETE" + "DELETE", ); } @@ -240,7 +240,7 @@ export function deleteGroupPushTask(taskId: string) { return request( `/api/workspace/group-push/tasks/${taskId}`, undefined, - "DELETE" + "DELETE", ); } @@ -266,7 +266,7 @@ export function deleteAutoGroupTask(taskId: string) { return request( `/api/workspace/auto-group/tasks/${taskId}`, undefined, - "DELETE" + "DELETE", ); } @@ -287,7 +287,7 @@ export function getAIAnalysisReport() { return request( "/api/workspace/ai-assistant/analysis-report", undefined, - "GET" + "GET", ); } @@ -379,6 +379,6 @@ export function markNotificationAsRead(notificationId: string) { return request( `/api/system/notifications/${notificationId}/read`, undefined, - "PUT" + "PUT", ); } diff --git a/nkebao/src/pages/mobile/workspace/auto-group/detail/index.tsx b/nkebao/src/pages/mobile/workspace/auto-group/detail/index.tsx index 73426064c..a0d88e7d8 100644 --- a/nkebao/src/pages/mobile/workspace/auto-group/detail/index.tsx +++ b/nkebao/src/pages/mobile/workspace/auto-group/detail/index.tsx @@ -59,7 +59,7 @@ const mockTaskDetail: GroupTaskDetail = { nickname: `用户${mIndex + 1}`, wechatId: `wx_${mIndex}`, tags: [`标签${(mIndex % 3) + 1}`], - }) + }), ), })), createTime: "2024-11-20 19:04:14", @@ -169,10 +169,10 @@ const GroupCreationProgress: React.FC<{ }> = ({ taskDetail, onComplete }) => { const [groups, setGroups] = useState(taskDetail.groups); const [currentGroupIndex, setCurrentGroupIndex] = useState( - taskDetail.currentGroupIndex + taskDetail.currentGroupIndex, ); const [status, setStatus] = useState( - taskDetail.status + taskDetail.status, ); useEffect(() => { @@ -207,7 +207,7 @@ const GroupCreationProgress: React.FC<{ }; } return group; - }) + }), ); }; diff --git a/nkebao/src/pages/mobile/workspace/auto-group/list/index.tsx b/nkebao/src/pages/mobile/workspace/auto-group/list/index.tsx index 0ef828615..1b0a9094e 100644 --- a/nkebao/src/pages/mobile/workspace/auto-group/list/index.tsx +++ b/nkebao/src/pages/mobile/workspace/auto-group/list/index.tsx @@ -149,8 +149,8 @@ const AutoGroupList: React.FC = () => { ...task, status: task.status === "running" ? "paused" : "running", } - : task - ) + : task, + ), ); Toast.show({ content: "状态已切换" }); }; @@ -160,7 +160,7 @@ const AutoGroupList: React.FC = () => { }; const filteredTasks = tasks.filter(task => - task.name.toLowerCase().includes(searchTerm.toLowerCase()) + task.name.toLowerCase().includes(searchTerm.toLowerCase()), ); return ( diff --git a/nkebao/src/pages/mobile/workspace/auto-like/list/api.ts b/nkebao/src/pages/mobile/workspace/auto-like/list/api.ts index 23b89dc1c..7f828d56a 100644 --- a/nkebao/src/pages/mobile/workspace/auto-like/list/api.ts +++ b/nkebao/src/pages/mobile/workspace/auto-like/list/api.ts @@ -9,7 +9,7 @@ import { // 获取自动点赞任务列表 export function fetchAutoLikeTasks( - params = { type: 1, page: 1, limit: 100 } + params = { type: 1, page: 1, limit: 100 }, ): Promise { return request("/v1/workbench/list", params, "GET"); } @@ -49,7 +49,7 @@ export function fetchLikeRecords( workbenchId: string, page: number = 1, limit: number = 20, - keyword?: string + keyword?: string, ): Promise> { const params: any = { workbenchId, diff --git a/nkebao/src/pages/mobile/workspace/auto-like/list/index.tsx b/nkebao/src/pages/mobile/workspace/auto-like/list/index.tsx index 33cbb242a..a4910269f 100644 --- a/nkebao/src/pages/mobile/workspace/auto-like/list/index.tsx +++ b/nkebao/src/pages/mobile/workspace/auto-like/list/index.tsx @@ -224,7 +224,7 @@ const AutoLike: React.FC = () => { // 过滤任务 const filteredTasks = tasks.filter(task => - task.name.toLowerCase().includes(searchTerm.toLowerCase()) + task.name.toLowerCase().includes(searchTerm.toLowerCase()), ); return ( diff --git a/nkebao/src/pages/mobile/workspace/auto-like/new/index.tsx b/nkebao/src/pages/mobile/workspace/auto-like/new/index.tsx index 4443b09b1..56cd6a5a6 100644 --- a/nkebao/src/pages/mobile/workspace/auto-like/new/index.tsx +++ b/nkebao/src/pages/mobile/workspace/auto-like/new/index.tsx @@ -82,7 +82,7 @@ const NewAutoLike: React.FC = () => { }); setAutoEnabled( (taskDetail as any).status === 1 || - (taskDetail as any).status === "running" + (taskDetail as any).status === "running", ); } } catch (error) { diff --git a/nkebao/src/pages/mobile/workspace/auto-like/record/api.ts b/nkebao/src/pages/mobile/workspace/auto-like/record/api.ts index 81a7c26be..6142fde86 100644 --- a/nkebao/src/pages/mobile/workspace/auto-like/record/api.ts +++ b/nkebao/src/pages/mobile/workspace/auto-like/record/api.ts @@ -9,7 +9,7 @@ import { // 获取自动点赞任务列表 export function fetchAutoLikeTasks( - params = { type: 1, page: 1, limit: 100 } + params = { type: 1, page: 1, limit: 100 }, ): Promise { return request("/v1/workbench/list", params, "GET"); } @@ -49,7 +49,7 @@ export function fetchLikeRecords( workbenchId: string, page: number = 1, limit: number = 20, - keyword?: string + keyword?: string, ): Promise> { const params: any = { workbenchId, diff --git a/nkebao/src/pages/mobile/workspace/group-push/detail/groupPush.ts b/nkebao/src/pages/mobile/workspace/group-push/detail/groupPush.ts index 8692db6e0..0295e2f96 100644 --- a/nkebao/src/pages/mobile/workspace/group-push/detail/groupPush.ts +++ b/nkebao/src/pages/mobile/workspace/group-push/detail/groupPush.ts @@ -40,12 +40,12 @@ export async function deleteGroupPushTask(id: string): Promise { export async function toggleGroupPushTask( id: string, - status: string + status: string, ): Promise { return request( `/v1/workspace/group-push/tasks/${id}/toggle`, { status }, - "POST" + "POST", ); } @@ -54,20 +54,20 @@ export async function copyGroupPushTask(id: string): Promise { } export async function createGroupPushTask( - taskData: Partial + taskData: Partial, ): Promise { return request("/v1/workspace/group-push/tasks", taskData, "POST"); } export async function updateGroupPushTask( id: string, - taskData: Partial + taskData: Partial, ): Promise { return request(`/v1/workspace/group-push/tasks/${id}`, taskData, "PUT"); } export async function getGroupPushTaskDetail( - id: string + id: string, ): Promise { return request(`/v1/workspace/group-push/tasks/${id}`); } diff --git a/nkebao/src/pages/mobile/workspace/group-push/detail/index.tsx b/nkebao/src/pages/mobile/workspace/group-push/detail/index.tsx index 7ab430491..ab2a9a0f2 100644 --- a/nkebao/src/pages/mobile/workspace/group-push/detail/index.tsx +++ b/nkebao/src/pages/mobile/workspace/group-push/detail/index.tsx @@ -226,7 +226,7 @@ const Detail: React.FC = () => {
diff --git a/nkebao/src/pages/mobile/workspace/group-push/form/components/BasicSettings.tsx b/nkebao/src/pages/mobile/workspace/group-push/form/components/BasicSettings.tsx index c2d98bd84..34ba3f9ed 100644 --- a/nkebao/src/pages/mobile/workspace/group-push/form/components/BasicSettings.tsx +++ b/nkebao/src/pages/mobile/workspace/group-push/form/components/BasicSettings.tsx @@ -105,7 +105,7 @@ const BasicSettings: React.FC = ({ onChange={e => handleChange( "dailyPushCount", - Number.parseInt(e.target.value) || 1 + Number.parseInt(e.target.value) || 1, ) } style={{ width: 80, textAlign: "center" }} diff --git a/nkebao/src/pages/mobile/workspace/group-push/form/components/ContentSelector.tsx b/nkebao/src/pages/mobile/workspace/group-push/form/components/ContentSelector.tsx index 2d35916e6..ebcb1b529 100644 --- a/nkebao/src/pages/mobile/workspace/group-push/form/components/ContentSelector.tsx +++ b/nkebao/src/pages/mobile/workspace/group-push/form/components/ContentSelector.tsx @@ -81,7 +81,7 @@ const ContentSelector: React.FC = ({ const [libraries] = useState(mockLibraries); const filteredLibraries = libraries.filter(library => - library.name.toLowerCase().includes(searchTerm.toLowerCase()) + library.name.toLowerCase().includes(searchTerm.toLowerCase()), ); const handleLibraryToggle = (library: ContentLibrary, checked: boolean) => { diff --git a/nkebao/src/pages/mobile/workspace/group-push/form/components/GroupSelector.tsx b/nkebao/src/pages/mobile/workspace/group-push/form/components/GroupSelector.tsx index 016e76f6d..c75138458 100644 --- a/nkebao/src/pages/mobile/workspace/group-push/form/components/GroupSelector.tsx +++ b/nkebao/src/pages/mobile/workspace/group-push/form/components/GroupSelector.tsx @@ -101,7 +101,9 @@ const GroupSelector: React.FC = ({ const filteredGroups = groups.filter( group => group.name.toLowerCase().includes(searchTerm.toLowerCase()) || - group.serviceAccount.name.toLowerCase().includes(searchTerm.toLowerCase()) + group.serviceAccount.name + .toLowerCase() + .includes(searchTerm.toLowerCase()), ); const handleGroupToggle = (group: WechatGroup, checked: boolean) => { diff --git a/nkebao/src/pages/mobile/workspace/group-push/form/index.api.ts b/nkebao/src/pages/mobile/workspace/group-push/form/index.api.ts index 7b4b9ecce..15384c115 100644 --- a/nkebao/src/pages/mobile/workspace/group-push/form/index.api.ts +++ b/nkebao/src/pages/mobile/workspace/group-push/form/index.api.ts @@ -1,6 +1,6 @@ import request from "@/api/request"; export async function createGroupPushTask( - taskData: Partial + taskData: Partial, ): Promise { return request("/v1/workspace/group-push/tasks", taskData, "POST"); } diff --git a/nkebao/src/pages/mobile/workspace/group-push/list/index.api.ts b/nkebao/src/pages/mobile/workspace/group-push/list/index.api.ts index 8692db6e0..0295e2f96 100644 --- a/nkebao/src/pages/mobile/workspace/group-push/list/index.api.ts +++ b/nkebao/src/pages/mobile/workspace/group-push/list/index.api.ts @@ -40,12 +40,12 @@ export async function deleteGroupPushTask(id: string): Promise { export async function toggleGroupPushTask( id: string, - status: string + status: string, ): Promise { return request( `/v1/workspace/group-push/tasks/${id}/toggle`, { status }, - "POST" + "POST", ); } @@ -54,20 +54,20 @@ export async function copyGroupPushTask(id: string): Promise { } export async function createGroupPushTask( - taskData: Partial + taskData: Partial, ): Promise { return request("/v1/workspace/group-push/tasks", taskData, "POST"); } export async function updateGroupPushTask( id: string, - taskData: Partial + taskData: Partial, ): Promise { return request(`/v1/workspace/group-push/tasks/${id}`, taskData, "PUT"); } export async function getGroupPushTaskDetail( - id: string + id: string, ): Promise { return request(`/v1/workspace/group-push/tasks/${id}`); } diff --git a/nkebao/src/pages/mobile/workspace/group-push/list/index.tsx b/nkebao/src/pages/mobile/workspace/group-push/list/index.tsx index daf048be0..7634c769f 100644 --- a/nkebao/src/pages/mobile/workspace/group-push/list/index.tsx +++ b/nkebao/src/pages/mobile/workspace/group-push/list/index.tsx @@ -97,7 +97,7 @@ const GroupPush: React.FC = () => { }; const filteredTasks = tasks.filter(task => - task.name.toLowerCase().includes(searchTerm.toLowerCase()) + task.name.toLowerCase().includes(searchTerm.toLowerCase()), ); const getStatusColor = (status: number) => { @@ -361,7 +361,7 @@ const GroupPush: React.FC = () => {
diff --git a/nkebao/src/pages/mobile/workspace/moments-sync/Detail.tsx b/nkebao/src/pages/mobile/workspace/moments-sync/Detail.tsx index 5f3b6bff8..3b1f0ffd3 100644 --- a/nkebao/src/pages/mobile/workspace/moments-sync/Detail.tsx +++ b/nkebao/src/pages/mobile/workspace/moments-sync/Detail.tsx @@ -73,7 +73,7 @@ const MomentsSyncDetail: React.FC = () => { await request( "/v1/workbench/update-status", { id, status: newStatus }, - "POST" + "POST", ); setTask({ ...task, status: newStatus }); message.success(newStatus === 1 ? "任务已开启" : "任务已暂停"); diff --git a/nkebao/src/pages/mobile/workspace/moments-sync/MomentsSync.tsx b/nkebao/src/pages/mobile/workspace/moments-sync/MomentsSync.tsx index 0815f9548..15c5bcfc8 100644 --- a/nkebao/src/pages/mobile/workspace/moments-sync/MomentsSync.tsx +++ b/nkebao/src/pages/mobile/workspace/moments-sync/MomentsSync.tsx @@ -55,7 +55,7 @@ const MomentsSync: React.FC = () => { const res = await request( "/v1/workbench/list", { type: 2, page: 1, limit: 100 }, - "GET" + "GET", ); setTasks(res.list || []); } catch (e) { @@ -96,10 +96,10 @@ const MomentsSync: React.FC = () => { await request( "/v1/workbench/update-status", { id, status: newStatus }, - "POST" + "POST", ); setTasks(prev => - prev.map(t => (t.id === id ? { ...t, status: newStatus } : t)) + prev.map(t => (t.id === id ? { ...t, status: newStatus } : t)), ); message.success("操作成功"); } catch { @@ -108,7 +108,7 @@ const MomentsSync: React.FC = () => { }; const filteredTasks = tasks.filter(task => - task.name.toLowerCase().includes(searchTerm.toLowerCase()) + task.name.toLowerCase().includes(searchTerm.toLowerCase()), ); // 菜单 diff --git a/nkebao/src/pages/mobile/workspace/traffic-distribution/form/api.ts b/nkebao/src/pages/mobile/workspace/traffic-distribution/form/api.ts index 4c8b968b5..db35a752f 100644 --- a/nkebao/src/pages/mobile/workspace/traffic-distribution/form/api.ts +++ b/nkebao/src/pages/mobile/workspace/traffic-distribution/form/api.ts @@ -8,14 +8,14 @@ export const getTrafficDistributionDetail = (id: string) => { // 更新流量分发 export const updateTrafficDistribution = ( - data: TrafficDistributionFormData + data: TrafficDistributionFormData, ) => { return request("/v1/workbench/update", data, "POST"); }; // 创建流量分发 export const createTrafficDistribution = ( - data: TrafficDistributionFormData + data: TrafficDistributionFormData, ) => { return request("/v1/workbench/create", data, "POST"); }; diff --git a/nkebao/src/pages/mobile/workspace/traffic-distribution/form/index.tsx b/nkebao/src/pages/mobile/workspace/traffic-distribution/form/index.tsx index 7ebd417fe..5155245cc 100644 --- a/nkebao/src/pages/mobile/workspace/traffic-distribution/form/index.tsx +++ b/nkebao/src/pages/mobile/workspace/traffic-distribution/form/index.tsx @@ -393,7 +393,7 @@ const TrafficDistributionForm: React.FC = () => { setSelectedPools(val => e.target.checked ? [...val, pool.id] - : val.filter(v => v !== pool.id) + : val.filter(v => v !== pool.id), ); }} /> diff --git a/nkebao/src/pages/mobile/workspace/traffic-distribution/list/api.ts b/nkebao/src/pages/mobile/workspace/traffic-distribution/list/api.ts index 0a071cfd4..ceacca1c5 100644 --- a/nkebao/src/pages/mobile/workspace/traffic-distribution/list/api.ts +++ b/nkebao/src/pages/mobile/workspace/traffic-distribution/list/api.ts @@ -17,7 +17,7 @@ export function updateDistributionRule(data: any): Promise { // 暂停/启用计划 export function toggleDistributionRuleStatus( id: number, - status: 0 | 1 + status: 0 | 1, ): Promise { return request("/v1/workbench/update-status", { id, status }, "POST"); } diff --git a/nkebao/src/pages/mobile/workspace/traffic-distribution/list/index.tsx b/nkebao/src/pages/mobile/workspace/traffic-distribution/list/index.tsx index d38018ab8..5e9fd5a81 100644 --- a/nkebao/src/pages/mobile/workspace/traffic-distribution/list/index.tsx +++ b/nkebao/src/pages/mobile/workspace/traffic-distribution/list/index.tsx @@ -115,7 +115,7 @@ const TrafficDistributionList: React.FC = () => { // 新增:Switch点击切换计划状态 const handleSwitchChange = async ( checked: boolean, - item: DistributionRule + item: DistributionRule, ) => { setMenuLoadingId(item.id); try { @@ -124,8 +124,8 @@ const TrafficDistributionList: React.FC = () => { // 本地只更新当前item的status,不刷新全列表 setList(prevList => prevList.map(rule => - rule.id === item.id ? { ...rule, status: checked ? 1 : 0 } : rule - ) + rule.id === item.id ? { ...rule, status: checked ? 1 : 0 } : rule, + ), ); } catch (e) { message.error("操作失败"); diff --git a/nkebao/src/router/config.ts b/nkebao/src/router/config.ts index 136e3cb59..ef1d69020 100644 --- a/nkebao/src/router/config.ts +++ b/nkebao/src/router/config.ts @@ -159,7 +159,7 @@ export const getRouteTitle = (path: string): string => { // 检查路由权限 export const checkRoutePermission = ( path: string, - userRole: string = "user" + userRole: string = "user", ): boolean => { const allowedRoutes = routePermissions[userRole as keyof typeof routePermissions] || []; diff --git a/nkebao/src/router/index.tsx b/nkebao/src/router/index.tsx index 4a02716eb..117681a75 100644 --- a/nkebao/src/router/index.tsx +++ b/nkebao/src/router/index.tsx @@ -16,7 +16,7 @@ Object.values(modules).forEach((mod: any) => { // 权限包装 function wrapWithPermission( - route: RouteObject & { auth?: boolean; requiredRole?: string } + route: RouteObject & { auth?: boolean; requiredRole?: string }, ) { if (route.auth) { return { diff --git a/nkebao/src/store/createPersistStore.ts b/nkebao/src/store/createPersistStore.ts index cf2c80c71..79d7b6347 100644 --- a/nkebao/src/store/createPersistStore.ts +++ b/nkebao/src/store/createPersistStore.ts @@ -5,12 +5,12 @@ import { persist, PersistOptions } from "zustand/middleware"; export function createPersistStore( createState: (set: any, get: any) => T, name: string, - partialize?: (state: T) => Partial + partialize?: (state: T) => Partial, ) { return create()( persist(createState, { name, partialize, - } as PersistOptions) + } as PersistOptions), ); } diff --git a/nkebao/src/store/module/user.ts b/nkebao/src/store/module/user.ts index 6ecd32f74..a26c3e58d 100644 --- a/nkebao/src/store/module/user.ts +++ b/nkebao/src/store/module/user.ts @@ -70,5 +70,5 @@ export const useUserStore = createPersistStore( user: state.user, token: state.token, isLoggedIn: state.isLoggedIn, - }) + }), ); diff --git a/nkebao/src/utils/common.ts b/nkebao/src/utils/common.ts index 3c54d78e6..2dd200f89 100644 --- a/nkebao/src/utils/common.ts +++ b/nkebao/src/utils/common.ts @@ -12,7 +12,7 @@ export const comfirm = ( title?: string; cancelText?: string; confirmText?: string; - } + }, ): Promise => { return new Promise((resolve, reject) => { Modal.show({