feat(wms): 新增报表透视表组件并优化操作状态显示
refactor: 替换select为tag显示操作状态 feat: 添加今天按钮到时间范围选择器 fix: 移除调试用的console.log style: 调整按钮间距样式 feat: 新增厂家材质透视表和宽度厚度统计表 refactor: 优化导出功能去除orderBy参数 feat: 添加表面处理等查询条件 feat: 在coilTable中添加settings插槽 feat: 使用下拉菜单整合报表操作按钮
This commit is contained in:
@@ -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">
|
||||
|
||||
195
klp-ui/src/views/wms/report/components/crossTable/index.vue
Normal file
195
klp-ui/src/views/wms/report/components/crossTable/index.vue
Normal 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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user