From 5a287a42ac4deca706504f982d52f768d30e7b03 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: Mon, 10 Nov 2025 18:08:53 +0800 Subject: [PATCH] Refactor CreatePushTask component to streamline account and contact selection process. Update styles for layout consistency, enhance user experience with improved step indicators, and simplify state management for selected accounts and contacts. --- .../src/components/Layout/layout.module.scss | 1 - .../components/StepSelectAccount/index.tsx | 107 +++ .../components/StepSelectContacts/index.tsx | 339 ++++++++ .../components/StepSendMessage/index.tsx | 184 ++++ .../create-push-task/index.module.scss | 5 +- .../create-push-task/index.tsx | 805 +++--------------- .../create-push-task/types.ts | 15 + 7 files changed, 752 insertions(+), 704 deletions(-) create mode 100644 Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/components/StepSelectAccount/index.tsx create mode 100644 Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/components/StepSelectContacts/index.tsx create mode 100644 Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/components/StepSendMessage/index.tsx create mode 100644 Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/types.ts diff --git a/Touchkebao/src/components/Layout/layout.module.scss b/Touchkebao/src/components/Layout/layout.module.scss index 3818f44d..c43e8901 100644 --- a/Touchkebao/src/components/Layout/layout.module.scss +++ b/Touchkebao/src/components/Layout/layout.module.scss @@ -2,7 +2,6 @@ display: flex; height: 100vh; flex-direction: column; - background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%); } .container main { diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/components/StepSelectAccount/index.tsx b/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/components/StepSelectAccount/index.tsx new file mode 100644 index 00000000..278889cc --- /dev/null +++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/components/StepSelectAccount/index.tsx @@ -0,0 +1,107 @@ +"use client"; + +import React, { useMemo, useState } from "react"; +import { Avatar, Empty, Input } from "antd"; +import { CheckCircleOutlined, SearchOutlined } from "@ant-design/icons"; + +import styles from "../../index.module.scss"; + +interface StepSelectAccountProps { + customerList: any[]; + selectedAccounts: any[]; + onChange: (accounts: any[]) => void; +} + +const StepSelectAccount: React.FC = ({ + customerList, + selectedAccounts, + onChange, +}) => { + const [searchKeyword, setSearchKeyword] = useState(""); + + 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]); + + const handleAccountToggle = (account: any) => { + const isSelected = selectedAccounts.some(a => a.id === account.id); + if (isSelected) { + onChange(selectedAccounts.filter(a => a.id !== account.id)); + return; + } + onChange([...selectedAccounts, account]); + }; + + return ( +
+
+

选择微信账号

+

可选择多个微信账号进行推送

+
+ +
+ } + value={searchKeyword} + onChange={e => setSearchKeyword(e.target.value)} + allowClear + /> +
+ + {filteredAccounts.length > 0 ? ( +
+ {filteredAccounts.map(account => { + const isSelected = selectedAccounts.some(s => s.id === account.id); + return ( +
handleAccountToggle(account)} + > + + {!account.avatar && + (account.nickname || account.name || "").charAt(0)} + +
+
+ {account.nickname || account.name || "未知"} +
+
+ + + {account.isOnline ? "在线" : "离线"} + +
+
+ {isSelected && ( + + )} +
+ ); + })} +
+ ) : ( + + )} +
+ ); +}; + +export default StepSelectAccount; diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/components/StepSelectContacts/index.tsx b/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/components/StepSelectContacts/index.tsx new file mode 100644 index 00000000..b804cafc --- /dev/null +++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/components/StepSelectContacts/index.tsx @@ -0,0 +1,339 @@ +"use client"; + +import React, { useCallback, useEffect, useMemo, useState } from "react"; +import { + Avatar, + Button, + Checkbox, + Empty, + Input, + Pagination, + Spin, + message, +} from "antd"; +import { + CloseOutlined, + SearchOutlined, + TeamOutlined, + UserOutlined, +} from "@ant-design/icons"; + +import { getContactList, getGroupList } from "@/pages/pc/ckbox/weChat/api"; + +import styles from "../../index.module.scss"; +import { ContactItem, PushType } from "../../types"; + +interface StepSelectContactsProps { + pushType: PushType; + selectedAccounts: any[]; + selectedContacts: ContactItem[]; + onChange: (contacts: ContactItem[]) => void; +} + +const StepSelectContacts: React.FC = ({ + pushType, + selectedAccounts, + selectedContacts, + onChange, +}) => { + const [contactsData, setContactsData] = useState([]); + const [loadingContacts, setLoadingContacts] = useState(false); + const [page, setPage] = useState(1); + const [searchValue, setSearchValue] = useState(""); + const [total, setTotal] = useState(0); + + const pageSize = 20; + + const stepTitle = useMemo(() => { + switch (pushType) { + case "friend-message": + return "好友"; + case "group-message": + case "group-announcement": + return "群"; + default: + return "选择"; + } + }, [pushType]); + + const loadContacts = useCallback(async () => { + if (selectedAccounts.length === 0) { + setContactsData([]); + setTotal(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, + limit: pageSize, + wechatAccountId: accountId, + }; + + if (searchValue.trim()) { + params.keyword = searchValue.trim(); + } + + const response = + pushType === "friend-message" + ? await getContactList(params) + : await getGroupList(params); + + const data = + response.data?.list || response.data || response.list || []; + const totalValue = response.data?.total || response.total || 0; + + const filteredData = data.filter((item: any) => { + const itemAccountId = item.wechatAccountId || item.accountId; + return itemAccountId === accountId; + }); + + filteredData.forEach((item: ContactItem) => { + if (!allData.some(d => d.id === item.id)) { + allData.push(item); + } + }); + + totalCount += totalValue; + } + + setContactsData(allData); + setTotal(totalCount > 0 ? totalCount : allData.length); + } catch (error) { + console.error("加载数据失败:", error); + message.error("加载数据失败"); + setContactsData([]); + setTotal(0); + } finally { + setLoadingContacts(false); + } + }, [page, pushType, searchValue, selectedAccounts]); + + useEffect(() => { + loadContacts(); + }, [loadContacts]); + + useEffect(() => { + if (!searchValue.trim()) { + return; + } + setPage(1); + }, [searchValue]); + + useEffect(() => { + setPage(1); + if (selectedAccounts.length === 0 && selectedContacts.length > 0) { + onChange([]); + } + }, [onChange, selectedAccounts, selectedContacts.length]); + + const handleSearchChange = (value: string) => { + setSearchValue(value); + if (!value.trim()) { + setPage(1); + } + }; + + const filteredContacts = useMemo(() => { + if (searchValue.trim()) { + return contactsData; + } + return contactsData; + }, [contactsData, searchValue]); + + const handleContactToggle = (contact: ContactItem) => { + const isSelected = selectedContacts.some(c => c.id === contact.id); + if (isSelected) { + onChange(selectedContacts.filter(c => c.id !== contact.id)); + return; + } + onChange([...selectedContacts, contact]); + }; + + const handleRemoveContact = (contactId: number) => { + onChange(selectedContacts.filter(c => c.id !== contactId)); + }; + + const handleSelectAllContacts = () => { + if (filteredContacts.length === 0) return; + const allSelected = filteredContacts.every(contact => + selectedContacts.some(c => c.id === contact.id), + ); + if (allSelected) { + const currentIds = filteredContacts.map(c => c.id); + onChange(selectedContacts.filter(c => !currentIds.includes(c.id))); + return; + } + const toAdd = filteredContacts.filter( + contact => !selectedContacts.some(c => c.id === contact.id), + ); + onChange([...selectedContacts, ...toAdd]); + }; + + const handlePageChange = (p: number) => { + setPage(p); + }; + + return ( +
+
+
+

选择{stepTitle}

+

从{stepTitle}列表中选择推送对象

+
+
+ } + value={searchValue} + onChange={e => handleSearchChange(e.target.value)} + allowClear + /> + +
+
+
+
+ + {stepTitle}列表(共{total}个) + +
+
+ {loadingContacts ? ( +
+ + 加载中... +
+ ) : filteredContacts.length > 0 ? ( + filteredContacts.map(contact => { + const isSelected = selectedContacts.some( + c => c.id === contact.id, + ); + return ( +
handleContactToggle(contact)} + > + + + ) : ( + + ) + } + /> +
+
+ {contact.nickname} +
+ {contact.conRemark && ( +
+ {contact.conRemark} +
+ )} +
+ {contact.type === "group" && ( + + )} +
+ ); + }) + ) : ( + + )} +
+ {total > 0 && ( +
+ +
+ )} +
+ +
+
+ + 已选{stepTitle}列表(共{selectedContacts.length}个) + + {selectedContacts.length > 0 && ( + + )} +
+
+ {selectedContacts.length > 0 ? ( + selectedContacts.map(contact => ( +
+
+ + ) : ( + + ) + } + /> +
+
{contact.nickname}
+ {contact.conRemark && ( +
+ {contact.conRemark} +
+ )} +
+ {contact.type === "group" && ( + + )} +
+ { + e.stopPropagation(); + handleRemoveContact(contact.id); + }} + /> +
+ )) + ) : ( + + )} +
+
+
+
+
+ ); +}; + +export default StepSelectContacts; diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/components/StepSendMessage/index.tsx b/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/components/StepSendMessage/index.tsx new file mode 100644 index 00000000..86041eca --- /dev/null +++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/components/StepSendMessage/index.tsx @@ -0,0 +1,184 @@ +"use client"; + +import React, { useState } from "react"; +import { Button, Input, Select, Slider, Switch } from "antd"; + +import styles from "../../index.module.scss"; +import { ContactItem } from "../../types"; +import ContentSelection from "@/components/ContentSelection"; +import { ContentItem } from "@/components/ContentSelection/data"; + +interface StepSendMessageProps { + selectedAccounts: any[]; + selectedContacts: ContactItem[]; + targetLabel: string; + messageContent: string; + onMessageContentChange: (value: string) => void; + friendInterval: number; + onFriendIntervalChange: (value: number) => void; + messageInterval: number; + onMessageIntervalChange: (value: number) => void; + selectedTag: string; + onSelectedTagChange: (value: string) => void; + aiRewriteEnabled: boolean; + onAiRewriteToggle: (value: boolean) => void; + aiPrompt: string; + onAiPromptChange: (value: string) => void; +} + +const StepSendMessage: React.FC = ({ + selectedAccounts, + selectedContacts, + targetLabel, + messageContent, + onMessageContentChange, + friendInterval, + onFriendIntervalChange, + messageInterval, + onMessageIntervalChange, + selectedTag, + onSelectedTagChange, + aiRewriteEnabled, + onAiRewriteToggle, + aiPrompt, + onAiPromptChange, +}) => { + const [selectedContentLibraries, setSelectedContentLibraries] = useState< + ContentItem[] + >([]); + + return ( +
+
+
+
+
模拟推送内容
+
+
当前编辑话术
+
+ {messageContent || "开始添加消息内容..."} +
+
+
+ +
+
已保存话术组
+ +
+ +
+ onMessageContentChange(e.target.value)} + rows={4} + onKeyDown={e => { + if (e.ctrlKey && e.key === "Enter") { + e.preventDefault(); + onMessageContentChange(`${messageContent}\n`); + } + }} + /> +
+
+
+ + AI智能话术改写 + {aiRewriteEnabled && ( + onAiPromptChange(e.target.value)} + style={{ marginLeft: 12, width: 200 }} + /> + )} + +
+
+ 按住CTRL+ENTER换行,已选择{selectedContentLibraries.length} + 个话术组,已选择{selectedContacts.length} + 个进行推送 +
+
+
+ +
+
+
相关设置
+
+
好友间间隔
+
+ 间隔时间(秒) + onFriendIntervalChange(value as number)} + style={{ flex: 1, margin: "0 16px" }} + /> + {friendInterval} - 20 +
+
+
+
消息间间隔
+
+ 间隔时间(秒) + onMessageIntervalChange(value as number)} + style={{ flex: 1, margin: "0 16px" }} + /> + {messageInterval} - 12 +
+
+
+ +
+
完成打标签
+ +
+ +
+
推送预览
+
    +
  • 推送账号: {selectedAccounts.length}个
  • +
  • + 推送{targetLabel}: {selectedContacts.length}个 +
  • +
  • 话术组数: {selectedContentLibraries.length}个
  • +
  • 随机推送: 否
  • +
  • 预计耗时: ~1分钟
  • +
