feat(wms): 新增报表透视表组件并优化操作状态显示

refactor: 替换select为tag显示操作状态
feat: 添加今天按钮到时间范围选择器
fix: 移除调试用的console.log
style: 调整按钮间距样式
feat: 新增厂家材质透视表和宽度厚度统计表
refactor: 优化导出功能去除orderBy参数
feat: 添加表面处理等查询条件
feat: 在coilTable中添加settings插槽
feat: 使用下拉菜单整合报表操作按钮
This commit is contained in:
2026-05-05 14:52:24 +08:00
parent 723ccc9e58
commit fa198181ee
11 changed files with 491 additions and 91 deletions

View File

@@ -22,6 +22,8 @@
<el-pagination layout="total, sizes, prev, pager, next, jumper" :total="total" :page-size.sync="pageSize"
:page-sizes="[10, 20, 50, 100, 200, 500, 1000]" @size-change="handleSizeChange"
@current-change="handleCurrentChange" />
<slot name="settings"></slot>
</div>
<el-table :data="tableData" style="width: 100%" height="calc(100vh - 320px)" border row-key="coilId" :row-class-name="getRowClassName">

View File

@@ -0,0 +1,195 @@
<template>
<el-table :data="tableData" border :style="{ width: '100%', marginBottom: '20px' }">
<el-table-column :prop="config.rowField" :label="config.rowLabel" :width="config.rowWidth" align="center"
fixed></el-table-column>
<template v-for="colVal in columnValues">
<el-table-column :key="'group_' + colVal" :label="colVal + ''" align="center">
<template v-for="summary in config.summaryColumns">
<el-table-column :key="`${colVal}_${summary.prop}`" :label="summary.label" align="center">
<template #default="scope">
<!-- 强制显示值 -->
<span>{{ scope.row[`${summary.prop}_${colVal}`] || '--' }}</span>
</template>
</el-table-column>
</template>
</el-table-column>
</template>
<el-table-column label="合计" align="center">
<template v-for="summary in config.summaryColumns">
<el-table-column :key="'total_' + summary.prop" :prop="'total_' + summary.prop" :label="summary.label"
:width="summary.width" align="center"></el-table-column>
</template>
</el-table-column>
</el-table>
</template>
<script>
export default {
name: 'CrossTable',
props: {
data: {
type: Array,
required: true
},
config: {
type: Object,
required: true
}
},
computed: {
// 第一步:统计数据,生成行、列和单元格统计值
statisticsData() {
const {
rowField,
columnField,
summaryColumns,
formatKey
} = this.config
const formatNum = (num) => {
if (num === null || num === undefined) return null
return parseFloat(num).toFixed(2)
}
const formatKeyFn = formatKey || formatNum
// 数据结构rowValues 行列表colValues 列列表cellStats[row][col][prop] = 统计值
const rowValues = new Set()
const colValues = new Set()
const cellStats = new Map() // Map<rowKey, Map<colKey, { prop: value }>>
// 遍历原始数据,统计每个单元格
this.data.forEach(item => {
const colVal = item[columnField]
const rowVal = item[rowField]
if (colVal != null && colVal !== '' && rowVal != null && rowVal !== '') {
const colKey = formatKeyFn(colVal)
const rowKey = formatKeyFn(rowVal)
rowValues.add(rowKey)
colValues.add(colKey)
// 初始化单元格
if (!cellStats.has(rowKey)) {
cellStats.set(rowKey, new Map())
}
const rowStats = cellStats.get(rowKey)
if (!rowStats.has(colKey)) {
const initial = {}
summaryColumns.forEach(sc => {
initial[sc.prop] = 0
})
rowStats.set(colKey, initial)
}
const cell = rowStats.get(colKey)
// 累计统计值
summaryColumns.forEach(sc => {
if (sc.field === '') {
cell[sc.prop] += 1
} else {
cell[sc.prop] += parseFloat(item[sc.field]) || 0
}
})
}
})
// 排序并返回
return {
rowValues: Array.from(rowValues).map(v => parseFloat(v)).sort((a, b) => a - b).map(v => formatKeyFn(v)),
colValues: Array.from(colValues).map(v => parseFloat(v)).sort((a, b) => a - b).map(v => formatKeyFn(v)),
cellStats
}
},
// 第二步:根据统计数据生成 Element-UI Table 可用的数据
tableData() {
const {
rowField,
summaryColumns,
formatValue
} = this.config
const { rowValues, colValues, cellStats } = this.statisticsData
// 生成每一行数据
const rows = rowValues.map(rowKey => {
const row = { [rowField]: parseFloat(rowKey) }
const rowStats = cellStats.get(rowKey)
const rowTotals = {}
summaryColumns.forEach(sc => {
rowTotals[sc.prop] = 0
})
// 填充每一列的数据
colValues.forEach(colKey => {
const cell = rowStats?.get(colKey) || {}
summaryColumns.forEach(sc => {
const val = cell[sc.prop] || 0
row[`${sc.prop}_${colKey}`] = formatValue ? formatValue(val, sc) : val.toFixed(3)
rowTotals[sc.prop] += val
})
})
// 填充行合计
summaryColumns.forEach(sc => {
row[`total_${sc.prop}`] = formatValue ? formatValue(rowTotals[sc.prop], sc) : rowTotals[sc.prop].toFixed(3)
})
return row
})
// 在 tableData 计算属性中,生成 rows 后添加
console.log('Generated row sample:', rows[0])
console.log('Field names:', Object.keys(rows[0] || {}))
console.log('rows length:', rows.length)
console.log('colValues:', colValues)
console.log('summaryColumns:', summaryColumns)
// 生成合计行
const totalRow = { [rowField]: '合计' }
const grandTotals = {}
summaryColumns.forEach(sc => {
grandTotals[sc.prop] = 0
})
colValues.forEach(colKey => {
const colTotals = {}
summaryColumns.forEach(sc => {
colTotals[sc.prop] = 0
})
rows.forEach(row => {
summaryColumns.forEach(sc => {
const val = parseFloat(row[`${sc.prop}_${colKey}`]) || 0
colTotals[sc.prop] += val
grandTotals[sc.prop] += val
})
})
summaryColumns.forEach(sc => {
totalRow[`${sc.prop}_${colKey}`] = formatValue ? formatValue(colTotals[sc.prop], sc) : colTotals[sc.prop].toFixed(3)
})
})
// 填充总合计
summaryColumns.forEach(sc => {
totalRow[`total_${sc.prop}`] = formatValue ? formatValue(grandTotals[sc.prop], sc) : grandTotals[sc.prop].toFixed(3)
})
console.log(rows, 'rows')
return [...rows, totalRow]
},
columnValues() {
console.log(this.statisticsData.colValues, 'colValues')
return this.statisticsData.colValues
}
}
}
</script>
<style scoped></style>

