From 7c3610055338ef0ce2ea3b28be4c5ffde0761d77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B6=85=E7=BA=A7=E8=80=81=E7=99=BD=E5=85=94?= Date: Mon, 13 Oct 2025 17:23:31 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E5=BF=AB=E6=8D=B7=E8=AF=AD?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=EF=BC=9A=E5=BC=95=E5=85=A5Layout=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=EF=BC=8C=E4=BC=98=E5=8C=96=E7=95=8C=E9=9D=A2=E7=BB=93?= =?UTF-8?q?=E6=9E=84=E5=92=8C=E6=A0=B7=E5=BC=8F=EF=BC=8C=E6=8F=90=E5=8D=87?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E4=BD=93=E9=AA=8C=E5=92=8C=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=8F=AF=E8=AF=BB=E6=80=A7=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../QuickWords/components/GroupModal.tsx | 67 ++++ .../QuickWords/components/QuickReplyModal.tsx | 247 ++++++++++++ .../components/QuickWords/index.tsx | 371 ++++++++---------- 3 files changed, 476 insertions(+), 209 deletions(-) create mode 100644 Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/QuickWords/components/GroupModal.tsx create mode 100644 Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/QuickWords/components/QuickReplyModal.tsx diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/QuickWords/components/GroupModal.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/QuickWords/components/GroupModal.tsx new file mode 100644 index 00000000..420d942b --- /dev/null +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/QuickWords/components/GroupModal.tsx @@ -0,0 +1,67 @@ +import React from "react"; +import { Modal, Form, Input, Space, Button } from "antd"; +import { AddGroupRequest } from "../api"; + +export interface GroupModalProps { + open: boolean; + mode: "add" | "edit"; + initialValues?: Partial; + onSubmit: (values: AddGroupRequest) => void; + onCancel: () => void; +} + +const GroupModal: React.FC = ({ + open, + mode, + initialValues, + onSubmit, + onCancel, +}) => { + const [form] = Form.useForm(); + + return ( + { + onCancel(); + form.resetFields(); + }} + footer={null} + destroyOnClose + > +
onSubmit(values)} + initialValues={initialValues} + > + + + + + + + + + + +
+
+ ); +}; + +export default GroupModal; diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/QuickWords/components/QuickReplyModal.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/QuickWords/components/QuickReplyModal.tsx new file mode 100644 index 00000000..478f1097 --- /dev/null +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/QuickWords/components/QuickReplyModal.tsx @@ -0,0 +1,247 @@ +import React, { useMemo } from "react"; +import { Modal, Form, Input, Select, Space, Button } from "antd"; +import { + PictureOutlined, + VideoCameraOutlined, + LinkOutlined, +} from "@ant-design/icons"; +import SimpleFileUpload from "@/components/Upload/SimpleFileUpload"; +// 简化版不再使用样式与解析组件 +import { AddReplyRequest } from "../api"; + +export interface QuickReplyModalProps { + open: boolean; + mode: "add" | "edit"; + initialValues?: Partial; + onSubmit: (values: AddReplyRequest) => void; + onCancel: () => void; + groupOptions?: { label: string; value: string }[]; + defaultGroupId?: string; +} + +const QuickReplyModal: React.FC = ({ + open, + mode, + initialValues, + onSubmit, + onCancel, + groupOptions, + defaultGroupId, +}) => { + const [form] = Form.useForm(); + + const mergedInitialValues = useMemo(() => { + return { + groupId: defaultGroupId, + msgType: initialValues?.msgType || ["1"], + ...initialValues, + } as Partial; + }, [initialValues, defaultGroupId]); + + // 监听类型变化 + const msgTypeWatch = Form.useWatch("msgType", form); + const selectedMsgType = useMemo(() => { + const value = msgTypeWatch; + const raw = Array.isArray(value) ? value[0] : value; + return Number(raw || "1"); + }, [msgTypeWatch]); + + // 根据文件格式判断消息类型 + 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 FileType = { + TEXT: 1, + IMAGE: 2, + VIDEO: 3, + AUDIO: 4, + FILE: 5, + } as const; + + const handleFileUploaded = ( + filePath: string | { url: string; durationMs: number }, + fileType: number, + ) => { + let msgType = 1; + if (([FileType.TEXT] as number[]).includes(fileType)) { + msgType = getMsgTypeByFileFormat(filePath as string); + } else if (([FileType.IMAGE] as number[]).includes(fileType)) { + msgType = 3; + } else if (([FileType.VIDEO] as number[]).includes(fileType)) { + msgType = 43; + } else if (([FileType.AUDIO] as number[]).includes(fileType)) { + msgType = 34; + } else if (([FileType.FILE] as number[]).includes(fileType)) { + msgType = 49; + } + + form.setFieldsValue({ + msgType: [String(msgType)], + content: ([FileType.AUDIO] as number[]).includes(fileType) + ? JSON.stringify(filePath) + : (filePath as string), + }); + }; + + const handleKeyPress = (e: React.KeyboardEvent) => { + if (e.key === "Enter" && !e.ctrlKey && !e.shiftKey) { + e.preventDefault(); + form.submit(); + } + }; + + // 简化后不再有预览解析 + + return ( + { + onCancel(); + form.resetFields(); + }} + footer={null} + destroyOnClose + > +
{ + const normalized = { + ...values, + msgType: Array.isArray(values.msgType) + ? values.msgType + : [String(values.msgType)], + } as AddReplyRequest; + onSubmit(normalized); + }} + initialValues={mergedInitialValues} + > + + + + + + + + 文本 + 图片 + 视频 + 链接 + + + + + {selectedMsgType === 1 && ( + form.setFieldsValue({ content: e.target.value })} + onKeyDown={handleKeyPress} + /> + )} + {selectedMsgType === 3 && ( + + handleFileUploaded(filePath, FileType.IMAGE) + } + maxSize={1} + type={1} + slot={} + /> + )} + {selectedMsgType === 43 && ( + + handleFileUploaded(filePath, FileType.VIDEO) + } + maxSize={1} + type={4} + slot={} + /> + )} + {selectedMsgType === 49 && ( + } + value={form.getFieldValue("content")} + onChange={e => form.setFieldsValue({ content: e.target.value })} + /> + )} + + + + + + + + + +
+ ); +}; + +export default QuickReplyModal; diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/QuickWords/index.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/QuickWords/index.tsx index b0eda51e..fd10071f 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/QuickWords/index.tsx +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/QuickWords/index.tsx @@ -1,6 +1,5 @@ import React, { useMemo, useState, useEffect, useCallback } from "react"; import { - Card, Input, Button, Space, @@ -8,10 +7,10 @@ import { Tree, Modal, Form, - Select, message, Tooltip, Spin, + Dropdown, } from "antd"; import { PlusOutlined, @@ -33,7 +32,12 @@ import { deleteGroup, AddReplyRequest, AddGroupRequest, + addGroup, } from "./api"; +import Layout from "@/components/Layout/LayoutFiexd"; +import QuickReplyModal from "./components/QuickReplyModal"; +import GroupModal from "./components/GroupModal"; +import { useWeChatStore } from "@/store/module/weChat/weChat"; // 消息类型枚举 export enum MessageType { @@ -70,8 +74,12 @@ const QuickWords: React.FC = ({ onInsert }) => { const [groupModalVisible, setGroupModalVisible] = useState(false); const [editingItem, setEditingItem] = useState(null); const [editingGroup, setEditingGroup] = useState(null); + const [isAddingGroup, setIsAddingGroup] = useState(false); const [form] = Form.useForm(); const [groupForm] = Form.useForm(); + const updateQuoteMessageContent = useWeChatStore( + state => state.updateQuoteMessageContent, + ); // 获取快捷语数据 const fetchQuickWords = useCallback(async () => { @@ -157,24 +165,26 @@ const QuickWords: React.FC = ({ onInsert }) => { justifyContent: "space-between", width: "100%", }} + onClick={e => { + e.stopPropagation(); + // 将快捷语内容写入输入框(仅文本或可直接粘贴的内容) + try { + if ([MessageType.TEXT].includes(reply.msgType)) { + updateQuoteMessageContent(reply.content || ""); + } else if ([MessageType.LINK].includes(reply.msgType)) { + updateQuoteMessageContent(reply.content || ""); + } else { + // 非文本类型,插入标题作为占位,方便用户手动调整 + updateQuoteMessageContent(reply.title || ""); + } + } catch (_) {} + }} >
{getMessageTypeIcon(reply.msgType)} {reply.title}
- - -
- + } + > + = ({ onInsert }) => { onExpand={setExpandedKeys} onSelect={setSelectedKeys} treeData={treeData} - style={{ maxHeight: 400, overflow: "auto" }} /> - {/* 添加快捷回复模态框 */} - { - setAddModalVisible(false); - form.resetFields(); - }} - footer={null} - > -
- - - + mode="add" + groupOptions={groupOptions} + defaultGroupId={ + selectedKeys[0]?.toString().replace("group-", "") || + groupOptions[0]?.value + } + onSubmit={handleAddReply} + onCancel={() => setAddModalVisible(false)} + /> - - - - - - - - - - - - - - -
-
- - {/* 编辑快捷回复模态框 */} - { setEditModalVisible(false); setEditingItem(null); - form.resetFields(); }} - footer={null} - > -
- - - + /> - - - - - - - - - - - - - - -
-
- - {/* 编辑分组模态框 */} - { setGroupModalVisible(false); setEditingGroup(null); - groupForm.resetFields(); + setIsAddingGroup(false); }} - footer={null} - > -
- - - - - - - - - - -
-
- + /> + ); };