init
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Form, Modal, Input, TimePicker, Button } from 'antd';
|
||||
|
||||
interface SubItem {
|
||||
time: string;
|
||||
title: string;
|
||||
description: string;
|
||||
cover?: string | null;
|
||||
}
|
||||
|
||||
interface ModalProps {
|
||||
visible: boolean;
|
||||
onCancel: () => void;
|
||||
onOk: (values: SubItem) => void;
|
||||
}
|
||||
|
||||
const AddSubTimeLineItemModal: React.FC<ModalProps> = ({ visible, onCancel, onOk }) => {
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const handleOk = async () => {
|
||||
try {
|
||||
const values = await form.validateFields();
|
||||
onOk(values);
|
||||
} catch (error) {
|
||||
console.error('表单校验失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={visible}
|
||||
title="添加子时间点"
|
||||
onCancel={() => {
|
||||
form.resetFields();
|
||||
onCancel();
|
||||
}}
|
||||
footer={[
|
||||
<Button key="back" onClick={onCancel}>
|
||||
取消
|
||||
</Button>,
|
||||
<Button key="submit" type="primary" onClick={handleOk}>
|
||||
确定
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
<Form form={form} layout="vertical" name="subTimeLineItemForm">
|
||||
<Form.Item label="时间(HH:mm)" name="time" rules={[{ required: true, message: '请输入时间' }]}>
|
||||
<Input placeholder="例如:14:30" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="子标题" name="title" rules={[{ required: true, message: '请输入标题' }]}>
|
||||
<Input placeholder="请输入子标题" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="子描述" name="description">
|
||||
<Input.TextArea rows={4} placeholder="请输入子时间点描述" />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddSubTimeLineItemModal;
|
||||
196
src/pages/list/basic-list/components/AddTimeLineItemModal.tsx
Normal file
196
src/pages/list/basic-list/components/AddTimeLineItemModal.tsx
Normal file
@@ -0,0 +1,196 @@
|
||||
// src/pages/list/basic-list/components/AddTimeLineItemModal.tsx
|
||||
import { addStoryItem } from '@/pages/list/basic-list/service';
|
||||
import chinaRegion, { code2Location } from '@/utils/chinaRegion';
|
||||
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;
|
||||
128
src/pages/list/basic-list/components/OperationModal.tsx
Normal file
128
src/pages/list/basic-list/components/OperationModal.tsx
Normal file
@@ -0,0 +1,128 @@
|
||||
import {
|
||||
ModalForm,
|
||||
ProFormDateTimePicker,
|
||||
ProFormSelect,
|
||||
ProFormText,
|
||||
ProFormTextArea,
|
||||
} from '@ant-design/pro-components';
|
||||
import { Button, Result } from 'antd';
|
||||
import React, { FC } from 'react';
|
||||
import type {StoryType} from '../data.d';
|
||||
import useStyles from '../style.style';
|
||||
type OperationModalProps = {
|
||||
done: boolean;
|
||||
open: boolean;
|
||||
current: Partial<StoryType> | undefined;
|
||||
onDone: () => void;
|
||||
onSubmit: (values: StoryType) => void;
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
const OperationModal: FC<OperationModalProps> = (props) => {
|
||||
const { styles } = useStyles();
|
||||
const { done, open, current, onDone, onSubmit, children } = props;
|
||||
if (!open) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<ModalForm<StoryType>
|
||||
open={open}
|
||||
title={done ? null : `任务${current ? '编辑' : '添加'}`}
|
||||
className={styles.standardListForm}
|
||||
width={640}
|
||||
onFinish={async (values) => {
|
||||
onSubmit(values);
|
||||
}}
|
||||
initialValues={current}
|
||||
submitter={{
|
||||
render: (_, dom) => (done ? null : dom),
|
||||
}}
|
||||
trigger={<>{children}</>}
|
||||
modalProps={{
|
||||
onCancel: () => onDone(),
|
||||
destroyOnClose: true,
|
||||
bodyStyle: done
|
||||
? {
|
||||
padding: '72px 0',
|
||||
}
|
||||
: {},
|
||||
}}
|
||||
>
|
||||
{!done ? (
|
||||
<>
|
||||
<ProFormText
|
||||
name="title"
|
||||
label="任务名称"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: '请输入任务名称',
|
||||
},
|
||||
]}
|
||||
placeholder="请输入"
|
||||
/>
|
||||
<ProFormDateTimePicker
|
||||
name="createTime"
|
||||
label="开始时间"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: '请选择开始时间',
|
||||
},
|
||||
]}
|
||||
fieldProps={{
|
||||
style: {
|
||||
width: '100%',
|
||||
},
|
||||
}}
|
||||
placeholder="请选择"
|
||||
/>
|
||||
<ProFormSelect
|
||||
name="ownerId"
|
||||
label="任务负责人"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: '请选择任务负责人',
|
||||
},
|
||||
]}
|
||||
options={[
|
||||
{
|
||||
label: '付晓晓',
|
||||
value: 'xiao',
|
||||
},
|
||||
{
|
||||
label: '周毛毛',
|
||||
value: 'mao',
|
||||
},
|
||||
]}
|
||||
placeholder="请选择管理员"
|
||||
/>
|
||||
<ProFormTextArea
|
||||
name="description"
|
||||
label="产品描述"
|
||||
rules={[
|
||||
{
|
||||
message: '请输入至少五个字符的产品描述!',
|
||||
min: 5,
|
||||
},
|
||||
]}
|
||||
placeholder="请输入至少五个字符"
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<Result
|
||||
status="success"
|
||||
title="操作成功"
|
||||
subTitle="一系列的信息描述,很短同样也可以带标点。"
|
||||
extra={
|
||||
<Button type="primary" onClick={onDone}>
|
||||
知道了
|
||||
</Button>
|
||||
}
|
||||
className={styles.formResult}
|
||||
/>
|
||||
)}
|
||||
</ModalForm>
|
||||
);
|
||||
};
|
||||
export default OperationModal;
|
||||
@@ -0,0 +1,97 @@
|
||||
// file: SubTimeLineItemModal.tsx
|
||||
import React, { useEffect } from 'react';
|
||||
import { Form, Modal, Input, DatePicker, Upload, Button } from 'antd';
|
||||
import { UploadOutlined } from '@ant-design/icons';
|
||||
import moment from 'moment';
|
||||
|
||||
interface ModalProps {
|
||||
visible: boolean;
|
||||
onCancel: () => void;
|
||||
onOk: (values: any) => void;
|
||||
initialValues?: any;
|
||||
}
|
||||
|
||||
const SubTimeLineItemModal: React.FC<ModalProps> = ({ visible, onCancel, onOk, initialValues }) => {
|
||||
const [form] = Form.useForm();
|
||||
|
||||
useEffect(() => {
|
||||
if (initialValues) {
|
||||
form.setFieldsValue({
|
||||
title: initialValues.title,
|
||||
date: initialValues.date ? moment(initialValues.date) : undefined,
|
||||
location: initialValues.location,
|
||||
description: initialValues.description,
|
||||
cover: initialValues.cover,
|
||||
});
|
||||
}
|
||||
}, [initialValues]);
|
||||
|
||||
const handleOk = async () => {
|
||||
try {
|
||||
const values = await form.validateFields();
|
||||
const newItem = {
|
||||
...values,
|
||||
id: initialValues?.id || Date.now(),
|
||||
cover:
|
||||
form.getFieldValue('cover') ||
|
||||
(initialValues && initialValues.cover),
|
||||
date: values.date.format('YYYY-MM-DD'),
|
||||
};
|
||||
onOk(newItem);
|
||||
} catch (error) {
|
||||
console.error('表单验证失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const uploadProps = {
|
||||
beforeUpload: () => false,
|
||||
onChange: ({ fileList }) => {},
|
||||
listType: 'picture' as const,
|
||||
maxCount: 1,
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={visible}
|
||||
title={initialValues ? '编辑时间点' : '添加时间点'}
|
||||
onCancel={() => {
|
||||
form.resetFields();
|
||||
onCancel();
|
||||
}}
|
||||
footer={[
|
||||
<Button key="back" onClick={onCancel}>
|
||||
取消
|
||||
</Button>,
|
||||
<Button key="submit" type="primary" onClick={handleOk}>
|
||||
确定
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
<Form form={form} layout="vertical" name="timeLineItemForm">
|
||||
<Form.Item label="标题" name="title" rules={[{ required: true, message: '请输入标题' }]}>
|
||||
<Input placeholder="请输入标题" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="时间" name="date" rules={[{ required: true, message: '请选择时间' }]}>
|
||||
<DatePicker style={{ width: '100%' }} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="地点" name="location">
|
||||
<Input placeholder="请输入地点" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="描述" name="description">
|
||||
<Input.TextArea rows={4} placeholder="请输入时间点描述" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="封面图片" name="cover">
|
||||
<Upload {...uploadProps} maxCount={1}>
|
||||
<Button icon={<UploadOutlined />}>上传图片</Button>
|
||||
</Upload>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default SubTimeLineItemModal;
|
||||
@@ -0,0 +1,155 @@
|
||||
import { Badge, Col, Drawer, Row, Timeline } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
import TimelineImage from '@/components/TimelineImage';
|
||||
import TimelineItemDrawer from '@/pages/list/basic-list/components/TimelineItemDrawer';
|
||||
import { StoryItem, TimelineEvent } from '@/pages/list/basic-list/data';
|
||||
import './index.css';
|
||||
|
||||
interface TimelineItemProps {
|
||||
event: StoryItem;
|
||||
onUpdate?: (updatedEvent: TimelineEvent) => void; // 数据更新回调
|
||||
}
|
||||
|
||||
const TimelineItem = ({ event: initialEvent, onUpdate }: TimelineItemProps) => {
|
||||
const [openMainDrawer, setOpenMainDrawer] = React.useState(false);
|
||||
const [expanded, setExpanded] = React.useState(false); // 控制子项展开状态
|
||||
|
||||
const [openSubDrawer, setOpenSubDrawer] = React.useState(false);
|
||||
const [selectedSubItem, setSelectedSubItem] = React.useState<TimelineEvent | null>(null);
|
||||
|
||||
const showMainDrawer = () => {
|
||||
setOpenMainDrawer(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`timeline-item ${expanded ? 'expanded' : ''}`}>
|
||||
{/* 主时间线容器 */}
|
||||
<div className={`timeline-event-container ${expanded ? 'expanded' : ''}`}>
|
||||
<div className={`timeline-event ${expanded ? 'minimized' : ''}`}>
|
||||
<Row gutter={24} align="middle">
|
||||
<Col span={6}>
|
||||
<TimelineImage
|
||||
width={200}
|
||||
height={200}
|
||||
title={initialEvent.title}
|
||||
imageInstanceId={initialEvent.coverInstanceId}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={16} offset={2}>
|
||||
<div className="timeline-content-text" onClick={showMainDrawer}>
|
||||
<h2 style={{ fontSize: '1.5rem', fontWeight: 'bold' }}>{initialEvent.title}</h2>
|
||||
<p style={{ marginBottom: '0.5rem' }}>{initialEvent.description}</p>
|
||||
<div style={{ color: '#666', lineHeight: '1.6' }}>
|
||||
<span>故事时间:{initialEvent.storyItemTime}</span>
|
||||
<br />
|
||||
<span>创建时间:{initialEvent.createTime}</span>
|
||||
<br />
|
||||
<span>更新时间:{initialEvent.updateTime}</span>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
{/* 子时间点徽章 */}
|
||||
{initialEvent.subItems && initialEvent.subItems.length > 0 && (
|
||||
<Badge
|
||||
count={`${initialEvent.subItems.length} 个子时间点`}
|
||||
style={{
|
||||
backgroundColor: '#1890ff',
|
||||
position: 'absolute',
|
||||
top: 10,
|
||||
right: 10,
|
||||
transform: 'translate(50%, -50%)', // 修复超出边界
|
||||
padding: '0 6px',
|
||||
borderRadius: 4,
|
||||
cursor: 'pointer',
|
||||
fontSize: 12,
|
||||
height: 20,
|
||||
lineHeight: '20px',
|
||||
textAlign: 'center',
|
||||
minWidth: 60,
|
||||
boxSizing: 'border-box',
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setExpanded(!expanded);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 子时间线列表 */}
|
||||
{initialEvent.subItems && initialEvent.subItems.length > 0 && expanded && (
|
||||
<div className="sub-timeline-wrapper">
|
||||
<Timeline
|
||||
mode="left"
|
||||
items={initialEvent.subItems.map((sub) => ({
|
||||
children: (
|
||||
<div
|
||||
className="sub-timeline-item"
|
||||
style={{
|
||||
fontSize: '12px',
|
||||
padding: '8px 0',
|
||||
cursor: 'pointer',
|
||||
transition: 'background-color 0.2s',
|
||||
':hover': { backgroundColor: '#f5f5f5' }, // 添加 hover 效果
|
||||
}}
|
||||
>
|
||||
<Row>
|
||||
<Col span={4}>
|
||||
<TimelineImage title={sub.title} imageInstanceId={sub.coverInstanceId} />
|
||||
</Col>
|
||||
<Col span={18} offset={2}>
|
||||
<div className="timeline-content-text" onClick={showMainDrawer}>
|
||||
<h2 style={{ fontSize: '1.2rem', fontWeight: 'normal' }}>{sub.title}</h2>
|
||||
<p style={{ marginBottom: '0.5rem' }}>{sub.description}</p>
|
||||
<div style={{ color: '#999', lineHeight: '1.6' }}>
|
||||
<span>故事时间:{sub.storyItemTime}</span>
|
||||
<br />
|
||||
<span>创建时间:{sub.createTime}</span>
|
||||
<br />
|
||||
<span>更新时间:{sub.updateTime}</span>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
),
|
||||
}))}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 主时间点详情抽屉 */}
|
||||
<TimelineItemDrawer
|
||||
storyItem={initialEvent}
|
||||
open={openMainDrawer}
|
||||
setOpen={setOpenMainDrawer}
|
||||
/>
|
||||
|
||||
{/* 子时间点详情抽屉 */}
|
||||
{selectedSubItem && (
|
||||
<Drawer
|
||||
width={640}
|
||||
placement="right"
|
||||
onClose={() => setOpenSubDrawer(false)}
|
||||
open={openSubDrawer}
|
||||
title={selectedSubItem.title}
|
||||
>
|
||||
<p style={{ marginBottom: 24 }}>
|
||||
<strong>描述:</strong>
|
||||
{selectedSubItem.description}
|
||||
</p>
|
||||
<p>
|
||||
<strong>时间:</strong>
|
||||
{selectedSubItem.time || '未设置'}
|
||||
</p>
|
||||
</Drawer>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TimelineItem;
|
||||
38
src/pages/list/basic-list/components/TimelineItem/index.css
Normal file
38
src/pages/list/basic-list/components/TimelineItem/index.css
Normal file
@@ -0,0 +1,38 @@
|
||||
.timeline-item {
|
||||
margin-bottom: 24px;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
transition: box-shadow 0.3s;
|
||||
&:hover {
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.timeline-event-container {
|
||||
padding: 16px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #e8e8e8;
|
||||
}
|
||||
|
||||
.timeline-content-text {
|
||||
cursor: pointer;
|
||||
transition: color 0.3s;
|
||||
&:hover {
|
||||
color: #1890ff;
|
||||
}
|
||||
}
|
||||
|
||||
.sub-timeline-wrapper {
|
||||
margin-top: 16px;
|
||||
background-color: #f9f9f9;
|
||||
padding: 16px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.sub-timeline-item {
|
||||
padding: 8px 0;
|
||||
transition: background-color 0.2s;
|
||||
&:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
}
|
||||
143
src/pages/list/basic-list/components/TimelineItemDrawer.tsx
Normal file
143
src/pages/list/basic-list/components/TimelineItemDrawer.tsx
Normal file
@@ -0,0 +1,143 @@
|
||||
import TimelineImage from '@/components/TimelineImage';
|
||||
import AddTimeLineItemModal from '@/pages/list/basic-list/components/AddTimeLineItemModal';
|
||||
import SubTimeLineItemModal from '@/pages/list/basic-list/components/SubTimeLineItemModal';
|
||||
import { StoryItem } from '@/pages/list/basic-list/data';
|
||||
import { queryStoryItemImages } from '@/pages/list/basic-list/service';
|
||||
import { EditOutlined, PlusCircleOutlined } from '@ant-design/icons';
|
||||
import { useRequest } from '@umijs/max';
|
||||
import { Button, Drawer, Space } from 'antd';
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
interface Props {
|
||||
storyItem: StoryItem;
|
||||
open: boolean;
|
||||
setOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
}
|
||||
|
||||
const TimelineItemDrawer = (props: Props) => {
|
||||
const { storyItem, open, setOpen } = props;
|
||||
const [editModalVisible, setEditModalVisible] = React.useState(false);
|
||||
const [openAddSubItemModal, setOpenAddSubItemModal] = React.useState(false);
|
||||
const [openEditMainItemModal, setOpenEditMainItemModal] = React.useState(false);
|
||||
const { data: imagesList, run } = useRequest(
|
||||
async (itemId) => {
|
||||
return await queryStoryItemImages(itemId);
|
||||
},
|
||||
{
|
||||
manual: true,
|
||||
},
|
||||
);
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
console.log(storyItem);
|
||||
run(storyItem.itemId);
|
||||
}
|
||||
}, [open]);
|
||||
const closeDrawer = () => {
|
||||
setOpen(false);
|
||||
};
|
||||
const handleEditMainItem = (updatedItem: any) => {
|
||||
const mergedEvent = {
|
||||
...storyItem,
|
||||
...updatedItem,
|
||||
};
|
||||
setOpenEditMainItemModal(false);
|
||||
};
|
||||
const handleAddSubItem = () => {
|
||||
setOpenAddSubItemModal(false);
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
{/* 主时间点详情抽屉 */}
|
||||
<Drawer
|
||||
width={1000}
|
||||
placement="right"
|
||||
onClose={() => {
|
||||
closeDrawer();
|
||||
}}
|
||||
open={open}
|
||||
title={storyItem.title}
|
||||
footer={
|
||||
<div style={{ textAlign: 'right' }}>
|
||||
<Space>
|
||||
<Button
|
||||
icon={<PlusCircleOutlined />}
|
||||
type="primary"
|
||||
onClick={() => setOpenAddSubItemModal(true)}
|
||||
>
|
||||
添加子时间点
|
||||
</Button>
|
||||
<Button icon={<EditOutlined />} onClick={() => setEditModalVisible(true)}>
|
||||
编辑
|
||||
</Button>
|
||||
<Button onClick={closeDrawer}>关闭</Button>
|
||||
</Space>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<p>
|
||||
<strong>描述:</strong> {storyItem.description}
|
||||
</p>
|
||||
<p>
|
||||
<strong>日期:</strong> {storyItem.storyItemTime}
|
||||
</p>
|
||||
<p>
|
||||
<strong>位置:</strong> {storyItem.location}
|
||||
</p>
|
||||
{/* 封面图 */}
|
||||
{storyItem.coverInstanceId && (
|
||||
<>
|
||||
<p>
|
||||
<strong>封面图:</strong>
|
||||
</p>
|
||||
<TimelineImage
|
||||
title={storyItem.title + 'cover'}
|
||||
imageInstanceId={storyItem.coverInstanceId}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 时刻图库 */}
|
||||
{imagesList && imagesList.length > 0 && (
|
||||
<>
|
||||
<p>
|
||||
<strong>时刻图库:</strong>
|
||||
</p>
|
||||
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '10px', marginBottom: '20px' }}>
|
||||
{imagesList.map((imageInstanceId, index) => (
|
||||
<TimelineImage
|
||||
key={imageInstanceId + index}
|
||||
title={imageInstanceId}
|
||||
imageInstanceId={imageInstanceId}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 编辑模态框入口 */}
|
||||
{/*<Button type="default" onClick={() => setOpenEditMainItemModal(true)} block style={{ marginTop: '16px' }}>
|
||||
编辑时间点信息
|
||||
</Button>*/}
|
||||
{/* 添加子时间点模态框 */}
|
||||
<AddTimeLineItemModal
|
||||
visible={openAddSubItemModal}
|
||||
onOk={handleAddSubItem}
|
||||
onCancel={() => setOpenAddSubItemModal(false)}
|
||||
lineId={storyItem.itemId}
|
||||
isRoot={false}
|
||||
/>
|
||||
|
||||
{/* 编辑主时间点模态框 */}
|
||||
<SubTimeLineItemModal
|
||||
visible={openEditMainItemModal}
|
||||
onOk={handleEditMainItem}
|
||||
onCancel={() => setOpenEditMainItemModal(false)}
|
||||
initialValues={storyItem}
|
||||
/>
|
||||
</Drawer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TimelineItemDrawer;
|
||||
Reference in New Issue
Block a user