diff --git a/Touchkebao/src/components/Upload/AudioUpload/index.module.scss b/Touchkebao/src/components/Upload/AudioUpload/index.module.scss new file mode 100644 index 00000000..c8dd27df --- /dev/null +++ b/Touchkebao/src/components/Upload/AudioUpload/index.module.scss @@ -0,0 +1,243 @@ +.videoUploadContainer { + width: 100%; + + // 覆盖 antd Upload 组件的默认样式 + :global { + .ant-upload { + width: 100%; + } + + .ant-upload-list { + width: 100%; + } + + .ant-upload-list-text { + width: 100%; + } + + .ant-upload-list-text .ant-upload-list-item { + width: 100%; + } + } + + .videoUploadButton { + width: 100%; + aspect-ratio: 16 / 9; + min-height: clamp(90px, 20vw, 180px); + border: 2px dashed #d9d9d9; + border-radius: 12px; + background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%); + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: all 0.3s ease; + position: relative; + overflow: hidden; + + &:hover { + border-color: #1890ff; + background: linear-gradient(135deg, #f0f8ff 0%, #e6f7ff 100%); + transform: translateY(-2px); + box-shadow: 0 8px 25px rgba(24, 144, 255, 0.15); + } + + &:active { + transform: translateY(0); + } + + .uploadingContainer { + display: flex; + flex-direction: column; + align-items: center; + gap: 12px; + width: 100%; + padding: 20px; + + .uploadingIcon { + font-size: clamp(24px, 4vw, 32px); + color: #1890ff; + animation: pulse 2s infinite; + } + + .uploadingText { + font-size: clamp(11px, 2vw, 14px); + color: #666; + font-weight: 500; + } + + .uploadProgress { + width: 100%; + max-width: 200px; + } + } + + .uploadContent { + display: flex; + flex-direction: column; + align-items: center; + gap: 12px; + padding: 20px; + text-align: center; + + .uploadIcon { + font-size: clamp(50px, 6vw, 48px); + color: #1890ff; + transition: all 0.3s ease; + } + + .uploadText { + .uploadTitle { + font-size: clamp(14px, 2.5vw, 16px); + font-weight: 600; + color: #333; + margin-bottom: 4px; + } + + .uploadSubtitle { + font-size: clamp(10px, 1.5vw, 14px); + color: #666; + line-height: 1.4; + } + } + + &:hover .uploadIcon { + transform: scale(1.1); + color: #40a9ff; + } + } + } + + .videoItem { + width: 100%; + background: #fff; + border: 1px solid #f0f0f0; + border-radius: 8px; + padding: 12px; + margin-bottom: 8px; + transition: all 0.3s ease; + + &:hover { + border-color: #1890ff; + box-shadow: 0 4px 12px rgba(24, 144, 255, 0.1); + } + + .videoItemContent { + display: flex; + align-items: center; + gap: 12px; + + .videoIcon { + width: clamp(28px, 5vw, 40px); + height: clamp(28px, 5vw, 40px); + background: linear-gradient(135deg, #1890ff 0%, #40a9ff 100%); + border-radius: 8px; + display: flex; + align-items: center; + justify-content: center; + color: white; + font-size: clamp(14px, 2.5vw, 18px); + flex-shrink: 0; + } + + .videoInfo { + flex: 1; + min-width: 0; + + .videoName { + font-size: clamp(11px, 2vw, 14px); + font-weight: 500; + color: #333; + margin-bottom: 4px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .videoSize { + font-size: clamp(10px, 1.5vw, 12px); + color: #666; + } + } + + .videoActions { + display: flex; + gap: 4px; + flex-shrink: 0; + + .previewBtn, + .deleteBtn { + padding: 4px 8px; + border-radius: 6px; + transition: all 0.3s ease; + + &:hover { + background: #f5f5f5; + } + } + + .previewBtn { + color: #1890ff; + + &:hover { + color: #40a9ff; + background: #e6f7ff; + } + } + + .deleteBtn { + color: #ff4d4f; + + &:hover { + color: #ff7875; + background: #fff2f0; + } + } + } + } + + .itemProgress { + margin-top: 8px; + } + } + + .videoPreview { + display: flex; + justify-content: center; + align-items: center; + background: #000; + border-radius: 8px; + overflow: hidden; + + video { + border-radius: 8px; + } + } +} + +// 禁用状态 +.videoUploadContainer.disabled { + opacity: 0.6; + pointer-events: none; +} + +// 错误状态 +.videoUploadContainer.error { + .videoUploadButton { + border-color: #ff4d4f; + background: #fff2f0; + } +} + +// 动画效果 +@keyframes pulse { + 0% { + transform: scale(1); + } + 50% { + transform: scale(1.1); + } + 100% { + transform: scale(1); + } +} \ No newline at end of file diff --git a/Touchkebao/src/components/Upload/AudioUpload/index.tsx b/Touchkebao/src/components/Upload/AudioUpload/index.tsx new file mode 100644 index 00000000..68e82657 --- /dev/null +++ b/Touchkebao/src/components/Upload/AudioUpload/index.tsx @@ -0,0 +1,385 @@ +import React, { useState } from "react"; +import { Upload, message, Progress, Button, Modal } from "antd"; +import { + LoadingOutlined, + SoundOutlined, + DeleteOutlined, + EyeOutlined, + FileOutlined, + CloudUploadOutlined, +} from "@ant-design/icons"; +import type { UploadProps, UploadFile } from "antd/es/upload/interface"; +import style from "./index.module.scss"; + +interface AudioUploadProps { + value?: string | string[]; // 支持单个字符串或字符串数组 + onChange?: (url: string | string[]) => void; // 支持单个字符串或字符串数组 + disabled?: boolean; + className?: string; + maxSize?: number; // 最大文件大小(MB) + showPreview?: boolean; // 是否显示预览 + maxCount?: number; // 最大上传数量,默认为1 +} + +const AudioUpload: React.FC = ({ + value = "", + onChange, + disabled = false, + className, + maxSize = 50, + showPreview = true, + maxCount = 1, +}) => { + const [loading, setLoading] = useState(false); + const [fileList, setFileList] = useState([]); + const [uploadProgress, setUploadProgress] = useState(0); + const [previewVisible, setPreviewVisible] = useState(false); + const [previewUrl, setPreviewUrl] = useState(""); + + React.useEffect(() => { + if (value) { + // 处理单个字符串或字符串数组 + const urls = Array.isArray(value) ? value : [value]; + const files: UploadFile[] = urls.map((url, index) => ({ + uid: `file-${index}`, + name: `audio-${index + 1}`, + status: "done", + url: url || "", + })); + setFileList(files); + } else { + setFileList([]); + } + }, [value]); + + // 文件验证 + const beforeUpload = (file: File) => { + const isAudio = file.type.startsWith("audio/"); + if (!isAudio) { + message.error("只能上传音频文件!"); + return false; + } + + const isLtMaxSize = file.size / 1024 / 1024 < maxSize; + if (!isLtMaxSize) { + message.error(`音频大小不能超过${maxSize}MB!`); + return false; + } + + return true; + }; + + // 处理文件变化 + const handleChange: UploadProps["onChange"] = info => { + // 更新 fileList,确保所有 URL 都是字符串 + const updatedFileList = info.fileList.map(file => { + let url = ""; + + if (file.url) { + url = file.url; + } else if (file.response) { + // 处理响应对象 + if (typeof file.response === "string") { + url = file.response; + } else if (file.response.data) { + url = + typeof file.response.data === "string" + ? file.response.data + : file.response.data.url || ""; + } else if (file.response.url) { + url = file.response.url; + } + } + + return { + ...file, + url: url, + }; + }); + + setFileList(updatedFileList); + + // 处理上传状态 + if (info.file.status === "uploading") { + setLoading(true); + // 模拟上传进度 + const progress = Math.min(99, Math.random() * 100); + setUploadProgress(progress); + } else if (info.file.status === "done") { + setLoading(false); + setUploadProgress(100); + + // 从响应中获取上传后的URL + let uploadedUrl = ""; + + if (info.file.response) { + // 检查响应是否成功 + if (info.file.response.code && info.file.response.code !== 200) { + message.error(info.file.response.message || "上传失败"); + return; + } + + if (typeof info.file.response === "string") { + uploadedUrl = info.file.response; + } else if (info.file.response.data) { + uploadedUrl = + typeof info.file.response.data === "string" + ? info.file.response.data + : info.file.response.data.url || ""; + } else if (info.file.response.url) { + uploadedUrl = info.file.response.url; + } + } + + if (uploadedUrl) { + message.success("音频上传成功!"); + if (maxCount === 1) { + // 单个音频模式 + onChange?.(uploadedUrl); + } else { + // 多个音频模式 + const currentUrls = Array.isArray(value) + ? value + : value + ? [value] + : []; + const newUrls = [...currentUrls, uploadedUrl]; + onChange?.(newUrls); + } + } else { + message.error("上传失败,未获取到音频URL"); + } + } else if (info.file.status === "error") { + setLoading(false); + setUploadProgress(0); + message.error("上传失败,请重试"); + } else if (info.file.status === "removed") { + if (maxCount === 1) { + onChange?.(""); + } else { + // 多个音频模式,移除对应的音频 + const currentUrls = Array.isArray(value) ? value : value ? [value] : []; + const removedIndex = info.fileList.findIndex( + f => f.uid === info.file.uid, + ); + if (removedIndex !== -1) { + const newUrls = currentUrls.filter( + (_, index) => index !== removedIndex, + ); + onChange?.(newUrls); + } + } + } + }; + + // 删除文件 + const handleRemove = (file?: UploadFile) => { + Modal.confirm({ + title: "确认删除", + content: "确定要删除这个音频文件吗?", + okText: "确定", + cancelText: "取消", + onOk: () => { + if (maxCount === 1) { + setFileList([]); + onChange?.(""); + } else if (file) { + // 多个音频模式,删除指定音频 + const currentUrls = Array.isArray(value) + ? value + : value + ? [value] + : []; + const fileIndex = fileList.findIndex(f => f.uid === file.uid); + if (fileIndex !== -1) { + const newUrls = currentUrls.filter( + (_, index) => index !== fileIndex, + ); + onChange?.(newUrls); + } + } + message.success("音频已删除"); + }, + }); + return true; + }; + + // 预览音频 + const handlePreview = (url: string) => { + setPreviewUrl(url); + setPreviewVisible(true); + }; + + // 获取文件大小显示 + const formatFileSize = (bytes: number) => { + if (bytes === 0) return "0 B"; + const k = 1024; + const sizes = ["B", "KB", "MB", "GB"]; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]; + }; + + // 自定义上传按钮 + const uploadButton = ( +
+ {loading ? ( +
+
+ +
+
上传中...
+ +
+ ) : ( +
+
+ +
+
+
+ {maxCount === 1 + ? "上传音频" + : `上传音频 (${fileList.length}/${maxCount})`} +
+
+ 支持 MP3、WAV、AAC 等格式,最大 {maxSize}MB + {maxCount > 1 && `,最多上传 ${maxCount} 个音频`} +
+
+
+ )} +
+ ); + + // 自定义文件列表项 + const customItemRender = ( + originNode: React.ReactElement, + file: UploadFile, + ) => { + if (file.status === "uploading") { + return ( +
+
+
+ +
+
+
{file.name}
+
+ {file.size ? formatFileSize(file.size) : "计算中..."} +
+
+
+
+
+ +
+ ); + } + + if (file.status === "done") { + return ( +
+
+
+ +
+
+
{file.name}
+
+ {file.size ? formatFileSize(file.size) : "未知大小"} +
+
+
+ {showPreview && ( +
+
+
+ ); + } + + return originNode; + }; + + const action = import.meta.env.VITE_API_BASE_URL + "/v1/attachment/upload"; + + return ( +
+ 1} + fileList={fileList} + accept="audio/*" + listType="text" + showUploadList={{ + showPreviewIcon: false, + showRemoveIcon: false, + showDownloadIcon: false, + }} + disabled={disabled || loading} + beforeUpload={beforeUpload} + onChange={handleChange} + onRemove={handleRemove} + maxCount={maxCount} + itemRender={customItemRender} + > + {fileList.length >= maxCount ? null : uploadButton} + + + {/* 音频预览模态框 */} + setPreviewVisible(false)} + footer={null} + width={600} + centered + > +
+ +
+
+
+ ); +}; + +export default AudioUpload; diff --git a/Touchkebao/src/components/Upload/FileUpload/index.tsx b/Touchkebao/src/components/Upload/FileUpload/index.tsx index 9edf1c4f..c70b19db 100644 --- a/Touchkebao/src/components/Upload/FileUpload/index.tsx +++ b/Touchkebao/src/components/Upload/FileUpload/index.tsx @@ -176,12 +176,17 @@ const FileUpload: React.FC = ({ } else if (info.file.status === "done") { setLoading(false); setUploadProgress(100); - message.success("文件上传成功!"); // 从响应中获取上传后的URL let uploadedUrl = ""; if (info.file.response) { + // 检查响应是否成功 + if (info.file.response.code && info.file.response.code !== 200) { + message.error(info.file.response.message || "上传失败"); + return; + } + if (typeof info.file.response === "string") { uploadedUrl = info.file.response; } else if (info.file.response.data) { @@ -195,6 +200,7 @@ const FileUpload: React.FC = ({ } if (uploadedUrl) { + message.success("文件上传成功!"); if (maxCount === 1) { // 单个文件模式 onChange?.(uploadedUrl); @@ -208,6 +214,8 @@ const FileUpload: React.FC = ({ const newUrls = [...currentUrls, uploadedUrl]; onChange?.(newUrls); } + } else { + message.error("上传失败,未获取到文件URL"); } } else if (info.file.status === "error") { setLoading(false); diff --git a/Touchkebao/src/components/Upload/VideoUpload/index.tsx b/Touchkebao/src/components/Upload/VideoUpload/index.tsx index 7775eacd..7017e327 100644 --- a/Touchkebao/src/components/Upload/VideoUpload/index.tsx +++ b/Touchkebao/src/components/Upload/VideoUpload/index.tsx @@ -108,12 +108,17 @@ const VideoUpload: React.FC = ({ } else if (info.file.status === "done") { setLoading(false); setUploadProgress(100); - message.success("视频上传成功!"); // 从响应中获取上传后的URL let uploadedUrl = ""; if (info.file.response) { + // 检查响应是否成功 + if (info.file.response.code && info.file.response.code !== 200) { + message.error(info.file.response.message || "上传失败"); + return; + } + if (typeof info.file.response === "string") { uploadedUrl = info.file.response; } else if (info.file.response.data) { @@ -127,6 +132,7 @@ const VideoUpload: React.FC = ({ } if (uploadedUrl) { + message.success("视频上传成功!"); if (maxCount === 1) { // 单个视频模式 onChange?.(uploadedUrl); @@ -140,6 +146,8 @@ const VideoUpload: React.FC = ({ const newUrls = [...currentUrls, uploadedUrl]; onChange?.(newUrls); } + } else { + message.error("上传失败,未获取到视频URL"); } } else if (info.file.status === "error") { setLoading(false); 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 7557e635..62d156ce 100644 --- a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/api.ts +++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/api.ts @@ -15,7 +15,9 @@ export interface ContentItem { // 链接数据类型 export interface LinkData { + title: string; url: string; + cover: string; } export interface MaterialAddRequest { @@ -50,7 +52,7 @@ export function getMaterialDetails(id: string) { // 素材管理-删除 export function deleteMaterial(id: string) { - return request("/v1/kefu/content/material/del", { id }, "GET"); + return request("/v1/kefu/content/material/del", { id }, "DELETE"); } // 素材管理-更新 @@ -106,7 +108,7 @@ export function getSensitiveWordDetails(id: string) { // 违禁词管理-删除 export function deleteSensitiveWord(id: string) { - return request("/v1/kefu/content/sensitiveWord/del", { id }, "GET"); + return request("/v1/kefu/content/sensitiveWord/del", { id }, "DELETE"); } // 违禁词管理-更新 @@ -161,7 +163,7 @@ export function getKeywordDetails(id: string) { // 关键词回复-删除 export function deleteKeyword(id: string) { - return request("/v1/kefu/content/keywords/del", { id }, "GET"); + return request("/v1/kefu/content/keywords/del", { id }, "DELETE"); } // 关键词回复-更新 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 712ddd3a..5680d28f 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, Switch, Tag } from "antd"; +import { Button, Input, Card, message, Modal } from "antd"; import { SearchOutlined, FilterOutlined, @@ -12,12 +12,12 @@ import { FileTextOutlined, FileImageOutlined, PlayCircleOutlined, + DeleteOutlined, } from "@ant-design/icons"; import styles from "../../index.module.scss"; import { getMaterialList, deleteMaterial, - setMaterialStatus, type MaterialListParams, } from "../../api"; import MaterialModal from "../modals/MaterialModal"; @@ -40,232 +40,234 @@ interface MaterialItem { userName: string; } -const MaterialManagement = forwardRef((props, ref) => { - const [searchValue, setSearchValue] = useState(""); - const [materialsList, setMaterialsList] = useState([]); - const [loading, setLoading] = useState(false); - const [editModalVisible, setEditModalVisible] = useState(false); - const [editingMaterialId, setEditingMaterialId] = useState( - null, - ); +const MaterialManagement = forwardRef>( + (props, ref) => { + const [searchValue, setSearchValue] = useState(""); + const [materialsList, setMaterialsList] = useState([]); + const [loading, setLoading] = useState(false); + const [editModalVisible, setEditModalVisible] = useState(false); + const [editingMaterialId, setEditingMaterialId] = useState( + null, + ); - // 获取类型图标 - const getTypeIcon = (type: string) => { - switch (type) { - case "文本": - return ; - case "图片": - return ; - case "视频": - return ; - default: - return ; - } - }; + // 获取类型图标 + const getTypeIcon = (type: string) => { + switch (type) { + case "文本": + return ; + case "图片": + return ; + case "视频": + return ; + default: + return ; + } + }; - // 获取类型颜色 - const getTypeColor = (type: string) => { - switch (type) { - case "文本": - return "#fa8c16"; - case "图片": - return "#52c41a"; - case "视频": - return "#1890ff"; - default: - return "#8c8c8c"; - } - }; - - // 获取素材列表 - const fetchMaterials = async (params?: MaterialListParams) => { - try { - setLoading(true); - const response = await getMaterialList(params || {}); - if (response) { - setMaterialsList(response.list || []); - } else { + // 获取素材列表 + const fetchMaterials = async (params?: MaterialListParams) => { + try { + setLoading(true); + const response = await getMaterialList(params || {}); + if (response) { + setMaterialsList(response.list || []); + } 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); - } - }; + }; - // 暴露方法给父组件 - useImperativeHandle(ref, () => ({ - fetchMaterials, - })); + // 暴露方法给父组件 + useImperativeHandle(ref, () => ({ + fetchMaterials, + })); - // 素材管理相关函数 - const handleDeleteMaterial = async (id: number) => { - try { - const response = await deleteMaterial(id.toString()); - if (response) { - setMaterialsList(prev => prev.filter(item => item.id !== id)); - message.success("删除成功"); - } else { - message.error(response?.message || "删除失败"); - } - } catch (error) { - console.error("删除失败:", error); - message.error("删除失败"); - } - }; + // 素材管理相关函数 + 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("删除失败"); + } + }, + }); + }; - // 切换素材状态 - const handleToggleMaterialStatus = async (id: number, checked: boolean) => { - try { - const response = await setMaterialStatus({ id: id.toString() }); - if (response) { - setMaterialsList(prev => - prev.map(item => - item.id === id ? { ...item, status: checked ? 1 : 0 } : item, - ), - ); - message.success(checked ? "启用成功" : "禁用成功"); - } else { - message.error(response?.message || "状态更新失败"); - } - } catch (error) { - console.error("状态更新失败:", error); - message.error("状态更新失败"); - } - }; + // 编辑素材 + const handleEditMaterial = (id: number) => { + setEditingMaterialId(id); + setEditModalVisible(true); + }; - // 编辑素材 - const handleEditMaterial = (id: number) => { - setEditingMaterialId(id); - setEditModalVisible(true); - }; + // 编辑弹窗成功回调 + const handleEditSuccess = () => { + fetchMaterials(); // 重新获取数据 + }; - // 编辑弹窗成功回调 - const handleEditSuccess = () => { - fetchMaterials(); // 重新获取数据 - }; + // 搜索处理函数 + const handleSearch = (value: string) => { + fetchMaterials({ keyword: value }); + }; - // 搜索处理函数 - const handleSearch = (value: string) => { - fetchMaterials({ keyword: value }); - }; + // 组件挂载时获取数据 + useEffect(() => { + fetchMaterials(); + }, []); - // 组件挂载时获取数据 - useEffect(() => { - fetchMaterials(); - }, []); + return ( +
+
+ setSearchValue(e.target.value)} + onSearch={handleSearch} + style={{ width: 300 }} + prefix={} + /> + +
- return ( -
-
- setSearchValue(e.target.value)} - onSearch={handleSearch} - style={{ width: 300 }} - prefix={} - /> - -
- -
- {loading ? ( -
加载中...
- ) : materialsList.length === 0 ? ( -
暂无素材数据
- ) : ( - materialsList.map(item => ( - handleEditMaterial(item.id)} - > -
- {item.cover ? ( -
+ {loading ? ( +
加载中...
+ ) : materialsList.length === 0 ? ( +
暂无素材数据
+ ) : ( + materialsList.map(item => ( + } + onClick={e => { + e.stopPropagation(); + handleEditMaterial(item.id); }} > - {item.title}, + , + ]} + > +
handleEditMaterial(item.id)} + style={{ cursor: "pointer" }} + > + {item.cover ? ( +
+ {item.title} +
+ {item.type} +
+
+ ) : ( +
-
- {item.type} + {getTypeIcon(item.type)} + + {item.type} +
-
- ) : ( -
- {getTypeIcon(item.type)} - - {item.type} - -
- )} -
- -
-
{item.title}
-
-
创建人: {item.userName}
-
{item.createTime}
+ )}
-
-
- )) - )} -
- {/* 编辑弹窗 */} - { - setEditModalVisible(false); - setEditingMaterialId(null); - }} - onSuccess={handleEditSuccess} - /> -
- ); -}); +
+
{item.title}
+
+
创建人: {item.userName}
+
{item.createTime}
+
+
+
+ )) + )} +
+ + {/* 编辑弹窗 */} + { + setEditModalVisible(false); + setEditingMaterialId(null); + }} + onSuccess={handleEditSuccess} + /> +
+ ); + }, +); + +MaterialManagement.displayName = "MaterialManagement"; export default MaterialManagement; diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/modals/ContentManager.tsx b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/modals/ContentManager.tsx index d471926f..915f2de6 100644 --- a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/modals/ContentManager.tsx +++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/modals/ContentManager.tsx @@ -13,7 +13,8 @@ import { import ImageUpload from "@/components/Upload/ImageUpload/ImageUpload"; import VideoUpload from "@/components/Upload/VideoUpload"; import FileUpload from "@/components/Upload/FileUpload"; -import type { ContentItem } from "../../api"; +import AudioUpload from "@/components/Upload/AudioUpload"; +import type { ContentItem, LinkData } from "../../api"; const { TextArea } = Input; const { Option } = Select; @@ -93,7 +94,7 @@ const ContentManager: React.FC = ({ // 根据新类型重置数据 let newData: any; if (newType === "link") { - newData = ""; + newData = { title: "", url: "", cover: "" }; } else { newData = ""; } @@ -181,7 +182,10 @@ const ContentManager: React.FC = ({ return ( updateItemData(index, url)} + onChange={url => { + const videoUrl = Array.isArray(url) ? url[0] || "" : url || ""; + updateItemData(index, videoUrl); + }} maxSize={50} showPreview={true} /> @@ -190,7 +194,10 @@ const ContentManager: React.FC = ({ return ( updateItemData(index, url)} + onChange={url => { + const fileUrl = Array.isArray(url) ? url[0] || "" : url || ""; + updateItemData(index, fileUrl); + }} maxSize={10} showPreview={true} acceptTypes={["excel", "word", "ppt", "pdf", "txt"]} @@ -198,23 +205,48 @@ const ContentManager: React.FC = ({ ); case "audio": return ( - updateItemData(index, url)} + onChange={url => { + const audioUrl = Array.isArray(url) ? url[0] || "" : url || ""; + updateItemData(index, audioUrl); + }} maxSize={50} showPreview={true} - acceptTypes={["mp3", "wav", "aac", "m4a", "ogg"]} /> ); case "link": { + const linkData = (item.data as LinkData) || { + title: "", + url: "", + cover: "", + }; return (
updateItemData(index, e.target.value)} + value={linkData.title} + onChange={e => + updateItemData(index, { ...linkData, title: e.target.value }) + } + placeholder="链接标题" + style={{ marginBottom: 8 }} + /> + + updateItemData(index, { ...linkData, url: e.target.value }) + } placeholder="链接URL" style={{ marginBottom: 8 }} /> + + updateItemData(index, { ...linkData, cover: urls[0] || "" }) + } + />
); } diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/modals/MaterialModal.tsx b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/modals/MaterialModal.tsx index 9a3f6bea..7009b0c8 100644 --- a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/modals/MaterialModal.tsx +++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/components/modals/MaterialModal.tsx @@ -55,14 +55,17 @@ const MaterialModal: React.FC = ({ [form], ); - // 当弹窗打开且为编辑模式时,获取详情 + // 当弹窗打开时处理数据 useEffect(() => { - if (visible && mode === "edit" && materialId) { - fetchMaterialDetails(materialId); - } else if (visible && mode === "add") { - // 添加模式时重置表单 - form.resetFields(); - setContentItems([]); + if (visible) { + if (mode === "edit" && materialId) { + // 编辑模式:获取详情 + fetchMaterialDetails(materialId); + } else if (mode === "add") { + // 添加模式:重置表单 + form.resetFields(); + setContentItems([]); + } } }, [visible, mode, materialId, fetchMaterialDetails, form]);