forgeplus-react-v2/src/factory/feedback/FeedbackModal.jsx

260 lines
8.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState, useCallback } from 'react';
import './FeedbackModal.scss';
import { createMemo } from '../../forums/api';
import { message, Upload, Icon, Button } from 'antd';
import { httpUrl } from '../../forums/fetch';
import RichEditor from '../../forge/Component/RichEditor';
import cookie from 'react-cookies';
import '../common.scss'
import { feedBackId } from '../../forums/static'
// 导入图片资源
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 [errors, setErrors] = useState({});
const [isSubmitting, setIsSubmitting] = useState(false);
const [fileList, setFileList] = useState([]);
// 处理输入变化
const handleInputChange = useCallback((field, value) => {
setFormData(prev => ({
...prev,
[field]: value,
}));
// 清除对应字段的错误
if (errors[field]) {
setErrors(prev => ({
...prev,
[field]: null,
}));
}
}, [errors]);
// 文件上传前校验
const beforeUpload = (file) => {
const isLt100M = file.size / 1024 / 1024 < 100;
if (!isLt100M) {
message.error('文件大小必须小于100MB!');
}
return isLt100M;
};
// 文件列表变化
const handleFileChange = (info) => {
if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') {
let fileList = info.fileList || [];
// 处理后端返回格式与Ant Design Upload预期格式不匹配的问题
// 后端返回 { status: 1, message: "成功", id: 123 } 格式
// Ant Design 期望 response 中包含能识别的成功标识
fileList = fileList.map(file => {
if (file.response && file.response.status === 1 && file.status === 'uploading') {
return {
...file,
status: 'done',
};
}
return file;
});
setFileList(fileList);
}
};
// 验证表单
const validateForm = useCallback(() => {
const newErrors = {};
if (!formData.title.trim()) {
newErrors.title = '请输入标题';
}
if (!formData.description) {
newErrors.description = '请输入问题描述';
} else {
if (formData.description === '<p><br></p>') {
newErrors.description = '请输入问题描述';
}
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
}, [formData]);
// 处理提交
const handleSubmit = useCallback(async () => {
if (!validateForm()) return;
setIsSubmitting(true);
try {
// 构建附件ID数组参考forums/edit的实现
const attachmentIdArray = fileList.map(item =>
item.response && item.response.id ? item.response.id : item.id ? item.id : item
);
// 构建接口参数主题板块默认填12
const params = {
forum_id: feedBackId,
attachments: attachmentIdArray,
memo: {
subject: formData.title,
content: formData.description,
is_original: 1,
tag_id: 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, validateForm, fileList, onClose, onSuccess]);
// 处理取消
const handleCancel = useCallback(() => {
setFormData({ title: '', description: '' });
setErrors({});
setFileList([]);
onClose?.();
}, [onClose]);
// 点击蒙层关闭已禁用仅通过X按钮和取消按钮关闭
const handleMaskClick = useCallback((e) => {
// 点击蒙层不关闭弹窗
}, []);
if (!visible) return null;
return (
<div className="feedback-modal-wrap">
<div className="feedback-modal-mask" onClick={handleMaskClick}>
<div className="feedback-modal">
{/* 弹窗背景 */}
<div className="feedback-modal-bg">
<img src={modalBg} alt="" />
</div>
{/* 弹窗头部 */}
<div className="feedback-modal-header">
<h2 className="feedback-modal-title">意见反馈</h2>
<button
className="feedback-modal-close"
onClick={handleCancel}
aria-label="关闭"
>
<img src={iconClose} alt="关闭" />
</button>
</div>
{/* 弹窗内容 */}
<div className="feedback-modal-content">
{/* 标题输入 */}
<div className="feedback-form-item">
<div className="feedback-form-label">
<img src={iconRequired} alt="必填" className="required-icon" />
<span>标题</span>
</div>
<div className={`feedback-form-input-wrapper ${errors.title ? 'error' : ''}`}>
<input
type="text"
className="feedback-form-input"
placeholder="请输入标题"
value={formData.title}
onChange={(e) => handleInputChange('title', e.target.value)}
maxLength={200}
/>
</div>
{errors.title && <span className="feedback-form-error">{errors.title}</span>}
</div>
{/* 问题描述输入 */}
<div className="feedback-form-item">
<div className="feedback-form-label">
<img src={iconRequired} alt="必填" className="required-icon" />
<span>问题描述</span>
</div>
<div className="feedback-form-rich">
<RichEditor httpUrl={httpUrl} uploadUrl={`${httpUrl}/api/attachments.json`} videoUploadUrl={`${httpUrl}/api/attachments.json`} height={'270px'} setValue={(value) => { handleInputChange('description', value) }} />
</div>
{errors.description && <span className="feedback-form-error">{errors.description}</span>}
</div>
{/* 附件上传 - 参考forums/edit配置 */}
<div className="feedback-form-item">
<div className="feedback-form-label">
<span>附件</span>
</div>
<div className="feedback-form-upload">
<Upload
action={`${httpUrl}/api/attachments.json`}
beforeUpload={beforeUpload}
onChange={handleFileChange}
fileList={fileList}
className="feedback-upload-dragger"
withCredentials={true}
headers={{ Authorization: cookie.load('autologin_trustie') }}
>
{
fileList.length < 3 && <>
<Button className="uploadBtn">
<Icon type="upload" /> 选择文件
</Button>
<span className={"ml10 color-ooo"}>(单个文件最大100M,最多3个附件)</span></>
}
</Upload>
</div>
</div>
<div className="feedback-modal-footer">
<button
className="feedback-btn feedback-btn-cancel"
onClick={handleCancel}
disabled={isSubmitting}
>
取消
</button>
<button
className="feedback-btn feedback-btn-submit shiny-button"
onClick={handleSubmit}
disabled={isSubmitting}
>
{isSubmitting ? '提交中...' : '提交'}
</button>
</div>
</div>
</div>
</div>
</div>
);
}
export default FeedbackModal;