表格树动态计算
# 效果图

# 逻辑思考整理
- 在考虑实现这个问题时,首先要和后端约定好数据格式,确定表格树的结构大致为什么样子。例如:
// An highlighted block
const tableData = ref([
{
id: '1',
name: 'tem1',
startTime: '',
endTime: '',
val: 0,
parentId: '0',
children: [
{
id: '1-1',
parentId: '1',
name: 'tem1-1',
startTime: '',
endTime: '',
val: 0,
children: [
{
id: '1-1-1',
parentId: '1-1',
name: 'tem1-1-1',
startTime: '',
endTime: '',
val: 0,
children: []
},
{
id: '1-1-2',
parentId: '1-1',
name: 'tem1-1-2',
startTime: '',
endTime: '',
val: 0,
children: []
}
]
},
{
id: '1-2',
parentId: '1',
name: 'tem1-2',
startTime: '',
endTime: '',
val: 0,
children: []
}
]
},
{
id: '2',
parentId: '0',
name: 'tem2',
startTime: '',
endTime: '',
val: 0,
children: []
}
])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
- 然后开始思考如何根据id等标识去计算父级数据:
// 动态计算逻辑
const calculate = (row, rowKey) => {
// 数值计算(子集求和)
if (rowKey === 'val') {
const valArr = row.children.map(el => Number(el.val))
row.val = valArr.reduce((acc, cur) => acc + cur, 0)
}
// 开始日期
if (rowKey === 'startTime') {
if (row.children.length === 1) {
row.startTime = row.children[0].startTime
} else {
for (let item of row.children) {
if (!item.startTime) {
return
}
}
const startTimeArr = row.children.map(el => new Date(el.startTime).getTime())
if (startTimeArr.length) {
row.startTime = startTimeArr.reduce((acc, cur) => acc < cur ? acc : cur)
}
}
}
// 结束日期
if (rowKey === 'endTime') {
if (row.children.length === 1) {
row.endTime = row.children[0].endTime
} else {
for (let item of row.children) {
if (!item.endTime) {
return
}
}
const endTimeArr = row.children.map(el => new Date(el.endTime).getTime())
if (endTimeArr.length) {
row.endTime = endTimeArr.reduce((acc, cur) => acc > cur ? acc : cur)
}
}
}
// 如果不是最顶级父级树,要继续递归查找父级并计算
if (row.parentId !== '0') {
setParentData(row, rowKey)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
- 然后再计算出父级后,考虑是否为顶级树,如果不是,需要递归处理:
// 递归设置父级数据(父ID为0时,代表当前内容为最顶级树)
const setParentData = (row, rowKey) => {
const traverseTree = (tree) => {
if (row.parentId == tree.id) {
calculate(tree, rowKey)
return
}
if (tree.children.length) {
for (let el of tree.children) {
if (el.id == row.parentId) {
calculate(el, rowKey)
return
} else {
traverseTree(el)
}
}
}
}
// 父ID不为0,执行递归
if (row.parentId !== '0') {
tableData.value.forEach(tree => {
traverseTree(tree)
})
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
- 源码(KekeEditor.vue)
<template>
<div>
<h2>表格树动态计算日期和值</h2>
<el-table style="width: 800px;" :data="tableData" row-key="id" :tree-props="{ children: 'children' }"
default-expand-all>
<el-table-column prop="name" label="模板名称" />
<el-table-column prop="startTime" label="开始日期">
<template #default="{ row }">
<el-date-picker v-model="row.startTime" type="date" placeholder="选择日期" @change="calculate(row, 'startTime')" />
</template>
</el-table-column>
<el-table-column prop="endTime" label="结束日期">
<template #default="{ row }">
<el-date-picker v-model="row.endTime" type="date" placeholder="选择日期" @change="calculate(row, 'endTime')" />
</template>
</el-table-column>
<el-table-column prop="val" label="数值">
<template #default="{ row }">
<el-input v-model="row.val" @change="setParentData(row, 'val')"></el-input>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script setup>
import { ref } from 'vue'
// 表格数据
const tableData = ref([
{
id: '1',
name: 'tem1',
startTime: '',
endTime: '',
val: 0,
parentId: '0',
children: [
{
id: '1-1',
parentId: '1',
name: 'tem1-1',
startTime: '',
endTime: '',
val: 0,
children: [
{
id: '1-1-1',
parentId: '1-1',
name: 'tem1-1-1',
startTime: '',
endTime: '',
val: 0,
children: []
},
{
id: '1-1-2',
parentId: '1-1',
name: 'tem1-1-2',
startTime: '',
endTime: '',
val: 0,
children: []
}
]
},
{
id: '1-2',
parentId: '1',
name: 'tem1-2',
startTime: '',
endTime: '',
val: 0,
children: []
}
]
},
{
id: '2',
parentId: '0',
name: 'tem2',
startTime: '',
endTime: '',
val: 0,
children: []
}
])
// 动态计算逻辑
const calculate = (row, rowKey) => {
// 数值计算(子集求和)
if (rowKey === 'val') {
const valArr = row.children.map(el => Number(el.val))
row.val = valArr.reduce((acc, cur) => acc + cur, 0)
}
// 开始日期
if (rowKey === 'startTime') {
if (row.children.length === 1) {
row.startTime = row.children[0].startTime
} else {
for (let item of row.children) {
if (!item.startTime) {
return
}
}
const startTimeArr = row.children.map(el => new Date(el.startTime).getTime())
if (startTimeArr.length) {
row.startTime = startTimeArr.reduce((acc, cur) => acc < cur ? acc : cur)
}
}
}
// 结束日期
if (rowKey === 'endTime') {
if (row.children.length === 1) {
row.endTime = row.children[0].endTime
} else {
for (let item of row.children) {
if (!item.endTime) {
return
}
}
const endTimeArr = row.children.map(el => new Date(el.endTime).getTime())
if (endTimeArr.length) {
row.endTime = endTimeArr.reduce((acc, cur) => acc > cur ? acc : cur)
}
}
}
// 如果不是最顶级父级树,要继续递归查找父级并计算
if (row.parentId !== '0') {
setParentData(row, rowKey)
}
}
// 递归设置父级数据(父ID为0时,代表当前内容为最顶级树)
const setParentData = (row, rowKey) => {
const traverseTree = (tree) => {
if (row.parentId == tree.id) {
calculate(tree, rowKey)
return
}
if (tree.children.length) {
for (let el of tree.children) {
if (el.id == row.parentId) {
calculate(el, rowKey)
return
} else {
traverseTree(el)
}
}
}
}
// 父ID不为0,执行递归
if (row.parentId !== '0') {
tableData.value.forEach(tree => {
traverseTree(tree)
})
}
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
这里的难点在于何时用递归;递归时,在什么时候跳出递归并调用计算。这个捋清楚,此需求就不难解决。
上次更新: 2024/04/14, 17:23:46