style:仓库数量汇总报表页优化显示(KPI概览/对比图/网格/合计/安全库存预警)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
caoqianming 2026-07-14 16:03:25 +08:00
parent ee40da3461
commit b301a09cac
1 changed files with 184 additions and 50 deletions

View File

@ -19,61 +19,104 @@
style="margin-right: 12px;" style="margin-right: 12px;"
/> />
<el-button type="primary" icon="el-icon-search" @click="fetchData" :loading="loading">查询</el-button> <el-button type="primary" icon="el-icon-search" @click="fetchData" :loading="loading">查询</el-button>
<span style="margin-left: auto; color: #909399; font-size: 13px;">分组维度: 物料型号(model)</span>
</el-header> </el-header>
<el-main v-loading="loading">
<!-- ds0: 库存量 + 安全库存上下限 --> <el-main id="elMain" v-loading="loading">
<el-card shadow="never" class="rpt-card"> <!-- KPI 概览 -->
<template #header>库存量 安全库存上下限(个人设定)</template> <el-row :gutter="15" style="margin-bottom: 15px;">
<el-table :data="ds0" border stripe size="small" :span-method="ds0Span"> <el-col :md="6" :sm="12" v-for="k in kpiList" :key="k.label">
<el-table-column label="区块" prop="区块" width="90" /> <el-card shadow="hover" class="kpi-card" :style="{ borderLeft: '4px solid ' + k.color }">
<el-table-column label="型号" prop="型号" width="120" /> <div class="kpi-label">{{ k.label }} · 现有库存合计</div>
<el-table-column label="库存量" prop="库存量" align="right" /> <div class="kpi-value" :style="{ color: k.color }">{{ fmtNum(k.total) }}</div>
<el-table-column label="安全库存下限" prop="安全库存下限" align="right" /> <div class="kpi-sub">覆盖 {{ k.count }} 个型号</div>
<el-table-column label="安全库存上限" prop="安全库存上限" align="right" /> </el-card>
</el-table> </el-col>
</el-row>
<!-- 库存量对比图 -->
<el-card shadow="never" style="margin-bottom: 15px;">
<template #header><b>各库/工段 现有库存量对比(按型号)</b></template>
<scEcharts height="320px" :option="chartOption" />
</el-card> </el-card>
<!-- ds1: 成品库周数据 --> <!-- 明细表格 2x2 网格 -->
<el-row :gutter="15">
<el-col :lg="12">
<el-card shadow="never" class="rpt-card"> <el-card shadow="never" class="rpt-card">
<template #header>成品库周数据(入库=生产入库 / 出库=销售发货)</template> <template #header><b>库存量 安全库存上下限</b><span class="hd-tip">(个人设定)</span></template>
<el-table :data="ds1" border stripe size="small"> <el-table :data="ds0" border stripe size="small" :span-method="ds0Span"
<el-table-column label="型号" prop="型号" width="120" /> show-summary :summary-method="sumRow" :row-class-name="ds0RowClass">
<el-table-column label="入库数据" prop="入库数据" align="right" /> <el-table-column label="区块" prop="区块" width="70" align="center" />
<el-table-column label="出库数据" prop="出库数据" align="right" /> <el-table-column label="型号" prop="型号" width="90" />
<el-table-column label="现有库存" prop="现有库存" align="right" /> <el-table-column label="库存量" prop="库存量" align="right" :formatter="numCell" />
<el-table-column label="下限" prop="安全库存下限" align="right" :formatter="numCell" />
<el-table-column label="上限" prop="安全库存上限" align="right" :formatter="numCell" />
<el-table-column label="状态" width="72" align="center">
<template #default="{ row }">
<el-tag :type="safeState(row).type" size="small" effect="light">{{ safeState(row).text }}</el-tag>
</template>
</el-table-column>
</el-table> </el-table>
</el-card> </el-card>
</el-col>
<!-- ds2: 原料库(棒料)周数据 --> <el-col :lg="12">
<el-card shadow="never" class="rpt-card"> <el-card shadow="never" class="rpt-card">
<template #header>原料库(棒料)周数据</template> <template #header><b>成品库周数据</b><span class="hd-tip">(入库=生产入库 / 出库=销售发货)</span></template>
<el-table :data="ds2" border stripe size="small"> <el-table :data="ds1" border stripe size="small" show-summary :summary-method="sumRow">
<el-table-column label="型号" prop="型号" width="120" /> <el-table-column label="型号" prop="型号" width="110" />
<el-table-column label="入库数据" prop="入库数据" align="right" /> <el-table-column label="入库数据" prop="入库数据" align="right" :formatter="numCell" />
<el-table-column label="外协出库" prop="外协出库" align="right" /> <el-table-column label="出库数据" prop="出库数据" align="right" :formatter="numCell" />
<el-table-column label="光芯出库" prop="光芯出库" align="right" /> <el-table-column label="现有库存" prop="现有库存" align="right" :formatter="numCell" />
<el-table-column label="现有库存" prop="现有库存" align="right" />
</el-table> </el-table>
</el-card> </el-card>
</el-col>
<!-- ds3: 白玻 / 黑玻 周数据 --> <el-col :lg="12">
<el-card shadow="never" class="rpt-card"> <el-card shadow="never" class="rpt-card">
<template #header>白玻 / 黑玻 周数据(外协以"外协%"工段判定)</template> <template #header><b>原料库(棒料)周数据</b></template>
<el-table :data="ds3" border stripe size="small" :span-method="ds3Span"> <el-table :data="ds2" border stripe size="small" show-summary :summary-method="sumRow">
<el-table-column label="区块" prop="区块" width="90" /> <el-table-column label="型号" prop="型号" width="90" />
<el-table-column label="型号" prop="型号" width="120" /> <el-table-column label="入库数据" prop="入库数据" align="right" :formatter="numCell" />
<el-table-column label="外协外发数量" prop="外协外发数量" align="right" /> <el-table-column label="外协出库" prop="外协出库" align="right" :formatter="numCell" />
<el-table-column label="外协来料数量" prop="外协来料数量" align="right" /> <el-table-column label="光芯出库" prop="光芯出库" align="right" :formatter="numCell" />
<el-table-column label="现有库存" prop="现有库存" align="right" /> <el-table-column label="现有库存" prop="现有库存" align="right" :formatter="numCell" />
</el-table> </el-table>
</el-card> </el-card>
</el-col>
<el-col :lg="12">
<el-card shadow="never" class="rpt-card">
<template #header><b>白玻 / 黑玻 周数据</b><span class="hd-tip">(外协以"外协%"工段判定)</span></template>
<el-table :data="ds3" border stripe size="small" :span-method="ds3Span"
show-summary :summary-method="sumRow">
<el-table-column label="区块" prop="区块" width="70" align="center" />
<el-table-column label="型号" prop="型号" width="90" />
<el-table-column label="外协外发" prop="外协外发数量" align="right" :formatter="numCell" />
<el-table-column label="外协来料" prop="外协来料数量" align="right" :formatter="numCell" />
<el-table-column label="现有库存" prop="现有库存" align="right" :formatter="numCell" />
</el-table>
</el-card>
</el-col>
</el-row>
</el-main> </el-main>
</el-container> </el-container>
</template> </template>
<script> <script>
import scEcharts from "@/components/scEcharts/index.vue";
const BLOCKS = [
{ key: "原料", color: "#409EFF" },
{ key: "成品", color: "#67C23A" },
{ key: "白玻", color: "#E6A23C" },
{ key: "黑玻", color: "#909399" },
];
export default { export default {
name: "materialWhSummary", name: "materialWhSummary",
components: { scEcharts },
data() { data() {
return { return {
loading: false, loading: false,
@ -84,15 +127,85 @@ export default {
ds3: [], ds3: [],
}; };
}, },
computed: {
// KPI:
kpiList() {
return BLOCKS.map((b) => {
const rows = this.ds0.filter((r) => r["区块"] === b.key);
return {
label: b.key,
color: b.color,
total: rows.reduce((s, r) => s + this.num(r["库存量"]), 0),
count: rows.length,
};
});
},
//
chartOption() {
const models = [...new Set(this.ds0.map((r) => r["型号"]))];
const series = BLOCKS.map((b) => ({
name: b.key,
type: "bar",
itemStyle: { color: b.color },
barMaxWidth: 28,
data: models.map((m) =>
this.ds0
.filter((r) => r["区块"] === b.key && r["型号"] === m)
.reduce((s, r) => s + this.num(r["库存量"]), 0)
),
}));
return {
tooltip: { trigger: "axis", axisPointer: { type: "shadow" } },
legend: { data: BLOCKS.map((b) => b.key), top: 0 },
grid: { left: 10, right: 20, bottom: 10, top: 40, containLabel: true },
xAxis: { type: "category", data: models },
yAxis: { type: "value" },
series,
};
},
},
created() { created() {
this.dateRange = this.currentWeekRange(); this.dateRange = this.currentWeekRange();
this.fetchData(); this.fetchData();
}, },
methods: { methods: {
// ~ num(v) {
const n = Number(v);
return isNaN(n) ? 0 : n;
},
fmtNum(v) {
return this.num(v).toLocaleString();
},
numCell(row, col, val) {
return val === null || val === undefined || val === "" ? "-" : this.num(val).toLocaleString();
},
// :
safeState(row) {
const c = this.num(row["库存量"]);
const lo = row["安全库存下限"];
const hi = row["安全库存上限"];
const noLo = lo === null || lo === undefined || lo === "";
const noHi = hi === null || hi === undefined || hi === "";
if (noLo && noHi) return { type: "info", text: "未设定" };
if (!noLo && c < this.num(lo)) return { type: "danger", text: "偏低" };
if (!noHi && c > this.num(hi)) return { type: "warning", text: "偏高" };
return { type: "success", text: "正常" };
},
ds0RowClass({ row }) {
return this.safeState(row).type === "danger" ? "row-warn" : "";
},
// :
sumRow({ columns, data }) {
return columns.map((col, i) => {
if (i === 0) return "合计";
if (!col.property || col.label === "状态" || col.property === "型号" || col.property === "区块") return "";
const total = data.reduce((s, r) => s + this.num(r[col.property]), 0);
return total ? total.toLocaleString() : "";
});
},
currentWeekRange() { currentWeekRange() {
const now = new Date(); const now = new Date();
const day = now.getDay() || 7; // 7 const day = now.getDay() || 7;
const monday = new Date(now); const monday = new Date(now);
monday.setDate(now.getDate() - day + 1); monday.setDate(now.getDate() - day + 1);
const sunday = new Date(monday); const sunday = new Date(monday);
@ -123,14 +236,11 @@ export default {
this.loading = false; this.loading = false;
}); });
}, },
// ""
mergeFirstCol(rows, key, rowIndex, columnIndex) { mergeFirstCol(rows, key, rowIndex, columnIndex) {
if (columnIndex !== 0) return; if (columnIndex !== 0) return;
const cur = rows[rowIndex] && rows[rowIndex][key]; const cur = rows[rowIndex] && rows[rowIndex][key];
const prev = rows[rowIndex - 1] && rows[rowIndex - 1][key]; const prev = rows[rowIndex - 1] && rows[rowIndex - 1][key];
if (rowIndex > 0 && cur === prev) { if (rowIndex > 0 && cur === prev) return { rowspan: 0, colspan: 0 };
return { rowspan: 0, colspan: 0 };
}
let span = 1; let span = 1;
for (let i = rowIndex + 1; i < rows.length; i++) { for (let i = rowIndex + 1; i < rows.length; i++) {
if (rows[i][key] === cur) span++; if (rows[i][key] === cur) span++;
@ -149,7 +259,31 @@ export default {
</script> </script>
<style scoped> <style scoped>
.kpi-card {
margin-bottom: 8px;
}
.kpi-label {
color: #909399;
font-size: 13px;
}
.kpi-value {
font-size: 26px;
font-weight: 700;
line-height: 1.6;
}
.kpi-sub {
color: #c0c4cc;
font-size: 12px;
}
.rpt-card { .rpt-card {
margin-bottom: 16px; margin-bottom: 15px;
}
.hd-tip {
color: #909399;
font-size: 12px;
margin-left: 6px;
}
:deep(.row-warn) {
background: #fef0f0;
} }
</style> </style>