style:仓库数量汇总报表页优化显示(KPI概览/对比图/网格/合计/安全库存预警)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ee40da3461
commit
b301a09cac
|
|
@ -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>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue