电子请购单优化 库存明细页面

This commit is contained in:
jhd
2026-07-01 09:11:15 +08:00
parent 272b29e54a
commit ad650b9a57
6 changed files with 434 additions and 21 deletions

View File

@@ -0,0 +1,235 @@
<template>
<div class="app-container" v-loading="loading">
<!-- 标题 -->
<h2 style="text-align: center; margin-bottom: 16px;">{{ title }}</h2>
<!-- 空数据 -->
<el-empty v-if="!loading && tableData.length === 0" description="暂无数据" />
<!-- 数据表格 -->
<el-table
v-if="tableData.length > 0"
:data="tableData"
border
style="width: 100%"
:header-cell-style="{ background: '#f2f2f2', fontWeight: 600, textAlign: 'center' }"
:cell-style="cellStyle"
size="small"
>
<el-table-column label="业务员" prop="dimension" min-width="100" align="center" />
<el-table-column v-for="cat in CATEGORIES" :key="cat.key" :label="cat.label" align="center">
<el-table-column :label="'A/B'" :prop="cat.key + '_AB'" width="85" align="center">
<template slot-scope="{ row }">{{ formatNum(row[cat.key + '_AB']) }}</template>
</el-table-column>
<el-table-column :label="'C/D/O'" :prop="cat.key + '_CDO'" width="85" align="center">
<template slot-scope="{ row }">{{ formatNum(row[cat.key + '_CDO']) }}</template>
</el-table-column>
</el-table-column>
<el-table-column label="合计" prop="total" width="100" align="center">
<template slot-scope="{ row }">{{ formatNum(row.total) }}</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import { listCoilWithIds } from "@/api/wms/coil";
const CATEGORIES = [
{ key: '冷硬钢卷', label: '冷硬钢卷' },
{ key: '冷轧钢卷', label: '冷轧钢卷' },
{ key: '镀锌钢卷', label: '镀锌钢卷' },
{ key: '镀锌管材', label: '镀锌管材' },
{ key: '花纹板', label: '花纹板' },
{ key: '镀铬', label: '镀铬' },
]
/**
* 品质等级 → 分组映射
* A/B → 'AB', C/D/O → 'CDO', 其他 → null
*/
function getQualityGroup(status) {
if (!status) return null
const s = String(status).toUpperCase().trim()
if (['A', 'B'].includes(s)) return 'AB'
if (['C', 'D', 'O'].includes(s)) return 'CDO'
return null
}
export default {
name: "StockDetail",
dicts: ['wip_pack_saleman'],
data() {
return {
loading: false,
rawList: [],
}
},
computed: {
CATEGORIES: () => CATEGORIES,
title() {
const now = new Date()
return `${now.getMonth()+1}.${now.getDate()}.${String(now.getFullYear()).slice(-2)} 库存明细`
},
tableData() {
if (!this.rawList.length) return []
// ========== 第一层:分类聚合 ==========
// orderData: { [saleName]: { [catKey]: { AB: 0, CDO: 0 } } }
// spotData: { [catKey]: { AB: 0, CDO: 0 } }
const orderData = {}
const spotData = {}
let unmatchedCount = 0
this.rawList.forEach(item => {
// 匹配钢材品类
const cat = CATEGORIES.find(c => item.itemName && item.itemName.includes(c.key))
if (!cat) { unmatchedCount++; return }
// 品质分组
const group = getQualityGroup(item.qualityStatus)
if (!group) { unmatchedCount++; return }
const weight = Number(item.netWeight) || 0
if (Number(item.isRelatedToOrder) === 1 && item.saleName) {
// 订单相关 → 归属业务员
if (!orderData[item.saleName]) orderData[item.saleName] = {}
if (!orderData[item.saleName][cat.key]) orderData[item.saleName][cat.key] = { AB: 0, CDO: 0 }
orderData[item.saleName][cat.key][group] += weight
} else {
// 非订单相关 → 现货
if (!spotData[cat.key]) spotData[cat.key] = { AB: 0, CDO: 0 }
spotData[cat.key][group] += weight
}
})
if (unmatchedCount > 0) {
console.log(`[库存明细] 未匹配品类的钢卷数: ${unmatchedCount}`)
}
// ========== 第二层:组装行数据 ==========
// 工具函数:创建空行
const createRow = (dimension, rowType, remark = '') => {
const row = { dimension, rowType, remark, total: 0 }
CATEGORIES.forEach(c => { row[c.key + '_AB'] = 0; row[c.key + '_CDO'] = 0 })
return row
}
// 2.1 业务员行(按字典 wip_pack_saleman 顺序)
const dictItems = this.dict.type.wip_pack_saleman || []
const personRows = dictItems.map(dictItem => {
const row = createRow(dictItem.label, 'person')
const personData = orderData[dictItem.value] || {}
CATEGORIES.forEach(cat => {
const vals = personData[cat.key] || { AB: 0, CDO: 0 }
row[cat.key + '_AB'] = vals.AB
row[cat.key + '_CDO'] = vals.CDO
row.total += vals.AB + vals.CDO
})
return row
})
// 2.2 订单小计
const makeSubtotal = () => {
const row = createRow('订单小计', 'summary')
CATEGORIES.forEach(cat => {
let ab = 0, cdo = 0
personRows.forEach(r => {
ab += r[cat.key + '_AB'] || 0
cdo += r[cat.key + '_CDO'] || 0
})
row[cat.key + '_AB'] = ab
row[cat.key + '_CDO'] = cdo
row.total += ab + cdo
})
return row
}
// 2.3 现货
const makeSpot = () => {
const row = createRow('现货', 'summary')
CATEGORIES.forEach(cat => {
const vals = spotData[cat.key] || { AB: 0, CDO: 0 }
row[cat.key + '_AB'] = vals.AB
row[cat.key + '_CDO'] = vals.CDO
row.total += vals.AB + vals.CDO
})
return row
}
// 2.4 合计 = 订单小计 + 现货
const makeTotal = () => {
const row = createRow('合计', 'summary')
CATEGORIES.forEach(cat => {
const ab = (orderSubtotal[cat.key + '_AB'] || 0) + (spotRow[cat.key + '_AB'] || 0)
const cdo = (orderSubtotal[cat.key + '_CDO'] || 0) + (spotRow[cat.key + '_CDO'] || 0)
row[cat.key + '_AB'] = ab
row[cat.key + '_CDO'] = cdo
row.total += ab + cdo
})
return row
}
// 2.5 各产品小计
const makeProductSummary = () => {
const row = createRow('各产品小计', 'summary')
CATEGORIES.forEach(cat => {
const merged = (totalRow[cat.key + '_AB'] || 0) + (totalRow[cat.key + '_CDO'] || 0)
row[cat.key + '_AB'] = merged
row[cat.key + '_CDO'] = 0
row.total += merged
})
return row
}
const orderSubtotal = makeSubtotal()
const spotRow = makeSpot()
const totalRow = makeTotal()
return [
...personRows,
orderSubtotal,
spotRow,
totalRow,
makeProductSummary(),
]
},
},
methods: {
formatNum(val) {
if (val === null || val === undefined || isNaN(val)) return '0.00'
return Number(val).toFixed(2)
},
cellStyle({ row }) {
return row.rowType === 'summary' ? { backgroundColor: '#FFFACD' } : {}
},
getList() {
this.loading = true
this.rawList = []
listCoilWithIds({
status: 0,
dataType: 1,
pageSize: 99999,
pageNum: 1,
}).then(res => {
this.rawList = res.rows || []
this.loading = false
}).catch(() => {
this.loading = false
})
},
},
mounted() {
this.getList()
},
}
</script>
<style scoped>
</style>