2025-07-22 22:52:55 +08:00
|
|
|
// src/pages/list/basic-list/components/AddTimeLineItemModal.tsx
|
|
|
|
|
import { addStoryItem } from '@/pages/list/basic-list/service';
|
2025-08-04 16:56:39 +08:00
|
|
|
import chinaRegion, { code2Location } from '@/commonConstant/chinaRegion';
|
2025-07-22 22:52:55 +08:00
|
|
|
import { UploadOutlined } from '@ant-design/icons';
|
|
|
|
|
import { useRequest } from '@umijs/max';
|
|
|
|
|
import { Button, Cascader, DatePicker, Form, Input, message, Modal, Upload } from 'antd';
|
|
|
|
|
import ImgCrop from 'antd-img-crop';
|
|
|
|
|
import dayjs from 'dayjs';
|
|
|
|
|
import moment from 'moment';
|
|
|
|
|
import React, { useEffect, useState } from 'react';
|
|
|
|
|
|
|
|
|
|
interface ModalProps {
|
|
|
|
|
visible: boolean;
|
|
|
|
|
onCancel: () => void;
|
|
|
|
|
onOk: () => void;
|
|
|
|
|
lineId: string | number | undefined;
|
|
|
|
|
initialValues?: any;
|
|
|
|
|
isRoot: boolean; // 是否根节点
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const AddTimeLineItemModal: React.FC<ModalProps> = ({
|
|
|
|
|
visible,
|
|
|
|
|
onCancel,
|
|
|
|
|
onOk,
|
|
|
|
|
lineId,
|
|
|
|
|
initialValues,
|
|
|
|
|
isRoot = true,
|
|
|
|
|
}) => {
|
|
|
|
|
const [form] = Form.useForm();
|
|
|
|
|
const [fileList, setFileList] = useState<any[]>([]);
|
|
|
|
|
const [imageList, setImageList] = useState<any[]>(initialValues?.images || []);
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (initialValues) {
|
|
|
|
|
form.setFieldsValue({
|
|
|
|
|
title: initialValues.title,
|
|
|
|
|
storyItemTime: initialValues.date ? moment(initialValues.date) : undefined,
|
|
|
|
|
location: initialValues.location,
|
|
|
|
|
description: initialValues.description,
|
|
|
|
|
cover: initialValues.cover ? [{ url: initialValues.cover }] : [],
|
|
|
|
|
images: initialValues.images?.map((url) => ({ url })) || [],
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}, [initialValues]);
|
|
|
|
|
const { run: submitItem, loading } = useRequest((newItem) => addStoryItem(newItem), {
|
|
|
|
|
manual: true,
|
|
|
|
|
onSuccess: (data) => {
|
|
|
|
|
console.log(data);
|
|
|
|
|
if (data.code === 200) {
|
|
|
|
|
onOk();
|
|
|
|
|
message.success(initialValues ? '时间点已更新' : '时间点已保存');
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
onError: (error) => {
|
|
|
|
|
message.error('保存失败');
|
|
|
|
|
console.error('保存失败:', error);
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const handleOk = async () => {
|
|
|
|
|
try {
|
|
|
|
|
const values = await form.validateFields();
|
|
|
|
|
const location = code2Location(values.location);
|
|
|
|
|
const newItem = {
|
|
|
|
|
...values,
|
|
|
|
|
id: initialValues?.id || Date.now(),
|
|
|
|
|
storyItemTime: dayjs(values.date).format('YYYY-MM-DDTHH:mm:ss'),
|
|
|
|
|
masterItemId: lineId,
|
|
|
|
|
subItems: initialValues?.subItems || [],
|
|
|
|
|
isRoot: isRoot ? 1 : 0,
|
|
|
|
|
location,
|
|
|
|
|
};
|
|
|
|
|
delete newItem.cover;
|
|
|
|
|
delete newItem.images;
|
|
|
|
|
// 构建 FormData
|
|
|
|
|
const formData = new FormData();
|
|
|
|
|
|
|
|
|
|
// 添加 storyItem 作为 JSON 字符串
|
|
|
|
|
formData.append('storyItem', JSON.stringify(newItem));
|
|
|
|
|
|
|
|
|
|
// 添加封面文件
|
|
|
|
|
if (fileList.length > 0 && fileList[0].originFileObj instanceof File) {
|
|
|
|
|
formData.append('cover', fileList[0].originFileObj);
|
|
|
|
|
}
|
|
|
|
|
console.log(imageList);
|
|
|
|
|
if (imageList.length > 0) {
|
|
|
|
|
imageList.forEach((file) => {
|
|
|
|
|
if (file.originFileObj && file.originFileObj instanceof File) {
|
|
|
|
|
formData.append('images', file.originFileObj);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
// 提交
|
|
|
|
|
submitItem(formData);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('表单验证失败:', error);
|
|
|
|
|
message.error('请检查表单内容');
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const uploadCoverProps = {
|
|
|
|
|
beforeUpload: (file) => {
|
|
|
|
|
// 确保 originFileObj 是真正的 File 对象
|
|
|
|
|
return false; // 阻止自动上传
|
|
|
|
|
},
|
|
|
|
|
onChange: ({ fileList }) => {
|
|
|
|
|
// 确保 originFileObj 是真正的 File 对象
|
|
|
|
|
const updatedFileList = fileList.map((file) => {
|
|
|
|
|
if (file.originFileObj && !(file.originFileObj instanceof File)) {
|
|
|
|
|
file.originFileObj = new File([file.originFileObj], file.name, { type: file.type });
|
|
|
|
|
}
|
|
|
|
|
return file;
|
|
|
|
|
});
|
|
|
|
|
setFileList(updatedFileList);
|
|
|
|
|
},
|
|
|
|
|
listType: 'picture',
|
|
|
|
|
maxCount: 1,
|
|
|
|
|
defaultFileList: initialValues?.cover ? [{ url: initialValues.cover }] : [],
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const uploadImagesProps = {
|
|
|
|
|
beforeUpload: () => false,
|
|
|
|
|
onChange: ({ fileList }) => {
|
|
|
|
|
const updatedFileList = fileList.map((file) => {
|
|
|
|
|
if (file.originFileObj && !(file.originFileObj instanceof File)) {
|
|
|
|
|
file.originFileObj = new File([file.originFileObj], file.name, { type: file.type });
|
|
|
|
|
}
|
|
|
|
|
return file;
|
|
|
|
|
});
|
|
|
|
|
setImageList(updatedFileList);
|
|
|
|
|
},
|
|
|
|
|
listType: 'picture-card',
|
|
|
|
|
multiple: true,
|
|
|
|
|
defaultFileList: initialValues?.images?.map((url) => ({ url })),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Modal
|
|
|
|
|
open={visible}
|
|
|
|
|
title={initialValues ? '编辑时间点' : '添加时间点'}
|
|
|
|
|
onCancel={() => {
|
|
|
|
|
form.resetFields();
|
|
|
|
|
onCancel();
|
|
|
|
|
}}
|
|
|
|
|
footer={[
|
|
|
|
|
<Button key="back" onClick={onCancel}>
|
|
|
|
|
取消
|
|
|
|
|
</Button>,
|
|
|
|
|
<Button key="submit" type="primary" onClick={handleOk} loading={loading}>
|
|
|
|
|
确定
|
|
|
|
|
</Button>,
|
|
|
|
|
]}
|
|
|
|
|
>
|
|
|
|
|
<Form form={form} layout="vertical">
|
|
|
|
|
{!isRoot && <Form.Item label={'主时间点'}>{lineId}</Form.Item>}
|
|
|
|
|
<Form.Item label="标题" name="title" rules={[{ required: true, message: '请输入标题' }]}>
|
|
|
|
|
<Input placeholder="请输入标题" />
|
|
|
|
|
</Form.Item>
|
|
|
|
|
|
|
|
|
|
<Form.Item label="时间" name="date" rules={[{ required: true, message: '请选择时间' }]}>
|
|
|
|
|
<DatePicker
|
|
|
|
|
showTime={{ defaultValue: dayjs('00:00:00', 'HH:mm:ss') }}
|
|
|
|
|
format="YYYY-MM-DD HH:mm:ss"
|
|
|
|
|
style={{ width: '100%' }}
|
|
|
|
|
/>
|
|
|
|
|
</Form.Item>
|
|
|
|
|
<Form.Item label="位置" name="location" rules={[{ required: false }]}>
|
|
|
|
|
<Cascader
|
|
|
|
|
options={chinaRegion}
|
|
|
|
|
fieldNames={{ label: 'title', value: 'value', children: 'children' }}
|
|
|
|
|
placeholder="请选择省/市/区"
|
|
|
|
|
/>
|
|
|
|
|
</Form.Item>
|
|
|
|
|
<Form.Item label="描述" name="description">
|
|
|
|
|
<Input.TextArea rows={4} placeholder="请输入时间点描述" />
|
|
|
|
|
</Form.Item>
|
|
|
|
|
|
|
|
|
|
<Form.Item label="封面图" name="cover">
|
|
|
|
|
<ImgCrop aspect={1} quality={0.5} rotationSlider>
|
|
|
|
|
<Upload {...uploadCoverProps} maxCount={1}>
|
|
|
|
|
<Button icon={<UploadOutlined />}>上传封面</Button>
|
|
|
|
|
</Upload>
|
|
|
|
|
</ImgCrop>
|
|
|
|
|
</Form.Item>
|
|
|
|
|
|
|
|
|
|
{/* 新增:时刻图库 */}
|
|
|
|
|
<Form.Item label="时刻图库(多图)" name="images">
|
|
|
|
|
<Upload {...uploadImagesProps} maxCount={5}>
|
|
|
|
|
<Button size={"small"} icon={<UploadOutlined />}>上传多图</Button>
|
|
|
|
|
</Upload>
|
|
|
|
|
</Form.Item>
|
|
|
|
|
</Form>
|
|
|
|
|
</Modal>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default AddTimeLineItemModal;
|