+
+
+
+
+ ); +}; + +export default StepSendMessage; 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 index 55504547..c9c1ed8e 100644 --- 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 @@ -1,7 +1,5 @@ .container { - padding: 24px; - background: #f5f5f5; - min-height: calc(100vh - 64px); + padding: 15px; display: flex; flex-direction: column; } @@ -586,6 +584,7 @@ background: #fff; border-radius: 12px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); + margin: 20px; .footerLeft { font-size: 14px; 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 index 2c3ee85a..c09a1d53 100644 --- 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 @@ -1,29 +1,7 @@ -import React, { useState, useEffect, useMemo } from "react"; +import React, { useEffect, useMemo, useState } 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, - SendOutlined, - CopyOutlined, - DeleteOutlined, -} from "@ant-design/icons"; +import { Button, message } from "antd"; +import { SendOutlined } from "@ant-design/icons"; import PowerNavigation from "@/components/PowerNavtion"; import Layout from "@/components/Layout/LayoutFiexd"; import styles from "./index.module.scss"; @@ -31,33 +9,18 @@ import { useCustomerStore, updateCustomerList, } from "@/store/module/weChat/customer"; -import { - getContactList, - getGroupList, - getCustomerList, -} from "@/pages/pc/ckbox/weChat/api"; +import { getCustomerList } from "@/pages/pc/ckbox/weChat/api"; -export type PushType = - | "friend-message" - | "group-message" - | "group-announcement"; - -interface ContactItem { - id: number; - nickname: string; - avatar?: string; - conRemark?: string; - wechatId?: string; - gender?: number; - region?: string; - type?: "friend" | "group"; -} +import StepSelectAccount from "./components/StepSelectAccount"; +import StepSelectContacts from "./components/StepSelectContacts"; +import StepSendMessage from "./components/StepSendMessage"; +import { ContactItem, PushType } from "./types"; +import StepIndicator from "@/components/StepIndicator"; const CreatePushTask: React.FC = () => { const navigate = useNavigate(); const { pushType } = useParams<{ pushType: PushType }>(); - // 验证推送类型 const validPushType: PushType = pushType === "friend-message" || pushType === "group-message" || @@ -66,7 +29,6 @@ const CreatePushTask: React.FC = () => { : "friend-message"; const [currentStep, setCurrentStep] = useState(1); - const [searchKeyword, setSearchKeyword] = useState(""); const [selectedAccounts, setSelectedAccounts] = useState([]); const [selectedContacts, setSelectedContacts] = useState([]); const [messageContent, setMessageContent] = useState(""); @@ -75,23 +37,9 @@ const CreatePushTask: React.FC = () => { 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); - // 如果 customerList 为空,重新请求客服账户列表接口 useEffect(() => { if (customerList.length === 0) { getCustomerList() @@ -105,8 +53,7 @@ const CreatePushTask: React.FC = () => { } }, [customerList.length]); - // 获取标题和描述 - const getTitle = () => { + const title = useMemo(() => { switch (validPushType) { case "friend-message": return "好友消息推送"; @@ -117,14 +64,11 @@ const CreatePushTask: React.FC = () => { default: return "消息推送"; } - }; + }, [validPushType]); - const getSubtitle = () => { - return "智能批量推送,AI智能话术改写"; - }; + const subtitle = "智能批量推送,AI智能话术改写"; - // 步骤2的标题 - const getStep2Title = () => { + const step2Title = useMemo(() => { switch (validPushType) { case "friend-message": return "好友"; @@ -132,188 +76,14 @@ const CreatePushTask: React.FC = () => { case "group-announcement": return "群"; default: - return "选择"; + return "对象"; } - }; + }, [validPushType]); - // 重置状态 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 handleClearSelection = () => { - 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) { @@ -321,7 +91,10 @@ const CreatePushTask: React.FC = () => { return; } setCurrentStep(2); - } else if (currentStep === 2) { + return; + } + + if (currentStep === 2) { if (selectedContacts.length === 0) { message.warning( `请至少选择一个${validPushType === "friend-message" ? "好友" : "群"}`, @@ -332,14 +105,20 @@ const CreatePushTask: React.FC = () => { } }; - // 上一步 const handlePrev = () => { if (currentStep > 1) { setCurrentStep(currentStep - 1); } }; - // 发送 + const handleClearAccounts = () => { + if (selectedAccounts.length === 0) { + message.info("暂无已选微信账号"); + return; + } + setSelectedAccounts([]); + }; + const handleSend = () => { if (!messageContent.trim()) { message.warning("请输入消息内容"); @@ -361,460 +140,42 @@ const CreatePushTask: React.FC = () => { navigate("/pc/powerCenter/message-push-assistant"); }; - // 渲染步骤1:选择微信账号 - const renderStep1 = () => { - return ( -
-
-

选择微信账号

-

可选择多个微信账号进行推送

-
- -
- } - value={searchKeyword} - onChange={e => setSearchKeyword(e.target.value)} - allowClear - /> -
- - {/* 账号列表 */} - {filteredAccounts.length > 0 ? ( -
- {filteredAccounts.map(account => { - const isSelected = selectedAccounts.some( - s => s.id === account.id, - ); - return ( -
handleAccountToggle(account)} - > - - {!account.avatar && - (account.nickname || account.name || "").charAt(0)} - -
-
- {account.nickname || account.name || "未知"} -
-
- - - {account.isOnline ? "在线" : "离线"} - -
-
- {isSelected && ( - - )} -
- ); - })} -
- ) : ( - - )} -
- ); - }; - - // 渲染步骤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}条消息 - -
-
-
-
- {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()} -
- - {/* 底部操作栏 */} + footer={
{currentStep === 1 && ( @@ -822,12 +183,12 @@ const CreatePushTask: React.FC = () => { )} {currentStep === 2 && ( - 已选择{selectedContacts.length}个{getStep2Title()} + 已选择{selectedContacts.length}个{step2Title} )} {currentStep === 3 && ( - 推送账号: {selectedAccounts.length}个, 推送{getStep2Title()}:{" "} + 推送账号: {selectedAccounts.length}个, 推送{step2Title}:{" "} {selectedContacts.length}个 )} @@ -835,7 +196,12 @@ const CreatePushTask: React.FC = () => {
{currentStep === 1 && ( <> - + @@ -863,6 +229,45 @@ const CreatePushTask: React.FC = () => { )}
+ } + > +
+
+ {currentStep === 1 && ( + + )} + {currentStep === 2 && ( + + )} + {currentStep === 3 && ( + + )} +
); diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/types.ts b/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/types.ts new file mode 100644 index 00000000..c0505f80 --- /dev/null +++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/types.ts @@ -0,0 +1,15 @@ +export type PushType = + | "friend-message" + | "group-message" + | "group-announcement"; + +export interface ContactItem { + id: number; + nickname: string; + avatar?: string; + conRemark?: string; + wechatId?: string; + gender?: number; + region?: string; + type?: "friend" | "group"; +}