|
@@ -0,0 +1,210 @@ |
|
|
|
|
|
import React, { useEffect, useMemo, useState } from 'react' |
|
|
|
|
|
import { CloseOutlined, DeleteOutlined, PlusOutlined } from '@ant-design/icons'; |
|
|
|
|
|
import { Drawer, Form, Input, Card, Space, Button, Upload, Popconfirm, Modal, FormListOperation, FormListFieldData } from 'antd' |
|
|
|
|
|
import type { RcFile, UploadProps } from 'antd/es/upload'; |
|
|
|
|
|
import type { UploadFile } from 'antd/es/upload/interface'; |
|
|
|
|
|
|
|
|
|
|
|
const layout = { |
|
|
|
|
|
labelCol: { span: 4, }, |
|
|
|
|
|
wrapperCol: { span: 16 }, |
|
|
|
|
|
bordered: false, |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
interface AttributeValue { |
|
|
|
|
|
id: number; |
|
|
|
|
|
attrId: number; |
|
|
|
|
|
valName: string; |
|
|
|
|
|
imgId?: number; |
|
|
|
|
|
imgUrl?: string; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
export interface SampleAttribute { |
|
|
|
|
|
id: number; |
|
|
|
|
|
prototypeId: number; |
|
|
|
|
|
attrName: string; |
|
|
|
|
|
isContainImg: number; |
|
|
|
|
|
attrVals: AttributeValue[] |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
interface CreateSampleAttrProps { |
|
|
|
|
|
visible: boolean; |
|
|
|
|
|
onCancel: (flag?: boolean) => void; |
|
|
|
|
|
curRecord?: SampleAttribute[] | null; |
|
|
|
|
|
onSave: () => void; |
|
|
|
|
|
editData?: SampleAttribute[] | null; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const getBase64 = (file: RcFile): Promise<string> => |
|
|
|
|
|
new Promise((resolve, reject) => { |
|
|
|
|
|
const reader = new FileReader(); |
|
|
|
|
|
reader.readAsDataURL(file); |
|
|
|
|
|
reader.onload = () => resolve(reader.result as string); |
|
|
|
|
|
reader.onerror = (error) => reject(error); |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const CreateSampleAttr: React.FC<CreateSampleAttrProps> = (props) => { |
|
|
|
|
|
|
|
|
|
|
|
const { visible, onCancel, curRecord, onSave, editData } = props; |
|
|
|
|
|
const [saveLoading, setSaveLoading] = useState(false); |
|
|
|
|
|
const [previewOpen, setPreviewOpen] = useState(false); |
|
|
|
|
|
const [previewImage, setPreviewImage] = useState(''); |
|
|
|
|
|
const [previewTitle, setPreviewTitle] = useState(''); |
|
|
|
|
|
const [form] = Form.useForm(); |
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
|
|
if (visible) { |
|
|
|
|
|
setInitValue(); |
|
|
|
|
|
} else { |
|
|
|
|
|
form.resetFields(); |
|
|
|
|
|
} |
|
|
|
|
|
}, [visible]); |
|
|
|
|
|
|
|
|
|
|
|
async function setInitValue() { |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const save = async (values: any) => { |
|
|
|
|
|
setSaveLoading(true); |
|
|
|
|
|
debugger |
|
|
|
|
|
|
|
|
|
|
|
setSaveLoading(false); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const handleCancel = () => setPreviewOpen(false); |
|
|
|
|
|
|
|
|
|
|
|
const handlePreview = async (file: UploadFile) => { |
|
|
|
|
|
if (!file.url && !file.preview) { |
|
|
|
|
|
file.preview = await getBase64(file.originFileObj as RcFile); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
setPreviewImage(file.url || (file.preview as string)); |
|
|
|
|
|
setPreviewOpen(true); |
|
|
|
|
|
setPreviewTitle(file.name || file.url!.substring(file.url!.lastIndexOf('/') + 1)); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const uploadButton = ( |
|
|
|
|
|
<div> |
|
|
|
|
|
<PlusOutlined /> |
|
|
|
|
|
<div style={{ marginTop: 8 }}>上传</div> |
|
|
|
|
|
</div> |
|
|
|
|
|
); |
|
|
|
|
|
const handleChange: UploadProps['onChange'] = ({ fileList: newFileList }) => { |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const readerAttrValueEditForm = (subFields: FormListFieldData[], {remove, add}: FormListOperation, isContainImg: boolean) => { |
|
|
|
|
|
return (<div style={{ display: 'flex', flexDirection: 'column', rowGap: 16 }}> |
|
|
|
|
|
{subFields.map((subField) => ( |
|
|
|
|
|
<Space key={subField.key}> |
|
|
|
|
|
<Form.Item noStyle name={[subField.name, 'valName']}> |
|
|
|
|
|
<Input size='middle'/> |
|
|
|
|
|
</Form.Item> |
|
|
|
|
|
{isContainImg && <Form.Item noStyle shouldUpdate name={[subField.name, 'imgUrl']}> |
|
|
|
|
|
<Upload |
|
|
|
|
|
action="https://run.mocky.io/v3/435e224c-44fb-4773-9faf-380c5e6a2188" |
|
|
|
|
|
listType="picture-card" |
|
|
|
|
|
onPreview={handlePreview} |
|
|
|
|
|
onChange={handleChange} |
|
|
|
|
|
showUploadList={false} |
|
|
|
|
|
maxCount={1} |
|
|
|
|
|
> |
|
|
|
|
|
{uploadButton} |
|
|
|
|
|
</Upload> |
|
|
|
|
|
</Form.Item> |
|
|
|
|
|
} |
|
|
|
|
|
<Popconfirm |
|
|
|
|
|
title="删除属性值" |
|
|
|
|
|
description="确认是否删除当前属性值?" |
|
|
|
|
|
onConfirm={() => { remove(subField.name); }} |
|
|
|
|
|
okText="确认" |
|
|
|
|
|
cancelText="取消" |
|
|
|
|
|
> |
|
|
|
|
|
<CloseOutlined/> |
|
|
|
|
|
</Popconfirm> |
|
|
|
|
|
</Space> |
|
|
|
|
|
))} |
|
|
|
|
|
<Button type="dashed" size='middle' onClick={() => add()} block> |
|
|
|
|
|
+ 添加自定义值 |
|
|
|
|
|
</Button> |
|
|
|
|
|
</div>) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const renderAttrEditForm = (fields: FormListFieldData[], {remove, add}: FormListOperation, dataSource: SampleAttribute[]) => { |
|
|
|
|
|
return ( |
|
|
|
|
|
<div style={{ display: 'flex', rowGap: 16, flexDirection: 'column' }}> |
|
|
|
|
|
{fields.map((field) => { |
|
|
|
|
|
const data = dataSource[field.key] |
|
|
|
|
|
return ( |
|
|
|
|
|
<Card size="small" title={field.name} key={field.key} |
|
|
|
|
|
extra={ |
|
|
|
|
|
<Popconfirm |
|
|
|
|
|
title="删除属性" |
|
|
|
|
|
description="确认是否删除当前属性?" |
|
|
|
|
|
onConfirm={() => { remove(field.name); }} |
|
|
|
|
|
okText="确认" |
|
|
|
|
|
cancelText="取消" |
|
|
|
|
|
> |
|
|
|
|
|
<DeleteOutlined style={{ fontSize: '16px' }}/> |
|
|
|
|
|
</Popconfirm> |
|
|
|
|
|
} |
|
|
|
|
|
> |
|
|
|
|
|
<Form.Item label="属性名称" name={[field.name, 'attrName']}> |
|
|
|
|
|
<Input size='middle'/> |
|
|
|
|
|
</Form.Item> |
|
|
|
|
|
|
|
|
|
|
|
<Form.Item label="属性值"> |
|
|
|
|
|
<Form.List name={[field.name, 'attrVals']}> |
|
|
|
|
|
{(subFields, subOpt) => readerAttrValueEditForm(subFields, subOpt, data.isContainImg === 1)} |
|
|
|
|
|
</Form.List> |
|
|
|
|
|
</Form.Item> |
|
|
|
|
|
</Card> |
|
|
|
|
|
)})} |
|
|
|
|
|
|
|
|
|
|
|
<Button type="dashed" size='middle' onClick={() => add()} block> |
|
|
|
|
|
+ 添加属性 |
|
|
|
|
|
</Button> |
|
|
|
|
|
</div> |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
|
<> |
|
|
|
|
|
<Drawer |
|
|
|
|
|
open={visible} |
|
|
|
|
|
title="新建" |
|
|
|
|
|
width={640} |
|
|
|
|
|
onClose={() => { onCancel() }} |
|
|
|
|
|
extra={ |
|
|
|
|
|
<Space> |
|
|
|
|
|
<Button type="primary" size='middle' onClick={() => {save}}> |
|
|
|
|
|
保存 |
|
|
|
|
|
</Button> |
|
|
|
|
|
</Space> |
|
|
|
|
|
} |
|
|
|
|
|
destroyOnClose |
|
|
|
|
|
> |
|
|
|
|
|
<Form |
|
|
|
|
|
form={form} |
|
|
|
|
|
{...layout} |
|
|
|
|
|
initialValues={{ editData }} |
|
|
|
|
|
onFinish={save} |
|
|
|
|
|
labelCol={{ flex: '0 0 100px' }} |
|
|
|
|
|
wrapperCol={{ span: 16 }} |
|
|
|
|
|
> |
|
|
|
|
|
<Form.List name="editData"> |
|
|
|
|
|
{(fields, operation) => { |
|
|
|
|
|
return renderAttrEditForm(fields, operation, editData!!) |
|
|
|
|
|
}} |
|
|
|
|
|
</Form.List> |
|
|
|
|
|
|
|
|
|
|
|
</Form> |
|
|
|
|
|
</Drawer> |
|
|
|
|
|
<Modal open={previewOpen} title={previewTitle} footer={null} onCancel={handleCancel}> |
|
|
|
|
|
<img alt="example" style={{ width: '100%' }} src={previewImage} /> |
|
|
|
|
|
</Modal> |
|
|
|
|
|
</> |
|
|
|
|
|
) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
export default CreateSampleAttr; |