展示效果
全部代码
import React, { useState } from 'react';
import { Button, InputNumber, Flex, Table, Typography } from 'antd';
import { Item } from 'rc-menu';
// 生成随机数
function generateRandomString(length: number) {
let charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
let values = new Uint32Array(length);
window.crypto.getRandomValues(values);
let str = '';
for (let i = 0; i < length; i++) {
str += charset[values[i] % charset.length];
}
return str;
}
// 定义类型
interface Item {
id?: string;
pId?: string;
unit?: number;
num?: number;
total?: number;
children?: Item[];
}
// 创建于一个类表数据 -- 类似后端返回数据
const originData: Item[] = [];
for (let i = 0; i < 3; i++) {
const a = generateRandomString(20)
originData.push({
id: a,
unit: 0,
num: 0,
total: 0,
});
for (let j = 0; j < 3; j++) {
originData.push({
id: generateRandomString(20),
pId: a,
unit: 0,
num: 0,
total: 0,
})
}
}
// 定义一个空对象等会与数组数据联动 哈希对照表
const hash: Item = {}
// 新创建数组,只选择顶层数据使用
const newOriginData: Item[] = []
// 新创建类型格式对象
class hashObj {
constructor(obj) {
this.id = obj.id || generateRandomString(20);
this.pId = obj.pId;
this._unit = obj.unit || 0;
this._num = obj.num || 0;
this.total = obj.total || 0;
}
get unit() {
return this._unit
};
set unit(value) {
this._unit = value
// 设置单价时调用 汇总函数
this.huizong()
};
get num() {
return this._num
};
set num(value) {
this._num = value;
// 设置数量时调用 汇总函数
this.huizong()
};
huizong = function () {
if (this.children && this.children.length) {
// 存在子级,总价=一级子级的和
this.total = this.children.reduce((a: number, b: Item) => a + b.total, 0)
} else if (this.unit && this.num) {
// 不存在子级,总价= 数量*单价
this.total = this.unit * this.num
} else {
this.total = 0
}
if (this.pId) {
// 存在父级调用父级的汇总函数
hash[this.pId].huizong()
}
}
}
// 遍历数据将数据转化为ID为 key 内容为value
originData.forEach((item: Item) => {
// 对象添加数据内容
hash[item.id] = new hashObj(item)
if (item.pId) {
// 如果有父级给父级添加子级数组
if (hash[item.pId].children) {
hash[item.pId].children.push(hash[item.id])
} else {
hash[item.pId].children = [hash[item.id]]
}
} else {
// 没有父级的顶层数据并且与hash对象利用浅拷贝关联
newOriginData.push(hash[item.id])
}
})
console.log(hash, 'hash')
console.log(originData, 'originData')
console.log(newOriginData, 'newOriginData')
const App: React.FC = () => {
// 响应数据
const [data, setData] = useState(newOriginData);
// 添加子级时
const add = (record: Item[]) => {
// 生成新数据
const obj = new hashObj({ pId: record.id })
// 添加哈希对照表
hash[obj.id] = obj
if (record.children) {
record.children.push(obj)
} else {
record.children = [obj]
}
// 调用汇总会触发父级汇总
obj.huizong();
// 更新数据
setData([...data])
}
// 生成一级数据
const addAll = () => {
const obj = new hashObj({})
hash[obj.id] = obj
data.push(obj)
// 更新数据
setData([...data])
}
// 递归删除hash 对照表中数据
const delHash = (record: Item[]) => {
record.forEach(item => {
if (item.children && item.children.length) {
delHash(item.children)
}
hash[item.id] = undefined;
})
}
// 删除数据
const del = (record: Item[]) => {
let arr = data;
if (record.pId) {
hash[record.pId].children = hash[record.pId].children.filter((item) => {
return item.id !== record.id
});
hash[record.pId].huizong();
hash[record.id] = undefined;
} else {
hash[record.id] = undefined;
arr = data.filter((item) => item.id !== record.id);
}
// 如果有子级,删除hash表中数据
if (record.children && record.children.length) {
delHash(record.children)
}
// 更新数据
setData([...arr])
}
const columns = [
{
title: 'id',
dataIndex: 'id',
width: '20%',
editable: true,
},
{
title: '单价',
dataIndex: 'unit',
width: '20%',
editable: true,
render: (_: any, record: Item) => {
return (
<InputNumber defaultValue={record.unit} style={{ width: '100%' }} onChange={(val: number) => {
// 输入时跟新展示
record.unit = val
setData([...data])
}} />
);
},
},
{
title: '数量',
dataIndex: 'num',
width: '20%',
editable: true,
render: (_: any, record: Item) => {
return (
<InputNumber defaultValue={record.num} style={{ width: '100%' }} onChange={(val: number) => {
// 输入时跟新展示
record.num = val
setData([...data])
}} />
);
},
},
{
title: '总价',
dataIndex: 'total',
width: '20%',
editable: true,
},
{
title: '操作',
width: '20%',
dataIndex: 'operation',
render: (_: any, record: Item) => {
return (
<span>
<Typography.Link onClick={() => add(record)} style={{ marginRight: 8 }}>
新增
</Typography.Link>
<Typography.Link onClick={() => del(record)}>
删除
</Typography.Link>
</span>
);
},
},
];
return (
<div>
<Flex gap="small" wrap="wrap">
<Button onClick={addAll} type="primary">新增</Button>
</Flex>
<Table
bordered
dataSource={data}
columns={columns}
rowKey="id"
pagination={false}
/>
</div>
);
};
export default App;