目录
需求 - 要实现的效果
父组件中 info 数据示例
const info = {
itemMap: {
警告: [
{
total: 28,
cfzl: '1',
cfzlView: '警告',
wfxl: '12',
wfxlView: '超速行驶',
jtfs: 'B11',
jtfsView: '重型栏板半挂车'
},
{
total: 3,
cfzl: '1',
cfzlView: '警告',
wfxl: '17',
wfxlView: '未低速通过',
jtfs: 'B11',
jtfsView: '重型栏板半挂车'
},
{
total: 6,
cfzl: '1',
cfzlView: '警告',
wfxl: '26',
wfxlView: '违法停车',
jtfs: 'B11',
jtfsView: '重型栏板半挂车'
},
{
total: 21,
cfzl: '1',
cfzlView: '警告',
wfxl: '28',
wfxlView: '违法装载',
jtfs: 'B11',
jtfsView: '重型栏板半挂车'
},
{
total: 3,
cfzl: '1',
cfzlView: '警告',
wfxl: '49',
wfxlView: '其他影响安全行为',
jtfs: 'B11',
jtfsView: '重型栏板半挂车'
},
{
total: 1,
cfzl: '1',
cfzlView: '警告',
wfxl: '28',
wfxlView: '违法装载',
jtfs: 'B21',
jtfsView: '中型栏板半挂车'
}
],
罚款: [
{
total: 56,
cfzl: '2',
cfzlView: '罚款',
wfxl: '12',
wfxlView: '超速行驶',
jtfs: 'B11',
jtfsView: '重型栏板半挂车'
},
{
total: 6,
cfzl: '2',
cfzlView: '罚款',
wfxl: '17',
wfxlView: '未低速通过',
jtfs: 'B11',
jtfsView: '重型栏板半挂车'
},
{
total: 12,
cfzl: '2',
cfzlView: '罚款',
wfxl: '26',
wfxlView: '违法停车',
jtfs: 'B11',
jtfsView: '重型栏板半挂车'
},
{
total: 42,
cfzl: '2',
cfzlView: '罚款',
wfxl: '28',
wfxlView: '违法装载',
jtfs: 'B11',
jtfsView: '重型栏板半挂车'
},
{
total: 6,
cfzl: '2',
cfzlView: '罚款',
wfxl: '49',
wfxlView: '其他影响安全行为',
jtfs: 'B11',
jtfsView: '重型栏板半挂车'
},
{
total: 2,
cfzl: '2',
cfzlView: '罚款',
wfxl: '28',
wfxlView: '违法装载',
jtfs: 'B21',
jtfsView: '中型栏板半挂车'
}
]
},
columns: [
{
// total: 28,
// cfzl: '1',
// cfzlView: '警告',
// wfxl: '12',
// wfxlView: '超速行驶',
jtfs: 'B11',
jtfsView: '重型栏板半挂车'
},
{
// total: 1,
// cfzl: '1',
// cfzlView: '警告',
// wfxl: '28',
// wfxlView: '违法装载',
jtfs: 'B21',
jtfsView: '中型栏板半挂车'
}
]
}
初始代码
父组件
<!-- info 数据来源 → info 数据示例 -->
<CommonTable :info="info"/>
CommonTable.vue 子组件
<template>
<el-table
:data="tableData"
border
stripe
max-height="400"
size="mini"
:span-method="firstColMergeSpan"
>
<el-table-column align="center" prop="cfzl" label="处罚种类" />
<el-table-column align="center" prop="wfxwfl" label="违法行为分类" />
<el-table-column
v-for="(item, index) in columns"
:key="index"
align="center"
:prop="item.jtfs"
:label="item.jtfsView || '-'"
>
<template slot-scope="{ row, $index }">
<span>{{ row[item.jtfs] | noDataFilter }}</span>
</template>
</el-table-column>
<el-table-column align="center" prop="xj" label="小计" />
</el-table>
</template>
<script>
export default {
name: 'CommonTable',
components: {},
props: {
info: {
type: Object,
required: true
}
},
data() {
return {
spanArr: []
}
},
computed: {
tableData() {
const list = []
Object.entries(this.info?.itemMap || {}).forEach((i) => {
i[1]?.forEach((ii) => {
list.push({
// cfzl: ii.cfzlView,
cfzl: i[0],
wfxwfl: ii.wfxlView,
[ii.jtfs]: ii.total,
jtfs: ii.jtfs
})
})
})
return list.map((item) => {
return {
...item,
xj: this.columnKeyList.reduce((a, b) => {
return a + (item[b] || 0)
}, 0)
}
})
},
columns() {
return this.info.columns || []
},
columnKeyList() {
return this.columns.map((item) => item.jtfs)
}
},
watch: {
info() {
this.xjPosition()
this.firstColMergeCount()
}
},
created() {},
methods: {
// 计算小计行插入位置
xjPosition(Human = this.tableData) {
const doctorMap = {}
for (let i = 0; i < Human.length; i++) {
// 找出相同名称的行数
const doctorName = Human[i].cfzl
if (doctorMap[doctorName] !== undefined) {
doctorMap[doctorName].push(i)
} else {
doctorMap[doctorName] = [i]
}
}
const keyArr = []
for (const k in doctorMap) {
// 取出key并倒序,防止正序插入会影响行下标
keyArr.unshift(k)
}
keyArr.forEach((ele, index) => {
const lastIndex = doctorMap[ele][doctorMap[ele].length - 1] // 找出相同名称最后一行插入合计数据
const obj = this.xjRowDataCalc(Human, ele) // 计算出小计行数据
Human.splice(lastIndex + 1, 0, obj) // 插入
})
},
// 小计行计算
xjRowDataCalc(data, name) {
const obj = {
cfzl: name, // 第一列用于合并单元格
wfxwfl: '小计'
}
this.columnKeyList.forEach((key) => {
obj[key] = 0
})
data.forEach((item) => {
// “处罚种类” 相同的加起来
if (item.cfzl === name) {
this.columnKeyList.forEach((key) => {
obj[key] += Number(item[key] || 0)
})
}
})
obj.xj = this.columnKeyList.reduce((a, b) => {
return a + (obj[b] || 0)
}, 0)
return obj
},
// 合并单元格
firstColMergeSpan({ row, column, rowIndex, columnIndex }) {
if (columnIndex === 0) {
const _row = this.spanArr[rowIndex]
const _col = _row > 0 ? 1 : 0
return {
rowspan: _row,
colspan: _col
}
}
},
// 计算要合并的单元格
firstColMergeCount() {
let contactDot = 0
this.spanArr = []
this.tableData.forEach((item, index) => {
item.index = index
if (index === 0) {
this.spanArr.push(1)
} else {
// 根据相同 “处罚种类” 来合并
if (item.cfzl === this.tableData[index - 1].cfzl) {
this.spanArr[contactDot] += 1
this.spanArr.push(0)
} else {
contactDot = index
this.spanArr.push(1)
}
}
})
}
}
}
</script>
<style lang='scss' scoped>
</style>
代码升级(可供多个表格使用)
图1
图2
图3
图4
根据接口返回数据不同(数据格式一致,只是部分字段名不一致),向子组件传入不同的字段名。
CommonTable.vue 子组件
<template>
<el-table
:data="tableData"
border
stripe
max-height="400"
size="mini"
:span-method="firstColMergeSpan"
>
<!-- 第一列 -->
<el-table-column align="center" :prop="oneColPropField" :label="oneColLabelField" />
<!-- 第二列 -->
<el-table-column align="center" :prop="twoColPropField" :label="twoColLabelField" />
<!-- 其他数量列 -->
<el-table-column
v-for="(item, index) in columns"
:key="index"
align="center"
:prop="item[countColPropsField]"
:label="item[countColLabelField] || '-'"
>
<template slot-scope="{ row, $index }">
<span>{{ row[item[countColPropsField]] | noDataFilter }}</span>
</template>
</el-table-column>
<!-- “小计”列 -->
<el-table-column align="center" prop="xj" label="小计" />
</el-table>
</template>
<script>
export default {
name: 'CommonTable',
components: {},
props: {
info: {
type: Object,
required: true
},
// 自定义表格列
selfColumns: {
type: Array,
default: () => []
},
// 表格列非自定义时(接口获取)列字段名
columnKeyField: {
type: String,
default: 'jtfs'
},
// 第一列字段名
oneColPropField: {
type: String,
required: true
},
// 第一列展示header文本
oneColLabelField: {
type: String,
required: true
},
// 第一列数据来源字段名
oneColDataField: {
type: String,
required: true
},
// 第二列字段名
twoColPropField: {
type: String,
required: true
},
// 第二列展示header文本
twoColLabelField: {
type: String,
required: true
},
// 第二列数据来源字段名
twoColDataField: {
type: String,
required: true
},
// 其他数量 列 字段名
countColPropsField: {
type: String,
default: 'jtfs'
},
// 其他数量 列 展示header文本字段名
countColLabelField: {
type: String,
default: 'jtfsView'
},
// 其他数量 列 数据来源字段名
countColDataField: {
type: String,
default: 'total'
}
},
data() {
return {
spanArr: []
}
},
computed: {
// 表格数据处理
tableData() {
const list = []
Object.entries(this.info?.itemMap || {}).forEach((i) => {
i[1]?.forEach((ii) => {
list.push({
// [this.oneColPropField]: ii[this.oneColDataField] // 第一列数据(第一列数据部分表格 ii 中无第一列数据,所以使用↓↓↓下一行代码 i[0] 取第一列数据)
[this.oneColPropField]: i[0], // 第一列数据
[this.twoColPropField]: ii[this.twoColDataField], // 第二列数据
[ii[this.countColPropsField]]: ii[this.countColDataField] // 其他数量列数据
// jtfs: ii.jtfs
})
})
})
return list.map((item) => {
return {
...item,
// 计算小计数量
xj: this.columnKeyList.reduce((a, b) => {
return a + (item[b] || 0)
}, 0)
}
})
},
columns() {
/**
* 表格列获取
* 父组件有传表格列 selfColumns 就用 selfColumns
* 否则用接口获的表格列
*/
return (this.selfColumns.length && this.selfColumns) || this.info.columns || []
},
columnKeyList() {
// 获取表格每列字段名组成数组
return this.columns.map((item) => item[this.columnKeyField])
}
},
watch: {
info() {
this.xjPosition()
this.oneColMergeCount()
}
},
created() {},
methods: {
// 计算小计行插入位置
xjPosition(Human = this.tableData) {
const doctorMap = {}
for (let i = 0; i < Human.length; i++) {
// 找出相同名称的行数
const doctorName = Human[i][this.oneColPropField]
if (doctorMap[doctorName] !== undefined) {
doctorMap[doctorName].push(i)
} else {
doctorMap[doctorName] = [i]
}
}
const keyArr = []
for (const k in doctorMap) {
// 取出key并倒序,防止正序插入会影响行下标
keyArr.unshift(k)
}
keyArr.forEach((ele, index) => {
const lastIndex = doctorMap[ele][doctorMap[ele].length - 1] // 找出相同名称最后一行插入合计数据
const obj = this.xjRowDataCalc(Human, ele) // 计算出小计行数据
Human.splice(lastIndex + 1, 0, obj) // 插入
})
},
// 小计行数据计算
xjRowDataCalc(data, name) {
const obj = {
[this.oneColPropField]: name, // 第一列用于合并单元格
[this.twoColPropField]: '小计' // 第二列数据
}
this.columnKeyList.forEach((key) => {
obj[key] = 0 // 其他书两列数据
})
data.forEach((item) => {
// 第一列 oneColPropField 数据相同的加起来
if (item[this.oneColPropField] === name) {
this.columnKeyList.forEach((key) => {
obj[key] += Number(item[key] || 0)
})
}
})
// 小计列数据总和(小计行和小计列交汇处数据)
obj.xj = this.columnKeyList.reduce((a, b) => {
return a + (obj[b] || 0)
}, 0)
return obj
},
// 合并单元格
firstColMergeSpan({ row, column, rowIndex, columnIndex }) {
if (columnIndex === 0) {
const _row = this.spanArr[rowIndex]
const _col = _row > 0 ? 1 : 0
return {
rowspan: _row,
colspan: _col
}
}
},
// 计算要合并的单元格
oneColMergeCount() {
let contactDot = 0
this.spanArr = []
this.tableData.forEach((item, index) => {
item.index = index
if (index === 0) {
this.spanArr.push(1)
} else {
// 第一列相同的合并
if (item[this.oneColPropField] === this.tableData[index - 1][this.oneColPropField]) {
this.spanArr[contactDot] += 1
this.spanArr.push(0)
} else {
contactDot = index
this.spanArr.push(1)
}
}
})
}
}
}
</script>
<style lang='scss' scoped>
</style>
使用子组件1 - 父组件 - 图1~图3使用
<!-- info 数据来源 → info 数据示例 -->
<CommonTable
:info="info"
one-col-prop-field="cfzl"
one-col-label-field="处罚种类"
one-col-data-field="cfzlView"
two-col-prop-field="wfxwfl"
two-col-label-field="违法行为分类"
two-col-data-field="wfxlView"
/>
效果展示
使用子组件2 - 父组件 - 图4使用
<CommonTable
:info="info"
one-col-prop-field="cfzl"
one-col-label-field="处罚种类"
one-col-data-field="cfzlView"
two-col-prop-field="wfxwfl"
two-col-label-field="违法行为分类"
two-col-data-field="wfxlView"
column-key-field="timenum"
count-col-props-field="timenum"
count-col-label-field="label"
:self-columns="columns"
/>
<script>
export default {
data() {
return {
columns: [...Array(24).keys()].map((item) => {
return {
timenum: `${item}`,
label: `${item}-${item + 1}`
}
}),
info: {
itemMap: {
警告: [
{
timenum: 17,
total: 9,
cfzl: '1',
cfzlView: '警告',
wfxl: '69',
wfxlView: '其他影响安全行为',
jtfs: null,
jtfsView: null
},
{
timenum: 17,
total: 3,
cfzl: '1',
cfzlView: '警告',
wfxl: '58',
wfxlView: '违法上道路行驶',
jtfs: null,
jtfsView: null
}
]
},
columns: []
}
}
}
}
</script>
效果展示
注意
- 使用子组件1 和 使用子组件2 中 info 数据不同
【代码优化 - 解决bug】数据重复问题
解决数据重复问题
CommonTable 组件
<template>
<el-table
:data="tableData"
border
stripe
max-height="400"
size="mini"
:span-method="firstColMergeSpan"
>
<!-- 第一列 -->
<el-table-column align="center" :prop="oneColPropField" :label="oneColLabelField" />
<!-- 第二列 -->
<el-table-column align="center" :prop="twoColPropField" :label="twoColLabelField" />
<!-- 其他数量列 -->
<el-table-column
v-for="(item, index) in columns"
:key="index"
align="center"
:prop="item[countColPropsField]"
:label="item[countColLabelField] || '-'"
>
<template slot-scope="{ row, $index }">
<span>{{ row[item[countColPropsField]] | noDataFilter }}</span>
</template>
</el-table-column>
<!-- “小计”列 -->
<el-table-column align="center" prop="xj" label="小计" />
</el-table>
</template>
<script>
export default {
name: 'CommonTable',
components: {},
props: {
info: {
type: Object,
required: true
},
// 自定义表格列
selfColumns: {
type: Array,
default: () => []
},
// 表格列非自定义时(接口获取)列字段名
columnKeyField: {
type: String,
default: 'jtfs'
},
// 第一列字段名
oneColPropField: {
type: String,
required: true
},
// 第一列展示header文本
oneColLabelField: {
type: String,
required: true
},
// 第一列数据来源字段名
oneColDataField: {
type: String,
required: true
},
// 第二列字段名
twoColPropField: {
type: String,
required: true
},
// 第二列展示header文本
twoColLabelField: {
type: String,
required: true
},
// 第二列数据来源字段名
twoColDataField: {
type: String,
required: true
},
// 其他数量 列 字段名
countColPropsField: {
type: String,
default: 'jtfs'
},
// 其他数量 列 展示header文本字段名
countColLabelField: {
type: String,
default: 'jtfsView'
},
// 其他数量 列 数据来源字段名
countColDataField: {
type: String,
default: 'total'
}
},
data() {
return {
spanArr: []
}
},
computed: {
// 表格数据处理
tableData() {
const list = []
Object.entries(this.info?.itemMap || {}).forEach((i) => {
i[1]?.forEach((ii) => {
/** ** 解决数据重复问题 start ****/
const listDataIndex = list.findIndex(
(listItem) => listItem[this.twoColPropField] === ii[this.twoColDataField]
)
// 判断即将要 push 进 list 的数据 是否与 list 中已有数据的第二列数据有重复,有重复的话,就在 list 中重复的第二列数据的行数据中只添加当前数据为“其他数量列数据”
if (listDataIndex !== -1) {
list[listDataIndex][ii[this.countColPropsField]] = ii[this.countColDataField]
return
}
/** ** 解决数据重复问题 end ****/
list.push({
// [this.oneColPropField]: ii[this.oneColDataField] // 第一列数据(第一列数据部分表格 ii 中无第一列数据,所以使用↓↓↓下一行代码 i[0] 取第一列数据)
[this.oneColPropField]: i[0], // 第一列数据
[this.twoColPropField]: ii[this.twoColDataField], // 第二列数据
[ii[this.countColPropsField]]: ii[this.countColDataField] // 其他数量列数据
// jtfs: ii.jtfs
})
})
})
return list.map((item) => {
return {
...item,
// 计算小计数量
xj: this.columnKeyList.reduce((a, b) => {
return a + (item[b] || 0)
}, 0)
}
})
},
columns() {
/**
* 表格列获取
* 父组件有传表格列 selfColumns 就用 selfColumns
* 否则用接口获的表格列
*/
return (this.selfColumns.length && this.selfColumns) || this.info.columns || []
},
columnKeyList() {
// 获取表格每列字段名组成数组
return this.columns.map((item) => item[this.columnKeyField])
}
},
watch: {
info() {
this.xjPosition()
this.oneColMergeCount()
}
},
created() {},
methods: {
// 计算小计行插入位置
xjPosition(Human = this.tableData) {
const doctorMap = {}
for (let i = 0; i < Human.length; i++) {
// 找出相同名称的行数
const doctorName = Human[i][this.oneColPropField]
if (doctorMap[doctorName] !== undefined) {
doctorMap[doctorName].push(i)
} else {
doctorMap[doctorName] = [i]
}
}
const keyArr = []
for (const k in doctorMap) {
// 取出key并倒序,防止正序插入会影响行下标
keyArr.unshift(k)
}
keyArr.forEach((ele, index) => {
const lastIndex = doctorMap[ele][doctorMap[ele].length - 1] // 找出相同名称最后一行插入合计数据
const obj = this.xjRowDataCalc(Human, ele) // 计算出小计行数据
Human.splice(lastIndex + 1, 0, obj) // 插入
})
},
// 小计行数据计算
xjRowDataCalc(data, name) {
const obj = {
[this.oneColPropField]: name, // 第一列用于合并单元格
[this.twoColPropField]: '小计' // 第二列数据
}
this.columnKeyList.forEach((key) => {
obj[key] = 0 // 其他书两列数据
})
data.forEach((item) => {
// 第一列 oneColPropField 数据相同的加起来
if (item[this.oneColPropField] === name) {
this.columnKeyList.forEach((key) => {
obj[key] += Number(item[key] || 0)
})
}
})
// 小计列数据总和(小计行和小计列交汇处数据)
obj.xj = this.columnKeyList.reduce((a, b) => {
return a + (obj[b] || 0)
}, 0)
return obj
},
// 合并单元格
firstColMergeSpan({ row, column, rowIndex, columnIndex }) {
if (columnIndex === 0) {
const _row = this.spanArr[rowIndex]
const _col = _row > 0 ? 1 : 0
return {
rowspan: _row,
colspan: _col
}
}
},
// 计算要合并的单元格
oneColMergeCount() {
let contactDot = 0
this.spanArr = []
this.tableData.forEach((item, index) => {
item.index = index
if (index === 0) {
this.spanArr.push(1)
} else {
// 第一列相同的合并
if (item[this.oneColPropField] === this.tableData[index - 1][this.oneColPropField]) {
this.spanArr[contactDot] += 1
this.spanArr.push(0)
} else {
contactDot = index
this.spanArr.push(1)
}
}
})
}
}
}
</script>
<style lang='scss' scoped>
</style>
【代码优化 - bug 解决】 tableData 缺少部分数据
- 【问题】数据处理不对 - 缺少部分数据
- 【原因】 现有数据中第一列数据重复、其他数据列重复情况未考虑,导致
其他数据列数据被替换
/非同一第一列数据而同其他数据列数据相同时数据累加
数据处理代码优化前 - 变量命名优化
【变量命名优化】
i
重命名为itemMapKeyValueArray
ii
重命名为itemMapListItem
list[listDataIndex]
命名变量为listCurrent
CommonTable 组件
- 变量命名优化
computed: {
// 表格数据处理
tableData() {
const list = []
Object.entries(this.info?.itemMap || {}).forEach((itemMapKeyValueArray) => {
itemMapKeyValueArray[1]?.forEach((itemMapListItem) => {
/** ** 解决数据重复问题 start ****/
const listDataIndex = list.findIndex(
(listItem) => listItem[this.twoColPropField] === itemMapListItem[this.twoColDataField]
)
// 判断即将要 push 进 list 的数据 是否与 list 中已有数据的第二列数据有重复,有重复的话,就在 list 中重复的第二列数据的行数据中只添加当前数据为“其他数量列数据”
if (listDataIndex !== -1) {
const listCurrent = list[listDataIndex]
listCurrent[itemMapListItem[this.countColPropsField]] =
itemMapListItem[this.countColDataField]
return
}
/** ** 解决数据重复问题 end ****/
list.push({
[this.oneColPropField]: itemMapKeyValueArray[0], // 第一列数据
[this.twoColPropField]: itemMapListItem[this.twoColDataField], // 第二列数据
[itemMapListItem[this.countColPropsField]]: itemMapListItem[this.countColDataField] // 其他数量列数据
})
})
})
return list.map((item) => {
return {
...item,
// 计算小计数量
xj: this.columnKeyList.reduce((a, b) => {
return a + (item[b] || 0)
}, 0)
}
})
},
// ...
},
代码优化
CommonTable 组件
- tableData 数据优化 只摘出相关代码,未摘出代码不变
computed: {
// 表格数据处理
tableData() {
const list = []
Object.entries(this.info?.itemMap || {}).forEach((itemMapKeyValueArray) => {
itemMapKeyValueArray[1]?.forEach((itemMapListItem) => {
/** ** 解决数据重复问题 start ****/
const listDataIndex = list.findIndex(
(listItem) => listItem[this.twoColPropField] === itemMapListItem[this.twoColDataField]
)
// 判断即将要 push 进 list 的数据 是否与 list 中已有数据的第二列数据有重复,有重复的话,就在 list 中重复的第二列数据的行数据中只添加当前数据为“其他数量列数据”
if (listDataIndex !== -1) {
const listCurrent = list[listDataIndex]
/** ** 解决数据缺失问题 start ****/
/**
* 现有 list 表格数据中当前数据 - 有无即将处理数据的 “其他数量列数据”
* 没有:现有 list 表格数据中当前数据添加 “其他数量列数据”
* 有:判断 现有 list 表格数据中当前数据 的 oneColDataField 是否和即将处理的数据中 oneColDataField 一致
* 一致:说明现有 list 表格数据 已经存在 “相同第一列数据 && 相同其他数据列字段” --> 数据处理:其他数据列的数据叠加即将处理数据的其他数据列数据
* 不一致:说明现有 list 数据中不存在 “第一列数据和即将处理数据的第一列数据相同 && 其他数据列字段和即将处理数据的其他数据列相同” 的数据 --> 数据处理:向 list 添加新一条数据
*/
if (listCurrent[itemMapListItem[this.countColPropsField]]) {
// 【判断】现有 list 表格数据中当前数据 - 【有】即将处理数据的 “其他数量列数据”
if (
listCurrent[this.oneColDataField] ===
`${itemMapListItem[this.oneColDataField]}-${
itemMapListItem[this.oneColDataField] + 1
}`
) {
// 【判断】 现有 list 表格数据中当前数据 的 oneColDataField 和即将处理的数据中 oneColDataField 【一致】
// 一致:说明现有 list 表格数据 已经存在 “相同第一列数据 && 相同其他数据列字段” --> 数据处理:其他数据列的数据叠加即将处理数据的其他数据列数据
listCurrent[itemMapListItem[this.countColPropsField]] +=
itemMapListItem[this.countColDataField]
} else {
// 【判断】 现有 list 表格数据中当前数据 的 oneColDataField 和即将处理的数据中 oneColDataField 【不一致】
// 不一致:说明现有 list 数据中不存在 “第一列数据和即将处理数据的第一列数据相同 && 其他数据列字段和即将处理数据的其他数据列相同” 的数据 --> 数据处理:向 list 添加新一条数据
list.push({
[this.oneColPropField]: itemMapKeyValueArray[0],
[this.twoColPropField]: itemMapListItem[this.twoColDataField],
[itemMapListItem[this.countColPropsField]]:
itemMapListItem[this.countColDataField]
})
}
/** ** 解决数据缺失问题 end ****/
} else {
// 【判断】现有 list 表格数据中当前数据 - 【没有】即将处理数据的 “其他数量列数据”
// 现有 list 表格数据中当前数据添加 “其他数量列数据”
listCurrent[itemMapListItem[this.countColPropsField]] =
itemMapListItem[this.countColDataField]
}
return
}
/** ** 解决数据重复问题 end ****/
list.push({
[this.oneColPropField]: itemMapKeyValueArray[0], // 第一列数据
[this.twoColPropField]: itemMapListItem[this.twoColDataField], // 第二列数据
[itemMapListItem[this.countColPropsField]]: itemMapListItem[this.countColDataField] // 其他数量列数据
})
})
})
return list.map((item) => {
return {
...item,
// 计算小计数量
xj: this.columnKeyList.reduce((a, b) => {
return a + (item[b] || 0)
}, 0)
}
})
}
// ...
},
问题解决图
【注】相关组件使用、数据源
相关组件使用
违法Table使用 CommonTable 组件
<template>
<CommonTable
:info="info"
one-col-prop-field="timenum"
one-col-label-field="时段"
one-col-data-field="timenum"
two-col-prop-field="cfzl"
two-col-label-field="处罚种类"
two-col-data-field="cfzlView"
/>
</template>
<script>
import CommonTable from './CommonTable'
export default {
name: 'JdcIllegalTable',
components: {
CommonTable
},
props: {
info: {
type: Object,
required: true
}
}
}
</script>
<style lang='scss' scoped></style>
违法Table使用 CommonTable 组件
<template>
<CommonTable
:info="info"
one-col-prop-field="timenum"
one-col-label-field="时段"
one-col-data-field="timenum"
two-col-prop-field="wfxwfl"
two-col-label-field="违法行为分类"
two-col-data-field="wfxlView"
/>
</template>
<script>
import CommonTable from './CommonTable'
export default {
name: 'JdcPunishTable',
components: {
CommonTable
},
props: {
info: {
type: Object,
required: true
}
}
}
</script>
<style lang='scss' scoped>
</style>
数据源
违法Table数据源
this.jdcIllegalInfo = {
itemMap: {
'17-18': [
{
timenum: 17,
total: 61,
cfzl: '1',
cfzlView: '警告',
jtfs: 'B11',
jtfsView: '重型栏板半挂车'
},
{
timenum: 17,
total: 1,
cfzl: '1',
cfzlView: '警告',
jtfs: 'B21',
jtfsView: '中型栏板半挂车'
}
],
'18-19': [
{
timenum: 18,
total: 122,
cfzl: '1',
cfzlView: '处罚',
jtfs: 'B11',
jtfsView: '重型栏板半挂车'
},
{
timenum: 18,
total: 2,
cfzl: '1',
cfzlView: '处罚',
jtfs: 'B21',
jtfsView: '中型栏板半挂车'
}
],
'19-20': [
{
timenum: 19,
total: 50,
cfzl: '1',
cfzlView: '警告',
jtfs: 'B11',
jtfsView: '重型栏板半挂车'
},
{
timenum: 19,
total: 50,
cfzl: '1',
cfzlView: '处罚',
jtfs: 'B21',
jtfsView: '中型栏板半挂车'
},
{
timenum: 19,
total: 25,
cfzl: '1',
cfzlView: '罚款',
jtfs: 'B21',
jtfsView: '中型栏板半挂车'
}
]
},
columns: [
{
timenum: 17,
total: 61,
cfzl: '1',
cfzlView: '警告',
jtfs: 'B11',
jtfsView: '重型栏板半挂车'
},
{
timenum: 17,
total: 1,
cfzl: '1',
cfzlView: '警告',
jtfs: 'B21',
jtfsView: '中型栏板半挂车'
}
]
}
处罚Table数据源
this.jdcPunishInfo = {
itemMap: {
'17-18': [
{
timenum: 17,
total: 6,
wfxl: '26',
wfxlView: '违法停车',
jtfs: 'B11',
jtfsView: '重型栏板半挂车'
},
{
timenum: 17,
total: 21,
wfxl: '28',
wfxlView: '违法装载',
jtfs: 'B11',
jtfsView: '重型栏板半挂车'
},
{
timenum: 17,
total: 3,
wfxl: '17',
wfxlView: '未低速通过',
jtfs: 'B11',
jtfsView: '重型栏板半挂车'
},
{
timenum: 17,
total: 28,
wfxl: '12',
wfxlView: '超速行驶',
jtfs: 'B11',
jtfsView: '重型栏板半挂车'
},
{
timenum: 17,
total: 3,
wfxl: '49',
wfxlView: '其他影响安全行为',
jtfs: 'B11',
jtfsView: '重型栏板半挂车'
},
{
timenum: 17,
total: 1,
wfxl: '28',
wfxlView: '违法装载',
jtfs: 'B21',
jtfsView: '中型栏板半挂车'
}
],
'18-19': [
{
timenum: 18,
total: 6 * 2,
wfxl: '26',
wfxlView: '违法停车',
jtfs: 'B11',
jtfsView: '重型栏板半挂车'
},
{
timenum: 18,
total: 21 * 2,
wfxl: '28',
wfxlView: '违法装载',
jtfs: 'B11',
jtfsView: '重型栏板半挂车'
},
{
timenum: 18,
total: 3 * 2,
wfxl: '17',
wfxlView: '未低速通过',
jtfs: 'B11',
jtfsView: '重型栏板半挂车'
},
{
timenum: 18,
total: 28 * 2,
wfxl: '12',
wfxlView: '超速行驶',
jtfs: 'B11',
jtfsView: '重型栏板半挂车'
},
{
timenum: 18,
total: 3 * 2,
wfxl: '49',
wfxlView: '其他影响安全行为',
jtfs: 'B11',
jtfsView: '重型栏板半挂车'
},
{
timenum: 18,
total: 1 * 2,
wfxl: '28',
wfxlView: '违法装载',
jtfs: 'B21',
jtfsView: '中型栏板半挂车'
}
],
'19-20': [
{
timenum: 19,
total: 6 * 3,
wfxl: '26',
wfxlView: '违法停车',
jtfs: 'B11',
jtfsView: '重型栏板半挂车'
},
{
timenum: 19,
total: 21 * 3,
wfxl: '28',
wfxlView: '违法装载',
jtfs: 'B11',
jtfsView: '重型栏板半挂车'
},
{
timenum: 19,
total: 3 * 3,
wfxl: '17',
wfxlView: '未低速通过',
jtfs: 'B11',
jtfsView: '重型栏板半挂车'
},
{
timenum: 19,
total: 28 * 3,
wfxl: '12',
wfxlView: '超速行驶',
jtfs: 'B11',
jtfsView: '重型栏板半挂车'
},
{
timenum: 19,
total: 3 * 3,
wfxl: '49',
wfxlView: '其他影响安全行为',
jtfs: 'B11',
jtfsView: '重型栏板半挂车'
},
{
timenum: 19,
total: 1 * 3,
wfxl: '28',
wfxlView: '违法装载',
jtfs: 'B21',
jtfsView: '中型栏板半挂车'
}
]
},
columns: [
{
timenum: 17,
total: 6,
wfxl: '26',
wfxlView: '违法停车',
jtfs: 'B11',
jtfsView: '重型栏板半挂车'
},
{
timenum: 17,
total: 1,
wfxl: '28',
wfxlView: '违法装载',
jtfs: 'B21',
jtfsView: '中型栏板半挂车'
}
]
}
【注】计算小计插入位置等部分方法参考文章 https://blog.csdn.net/seeeeeeeeeee/article/details/133122424