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"; +}