新增跟進提醒和待辦事項模態框功能:在聊天窗口中添加相應的狀態管理和事件處理,提升用戶互動體驗。
This commit is contained in:
@@ -0,0 +1,224 @@
|
||||
// 跟进提醒模态框样式
|
||||
.followupModal {
|
||||
:global(.ant-modal-header) {
|
||||
border-bottom: none;
|
||||
padding: 16px 20px 0 20px;
|
||||
}
|
||||
|
||||
:global(.ant-modal-body) {
|
||||
padding: 0 20px 20px 20px;
|
||||
max-height: 60vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:global(.ant-modal-close) {
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.modalHeader {
|
||||
.modalTitle {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #262626;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.modalSubtitle {
|
||||
font-size: 12px;
|
||||
color: #8c8c8c;
|
||||
margin: 2px 0 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
.modalContent {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
.addReminderSection {
|
||||
margin-bottom: 16px;
|
||||
padding: 16px;
|
||||
background: #fafafa;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #f0f0f0;
|
||||
flex-shrink: 0;
|
||||
|
||||
.reminderForm {
|
||||
.formRow {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
margin-bottom: 12px;
|
||||
|
||||
.formItem {
|
||||
flex: 1;
|
||||
margin-bottom: 0;
|
||||
|
||||
:global(.ant-form-item-label) {
|
||||
padding-bottom: 4px;
|
||||
|
||||
label {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #262626;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.selectInput,
|
||||
.dateInput,
|
||||
.contentInput {
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 6px;
|
||||
transition: all 0.3s;
|
||||
|
||||
&:hover {
|
||||
border-color: #40a9ff;
|
||||
}
|
||||
|
||||
&:focus,
|
||||
&:focus-within {
|
||||
border-color: #40a9ff;
|
||||
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
.contentInput {
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.addButton {
|
||||
height: 36px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
background: #1890ff;
|
||||
border-color: #1890ff;
|
||||
box-shadow: 0 2px 4px rgba(24, 144, 255, 0.2);
|
||||
|
||||
&:hover {
|
||||
background: #40a9ff;
|
||||
border-color: #40a9ff;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 8px rgba(24, 144, 255, 0.3);
|
||||
}
|
||||
|
||||
.anticon {
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.remindersList {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
max-height: 200px;
|
||||
border: 1px solid #f0f0f0;
|
||||
border-radius: 6px;
|
||||
background: #fff;
|
||||
|
||||
:global(.ant-list) {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
:global(.ant-list-item) {
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.reminderItem {
|
||||
padding: 10px;
|
||||
|
||||
.reminderContent {
|
||||
width: 100%;
|
||||
|
||||
.reminderHeader {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 6px;
|
||||
|
||||
.typeTag {
|
||||
font-size: 11px;
|
||||
padding: 1px 6px;
|
||||
border-radius: 10px;
|
||||
font-weight: 500;
|
||||
|
||||
.anticon {
|
||||
margin-right: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.recipient {
|
||||
font-size: 12px;
|
||||
color: #595959;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.reminderBody {
|
||||
margin-bottom: 6px;
|
||||
|
||||
.reminderText {
|
||||
font-size: 13px;
|
||||
color: #262626;
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
||||
|
||||
.reminderFooter {
|
||||
.clockIcon {
|
||||
color: #8c8c8c;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.scheduledTime {
|
||||
font-size: 11px;
|
||||
color: #8c8c8c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 响应式设计
|
||||
@media (max-width: 768px) {
|
||||
.followupModal {
|
||||
:global(.ant-modal) {
|
||||
margin: 0;
|
||||
max-width: 100vw;
|
||||
top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
:global(.ant-modal-body) {
|
||||
padding: 0 16px 16px 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.modalContent {
|
||||
.addReminderSection {
|
||||
padding: 12px;
|
||||
margin-bottom: 12px;
|
||||
|
||||
.reminderForm {
|
||||
.formRow {
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.remindersList {
|
||||
max-height: 150px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
import React, { useState } from "react";
|
||||
import {
|
||||
Modal,
|
||||
Form,
|
||||
Select,
|
||||
DatePicker,
|
||||
Input,
|
||||
Button,
|
||||
List,
|
||||
Tag,
|
||||
Space,
|
||||
Typography,
|
||||
} from "antd";
|
||||
import {
|
||||
PlusOutlined,
|
||||
ClockCircleOutlined,
|
||||
PhoneOutlined,
|
||||
MessageOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import styles from "./index.module.scss";
|
||||
|
||||
const { Option } = Select;
|
||||
const { TextArea } = Input;
|
||||
const { Text } = Typography;
|
||||
|
||||
interface FollowupReminder {
|
||||
id: string;
|
||||
type: "电话" | "消息";
|
||||
status: "待处理" | "已完成" | "已取消";
|
||||
content: string;
|
||||
scheduledTime: string;
|
||||
recipient: string;
|
||||
}
|
||||
|
||||
interface FollowupReminderModalProps {
|
||||
visible: boolean;
|
||||
onClose: () => void;
|
||||
recipientName?: string;
|
||||
}
|
||||
|
||||
const FollowupReminderModal: React.FC<FollowupReminderModalProps> = ({
|
||||
visible,
|
||||
onClose,
|
||||
recipientName = "客户",
|
||||
}) => {
|
||||
const [form] = Form.useForm();
|
||||
const [reminders, setReminders] = useState<FollowupReminder[]>([
|
||||
{
|
||||
id: "1",
|
||||
type: "电话",
|
||||
status: "待处理",
|
||||
content: "周三14点回访",
|
||||
scheduledTime: "2024/3/6 14:00:00",
|
||||
recipient: "李先生",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
type: "消息",
|
||||
status: "待处理",
|
||||
content: "发送产品演示视频",
|
||||
scheduledTime: "2024/3/7 09:00:00",
|
||||
recipient: "张总",
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
type: "消息",
|
||||
status: "待处理",
|
||||
content: "发送产品演示视频",
|
||||
scheduledTime: "2024/3/7 09:00:00",
|
||||
recipient: "张总",
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
type: "消息",
|
||||
status: "待处理",
|
||||
content: "发送产品演示视频",
|
||||
scheduledTime: "2024/3/7 09:00:00",
|
||||
recipient: "张总",
|
||||
},
|
||||
]);
|
||||
|
||||
// 跟进方式选项
|
||||
const followupMethods = [
|
||||
{ value: "电话回访", label: "电话回访" },
|
||||
{ value: "微信消息", label: "微信消息" },
|
||||
{ value: "邮件", label: "邮件" },
|
||||
{ value: "短信", label: "短信" },
|
||||
];
|
||||
|
||||
// 处理添加提醒
|
||||
const handleAddReminder = async () => {
|
||||
try {
|
||||
const values = await form.validateFields();
|
||||
const newReminder: FollowupReminder = {
|
||||
id: Date.now().toString(),
|
||||
type: values.method === "电话回访" ? "电话" : "消息",
|
||||
status: "待处理",
|
||||
content: values.content,
|
||||
scheduledTime: values.dateTime.format("YYYY/M/D HH:mm:ss"),
|
||||
recipient: recipientName,
|
||||
};
|
||||
|
||||
setReminders([...reminders, newReminder]);
|
||||
form.resetFields();
|
||||
} catch (error) {
|
||||
console.error("表单验证失败:", error);
|
||||
}
|
||||
};
|
||||
|
||||
// 获取状态标签颜色
|
||||
const getStatusColor = (status: string) => {
|
||||
switch (status) {
|
||||
case "待处理":
|
||||
return "warning";
|
||||
case "已完成":
|
||||
return "success";
|
||||
case "已取消":
|
||||
return "default";
|
||||
default:
|
||||
return "default";
|
||||
}
|
||||
};
|
||||
|
||||
// 获取类型图标
|
||||
const getTypeIcon = (type: string) => {
|
||||
return type === "电话" ? <PhoneOutlined /> : <MessageOutlined />;
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={
|
||||
<div className={styles.modalHeader}>
|
||||
<div className={styles.modalTitle}>跟进提醒设置</div>
|
||||
<div className={styles.modalSubtitle}>设置客户跟进时间和方式</div>
|
||||
</div>
|
||||
}
|
||||
open={visible}
|
||||
onCancel={onClose}
|
||||
footer={null}
|
||||
width={480}
|
||||
className={styles.followupModal}
|
||||
>
|
||||
<div className={styles.modalContent}>
|
||||
{/* 添加新提醒区域 */}
|
||||
<div className={styles.addReminderSection}>
|
||||
<Form form={form} layout="vertical" className={styles.reminderForm}>
|
||||
<div className={styles.formRow}>
|
||||
<Form.Item
|
||||
name="method"
|
||||
label="跟进方式"
|
||||
rules={[{ required: true, message: "请选择跟进方式" }]}
|
||||
className={styles.formItem}
|
||||
>
|
||||
<Select placeholder="电话回访" className={styles.selectInput}>
|
||||
{followupMethods.map(method => (
|
||||
<Option key={method.value} value={method.value}>
|
||||
{method.label}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="dateTime"
|
||||
label="提醒时间"
|
||||
rules={[{ required: true, message: "请选择提醒时间" }]}
|
||||
className={styles.formItem}
|
||||
>
|
||||
<DatePicker
|
||||
showTime
|
||||
format="YYYY/M/D HH:mm"
|
||||
placeholder="年/月/日 --:--"
|
||||
className={styles.dateInput}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
|
||||
<Form.Item
|
||||
name="content"
|
||||
label="提醒内容"
|
||||
rules={[{ required: true, message: "请输入提醒内容" }]}
|
||||
>
|
||||
<TextArea
|
||||
placeholder="提醒内容..."
|
||||
rows={3}
|
||||
className={styles.contentInput}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={handleAddReminder}
|
||||
className={styles.addButton}
|
||||
block
|
||||
>
|
||||
添加提醒
|
||||
</Button>
|
||||
</Form>
|
||||
</div>
|
||||
|
||||
{/* 现有提醒列表 */}
|
||||
<div className={styles.remindersList}>
|
||||
<List
|
||||
dataSource={reminders}
|
||||
renderItem={reminder => (
|
||||
<List.Item className={styles.reminderItem}>
|
||||
<div className={styles.reminderContent}>
|
||||
<div className={styles.reminderHeader}>
|
||||
<Space>
|
||||
<Tag
|
||||
icon={getTypeIcon(reminder.type)}
|
||||
color="blue"
|
||||
className={styles.typeTag}
|
||||
>
|
||||
{reminder.type}
|
||||
</Tag>
|
||||
<Tag color={getStatusColor(reminder.status)}>
|
||||
{reminder.status}
|
||||
</Tag>
|
||||
</Space>
|
||||
<Text className={styles.recipient}>
|
||||
{reminder.recipient}
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
<div className={styles.reminderBody}>
|
||||
<Text className={styles.reminderText}>
|
||||
{reminder.content}
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
<div className={styles.reminderFooter}>
|
||||
<Space>
|
||||
<ClockCircleOutlined className={styles.clockIcon} />
|
||||
<Text className={styles.scheduledTime}>
|
||||
{reminder.scheduledTime}
|
||||
</Text>
|
||||
</Space>
|
||||
</div>
|
||||
</div>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default FollowupReminderModal;
|
||||
@@ -0,0 +1,266 @@
|
||||
// 待办事项模态框样式
|
||||
.todoModal {
|
||||
:global(.ant-modal-header) {
|
||||
border-bottom: none;
|
||||
padding: 16px 20px 0 20px;
|
||||
}
|
||||
|
||||
:global(.ant-modal-body) {
|
||||
padding: 0 20px 20px 20px;
|
||||
max-height: 60vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:global(.ant-modal-close) {
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.modalHeader {
|
||||
.modalTitle {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #262626;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.modalSubtitle {
|
||||
font-size: 12px;
|
||||
color: #8c8c8c;
|
||||
margin: 2px 0 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
.modalContent {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
.addTaskSection {
|
||||
margin-bottom: 16px;
|
||||
padding: 16px;
|
||||
background: #fafafa;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #f0f0f0;
|
||||
flex-shrink: 0;
|
||||
|
||||
.taskForm {
|
||||
.titleInput,
|
||||
.descriptionInput,
|
||||
.prioritySelect,
|
||||
.dateInput {
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 6px;
|
||||
transition: all 0.3s;
|
||||
|
||||
&:hover {
|
||||
border-color: #40a9ff;
|
||||
}
|
||||
|
||||
&:focus,
|
||||
&:focus-within {
|
||||
border-color: #40a9ff;
|
||||
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
.descriptionInput {
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.formRow {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
margin-bottom: 12px;
|
||||
|
||||
.formItem {
|
||||
flex: 1;
|
||||
margin-bottom: 0;
|
||||
|
||||
:global(.ant-form-item-label) {
|
||||
padding-bottom: 4px;
|
||||
|
||||
label {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #262626;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.addButton {
|
||||
height: 36px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
background: #1890ff;
|
||||
border-color: #1890ff;
|
||||
box-shadow: 0 2px 4px rgba(24, 144, 255, 0.2);
|
||||
|
||||
&:hover {
|
||||
background: #40a9ff;
|
||||
border-color: #40a9ff;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 8px rgba(24, 144, 255, 0.3);
|
||||
}
|
||||
|
||||
.anticon {
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.todoList {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
max-height: 200px;
|
||||
border: 1px solid #f0f0f0;
|
||||
border-radius: 6px;
|
||||
background: #fff;
|
||||
|
||||
// 自定义滚动条样式
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #c1c1c1;
|
||||
border-radius: 3px;
|
||||
|
||||
&:hover {
|
||||
background: #a8a8a8;
|
||||
}
|
||||
}
|
||||
|
||||
:global(.ant-list) {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
:global(.ant-list-item) {
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.todoItem {
|
||||
padding: 10px;
|
||||
|
||||
.todoContent {
|
||||
width: 100%;
|
||||
|
||||
.todoHeader {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 6px;
|
||||
|
||||
.todoCheckbox {
|
||||
margin-right: 8px;
|
||||
|
||||
:global(.ant-checkbox-inner) {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.todoTitle {
|
||||
font-size: 14px;
|
||||
color: #262626;
|
||||
font-weight: 500;
|
||||
flex: 1;
|
||||
|
||||
&.completed {
|
||||
text-decoration: line-through;
|
||||
color: #8c8c8c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.todoDescription {
|
||||
margin-bottom: 8px;
|
||||
|
||||
.descriptionText {
|
||||
font-size: 12px;
|
||||
color: #595959;
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
||||
|
||||
.todoFooter {
|
||||
.clientInfo {
|
||||
font-size: 11px;
|
||||
color: #8c8c8c;
|
||||
}
|
||||
|
||||
.priorityTag {
|
||||
font-size: 10px;
|
||||
padding: 1px 6px;
|
||||
border-radius: 8px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.dueDate {
|
||||
.calendarIcon {
|
||||
color: #8c8c8c;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.dueDateText {
|
||||
font-size: 11px;
|
||||
color: #8c8c8c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 响应式设计
|
||||
@media (max-width: 768px) {
|
||||
.todoModal {
|
||||
:global(.ant-modal) {
|
||||
margin: 0;
|
||||
max-width: 100vw;
|
||||
top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
:global(.ant-modal-body) {
|
||||
padding: 0 16px 16px 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.modalContent {
|
||||
.addTaskSection {
|
||||
padding: 12px;
|
||||
margin-bottom: 12px;
|
||||
|
||||
.taskForm {
|
||||
.formRow {
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.todoList {
|
||||
max-height: 150px;
|
||||
|
||||
// 移动端滚动条样式
|
||||
&::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
import React, { useState } from "react";
|
||||
import {
|
||||
Modal,
|
||||
Form,
|
||||
Input,
|
||||
Select,
|
||||
DatePicker,
|
||||
Button,
|
||||
List,
|
||||
Checkbox,
|
||||
Tag,
|
||||
Space,
|
||||
Typography,
|
||||
} from "antd";
|
||||
import { PlusOutlined, CalendarOutlined } from "@ant-design/icons";
|
||||
import styles from "./index.module.scss";
|
||||
|
||||
const { Option } = Select;
|
||||
const { TextArea } = Input;
|
||||
const { Text } = Typography;
|
||||
|
||||
interface TodoItem {
|
||||
id: string;
|
||||
title: string;
|
||||
description?: string;
|
||||
client?: string;
|
||||
priority: "高" | "中" | "低";
|
||||
dueDate: string;
|
||||
completed: boolean;
|
||||
}
|
||||
|
||||
interface TodoListModalProps {
|
||||
visible: boolean;
|
||||
onClose: () => void;
|
||||
clientName?: string;
|
||||
}
|
||||
|
||||
const TodoListModal: React.FC<TodoListModalProps> = ({
|
||||
visible,
|
||||
onClose,
|
||||
clientName = "客户",
|
||||
}) => {
|
||||
const [form] = Form.useForm();
|
||||
const [todos, setTodos] = useState<TodoItem[]>([
|
||||
{
|
||||
id: "1",
|
||||
title: "整理客户需求文档",
|
||||
description: "汇总本周客户反馈的功能需求",
|
||||
client: "李先生",
|
||||
priority: "高",
|
||||
dueDate: "03/08 18:00",
|
||||
completed: false,
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
title: "准备产品演示PPT",
|
||||
description: "针对大客户的定制化演示材料",
|
||||
client: "张总",
|
||||
priority: "中",
|
||||
dueDate: "03/09 16:00",
|
||||
completed: false,
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
title: "准备产品演示PPT",
|
||||
description: "针对大客户的定制化演示材料",
|
||||
client: "张总",
|
||||
priority: "中",
|
||||
dueDate: "03/09 16:00",
|
||||
completed: false,
|
||||
},
|
||||
]);
|
||||
|
||||
// 优先级选项
|
||||
const priorityOptions = [
|
||||
{ value: "高", label: "高优先级", color: "orange" },
|
||||
{ value: "中", label: "中优先级", color: "blue" },
|
||||
{ value: "低", label: "低优先级", color: "green" },
|
||||
];
|
||||
|
||||
// 处理添加任务
|
||||
const handleAddTask = async () => {
|
||||
try {
|
||||
const values = await form.validateFields();
|
||||
const newTodo: TodoItem = {
|
||||
id: Date.now().toString(),
|
||||
title: values.title,
|
||||
description: values.description,
|
||||
client: clientName,
|
||||
priority: values.priority,
|
||||
dueDate: values.dueDate.format("MM/DD HH:mm"),
|
||||
completed: false,
|
||||
};
|
||||
|
||||
setTodos([...todos, newTodo]);
|
||||
form.resetFields();
|
||||
} catch (error) {
|
||||
console.error("表单验证失败:", error);
|
||||
}
|
||||
};
|
||||
|
||||
// 处理任务完成状态切换
|
||||
const handleToggleComplete = (id: string) => {
|
||||
setTodos(
|
||||
todos.map(todo =>
|
||||
todo.id === id ? { ...todo, completed: !todo.completed } : todo,
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
// 获取优先级标签颜色
|
||||
const getPriorityColor = (priority: string) => {
|
||||
const option = priorityOptions.find(opt => opt.value === priority);
|
||||
return option?.color || "default";
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={
|
||||
<div className={styles.modalHeader}>
|
||||
<div className={styles.modalTitle}>待办事项清单</div>
|
||||
<div className={styles.modalSubtitle}>管理日常工作任务</div>
|
||||
</div>
|
||||
}
|
||||
open={visible}
|
||||
onCancel={onClose}
|
||||
footer={null}
|
||||
width={480}
|
||||
className={styles.todoModal}
|
||||
>
|
||||
<div className={styles.modalContent}>
|
||||
{/* 添加新任务区域 */}
|
||||
<div className={styles.addTaskSection}>
|
||||
<Form form={form} layout="vertical" className={styles.taskForm}>
|
||||
<Form.Item
|
||||
name="title"
|
||||
rules={[{ required: true, message: "请输入任务标题" }]}
|
||||
>
|
||||
<Input placeholder="任务标题..." className={styles.titleInput} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="description">
|
||||
<TextArea
|
||||
placeholder="任务描述 (可选)..."
|
||||
rows={2}
|
||||
className={styles.descriptionInput}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<div className={styles.formRow}>
|
||||
<Form.Item
|
||||
name="priority"
|
||||
rules={[{ required: true, message: "请选择优先级" }]}
|
||||
className={styles.formItem}
|
||||
>
|
||||
<Select
|
||||
placeholder="中优先级"
|
||||
className={styles.prioritySelect}
|
||||
>
|
||||
{priorityOptions.map(option => (
|
||||
<Option key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="dueDate"
|
||||
rules={[{ required: true, message: "请选择截止时间" }]}
|
||||
className={styles.formItem}
|
||||
>
|
||||
<DatePicker
|
||||
showTime
|
||||
format="MM/DD HH:mm"
|
||||
placeholder="年/月/日 --:--"
|
||||
className={styles.dateInput}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={handleAddTask}
|
||||
className={styles.addButton}
|
||||
block
|
||||
>
|
||||
添加任务
|
||||
</Button>
|
||||
</Form>
|
||||
</div>
|
||||
|
||||
{/* 任务列表 */}
|
||||
<div className={styles.todoList}>
|
||||
<List
|
||||
dataSource={todos}
|
||||
renderItem={todo => (
|
||||
<List.Item className={styles.todoItem}>
|
||||
<div className={styles.todoContent}>
|
||||
<div className={styles.todoHeader}>
|
||||
<Checkbox
|
||||
checked={todo.completed}
|
||||
onChange={() => handleToggleComplete(todo.id)}
|
||||
className={styles.todoCheckbox}
|
||||
/>
|
||||
<Text
|
||||
className={`${styles.todoTitle} ${todo.completed ? styles.completed : ""}`}
|
||||
>
|
||||
{todo.title}
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
{todo.description && (
|
||||
<div className={styles.todoDescription}>
|
||||
<Text className={styles.descriptionText}>
|
||||
{todo.description}
|
||||
</Text>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className={styles.todoFooter}>
|
||||
<Space>
|
||||
<Text className={styles.clientInfo}>
|
||||
客户:{todo.client}
|
||||
</Text>
|
||||
<Tag
|
||||
color={getPriorityColor(todo.priority)}
|
||||
className={styles.priorityTag}
|
||||
>
|
||||
{todo.priority}
|
||||
</Tag>
|
||||
<Space className={styles.dueDate}>
|
||||
<CalendarOutlined className={styles.calendarIcon} />
|
||||
<Text className={styles.dueDateText}>
|
||||
{todo.dueDate}
|
||||
</Text>
|
||||
</Space>
|
||||
</Space>
|
||||
</div>
|
||||
</div>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default TodoListModal;
|
||||
@@ -14,6 +14,8 @@ import styles from "./ChatWindow.module.scss";
|
||||
import ProfileCard from "./components/ProfileCard";
|
||||
import MessageEnter from "./components/MessageEnter";
|
||||
import MessageRecord from "./components/MessageRecord";
|
||||
import FollowupReminderModal from "./components/FollowupReminderModal";
|
||||
import TodoListModal from "./components/TodoListModal";
|
||||
import { setFriendInjectConfig } from "@/pages/pc/ckbox/weChat/api";
|
||||
import { useWeChatStore } from "@/store/module/weChat/weChat";
|
||||
const { Header, Content } = Layout;
|
||||
@@ -22,6 +24,12 @@ interface ChatWindowProps {
|
||||
contract: ContractData | weChatGroup;
|
||||
}
|
||||
|
||||
const typeOptions = [
|
||||
{ value: 0, label: "人工接待" },
|
||||
{ value: 1, label: "AI辅助" },
|
||||
{ value: 2, label: "AI接管" },
|
||||
];
|
||||
|
||||
const ChatWindow: React.FC<ChatWindowProps> = ({ contract }) => {
|
||||
const updateAiQuoteMessageContent = useWeChatStore(
|
||||
state => state.updateAiQuoteMessageContent,
|
||||
@@ -30,15 +38,28 @@ const ChatWindow: React.FC<ChatWindowProps> = ({ contract }) => {
|
||||
state => state.aiQuoteMessageContent,
|
||||
);
|
||||
const [showProfile, setShowProfile] = useState(true);
|
||||
const [followupModalVisible, setFollowupModalVisible] = useState(false);
|
||||
const [todoModalVisible, setTodoModalVisible] = useState(false);
|
||||
|
||||
const onToggleProfile = () => {
|
||||
setShowProfile(!showProfile);
|
||||
};
|
||||
|
||||
const typeOptions = [
|
||||
{ value: 0, label: "人工接待" },
|
||||
{ value: 1, label: "AI辅助" },
|
||||
{ value: 2, label: "AI接管" },
|
||||
];
|
||||
const handleFollowupClick = () => {
|
||||
setFollowupModalVisible(true);
|
||||
};
|
||||
|
||||
const handleFollowupModalClose = () => {
|
||||
setFollowupModalVisible(false);
|
||||
};
|
||||
|
||||
const handleTodoClick = () => {
|
||||
setTodoModalVisible(true);
|
||||
};
|
||||
|
||||
const handleTodoModalClose = () => {
|
||||
setTodoModalVisible(false);
|
||||
};
|
||||
|
||||
const [currentConfig, setCurrentConfig] = useState(
|
||||
typeOptions.find(option => option.value === aiQuoteMessageContent),
|
||||
@@ -115,8 +136,12 @@ const ChatWindow: React.FC<ChatWindowProps> = ({ contract }) => {
|
||||
</Space>
|
||||
</Header>
|
||||
<div className={styles.extend}>
|
||||
<Button icon={<BellOutlined />}>跟进提醒</Button>
|
||||
<Button icon={<CheckSquareOutlined />}>待办事项</Button>
|
||||
<Button icon={<BellOutlined />} onClick={handleFollowupClick}>
|
||||
跟进提醒
|
||||
</Button>
|
||||
<Button icon={<CheckSquareOutlined />} onClick={handleTodoClick}>
|
||||
待办事项
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* 聊天内容 */}
|
||||
@@ -130,6 +155,20 @@ const ChatWindow: React.FC<ChatWindowProps> = ({ contract }) => {
|
||||
|
||||
{/* 右侧个人资料卡片 */}
|
||||
{showProfile && <ProfileCard contract={contract} />}
|
||||
|
||||
{/* 跟进提醒模态框 */}
|
||||
<FollowupReminderModal
|
||||
visible={followupModalVisible}
|
||||
onClose={handleFollowupModalClose}
|
||||
recipientName={contract.nickname || contract.name}
|
||||
/>
|
||||
|
||||
{/* 待办事项模态框 */}
|
||||
<TodoListModal
|
||||
visible={todoModalVisible}
|
||||
onClose={handleTodoModalClose}
|
||||
clientName={contract.nickname || contract.name}
|
||||
/>
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user