diff --git a/src/factory/feedback/FeedbackModal.jsx b/src/factory/feedback/FeedbackModal.jsx new file mode 100644 index 00000000..5456ca99 --- /dev/null +++ b/src/factory/feedback/FeedbackModal.jsx @@ -0,0 +1,242 @@ +import React, { useState, useCallback } from 'react'; +import './FeedbackModal.scss'; +import { createMemo } from '../../forums/api'; +import { message, Upload } from 'antd'; +import { httpUrl } from '../../forums/fetch'; + +// 导入图片资源 +import modalBg from '../../images/factory/feedback/modal-bg.webp'; +import iconClose from '../../images/factory/feedback/icon-close-new.webp'; +import iconRequired from '../../images/factory/feedback/icon-required-new.webp'; + +/** + * 反馈中心-新增弹窗组件 + * @param {boolean} visible - 弹窗显示状态 + * @param {function} onClose - 关闭弹窗回调 + * @param {function} onSuccess - 提交成功回调 + */ +function FeedbackModal({ visible, onClose, onSuccess }) { + const [formData, setFormData] = useState({ + title: '', + description: '', + }); + const [fileList, setFileList] = useState([]); + const [errors, setErrors] = useState({}); + const [isSubmitting, setIsSubmitting] = useState(false); + + // 处理输入变化 + const handleInputChange = useCallback((field, value) => { + setFormData(prev => ({ + ...prev, + [field]: value, + })); + // 清除对应字段的错误 + if (errors[field]) { + setErrors(prev => ({ + ...prev, + [field]: null, + })); + } + }, [errors]); + + // 文件上传前校验 + const beforeUpload = useCallback((file) => { + const isLt100M = file.size / 1024 / 1024 < 100; + if (!isLt100M) { + message.error('文件大小必须小于100MB!'); + } + return isLt100M; + }, []); + + // 文件列表变化处理 + const handleFileChange = useCallback((info) => { + if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') { + const newFileList = info.fileList || []; + setFileList(newFileList); + } + }, []); + + // 验证表单 + const validateForm = useCallback(() => { + const newErrors = {}; + if (!formData.title.trim()) { + newErrors.title = '请输入标题'; + } + if (!formData.description.trim()) { + newErrors.description = '请输入问题描述'; + } + setErrors(newErrors); + return Object.keys(newErrors).length === 0; + }, [formData]); + + // 处理提交 + const handleSubmit = useCallback(async () => { + if (!validateForm()) return; + + setIsSubmitting(true); + try { + // 获取附件ID数组 + const attachmentIdArray = fileList.map(item => { + // 优先使用 response.id(新上传的文件),其次是 item.id(已存在的文件) + return item.response && item.response.id ? item.response.id : item.id; + }).filter(id => id); // 过滤掉undefined/null + + // 构建接口参数,主题板块默认填11 + const params = { + forum_id: 11, + children_forum_id: '0', + attachments: attachmentIdArray.length > 0 ? attachmentIdArray : undefined, + memo: { + subject: formData.title, + content: formData.description, + is_original: 1, + }, + }; + + const ret = await createMemo(params); + + if (ret.status === 1) { + message.success(ret.message || '提交成功'); + // 提交成功后重置表单并关闭 + setFormData({ title: '', description: '' }); + setFileList([]); + onClose?.(); + onSuccess?.(ret); + } else { + message.error(ret.message || '提交失败'); + } + } catch (error) { + console.error('提交反馈失败:', error); + message.error('提交失败,请稍后重试'); + } finally { + setIsSubmitting(false); + } + }, [formData, fileList, validateForm, onClose, onSuccess]); + + // 处理取消 + const handleCancel = useCallback(() => { + setFormData({ title: '', description: '' }); + setFileList([]); + setErrors({}); + onClose?.(); + }, [onClose]); + + // 点击蒙层关闭 + const handleMaskClick = useCallback((e) => { + if (e.target === e.currentTarget) { + handleCancel(); + } + }, [handleCancel]); + + if (!visible) return null; + + return ( +
+
+ {/* 弹窗背景 */} +
+ +
+ + {/* 弹窗头部 */} +
+

意见反馈

+ +
+ + {/* 弹窗内容 */} +
+ {/* 标题输入 */} +
+
+ 必填 + 标题 +
+
+ handleInputChange('title', e.target.value)} + maxLength={500} + /> +
+ {errors.title && {errors.title}} +
+ + {/* 问题描述输入 */} +
+
+ 必填 + 问题描述 +
+
+