Parcourir la source

add attr-editor

dev
powersir il y a 1 an
Parent
révision
9823b816a8
2 fichiers modifiés avec 289 ajouts et 14 suppressions
  1. +210
    -0
      src/pages/custom/product/sample/attr-editor.tsx
  2. +79
    -14
      src/pages/custom/product/sample/index.tsx

+ 210
- 0
src/pages/custom/product/sample/attr-editor.tsx Voir le fichier

@@ -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;

+ 79
- 14
src/pages/custom/product/sample/index.tsx Voir le fichier

@@ -4,6 +4,8 @@ import { t } from '@/utils/i18n';
import { IconBuguang } from '@/assets/icons/buguang';
import React, { useState } from 'react';
import type { TableRowSelection } from 'antd/es/table/interface';
import CreateSampleAttr from './attr-editor'
import type { SampleAttribute } from './attr-editor'

interface DataType {
id: number;
@@ -68,7 +70,11 @@ const TablePage: React.FC = () => {
render: (_, record) => (
<Space size="middle">
<a>蒙版图 </a>
<a>属性设置</a>
<a
onClick={() => {
// setEditData(record);
setCreateVisible(true);
}}>属性设置</a>
<a>编辑</a>
<a>删除</a>
</Space>
@@ -88,23 +94,76 @@ const TablePage: React.FC = () => {
createName: "陈相荣✨",
oneImgUrl: "https://test.vogocm.com:9010/eshop/eshop_img/2023/8/21/20230821114346A056.jpg",
dictDetails: []
},
{
id: 76,
createTime: "2023-07-13 17:24:25",
spuCode: "2-47GEE7",
categoryId: 1264,
prototypeName: "kfc-test",
createId: 2,
categoryName: "男士T恤",
createName: "陈相荣✨",
oneImgUrl: "https://test.vogocm.com:9010/eshop/eshop_img/2023/8/21/20230821114346A056.jpg",
dictDetails: []
}
},
{
id: 76,
createTime: "2023-07-13 17:24:25",
spuCode: "2-47GEE7",
categoryId: 1264,
prototypeName: "kfc-test",
createId: 2,
categoryName: "男士T恤",
createName: "陈相荣✨",
oneImgUrl: "https://test.vogocm.com:9010/eshop/eshop_img/2023/8/21/20230821114346A056.jpg",
dictDetails: []
}
];

const attrData: SampleAttribute[] = [
{
"id": 105,
"prototypeId": 88,
"attrName": "Color",
"isContainImg": 1,
"attrVals": [
{
"id": 273,
"attrId": 105,
"valName": "Pink",
"imgId": 1990260,
"imgUrl": "https://test.vogocm.com:9010/eshop/eshop_img/2023/8/21/20230821104322A047.jpg"
},
{
"id": 274,
"attrId": 105,
"valName": "Black",
"imgId": 1990263,
"imgUrl": "https://test.vogocm.com:9010/eshop/eshop_img/2023/8/21/20230821104322A050.jpg"
}
]
},
{
"id": 106,
"prototypeId": 88,
"attrName": "Size",
"isContainImg": 2,
"attrVals": [
{
"id": 275,
"attrId": 106,
"valName": "XL"
},
{
"id": 276,
"attrId": 106,
"valName": "XXL"
}
]
}
]


const [createVisible, setCreateVisible] = useState(false);
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);

const cancelHandle = () => {
setCreateVisible(false);
};

const saveHandle = () => {
setCreateVisible(false);
}

const onSelectChange = (newSelectedRowKeys: React.Key[]) => {
console.log('selectedRowKeys changed: ', newSelectedRowKeys);
setSelectedRowKeys(newSelectedRowKeys);
@@ -156,6 +215,12 @@ const TablePage: React.FC = () => {
pagination={{ position: ['bottomRight'] }}
/>
</div>
<CreateSampleAttr
onSave={saveHandle}
onCancel={cancelHandle}
visible={createVisible}
curRecord={attrData}
editData={attrData}></CreateSampleAttr>
</div>
);
};


Chargement…
Annuler
Enregistrer