diff --git a/.gitignore b/.gitignore
index 8b8fee88..df8964e0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,4 @@ Store_vue/unpackage/
Store_vue/.vscode/
SuperAdmin/.specstory/
Cunkebao/dist
+Touchkebao/.specstory/
diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/data-statistics/index.data.tsx b/Touchkebao/src/pages/pc/ckbox/powerCenter/data-statistics/index.data.tsx
new file mode 100644
index 00000000..770291a6
--- /dev/null
+++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/data-statistics/index.data.tsx
@@ -0,0 +1,113 @@
+// KPI数据类型定义
+export interface KPIData {
+ id: string;
+ value: string;
+ label: string;
+ subtitle?: string;
+ trend?: {
+ direction: "up" | "down";
+ text: string;
+ };
+}
+
+// 话术组数据类型定义
+export interface DialogueGroupData {
+ status: string;
+ reachRate: number;
+ replyRate: number;
+ clickRate: number;
+ conversionRate: number;
+ avgReplyTime: string;
+ pushCount: number;
+}
+
+// KPI统计数据
+export const kpiData: KPIData[] = [
+ {
+ id: "reach-rate",
+ value: "96.5%",
+ label: "触达率",
+ subtitle: "成功发送/计划发送",
+ trend: {
+ direction: "up",
+ text: "+2.3% 本月",
+ },
+ },
+ {
+ id: "reply-rate",
+ value: "42.8%",
+ label: "回复率",
+ subtitle: "收到回复/成功发送",
+ trend: {
+ direction: "up",
+ text: "+5.1% 本月",
+ },
+ },
+ {
+ id: "avg-reply-time",
+ value: "18分钟",
+ label: "平均回复时间",
+ subtitle: "从发送到回复的平均时长",
+ trend: {
+ direction: "down",
+ text: "-3分钟",
+ },
+ },
+ {
+ id: "link-click-rate",
+ value: "28.3%",
+ label: "链接点击率",
+ subtitle: "点击链接/成功发送",
+ trend: {
+ direction: "up",
+ text: "+1.8% 本月",
+ },
+ },
+];
+
+// 话术组对比数据
+export const dialogueGroupData: DialogueGroupData[] = [
+ {
+ status: "优秀",
+ reachRate: 98.1,
+ replyRate: 48.7,
+ clickRate: 32.5,
+ conversionRate: 12.8,
+ avgReplyTime: "15分钟",
+ pushCount: 156,
+ },
+ {
+ status: "良好",
+ reachRate: 95.8,
+ replyRate: 38.2,
+ clickRate: 25.4,
+ conversionRate: 9.2,
+ avgReplyTime: "22分钟",
+ pushCount: 142,
+ },
+ {
+ status: "一般",
+ reachRate: 92.3,
+ replyRate: 28.5,
+ clickRate: 18.7,
+ conversionRate: 6.5,
+ avgReplyTime: "28分钟",
+ pushCount: 98,
+ },
+];
+
+// 时间范围选项
+export const timeRangeOptions = [
+ { label: "最近7天", value: "7days" },
+ { label: "最近30天", value: "30days" },
+ { label: "最近90天", value: "90days" },
+ { label: "自定义", value: "custom" },
+];
+
+
+
+
+
+
+
+
diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/data-statistics/index.module.scss b/Touchkebao/src/pages/pc/ckbox/powerCenter/data-statistics/index.module.scss
new file mode 100644
index 00000000..9ec10d60
--- /dev/null
+++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/data-statistics/index.module.scss
@@ -0,0 +1,433 @@
+.container {
+ padding: 24px;
+ background: #f5f5f5;
+ min-height: 100vh;
+}
+
+// 筛选和操作区域
+.filterSection {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 24px;
+ padding: 20px;
+ background: #fff;
+ border-radius: 8px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+
+ .filterLeft {
+ display: flex;
+ gap: 24px;
+ align-items: center;
+
+ .filterItem {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+
+ .filterLabel {
+ font-size: 14px;
+ color: #666;
+ white-space: nowrap;
+ }
+
+ .filterSelect {
+ width: 160px;
+ border-radius: 6px;
+
+ :global(.ant-select-selector) {
+ border-radius: 6px;
+ }
+ }
+ }
+ }
+
+ .filterRight {
+ .generateReportBtn {
+ height: 40px;
+ padding: 0 24px;
+ border-radius: 6px;
+ font-weight: 500;
+ background: #ff7a00;
+ border-color: #ff7a00;
+
+ &:hover {
+ background: #ff8c1a;
+ border-color: #ff8c1a;
+ }
+ }
+ }
+}
+
+// KPI统计区域
+.kpiSection {
+ margin-bottom: 24px;
+
+ .kpiGrid {
+ display: grid;
+ grid-template-columns: repeat(4, 1fr);
+ gap: 16px;
+
+ .kpiCard {
+ background: #fff;
+ border-radius: 12px;
+ padding: 24px;
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
+ transition: all 0.3s ease;
+ display: flex;
+ flex-direction: column;
+
+ &:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.12);
+ }
+
+ .kpiContentWrapper {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ height: 100%;
+ }
+
+ .kpiContent {
+ flex: 1;
+
+ .kpiValue {
+ font-size: 32px;
+ font-weight: 700;
+ color: #1a1a1a;
+ margin-bottom: 8px;
+ line-height: 1.2;
+ }
+
+ .kpiLabel {
+ font-size: 14px;
+ color: #666;
+ margin-bottom: 8px;
+ }
+
+ .kpiTrend {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ margin-bottom: 8px;
+
+ .trendIconUp {
+ color: #52c41a;
+ font-size: 14px;
+ }
+
+ .trendIconDown {
+ color: #52c41a;
+ font-size: 14px;
+ }
+
+ .trendText {
+ font-size: 12px;
+ color: #52c41a;
+ font-weight: 500;
+ }
+ }
+
+ .kpiSubtitle {
+ font-size: 12px;
+ color: #999;
+ line-height: 1.4;
+ }
+ }
+
+ .kpiIcon {
+ width: 56px;
+ height: 56px;
+ border-radius: 12px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ box-shadow: 0 6px 14px rgba(0, 0, 0, 0.18);
+ flex-shrink: 0;
+ }
+ }
+ }
+}
+
+// 标签页导航
+.tabsSection {
+ margin-bottom: 24px;
+
+ .tabs {
+ display: flex;
+ gap: 0;
+ border-bottom: 1px solid #e8e8e8;
+ background: #fff;
+ border-radius: 8px 8px 0 0;
+ padding: 16px 16px 0 16px;
+
+ .tab {
+ padding: 12px 24px;
+ cursor: pointer;
+ border-bottom: 2px solid transparent;
+ color: #666;
+ font-size: 14px;
+ transition: all 0.3s;
+ user-select: none;
+
+ &:hover {
+ color: #1890ff;
+ background-color: #f5f5f5;
+ }
+ }
+
+ .tabActive {
+ color: #1890ff;
+ border-bottom-color: #1890ff;
+ background-color: #f0f8ff;
+ font-weight: 500;
+ }
+ }
+}
+
+// 内容区域
+.contentSection {
+ background: #fff;
+ border-radius: 8px;
+ padding: 24px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+
+ .sectionHeader {
+ margin-bottom: 24px;
+
+ .sectionTitle {
+ font-size: 18px;
+ font-weight: 600;
+ color: #1a1a1a;
+ margin: 0 0 8px 0;
+ }
+
+ .sectionSubtitle {
+ font-size: 14px;
+ color: #666;
+ margin: 0;
+ }
+ }
+
+ // 话术组对比内容
+ .comparisonContent {
+ .dialogueGroupList {
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+
+ .dialogueGroupCard {
+ background: #fafafa;
+ border: 1px solid #f0f0f0;
+ border-radius: 8px;
+ padding: 20px;
+ transition: all 0.3s ease;
+
+ &:hover {
+ border-color: #d9d9d9;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
+ }
+
+ .groupHeader {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 20px;
+
+ .groupTitle {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ font-size: 16px;
+ font-weight: 600;
+ color: #1a1a1a;
+
+ .statusTag {
+ font-size: 12px;
+ padding: 2px 8px;
+ border-radius: 12px;
+ font-weight: 500;
+ }
+ }
+
+ .pushCount {
+ font-size: 14px;
+ color: #666;
+ }
+ }
+
+ .metricsList {
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+
+ .metricItem {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 16px;
+
+ .metricLabel {
+ font-size: 14px;
+ color: #666;
+ min-width: 100px;
+ }
+
+ .metricValue {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ gap: 12px;
+
+ .metricPercent {
+ font-size: 14px;
+ font-weight: 600;
+ color: #1a1a1a;
+ min-width: 50px;
+ text-align: right;
+ }
+
+ .metricTime {
+ font-size: 14px;
+ font-weight: 600;
+ color: #1a1a1a;
+ }
+
+ .metricProgress {
+ flex: 1;
+ max-width: 300px;
+
+ :global(.ant-progress-bg) {
+ background: #1890ff;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // 时段分析内容
+ .timeAnalysisContent {
+ min-height: 400px;
+ }
+
+ // 互动深度内容
+ .depthAnalysisContent {
+ min-height: 400px;
+ }
+
+ // 占位符
+ .placeholder {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 300px;
+ background: #fafafa;
+ border: 1px dashed #d9d9d9;
+ border-radius: 6px;
+ font-size: 16px;
+ color: #8c8c8c;
+ }
+}
+
+// 响应式设计
+@media (max-width: 1200px) {
+ .kpiSection {
+ .kpiGrid {
+ grid-template-columns: repeat(2, 1fr);
+ }
+ }
+}
+
+@media (max-width: 768px) {
+ .container {
+ padding: 16px;
+ }
+
+ .filterSection {
+ flex-direction: column;
+ gap: 16px;
+ align-items: stretch;
+
+ .filterLeft {
+ flex-direction: column;
+ gap: 12px;
+ align-items: stretch;
+
+ .filterItem {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 8px;
+
+ .filterSelect {
+ width: 100%;
+ }
+ }
+ }
+
+ .filterRight {
+ .generateReportBtn {
+ width: 100%;
+ }
+ }
+ }
+
+ .kpiSection {
+ .kpiGrid {
+ grid-template-columns: 1fr;
+ }
+ }
+
+ .tabsSection {
+ .tabs {
+ flex-direction: column;
+ padding: 0;
+
+ .tab {
+ border-bottom: 1px solid #e8e8e8;
+ border-radius: 0;
+
+ &:last-child {
+ border-bottom: none;
+ }
+ }
+ }
+ }
+
+ .contentSection {
+ padding: 16px;
+
+ .comparisonContent {
+ .dialogueGroupList {
+ .dialogueGroupCard {
+ padding: 16px;
+
+ .groupHeader {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 12px;
+ }
+
+ .metricsList {
+ .metricItem {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 8px;
+
+ .metricValue {
+ width: 100%;
+
+ .metricProgress {
+ max-width: 100%;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/data-statistics/index.tsx b/Touchkebao/src/pages/pc/ckbox/powerCenter/data-statistics/index.tsx
new file mode 100644
index 00000000..303a8f1a
--- /dev/null
+++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/data-statistics/index.tsx
@@ -0,0 +1,320 @@
+import React, { useState } from "react";
+import { useNavigate } from "react-router-dom";
+import { Select, Button, Progress, Tag } from "antd";
+import {
+ BarChartOutlined,
+ EyeOutlined,
+ MessageOutlined,
+ ClockCircleOutlined,
+ ThunderboltOutlined,
+ ArrowUpOutlined,
+ ArrowDownOutlined,
+} from "@ant-design/icons";
+import PowerNavigation from "@/components/PowerNavtion";
+import Layout from "@/components/Layout/LayoutFiexd";
+import styles from "./index.module.scss";
+import { kpiData, dialogueGroupData, timeRangeOptions } from "./index.data";
+
+const DataStatistics: React.FC = () => {
+ const navigate = useNavigate();
+ const [timeRange, setTimeRange] = useState("7days");
+ const [dialogueGroup, setDialogueGroup] = useState("all");
+ const [activeTab, setActiveTab] = useState("comparison");
+
+ // 获取KPI图标和背景色
+ const getKpiConfig = (id: string) => {
+ switch (id) {
+ case "reach-rate":
+ return {
+ icon: ,
+ bgColor: "#1890ff",
+ };
+ case "reply-rate":
+ return {
+ icon: ,
+ bgColor: "#52c41a",
+ };
+ case "avg-reply-time":
+ return {
+ icon: ,
+ bgColor: "#722ed1",
+ };
+ case "link-click-rate":
+ return {
+ icon: ,
+ bgColor: "#ff7a00",
+ };
+ default:
+ return {
+ icon: null,
+ bgColor: "#1890ff",
+ };
+ }
+ };
+
+ // 获取状态标签颜色
+ const getStatusColor = (status: string) => {
+ switch (status) {
+ case "优秀":
+ return "green";
+ case "良好":
+ return "blue";
+ case "一般":
+ return "orange";
+ default:
+ return "default";
+ }
+ };
+
+ // 处理生成报告
+ const handleGenerateReport = () => {
+ console.log("生成报告", { timeRange, dialogueGroup });
+ // TODO: 实现生成报告功能
+ };
+
+ return (
+
+ navigate("/pc/powerCenter/message-push-assistant")}
+ />
+
+ }
+ footer={null}
+ >
+
+ {/* 筛选和操作区域 */}
+
+
+
+ 时间范围
+
+
+
+ 话术组筛选
+
+
+
+
+ }
+ onClick={handleGenerateReport}
+ className={styles.generateReportBtn}
+ >
+ 生成报告
+
+
+
+
+ {/* KPI统计卡片区域 */}
+
+
+ {kpiData.map(kpi => {
+ const kpiConfig = getKpiConfig(kpi.id);
+ return (
+
+
+
+
{kpi.value}
+
{kpi.label}
+ {kpi.trend && (
+
+ {kpi.trend.direction === "up" ? (
+
+ ) : (
+
+ )}
+
{kpi.trend.text}
+
+ )}
+ {kpi.subtitle && (
+
{kpi.subtitle}
+ )}
+
+
+ {kpiConfig.icon}
+
+
+
+ );
+ })}
+
+
+
+ {/* 标签页导航 */}
+
+
+
setActiveTab("comparison")}
+ >
+ 话术组对比
+
+
setActiveTab("time")}
+ >
+ 时段分析
+
+
setActiveTab("depth")}
+ >
+ 互动深度
+
+
+
+
+ {/* 内容区域 */}
+
+ {activeTab === "comparison" && (
+
+
+
话术组效果对比
+
+ 对比不同话术组的推送效果,找出表现最佳的话术
+
+
+
+ {dialogueGroupData.map((group, index) => (
+
+
+
+ 话术组 {index + 1}
+
+ {group.status}
+
+
+
+ 推送 {group.pushCount}次
+
+
+
+
+
+
+
+
转化率
+
+
{group.conversionRate}%
+
+
+
+
+
平均回复时间
+
+ {group.avgReplyTime}
+
+
+
+
+ ))}
+
+
+ )}
+
+ {activeTab === "time" && (
+
+
+
时段分析
+
+ 分析不同时段的推送效果,优化推送时间策略
+
+
+
+
+ )}
+
+ {activeTab === "depth" && (
+
+
+
互动深度
+
+ 分析用户互动深度,优化推送内容策略
+
+
+
+
+ )}
+
+
+
+ );
+};
+
+export default DataStatistics;
diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/index.data.tsx b/Touchkebao/src/pages/pc/ckbox/powerCenter/index.data.tsx
index 34c14dd2..1410691b 100644
--- a/Touchkebao/src/pages/pc/ckbox/powerCenter/index.data.tsx
+++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/index.data.tsx
@@ -1,4 +1,4 @@
-import { TeamOutlined, CommentOutlined, BookOutlined } from "@ant-design/icons";
+import { TeamOutlined, CommentOutlined, BookOutlined, SendOutlined } from "@ant-design/icons";
// 数据类型定义
export interface FeatureCard {
@@ -22,7 +22,9 @@ export interface KPIData {
};
}
-// 功能数据 - 匹配图片中的三个核心模块
+// 功能数据 - 匹配图片中的布局
+// 第一行:客户好友管理、AI接待设置、AI内容库配置
+// 第二行:消息推送助手(单独一行,左边对齐)
export const featureCategories: FeatureCard[] = [
{
id: "customer-management",
@@ -69,6 +71,21 @@ export const featureCategories: FeatureCard[] = [
],
path: "/pc/powerCenter/content-library",
},
+ {
+ id: "message-push-assistant",
+ title: "消息推送助手",
+ description: "批量推送消息,AI智能话术改写,支持好友、群聊、公告推送",
+ icon: ,
+ color: "#ff7a00",
+ tag: "消息推送",
+ features: [
+ "微信好友消息推送",
+ "微信群消息推送",
+ "群公告消息推送",
+ "AI智能话术改写",
+ ],
+ path: "/pc/powerCenter/message-push-assistant",
+ },
];
// KPI统计数据
diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/index.module.scss b/Touchkebao/src/pages/pc/ckbox/powerCenter/index.module.scss
index 8209d4d6..6e4d6aea 100644
--- a/Touchkebao/src/pages/pc/ckbox/powerCenter/index.module.scss
+++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/index.module.scss
@@ -75,7 +75,6 @@
display: flex;
align-items: center;
justify-content: center;
- background: rgba(24, 144, 255, 0.1);
}
.cardTag {
@@ -127,7 +126,7 @@
&::before {
content: "•";
- color: #1890ff;
+ color: var(--dot-color, #1890ff);
font-weight: bold;
position: absolute;
left: 0;
diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/index.tsx b/Touchkebao/src/pages/pc/ckbox/powerCenter/index.tsx
index d6d09e5d..51652b1e 100644
--- a/Touchkebao/src/pages/pc/ckbox/powerCenter/index.tsx
+++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/index.tsx
@@ -17,6 +17,19 @@ const PowerCenter: React.FC = () => {
return "#722ed1";
};
+ // 将十六进制颜色转换为带透明度的rgba
+ const getIconBgColor = (color: string) => {
+ // 如果是十六进制颜色,转换为rgba
+ if (color.startsWith("#")) {
+ const hex = color.slice(1);
+ const r = parseInt(hex.slice(0, 2), 16);
+ const g = parseInt(hex.slice(2, 4), 16);
+ const b = parseInt(hex.slice(4, 6), 16);
+ return `rgba(${r}, ${g}, ${b}, 0.1)`;
+ }
+ return color;
+ };
+
const handleCardClick = (card: FeatureCard) => {
if (card.path) {
navigate(card.path);
@@ -112,8 +125,9 @@ const PowerCenter: React.FC = () => {
{/* 核心功能模块 */}
-
- {featureCategories.map(card => (
+ {/* 第一行:3个功能卡片 */}
+
+ {featureCategories.slice(0, 3).map(card => (
{
>
-
{card.icon}
+
+ {card.icon}
+
{
{card.features.map((feature, index) => (
- - {feature}
+ -
+ {feature}
+
))}
@@ -145,6 +171,61 @@ const PowerCenter: React.FC = () => {
))}
+
+ {/* 第二行:消息推送助手(单独一行,左边对齐) */}
+ {featureCategories.length > 3 && (
+
+
+ handleCardClick(featureCategories[3])}
+ >
+
+
+
+ {featureCategories[3].icon}
+
+
+ {featureCategories[3].tag}
+
+
+
+
+
+ {featureCategories[3].title}
+
+
+ {featureCategories[3].description}
+
+
+
+ {featureCategories[3].features.map((feature, index) => (
+ -
+ {feature}
+
+ ))}
+
+
+
+
+
+
+ )}
{/* 页面底部 */}
diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/components/PushTaskModal.module.scss b/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/components/PushTaskModal.module.scss
new file mode 100644
index 00000000..a7d38777
--- /dev/null
+++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/components/PushTaskModal.module.scss
@@ -0,0 +1,521 @@
+.pushTaskModal {
+ .ant-modal-content {
+ padding: 0;
+ border-radius: 12px;
+ overflow: hidden;
+ }
+
+ .ant-modal-body {
+ padding: 0;
+ }
+}
+
+.modalHeader {
+ display: flex;
+ align-items: center;
+ padding: 20px 24px;
+ border-bottom: 1px solid #e8e8e8;
+ background: #fff;
+
+ .backButton {
+ margin-right: 16px;
+ color: #666;
+ padding: 0;
+ height: auto;
+
+ &:hover {
+ color: #1890ff;
+ }
+ }
+
+ .headerTitle {
+ flex: 1;
+
+ h2 {
+ font-size: 20px;
+ font-weight: 600;
+ color: #1a1a1a;
+ margin: 0 0 4px 0;
+ }
+
+ p {
+ font-size: 14px;
+ color: #666;
+ margin: 0;
+ }
+ }
+}
+
+.steps {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 24px;
+ background: #fafafa;
+ border-bottom: 1px solid #e8e8e8;
+
+ .step {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ flex: 1;
+ position: relative;
+
+ &:not(:last-child)::after {
+ content: "";
+ position: absolute;
+ top: 20px;
+ left: 60%;
+ right: -40%;
+ height: 2px;
+ background: #d9d9d9;
+ z-index: 0;
+ }
+
+ &.active:not(:last-child)::after,
+ &.completed:not(:last-child)::after {
+ background: #52c41a;
+ }
+
+ .stepIcon {
+ width: 40px;
+ height: 40px;
+ border-radius: 50%;
+ background: #d9d9d9;
+ color: #fff;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 16px;
+ font-weight: 600;
+ position: relative;
+ z-index: 1;
+ margin-bottom: 8px;
+ }
+
+ &.active .stepIcon {
+ background: #52c41a;
+ }
+
+ &.completed .stepIcon {
+ background: #52c41a;
+ color: #fff;
+ }
+
+ span {
+ font-size: 14px;
+ color: #666;
+ }
+
+ &.active span {
+ color: #52c41a;
+ font-weight: 500;
+ }
+ }
+}
+
+.stepBody {
+ min-height: 500px;
+ max-height: 600px;
+ overflow-y: auto;
+ padding: 24px;
+}
+
+.stepContent {
+ .stepHeader {
+ margin-bottom: 20px;
+
+ h3 {
+ font-size: 18px;
+ font-weight: 600;
+ color: #1a1a1a;
+ margin: 0 0 8px 0;
+ }
+
+ p {
+ font-size: 14px;
+ color: #666;
+ margin: 0;
+ }
+ }
+}
+
+.searchBar {
+ margin-bottom: 20px;
+
+ .ant-input {
+ height: 40px;
+ }
+}
+
+.accountSelection {
+ .selectionControls {
+ margin-bottom: 16px;
+ }
+
+ .accountCards {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
+ gap: 16px;
+ max-height: 400px;
+ overflow-y: auto;
+ padding: 8px;
+
+ .accountCard {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 16px;
+ border: 2px solid #e8e8e8;
+ border-radius: 8px;
+ cursor: pointer;
+ transition: all 0.3s;
+ background: #fff;
+
+ &:hover {
+ border-color: #1890ff;
+ transform: translateY(-2px);
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+ }
+
+ &.selected {
+ border-color: #52c41a;
+ background: #f6ffed;
+ }
+
+ .cardName {
+ margin-top: 8px;
+ font-size: 14px;
+ color: #1a1a1a;
+ text-align: center;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ width: 100%;
+ }
+
+ .onlineStatus {
+ margin-top: 4px;
+ font-size: 12px;
+ padding: 2px 8px;
+ border-radius: 12px;
+
+ &.online {
+ background: #f6ffed;
+ color: #52c41a;
+ }
+
+ &.offline {
+ background: #f5f5f5;
+ color: #999;
+ }
+ }
+
+ .checkmark {
+ position: absolute;
+ top: 8px;
+ right: 8px;
+ color: #52c41a;
+ font-size: 20px;
+ }
+ }
+ }
+}
+
+.step2Content {
+ .searchContainer {
+ display: flex;
+ gap: 12px;
+ margin-bottom: 16px;
+
+ .ant-input {
+ flex: 1;
+ }
+ }
+
+ .contentBody {
+ display: flex;
+ gap: 16px;
+ min-height: 400px;
+ }
+
+ .contactList,
+ .selectedList {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ border: 1px solid #d9d9d9;
+ border-radius: 6px;
+ overflow: hidden;
+ }
+
+ .listHeader {
+ padding: 12px 16px;
+ background-color: #fafafa;
+ border-bottom: 1px solid #d9d9d9;
+ font-weight: 500;
+ font-size: 14px;
+ color: #262626;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ }
+
+ .listContent {
+ flex: 1;
+ overflow-y: auto;
+ padding: 8px;
+ min-height: 300px;
+ }
+
+ .contactItem {
+ display: flex;
+ align-items: center;
+ padding: 8px 12px;
+ border-radius: 4px;
+ margin-bottom: 4px;
+ transition: background-color 0.2s;
+ cursor: pointer;
+ gap: 12px;
+
+ &:hover {
+ background-color: #f5f5f5;
+ }
+
+ &.selected {
+ background-color: #e6f7ff;
+ }
+
+ .contactInfo {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ min-width: 0;
+ }
+
+ .contactName {
+ font-size: 14px;
+ color: #262626;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ .conRemark {
+ font-size: 12px;
+ color: #8c8c8c;
+ }
+
+ .groupIcon {
+ color: #1890ff;
+ font-size: 12px;
+ margin-left: 4px;
+ }
+ }
+
+ .selectedItem {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 8px 12px;
+ border-radius: 4px;
+ margin-bottom: 4px;
+ transition: background-color 0.2s;
+
+ &:hover {
+ background-color: #f5f5f5;
+ }
+
+ .contactInfo {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ min-width: 0;
+ }
+
+ .removeIcon {
+ color: #8c8c8c;
+ cursor: pointer;
+ padding: 4px;
+ border-radius: 2px;
+ transition: all 0.2s;
+
+ &:hover {
+ color: #ff4d4f;
+ background-color: #fff2f0;
+ }
+ }
+ }
+
+ .paginationContainer {
+ padding: 12px;
+ border-top: 1px solid #d9d9d9;
+ display: flex;
+ justify-content: center;
+ }
+
+ .loadingContainer {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 200px;
+ flex-direction: column;
+ gap: 12px;
+ color: #8c8c8c;
+ }
+}
+
+.step3Content {
+ display: flex;
+ flex-direction: column;
+ gap: 24px;
+
+ .messagePreview {
+ border: 2px dashed #52c41a;
+ border-radius: 8px;
+ padding: 20px;
+ background: #f6ffed;
+
+ .previewTitle {
+ font-size: 14px;
+ color: #52c41a;
+ font-weight: 500;
+ margin-bottom: 12px;
+ }
+
+ .messageBubble {
+ min-height: 60px;
+ padding: 12px;
+ background: #fff;
+ border-radius: 6px;
+ color: #666;
+ font-size: 14px;
+ line-height: 1.6;
+ }
+ }
+
+ .messageInputArea {
+ .messageInput {
+ margin-bottom: 12px;
+ }
+
+ .attachmentButtons {
+ display: flex;
+ gap: 8px;
+ margin-bottom: 12px;
+ }
+
+ .aiRewriteSection {
+ display: flex;
+ align-items: center;
+ margin-bottom: 8px;
+ }
+
+ .messageHint {
+ font-size: 12px;
+ color: #999;
+ }
+ }
+
+ .settingsPanel {
+ border: 1px solid #e8e8e8;
+ border-radius: 8px;
+ padding: 20px;
+ background: #fafafa;
+
+ .settingItem {
+ margin-bottom: 20px;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ .settingLabel {
+ font-size: 14px;
+ font-weight: 500;
+ color: #1a1a1a;
+ margin-bottom: 12px;
+ }
+
+ .settingControl {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+
+ span {
+ font-size: 14px;
+ color: #666;
+ min-width: 80px;
+ }
+ }
+ }
+ }
+
+ .pushPreview {
+ border: 1px solid #e8e8e8;
+ border-radius: 8px;
+ padding: 20px;
+ background: #fafafa;
+
+ .previewTitle {
+ font-size: 14px;
+ font-weight: 500;
+ color: #1a1a1a;
+ margin-bottom: 12px;
+ }
+
+ ul {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+
+ li {
+ font-size: 14px;
+ color: #666;
+ line-height: 1.8;
+ }
+ }
+ }
+}
+
+.modalFooter {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 16px 24px;
+ border-top: 1px solid #e8e8e8;
+ background: #fff;
+
+ .footerLeft {
+ font-size: 14px;
+ color: #666;
+ }
+
+ .footerRight {
+ display: flex;
+ gap: 12px;
+ }
+}
+
+// 响应式设计
+@media (max-width: 1200px) {
+ .pushTaskModal {
+ .ant-modal-content {
+ width: 90vw !important;
+ }
+ }
+
+ .step2Content {
+ .contentBody {
+ flex-direction: column;
+ }
+
+ .contactList,
+ .selectedList {
+ min-height: 200px;
+ }
+ }
+}
diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/components/PushTaskModal.tsx b/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/components/PushTaskModal.tsx
new file mode 100644
index 00000000..18ef6ce4
--- /dev/null
+++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/components/PushTaskModal.tsx
@@ -0,0 +1,767 @@
+import React, { useState, useEffect, useMemo } from "react";
+import {
+ Modal,
+ Input,
+ Button,
+ Avatar,
+ Checkbox,
+ Empty,
+ Spin,
+ message,
+ Pagination,
+ Slider,
+ Select,
+ Switch,
+} from "antd";
+import {
+ SearchOutlined,
+ CloseOutlined,
+ UserOutlined,
+ TeamOutlined,
+ ArrowLeftOutlined,
+ CheckCircleOutlined,
+ SendOutlined,
+} from "@ant-design/icons";
+import styles from "./PushTaskModal.module.scss";
+import {
+ useCustomerStore,
+} from "@/store/module/weChat/customer";
+import { getContactList, getGroupList } from "@/pages/pc/ckbox/weChat/api";
+
+export type PushType = "friend-message" | "group-message" | "group-announcement";
+
+interface PushTaskModalProps {
+ visible: boolean;
+ pushType: PushType;
+ onCancel: () => void;
+ onConfirm?: () => void;
+}
+
+interface WeChatAccount {
+ id: number;
+ name: string;
+ avatar?: string;
+ isOnline?: boolean;
+ wechatId?: string;
+}
+
+interface ContactItem {
+ id: number;
+ nickname: string;
+ avatar?: string;
+ conRemark?: string;
+ wechatId?: string;
+ gender?: number;
+ region?: string;
+ type?: "friend" | "group";
+}
+
+const PushTaskModal: React.FC
= ({
+ visible,
+ pushType,
+ onCancel,
+ onConfirm,
+}) => {
+ const [currentStep, setCurrentStep] = useState(1);
+ const [searchKeyword, setSearchKeyword] = useState("");
+ const [selectedAccounts, setSelectedAccounts] = useState([]);
+ const [selectedContacts, setSelectedContacts] = useState([]);
+ const [messageContent, setMessageContent] = useState("");
+ const [friendInterval, setFriendInterval] = useState(10);
+ const [messageInterval, setMessageInterval] = useState(1);
+ const [selectedTag, setSelectedTag] = useState("");
+ const [aiRewriteEnabled, setAiRewriteEnabled] = useState(false);
+ const [aiPrompt, setAiPrompt] = useState("");
+
+ // 步骤2数据
+ const [contactsData, setContactsData] = useState([]);
+ const [loadingContacts, setLoadingContacts] = useState(false);
+ const [step2Page, setStep2Page] = useState(1);
+ const [step2SearchValue, setStep2SearchValue] = useState("");
+ const step2PageSize = 20;
+
+ const customerList = useCustomerStore(state => state.customerList);
+
+ // 获取标题和描述
+ const getTitle = () => {
+ switch (pushType) {
+ case "friend-message":
+ return "好友消息推送";
+ case "group-message":
+ return "群消息推送";
+ case "group-announcement":
+ return "群公告推送";
+ default:
+ return "消息推送";
+ }
+ };
+
+ const getSubtitle = () => {
+ return "智能批量推送,AI智能话术改写";
+ };
+
+ // 步骤2的标题
+ const getStep2Title = () => {
+ switch (pushType) {
+ case "friend-message":
+ return "选择好友";
+ case "group-message":
+ case "group-announcement":
+ return "选择群";
+ default:
+ return "选择";
+ }
+ };
+
+ // 重置状态
+ const handleClose = () => {
+ setCurrentStep(1);
+ setSearchKeyword("");
+ setSelectedAccounts([]);
+ setSelectedContacts([]);
+ setMessageContent("");
+ setFriendInterval(10);
+ setMessageInterval(1);
+ setSelectedTag("");
+ setAiRewriteEnabled(false);
+ setAiPrompt("");
+ setStep2Page(1);
+ setStep2SearchValue("");
+ setContactsData([]);
+ onCancel();
+ };
+
+ // 步骤1:过滤微信账号
+ const filteredAccounts = useMemo(() => {
+ if (!searchKeyword.trim()) return customerList;
+ const keyword = searchKeyword.toLowerCase();
+ return customerList.filter(
+ account =>
+ (account.nickname || "").toLowerCase().includes(keyword) ||
+ (account.wechatId || "").toLowerCase().includes(keyword),
+ );
+ }, [customerList, searchKeyword]);
+
+ // 步骤1:切换账号选择
+ const handleAccountToggle = (account: any) => {
+ setSelectedAccounts(prev => {
+ const isSelected = prev.some(a => a.id === account.id);
+ if (isSelected) {
+ return prev.filter(a => a.id !== account.id);
+ }
+ return [...prev, account];
+ });
+ };
+
+ // 步骤1:全选/取消全选
+ const handleSelectAll = () => {
+ if (selectedAccounts.length === filteredAccounts.length) {
+ setSelectedAccounts([]);
+ } else {
+ setSelectedAccounts([...filteredAccounts]);
+ }
+ };
+
+ // 步骤2:加载好友/群数据
+ const loadStep2Data = async () => {
+ if (selectedAccounts.length === 0) return;
+
+ setLoadingContacts(true);
+ try {
+ const accountIds = selectedAccounts.map(a => a.id);
+ const params: any = {
+ page: step2Page,
+ limit: step2PageSize,
+ };
+
+ if (step2SearchValue.trim()) {
+ params.keyword = step2SearchValue.trim();
+ }
+
+ let response;
+ if (pushType === "friend-message") {
+ // 好友消息推送:获取好友列表
+ response = await getContactList(params);
+ } else {
+ // 群消息推送/群公告推送:获取群列表
+ response = await getGroupList(params);
+ }
+
+ const data = response.data || response.list || [];
+ setContactsData(data);
+ } catch (error) {
+ console.error("加载数据失败:", error);
+ message.error("加载数据失败");
+ } finally {
+ setLoadingContacts(false);
+ }
+ };
+
+ // 步骤2:当进入步骤2时加载数据
+ useEffect(() => {
+ if (currentStep === 2 && selectedAccounts.length > 0) {
+ loadStep2Data();
+ }
+ }, [currentStep, selectedAccounts, step2Page, step2SearchValue, pushType]);
+
+ // 步骤2:过滤联系人
+ const filteredContacts = useMemo(() => {
+ if (!step2SearchValue.trim()) return contactsData;
+ const keyword = step2SearchValue.toLowerCase();
+ return contactsData.filter(
+ contact =>
+ contact.nickname?.toLowerCase().includes(keyword) ||
+ contact.conRemark?.toLowerCase().includes(keyword) ||
+ contact.wechatId?.toLowerCase().includes(keyword),
+ );
+ }, [contactsData, step2SearchValue]);
+
+ // 步骤2:分页显示
+ const paginatedContacts = useMemo(() => {
+ const start = (step2Page - 1) * step2PageSize;
+ const end = start + step2PageSize;
+ return filteredContacts.slice(start, end);
+ }, [filteredContacts, step2Page]);
+
+ // 步骤2:切换联系人选择
+ const handleContactToggle = (contact: ContactItem) => {
+ setSelectedContacts(prev => {
+ const isSelected = prev.some(c => c.id === contact.id);
+ if (isSelected) {
+ return prev.filter(c => c.id !== contact.id);
+ }
+ return [...prev, contact];
+ });
+ };
+
+ // 步骤2:移除已选联系人
+ const handleRemoveContact = (contactId: number) => {
+ setSelectedContacts(prev => prev.filter(c => c.id !== contactId));
+ };
+
+ // 步骤2:全选当前页
+ const handleSelectAllContacts = () => {
+ if (paginatedContacts.length === 0) return;
+ const allSelected = paginatedContacts.every(contact =>
+ selectedContacts.some(c => c.id === contact.id),
+ );
+ if (allSelected) {
+ // 取消全选当前页
+ const currentPageIds = paginatedContacts.map(c => c.id);
+ setSelectedContacts(prev =>
+ prev.filter(c => !currentPageIds.includes(c.id)),
+ );
+ } else {
+ // 全选当前页
+ const toAdd = paginatedContacts.filter(
+ contact => !selectedContacts.some(c => c.id === contact.id),
+ );
+ setSelectedContacts(prev => [...prev, ...toAdd]);
+ }
+ };
+
+ // 下一步
+ const handleNext = () => {
+ if (currentStep === 1) {
+ if (selectedAccounts.length === 0) {
+ message.warning("请至少选择一个微信账号");
+ return;
+ }
+ setCurrentStep(2);
+ } else if (currentStep === 2) {
+ if (selectedContacts.length === 0) {
+ message.warning(`请至少选择一个${pushType === "friend-message" ? "好友" : "群"}`);
+ return;
+ }
+ setCurrentStep(3);
+ }
+ };
+
+ // 上一步
+ const handlePrev = () => {
+ if (currentStep > 1) {
+ setCurrentStep(currentStep - 1);
+ }
+ };
+
+ // 发送
+ const handleSend = () => {
+ if (!messageContent.trim()) {
+ message.warning("请输入消息内容");
+ return;
+ }
+ // TODO: 实现发送逻辑
+ console.log("发送推送", {
+ pushType,
+ accounts: selectedAccounts,
+ contacts: selectedContacts,
+ messageContent,
+ friendInterval,
+ messageInterval,
+ selectedTag,
+ aiRewriteEnabled,
+ aiPrompt,
+ });
+ message.success("推送任务已创建");
+ handleClose();
+ if (onConfirm) onConfirm();
+ };
+
+ // 渲染步骤1:选择微信账号
+ const renderStep1 = () => (
+
+
+
选择微信账号
+
可选择多个微信账号进行推送
+
+
+ }
+ value={searchKeyword}
+ onChange={e => setSearchKeyword(e.target.value)}
+ allowClear
+ />
+
+
+
+ 0 &&
+ selectedAccounts.length === filteredAccounts.length
+ }
+ indeterminate={
+ selectedAccounts.length > 0 &&
+ selectedAccounts.length < filteredAccounts.length
+ }
+ onChange={handleSelectAll}
+ disabled={filteredAccounts.length === 0}
+ >
+ 全选
+
+
+
+ {filteredAccounts.length > 0 ? (
+ filteredAccounts.map(account => {
+ const isSelected = selectedAccounts.some(a => a.id === account.id);
+ return (
+
handleAccountToggle(account)}
+ >
+
+ {!account.avatar && (account.nickname || account.name || "").charAt(0)}
+
+
+ {account.nickname || account.name || "未知"}
+
+
+ {account.isOnline ? "在线" : "离线"}
+
+ {isSelected && (
+
+ )}
+
+ );
+ })
+ ) : (
+
+ )}
+
+
+
+ );
+
+ // 渲染步骤2:选择好友/群
+ const renderStep2 = () => (
+
+
+
+ }
+ value={step2SearchValue}
+ onChange={e => setStep2SearchValue(e.target.value)}
+ allowClear
+ />
+
+
+
+ {/* 左侧:好友/群列表 */}
+
+
+
+ {getStep2Title()}列表(共{filteredContacts.length}个)
+
+
+
+ {loadingContacts ? (
+
+
+ 加载中...
+
+ ) : paginatedContacts.length > 0 ? (
+ paginatedContacts.map(contact => {
+ const isSelected = selectedContacts.some(
+ c => c.id === contact.id,
+ );
+ return (
+
handleContactToggle(contact)}
+ >
+
+
+ ) : (
+
+ )
+ }
+ />
+
+
+ {contact.nickname}
+
+ {contact.conRemark && (
+
+ {contact.conRemark}
+
+ )}
+
+ {contact.type === "group" && (
+
+ )}
+
+ );
+ })
+ ) : (
+
+ )}
+
+ {filteredContacts.length > 0 && (
+
+
setStep2Page(p)}
+ showSizeChanger={false}
+ />
+
+ )}
+
+
+ {/* 右侧:已选列表 */}
+
+
+
+ 已选{getStep2Title()}列表(共{selectedContacts.length}个)
+
+ {selectedContacts.length > 0 && (
+
+ )}
+
+
+ {selectedContacts.length > 0 ? (
+ selectedContacts.map(contact => (
+
+
+
+ ) : (
+
+ )
+ }
+ />
+
+
{contact.nickname}
+ {contact.conRemark && (
+
+ {contact.conRemark}
+
+ )}
+
+ {contact.type === "group" && (
+
+ )}
+
+
handleRemoveContact(contact.id)}
+ />
+
+ ))
+ ) : (
+
+ )}
+
+
+
+
+
+ );
+
+ // 渲染步骤3:一键群发
+ const renderStep3 = () => (
+
+
+
+
模拟推送内容
+
+ {messageContent || "开始添加消息内容..."}
+
+
+
+
+
setMessageContent(e.target.value)}
+ rows={4}
+ onKeyDown={e => {
+ if (e.ctrlKey && e.key === "Enter") {
+ e.preventDefault();
+ setMessageContent(prev => prev + "\n");
+ }
+ }}
+ />
+
+
+
+
+
+
+
+
+
+ AI智能话术改写
+ {aiRewriteEnabled && (
+ setAiPrompt(e.target.value)}
+ style={{ marginLeft: 12, width: 200 }}
+ />
+ )}
+
+
+
+ 按住CTRL+ENTER换行,已配置1个话术组,已选择0个进行推送
+
+
+
+
+
+
好友间间隔
+
+ 间隔时间(秒)
+
+ {friendInterval} - 20
+
+
+
+
消息间间隔
+
+ 间隔时间(秒)
+
+ {messageInterval} - 12
+
+
+
+
完成打标签
+
+
+
+
+
+
+
+
推送预览
+
+ - 推送账号: {selectedAccounts.length}个
+ -
+ 推送{getStep2Title()}: {selectedContacts.length}个
+
+ - 话术组数: 0个
+ - 随机推送: 否
+ - 预计耗时: ~1分钟
+
+
+
+
+ );
+
+ return (
+
+
+
}
+ onClick={handleClose}
+ className={styles.backButton}
+ >
+ 返回
+
+
+
{getTitle()}
+
{getSubtitle()}
+
+
+
+ {/* 步骤指示器 */}
+
+
= 1 ? styles.active : ""} ${currentStep > 1 ? styles.completed : ""}`}
+ >
+
+ {currentStep > 1 ? : "1"}
+
+
选择微信
+
+
= 2 ? styles.active : ""} ${currentStep > 2 ? styles.completed : ""}`}
+ >
+
+ {currentStep > 2 ? : "2"}
+
+
选择{getStep2Title()}
+
+
= 3 ? styles.active : ""}`}
+ >
+
3
+
一键群发
+
+
+
+ {/* 步骤内容 */}
+
+ {currentStep === 1 && renderStep1()}
+ {currentStep === 2 && renderStep2()}
+ {currentStep === 3 && renderStep3()}
+
+
+ {/* 底部操作栏 */}
+
+
+ {currentStep === 1 && (
+ 已选择{selectedAccounts.length}个微信账号
+ )}
+ {currentStep === 2 && (
+
+ 已选择{selectedContacts.length}个{getStep2Title()}
+
+ )}
+ {currentStep === 3 && (
+
+ 推送账号: {selectedAccounts.length}个, 推送{getStep2Title()}:{" "}
+ {selectedContacts.length}个
+
+ )}
+
+
+ {currentStep === 1 && (
+ <>
+
+
+ >
+ )}
+ {currentStep === 2 && (
+ <>
+
+
+ >
+ )}
+ {currentStep === 3 && (
+ <>
+
+ }
+ onClick={handleSend}
+ >
+ 一键发送
+
+ >
+ )}
+
+
+
+ );
+};
+
+export default PushTaskModal;
diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/index.module.scss b/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/index.module.scss
new file mode 100644
index 00000000..04b7188a
--- /dev/null
+++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/index.module.scss
@@ -0,0 +1,819 @@
+.container {
+ padding: 24px;
+ background: #f5f5f5;
+ min-height: calc(100vh - 64px);
+ display: flex;
+ flex-direction: column;
+}
+
+.steps {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 24px;
+ background: #fff;
+ border-radius: 12px;
+ margin-bottom: 16px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+
+ .step {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ flex: 1;
+ position: relative;
+
+ &:not(:last-child)::after {
+ content: "";
+ position: absolute;
+ top: 20px;
+ left: 60%;
+ right: -40%;
+ height: 2px;
+ background: #d9d9d9;
+ z-index: 0;
+ }
+
+ &.active:not(:last-child)::after,
+ &.completed:not(:last-child)::after {
+ background: #52c41a;
+ }
+
+ .stepIcon {
+ width: 40px;
+ height: 40px;
+ border-radius: 50%;
+ background: #d9d9d9;
+ color: #fff;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 16px;
+ font-weight: 600;
+ position: relative;
+ z-index: 1;
+ margin-bottom: 8px;
+ }
+
+ &.active .stepIcon {
+ background: #52c41a;
+ }
+
+ &.completed .stepIcon {
+ background: #52c41a;
+ color: #fff;
+ }
+
+ span {
+ font-size: 14px;
+ color: #666;
+ }
+
+ &.active span {
+ color: #52c41a;
+ font-weight: 500;
+ }
+ }
+}
+
+.stepBody {
+ flex: 1;
+ background: #fff;
+ border-radius: 12px;
+ padding: 24px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+ margin-bottom: 16px;
+ overflow-y: auto;
+ min-height: 500px;
+}
+
+.stepContent {
+ .stepHeader {
+ margin-bottom: 20px;
+
+ h3 {
+ font-size: 18px;
+ font-weight: 600;
+ color: #1a1a1a;
+ margin: 0 0 8px 0;
+ }
+
+ p {
+ font-size: 14px;
+ color: #666;
+ margin: 0;
+ }
+ }
+}
+
+.searchBar {
+ margin-bottom: 20px;
+
+ :global(.ant-input) {
+ height: 40px;
+ }
+}
+
+.accountSelection {
+ .selectionControls {
+ margin-bottom: 16px;
+ }
+
+ .accountCards {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
+ gap: 16px;
+ max-height: 500px;
+ overflow-y: auto;
+ padding: 8px;
+
+ .accountCard {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 16px;
+ border: 2px solid #e8e8e8;
+ border-radius: 8px;
+ cursor: pointer;
+ transition: all 0.3s;
+ background: #fff;
+
+ &:hover {
+ border-color: #1890ff;
+ transform: translateY(-2px);
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+ }
+
+ &.selected {
+ border-color: #52c41a;
+ background: #f6ffed;
+ }
+
+ .cardName {
+ margin-top: 8px;
+ font-size: 14px;
+ color: #1a1a1a;
+ text-align: center;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ width: 100%;
+ }
+
+ .onlineStatus {
+ margin-top: 4px;
+ font-size: 12px;
+ padding: 2px 8px;
+ border-radius: 12px;
+
+ &.online {
+ background: #f6ffed;
+ color: #52c41a;
+ }
+
+ &.offline {
+ background: #f5f5f5;
+ color: #999;
+ }
+ }
+
+ .checkmark {
+ position: absolute;
+ top: 8px;
+ right: 8px;
+ color: #52c41a;
+ font-size: 20px;
+ }
+ }
+ }
+}
+
+.step2Content {
+ .stepHeader {
+ margin-bottom: 20px;
+
+ h3 {
+ font-size: 18px;
+ font-weight: 600;
+ color: #1a1a1a;
+ margin: 0 0 8px 0;
+ }
+
+ p {
+ font-size: 14px;
+ color: #666;
+ margin: 0;
+ }
+ }
+
+ .searchContainer {
+ display: flex;
+ gap: 12px;
+ margin-bottom: 16px;
+
+ :global(.ant-input) {
+ flex: 1;
+ }
+ }
+
+ .contentBody {
+ display: flex;
+ gap: 16px;
+ min-height: 500px;
+ }
+
+ .contactList,
+ .selectedList {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ border: 1px solid #d9d9d9;
+ border-radius: 6px;
+ overflow: hidden;
+ }
+
+ .listHeader {
+ padding: 12px 16px;
+ background-color: #fafafa;
+ border-bottom: 1px solid #d9d9d9;
+ font-weight: 500;
+ font-size: 14px;
+ color: #262626;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ }
+
+ .listContent {
+ flex: 1;
+ overflow-y: auto;
+ padding: 8px;
+ min-height: 300px;
+ }
+
+ .contactItem {
+ display: flex;
+ align-items: center;
+ padding: 8px 12px;
+ border-radius: 4px;
+ margin-bottom: 4px;
+ transition: background-color 0.2s;
+ cursor: pointer;
+ gap: 12px;
+
+ &:hover {
+ background-color: #f5f5f5;
+ }
+
+ &.selected {
+ background-color: #e6f7ff;
+ }
+
+ .contactInfo {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ min-width: 0;
+ }
+
+ .contactName {
+ font-size: 14px;
+ color: #262626;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ .conRemark {
+ font-size: 12px;
+ color: #8c8c8c;
+ }
+
+ .groupIcon {
+ color: #1890ff;
+ font-size: 12px;
+ margin-left: 4px;
+ }
+ }
+
+ .selectedItem {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 8px 12px;
+ border-radius: 4px;
+ margin-bottom: 4px;
+ transition: background-color 0.2s;
+
+ &:hover {
+ background-color: #f5f5f5;
+ }
+
+ .contactInfo {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ min-width: 0;
+ }
+
+ .removeIcon {
+ color: #8c8c8c;
+ cursor: pointer;
+ padding: 4px;
+ border-radius: 2px;
+ transition: all 0.2s;
+
+ &:hover {
+ color: #ff4d4f;
+ background-color: #fff2f0;
+ }
+ }
+ }
+
+ .paginationContainer {
+ padding: 12px;
+ border-top: 1px solid #d9d9d9;
+ display: flex;
+ justify-content: center;
+ }
+
+ .loadingContainer {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 200px;
+ flex-direction: column;
+ gap: 12px;
+ color: #8c8c8c;
+ }
+}
+
+.step3Content {
+ display: flex;
+ gap: 24px;
+ align-items: flex-start;
+
+ // 左侧栏
+ .leftColumn {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+ }
+
+ // 右侧栏
+ .rightColumn {
+ width: 400px;
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+ }
+
+ .messagePreview {
+ border: 2px dashed #52c41a;
+ border-radius: 8px;
+ padding: 20px;
+ background: #f6ffed;
+
+ .previewTitle {
+ font-size: 14px;
+ color: #52c41a;
+ font-weight: 500;
+ margin-bottom: 12px;
+ }
+
+ .messageBubble {
+ min-height: 60px;
+ padding: 12px;
+ background: #fff;
+ border-radius: 6px;
+ color: #666;
+ font-size: 14px;
+ line-height: 1.6;
+
+ .currentEditingLabel {
+ font-size: 12px;
+ color: #999;
+ margin-bottom: 8px;
+ }
+
+ .messageText {
+ color: #333;
+ white-space: pre-wrap;
+ word-break: break-word;
+ }
+ }
+ }
+
+ // 已保存话术组
+ .savedScriptGroups {
+ .scriptGroupTitle {
+ font-size: 14px;
+ font-weight: 500;
+ color: #333;
+ margin-bottom: 12px;
+ }
+
+ .scriptGroupItem {
+ border: 1px solid #e8e8e8;
+ border-radius: 8px;
+ padding: 12px;
+ margin-bottom: 12px;
+ background: #fff;
+
+ .scriptGroupHeader {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+
+ .scriptGroupLeft {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ flex: 1;
+
+ :global(.ant-radio) {
+ margin-right: 4px;
+ }
+
+ .scriptGroupName {
+ font-size: 14px;
+ font-weight: 500;
+ color: #333;
+ }
+
+ .messageCount {
+ font-size: 12px;
+ color: #999;
+ margin-left: 8px;
+ }
+ }
+
+ .scriptGroupActions {
+ display: flex;
+ gap: 4px;
+
+ .actionButton {
+ padding: 4px;
+ color: #666;
+
+ &:hover {
+ color: #1890ff;
+ }
+ }
+ }
+ }
+
+ .scriptGroupContent {
+ margin-top: 8px;
+ padding-top: 8px;
+ border-top: 1px solid #f0f0f0;
+ font-size: 13px;
+ color: #666;
+ }
+ }
+ }
+
+ .messageInputArea {
+ .messageInput {
+ margin-bottom: 12px;
+ }
+
+ .attachmentButtons {
+ display: flex;
+ gap: 8px;
+ margin-bottom: 12px;
+ }
+
+ .aiRewriteSection {
+ display: flex;
+ align-items: center;
+ margin-bottom: 8px;
+ }
+
+ .messageHint {
+ font-size: 12px;
+ color: #999;
+ }
+ }
+
+ .settingsPanel {
+ border: 1px solid #e8e8e8;
+ border-radius: 8px;
+ padding: 20px;
+ background: #fafafa;
+
+ .settingsTitle {
+ font-size: 14px;
+ font-weight: 500;
+ color: #1a1a1a;
+ margin-bottom: 16px;
+ }
+
+ .settingItem {
+ margin-bottom: 20px;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ .settingLabel {
+ font-size: 14px;
+ font-weight: 500;
+ color: #1a1a1a;
+ margin-bottom: 12px;
+ }
+
+ .settingControl {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+
+ span {
+ font-size: 14px;
+ color: #666;
+ min-width: 80px;
+ }
+ }
+ }
+ }
+
+ .tagSection {
+ .settingLabel {
+ font-size: 14px;
+ font-weight: 500;
+ color: #1a1a1a;
+ margin-bottom: 12px;
+ }
+ }
+
+ .pushPreview {
+ border: 1px solid #e8e8e8;
+ border-radius: 8px;
+ padding: 20px;
+ background: #f0f7ff;
+
+ .previewTitle {
+ font-size: 14px;
+ font-weight: 500;
+ color: #1a1a1a;
+ margin-bottom: 12px;
+ }
+
+ ul {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+
+ li {
+ font-size: 14px;
+ color: #666;
+ line-height: 1.8;
+ }
+ }
+ }
+}
+
+.footer {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 16px 24px;
+ background: #fff;
+ border-radius: 12px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+
+ .footerLeft {
+ font-size: 14px;
+ color: #666;
+ }
+
+ .footerRight {
+ display: flex;
+ gap: 12px;
+ }
+}
+
+// 响应式设计
+@media (max-width: 1200px) {
+ .container {
+ padding: 20px;
+
+ .step2Content {
+ .contentBody {
+ flex-direction: column;
+ }
+
+ .contactList,
+ .selectedList {
+ min-height: 200px;
+ }
+ }
+
+ .step3Content {
+ .rightColumn {
+ width: 350px;
+ }
+ }
+ }
+}
+
+@media (max-width: 768px) {
+ .container {
+ padding: 16px;
+
+ .steps {
+ padding: 16px;
+
+ .step {
+ span {
+ font-size: 12px;
+ }
+
+ .stepIcon {
+ width: 32px;
+ height: 32px;
+ font-size: 14px;
+ }
+ }
+ }
+
+ .stepBody {
+ padding: 16px;
+ min-height: 400px;
+ }
+
+ .step2Content {
+ .contentBody {
+ min-height: 400px;
+ }
+ }
+
+ .step3Content {
+ flex-direction: column;
+
+ .leftColumn {
+ width: 100%;
+ }
+
+ .rightColumn {
+ width: 100%;
+ }
+ }
+
+ .footer {
+ padding: 12px 16px;
+ flex-direction: column;
+ gap: 12px;
+ align-items: stretch;
+
+ .footerRight {
+ width: 100%;
+ justify-content: flex-end;
+ }
+ }
+ }
+}
+
+// 步骤1样式(新版按照设计稿)
+.step1Content {
+ .stepHeader {
+ margin-bottom: 20px;
+
+ h3 {
+ font-size: 18px;
+ font-weight: 600;
+ color: #1a1a1a;
+ margin: 0 0 8px 0;
+ }
+
+ p {
+ font-size: 14px;
+ color: #666;
+ margin: 0;
+ }
+ }
+
+ .searchBar {
+ margin-bottom: 24px;
+
+ :global(.ant-input-affix-wrapper) {
+ height: 40px;
+ border-radius: 8px;
+ }
+ }
+
+ // 未选择的账号列表
+ .accountList {
+ margin-bottom: 30px;
+ max-height: 400px;
+ overflow-y: auto;
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 12px;
+
+ &::-webkit-scrollbar {
+ width: 6px;
+ }
+
+ &::-webkit-scrollbar-thumb {
+ background: #d9d9d9;
+ border-radius: 3px;
+ }
+
+ .accountItem {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding: 12px 16px;
+ border: 1px solid #e8e8e8;
+ border-radius: 8px;
+ background: #fff;
+ cursor: pointer;
+ transition: all 0.3s ease;
+
+ &:hover {
+ border-color: #52c41a;
+ background: #fafafa;
+ }
+
+ .accountInfo {
+ flex: 1;
+
+ .accountName {
+ font-size: 14px;
+ font-weight: 500;
+ color: #1a1a1a;
+ margin-bottom: 4px;
+ }
+
+ .accountId {
+ font-size: 12px;
+ color: #999;
+ }
+ }
+ }
+ }
+
+ // 已选择区域
+ .selectedSection {
+ .selectedHeader {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 12px 0;
+ margin-bottom: 12px;
+
+ span {
+ font-size: 14px;
+ color: #666;
+ }
+
+ .clearButton {
+ padding: 0;
+ font-size: 14px;
+ }
+ }
+
+ .selectedCards {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 12px;
+
+ .selectedCard {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding: 12px 16px;
+ border: 2px solid #52c41a;
+ border-radius: 8px;
+ background: #f6ffed;
+ position: relative;
+
+ .accountInfo {
+ flex: 1;
+
+ .accountName {
+ font-size: 14px;
+ font-weight: 500;
+ color: #1a1a1a;
+ margin-bottom: 4px;
+ }
+
+ .accountId {
+ font-size: 12px;
+ color: #999;
+ }
+ }
+
+ .checkIcon {
+ font-size: 20px;
+ color: #52c41a;
+ }
+ }
+ }
+ }
+}
diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/index.tsx b/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/index.tsx
new file mode 100644
index 00000000..30bb057b
--- /dev/null
+++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/index.tsx
@@ -0,0 +1,891 @@
+import React, { useState, useEffect, useMemo } from "react";
+import { useNavigate, useParams } from "react-router-dom";
+import {
+ Input,
+ Button,
+ Avatar,
+ Checkbox,
+ Empty,
+ Spin,
+ message,
+ Pagination,
+ Slider,
+ Select,
+ Switch,
+ Radio,
+} from "antd";
+import {
+ SearchOutlined,
+ CloseOutlined,
+ UserOutlined,
+ TeamOutlined,
+ CheckCircleOutlined,
+ CheckOutlined,
+ SendOutlined,
+ CopyOutlined,
+ DeleteOutlined,
+} from "@ant-design/icons";
+import PowerNavigation from "@/components/PowerNavtion";
+import Layout from "@/components/Layout/LayoutFiexd";
+import styles from "./index.module.scss";
+import {
+ useCustomerStore,
+} from "@/store/module/weChat/customer";
+import { getContactList, getGroupList } from "@/pages/pc/ckbox/weChat/api";
+
+export type PushType = "friend-message" | "group-message" | "group-announcement";
+
+interface WeChatAccount {
+ id: number;
+ name: string;
+ avatar?: string;
+ isOnline?: boolean;
+ wechatId?: string;
+}
+
+interface ContactItem {
+ id: number;
+ nickname: string;
+ avatar?: string;
+ conRemark?: string;
+ wechatId?: string;
+ gender?: number;
+ region?: string;
+ type?: "friend" | "group";
+}
+
+const CreatePushTask: React.FC = () => {
+ const navigate = useNavigate();
+ const { pushType } = useParams<{ pushType: PushType }>();
+
+ // 验证推送类型
+ const validPushType: PushType =
+ pushType === "friend-message" ||
+ pushType === "group-message" ||
+ pushType === "group-announcement"
+ ? pushType
+ : "friend-message";
+
+ const [currentStep, setCurrentStep] = useState(1);
+ const [searchKeyword, setSearchKeyword] = useState("");
+ const [selectedAccounts, setSelectedAccounts] = useState([]);
+ const [selectedContacts, setSelectedContacts] = useState([]);
+ const [messageContent, setMessageContent] = useState("");
+ const [friendInterval, setFriendInterval] = useState(10);
+ const [messageInterval, setMessageInterval] = useState(1);
+ const [selectedTag, setSelectedTag] = useState("");
+ const [aiRewriteEnabled, setAiRewriteEnabled] = useState(false);
+ const [aiPrompt, setAiPrompt] = useState("");
+ const [selectedScriptGroup, setSelectedScriptGroup] = useState("group1");
+ const [scriptGroups] = useState([
+ { id: "group1", name: "话术组 1", messageCount: 1, content: "啊实打实" },
+ ]);
+
+ // 步骤2数据
+ const [contactsData, setContactsData] = useState([]);
+ const [loadingContacts, setLoadingContacts] = useState(false);
+ const [step2Page, setStep2Page] = useState(1);
+ const [step2SearchValue, setStep2SearchValue] = useState("");
+ const [step2Total, setStep2Total] = useState(0);
+ const step2PageSize = 20;
+
+ const customerList = useCustomerStore(state => state.customerList);
+
+ // 获取标题和描述
+ const getTitle = () => {
+ switch (validPushType) {
+ case "friend-message":
+ return "好友消息推送";
+ case "group-message":
+ return "群消息推送";
+ case "group-announcement":
+ return "群公告推送";
+ default:
+ return "消息推送";
+ }
+ };
+
+ const getSubtitle = () => {
+ return "智能批量推送,AI智能话术改写";
+ };
+
+ // 步骤2的标题
+ const getStep2Title = () => {
+ switch (validPushType) {
+ case "friend-message":
+ return "好友";
+ case "group-message":
+ case "group-announcement":
+ return "群";
+ default:
+ return "选择";
+ }
+ };
+
+ // 重置状态
+ const handleClose = () => {
+ navigate("/pc/powerCenter/message-push-assistant");
+ };
+
+ // 步骤1:过滤微信账号
+ const filteredAccounts = useMemo(() => {
+ if (!searchKeyword.trim()) return customerList;
+ const keyword = searchKeyword.toLowerCase();
+ return customerList.filter(
+ account =>
+ (account.nickname || "").toLowerCase().includes(keyword) ||
+ (account.wechatId || "").toLowerCase().includes(keyword),
+ );
+ }, [customerList, searchKeyword]);
+
+ // 步骤1:切换账号选择
+ const handleAccountToggle = (account: any) => {
+ setSelectedAccounts(prev => {
+ const isSelected = prev.some(a => a.id === account.id);
+ if (isSelected) {
+ return prev.filter(a => a.id !== account.id);
+ }
+ return [...prev, account];
+ });
+ };
+
+ // 步骤1:全选/取消全选
+ const handleSelectAll = () => {
+ if (selectedAccounts.length === filteredAccounts.length) {
+ setSelectedAccounts([]);
+ } else {
+ setSelectedAccounts([...filteredAccounts]);
+ }
+ };
+
+ // 清空所有选择
+ const handleClearAll = () => {
+ setSelectedAccounts([]);
+ };
+
+ // 步骤2:加载好友/群数据
+ const loadStep2Data = async () => {
+ if (selectedAccounts.length === 0) {
+ setContactsData([]);
+ setStep2Total(0);
+ return;
+ }
+
+ setLoadingContacts(true);
+ try {
+ const accountIds = selectedAccounts.map(a => a.id);
+
+ // 如果有多个账号,分别请求每个账号的数据并合并
+ const allData: ContactItem[] = [];
+ let totalCount = 0;
+
+ // 为每个账号请求数据
+ for (const accountId of accountIds) {
+ const params: any = {
+ page: step2Page,
+ limit: step2PageSize,
+ wechatAccountId: accountId, // 传递微信账号ID
+ };
+
+ if (step2SearchValue.trim()) {
+ params.keyword = step2SearchValue.trim();
+ }
+
+ let response;
+ if (validPushType === "friend-message") {
+ // 好友消息推送:获取好友列表
+ response = await getContactList(params);
+ } else {
+ // 群消息推送/群公告推送:获取群列表
+ response = await getGroupList(params);
+ }
+
+ // 处理响应数据
+ const data = response.data?.list || response.data || response.list || [];
+ const total = response.data?.total || response.total || 0;
+
+ // 过滤出属于当前账号的数据(双重保险)
+ const filteredData = data.filter((item: any) => {
+ const itemAccountId = item.wechatAccountId || item.accountId;
+ return itemAccountId === accountId;
+ });
+
+ // 合并数据(去重,根据id)
+ filteredData.forEach((item: ContactItem) => {
+ if (!allData.some(d => d.id === item.id)) {
+ allData.push(item);
+ }
+ });
+
+ totalCount += total;
+ }
+
+ // 如果多个账号,需要重新排序和分页
+ // 这里简化处理:显示所有合并后的数据,但总数使用第一个账号的总数
+ // 实际应该根据业务需求调整
+ setContactsData(allData);
+ setStep2Total(totalCount > 0 ? totalCount : allData.length);
+ } catch (error) {
+ console.error("加载数据失败:", error);
+ message.error("加载数据失败");
+ setContactsData([]);
+ setStep2Total(0);
+ } finally {
+ setLoadingContacts(false);
+ }
+ };
+
+ // 步骤2:当进入步骤2或分页变化时加载数据
+ useEffect(() => {
+ if (currentStep === 2 && selectedAccounts.length > 0) {
+ loadStep2Data();
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [currentStep, selectedAccounts, step2Page, validPushType]);
+
+ // 步骤2:搜索时重置分页并重新加载数据
+ useEffect(() => {
+ if (currentStep === 2 && selectedAccounts.length > 0) {
+ // 搜索时重置到第一页
+ if (step2SearchValue.trim() && step2Page !== 1) {
+ setStep2Page(1);
+ } else if (!step2SearchValue.trim() && step2Page === 1) {
+ // 清空搜索时,如果已经在第一页,直接加载
+ loadStep2Data();
+ }
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [step2SearchValue]);
+
+ // 步骤2:过滤联系人(前端过滤,如果后端已支持搜索则不需要)
+ const filteredContacts = useMemo(() => {
+ // 如果后端已支持搜索,直接返回数据
+ if (step2SearchValue.trim()) {
+ // 后端已搜索,直接返回
+ return contactsData;
+ }
+ return contactsData;
+ }, [contactsData, step2SearchValue]);
+
+ // 步骤2:显示的数据(后端已分页,直接使用)
+ const paginatedContacts = filteredContacts;
+
+ // 步骤2:切换联系人选择
+ const handleContactToggle = (contact: ContactItem) => {
+ setSelectedContacts(prev => {
+ const isSelected = prev.some(c => c.id === contact.id);
+ if (isSelected) {
+ return prev.filter(c => c.id !== contact.id);
+ }
+ return [...prev, contact];
+ });
+ };
+
+ // 步骤2:移除已选联系人
+ const handleRemoveContact = (contactId: number) => {
+ setSelectedContacts(prev => prev.filter(c => c.id !== contactId));
+ };
+
+ // 步骤2:全选当前页
+ const handleSelectAllContacts = () => {
+ if (paginatedContacts.length === 0) return;
+ const allSelected = paginatedContacts.every(contact =>
+ selectedContacts.some(c => c.id === contact.id),
+ );
+ if (allSelected) {
+ // 取消全选当前页
+ const currentPageIds = paginatedContacts.map(c => c.id);
+ setSelectedContacts(prev =>
+ prev.filter(c => !currentPageIds.includes(c.id)),
+ );
+ } else {
+ // 全选当前页
+ const toAdd = paginatedContacts.filter(
+ contact => !selectedContacts.some(c => c.id === contact.id),
+ );
+ setSelectedContacts(prev => [...prev, ...toAdd]);
+ }
+ };
+
+ // 下一步
+ const handleNext = () => {
+ if (currentStep === 1) {
+ if (selectedAccounts.length === 0) {
+ message.warning("请至少选择一个微信账号");
+ return;
+ }
+ setCurrentStep(2);
+ } else if (currentStep === 2) {
+ if (selectedContacts.length === 0) {
+ message.warning(`请至少选择一个${validPushType === "friend-message" ? "好友" : "群"}`);
+ return;
+ }
+ setCurrentStep(3);
+ }
+ };
+
+ // 上一步
+ const handlePrev = () => {
+ if (currentStep > 1) {
+ setCurrentStep(currentStep - 1);
+ }
+ };
+
+ // 发送
+ const handleSend = () => {
+ if (!messageContent.trim()) {
+ message.warning("请输入消息内容");
+ return;
+ }
+ // TODO: 实现发送逻辑
+ console.log("发送推送", {
+ pushType: validPushType,
+ accounts: selectedAccounts,
+ contacts: selectedContacts,
+ messageContent,
+ friendInterval,
+ messageInterval,
+ selectedTag,
+ aiRewriteEnabled,
+ aiPrompt,
+ });
+ message.success("推送任务已创建");
+ navigate("/pc/powerCenter/message-push-assistant");
+ };
+
+ // 渲染步骤1:选择微信账号
+ const renderStep1 = () => {
+ const unselectedAccounts = filteredAccounts.filter(
+ a => !selectedAccounts.some(s => s.id === a.id)
+ );
+
+ return (
+
+
+
选择微信账号
+
可选择多个微信账号进行推送
+
+
+
+ }
+ value={searchKeyword}
+ onChange={e => setSearchKeyword(e.target.value)}
+ allowClear
+ />
+
+
+ {/* 未选择的账号列表 */}
+ {unselectedAccounts.length > 0 || filteredAccounts.length === 0 ? (
+
+ {unselectedAccounts.length > 0 ? (
+ unselectedAccounts.map(account => (
+
handleAccountToggle(account)}
+ >
+
+ {!account.avatar && (account.nickname || account.name || "").charAt(0)}
+
+
+
+ {account.nickname || account.name || "未知"}
+
+
+ {account.isOnline ? "在线" : "离线"}
+
+
+
+ ))
+ ) : (
+
+ )}
+
+ ) : null}
+
+ {/* 已选择的账号 */}
+ {selectedAccounts.length > 0 && (
+
+
+ 已选择 {selectedAccounts.length} 个微信账号
+
+
+
+ {selectedAccounts.map(account => (
+
+
+ {!account.avatar && (account.nickname || account.name || "").charAt(0)}
+
+
+
+ {account.nickname || account.name || "未知"}
+
+
+ {account.isOnline ? "在线" : "离线"}
+
+
+
+
+ ))}
+
+
+ )}
+
+ );
+ };
+
+ // 渲染步骤2:选择好友/群
+ const renderStep2 = () => (
+
+
+
+
选择{getStep2Title()}
+
从{getStep2Title()}列表中选择推送对象
+
+
+ }
+ value={step2SearchValue}
+ onChange={e => setStep2SearchValue(e.target.value)}
+ allowClear
+ />
+
+
+
+ {/* 左侧:好友/群列表 */}
+
+
+
+ {getStep2Title()}列表(共{step2Total}个)
+
+
+
+ {loadingContacts ? (
+
+
+ 加载中...
+
+ ) : paginatedContacts.length > 0 ? (
+ paginatedContacts.map(contact => {
+ const isSelected = selectedContacts.some(
+ c => c.id === contact.id,
+ );
+ return (
+
handleContactToggle(contact)}
+ >
+
+
+ ) : (
+
+ )
+ }
+ />
+
+
+ {contact.nickname}
+
+ {contact.conRemark && (
+
+ {contact.conRemark}
+
+ )}
+
+ {contact.type === "group" && (
+
+ )}
+
+ );
+ })
+ ) : (
+
+ )}
+
+ {step2Total > 0 && (
+
+
setStep2Page(p)}
+ showSizeChanger={false}
+ />
+
+ )}
+
+
+ {/* 右侧:已选列表 */}
+
+
+
+ 已选{getStep2Title()}列表(共{selectedContacts.length}个)
+
+ {selectedContacts.length > 0 && (
+
+ )}
+
+
+ {selectedContacts.length > 0 ? (
+ selectedContacts.map(contact => (
+
+
+
+ ) : (
+
+ )
+ }
+ />
+
+
{contact.nickname}
+ {contact.conRemark && (
+
+ {contact.conRemark}
+
+ )}
+
+ {contact.type === "group" && (
+
+ )}
+
+
handleRemoveContact(contact.id)}
+ />
+
+ ))
+ ) : (
+
+ )}
+
+
+
+
+
+ );
+
+ // 渲染步骤3:一键群发
+ const renderStep3 = () => (
+
+
+ {/* 左侧栏:内容编辑 */}
+
+ {/* 模拟推送内容 */}
+
+
模拟推送内容
+
+
当前编辑话术
+
+ {messageContent || "开始添加消息内容..."}
+
+
+
+
+ {/* 已保存话术组 */}
+
+
+ 已保存话术组({scriptGroups.length})
+
+ {scriptGroups.map(group => (
+
+
+
+ setSelectedScriptGroup(group.id)}
+ />
+ {group.name}
+
+ {group.messageCount}条消息
+
+
+
+ }
+ size="small"
+ className={styles.actionButton}
+ />
+ }
+ size="small"
+ className={styles.actionButton}
+ />
+
+
+ {selectedScriptGroup === group.id && (
+
+ {group.content}
+
+ )}
+
+ ))}
+
+
+ {/* 消息输入区域 */}
+
+
setMessageContent(e.target.value)}
+ rows={4}
+ onKeyDown={e => {
+ if (e.ctrlKey && e.key === "Enter") {
+ e.preventDefault();
+ setMessageContent(prev => prev + "\n");
+ }
+ }}
+ />
+
+
+
+
+
+
+
+
+
+ AI智能话术改写
+ {aiRewriteEnabled && (
+ setAiPrompt(e.target.value)}
+ style={{ marginLeft: 12, width: 200 }}
+ />
+ )}
+
+
+
+ 按住CTRL+ENTER换行,已配置{scriptGroups.length}个话术组,已选择0个进行推送
+
+
+
+
+ {/* 右侧栏:设置和预览 */}
+
+ {/* 相关设置 */}
+
+
相关设置
+
+
好友间间隔
+
+ 间隔时间(秒)
+
+ {friendInterval} - 20
+
+
+
+
消息间间隔
+
+ 间隔时间(秒)
+
+ {messageInterval} - 12
+
+
+
+
+ {/* 完成打标签 */}
+
+
完成打标签
+
+
+
+ {/* 推送预览 */}
+
+
推送预览
+
+ - 推送账号: {selectedAccounts.length}个
+ -
+ 推送{getStep2Title()}: {selectedContacts.length}个
+
+ - 话术组数: 0个
+ - 随机推送: 否
+ - 预计耗时: ~1分钟
+
+
+
+
+
+ );
+
+ return (
+
+
+
+ }
+ footer={null}
+ >
+
+ {/* 步骤指示器 */}
+
+
= 1 ? styles.active : ""} ${currentStep > 1 ? styles.completed : ""}`}
+ >
+
+ {currentStep > 1 ? : "1"}
+
+
选择微信
+
+
= 2 ? styles.active : ""} ${currentStep > 2 ? styles.completed : ""}`}
+ >
+
+ {currentStep > 2 ? : "2"}
+
+
选择{getStep2Title()}
+
+
= 3 ? styles.active : ""}`}
+ >
+
3
+
一键群发
+
+
+
+ {/* 步骤内容 */}
+
+ {currentStep === 1 && renderStep1()}
+ {currentStep === 2 && renderStep2()}
+ {currentStep === 3 && renderStep3()}
+
+
+ {/* 底部操作栏 */}
+
+
+ {currentStep === 1 && (
+ 已选择{selectedAccounts.length}个微信账号
+ )}
+ {currentStep === 2 && (
+
+ 已选择{selectedContacts.length}个{getStep2Title()}
+
+ )}
+ {currentStep === 3 && (
+
+ 推送账号: {selectedAccounts.length}个, 推送{getStep2Title()}:{" "}
+ {selectedContacts.length}个
+
+ )}
+
+
+ {currentStep === 1 && (
+ <>
+
+
+ >
+ )}
+ {currentStep === 2 && (
+ <>
+
+
+ >
+ )}
+ {currentStep === 3 && (
+ <>
+
+ }
+ onClick={handleSend}
+ >
+ 一键发送
+
+ >
+ )}
+
+
+
+
+ );
+};
+
+export default CreatePushTask;
diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/index.module.scss b/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/index.module.scss
new file mode 100644
index 00000000..ccef81f1
--- /dev/null
+++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/index.module.scss
@@ -0,0 +1,194 @@
+.container {
+ padding: 40px;
+ background: #ffffff;
+ min-height: calc(100vh - 64px);
+
+ .section {
+ margin-bottom: 48px;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ .sectionTitle {
+ font-size: 20px;
+ font-weight: 600;
+ color: #1a1a1a;
+ margin: 0 0 24px 0;
+ }
+
+ .cardGrid {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 24px;
+
+ @media (max-width: 1200px) {
+ grid-template-columns: repeat(2, 1fr);
+ }
+
+ @media (max-width: 768px) {
+ grid-template-columns: 1fr;
+ }
+ }
+
+ .dataRecordGrid {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 24px;
+
+ @media (max-width: 768px) {
+ grid-template-columns: 1fr;
+ }
+ }
+
+ .taskCard {
+ background: white;
+ border-radius: 12px;
+ padding: 32px;
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
+ transition: all 0.3s ease;
+ cursor: pointer;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ position: relative;
+ overflow: hidden;
+
+ &:hover {
+ transform: translateY(-4px);
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.12);
+ }
+
+ .cardIcon {
+ width: 56px;
+ height: 56px;
+ border-radius: 12px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: white;
+ font-size: 24px;
+ margin-bottom: 20px;
+ flex-shrink: 0;
+ }
+
+ .cardContent {
+ flex: 1;
+ width: 100%;
+
+ .cardTitle {
+ font-size: 18px;
+ font-weight: 600;
+ color: #1a1a1a;
+ margin: 0 0 12px 0;
+ line-height: 1.3;
+ }
+
+ .cardDescription {
+ font-size: 14px;
+ color: #666;
+ line-height: 1.6;
+ margin: 0;
+ }
+ }
+
+ .cardAction {
+ display: flex;
+ align-items: center;
+ margin-top: 20px;
+ font-size: 14px;
+ font-weight: 500;
+ cursor: pointer;
+ transition: all 0.2s ease;
+
+ &:hover {
+ opacity: 0.8;
+ }
+ }
+ }
+ }
+}
+
+// 响应式设计
+@media (max-width: 1200px) {
+ .container {
+ padding: 32px 24px;
+
+ .section {
+ margin-bottom: 40px;
+
+ .sectionTitle {
+ font-size: 18px;
+ margin-bottom: 20px;
+ }
+
+ .taskCard {
+ padding: 24px;
+
+ .cardIcon {
+ width: 48px;
+ height: 48px;
+ font-size: 20px;
+ margin-bottom: 16px;
+ }
+
+ .cardContent {
+ .cardTitle {
+ font-size: 16px;
+ margin-bottom: 10px;
+ }
+
+ .cardDescription {
+ font-size: 13px;
+ }
+ }
+ }
+ }
+ }
+}
+
+@media (max-width: 768px) {
+ .container {
+ padding: 24px 16px;
+
+ .section {
+ margin-bottom: 32px;
+
+ .sectionTitle {
+ font-size: 16px;
+ margin-bottom: 16px;
+ }
+
+ .cardGrid {
+ gap: 16px;
+ }
+
+ .taskCard {
+ padding: 20px;
+
+ .cardIcon {
+ width: 44px;
+ height: 44px;
+ font-size: 18px;
+ margin-bottom: 12px;
+ }
+
+ .cardContent {
+ .cardTitle {
+ font-size: 15px;
+ margin-bottom: 8px;
+ }
+
+ .cardDescription {
+ font-size: 12px;
+ }
+ }
+
+ .cardAction {
+ margin-top: 16px;
+ font-size: 13px;
+ }
+ }
+ }
+ }
+}
diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/index.tsx b/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/index.tsx
new file mode 100644
index 00000000..acf205f3
--- /dev/null
+++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/index.tsx
@@ -0,0 +1,156 @@
+import React from "react";
+import { useNavigate } from "react-router-dom";
+import PowerNavigation from "@/components/PowerNavtion";
+import Layout from "@/components/Layout/LayoutFiexd";
+import {
+ UserOutlined,
+ MessageOutlined,
+ SoundOutlined,
+ BarChartOutlined,
+ HistoryOutlined,
+ SendOutlined,
+} from "@ant-design/icons";
+import styles from "./index.module.scss";
+
+export type PushType = "friend-message" | "group-message" | "group-announcement";
+
+const MessagePushAssistant: React.FC = () => {
+ const navigate = useNavigate();
+
+ // 创建推送任务卡片数据
+ const createTaskCards = [
+ {
+ id: "friend-message",
+ title: "好友消息推送",
+ description: "向选定的微信好友批量发送消息",
+ icon:
,
+ color: "#1890ff",
+ onClick: () => {
+ navigate("/pc/powerCenter/message-push-assistant/create-push-task/friend-message");
+ },
+ },
+ {
+ id: "group-message",
+ title: "群消息推送",
+ description: "向选定的微信群批量发送消息",
+ icon:
,
+ color: "#52c41a",
+ onClick: () => {
+ navigate("/pc/powerCenter/message-push-assistant/create-push-task/group-message");
+ },
+ },
+ {
+ id: "group-announcement",
+ title: "群公告推送",
+ description: "向选定的微信群发布群公告",
+ icon:
,
+ color: "#722ed1",
+ onClick: () => {
+ navigate("/pc/powerCenter/message-push-assistant/create-push-task/group-announcement");
+ },
+ },
+ ];
+
+ // 数据与记录卡片数据
+ const dataRecordCards = [
+ {
+ id: "data-statistics",
+ title: "数据统计",
+ description: "查看推送效果统计与话术对比分析",
+ icon:
,
+ color: "#ff7a00",
+ onClick: () => {
+ navigate("/pc/powerCenter/data-statistics");
+ },
+ },
+ {
+ id: "push-history",
+ title: "推送历史",
+ description: "查看所有推送任务的历史记录",
+ icon:
,
+ color: "#666666",
+ onClick: () => {
+ navigate("/pc/powerCenter/push-history");
+ },
+ },
+ ];
+
+ return (
+
+ navigate("/pc/powerCenter")}
+ />
+
+ }
+ footer={null}
+ >
+
+ {/* 创建推送任务部分 */}
+
+
创建推送任务
+
+ {createTaskCards.map(card => (
+
+
+ {card.icon}
+
+
+
{card.title}
+
{card.description}
+
+
+
+
+ 立即创建
+
+
+
+ ))}
+
+
+
+ {/* 数据与记录部分 */}
+
+
数据与记录
+
+ {dataRecordCards.map(card => (
+
+
+ {card.icon}
+
+
+
{card.title}
+
{card.description}
+
+
+ ))}
+
+
+
+
+ );
+};
+
+export default MessagePushAssistant;
diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/push-history/api.ts b/Touchkebao/src/pages/pc/ckbox/powerCenter/push-history/api.ts
new file mode 100644
index 00000000..dceeed01
--- /dev/null
+++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/push-history/api.ts
@@ -0,0 +1,65 @@
+import { request } from "@/api/request";
+import { PushHistoryRecord } from "./index";
+
+// 获取推送历史接口参数
+export interface GetPushHistoryParams {
+ page?: number;
+ pageSize?: number;
+ keyword?: string;
+ pushType?: string;
+ status?: string;
+}
+
+// 获取推送历史接口响应
+export interface GetPushHistoryResponse {
+ success: boolean;
+ message?: string;
+ data?: {
+ list: PushHistoryRecord[];
+ total: number;
+ page: number;
+ pageSize: number;
+ };
+}
+
+/**
+ * 获取推送历史列表
+ */
+export const getPushHistory = async (
+ params: GetPushHistoryParams
+): Promise
=> {
+ try {
+ // TODO: 替换为实际的API接口地址
+ const response = await request.get("/api/push-history", { params });
+
+ // 如果接口返回的数据格式不同,需要在这里进行转换
+ if (response.data && response.data.success !== undefined) {
+ return response.data;
+ }
+
+ // 兼容不同的响应格式
+ return {
+ success: true,
+ data: {
+ list: response.data?.list || response.data?.data || [],
+ total: response.data?.total || 0,
+ page: response.data?.page || params.page || 1,
+ pageSize: response.data?.pageSize || params.pageSize || 10,
+ },
+ };
+ } catch (error: any) {
+ console.error("获取推送历史失败:", error);
+ return {
+ success: false,
+ message: error?.message || "获取推送历史失败",
+ };
+ }
+};
+
+
+
+
+
+
+
+
diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/push-history/index.module.scss b/Touchkebao/src/pages/pc/ckbox/powerCenter/push-history/index.module.scss
new file mode 100644
index 00000000..3de5b962
--- /dev/null
+++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/push-history/index.module.scss
@@ -0,0 +1,286 @@
+.pushHistory {
+ padding: 24px;
+ background: #f5f5f5;
+ min-height: calc(100vh - 64px);
+
+ // 筛选区域
+ .filterSection {
+ background: #fff;
+ border-radius: 12px;
+ padding: 20px 24px;
+ margin-bottom: 16px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 16px;
+
+ .filterLeft {
+ .tableTitle {
+ font-size: 16px;
+ font-weight: 600;
+ color: #1a1a1a;
+ margin: 0;
+ }
+ }
+
+ .filterRight {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ flex-wrap: wrap;
+
+ .searchInput {
+ width: 240px;
+ border-radius: 8px;
+ border: 1px solid #e9ecef;
+
+ &:focus,
+ &:hover {
+ border-color: #1890ff;
+ }
+
+ :global(.ant-input) {
+ border: none;
+ box-shadow: none;
+
+ &:focus {
+ box-shadow: none;
+ }
+ }
+ }
+
+ .filterSelect {
+ min-width: 120px;
+ border-radius: 8px;
+ border: 1px solid #e9ecef;
+
+ &:hover {
+ border-color: #1890ff;
+ }
+
+ :global(.ant-select-selector) {
+ border: none !important;
+ box-shadow: none !important;
+ border-radius: 8px !important;
+ }
+
+ :global(.ant-select-selection-item) {
+ color: #333;
+ }
+ }
+ }
+ }
+
+ // 表格区域
+ .tableSection {
+ background: #fff;
+ border-radius: 12px;
+ padding: 20px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+ margin-bottom: 16px;
+
+ .dataTable {
+ :global(.ant-table) {
+ .ant-table-thead > tr > th {
+ background-color: #fafafa;
+ border-bottom: 1px solid #f0f0f0;
+ font-weight: 600;
+ color: #333;
+ padding: 16px;
+ }
+
+ .ant-table-tbody > tr {
+ &:hover {
+ background-color: #f5f5f5;
+ }
+
+ > td {
+ padding: 16px;
+ border-bottom: 1px solid #f0f0f0;
+ }
+ }
+
+ .ant-table-tbody > tr:last-child > td {
+ border-bottom: none;
+ }
+ }
+
+ :global(.ant-table-empty) {
+ .ant-table-tbody > tr > td {
+ border-bottom: none;
+ }
+ }
+ }
+ }
+
+ // 分页区域
+ .paginationSection {
+ background: #fff;
+ border-radius: 12px;
+ padding: 16px 24px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+
+ .paginationInfo {
+ color: #666;
+ font-size: 14px;
+ }
+
+ .pagination {
+ :global(.ant-pagination-item) {
+ border-radius: 8px;
+ border: 1px solid #e9ecef;
+ margin: 0 4px;
+
+ &:hover {
+ border-color: #1890ff;
+
+ a {
+ color: #1890ff;
+ }
+ }
+
+ &.ant-pagination-item-active {
+ background: #1890ff;
+ border-color: #1890ff;
+
+ a {
+ color: #fff;
+ }
+ }
+ }
+
+ :global(.ant-pagination-prev),
+ :global(.ant-pagination-next) {
+ border-radius: 8px;
+ border: 1px solid #e9ecef;
+ margin: 0 4px;
+
+ &:hover {
+ border-color: #1890ff;
+ color: #1890ff;
+ }
+ }
+
+ :global(.ant-pagination-disabled) {
+ &:hover {
+ border-color: #e9ecef;
+ color: rgba(0, 0, 0, 0.25);
+ }
+ }
+ }
+ }
+}
+
+// 响应式设计
+@media (max-width: 1200px) {
+ .pushHistory {
+ padding: 20px;
+
+ .filterSection {
+ padding: 16px 20px;
+
+ .filterRight {
+ .searchInput {
+ width: 200px;
+ }
+
+ .filterSelect {
+ min-width: 100px;
+ }
+ }
+ }
+ }
+}
+
+@media (max-width: 768px) {
+ .pushHistory {
+ padding: 16px;
+
+ .filterSection {
+ padding: 16px;
+ flex-direction: column;
+ align-items: flex-start;
+
+ .filterLeft {
+ width: 100%;
+ }
+
+ .filterRight {
+ width: 100%;
+
+ .searchInput {
+ width: 100%;
+ }
+
+ .filterSelect {
+ flex: 1;
+ min-width: 0;
+ }
+ }
+ }
+
+ .tableSection {
+ padding: 12px;
+ overflow-x: auto;
+
+ .dataTable {
+ :global(.ant-table) {
+ min-width: 800px;
+ }
+ }
+ }
+
+ .paginationSection {
+ padding: 12px 16px;
+ flex-direction: column;
+ gap: 12px;
+ align-items: flex-start;
+
+ .pagination {
+ width: 100%;
+ display: flex;
+ justify-content: center;
+ }
+ }
+ }
+}
+
+@media (max-width: 480px) {
+ .pushHistory {
+ padding: 12px;
+
+ .filterSection {
+ padding: 12px;
+
+ .filterRight {
+ flex-direction: column;
+
+ .searchInput,
+ .filterSelect {
+ width: 100%;
+ }
+ }
+ }
+
+ .tableSection {
+ padding: 8px;
+ }
+
+ .paginationSection {
+ padding: 8px 12px;
+ }
+ }
+}
+
+
+
+
+
+
+
+
diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/push-history/index.tsx b/Touchkebao/src/pages/pc/ckbox/powerCenter/push-history/index.tsx
new file mode 100644
index 00000000..0697b99d
--- /dev/null
+++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/push-history/index.tsx
@@ -0,0 +1,379 @@
+import React, { useState, useEffect } from "react";
+import { useNavigate } from "react-router-dom";
+import { Table, Input, Select, Tag, Button, Pagination, message } from "antd";
+import {
+ SearchOutlined,
+ EyeOutlined,
+ CheckCircleOutlined,
+ ClockCircleOutlined,
+ CloseCircleOutlined,
+} from "@ant-design/icons";
+import PowerNavigation from "@/components/PowerNavtion";
+import Layout from "@/components/Layout/LayoutFiexd";
+import { getPushHistory } from "./api";
+import styles from "./index.module.scss";
+
+const { Option } = Select;
+
+// 推送类型枚举
+export enum PushType {
+ FRIEND_MESSAGE = "friend-message", // 好友消息
+ GROUP_MESSAGE = "group-message", // 群消息
+ GROUP_ANNOUNCEMENT = "group-announcement", // 群公告
+}
+
+// 推送状态枚举
+export enum PushStatus {
+ COMPLETED = "completed", // 已完成
+ IN_PROGRESS = "in-progress", // 进行中
+ FAILED = "failed", // 失败
+}
+
+// 推送历史记录接口
+export interface PushHistoryRecord {
+ id: string;
+ pushType: PushType;
+ pushContent: string;
+ targetCount: number;
+ successCount: number;
+ failureCount: number;
+ status: PushStatus;
+ createTime: string;
+}
+
+const PushHistory: React.FC = () => {
+ const navigate = useNavigate();
+ const [loading, setLoading] = useState(false);
+ const [dataSource, setDataSource] = useState([]);
+ const [searchValue, setSearchValue] = useState("");
+ const [typeFilter, setTypeFilter] = useState("all");
+ const [statusFilter, setStatusFilter] = useState("all");
+ const [pagination, setPagination] = useState({
+ current: 1,
+ pageSize: 10,
+ total: 0,
+ });
+
+ // 获取推送历史数据
+ const fetchPushHistory = async (page: number = 1) => {
+ try {
+ setLoading(true);
+ const params: any = {
+ page,
+ pageSize: pagination.pageSize,
+ };
+
+ if (searchValue.trim()) {
+ params.keyword = searchValue.trim();
+ }
+
+ if (typeFilter !== "all") {
+ params.pushType = typeFilter;
+ }
+
+ if (statusFilter !== "all") {
+ params.status = statusFilter;
+ }
+
+ const response = await getPushHistory(params);
+
+ if (response.success) {
+ setDataSource(response.data?.list || []);
+ setPagination(prev => ({
+ ...prev,
+ current: response.data?.page || page,
+ total: response.data?.total || 0,
+ }));
+ } else {
+ message.error(response.message || "获取推送历史失败");
+ setDataSource([]);
+ }
+ } catch (error) {
+ console.error("获取推送历史失败:", error);
+ message.error("获取推送历史失败,请稍后重试");
+ setDataSource([]);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ // 初始化加载数据
+ useEffect(() => {
+ fetchPushHistory(1);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
+ // 搜索处理
+ const handleSearch = (value: string) => {
+ setSearchValue(value);
+ setPagination(prev => ({ ...prev, current: 1 }));
+ fetchPushHistory(1);
+ };
+
+ // 类型筛选处理
+ const handleTypeFilterChange = (value: string) => {
+ setTypeFilter(value);
+ setPagination(prev => ({ ...prev, current: 1 }));
+ // 延迟执行,等待状态更新
+ setTimeout(() => {
+ fetchPushHistory(1);
+ }, 0);
+ };
+
+ // 状态筛选处理
+ const handleStatusFilterChange = (value: string) => {
+ setStatusFilter(value);
+ setPagination(prev => ({ ...prev, current: 1 }));
+ // 延迟执行,等待状态更新
+ setTimeout(() => {
+ fetchPushHistory(1);
+ }, 0);
+ };
+
+ // 分页处理
+ const handlePageChange = (page: number) => {
+ setPagination(prev => ({ ...prev, current: page }));
+ fetchPushHistory(page);
+ };
+
+ // 查看详情
+ const handleViewDetail = (record: PushHistoryRecord) => {
+ // TODO: 打开详情弹窗或跳转到详情页
+ console.log("查看详情:", record);
+ message.info("查看详情功能开发中");
+ };
+
+ // 获取推送类型标签
+ const getPushTypeTag = (type: PushType) => {
+ const typeMap = {
+ [PushType.FRIEND_MESSAGE]: { text: "好友消息", color: "#666" },
+ [PushType.GROUP_MESSAGE]: { text: "群消息", color: "#666" },
+ [PushType.GROUP_ANNOUNCEMENT]: { text: "群公告", color: "#666" },
+ };
+ const config = typeMap[type] || { text: "未知", color: "#666" };
+ return (
+
+ {config.text}
+
+ );
+ };
+
+ // 获取状态标签
+ const getStatusTag = (status: PushStatus) => {
+ const statusMap = {
+ [PushStatus.COMPLETED]: {
+ text: "已完成",
+ color: "#52c41a",
+ icon: ,
+ },
+ [PushStatus.IN_PROGRESS]: {
+ text: "进行中",
+ color: "#1890ff",
+ icon: ,
+ },
+ [PushStatus.FAILED]: {
+ text: "失败",
+ color: "#ff4d4f",
+ icon: ,
+ },
+ };
+ const config = statusMap[status] || {
+ text: "未知",
+ color: "#666",
+ icon: null,
+ };
+ return (
+
+ {config.text}
+
+ );
+ };
+
+ // 表格列定义
+ const columns = [
+ {
+ title: "推送类型",
+ dataIndex: "pushType",
+ key: "pushType",
+ width: 120,
+ render: (type: PushType) => getPushTypeTag(type),
+ },
+ {
+ title: "推送内容",
+ dataIndex: "pushContent",
+ key: "pushContent",
+ ellipsis: true,
+ render: (text: string) => (
+ {text}
+ ),
+ },
+ {
+ title: "目标数量",
+ dataIndex: "targetCount",
+ key: "targetCount",
+ width: 100,
+ align: "center" as const,
+ render: (count: number) => {count},
+ },
+ {
+ title: "成功数",
+ dataIndex: "successCount",
+ key: "successCount",
+ width: 100,
+ align: "center" as const,
+ render: (count: number) => (
+ {count}
+ ),
+ },
+ {
+ title: "失败数",
+ dataIndex: "failureCount",
+ key: "failureCount",
+ width: 100,
+ align: "center" as const,
+ render: (count: number) => (
+ {count}
+ ),
+ },
+ {
+ title: "状态",
+ dataIndex: "status",
+ key: "status",
+ width: 120,
+ align: "center" as const,
+ render: (status: PushStatus) => getStatusTag(status),
+ },
+ {
+ title: "创建时间",
+ dataIndex: "createTime",
+ key: "createTime",
+ width: 180,
+ render: (time: string) => (
+ {time}
+ ),
+ },
+ {
+ title: "操作",
+ key: "action",
+ width: 80,
+ align: "center" as const,
+ render: (_: any, record: PushHistoryRecord) => (
+ }
+ onClick={() => handleViewDetail(record)}
+ style={{ color: "#1890ff" }}
+ >
+ 查看
+
+ ),
+ },
+ ];
+
+ return (
+
+ navigate("/pc/powerCenter/message-push-assistant")}
+ />
+
+ }
+ footer={null}
+ >
+
+ {/* 筛选区域 */}
+
+
+
推送历史记录
+
+
+ }
+ value={searchValue}
+ onChange={e => setSearchValue(e.target.value)}
+ onPressEnter={e => handleSearch(e.currentTarget.value)}
+ className={styles.searchInput}
+ allowClear
+ />
+
+
+
+
+
+ {/* 数据表格 */}
+
+
+ {/* 分页组件 */}
+ {pagination.total > 0 && (
+
+
+ 共{pagination.total}条记录
+
+
+
+ )}
+
+
+ );
+};
+
+export default PushHistory;
+
+
+
+
+
+
+
+
diff --git a/Touchkebao/src/router/module/pc.tsx b/Touchkebao/src/router/module/pc.tsx
index 98578346..fa3dcd96 100644
--- a/Touchkebao/src/router/module/pc.tsx
+++ b/Touchkebao/src/router/module/pc.tsx
@@ -7,6 +7,10 @@ import CommunicationRecord from "@/pages/pc/ckbox/powerCenter/communication-reco
import ContentManagement from "@/pages/pc/ckbox/powerCenter/content-management/index";
import AiTraining from "@/pages/pc/ckbox/powerCenter/ai-training";
import AutoGreeting from "@/pages/pc/ckbox/powerCenter/auto-greeting";
+import MessagePushAssistant from "@/pages/pc/ckbox/powerCenter/message-push-assistant";
+import CreatePushTask from "@/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task";
+import DataStatistics from "@/pages/pc/ckbox/powerCenter/data-statistics";
+import PushHistory from "@/pages/pc/ckbox/powerCenter/push-history";
import CommonConfig from "@/pages/pc/ckbox/commonConfig";
const ckboxRoutes = [
{
@@ -50,6 +54,22 @@ const ckboxRoutes = [
path: "powerCenter/auto-greeting",
element: ,
},
+ {
+ path: "powerCenter/message-push-assistant",
+ element: ,
+ },
+ {
+ path: "powerCenter/message-push-assistant/create-push-task/:pushType",
+ element: ,
+ },
+ {
+ path: "powerCenter/data-statistics",
+ element: ,
+ },
+ {
+ path: "powerCenter/push-history",
+ element: ,
+ },
],
},
];