From 0ae2265c50d1b3085b39da6f3a2a0ae7a7c914df 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: Sun, 28 Sep 2025 16:36:17 +0800 Subject: [PATCH 01/19] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=95=8F=E6=84=9F?= =?UTF-8?q?=E8=AF=8D=E7=AE=A1=E7=90=86=E6=A8=A1=E5=9D=97=EF=BC=9A=E5=B0=86?= =?UTF-8?q?=E6=95=8F=E6=84=9F=E8=AF=8D=E7=AE=A1=E7=90=86=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E8=BD=AC=E6=8D=A2=E4=B8=BA=E4=BD=BF=E7=94=A8forwardRef?= =?UTF-8?q?=EF=BC=8C=E5=A2=9E=E5=BC=BA=E4=B8=8E=E7=88=B6=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E7=9A=84=E4=BA=A4=E4=BA=92=E8=83=BD=E5=8A=9B=EF=BC=9B=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E6=95=8F=E6=84=9F=E8=AF=8D=E7=8A=B6=E6=80=81=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E6=8E=A5=E5=8F=A3=E7=9A=84=E8=AF=B7=E6=B1=82=E6=96=B9?= =?UTF-8?q?=E6=B3=95=EF=BC=9B=E4=BC=98=E5=8C=96=E5=88=A0=E9=99=A4=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=EF=BC=8C=E5=A2=9E=E5=8A=A0=E7=A1=AE=E8=AE=A4=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E7=9A=84=E5=BC=B9=E7=AA=97=EF=BC=9B=E6=94=B9=E8=BF=9B?= =?UTF-8?q?=E6=95=8F=E6=84=9F=E8=AF=8D=E8=AF=A6=E6=83=85=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E9=80=BB=E8=BE=91=EF=BC=8C=E6=8F=90=E5=8D=87=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E4=BD=93=E9=AA=8C=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../powerCenter/content-management/api.ts | 2 +- .../management/MaterialManagement.tsx | 51 ++- .../management/SensitiveWordManagement.tsx | 386 +++++++++--------- .../components/modals/SensitiveWordModal.tsx | 56 +-- .../powerCenter/content-management/index.tsx | 23 +- 5 files changed, 277 insertions(+), 241 deletions(-) diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/api.ts b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/api.ts index 62d156ce..337c3518 100644 --- a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/api.ts +++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/api.ts @@ -118,7 +118,7 @@ export function updateSensitiveWord(data: SensitiveWordUpdateRequest) { // 违禁词管理-修改状态 export function setSensitiveWordStatus(data: SensitiveWordSetStatusRequest) { - return request("/v1/kefu/content/sensitiveWord/setStatus", data, "POST"); + return request("/v1/kefu/content/sensitiveWord/setStatus", data, "GET"); } // 关键词回复管理相关接口 diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/management/MaterialManagement.tsx b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/management/MaterialManagement.tsx index 5680d28f..603d6075 100644 --- a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/management/MaterialManagement.tsx +++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/management/MaterialManagement.tsx @@ -4,7 +4,7 @@ import React, { forwardRef, useImperativeHandle, } from "react"; -import { Button, Input, Card, message, Modal } from "antd"; +import { Button, Input, Card, message, Popconfirm } from "antd"; import { SearchOutlined, FilterOutlined, @@ -91,22 +91,13 @@ const MaterialManagement = forwardRef>( // 素材管理相关函数 const handleDeleteMaterial = async (id: number) => { - Modal.confirm({ - title: "确认删除", - content: "确定要删除这个素材吗?删除后无法恢复。", - okText: "确定", - cancelText: "取消", - okType: "danger", - onOk: async () => { - try { - await deleteMaterial(id.toString()); - setMaterialsList(prev => prev.filter(item => item.id !== id)); - message.success("删除成功"); - } catch (error) { - message.error("删除失败"); - } - }, - }); + try { + await deleteMaterial(id.toString()); + setMaterialsList(prev => prev.filter(item => item.id !== id)); + message.success("删除成功"); + } catch (error) { + message.error("删除失败"); + } }; // 编辑素材 @@ -167,18 +158,24 @@ const MaterialManagement = forwardRef>( > 编辑 , - , + + , ]} >
{ - const [searchValue, setSearchValue] = useState(""); - const [sensitiveWordsList, setSensitiveWordsList] = useState< - SensitiveWordItem[] - >([]); - const [loading, setLoading] = useState(false); - const [editModalVisible, setEditModalVisible] = useState(false); - const [editingSensitiveWordId, setEditingSensitiveWordId] = useState< - string | null - >(null); +const SensitiveWordManagement = forwardRef>( + (props, ref) => { + const [searchValue, setSearchValue] = useState(""); + const [sensitiveWordsList, setSensitiveWordsList] = useState< + SensitiveWordItem[] + >([]); + const [loading, setLoading] = useState(false); + const [editModalVisible, setEditModalVisible] = useState(false); + const [editingSensitiveWordId, setEditingSensitiveWordId] = useState< + string | null + >(null); - const getTagColor = (tag: string) => { - switch (tag) { - case "政治": - return "#ff4d4f"; - case "色情": - return "#ff4d4f"; - case "暴力": - return "#ff4d4f"; - default: - return "#ff4d4f"; - } - }; + const getTagColor = (tag: string) => { + switch (tag) { + case "政治": + return "#ff4d4f"; + case "色情": + return "#ff4d4f"; + case "暴力": + return "#ff4d4f"; + default: + return "#ff4d4f"; + } + }; - // 操作类型映射 - const getOperationText = (operation: string) => { - switch (operation) { - case "0": - return "不操作"; - case "1": - return "替换"; - case "2": - return "删除"; - case "3": - return "警告"; - case "4": - return "禁止发送"; - default: - return "未知操作"; - } - }; + // 操作类型映射 + const getOperationText = (operation: string) => { + switch (operation) { + case "0": + return "不操作"; + case "1": + return "替换"; + case "2": + return "删除"; + case "3": + return "警告"; + case "4": + return "禁止发送"; + default: + return "未知操作"; + } + }; - // 获取敏感词列表 - const fetchSensitiveWords = async (params?: SensitiveWordListParams) => { - try { - setLoading(true); - const response = await getSensitiveWordList(params || {}); - if (response) { - setSensitiveWordsList(response.list || []); - } else { + // 获取敏感词列表 + const fetchSensitiveWords = async (params?: SensitiveWordListParams) => { + try { + setLoading(true); + const response = await getSensitiveWordList(params || {}); + if (response) { + setSensitiveWordsList(response.list || []); + } else { + setSensitiveWordsList([]); + message.error(response?.message || "获取敏感词列表失败"); + } + } catch (error) { + console.error("获取敏感词列表失败:", error); setSensitiveWordsList([]); - message.error(response?.message || "获取敏感词列表失败"); + message.error("获取敏感词列表失败"); + } finally { + setLoading(false); } - } catch (error) { - console.error("获取敏感词列表失败:", error); - setSensitiveWordsList([]); - message.error("获取敏感词列表失败"); - } finally { - setLoading(false); - } - }; + }; - // 敏感词管理相关函数 - const handleToggleSensitiveWord = async (id: string) => { - try { - const response = await setSensitiveWordStatus({ id }); - if (response) { - setSensitiveWordsList(prev => - prev.map(item => - item.id === id ? { ...item, enabled: !item.enabled } : item, - ), - ); - message.success("状态更新成功"); - } else { - message.error(response?.message || "状态更新失败"); + // 暴露方法给父组件 + useImperativeHandle(ref, () => ({ + fetchSensitiveWords, + })); + + // 敏感词管理相关函数 + const handleToggleSensitiveWord = async (id: string) => { + try { + const response = await setSensitiveWordStatus({ id }); + if (response) { + setSensitiveWordsList(prev => + prev.map(item => + item.id === id ? { ...item, enabled: !item.enabled } : item, + ), + ); + message.success("状态更新成功"); + } else { + message.error(response?.message || "状态更新失败"); + } + } catch (error) { + console.error("状态更新失败:", error); + message.error("状态更新失败"); } - } catch (error) { - console.error("状态更新失败:", error); - message.error("状态更新失败"); - } - }; + }; - const handleEditSensitiveWord = (id: string) => { - setEditingSensitiveWordId(id); - setEditModalVisible(true); - }; + const handleEditSensitiveWord = (id: string) => { + setEditingSensitiveWordId(id); + setEditModalVisible(true); + }; - // 编辑弹窗成功回调 - const handleEditSuccess = () => { - fetchSensitiveWords(); // 重新获取数据 - }; + // 编辑弹窗成功回调 + const handleEditSuccess = () => { + fetchSensitiveWords(); // 重新获取数据 + }; - const handleDeleteSensitiveWord = async (id: string) => { - try { - const response = await deleteSensitiveWord(id); - if (response) { + const handleDeleteSensitiveWord = async (id: string) => { + try { + await deleteSensitiveWord(id); setSensitiveWordsList(prev => prev.filter(item => item.id !== id)); message.success("删除成功"); - } else { - message.error(response?.message || "删除失败"); + } catch (error) { + console.error("删除失败:", error); + message.error("删除失败"); } - } catch (error) { - console.error("删除失败:", error); - message.error("删除失败"); - } - }; + }; + + // 搜索和筛选功能 + const filteredSensitiveWords = sensitiveWordsList.filter(item => { + if (!searchValue) return true; + return ( + item.title.toLowerCase().includes(searchValue.toLowerCase()) || + item.keywords.toLowerCase().includes(searchValue.toLowerCase()) || + item.content.toLowerCase().includes(searchValue.toLowerCase()) + ); + }); + + // 搜索处理函数 + const handleSearch = (value: string) => { + fetchSensitiveWords({ keyword: value }); + }; + + // 组件挂载时获取数据 + useEffect(() => { + fetchSensitiveWords(); + }, []); - // 搜索和筛选功能 - const filteredSensitiveWords = sensitiveWordsList.filter(item => { - if (!searchValue) return true; return ( - item.title.toLowerCase().includes(searchValue.toLowerCase()) || - item.keywords.toLowerCase().includes(searchValue.toLowerCase()) || - item.content.toLowerCase().includes(searchValue.toLowerCase()) - ); - }); +
+
+ setSearchValue(e.target.value)} + onSearch={handleSearch} + style={{ width: 300 }} + prefix={} + /> + +
- // 搜索处理函数 - const handleSearch = (value: string) => { - fetchSensitiveWords({ keyword: value }); - }; - - // 组件挂载时获取数据 - useEffect(() => { - fetchSensitiveWords(); - }, []); - - return ( -
-
- setSearchValue(e.target.value)} - onSearch={handleSearch} - style={{ width: 300 }} - prefix={} - /> - -
- -
- {loading ? ( -
加载中...
- ) : filteredSensitiveWords.length === 0 ? ( -
暂无敏感词数据
- ) : ( - filteredSensitiveWords.map(item => ( -
-
-
{item.title}
- - {item.keywords} - -
- {getOperationText(item.operation)} +
+ {loading ? ( +
加载中...
+ ) : filteredSensitiveWords.length === 0 ? ( +
暂无敏感词数据
+ ) : ( + filteredSensitiveWords.map(item => ( +
+
+
{item.title}
+ + {item.keywords} + +
+ {getOperationText(item.operation)} +
+
+
+ handleToggleSensitiveWord(item.id)} + className={styles.toggleSwitch} + /> +
-
- handleToggleSensitiveWord(item.id)} - className={styles.toggleSwitch} - /> -
-
- )) - )} -
+ )) + )} +
- {/* 编辑弹窗 */} - { - setEditModalVisible(false); - setEditingSensitiveWordId(null); - }} - onSuccess={handleEditSuccess} - /> -
- ); -}; + {/* 编辑弹窗 */} + { + setEditModalVisible(false); + setEditingSensitiveWordId(null); + }} + onSuccess={handleEditSuccess} + /> +
+ ); + }, +); + +SensitiveWordManagement.displayName = "SensitiveWordManagement"; export default SensitiveWordManagement; diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/modals/SensitiveWordModal.tsx b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/modals/SensitiveWordModal.tsx index 22628f4e..c715e265 100644 --- a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/modals/SensitiveWordModal.tsx +++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/modals/SensitiveWordModal.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useCallback } from "react"; import { Modal, Form, Input, Button, message, Select } from "antd"; import { addSensitiveWord, @@ -30,34 +30,40 @@ const SensitiveWordModal: React.FC = ({ const [loading, setLoading] = useState(false); // 获取敏感词详情 - const fetchSensitiveWordDetails = async (id: string) => { - try { - const response = await getSensitiveWordDetails(id); - if (response) { - const sensitiveWord = response; - form.setFieldsValue({ - title: sensitiveWord.title, - keywords: sensitiveWord.keywords, - content: sensitiveWord.content, - operation: sensitiveWord.operation, - status: sensitiveWord.status, - }); + const fetchSensitiveWordDetails = useCallback( + async (id: string) => { + try { + const response = await getSensitiveWordDetails(id); + if (response) { + const sensitiveWord = response; + form.setFieldsValue({ + title: sensitiveWord.title, + keywords: sensitiveWord.keywords, + content: sensitiveWord.content, + operation: sensitiveWord.operation, + status: sensitiveWord.status, + }); + } + } catch (error) { + console.error("获取敏感词详情失败:", error); + message.error("获取敏感词详情失败"); } - } catch (error) { - console.error("获取敏感词详情失败:", error); - message.error("获取敏感词详情失败"); - } - }; + }, + [form], + ); - // 当弹窗打开且为编辑模式时,获取详情 + // 当弹窗打开时处理数据 useEffect(() => { - if (visible && mode === "edit" && sensitiveWordId) { - fetchSensitiveWordDetails(sensitiveWordId); - } else if (visible && mode === "add") { - // 添加模式时重置表单 - form.resetFields(); + if (visible) { + if (mode === "edit" && sensitiveWordId) { + // 编辑模式:获取详情 + fetchSensitiveWordDetails(sensitiveWordId); + } else if (mode === "add") { + // 添加模式:重置表单 + form.resetFields(); + } } - }, [visible, mode, sensitiveWordId]); + }, [visible, mode, sensitiveWordId, fetchSensitiveWordDetails, form]); const handleSubmit = async (values: any) => { try { diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/index.tsx b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/index.tsx index 4f8b4fa0..ead8b0bc 100644 --- a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/index.tsx +++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/index.tsx @@ -22,6 +22,7 @@ const ContentManagement: React.FC = () => { // 引用管理组件 const materialManagementRef = useRef(null); const keywordManagementRef = useRef(null); + const sensitiveWordManagementRef = useRef(null); const tabs = [ { key: "material", label: "素材资源库" }, @@ -44,12 +45,20 @@ const ContentManagement: React.FC = () => { // 弹窗成功回调 const handleModalSuccess = () => { + console.log("handleModalSuccess"); // 刷新素材列表 if (materialManagementRef.current?.fetchMaterials) { + console.log("刷新素材列表"); materialManagementRef.current.fetchMaterials(); } + // 刷新敏感词列表 + if (sensitiveWordManagementRef.current?.fetchSensitiveWords) { + console.log("刷新敏感词列表"); + sensitiveWordManagementRef.current.fetchSensitiveWords(); + } // 刷新关键词列表 if (keywordManagementRef.current?.fetchKeywords) { + console.log("刷新关键词列表"); keywordManagementRef.current.fetchKeywords(); } }; @@ -61,7 +70,12 @@ const ContentManagement: React.FC = () => { ); case "sensitive": - return ; + return ( + + ); case "keyword": return ( @@ -89,12 +103,12 @@ const ContentManagement: React.FC = () => { > 添加素材 - +
} /> @@ -136,6 +150,7 @@ const ContentManagement: React.FC = () => { setKeywordModalVisible(false)} onSuccess={handleModalSuccess} /> From 1bd3883b1866d5aece21492f6ecafb4263afc558 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: Sun, 28 Sep 2025 16:37:58 +0800 Subject: [PATCH 02/19] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=95=8F=E6=84=9F?= =?UTF-8?q?=E8=AF=8D=E7=AE=A1=E7=90=86=E6=A8=A1=E5=9D=97=EF=BC=9A=E5=B0=86?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E7=B1=BB=E5=9E=8B=E5=8F=82=E6=95=B0=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E4=BB=8E=E5=AD=97=E7=AC=A6=E4=B8=B2=E6=94=B9=E4=B8=BA?= =?UTF-8?q?=E6=95=B0=E5=AD=97=EF=BC=8C=E4=BB=A5=E6=8F=90=E9=AB=98=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E5=AE=89=E5=85=A8=E6=80=A7=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 --- .../management/SensitiveWordManagement.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/management/SensitiveWordManagement.tsx b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/management/SensitiveWordManagement.tsx index 31c289d8..0e2cd1ec 100644 --- a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/management/SensitiveWordManagement.tsx +++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/management/SensitiveWordManagement.tsx @@ -58,17 +58,17 @@ const SensitiveWordManagement = forwardRef>( }; // 操作类型映射 - const getOperationText = (operation: string) => { + const getOperationText = (operation: number) => { switch (operation) { - case "0": + case 0: return "不操作"; - case "1": + case 1: return "替换"; - case "2": + case 2: return "删除"; - case "3": + case 3: return "警告"; - case "4": + case 4: return "禁止发送"; default: return "未知操作"; From d8074502e06a80e950cab84637f76f8a75c4b4aa 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: Sun, 28 Sep 2025 17:41:01 +0800 Subject: [PATCH 03/19] =?UTF-8?q?=E9=87=8D=E6=9E=84=E5=85=B3=E9=94=AE?= =?UTF-8?q?=E8=AF=8D=E7=AE=A1=E7=90=86=E6=A8=A1=E5=9D=97=EF=BC=9A=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E5=85=B3=E9=94=AE=E8=AF=8D=E5=92=8C=E6=95=8F=E6=84=9F?= =?UTF-8?q?=E8=AF=8D=E7=9A=84=E8=AF=B7=E6=B1=82=E5=8F=82=E6=95=B0=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E4=B8=BA=E6=95=B0=E5=AD=97=EF=BC=8C=E5=A2=9E=E5=BC=BA?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E5=AE=89=E5=85=A8=E6=80=A7=EF=BC=9B=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E5=85=B3=E9=94=AE=E8=AF=8D=E7=AE=A1=E7=90=86=E7=95=8C?= =?UTF-8?q?=E9=9D=A2=EF=BC=8C=E6=94=B9=E8=BF=9B=E7=8A=B6=E6=80=81=E5=88=87?= =?UTF-8?q?=E6=8D=A2=E5=92=8C=E5=88=A0=E9=99=A4=E5=8A=9F=E8=83=BD=EF=BC=8C?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=A1=AE=E8=AE=A4=E5=88=A0=E9=99=A4=E7=9A=84?= =?UTF-8?q?=E5=BC=B9=E7=AA=97=EF=BC=8C=E6=8F=90=E5=8D=87=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E4=BD=93=E9=AA=8C=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../powerCenter/content-management/api.ts | 12 +- .../management/KeywordManagement.tsx | 174 ++++++------ .../management/SensitiveWordManagement.tsx | 8 +- .../components/management/index.module.scss | 252 ++++++++++++++++++ .../components/modals/KeywordModal.tsx | 48 ++-- .../components/modals/SensitiveWordModal.tsx | 14 +- .../content-management/index.module.scss | 205 -------------- 7 files changed, 387 insertions(+), 326 deletions(-) create mode 100644 Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/management/index.module.scss diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/api.ts b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/api.ts index 337c3518..eff8f497 100644 --- a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/api.ts +++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/api.ts @@ -132,9 +132,9 @@ export interface KeywordAddRequest { title: string; keywords: string; content: string; - matchType: string; // 匹配类型:模糊匹配、精确匹配 - priority: string; // 优先级 - replyType: string; // 回复类型:文本回复、模板回复 + type: number; // 匹配类型:模糊匹配、精确匹配 + level: number; // 优先级 + replyType: number; // 回复类型:文本回复、模板回复 status: string; } @@ -143,7 +143,7 @@ export interface KeywordUpdateRequest extends KeywordAddRequest { } export interface KeywordSetStatusRequest { - id: string; + id: number; } // 关键词回复-列表 @@ -157,12 +157,12 @@ export function addKeyword(data: KeywordAddRequest) { } // 关键词回复-详情 -export function getKeywordDetails(id: string) { +export function getKeywordDetails(id: number) { return request("/v1/kefu/content/keywords/details", { id }, "GET"); } // 关键词回复-删除 -export function deleteKeyword(id: string) { +export function deleteKeyword(id: number) { return request("/v1/kefu/content/keywords/del", { id }, "DELETE"); } diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/management/KeywordManagement.tsx b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/management/KeywordManagement.tsx index 99be1aaa..1dc3055f 100644 --- a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/management/KeywordManagement.tsx +++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/management/KeywordManagement.tsx @@ -4,14 +4,14 @@ import React, { forwardRef, useImperativeHandle, } from "react"; -import { Button, Input, Tag, Switch, message } from "antd"; +import { Button, Input, Tag, Switch, message, Popconfirm } from "antd"; import { SearchOutlined, FilterOutlined, FormOutlined, DeleteOutlined, } from "@ant-design/icons"; -import styles from "../../index.module.scss"; +import styles from "./index.module.scss"; import { getKeywordList, deleteKeyword, @@ -23,15 +23,15 @@ import KeywordModal from "../modals/KeywordModal"; const { Search } = Input; interface KeywordItem { - id: string; + id?: number; + type: number; + replyType: number; title: string; keywords: string; + status: number; content: string; - matchType: string; - priority: string; - replyType: string; - status: string; - enabled: boolean; + materialId: string; + level: number; } const KeywordManagement = forwardRef>( @@ -40,31 +40,52 @@ const KeywordManagement = forwardRef>( const [keywordsList, setKeywordsList] = useState([]); const [loading, setLoading] = useState(false); const [editModalVisible, setEditModalVisible] = useState(false); - const [editingKeywordId, setEditingKeywordId] = useState( + const [editingKeywordId, setEditingKeywordId] = useState( null, ); + //匹配类型 + const getMatchTypeText = (type: number) => { + switch (type) { + case 0: + return "模糊匹配"; + case 1: + return "精确匹配"; + } + }; + + //匹配优先级 + const getPriorityText = (level: number) => { + switch (level) { + case 0: + return "低优先级"; + case 1: + return "中优先级"; + case 2: + return "高优先级"; + } + }; // 回复类型映射 - const getReplyTypeText = (replyType: string) => { + const getReplyTypeText = (replyType: number) => { switch (replyType) { - case "text": - return "文本回复"; - case "template": - return "模板回复"; + case 0: + return "素材回复"; + case 1: + return "自定义"; default: return "未知类型"; } }; // 回复类型颜色 - const getReplyTypeColor = (replyType: string) => { + const getReplyTypeColor = (replyType: number) => { switch (replyType) { - case "text": - return "#1890ff"; - case "template": - return "#722ed1"; + case 0: + return "blue"; + case 1: + return "purple"; default: - return "#8c8c8c"; + return "gray"; } }; @@ -94,26 +115,24 @@ const KeywordManagement = forwardRef>( })); // 关键词管理相关函数 - const handleToggleKeyword = async (id: string) => { + const handleToggleKeyword = async (id: number) => { try { - const response = await setKeywordStatus({ id }); - if (response) { - setKeywordsList(prev => - prev.map(item => - item.id === id ? { ...item, enabled: !item.enabled } : item, - ), - ); - message.success("状态更新成功"); - } else { - message.error(response?.message || "状态更新失败"); - } + await setKeywordStatus({ id }); + setKeywordsList(prev => + prev.map(item => + item.id === id + ? { ...item, status: item.status === 1 ? 0 : 1 } + : item, + ), + ); + message.success("状态更新成功"); } catch (error) { console.error("状态更新失败:", error); message.error("状态更新失败"); } }; - const handleEditKeyword = (id: string) => { + const handleEditKeyword = (id: number) => { setEditingKeywordId(id); setEditModalVisible(true); }; @@ -123,15 +142,11 @@ const KeywordManagement = forwardRef>( fetchKeywords(); // 重新获取数据 }; - const handleDeleteKeyword = async (id: string) => { + const handleDeleteKeyword = async (id: number) => { try { - const response = await deleteKeyword(id); - if (response) { - setKeywordsList(prev => prev.filter(item => item.id !== id)); - message.success("删除成功"); - } else { - message.error(response?.message || "删除失败"); - } + await deleteKeyword(id); + setKeywordsList(prev => prev.filter(item => item.id !== id)); + message.success("删除成功"); } catch (error) { console.error("删除失败:", error); message.error("删除失败"); @@ -169,7 +184,7 @@ const KeywordManagement = forwardRef>( style={{ width: 300 }} prefix={} /> - + {/* */}
@@ -181,41 +196,48 @@ const KeywordManagement = forwardRef>( filteredKeywords.map(item => (
-
{item.title}
-
- {item.matchType} - - 优先级{item.priority} - +
+
+
{item.title}
+ {getMatchTypeText(item.type)} + {getPriorityText(item.level)} +
+
{item.content}
+
+ + {getReplyTypeText(item.replyType)} + +
+
+
+ handleToggleKeyword(item.id)} + className={styles.toggleSwitch} + /> +
-
{item.content}
- - {getReplyTypeText(item.replyType)} - -
-
- handleToggleKeyword(item.id)} - className={styles.toggleSwitch} - /> -
)) diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/management/SensitiveWordManagement.tsx b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/management/SensitiveWordManagement.tsx index 0e2cd1ec..d6f6d184 100644 --- a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/management/SensitiveWordManagement.tsx +++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/management/SensitiveWordManagement.tsx @@ -27,7 +27,7 @@ interface SensitiveWordItem { title: string; keywords: string; content: string; - operation: string; + operation: number; status: string; enabled: boolean; } @@ -185,12 +185,6 @@ const SensitiveWordManagement = forwardRef>(
{item.title}
- - {item.keywords} -
{getOperationText(item.operation)}
diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/management/index.module.scss b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/management/index.module.scss new file mode 100644 index 00000000..a7624c01 --- /dev/null +++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/management/index.module.scss @@ -0,0 +1,252 @@ +// 关键词管理样式 +.keywordContent { + .searchSection { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 24px; + + :global(.ant-input-search) { + width: 300px; + } + + :global(.ant-btn) { + height: 32px; + border-radius: 6px; + } + } + + .keywordList { + display: flex; + flex-direction: column; + gap: 12px; + } + + .keywordItem { + padding: 16px 20px; + background: #fff; + border: 1px solid #f0f0f0; + border-radius: 8px; + transition: all 0.3s; + + &:hover { + border-color: #d9d9d9; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); + } + + .itemContent { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 16px; + + .leftSection { + flex: 1; + display: flex; + flex-direction: column; + gap: 8px; + + .titleRow { + display: flex; + align-items: center; + gap: 12px; + margin-bottom: 4px; + + .title { + font-size: 16px; + font-weight: 500; + color: #262626; + margin: 0; + } + + .tags { + display: flex; + gap: 8px; + + .matchTag, + .priorityTag { + font-size: 12px; + padding: 2px 8px; + border-radius: 12px; + background: #f0f0f0; + color: #666; + border: none; + } + } + } + + .description { + font-size: 14px; + color: #666; + line-height: 1.5; + margin-bottom: 8px; + } + + .replyTypeTag { + font-size: 12px; + padding: 2px 8px; + border-radius: 20px; + background: #fff; + color: #fff; + } + } + + .rightSection { + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; + + .toggleSwitch { + :global(.ant-switch) { + background-color: #d9d9d9; + } + + :global(.ant-switch-checked) { + background-color: #1890ff; + } + } + + .actionBtn { + width: 28px; + height: 28px; + padding: 0; + border-radius: 4px; + display: flex; + align-items: center; + justify-content: center; + + &:hover { + background-color: #f5f5f5; + } + + .editIcon { + font-size: 14px; + color: #1890ff; + } + + .deleteIcon { + font-size: 14px; + color: #ff4d4f; + } + } + } + } + } +} + +// 响应式设计 +@media (max-width: 768px) { + .container { + padding: 16px; + } + + .headerActions { + flex-direction: column; + align-items: stretch; + + :global(.ant-btn) { + width: 100%; + } + } + + .tabs { + flex-direction: column; + padding: 0; + + .tab { + border-bottom: 1px solid #e8e8e8; + border-radius: 0; + + &:last-child { + border-bottom: none; + } + } + } + + .materialContent { + .searchSection { + flex-direction: column; + gap: 12px; + align-items: stretch; + + :global(.ant-input-search) { + width: 100%; + } + } + + .materialGrid { + grid-template-columns: 1fr; + gap: 16px; + } + } + + .sensitiveContent { + .searchSection { + flex-direction: column; + gap: 12px; + align-items: stretch; + + :global(.ant-input-search) { + width: 100%; + } + } + + .sensitiveItem { + flex-direction: column; + align-items: stretch; + gap: 12px; + + .itemContent { + flex-direction: column; + align-items: flex-start; + gap: 8px; + + .categoryName { + min-width: auto; + } + } + + .itemActions { + justify-content: flex-end; + } + } + } + + .keywordContent { + .searchSection { + flex-direction: column; + gap: 12px; + align-items: stretch; + + :global(.ant-input-search) { + width: 100%; + } + } + + .keywordItem { + .itemContent { + flex-direction: column; + gap: 12px; + + .leftSection { + .titleRow { + flex-direction: column; + align-items: flex-start; + gap: 8px; + + .tags { + flex-wrap: wrap; + } + } + } + + .rightSection { + flex-direction: row; + justify-content: flex-end; + align-items: center; + } + } + } + } +} diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/modals/KeywordModal.tsx b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/modals/KeywordModal.tsx index 209977fb..eeb3cea2 100644 --- a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/modals/KeywordModal.tsx +++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/modals/KeywordModal.tsx @@ -14,7 +14,7 @@ const { Option } = Select; interface KeywordModalProps { visible: boolean; mode: "add" | "edit"; - keywordId?: string | null; + keywordId?: number | null; onCancel: () => void; onSuccess: () => void; } @@ -31,7 +31,7 @@ const KeywordModal: React.FC = ({ // 获取关键词详情 const fetchKeywordDetails = useCallback( - async (id: string) => { + async (id: number) => { try { const response = await getKeywordDetails(id); if (response) { @@ -40,8 +40,8 @@ const KeywordModal: React.FC = ({ title: keyword.title, keywords: keyword.keywords, content: keyword.content, - matchType: keyword.matchType, - priority: keyword.priority, + type: keyword.type, + level: keyword.level, replyType: keyword.replyType, status: keyword.status, }); @@ -76,8 +76,8 @@ const KeywordModal: React.FC = ({ title: values.title, keywords: values.keywords, content: values.content, - matchType: values.matchType, - priority: values.priority, + type: values.type, + level: values.level, replyType: values.replyType, status: values.status || "1", }; @@ -97,8 +97,8 @@ const KeywordModal: React.FC = ({ title: values.title, keywords: values.keywords, content: values.content, - matchType: values.matchType, - priority: values.priority, + type: values.type, + level: values.level, replyType: values.replyType, status: values.status, }; @@ -141,10 +141,10 @@ const KeywordModal: React.FC = ({ layout="vertical" onFinish={handleSubmit} initialValues={{ - status: "1", - matchType: "模糊匹配", - priority: "1", - replyType: "text", + status: 1, + type: "模糊匹配", + level: 1, + replyType: 0, }} > = ({ @@ -202,8 +200,8 @@ const KeywordModal: React.FC = ({ rules={[{ required: true, message: "请选择回复类型" }]} > @@ -213,8 +211,8 @@ const KeywordModal: React.FC = ({ rules={[{ required: true, message: "请选择状态" }]} > diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/modals/SensitiveWordModal.tsx b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/modals/SensitiveWordModal.tsx index c715e265..5e125b4a 100644 --- a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/modals/SensitiveWordModal.tsx +++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/modals/SensitiveWordModal.tsx @@ -166,11 +166,11 @@ const SensitiveWordModal: React.FC = ({ rules={[{ required: true, message: "请选择操作类型" }]} > @@ -180,8 +180,8 @@ const SensitiveWordModal: React.FC = ({ rules={[{ required: true, message: "请选择状态" }]} > diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/index.module.scss b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/index.module.scss index 72934dce..7fa37698 100644 --- a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/index.module.scss +++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/index.module.scss @@ -291,133 +291,6 @@ } } -// 关键词管理样式 -.keywordContent { - .searchSection { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 24px; - - :global(.ant-input-search) { - width: 300px; - } - - :global(.ant-btn) { - height: 32px; - border-radius: 6px; - } - } - - .keywordList { - display: flex; - flex-direction: column; - gap: 12px; - } - - .keywordItem { - display: flex; - justify-content: space-between; - align-items: flex-start; - padding: 16px 20px; - background: #fff; - border: 1px solid #f0f0f0; - border-radius: 8px; - transition: all 0.3s; - - &:hover { - border-color: #d9d9d9; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); - } - - .itemContent { - display: flex; - flex-direction: column; - gap: 8px; - flex: 1; - - .title { - font-size: 16px; - font-weight: 500; - color: #262626; - margin: 0; - } - - .tags { - display: flex; - gap: 8px; - margin-bottom: 4px; - - .matchTag, - .priorityTag { - font-size: 12px; - padding: 2px 8px; - border-radius: 12px; - background: #f0f0f0; - color: #666; - border: none; - } - } - - .description { - font-size: 14px; - color: #666; - line-height: 1.5; - margin-bottom: 8px; - } - - .replyTypeTag { - font-size: 12px; - padding: 2px 8px; - border-radius: 4px; - border: none; - align-self: flex-start; - } - } - - .itemActions { - display: flex; - align-items: center; - gap: 8px; - margin-left: 16px; - - .toggleSwitch { - :global(.ant-switch) { - background-color: #d9d9d9; - } - - :global(.ant-switch-checked) { - background-color: #1890ff; - } - } - - .actionBtn { - width: 28px; - height: 28px; - padding: 0; - border-radius: 4px; - display: flex; - align-items: center; - justify-content: center; - - &:hover { - background-color: #f5f5f5; - } - - .editIcon { - font-size: 14px; - color: #1890ff; - } - - .deleteIcon { - font-size: 14px; - color: #ff4d4f; - } - } - } - } -} - // 弹窗中的图片上传组件样式 :global(.material-cover-upload) { .uploadContainer { @@ -525,82 +398,4 @@ } } } - - .materialContent { - .searchSection { - flex-direction: column; - gap: 12px; - align-items: stretch; - - :global(.ant-input-search) { - width: 100%; - } - } - - .materialGrid { - grid-template-columns: 1fr; - gap: 16px; - } - } - - .sensitiveContent { - .searchSection { - flex-direction: column; - gap: 12px; - align-items: stretch; - - :global(.ant-input-search) { - width: 100%; - } - } - - .sensitiveItem { - flex-direction: column; - align-items: stretch; - gap: 12px; - - .itemContent { - flex-direction: column; - align-items: flex-start; - gap: 8px; - - .categoryName { - min-width: auto; - } - } - - .itemActions { - justify-content: flex-end; - } - } - } - - .keywordContent { - .searchSection { - flex-direction: column; - gap: 12px; - align-items: stretch; - - :global(.ant-input-search) { - width: 100%; - } - } - - .keywordItem { - flex-direction: column; - align-items: stretch; - gap: 12px; - - .itemContent { - .tags { - flex-wrap: wrap; - } - } - - .itemActions { - justify-content: flex-end; - margin-left: 0; - } - } - } } From ed43e67047de84d0dbe63b938f8f2bf8895750e1 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: Sun, 28 Sep 2025 18:08:50 +0800 Subject: [PATCH 04/19] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=88=86=E9=A1=B5?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E5=88=B0=E5=85=B3=E9=94=AE=E8=AF=8D=E3=80=81?= =?UTF-8?q?=E7=B4=A0=E6=9D=90=E5=92=8C=E6=95=8F=E6=84=9F=E8=AF=8D=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E6=A8=A1=E5=9D=97=EF=BC=9A=E5=BC=95=E5=85=A5Paginatio?= =?UTF-8?q?n=E7=BB=84=E4=BB=B6=EF=BC=8C=E6=9B=B4=E6=96=B0=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E5=8F=82=E6=95=B0=E4=BB=A5=E6=94=AF=E6=8C=81=E5=88=86?= =?UTF-8?q?=E9=A1=B5=EF=BC=8C=E4=BC=98=E5=8C=96=E6=95=B0=E6=8D=AE=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E9=80=BB=E8=BE=91=EF=BC=8C=E6=8F=90=E5=8D=87=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E4=BD=93=E9=AA=8C=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../management/KeywordManagement.tsx | 56 ++++++++++++- .../management/MaterialManagement.tsx | 82 ++++++++++++++----- .../management/SensitiveWordManagement.tsx | 56 ++++++++++++- 3 files changed, 167 insertions(+), 27 deletions(-) diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/management/KeywordManagement.tsx b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/management/KeywordManagement.tsx index 1dc3055f..276306d4 100644 --- a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/management/KeywordManagement.tsx +++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/management/KeywordManagement.tsx @@ -4,7 +4,15 @@ import React, { forwardRef, useImperativeHandle, } from "react"; -import { Button, Input, Tag, Switch, message, Popconfirm } from "antd"; +import { + Button, + Input, + Tag, + Switch, + message, + Popconfirm, + Pagination, +} from "antd"; import { SearchOutlined, FilterOutlined, @@ -43,6 +51,11 @@ const KeywordManagement = forwardRef>( const [editingKeywordId, setEditingKeywordId] = useState( null, ); + const [pagination, setPagination] = useState({ + current: 1, + pageSize: 10, + total: 0, + }); //匹配类型 const getMatchTypeText = (type: number) => { @@ -93,9 +106,18 @@ const KeywordManagement = forwardRef>( const fetchKeywords = async (params?: KeywordListParams) => { try { setLoading(true); - const response = await getKeywordList(params || {}); + const requestParams = { + page: pagination.current.toString(), + limit: pagination.pageSize.toString(), + ...params, + }; + const response = await getKeywordList(requestParams); if (response) { setKeywordsList(response.list || []); + setPagination(prev => ({ + ...prev, + total: response.total || 0, + })); } else { setKeywordsList([]); message.error(response?.message || "获取关键词列表失败"); @@ -165,13 +187,23 @@ const KeywordManagement = forwardRef>( // 搜索处理函数 const handleSearch = (value: string) => { + setPagination(prev => ({ ...prev, current: 1 })); fetchKeywords({ keyword: value }); }; - // 组件挂载时获取数据 + // 分页处理函数 + const handlePageChange = (page: number, pageSize?: number) => { + setPagination(prev => ({ + ...prev, + current: page, + pageSize: pageSize || prev.pageSize, + })); + }; + + // 组件挂载和分页变化时获取数据 useEffect(() => { fetchKeywords(); - }, []); + }, [pagination.current, pagination.pageSize]); return (
@@ -244,6 +276,22 @@ const KeywordManagement = forwardRef>( )}
+ {/* 分页组件 */} +
+ + `第 ${range[0]}-${range[1]} 条/共 ${total} 条` + } + onChange={handlePageChange} + onShowSizeChange={handlePageChange} + /> +
+ {/* 编辑弹窗 */} >( const [editingMaterialId, setEditingMaterialId] = useState( null, ); + const [pagination, setPagination] = useState({ + current: 1, + pageSize: 10, + total: 0, + }); // 获取类型图标 const getTypeIcon = (type: string) => { @@ -65,24 +71,36 @@ const MaterialManagement = forwardRef>( }; // 获取素材列表 - const fetchMaterials = async (params?: MaterialListParams) => { - try { - setLoading(true); - const response = await getMaterialList(params || {}); - if (response) { - setMaterialsList(response.list || []); - } else { + const fetchMaterials = useCallback( + async (params?: MaterialListParams) => { + try { + setLoading(true); + const requestParams = { + page: pagination.current.toString(), + limit: pagination.pageSize.toString(), + ...params, + }; + const response = await getMaterialList(requestParams); + if (response) { + setMaterialsList(response.list || []); + setPagination(prev => ({ + ...prev, + total: response.total || 0, + })); + } else { + setMaterialsList([]); + message.error(response?.message || "获取素材列表失败"); + } + } catch (error) { + console.error("获取素材列表失败:", error); setMaterialsList([]); - message.error(response?.message || "获取素材列表失败"); + message.error("获取素材列表失败"); + } finally { + setLoading(false); } - } catch (error) { - console.error("获取素材列表失败:", error); - setMaterialsList([]); - message.error("获取素材列表失败"); - } finally { - setLoading(false); - } - }; + }, + [pagination], + ); // 暴露方法给父组件 useImperativeHandle(ref, () => ({ @@ -113,13 +131,23 @@ const MaterialManagement = forwardRef>( // 搜索处理函数 const handleSearch = (value: string) => { + setPagination(prev => ({ ...prev, current: 1 })); fetchMaterials({ keyword: value }); }; - // 组件挂载时获取数据 + // 分页处理函数 + const handlePageChange = (page: number, pageSize?: number) => { + setPagination(prev => ({ + ...prev, + current: page, + pageSize: pageSize || prev.pageSize, + })); + }; + + // 组件挂载和分页变化时获取数据 useEffect(() => { fetchMaterials(); - }, []); + }, [fetchMaterials]); return (
@@ -249,6 +277,22 @@ const MaterialManagement = forwardRef>( )}
+ {/* 分页组件 */} +
+ + `第 ${range[0]}-${range[1]} 条/共 ${total} 条` + } + onChange={handlePageChange} + onShowSizeChange={handlePageChange} + /> +
+ {/* 编辑弹窗 */} >( const [editingSensitiveWordId, setEditingSensitiveWordId] = useState< string | null >(null); + const [pagination, setPagination] = useState({ + current: 1, + pageSize: 10, + total: 0, + }); const getTagColor = (tag: string) => { switch (tag) { @@ -79,9 +92,18 @@ const SensitiveWordManagement = forwardRef>( const fetchSensitiveWords = async (params?: SensitiveWordListParams) => { try { setLoading(true); - const response = await getSensitiveWordList(params || {}); + const requestParams = { + page: pagination.current.toString(), + limit: pagination.pageSize.toString(), + ...params, + }; + const response = await getSensitiveWordList(requestParams); if (response) { setSensitiveWordsList(response.list || []); + setPagination(prev => ({ + ...prev, + total: response.total || 0, + })); } else { setSensitiveWordsList([]); message.error(response?.message || "获取敏感词列表失败"); @@ -153,13 +175,23 @@ const SensitiveWordManagement = forwardRef>( // 搜索处理函数 const handleSearch = (value: string) => { + setPagination(prev => ({ ...prev, current: 1 })); fetchSensitiveWords({ keyword: value }); }; - // 组件挂载时获取数据 + // 分页处理函数 + const handlePageChange = (page: number, pageSize?: number) => { + setPagination(prev => ({ + ...prev, + current: page, + pageSize: pageSize || prev.pageSize, + })); + }; + + // 组件挂载和分页变化时获取数据 useEffect(() => { fetchSensitiveWords(); - }, []); + }, [pagination.current, pagination.pageSize]); return (
@@ -223,6 +255,22 @@ const SensitiveWordManagement = forwardRef>( )}
+ {/* 分页组件 */} +
+ + `第 ${range[0]}-${range[1]} 条/共 ${total} 条` + } + onChange={handlePageChange} + onShowSizeChange={handlePageChange} + /> +
+ {/* 编辑弹窗 */} Date: Sun, 28 Sep 2025 18:11:00 +0800 Subject: [PATCH 05/19] =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=B4=A0=E6=9D=90?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E6=A8=A1=E5=9D=97=EF=BC=9A=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E8=8E=B7=E5=8F=96=E9=80=BB=E8=BE=91=EF=BC=8C?= =?UTF-8?q?=E4=BD=BF=E7=94=A8useRef=E5=AD=98=E5=82=A8=E5=88=86=E9=A1=B5?= =?UTF-8?q?=E7=8A=B6=E6=80=81=EF=BC=8C=E7=AE=80=E5=8C=96fetchMaterials?= =?UTF-8?q?=E5=87=BD=E6=95=B0=EF=BC=8C=E6=8F=90=E5=8D=87=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=8F=AF=E8=AF=BB=E6=80=A7=E5=92=8C=E6=80=A7=E8=83=BD=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../management/MaterialManagement.tsx | 68 ++++++++++--------- 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/management/MaterialManagement.tsx b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/management/MaterialManagement.tsx index ac5cb140..65411fd6 100644 --- a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/management/MaterialManagement.tsx +++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/management/MaterialManagement.tsx @@ -3,7 +3,7 @@ import React, { useEffect, forwardRef, useImperativeHandle, - useCallback, + useRef, } from "react"; import { Button, Input, Card, message, Popconfirm, Pagination } from "antd"; import { @@ -56,6 +56,10 @@ const MaterialManagement = forwardRef>( total: 0, }); + // 使用 ref 来存储最新的分页状态 + const paginationRef = useRef(pagination); + paginationRef.current = pagination; + // 获取类型图标 const getTypeIcon = (type: string) => { switch (type) { @@ -71,36 +75,34 @@ const MaterialManagement = forwardRef>( }; // 获取素材列表 - const fetchMaterials = useCallback( - async (params?: MaterialListParams) => { - try { - setLoading(true); - const requestParams = { - page: pagination.current.toString(), - limit: pagination.pageSize.toString(), - ...params, - }; - const response = await getMaterialList(requestParams); - if (response) { - setMaterialsList(response.list || []); - setPagination(prev => ({ - ...prev, - total: response.total || 0, - })); - } else { - setMaterialsList([]); - message.error(response?.message || "获取素材列表失败"); - } - } catch (error) { - console.error("获取素材列表失败:", error); + const fetchMaterials = async (params?: MaterialListParams) => { + try { + setLoading(true); + const currentPagination = paginationRef.current; + const requestParams = { + page: currentPagination.current.toString(), + limit: currentPagination.pageSize.toString(), + ...params, + }; + const response = await getMaterialList(requestParams); + if (response) { + setMaterialsList(response.list || []); + setPagination(prev => ({ + ...prev, + total: response.total || 0, + })); + } else { setMaterialsList([]); - message.error("获取素材列表失败"); - } finally { - setLoading(false); + message.error(response?.message || "获取素材列表失败"); } - }, - [pagination], - ); + } catch (error) { + console.error("获取素材列表失败:", error); + setMaterialsList([]); + message.error("获取素材列表失败"); + } finally { + setLoading(false); + } + }; // 暴露方法给父组件 useImperativeHandle(ref, () => ({ @@ -142,12 +144,16 @@ const MaterialManagement = forwardRef>( current: page, pageSize: pageSize || prev.pageSize, })); + // 分页变化后立即获取数据 + setTimeout(() => { + fetchMaterials(); + }, 0); }; - // 组件挂载和分页变化时获取数据 + // 组件挂载时获取数据 useEffect(() => { fetchMaterials(); - }, [fetchMaterials]); + }, []); return (
From 340f074f01797363e2ce08cec00eabf9feb076bb 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: Sun, 28 Sep 2025 18:11:40 +0800 Subject: [PATCH 06/19] =?UTF-8?q?=E8=B0=83=E6=95=B4=E5=8F=B3=E4=BE=A7?= =?UTF-8?q?=E5=8C=BA=E5=9F=9F=E7=9A=84=E5=B8=83=E5=B1=80=E6=A0=B7=E5=BC=8F?= =?UTF-8?q?=EF=BC=9A=E7=A7=BB=E9=99=A4flex-direction=E5=B1=9E=E6=80=A7?= =?UTF-8?q?=E4=BB=A5=E7=AE=80=E5=8C=96=E6=A0=B7=E5=BC=8F=E5=AE=9A=E4=B9=89?= =?UTF-8?q?=EF=BC=8C=E6=8F=90=E5=8D=87=E4=BB=A3=E7=A0=81=E5=8F=AF=E8=AF=BB?= =?UTF-8?q?=E6=80=A7=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../content-management/components/management/index.module.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/management/index.module.scss b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/management/index.module.scss index a7624c01..52943078 100644 --- a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/management/index.module.scss +++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/management/index.module.scss @@ -93,7 +93,7 @@ .rightSection { display: flex; - flex-direction: column; + align-items: center; gap: 8px; From e38b0fde51d158dc9e3fe191d2435bafec818312 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: Sun, 28 Sep 2025 18:22:17 +0800 Subject: [PATCH 07/19] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=95=8F=E6=84=9F?= =?UTF-8?q?=E8=AF=8D=E7=AE=A1=E7=90=86=E6=A8=A1=E5=9D=97=EF=BC=9A=E5=B0=86?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E5=8F=82=E6=95=B0=E7=B1=BB=E5=9E=8B=E4=BB=8E?= =?UTF-8?q?=E5=AD=97=E7=AC=A6=E4=B8=B2=E6=94=B9=E4=B8=BA=E6=95=B0=E5=AD=97?= =?UTF-8?q?=EF=BC=8C=E4=BC=98=E5=8C=96=E7=8A=B6=E6=80=81=E5=88=87=E6=8D=A2?= =?UTF-8?q?=E9=80=BB=E8=BE=91=EF=BC=8C=E6=8F=90=E5=8D=87=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=8F=AF=E8=AF=BB=E6=80=A7=E5=92=8C=E7=B1=BB=E5=9E=8B=E5=AE=89?= =?UTF-8?q?=E5=85=A8=E6=80=A7=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/management/SensitiveWordManagement.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/management/SensitiveWordManagement.tsx b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/management/SensitiveWordManagement.tsx index ebe4471a..7979be4c 100644 --- a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/management/SensitiveWordManagement.tsx +++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/management/SensitiveWordManagement.tsx @@ -36,8 +36,7 @@ interface SensitiveWordItem { keywords: string; content: string; operation: number; - status: string; - enabled: boolean; + status: number; } const SensitiveWordManagement = forwardRef>( @@ -129,7 +128,9 @@ const SensitiveWordManagement = forwardRef>( if (response) { setSensitiveWordsList(prev => prev.map(item => - item.id === id ? { ...item, enabled: !item.enabled } : item, + item.id === id + ? { ...item, status: item.status === 1 ? 0 : 1 } + : item, ), ); message.success("状态更新成功"); @@ -223,7 +224,7 @@ const SensitiveWordManagement = forwardRef>(
handleToggleSensitiveWord(item.id)} className={styles.toggleSwitch} /> From f5480bdc58376eac572f42cac2d7df3fe67852e0 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, 29 Sep 2025 11:05:52 +0800 Subject: [PATCH 08/19] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=85=B3=E9=94=AE?= =?UTF-8?q?=E8=AF=8D=E7=AE=A1=E7=90=86=E6=A8=A1=E5=9D=97=EF=BC=9A=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E7=B4=A0=E6=9D=90=E9=80=89=E6=8B=A9=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=EF=BC=8C=E4=BC=98=E5=8C=96=E5=9B=9E=E5=A4=8D=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E5=88=87=E6=8D=A2=E9=80=BB=E8=BE=91=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 --- .../src/components/MetailSelection/api.ts | 10 + .../src/components/MetailSelection/data.ts | 27 ++ .../MetailSelection/index.module.scss | 206 ++++++++++++++ .../src/components/MetailSelection/index.tsx | 138 ++++++++++ .../MetailSelection/selectionPopup.tsx | 257 ++++++++++++++++++ .../src/components/PopuLayout/footer.tsx | 18 +- .../powerCenter/content-management/api.ts | 3 +- .../components/modals/KeywordModal.tsx | 85 ++++-- 8 files changed, 720 insertions(+), 24 deletions(-) create mode 100644 Touchkebao/src/components/MetailSelection/api.ts create mode 100644 Touchkebao/src/components/MetailSelection/data.ts create mode 100644 Touchkebao/src/components/MetailSelection/index.module.scss create mode 100644 Touchkebao/src/components/MetailSelection/index.tsx create mode 100644 Touchkebao/src/components/MetailSelection/selectionPopup.tsx diff --git a/Touchkebao/src/components/MetailSelection/api.ts b/Touchkebao/src/components/MetailSelection/api.ts new file mode 100644 index 00000000..2df6bc32 --- /dev/null +++ b/Touchkebao/src/components/MetailSelection/api.ts @@ -0,0 +1,10 @@ +import request from "@/api/request"; + +// 获取群组列表 +export function getGroupList(params: { + page: number; + limit: number; + keyword?: string; +}) { + return request("/v1/kefu/content/material/list", params, "GET"); +} diff --git a/Touchkebao/src/components/MetailSelection/data.ts b/Touchkebao/src/components/MetailSelection/data.ts new file mode 100644 index 00000000..3061247a --- /dev/null +++ b/Touchkebao/src/components/MetailSelection/data.ts @@ -0,0 +1,27 @@ +export interface GroupSelectionItem { + id: string; + title: string; + cover?: string; + status: number; + [key: string]: any; +} + +// 组件属性接口 +export interface GroupSelectionProps { + selectedOptions: GroupSelectionItem[]; + onSelect: (groups: GroupSelectionItem[]) => void; + onSelectDetail?: (groups: GroupSelectionItem[]) => void; + placeholder?: string; + className?: string; + visible?: boolean; + onVisibleChange?: (visible: boolean) => void; + selectedListMaxHeight?: number; + showInput?: boolean; + showSelectedList?: boolean; + readonly?: boolean; + selectionMode?: "multiple" | "single"; // 新增:选择模式,默认为多选 + onConfirm?: ( + selectedIds: string[], + selectedItems: GroupSelectionItem[], + ) => void; +} diff --git a/Touchkebao/src/components/MetailSelection/index.module.scss b/Touchkebao/src/components/MetailSelection/index.module.scss new file mode 100644 index 00000000..bedba3ef --- /dev/null +++ b/Touchkebao/src/components/MetailSelection/index.module.scss @@ -0,0 +1,206 @@ +.inputWrapper { + position: relative; +} +.inputIcon { + position: absolute; + left: 12px; + top: 50%; + transform: translateY(-50%); + color: #bdbdbd; + font-size: 20px; +} +.input { + padding-left: 38px !important; + height: 48px; + border-radius: 16px !important; + border: 1px solid #e5e6eb !important; + font-size: 16px; + background: #f8f9fa; +} +.selectedListRow { + padding: 8px; + border-bottom: 1px solid #f0f0f0; + font-size: 14px; +} +.selectedListRowContent { + flex: 1; + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; +} +.selectedListRowContentText { + flex: 1; +} + +.popupContainer { + display: flex; + flex-direction: column; + height: 100vh; + background: #fff; +} +.popupHeader { + padding: 24px; +} +.popupTitle { + text-align: center; + font-size: 20px; + font-weight: 600; + margin-bottom: 24px; +} +.searchWrapper { + position: relative; + margin-bottom: 16px; +} +.searchInput { + padding-left: 40px !important; + padding-top: 8px !important; + padding-bottom: 8px !important; + border-radius: 24px !important; + border: 1px solid #e5e6eb !important; + font-size: 15px; + background: #f8f9fa; +} +.searchIcon { + position: absolute; + left: 12px; + top: 50%; + transform: translateY(-50%); + color: #bdbdbd; + font-size: 16px; +} +.clearBtn { + position: absolute; + right: 8px; + top: 50%; + transform: translateY(-50%); + height: 24px; + width: 24px; + border-radius: 50%; + min-width: 24px; +} + +.groupList { + flex: 1; + overflow-y: auto; +} +.groupListInner { + border-top: 1px solid #f0f0f0; +} +.groupItem { + display: flex; + align-items: center; + padding: 16px 24px; + border-bottom: 1px solid #f0f0f0; + transition: background 0.2s; + &:hover { + background: #f5f6fa; + } +} +.groupInfo { + display: flex; + align-items: center; + gap: 12px; + flex: 1; +} +.groupAvatar { + width: 40px; + height: 40px; + border-radius: 50%; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + display: flex; + align-items: center; + justify-content: center; + color: #fff; + font-size: 14px; + font-weight: 500; + overflow: hidden; +} +.avatarImg { + width: 100%; + height: 100%; + object-fit: cover; +} +.groupDetail { + flex: 1; +} +.groupName { + font-weight: 500; + font-size: 16px; + color: #222; + margin-bottom: 2px; +} +.groupId { + font-size: 13px; + color: #888; + margin-bottom: 2px; +} +.groupOwner { + font-size: 13px; + color: #bdbdbd; +} + +.loadingBox { + display: flex; + align-items: center; + justify-content: center; + height: 100%; +} +.loadingText { + color: #888; + font-size: 15px; +} +.emptyBox { + display: flex; + align-items: center; + justify-content: center; + height: 100%; +} +.emptyText { + color: #888; + font-size: 15px; +} + +.paginationRow { + border-top: 1px solid #f0f0f0; + padding: 16px; + display: flex; + align-items: center; + justify-content: space-between; + background: #fff; +} +.totalCount { + font-size: 14px; + color: #888; +} +.paginationControls { + display: flex; + align-items: center; + gap: 8px; +} +.pageBtn { + padding: 0 8px; + height: 32px; + min-width: 32px; +} +.pageInfo { + font-size: 14px; + color: #222; +} + +.popupFooter { + display: flex; + align-items: center; + justify-content: space-between; + padding: 16px; + border-top: 1px solid #f0f0f0; + background: #fff; +} +.selectedCount { + font-size: 14px; + color: #888; +} +.footerBtnGroup { + display: flex; + gap: 12px; +} diff --git a/Touchkebao/src/components/MetailSelection/index.tsx b/Touchkebao/src/components/MetailSelection/index.tsx new file mode 100644 index 00000000..f7600027 --- /dev/null +++ b/Touchkebao/src/components/MetailSelection/index.tsx @@ -0,0 +1,138 @@ +import React, { useState } from "react"; +import { SearchOutlined, DeleteOutlined } from "@ant-design/icons"; +import { Button, Input } from "antd"; +import { Avatar } from "antd-mobile"; +import style from "./index.module.scss"; +import SelectionPopup from "./selectionPopup"; +import { GroupSelectionProps } from "./data"; +export default function GroupSelection({ + selectedOptions, + onSelect, + onSelectDetail, + placeholder = "选择素材", + className = "", + visible, + onVisibleChange, + selectedListMaxHeight = 300, + showInput = true, + showSelectedList = true, + readonly = false, + selectionMode = "single", // 默认为多选模式 + onConfirm, +}: GroupSelectionProps) { + const [popupVisible, setPopupVisible] = useState(false); + + // 删除已选素材 + const handleRemoveGroup = (id: string) => { + if (readonly) return; + onSelect(selectedOptions.filter(g => g.id !== id)); + }; + + // 受控弹窗逻辑 + const realVisible = visible !== undefined ? visible : popupVisible; + const setRealVisible = (v: boolean) => { + if (onVisibleChange) onVisibleChange(v); + if (visible === undefined) setPopupVisible(v); + }; + + // 打开弹窗 + const openPopup = () => { + if (readonly) return; + setRealVisible(true); + }; + + // 清空已选择的素材 + const handleClear = () => { + if (readonly) return; + onSelect([]); + }; + + // 获取显示文本 + const getDisplayText = () => { + if (selectedOptions.length === 0) return ""; + if (selectionMode === "single") { + return selectedOptions[0]?.title || "已选择素材"; + } + return `已选择 ${selectedOptions.length} 个素材`; + }; + + return ( + <> + {/* 输入框 */} + {showInput && ( +
+ } + allowClear={!readonly && selectedOptions.length > 0} + onClear={handleClear} + size="large" + readOnly={readonly} + disabled={readonly} + style={ + readonly ? { background: "#f5f5f5", cursor: "not-allowed" } : {} + } + /> +
+ )} + {/* 已选素材列表窗口 */} + {showSelectedList && selectedOptions.length > 0 && ( +
+ {selectedOptions.map(group => ( +
+
+ +
+
{group.title}
+
ID: {group.id}
+
+ {!readonly && ( +
+
+ ))} +
+ )} + {/* 弹窗 */} + + + ); +} diff --git a/Touchkebao/src/components/MetailSelection/selectionPopup.tsx b/Touchkebao/src/components/MetailSelection/selectionPopup.tsx new file mode 100644 index 00000000..e318faf0 --- /dev/null +++ b/Touchkebao/src/components/MetailSelection/selectionPopup.tsx @@ -0,0 +1,257 @@ +import React, { useState, useEffect } from "react"; +import { Popup, Checkbox, Radio } from "antd-mobile"; + +import { getGroupList } from "./api"; +import style from "./index.module.scss"; +import Layout from "@/components/Layout/Layout"; +import PopupHeader from "@/components/PopuLayout/header"; +import PopupFooter from "@/components/PopuLayout/footer"; +import { GroupSelectionItem } from "./data"; + +// 弹窗属性接口 +interface SelectionPopupProps { + visible: boolean; + onVisibleChange: (visible: boolean) => void; + selectedOptions: GroupSelectionItem[]; + onSelect: (groups: GroupSelectionItem[]) => void; + onSelectDetail?: (groups: GroupSelectionItem[]) => void; + readonly?: boolean; + selectionMode?: "multiple" | "single"; // 新增:选择模式,默认为多选 + onConfirm?: ( + selectedIds: string[], + selectedItems: GroupSelectionItem[], + ) => void; +} + +export default function SelectionPopup({ + visible, + onVisibleChange, + selectedOptions, + onSelect, + onSelectDetail, + readonly = false, + selectionMode = "multiple", // 默认为多选模式 + onConfirm, +}: SelectionPopupProps) { + const [groups, setGroups] = useState([]); + const [searchQuery, setSearchQuery] = useState(""); + const [currentPage, setCurrentPage] = useState(1); + const [totalPages, setTotalPages] = useState(1); + const [totalGroups, setTotalGroups] = useState(0); + const [loading, setLoading] = useState(false); + const [tempSelectedOptions, setTempSelectedOptions] = useState< + GroupSelectionItem[] + >([]); + + // 获取素材列表API + const fetchGroups = async (page: number, keyword: string = "") => { + setLoading(true); + try { + const params: any = { + page, + limit: 20, + }; + + if (keyword.trim()) { + params.keyword = keyword.trim(); + } + + const response = await getGroupList(params); + if (response && response.list) { + setGroups(response.list); + setTotalGroups(response.total || 0); + setTotalPages(Math.ceil((response.total || 0) / 20)); + } + } catch (error) { + console.error("获取素材列表失败:", error); + } finally { + setLoading(false); + } + }; + + // 处理素材选择 + const handleGroupToggle = (group: GroupSelectionItem) => { + if (readonly) return; + + if (selectionMode === "single") { + // 单选模式:直接设置为当前选中的项 + setTempSelectedOptions([group]); + } else { + // 多选模式:切换选中状态 + const newSelectedGroups = tempSelectedOptions.some(g => g.id === group.id) + ? tempSelectedOptions.filter(g => g.id !== group.id) + : tempSelectedOptions.concat(group); + + setTempSelectedOptions(newSelectedGroups); + } + }; + + // 全选当前页(仅在多选模式下有效) + const handleSelectAllCurrentPage = (checked: boolean) => { + if (readonly || selectionMode === "single") return; + + if (checked) { + // 全选:添加当前页面所有未选中的素材 + const currentPageGroups = groups.filter( + group => !tempSelectedOptions.some(g => g.id === group.id), + ); + setTempSelectedOptions(prev => [...prev, ...currentPageGroups]); + } else { + // 取消全选:移除当前页面的所有素材 + const currentPageGroupIds = groups.map(g => g.id); + setTempSelectedOptions(prev => + prev.filter(g => !currentPageGroupIds.includes(g.id)), + ); + } + }; + + // 检查当前页是否全选(仅在多选模式下有效) + const isCurrentPageAllSelected = + selectionMode === "multiple" && + groups.length > 0 && + groups.every(group => tempSelectedOptions.some(g => g.id === group.id)); + + // 确认选择 + const handleConfirm = () => { + // 用户点击确认时,才更新实际的selectedOptions + onSelect(tempSelectedOptions); + + // 如果有 onSelectDetail 回调,传递完整的素材对象 + if (onSelectDetail) { + const selectedGroupObjs = groups.filter(group => + tempSelectedOptions.some(g => g.id === group.id), + ); + onSelectDetail(selectedGroupObjs); + } + + if (onConfirm) { + onConfirm( + tempSelectedOptions.map(g => g.id), + tempSelectedOptions, + ); + } + onVisibleChange(false); + }; + + // 弹窗打开时初始化数据(只执行一次) + useEffect(() => { + if (visible) { + setCurrentPage(1); + setSearchQuery(""); + // 复制一份selectedOptions到临时变量 + setTempSelectedOptions([...selectedOptions]); + fetchGroups(1, ""); + } + }, [visible]); + + // 搜索防抖(只在弹窗打开且搜索词变化时执行) + useEffect(() => { + if (!visible || searchQuery === "") return; + + const timer = setTimeout(() => { + setCurrentPage(1); + fetchGroups(1, searchQuery); + }, 500); + + return () => clearTimeout(timer); + }, [searchQuery, visible]); + + // 页码变化时请求数据(只在弹窗打开且页码不是1时执行) + useEffect(() => { + if (!visible) return; + fetchGroups(currentPage, searchQuery); + }, [currentPage, visible, searchQuery]); + + return ( + onVisibleChange(false)} + position="bottom" + bodyStyle={{ height: "100vh" }} + > + fetchGroups(currentPage, searchQuery)} + /> + } + footer={ + onVisibleChange(false)} + onConfirm={handleConfirm} + isAllSelected={isCurrentPageAllSelected} + onSelectAll={handleSelectAllCurrentPage} + showSelectAll={selectionMode === "multiple"} // 只在多选模式下显示全选功能 + /> + } + > +
+ {loading ? ( +
+
加载中...
+
+ ) : groups.length > 0 ? ( +
+ {groups.map(group => ( +
+ {selectionMode === "single" ? ( + g.id === group.id)} + onChange={() => !readonly && handleGroupToggle(group)} + disabled={readonly} + style={{ marginRight: 12 }} + /> + ) : ( + g.id === group.id)} + onChange={() => !readonly && handleGroupToggle(group)} + disabled={readonly} + style={{ marginRight: 12 }} + /> + )} +
+
+ {group.cover ? ( + {group.title} + ) : ( + group.title.charAt(0) + )} +
+
+
{group.title}
+
+ 创建人: {group.userName} +
+
+
+
+ ))} +
+ ) : ( +
+
+ {searchQuery + ? `没有找到包含"${searchQuery}"的素材` + : "没有找到素材"} +
+
+ )} +
+
+
+ ); +} diff --git a/Touchkebao/src/components/PopuLayout/footer.tsx b/Touchkebao/src/components/PopuLayout/footer.tsx index 60e562be..a4dbe0cd 100644 --- a/Touchkebao/src/components/PopuLayout/footer.tsx +++ b/Touchkebao/src/components/PopuLayout/footer.tsx @@ -14,6 +14,7 @@ interface PopupFooterProps { // 全选功能相关 isAllSelected?: boolean; onSelectAll?: (checked: boolean) => void; + showSelectAll?: boolean; // 新增:控制全选功能显示,默认为true } const PopupFooter: React.FC = ({ @@ -26,19 +27,22 @@ const PopupFooter: React.FC = ({ onConfirm, isAllSelected = false, onSelectAll, + showSelectAll = true, // 默认为true,显示全选功能 }) => { return ( <> {/* 分页栏 */}
- onSelectAll(e.target.checked)} - className={style.selectAllCheckbox} - > - 全选当前页 - + {showSelectAll && ( + onSelectAll?.(e.target.checked)} + className={style.selectAllCheckbox} + > + 全选当前页 + + )}