View File

@@ -0,0 +1,108 @@
<template>
<el-table :data="tableData" border :style="{ width: '100%', marginBottom: '20px' }">
<template v-for="(column, idx) in config.groupFields">
<el-table-column
:key="column"
:prop="column"
:label="config.groupLabels ? config.groupLabels[idx] : column"
:align="config.groupAligns ? config.groupAligns[idx] : 'left'"
>
<template slot-scope="scope">
<span :style="{
paddingLeft: (scope.row.level * 20) + 'px',
fontWeight: scope.row.level === idx ? 'bold' : 'normal'
}">
{{ scope.row[column] }}
</span>
</template>
</el-table-column>
</template>
<template v-for="summary in config.summaryFields">
<el-table-column
:key="summary.prop"
:prop="summary.prop"
:label="summary.label"
:width="summary.width"
:align="summary.align"
></el-table-column>
</template>
</el-table>
</template>
<script>
export default {
name: 'HierarchicalPivot',
props: {
data: {
type: Array,
required: true
},
config: {
type: Object,
required: true
}
},
computed: {
tableData() {
const {
groupFields,
summaryFields,
formatValue
} = this.config
const buildHierarchy = (items, level = 0, parentKey = null) => {
const result = []
const field = groupFields[level]
if (!field) return result
const groupMap = new Map()
items.forEach(item => {
const key = item[field] || '未知'
if (!groupMap.has(key)) {
groupMap.set(key, [])
}
groupMap.get(key).push(item)
})
const sortedKeys = Array.from(groupMap.keys()).sort()
sortedKeys.forEach(key => {
const groupItems = groupMap.get(key)
const groupRow = {
level,
...groupFields.reduce((acc, f, idx) => {
acc[f] = idx === level ? key : ''
return acc
}, {})
}
summaryFields.forEach(sf => {
let val
if (sf.field === '') {
val = groupItems.length
} else {
val = groupItems.reduce((acc, item) => {
return acc + (parseFloat(item[sf.field]) || 0)
}, 0)
}
groupRow[sf.prop] = formatValue ? formatValue(val, sf) : val.toFixed(3)
})
result.push(groupRow)
if (level < groupFields.length - 1) {
const children = buildHierarchy(groupItems, level + 1, key)
result.push(...children)
}
})
return result
}
return buildHierarchy(this.data)
}
}
}
</script>
<style scoped></style>

View File

@@ -21,6 +21,7 @@
</div>
<div class="quick-options">
<el-button size="small" @click="setQuickTime('prevDay')">前一天</el-button>
<el-button size="small" @click="setQuickTime('today')">今天</el-button>
<el-button size="small" @click="setQuickTime('nextDay')">后一天</el-button>
<el-button size="small" @click="setQuickTime('last7Days')">近7天</el-button>
<el-button size="small" @click="setQuickTime('last15Days')">近15天</el-button>
@@ -127,12 +128,15 @@ export default {
switch (type) {
case 'prevDay':
// 在当前时间基础上向前推一天
newStartDate.setDate(newStartDate.getDate() - 1)
newEndDate.setDate(newEndDate.getDate() - 1)
break
case 'today':
const todayNow = new Date()
newStartDate = todayNow
newEndDate = todayNow
break
case 'nextDay':
// 在当前时间基础上向后推一天
newStartDate.setDate(newStartDate.getDate() + 1)
newEndDate.setDate(newEndDate.getDate() + 1)
break
@@ -205,7 +209,11 @@ export default {
}
.quick-options {
display: flex;
gap: 8px;
gap: 2px;
flex-wrap: wrap;
}
.el-button + .el-button {
margin-left: 4px
}
</style>