重构PushTask组件以支持用于消息管理的脚本组。引入新的状态变量来处理脚本消息和组,更新消息发送逻辑,并增强用于添加和管理脚本组的UI。清理样式,改善消息输入区的用户体验。
This commit is contained in:
@@ -0,0 +1,147 @@
|
|||||||
|
.chatFooter {
|
||||||
|
background: #f7f7f7;
|
||||||
|
border-top: 1px solid #e1e1e1;
|
||||||
|
padding: 0;
|
||||||
|
height: auto;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputContainer {
|
||||||
|
padding: 8px 12px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputToolbar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leftTool {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbarButton {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #666;
|
||||||
|
font-size: 16px;
|
||||||
|
transition: all 0.15s;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #e6e6e6;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background: #d9d9d9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputArea {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputWrapper {
|
||||||
|
border: 1px solid #d1d1d1;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: #fff;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&:focus-within {
|
||||||
|
border-color: #07c160;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.messageInput {
|
||||||
|
width: 100%;
|
||||||
|
border: none;
|
||||||
|
resize: none;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.4;
|
||||||
|
padding: 8px 10px;
|
||||||
|
background: transparent;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
box-shadow: none;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
color: #b3b3b3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sendButtonArea {
|
||||||
|
padding: 8px 10px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sendButton {
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-weight: normal;
|
||||||
|
min-width: 60px;
|
||||||
|
font-size: 13px;
|
||||||
|
background: #07c160;
|
||||||
|
border-color: #07c160;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #06ad56;
|
||||||
|
border-color: #06ad56;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background: #059748;
|
||||||
|
border-color: #059748;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
background: #b3b3b3;
|
||||||
|
border-color: #b3b3b3;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hintButton {
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
color: #666;
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputHint {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #999;
|
||||||
|
text-align: right;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.inputToolbar {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sendButtonArea {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,265 @@
|
|||||||
|
.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.step3Content {
|
||||||
|
display: flex;
|
||||||
|
gap: 24px;
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
|
.leftColumn {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rightColumn {
|
||||||
|
width: 400px;
|
||||||
|
flex: 1;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1200px) {
|
||||||
|
.step3Content {
|
||||||
|
.rightColumn {
|
||||||
|
width: 350px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.step3Content {
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.leftColumn {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rightColumn {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import ContentSelection from "@/components/ContentSelection";
|
||||||
|
import { ContentItem } from "@/components/ContentSelection/data";
|
||||||
|
import InputMessage from "./InputMessage/InputMessage";
|
||||||
|
import styles from "./index.module.scss";
|
||||||
|
|
||||||
|
interface StepSendMessageProps {
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import React from "react";
|
||||||
|
import ContentSelection from "@/components/ContentSelection";
|
||||||
|
import type { ContentItem } from "@/components/ContentSelection/data";
|
||||||
|
import styles from "./index.module.scss";
|
||||||
|
|
||||||
|
interface ContentLibrarySelectorProps {
|
||||||
|
selectedContentLibraries: ContentItem[];
|
||||||
|
onSelectedContentLibrariesChange: (selectedItems: ContentItem[]) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ContentLibrarySelector: React.FC<ContentLibrarySelectorProps> = ({
|
||||||
|
selectedContentLibraries,
|
||||||
|
onSelectedContentLibrariesChange,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className={styles.contentLibrarySelector}>
|
||||||
|
<div className={styles.contentLibraryHeader}>
|
||||||
|
<div className={styles.contentLibraryTitle}>内容库选择</div>
|
||||||
|
<div className={styles.contentLibraryHint}>
|
||||||
|
选择内容库可快速引用现有话术
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ContentSelection
|
||||||
|
selectedOptions={selectedContentLibraries}
|
||||||
|
onSelect={onSelectedContentLibrariesChange}
|
||||||
|
onConfirm={onSelectedContentLibrariesChange}
|
||||||
|
placeholder="请选择内容库"
|
||||||
|
selectedListMaxHeight={200}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ContentLibrarySelector;
|
||||||
@@ -0,0 +1,251 @@
|
|||||||
|
import React, { useCallback, useEffect, useState } from "react";
|
||||||
|
import { Button, Input, message as antdMessage } from "antd";
|
||||||
|
import { FolderOutlined, PictureOutlined } from "@ant-design/icons";
|
||||||
|
|
||||||
|
import { EmojiPicker } from "@/components/EmojiSeclection";
|
||||||
|
import { EmojiInfo } from "@/components/EmojiSeclection/wechatEmoji";
|
||||||
|
import SimpleFileUpload from "@/components/Upload/SimpleFileUpload";
|
||||||
|
import AudioRecorder from "@/components/Upload/AudioRecorder";
|
||||||
|
|
||||||
|
import styles from "./index.module.scss";
|
||||||
|
|
||||||
|
const { TextArea } = Input;
|
||||||
|
|
||||||
|
type FileTypeValue = 1 | 2 | 3 | 4 | 5;
|
||||||
|
|
||||||
|
interface InputMessageProps {
|
||||||
|
defaultValue?: string;
|
||||||
|
onContentChange?: (value: string) => void;
|
||||||
|
onSend?: (value: string) => void;
|
||||||
|
clearOnSend?: boolean;
|
||||||
|
placeholder?: string;
|
||||||
|
hint?: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FileType: Record<string, FileTypeValue> = {
|
||||||
|
TEXT: 1,
|
||||||
|
IMAGE: 2,
|
||||||
|
VIDEO: 3,
|
||||||
|
AUDIO: 4,
|
||||||
|
FILE: 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
const getMsgTypeByFileFormat = (filePath: string): number => {
|
||||||
|
const extension = filePath.toLowerCase().split(".").pop() || "";
|
||||||
|
const imageFormats = [
|
||||||
|
"jpg",
|
||||||
|
"jpeg",
|
||||||
|
"png",
|
||||||
|
"gif",
|
||||||
|
"bmp",
|
||||||
|
"webp",
|
||||||
|
"svg",
|
||||||
|
"ico",
|
||||||
|
];
|
||||||
|
if (imageFormats.includes(extension)) {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
const videoFormats = [
|
||||||
|
"mp4",
|
||||||
|
"avi",
|
||||||
|
"mov",
|
||||||
|
"wmv",
|
||||||
|
"flv",
|
||||||
|
"mkv",
|
||||||
|
"webm",
|
||||||
|
"3gp",
|
||||||
|
"rmvb",
|
||||||
|
];
|
||||||
|
if (videoFormats.includes(extension)) {
|
||||||
|
return 43;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 49;
|
||||||
|
};
|
||||||
|
|
||||||
|
const InputMessage: React.FC<InputMessageProps> = ({
|
||||||
|
defaultValue = "",
|
||||||
|
onContentChange,
|
||||||
|
onSend,
|
||||||
|
clearOnSend = false,
|
||||||
|
placeholder = "输入消息...",
|
||||||
|
hint,
|
||||||
|
}) => {
|
||||||
|
const [inputValue, setInputValue] = useState(defaultValue);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (defaultValue !== inputValue) {
|
||||||
|
setInputValue(defaultValue);
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [defaultValue]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
onContentChange?.(inputValue);
|
||||||
|
}, [inputValue, onContentChange]);
|
||||||
|
|
||||||
|
const handleInputChange = useCallback(
|
||||||
|
(e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||||
|
setInputValue(e.target.value);
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSend = useCallback(() => {
|
||||||
|
const content = inputValue.trim();
|
||||||
|
if (!content) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onSend?.(content);
|
||||||
|
if (clearOnSend) {
|
||||||
|
setInputValue("");
|
||||||
|
}
|
||||||
|
antdMessage.success("已添加消息内容");
|
||||||
|
}, [clearOnSend, inputValue, onSend]);
|
||||||
|
|
||||||
|
const handleKeyPress = useCallback(
|
||||||
|
(e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
||||||
|
if (e.key !== "Enter") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.ctrlKey || e.metaKey) {
|
||||||
|
e.preventDefault();
|
||||||
|
const target = e.currentTarget;
|
||||||
|
const { selectionStart, selectionEnd, value } = target;
|
||||||
|
const nextValue =
|
||||||
|
value.slice(0, selectionStart) + "\n" + value.slice(selectionEnd);
|
||||||
|
setInputValue(nextValue);
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
const cursorPosition = selectionStart + 1;
|
||||||
|
target.selectionStart = cursorPosition;
|
||||||
|
target.selectionEnd = cursorPosition;
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!e.shiftKey) {
|
||||||
|
e.preventDefault();
|
||||||
|
handleSend();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[handleSend],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleEmojiSelect = useCallback((emoji: EmojiInfo) => {
|
||||||
|
setInputValue(prev => prev + `[${emoji.name}]`);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleFileUploaded = useCallback(
|
||||||
|
(
|
||||||
|
filePath: { url: string; name: string; durationMs?: number },
|
||||||
|
fileType: FileTypeValue,
|
||||||
|
) => {
|
||||||
|
let msgType = 1;
|
||||||
|
let content: string | Record<string, unknown> = filePath.url;
|
||||||
|
if ([FileType.TEXT].includes(fileType)) {
|
||||||
|
msgType = getMsgTypeByFileFormat(filePath.url);
|
||||||
|
} else if ([FileType.IMAGE].includes(fileType)) {
|
||||||
|
msgType = 3;
|
||||||
|
} else if ([FileType.AUDIO].includes(fileType)) {
|
||||||
|
msgType = 34;
|
||||||
|
content = JSON.stringify({
|
||||||
|
url: filePath.url,
|
||||||
|
durationMs: filePath.durationMs,
|
||||||
|
});
|
||||||
|
} else if ([FileType.FILE].includes(fileType)) {
|
||||||
|
msgType = getMsgTypeByFileFormat(filePath.url);
|
||||||
|
if (msgType === 49) {
|
||||||
|
content = JSON.stringify({
|
||||||
|
type: "file",
|
||||||
|
title: filePath.name,
|
||||||
|
url: filePath.url,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("模拟上传内容: ", {
|
||||||
|
msgType,
|
||||||
|
content,
|
||||||
|
});
|
||||||
|
antdMessage.success("附件上传成功,可在推送时使用");
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleAudioUploaded = useCallback(
|
||||||
|
(audioData: { name: string; url: string; durationMs?: number }) => {
|
||||||
|
handleFileUploaded(
|
||||||
|
{
|
||||||
|
name: audioData.name,
|
||||||
|
url: audioData.url,
|
||||||
|
durationMs: audioData.durationMs,
|
||||||
|
},
|
||||||
|
FileType.AUDIO,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[handleFileUploaded],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.chatFooter}>
|
||||||
|
<div className={styles.inputContainer}>
|
||||||
|
<div className={styles.inputToolbar}>
|
||||||
|
<div className={styles.leftTool}>
|
||||||
|
<EmojiPicker onEmojiSelect={handleEmojiSelect} />
|
||||||
|
|
||||||
|
<SimpleFileUpload
|
||||||
|
onFileUploaded={fileInfo =>
|
||||||
|
handleFileUploaded(fileInfo, FileType.IMAGE)
|
||||||
|
}
|
||||||
|
maxSize={10}
|
||||||
|
type={1}
|
||||||
|
slot={
|
||||||
|
<Button
|
||||||
|
className={styles.toolbarButton}
|
||||||
|
type="text"
|
||||||
|
icon={<PictureOutlined />}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<SimpleFileUpload
|
||||||
|
onFileUploaded={fileInfo =>
|
||||||
|
handleFileUploaded(fileInfo, FileType.FILE)
|
||||||
|
}
|
||||||
|
maxSize={20}
|
||||||
|
type={4}
|
||||||
|
slot={
|
||||||
|
<Button
|
||||||
|
className={styles.toolbarButton}
|
||||||
|
type="text"
|
||||||
|
icon={<FolderOutlined />}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<AudioRecorder
|
||||||
|
onAudioUploaded={handleAudioUploaded}
|
||||||
|
className={styles.toolbarButton}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.inputArea}>
|
||||||
|
<div className={styles.inputWrapper}>
|
||||||
|
<TextArea
|
||||||
|
value={inputValue}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
onKeyDown={handleKeyPress}
|
||||||
|
placeholder={placeholder}
|
||||||
|
className={styles.messageInput}
|
||||||
|
autoSize={{ minRows: 3, maxRows: 6 }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{hint && <div className={styles.inputHint}>{hint}</div>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default InputMessage;
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
.chatFooter {
|
||||||
|
height: auto;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputContainer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputToolbar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leftTool {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbarButton {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #666;
|
||||||
|
font-size: 16px;
|
||||||
|
transition: all 0.15s;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #e6e6e6;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background: #d9d9d9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputArea {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputWrapper {
|
||||||
|
border: 1px solid #d1d1d1;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: #fff;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&:focus-within {
|
||||||
|
border-color: #07c160;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.messageInput {
|
||||||
|
width: 100%;
|
||||||
|
border: none;
|
||||||
|
resize: none;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.4;
|
||||||
|
padding: 8px 10px;
|
||||||
|
background: transparent;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
box-shadow: none;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
color: #b3b3b3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sendButtonArea {
|
||||||
|
padding: 8px 10px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sendButton {
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-weight: normal;
|
||||||
|
min-width: 60px;
|
||||||
|
font-size: 13px;
|
||||||
|
background: #07c160;
|
||||||
|
border-color: #07c160;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #06ad56;
|
||||||
|
border-color: #06ad56;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background: #059748;
|
||||||
|
border-color: #059748;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
background: #b3b3b3;
|
||||||
|
border-color: #b3b3b3;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hintButton {
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
color: #666;
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputHint {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #999;
|
||||||
|
text-align: right;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.inputToolbar {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sendButtonArea {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,375 @@
|
|||||||
|
.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.step3Content {
|
||||||
|
display: flex;
|
||||||
|
gap: 24px;
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
|
.leftColumn {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rightColumn {
|
||||||
|
width: 400px;
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.previewHeader {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.previewHeaderTitle {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1a1a1a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.messagePreview {
|
||||||
|
border: 2px dashed #52c41a;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 15px;
|
||||||
|
|
||||||
|
.messageBubble {
|
||||||
|
min-height: 100px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 6px;
|
||||||
|
color: #666;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.6;
|
||||||
|
|
||||||
|
.currentEditingLabel {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #52c41a;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messageText {
|
||||||
|
color: #1a1a1a;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messagePlaceholder {
|
||||||
|
color: #999;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messageList {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messageItem {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 10px 0;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messageText {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messageAction {
|
||||||
|
color: #ff4d4f;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.scriptNameInput {
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.savedScriptGroups {
|
||||||
|
.contentLibrarySelector {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding: 16px;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #e8e8e8;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contentLibraryHeader {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contentLibraryTitle {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1a1a1a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contentLibraryHint {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scriptGroupHeaderRow {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scriptGroupTitle {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scriptGroupHint {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scriptGroupList {
|
||||||
|
max-height: 260px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emptyGroup {
|
||||||
|
padding: 24px;
|
||||||
|
text-align: center;
|
||||||
|
color: #999;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px dashed #d9d9d9;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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-checkbox) {
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scriptGroupInfo {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.messageInputArea {
|
||||||
|
.messageInput {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachmentButtons {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aiRewriteSection {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
.aiRewriteToggle {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aiRewriteLabel {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #1a1a1a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aiRewriteInput {
|
||||||
|
max-width: 240px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1200px) {
|
||||||
|
.step3Content {
|
||||||
|
.rightColumn {
|
||||||
|
width: 350px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.step3Content {
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.leftColumn {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rightColumn {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,23 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React, { useState } from "react";
|
import React, { useCallback } from "react";
|
||||||
import { Button, Input, Select, Slider, Switch } from "antd";
|
import {
|
||||||
|
Button,
|
||||||
|
Checkbox,
|
||||||
|
Input,
|
||||||
|
Select,
|
||||||
|
Slider,
|
||||||
|
Switch,
|
||||||
|
message as antdMessage,
|
||||||
|
} from "antd";
|
||||||
|
import { CopyOutlined, DeleteOutlined, PlusOutlined } from "@ant-design/icons";
|
||||||
|
import type { CheckboxChangeEvent } from "antd/es/checkbox";
|
||||||
|
|
||||||
import styles from "../../index.module.scss";
|
import styles from "./index.module.scss";
|
||||||
import { ContactItem } from "../../types";
|
import { ContactItem, ScriptGroup } from "../../types";
|
||||||
import ContentSelection from "@/components/ContentSelection";
|
import InputMessage from "./InputMessage/InputMessage";
|
||||||
import { ContentItem } from "@/components/ContentSelection/data";
|
import ContentLibrarySelector from "./ContentLibrarySelector";
|
||||||
|
import type { ContentItem } from "@/components/ContentSelection/data";
|
||||||
|
|
||||||
interface StepSendMessageProps {
|
interface StepSendMessageProps {
|
||||||
selectedAccounts: any[];
|
selectedAccounts: any[];
|
||||||
@@ -24,6 +35,16 @@ interface StepSendMessageProps {
|
|||||||
onAiRewriteToggle: (value: boolean) => void;
|
onAiRewriteToggle: (value: boolean) => void;
|
||||||
aiPrompt: string;
|
aiPrompt: string;
|
||||||
onAiPromptChange: (value: string) => void;
|
onAiPromptChange: (value: string) => void;
|
||||||
|
currentScriptMessages: string[];
|
||||||
|
onCurrentScriptMessagesChange: (messages: string[]) => void;
|
||||||
|
currentScriptName: string;
|
||||||
|
onCurrentScriptNameChange: (value: string) => void;
|
||||||
|
savedScriptGroups: ScriptGroup[];
|
||||||
|
onSavedScriptGroupsChange: (groups: ScriptGroup[]) => void;
|
||||||
|
selectedScriptGroupIds: string[];
|
||||||
|
onSelectedScriptGroupIdsChange: (ids: string[]) => void;
|
||||||
|
selectedContentLibraries: ContentItem[];
|
||||||
|
onSelectedContentLibrariesChange: (items: ContentItem[]) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const StepSendMessage: React.FC<StepSendMessageProps> = ({
|
const StepSendMessage: React.FC<StepSendMessageProps> = ({
|
||||||
@@ -42,77 +63,268 @@ const StepSendMessage: React.FC<StepSendMessageProps> = ({
|
|||||||
onAiRewriteToggle,
|
onAiRewriteToggle,
|
||||||
aiPrompt,
|
aiPrompt,
|
||||||
onAiPromptChange,
|
onAiPromptChange,
|
||||||
|
currentScriptMessages,
|
||||||
|
onCurrentScriptMessagesChange,
|
||||||
|
currentScriptName,
|
||||||
|
onCurrentScriptNameChange,
|
||||||
|
savedScriptGroups,
|
||||||
|
onSavedScriptGroupsChange,
|
||||||
|
selectedScriptGroupIds,
|
||||||
|
onSelectedScriptGroupIdsChange,
|
||||||
|
selectedContentLibraries,
|
||||||
|
onSelectedContentLibrariesChange,
|
||||||
}) => {
|
}) => {
|
||||||
const [selectedContentLibraries, setSelectedContentLibraries] = useState<
|
const handleAddMessage = useCallback(
|
||||||
ContentItem[]
|
(content?: string, showSuccess?: boolean) => {
|
||||||
>([]);
|
const finalContent = (content ?? messageContent).trim();
|
||||||
|
if (!finalContent) {
|
||||||
|
antdMessage.warning("请输入消息内容");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onCurrentScriptMessagesChange([...currentScriptMessages, finalContent]);
|
||||||
|
onMessageContentChange("");
|
||||||
|
if (showSuccess) {
|
||||||
|
antdMessage.success("已添加消息内容");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[
|
||||||
|
currentScriptMessages,
|
||||||
|
messageContent,
|
||||||
|
onCurrentScriptMessagesChange,
|
||||||
|
onMessageContentChange,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleRemoveMessage = useCallback(
|
||||||
|
(index: number) => {
|
||||||
|
const next = currentScriptMessages.filter((_, idx) => idx !== index);
|
||||||
|
onCurrentScriptMessagesChange(next);
|
||||||
|
},
|
||||||
|
[currentScriptMessages, onCurrentScriptMessagesChange],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSaveScriptGroup = useCallback(() => {
|
||||||
|
if (currentScriptMessages.length === 0) {
|
||||||
|
antdMessage.warning("请先添加消息内容");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const groupName =
|
||||||
|
currentScriptName.trim() || `话术组${savedScriptGroups.length + 1}`;
|
||||||
|
const newGroup: ScriptGroup = {
|
||||||
|
id: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
||||||
|
name: groupName,
|
||||||
|
messages: currentScriptMessages,
|
||||||
|
};
|
||||||
|
onSavedScriptGroupsChange([...savedScriptGroups, newGroup]);
|
||||||
|
onCurrentScriptMessagesChange([]);
|
||||||
|
onCurrentScriptNameChange("");
|
||||||
|
onMessageContentChange("");
|
||||||
|
antdMessage.success("已保存为话术组");
|
||||||
|
}, [
|
||||||
|
currentScriptMessages,
|
||||||
|
currentScriptName,
|
||||||
|
onCurrentScriptMessagesChange,
|
||||||
|
onCurrentScriptNameChange,
|
||||||
|
onMessageContentChange,
|
||||||
|
onSavedScriptGroupsChange,
|
||||||
|
savedScriptGroups,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const handleApplyGroup = useCallback(
|
||||||
|
(group: ScriptGroup) => {
|
||||||
|
onCurrentScriptMessagesChange(group.messages);
|
||||||
|
onCurrentScriptNameChange(group.name);
|
||||||
|
onMessageContentChange("");
|
||||||
|
antdMessage.success("已加载话术组");
|
||||||
|
},
|
||||||
|
[
|
||||||
|
onCurrentScriptMessagesChange,
|
||||||
|
onCurrentScriptNameChange,
|
||||||
|
onMessageContentChange,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleDeleteGroup = useCallback(
|
||||||
|
(groupId: string) => {
|
||||||
|
const nextGroups = savedScriptGroups.filter(
|
||||||
|
group => group.id !== groupId,
|
||||||
|
);
|
||||||
|
onSavedScriptGroupsChange(nextGroups);
|
||||||
|
if (selectedScriptGroupIds.includes(groupId)) {
|
||||||
|
const nextSelected = selectedScriptGroupIds.filter(
|
||||||
|
id => id !== groupId,
|
||||||
|
);
|
||||||
|
onSelectedScriptGroupIdsChange(nextSelected);
|
||||||
|
}
|
||||||
|
antdMessage.success("已删除话术组");
|
||||||
|
},
|
||||||
|
[
|
||||||
|
onSavedScriptGroupsChange,
|
||||||
|
savedScriptGroups,
|
||||||
|
onSelectedScriptGroupIdsChange,
|
||||||
|
selectedScriptGroupIds,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSelectChange = useCallback(
|
||||||
|
(groupId: string) => (event: CheckboxChangeEvent) => {
|
||||||
|
const checked = event.target.checked;
|
||||||
|
if (checked) {
|
||||||
|
if (!selectedScriptGroupIds.includes(groupId)) {
|
||||||
|
onSelectedScriptGroupIdsChange([...selectedScriptGroupIds, groupId]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
onSelectedScriptGroupIdsChange(
|
||||||
|
selectedScriptGroupIds.filter(id => id !== groupId),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[onSelectedScriptGroupIdsChange, selectedScriptGroupIds],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.stepContent}>
|
<div className={styles.stepContent}>
|
||||||
<div className={styles.step3Content}>
|
<div className={styles.step3Content}>
|
||||||
<div className={styles.leftColumn}>
|
<div className={styles.leftColumn}>
|
||||||
|
<div className={styles.previewHeader}>
|
||||||
|
<div className={styles.previewHeaderTitle}>模拟推送内容</div>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
icon={<PlusOutlined />}
|
||||||
|
onClick={handleSaveScriptGroup}
|
||||||
|
disabled={currentScriptMessages.length === 0}
|
||||||
|
>
|
||||||
|
保存为话术组
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
<div className={styles.messagePreview}>
|
<div className={styles.messagePreview}>
|
||||||
<div className={styles.previewTitle}>模拟推送内容</div>
|
|
||||||
<div className={styles.messageBubble}>
|
<div className={styles.messageBubble}>
|
||||||
<div className={styles.currentEditingLabel}>当前编辑话术</div>
|
<div className={styles.currentEditingLabel}>当前编辑话术</div>
|
||||||
<div className={styles.messageText}>
|
{currentScriptMessages.length === 0 ? (
|
||||||
{messageContent || "开始添加消息内容..."}
|
<div className={styles.messagePlaceholder}>
|
||||||
</div>
|
开始添加消息内容...
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className={styles.messageList}>
|
||||||
|
{currentScriptMessages.map((msg, index) => (
|
||||||
|
<div className={styles.messageItem} key={index}>
|
||||||
|
<div className={styles.messageText}>{msg}</div>
|
||||||
|
<Button
|
||||||
|
type="text"
|
||||||
|
danger
|
||||||
|
icon={<DeleteOutlined />}
|
||||||
|
onClick={() => handleRemoveMessage(index)}
|
||||||
|
className={styles.messageAction}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className={styles.scriptNameInput}>
|
||||||
|
<Input
|
||||||
|
placeholder="话术组名称(可选)"
|
||||||
|
value={currentScriptName}
|
||||||
|
onChange={event =>
|
||||||
|
onCurrentScriptNameChange(event.target.value)
|
||||||
|
}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.savedScriptGroups}>
|
<div className={styles.savedScriptGroups}>
|
||||||
<div className={styles.scriptGroupTitle}>已保存话术组</div>
|
{/* 内容库选择组件 */}
|
||||||
<ContentSelection
|
<ContentLibrarySelector
|
||||||
selectedOptions={selectedContentLibraries}
|
selectedContentLibraries={selectedContentLibraries}
|
||||||
onSelect={setSelectedContentLibraries}
|
onSelectedContentLibrariesChange={
|
||||||
placeholder="选择话术内容"
|
onSelectedContentLibrariesChange
|
||||||
showSelectedList
|
}
|
||||||
selectedListMaxHeight={220}
|
|
||||||
/>
|
/>
|
||||||
|
<div className={styles.scriptGroupHeaderRow}>
|
||||||
|
<div className={styles.scriptGroupTitle}>
|
||||||
|
已保存话术组 ({savedScriptGroups.length})
|
||||||
|
</div>
|
||||||
|
<div className={styles.scriptGroupHint}>勾选后将随机均分推送</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.scriptGroupList}>
|
||||||
|
{savedScriptGroups.length === 0 ? (
|
||||||
|
<div className={styles.emptyGroup}>暂无已保存话术组</div>
|
||||||
|
) : (
|
||||||
|
savedScriptGroups.map((group, index) => (
|
||||||
|
<div className={styles.scriptGroupItem} key={group.id}>
|
||||||
|
<div className={styles.scriptGroupHeader}>
|
||||||
|
<div className={styles.scriptGroupLeft}>
|
||||||
|
<Checkbox
|
||||||
|
checked={selectedScriptGroupIds.includes(group.id)}
|
||||||
|
onChange={handleSelectChange(group.id)}
|
||||||
|
/>
|
||||||
|
<div className={styles.scriptGroupInfo}>
|
||||||
|
<div className={styles.scriptGroupName}>
|
||||||
|
{group.name || `话术组${index + 1}`}
|
||||||
|
</div>
|
||||||
|
<div className={styles.messageCount}>
|
||||||
|
{group.messages.length}条消息
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.scriptGroupActions}>
|
||||||
|
<Button
|
||||||
|
type="text"
|
||||||
|
icon={<CopyOutlined />}
|
||||||
|
className={styles.actionButton}
|
||||||
|
onClick={() => handleApplyGroup(group)}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type="text"
|
||||||
|
icon={<DeleteOutlined />}
|
||||||
|
className={styles.actionButton}
|
||||||
|
onClick={() => handleDeleteGroup(group.id)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.scriptGroupContent}>
|
||||||
|
{group.messages[0]}
|
||||||
|
{group.messages.length > 1 && " ..."}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.messageInputArea}>
|
<div className={styles.messageInputArea}>
|
||||||
<Input.TextArea
|
<InputMessage
|
||||||
className={styles.messageInput}
|
defaultValue={messageContent}
|
||||||
|
onContentChange={onMessageContentChange}
|
||||||
|
onSend={value => handleAddMessage(value)}
|
||||||
|
clearOnSend
|
||||||
placeholder="请输入内容"
|
placeholder="请输入内容"
|
||||||
value={messageContent}
|
hint={`按住CTRL+ENTER换行,已配置${savedScriptGroups.length}个话术组,已选择${selectedScriptGroupIds.length}个进行推送,已选${selectedContentLibraries.length}个内容库`}
|
||||||
onChange={e => onMessageContentChange(e.target.value)}
|
|
||||||
rows={4}
|
|
||||||
onKeyDown={e => {
|
|
||||||
if (e.ctrlKey && e.key === "Enter") {
|
|
||||||
e.preventDefault();
|
|
||||||
onMessageContentChange(`${messageContent}\n`);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<div className={styles.attachmentButtons}>
|
|
||||||
<Button type="text" icon="😊" />
|
|
||||||
<Button type="text" icon="🖼️" />
|
|
||||||
<Button type="text" icon="📎" />
|
|
||||||
<Button type="text" icon="🔗" />
|
|
||||||
<Button type="text" icon="⭐" />
|
|
||||||
</div>
|
|
||||||
<div className={styles.aiRewriteSection}>
|
<div className={styles.aiRewriteSection}>
|
||||||
<Switch checked={aiRewriteEnabled} onChange={onAiRewriteToggle} />
|
<div className={styles.aiRewriteToggle}>
|
||||||
<span style={{ marginLeft: 8 }}>AI智能话术改写</span>
|
<Switch
|
||||||
|
checked={aiRewriteEnabled}
|
||||||
|
onChange={onAiRewriteToggle}
|
||||||
|
/>
|
||||||
|
<span className={styles.aiRewriteLabel}>AI智能话术改写</span>
|
||||||
|
</div>
|
||||||
{aiRewriteEnabled && (
|
{aiRewriteEnabled && (
|
||||||
<Input
|
<Input
|
||||||
placeholder="输入改写提示词"
|
placeholder="输入改写提示词"
|
||||||
value={aiPrompt}
|
value={aiPrompt}
|
||||||
onChange={e => onAiPromptChange(e.target.value)}
|
onChange={event => onAiPromptChange(event.target.value)}
|
||||||
style={{ marginLeft: 12, width: 200 }}
|
className={styles.aiRewriteInput}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Button type="primary" style={{ marginLeft: 12 }}>
|
<Button
|
||||||
+ 添加
|
type="primary"
|
||||||
|
icon={<PlusOutlined />}
|
||||||
|
onClick={() => handleAddMessage(undefined, true)}
|
||||||
|
>
|
||||||
|
添加
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.messageHint}>
|
|
||||||
按住CTRL+ENTER换行,已选择{selectedContentLibraries.length}
|
|
||||||
个话术组,已选择{selectedContacts.length}
|
|
||||||
个进行推送
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -170,7 +382,7 @@ const StepSendMessage: React.FC<StepSendMessageProps> = ({
|
|||||||
<li>
|
<li>
|
||||||
推送{targetLabel}: {selectedContacts.length}个
|
推送{targetLabel}: {selectedContacts.length}个
|
||||||
</li>
|
</li>
|
||||||
<li>话术组数: {selectedContentLibraries.length}个</li>
|
<li>话术组数: {savedScriptGroups.length}个</li>
|
||||||
<li>随机推送: 否</li>
|
<li>随机推送: 否</li>
|
||||||
<li>预计耗时: ~1分钟</li>
|
<li>预计耗时: ~1分钟</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -349,233 +349,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.step3Content {
|
|
||||||
display: flex;
|
|
||||||
gap: 24px;
|
|
||||||
align-items: flex-start;
|
|
||||||
|
|
||||||
// 左侧栏
|
|
||||||
.leftColumn {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 右侧栏
|
|
||||||
.rightColumn {
|
|
||||||
width: 400px;
|
|
||||||
flex: 1;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.filterModal {
|
.filterModal {
|
||||||
:global(.ant-modal-body) {
|
:global(.ant-modal-body) {
|
||||||
padding-bottom: 12px;
|
padding-bottom: 12px;
|
||||||
@@ -695,12 +468,6 @@
|
|||||||
min-height: 200px;
|
min-height: 200px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.step3Content {
|
|
||||||
.rightColumn {
|
|
||||||
width: 350px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -735,18 +502,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.step3Content {
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.leftColumn {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rightColumn {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
padding: 12px 16px;
|
padding: 12px 16px;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@@ -14,8 +14,9 @@ import { getCustomerList } from "@/pages/pc/ckbox/weChat/api";
|
|||||||
import StepSelectAccount from "./components/StepSelectAccount";
|
import StepSelectAccount from "./components/StepSelectAccount";
|
||||||
import StepSelectContacts from "./components/StepSelectContacts";
|
import StepSelectContacts from "./components/StepSelectContacts";
|
||||||
import StepSendMessage from "./components/StepSendMessage";
|
import StepSendMessage from "./components/StepSendMessage";
|
||||||
import { ContactItem, PushType } from "./types";
|
import { ContactItem, PushType, ScriptGroup } from "./types";
|
||||||
import StepIndicator from "@/components/StepIndicator";
|
import StepIndicator from "@/components/StepIndicator";
|
||||||
|
import type { ContentItem } from "@/components/ContentSelection/data";
|
||||||
|
|
||||||
const CreatePushTask: React.FC = () => {
|
const CreatePushTask: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -31,7 +32,18 @@ const CreatePushTask: React.FC = () => {
|
|||||||
const [currentStep, setCurrentStep] = useState(1);
|
const [currentStep, setCurrentStep] = useState(1);
|
||||||
const [selectedAccounts, setSelectedAccounts] = useState<any[]>([]);
|
const [selectedAccounts, setSelectedAccounts] = useState<any[]>([]);
|
||||||
const [selectedContacts, setSelectedContacts] = useState<ContactItem[]>([]);
|
const [selectedContacts, setSelectedContacts] = useState<ContactItem[]>([]);
|
||||||
const [messageContent, setMessageContent] = useState("");
|
const [messageDraft, setMessageDraft] = useState("");
|
||||||
|
const [currentScriptMessages, setCurrentScriptMessages] = useState<string[]>(
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
const [currentScriptName, setCurrentScriptName] = useState("");
|
||||||
|
const [savedScriptGroups, setSavedScriptGroups] = useState<ScriptGroup[]>([]);
|
||||||
|
const [selectedScriptGroupIds, setSelectedScriptGroupIds] = useState<
|
||||||
|
string[]
|
||||||
|
>([]);
|
||||||
|
const [selectedContentLibraries, setSelectedContentLibraries] = useState<
|
||||||
|
ContentItem[]
|
||||||
|
>([]);
|
||||||
const [friendInterval, setFriendInterval] = useState(10);
|
const [friendInterval, setFriendInterval] = useState(10);
|
||||||
const [messageInterval, setMessageInterval] = useState(1);
|
const [messageInterval, setMessageInterval] = useState(1);
|
||||||
const [selectedTag, setSelectedTag] = useState<string>("");
|
const [selectedTag, setSelectedTag] = useState<string>("");
|
||||||
@@ -120,8 +132,11 @@ const CreatePushTask: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleSend = () => {
|
const handleSend = () => {
|
||||||
if (!messageContent.trim()) {
|
const selectedGroups = savedScriptGroups.filter(group =>
|
||||||
message.warning("请输入消息内容");
|
selectedScriptGroupIds.includes(group.id),
|
||||||
|
);
|
||||||
|
if (currentScriptMessages.length === 0 && selectedGroups.length === 0) {
|
||||||
|
message.warning("请先添加话术内容或选择话术组");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// TODO: 实现发送逻辑
|
// TODO: 实现发送逻辑
|
||||||
@@ -129,12 +144,17 @@ const CreatePushTask: React.FC = () => {
|
|||||||
pushType: validPushType,
|
pushType: validPushType,
|
||||||
accounts: selectedAccounts,
|
accounts: selectedAccounts,
|
||||||
contacts: selectedContacts,
|
contacts: selectedContacts,
|
||||||
messageContent,
|
currentScript: {
|
||||||
|
name: currentScriptName,
|
||||||
|
messages: currentScriptMessages,
|
||||||
|
},
|
||||||
|
selectedScriptGroups: selectedGroups,
|
||||||
friendInterval,
|
friendInterval,
|
||||||
messageInterval,
|
messageInterval,
|
||||||
selectedTag,
|
selectedTag,
|
||||||
aiRewriteEnabled,
|
aiRewriteEnabled,
|
||||||
aiPrompt,
|
aiPrompt,
|
||||||
|
selectedContentLibraries,
|
||||||
});
|
});
|
||||||
message.success("推送任务已创建");
|
message.success("推送任务已创建");
|
||||||
navigate("/pc/powerCenter/message-push-assistant");
|
navigate("/pc/powerCenter/message-push-assistant");
|
||||||
@@ -253,8 +273,18 @@ const CreatePushTask: React.FC = () => {
|
|||||||
selectedAccounts={selectedAccounts}
|
selectedAccounts={selectedAccounts}
|
||||||
selectedContacts={selectedContacts}
|
selectedContacts={selectedContacts}
|
||||||
targetLabel={step2Title}
|
targetLabel={step2Title}
|
||||||
messageContent={messageContent}
|
messageContent={messageDraft}
|
||||||
onMessageContentChange={setMessageContent}
|
onMessageContentChange={setMessageDraft}
|
||||||
|
currentScriptMessages={currentScriptMessages}
|
||||||
|
onCurrentScriptMessagesChange={setCurrentScriptMessages}
|
||||||
|
currentScriptName={currentScriptName}
|
||||||
|
onCurrentScriptNameChange={setCurrentScriptName}
|
||||||
|
savedScriptGroups={savedScriptGroups}
|
||||||
|
onSavedScriptGroupsChange={setSavedScriptGroups}
|
||||||
|
selectedScriptGroupIds={selectedScriptGroupIds}
|
||||||
|
onSelectedScriptGroupIdsChange={setSelectedScriptGroupIds}
|
||||||
|
selectedContentLibraries={selectedContentLibraries}
|
||||||
|
onSelectedContentLibrariesChange={setSelectedContentLibraries}
|
||||||
friendInterval={friendInterval}
|
friendInterval={friendInterval}
|
||||||
onFriendIntervalChange={setFriendInterval}
|
onFriendIntervalChange={setFriendInterval}
|
||||||
messageInterval={messageInterval}
|
messageInterval={messageInterval}
|
||||||
|
|||||||
@@ -19,3 +19,9 @@ export interface ContactItem {
|
|||||||
city?: string;
|
city?: string;
|
||||||
extendFields?: Record<string, any>;
|
extendFields?: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ScriptGroup {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
messages: string[];
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user