This commit is contained in:
shijing 2026-07-20 09:50:16 +08:00
commit 564fa2e1c3
18 changed files with 805 additions and 94 deletions

View File

@ -1,3 +1,20 @@
## 3.1.2026071608
- feat: 新增功能
- scScanner支持扫码枪(蓝牙/USB)输入,套壳环境保留原生扫码按钮 [caoqianming]
- 仓库数量汇总报表支持点击状态设定安全库存上下限并日期自动查询 [caoqianming]
- 物料档案增加安全库存上限, 新增仓库数量汇总报表页 [caoqianming]
- 新增大批直通良率统计页zt_batch_gx [caoqianming]
- fix: 问题修复
- 上盘平磨合批弹窗总计不刷新,交接数量@change的countChanges方法未声明变量报错;总计改为computed计算属性,随handoverb自动汇总,并移除分散的手动累加与500ms定时重算 [caoqianming]
- zt_batch_gx展开行count_*字段后缀翻译为中文 [caoqianming]
- 出入库明细页单价/金额/合计改用后端返回的unit_price、price、total_price,修复前端浮点计算误差;库存导出总金额保留两位 [caoqianming]
- 客户页面删除接口调用错误,误用采购订单删除接口,并补充删除后刷新列表 [caoqianming]
- other: 其他变更
- perf:生产领料/其他出库(bxerp+gz)改按仓库批次选择,生产入库批次下拉显示工段 [caoqianming]
- perf:销售发货明细表单改为按仓库批次选择并精简冗余字段 [caoqianming]
- style:仓库数量汇总报表页优化显示(KPI概览/对比图/网格/合计/安全库存预警) [caoqianming]
- refactor:仓库数量汇总报表页移至 statistics 目录 [caoqianming]
## 2.6.2025041413
- fix: 问题修复
- fmlog日志添加时物料只展示来料未完成 [shijing]

View File

@ -94,6 +94,15 @@ export default {
);
},
},
setSafe: {
name: "设置安全库存上下限",
req: async function (id, data) {
return await http.put(
`${config.API_URL}/mtm/material/${id}/set_safe/`,
data
);
},
},
update: {
name: "更新",
req: async function (id, data) {

View File

@ -1,39 +1,65 @@
<template>
<div>
<el-button type="primary" @click="scanCode" style="margin-left:4px">{{labeltext}}</el-button>
</div>
<span class="sc-scanner" style="display:inline-flex;align-items:center;gap:4px;vertical-align:middle;">
<!-- 扫码枪(蓝牙/USB HID):聚焦后扫码即输入,回车触发平板蓝牙枪PC USB枪都走这里 -->
<el-input
ref="gunInput"
v-model="gunValue"
:placeholder="placeholder"
clearable
:style="{ width: gunWidth }"
@keydown.enter.prevent="onGunScan"
/>
<!-- 套壳App原生扫码(摄像头/激光):仅在检测到 window.Android 时显示,保留 gx 现有能力 -->
<el-button v-if="hasAndroid" type="primary" @click="scanCode">{{ labeltext }}</el-button>
</span>
</template>
<script>
export default {
props: {
labeltext: {
type: String,
default: "扫码",
}
labeltext: { type: String, default: "扫码" },
placeholder: { type: String, default: "扫码枪扫描" },
gunWidth: { type: String, default: "180px" },
},
data() {
return {
scannedResult: null, //
gunValue: "", //
hasAndroid: !!window.Android, // App()
};
},
mounted() {
window.onScanResult = this.onScanResult;
},
methods: {
scanCode(){
if (window.Android) {
window.Android.openScanner();
} else {
alert("当前环境不支持扫码");
}
},
onScanResult(data) {
this.scannedResult = data;
this.$emit('scanResult', data);
// App(/)
scanCode() {
if (window.Android) {
window.Android.openScanner();
} else {
alert("当前环境不支持原生扫码");
}
},
// ():
onGunScan() {
let v = (this.gunValue || "").trim();
this.gunValue = "";
if (!v) return;
if (/[一-龥]/.test(v)) {
this.$message
? this.$message.error("扫码内容含中文,请切换英文输入法后重试")
: alert("扫码内容含中文,请切换英文输入法后重试");
return;
}
this.$emit("scanResult", v);
},
// (window.onScanResult)
onScanResult(data) {
this.scannedResult = data;
this.$emit("scanResult", data);
return data;
},
},
},
};
</script>

View File

@ -6,7 +6,7 @@ const DEFAULT_CONFIG = {
DASHBOARD_URL: "/dashboard",
//版本号
APP_VER: "3.1.2026031316",
APP_VER: "3.1.2026071608",
//内核版本号
CORE_VER: "1.6.9",

View File

@ -214,7 +214,7 @@ export default {
that.$API.inm.warehouse.batch.req({ page: 0, count__gte: 1, material__type__in: "40" }).then((res) => {
res.forEach(item => {
let data0 = [];
let total = Number(item.material_.unit_price)*Number(item.count);
let total = Math.round(Number(item.material_.unit_price)*Number(item.count)*100)/100;
data0.push(item.update_time);
data0.push(item.material_.number);
data0.push(item.material_.name);

View File

@ -326,10 +326,17 @@ export default {
// console.log('userOption',userOption);
});
},
//,(mioitem_form,v-if)
resetForm() {
this.form = { state: 10, inout_date: this.$TOOL.dateFormat2(new Date()) };
},
//
open(mode = "add", type = "",htype="") {
this.mode = mode;
this.htype = htype;
if (mode == "add") {
this.resetForm();
}
this.form.type = type;
this.init();
this.visible = true;

View File

@ -104,15 +104,9 @@
<el-link v-else :underline="false" type="primary" @click="showWpr(scope.row)">{{scope.row.count}}</el-link>
</template>
</el-table-column>
<el-table-column label="单价" prop="price" v-if="cate == 'helpso'&&project_code=='gx'">
<template #default="scope">
<span v-if="scope.row.material_">{{ scope.row.material_.unit_price }}</span>
</template>
<el-table-column label="单价" prop="unit_price" v-if="cate == 'helpso'&&project_code=='gx'">
</el-table-column>
<el-table-column label="金额" prop="price" v-if="cate == 'helpso'&&project_code=='gx'">
<template #default="scope">
<span v-if="scope.row.material_">{{ scope.row.count * Number(scope.row.material_.unit_price) }}</span>
</template>
</el-table-column>
<el-table-column label="发出数量" prop="count_send" v-if="mioObj.type == 'sale_out'&&project_code=='gx'">
</el-table-column>
@ -721,13 +715,9 @@ that.$API.wpm.prints.req(printer_ip, obj).then((response) => {
}
}
if(index==7&&this.cate == 'helpso'&&this.project_code=='gx'){
let sumsss = 0;
data.forEach((item)=>{
if(item.count&&item.material_.unit_price&&item.material_.unit_price!==null){
sumsss += Number(item.count)*Number(item.material_.unit_price);
}
})
sums[index] =sumsss;
//
const total = this.mioObj ? this.mioObj.total_price : null;
sums[index] = total !== null && total !== undefined ? total : '';
}
});

View File

@ -19,27 +19,38 @@
</el-form-item>
</el-col>
<el-col>
<el-form-item label="物料" prop="material">
<el-form-item :label="isBatchPick?'仓库批次':'物料'" prop="material">
<xtSelect
:apiObj="apiObj"
v-model="form.material"
v-model:obj="selectObj"
:labelField="type=='sale_out'&&project_code=='bxerp'?'material_name':'full_name'"
:labelField="isBatchPick?'material_name':'full_name'"
:tableWidth="isBatchPick?760:600"
style="width:100%"
:params="query"
@change="selectMaterialChange"
>
<el-table-column label="物料">
<el-table-column label="物料" min-width="160">
<template #default="scope">
<span v-if="scope.row.full_name">{{ scope.row.full_name }}</span>
<span v-else>{{ scope.row.material_name }}</span>
</template>
</el-table-column>
<el-table-column v-if="isBatchPick" label="批次号" prop="batch" min-width="120" />
<el-table-column v-if="isBatchPick" label="仓库" prop="warehouse_name" width="90" />
<el-table-column v-if="isBatchPick" label="可发数量" prop="count_canmio" width="90" />
</xtSelect>
</el-form-item>
</el-col>
<el-col v-if="isBatchPick&&form.batch">
<el-form-item label="批次信息">
<span style="color: var(--el-text-color-regular);">
批次号{{ form.batch }}  仓库{{ selectObj&&selectObj.warehouse_name?selectObj.warehouse_name:'' }}  可发数量{{ batchcount }}
</span>
</el-form-item>
</el-col>
<el-col>
<el-form-item label="批次号" prop="batch">
<el-form-item label="批次号" prop="batch" v-if="!isBatchPick">
<el-input
v-if="form.type == 'pur_in'||form.type == 'other_in'"
v-model="form.batch"
@ -70,7 +81,7 @@
color: var(--el-text-color-secondary);
font-size: 13px;
"
>{{ item.count }}</span
>{{ item.count }}<template v-if="item.mgroup_name"> {{ item.mgroup_name }}</template></span
>
</el-option>
</el-select>
@ -84,7 +95,7 @@
/>
</el-form-item>
</el-col>
<el-col v-if="form.type == 'pur_in'||form.type == 'pur_out'||form.type == 'other_in'||form.type == 'do_out'||form.type=='sale_out'||form.type=='other_out'||form.type=='pur_out'||form.type=='borrow_out'">
<el-col v-if="(form.type == 'pur_in'||form.type == 'pur_out'||form.type == 'other_in'||form.type == 'do_out'||form.type=='sale_out'||form.type=='other_out'||form.type=='pur_out'||form.type=='borrow_out')&&!isBatchPick">
<el-form-item label="仓库已有批次">
<el-select
v-model="selectBatch"
@ -118,7 +129,7 @@
<scScanner v-if="project_code=='gx'" @scanResult="codeTextChange"></scScanner>
</el-form-item>
</el-col>
<el-col :md="12" :sm="24">
<el-col :md="12" :sm="24" v-if="!isBatchPick">
<el-form-item label="仓库" prop="warehouse">
<el-select
v-model="form.warehouse"
@ -357,6 +368,12 @@ export default {
}
},
computed: {
isBatchPick(){
// bxerp(gz/gx);/bxerp+gz(gx,)
if(this.type=='sale_out') return this.project_code=='bxerp';
if(this.type=='do_out'||this.type=='other_out') return ['bxerp','gz'].includes(this.project_code);
return false;
},
divStyle() {
return {
width: this.project_code=='gx' ? '80%' : '100%'
@ -490,7 +507,8 @@ export default {
// orderitem_material__order: this.mioObj.order,
type:10,
// material__type: 10
count__gte: 1,
// (-)0
count_canmio__gt: 0,
};
this.inputBatchDisable = true;
@ -511,9 +529,12 @@ export default {
this.inputBatchDisable = false;
}
this.query = query;
if(type == "sale_out"&&this.project_code=='bxerp'){
if(this.isBatchPick){
this.apiObj=this.$API.inm.warehouse.batch;
if(type=='do_out'||type=='other_out'){
// /(bxerp):,
this.query = type__in ? { material__type__in: type__in.replace(/\s/g,'') } : {};
}
}else{
this.apiObj=this.$API.mtm.material.list;
}
@ -572,7 +593,19 @@ export default {
sessionStorage.setItem("mioitemSelecObj",JSON.stringify(that.selectObj));
console.log("mioitemSelecObj",that.selectObj);
}
if(that.type == "sale_out"&&that.project_code=='bxerp'){
if((that.type == "do_out"||that.type == "other_out")&&that.isBatchPick){
// /(bxerp):,//
that.form.material = that.selectObj.material;
that.form.unit = that.selectObj.material_?that.selectObj.material_.unit:undefined;
that.form.unit_price = that.selectObj.material_?that.selectObj.material_.unit_price:0;
that.form.batch = that.selectObj.batch;
that.form.mb = that.selectObj.id;
that.form.warehouse = that.selectObj.warehouse;
that.batchcount = Number(that.selectObj.count_canmio);
that.form.count = Number(that.selectObj.count_canmio);
that.mTracking = that.selectObj.material_?that.selectObj.material_.tracking:10;
that.inputBatchDisable = true;
}else if(that.type == "sale_out"&&that.project_code=='bxerp'){
that.form.material = that.selectObj.material;
that.form.unit = that.selectObj.material_.unit;
that.form.unit_price = that.selectObj.material_.unit_price;
@ -703,11 +736,35 @@ export default {
this.form.warehouse = "";
this.inputBatchDisable = false;
},
//,""
resetForm() {
this.form = { count: 1, note: '', pack_index: 1 };
this.selectObj = null;
this.selectBatch = null;
this.mioitems = [''];
this.mioitemw = [];
this.wprList = [];
this.wbatchOptions = [];
this.batchOptions = [];
this.batchOptions_o = [];
this.components = [];
this.assembShow = false;
this.batchcount = null;
this.mTracking = 10;
this.prefix = '';
this.halfgoodprefix = '';
this.digitNum = null;
this.count_send_yp = 0;
this.inputBatchDisable = false;
},
//
open(mode = "add", type = "",mtype) {
let that = this;
this.mode = mode;
this.mtype = mtype?mtype:null;
if (mode == "add") {
this.resetForm();
}
this.form.type = this.type = type;
if (mode == "add") {
this.init();

View File

@ -133,13 +133,21 @@
</el-form-item>
</el-col>
<el-col :md="12" :sm="24">
<el-form-item label="安全库存" prop="count_safe">
<el-form-item label="安全库存下限" prop="count_safe">
<el-input-number
v-model="form.count_safe"
:min="0"
/>
</el-form-item>
</el-col>
<el-col :md="12" :sm="24">
<el-form-item label="安全库存上限" prop="count_safe_upper">
<el-input-number
v-model="form.count_safe_upper"
:min="0"
/>
</el-form-item>
</el-col>
<el-col :md="12" :sm="24">
<el-form-item label="组合件" prop="is_assemb">
<el-switch v-model="form.is_assemb" />

View File

@ -122,13 +122,21 @@
</el-form-item>
</el-col>
<el-col :md="12" :sm="24">
<el-form-item label="安全库存" prop="count_safe">
<el-form-item label="安全库存下限" prop="count_safe">
<el-input-number
v-model="form.count_safe"
:min="0"
/>
</el-form-item>
</el-col>
<el-col :md="12" :sm="24">
<el-form-item label="安全库存上限" prop="count_safe_upper">
<el-input-number
v-model="form.count_safe_upper"
:min="0"
/>
</el-form-item>
</el-col>
<!-- 光芯 -->
<el-col :md="12" :sm="24" v-if="form.type == 40">
<el-form-item label="仓库位号">
@ -346,6 +354,7 @@ export default {
that.form.process = data.process;
that.form.tracking = data.tracking;
that.form.count_safe = data.count_safe;
that.form.count_safe_upper = data.count_safe_upper;
that.form.is_hidden = data.is_hidden;
that.form.unit_price = data.unit_price;
that.form.photo = data.photo;

View File

@ -98,7 +98,7 @@
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="合批清除缺陷">
<el-form-item label="合格B类默认记合格">
<el-switch
v-model="form.clear_defect"
></el-switch>

View File

@ -121,8 +121,9 @@
this.$confirm(`确定删除吗?`, "提示", {
type: "warning",
}).then(() => {
this.$API.pum.order.delete.req(row.id).then((res) => {
this.$API.sam.customer.delete.req(row.id).then((res) => {
this.$message.success("删除成功");
this.$refs.table.refresh();
return res;
}).catch((err) => {
return err;

View File

@ -0,0 +1,365 @@
<!--
物料保障车间-仓库数量汇总表
数据来源: BI 数据集 code=wh_material_summary (POST api/bi/dataset/{code}/exec)
返回 res.data2.ds0~ds3, 分组维度为 material.model(型号)
菜单/路由由后端下发, component statistics/material_wh_summary
-->
<template>
<el-container>
<el-header style="display: flex; align-items: center;">
<span style="font-weight: 600; margin-right: 12px;">周区间</span>
<el-date-picker
v-model="dateRange"
type="daterange"
value-format="YYYY-MM-DD"
range-separator="至"
start-placeholder="开始时间(当周周一)"
end-placeholder="截止时间(当周周末)"
:clearable="false"
style="margin-right: 12px;"
@change="fetchData"
/>
<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-main id="elMain" v-loading="loading">
<!-- KPI 概览 -->
<el-row :gutter="15" style="margin-bottom: 15px;">
<el-col :md="6" :sm="12" v-for="k in kpiList" :key="k.label">
<el-card shadow="hover" class="kpi-card" :style="{ borderLeft: '4px solid ' + k.color }">
<div class="kpi-label">{{ k.label }} · 现有库存合计</div>
<div class="kpi-value" :style="{ color: k.color }">{{ fmtNum(k.total) }}</div>
<div class="kpi-sub">覆盖 {{ k.count }} 个型号</div>
</el-card>
</el-col>
</el-row>
<!-- 库存量对比图 -->
<el-card shadow="never" style="margin-bottom: 15px;">
<template #header><b>各库/工段 现有库存量对比(按型号)</b></template>
<scEcharts height="320px" :option="chartOption" />
</el-card>
<!-- 明细表格 2x2 网格 -->
<el-row :gutter="15">
<el-col :lg="12">
<el-card shadow="never" class="rpt-card">
<template #header><b>库存量 安全库存上下限</b><span class="hd-tip">(个人设定)</span></template>
<el-table :data="ds0" border stripe size="small" :span-method="ds0Span"
show-summary :summary-method="sumRow" :row-class-name="ds0RowClass">
<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-column label="状态" width="90" align="center">
<template #default="{ row }">
<el-tag :type="safeState(row).type" size="small" effect="light"
style="cursor: pointer;" @click="openSafe(row)" title="点击设置上下限">
{{ safeState(row).text }}
</el-tag>
</template>
</el-table-column>
</el-table>
</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="ds1" border stripe size="small" show-summary :summary-method="sumRow">
<el-table-column label="型号" prop="型号" width="110" />
<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-col :lg="12">
<el-card shadow="never" class="rpt-card">
<template #header><b>原料库(棒料)周数据</b></template>
<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="入库数据" 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="现有库存" prop="现有库存" align="right" :formatter="numCell" />
</el-table>
</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-dialog v-model="safeDialog.visible" title="设置安全库存上下限" width="420px" append-to-body>
<el-form label-width="110px">
<el-form-item label="区块 / 型号">
<b>{{ safeDialog.block }} / {{ safeDialog.model }}</b>
</el-form-item>
<el-form-item label="当前库存量">
<span>{{ fmtNum(safeDialog.stock) }}</span>
</el-form-item>
<el-form-item label="安全库存下限">
<el-input-number v-model="safeDialog.count_safe" :min="0" controls-position="right" style="width: 180px;" />
</el-form-item>
<el-form-item label="安全库存上限">
<el-input-number v-model="safeDialog.count_safe_upper" :min="0" controls-position="right" style="width: 180px;" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="safeDialog.visible = false">取消</el-button>
<el-button type="primary" :loading="safeDialog.saving" @click="saveSafe">保存</el-button>
</template>
</el-dialog>
</el-main>
</el-container>
</template>
<script>
import scEcharts from "@/components/scEcharts/index.vue";
const BLOCKS = [
{ key: "原料", color: "#409EFF" },
{ key: "成品", color: "#67C23A" },
{ key: "白玻", color: "#E6A23C" },
{ key: "黑玻", color: "#909399" },
];
export default {
name: "materialWhSummary",
components: { scEcharts },
data() {
return {
loading: false,
dateRange: [],
ds0: [],
ds1: [],
ds2: [],
ds3: [],
safeDialog: {
visible: false,
saving: false,
material_id: null,
block: "",
model: "",
stock: 0,
count_safe: undefined,
count_safe_upper: undefined,
},
};
},
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() {
this.dateRange = this.currentWeekRange();
this.fetchData();
},
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() {
const now = new Date();
const day = now.getDay() || 7;
const monday = new Date(now);
monday.setDate(now.getDate() - day + 1);
const sunday = new Date(monday);
sunday.setDate(monday.getDate() + 6);
return [this.fmt(monday), this.fmt(sunday)];
},
fmt(d) {
const p = (n) => (n < 10 ? "0" + n : "" + n);
return d.getFullYear() + "-" + p(d.getMonth() + 1) + "-" + p(d.getDate());
},
fetchData() {
const [start_date, end_date] = this.dateRange || [];
if (!start_date || !end_date) {
this.$message.warning("请选择周区间");
return;
}
this.loading = true;
this.$API.bi.dataset.exec
.req("wh_material_summary", { query: { start_date, end_date } })
.then((res) => {
const d = res.data2 || {};
this.ds0 = d.ds0 || [];
this.ds1 = d.ds1 || [];
this.ds2 = d.ds2 || [];
this.ds3 = d.ds3 || [];
})
.finally(() => {
this.loading = false;
});
},
// ( material_id )
openSafe(row) {
if (!row.material_id) {
this.$message.warning("该型号暂无可设定的物料");
return;
}
const lo = row["安全库存下限"];
const hi = row["安全库存上限"];
this.safeDialog = {
visible: true,
saving: false,
material_id: row.material_id,
block: row["区块"],
model: row["型号"],
stock: row["库存量"],
count_safe: lo === null || lo === undefined || lo === "" ? undefined : this.num(lo),
count_safe_upper: hi === null || hi === undefined || hi === "" ? undefined : this.num(hi),
};
},
saveSafe() {
const d = this.safeDialog;
if (d.count_safe != null && d.count_safe_upper != null && Number(d.count_safe) > Number(d.count_safe_upper)) {
this.$message.warning("下限不能大于上限");
return;
}
d.saving = true;
this.$API.mtm.material.setSafe
.req(d.material_id, {
count_safe: d.count_safe === undefined ? null : d.count_safe,
count_safe_upper: d.count_safe_upper === undefined ? null : d.count_safe_upper,
})
.then(() => {
this.$message.success("设置成功");
d.visible = false;
this.fetchData();
})
.finally(() => {
d.saving = false;
});
},
mergeFirstCol(rows, key, rowIndex, columnIndex) {
if (columnIndex !== 0) return;
const cur = rows[rowIndex] && rows[rowIndex][key];
const prev = rows[rowIndex - 1] && rows[rowIndex - 1][key];
if (rowIndex > 0 && cur === prev) return { rowspan: 0, colspan: 0 };
let span = 1;
for (let i = rowIndex + 1; i < rows.length; i++) {
if (rows[i][key] === cur) span++;
else break;
}
return { rowspan: span, colspan: 1 };
},
ds0Span({ rowIndex, columnIndex }) {
return this.mergeFirstCol(this.ds0, "区块", rowIndex, columnIndex);
},
ds3Span({ rowIndex, columnIndex }) {
return this.mergeFirstCol(this.ds3, "区块", rowIndex, columnIndex);
},
},
};
</script>
<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 {
margin-bottom: 15px;
}
.hd-tip {
color: #909399;
font-size: 12px;
margin-left: 6px;
}
:deep(.row-warn) {
background: #fef0f0;
}
</style>

View File

@ -0,0 +1,257 @@
<template>
<el-container>
<el-header>
<div class="right-panel">
<el-input v-model="query.batch"
placeholder="大批号"
clearable
style="width: 180px;"
></el-input>
<el-input v-model="query.sub_batch"
placeholder="子批号"
clearable
style="width: 180px; margin: 5px"
></el-input>
<el-select
v-model="query.kind"
placeholder="白料口径"
style="width: 130px;"
:clearable="true"
>
<el-option label="黑化领用" value="黑化领用"></el-option>
<el-option label="入库数量" value="入库数量"></el-option>
<el-option label="谱系兜底" value="谱系兜底"></el-option>
</el-select>
<el-date-picker
v-model="query.time_start"
type="date"
placeholder="最后操作开始日期"
value-format="YYYY-MM-DD"
clearable
style="width: 150px; margin: 5px"
></el-date-picker>
<el-date-picker
v-model="query.time_end"
type="date"
placeholder="最后操作结束日期"
value-format="YYYY-MM-DD"
clearable
style="width: 150px; margin:0"
></el-date-picker>
<el-button
type="primary"
icon="el-icon-search"
@click="handleQuery"
></el-button>
<el-button
@click="handleExport"
class="tables"
type="primary"
>导出</el-button>
</div>
</el-header>
<el-main class="nopadding">
<el-table
ref="table"
:data="tableData"
id="exportDiv"
stripe
class="totalFooterTable"
style="height: 100%;"
:summary-method="getSummaries"
show-summary
>
<el-table-column type="expand">
<template #default="scope">
<el-descriptions :column="4" size="small" border style="margin: 0 20px;">
<el-descriptions-item v-for="(v, k) in displayData(scope.row.data)" :key="k" :label="k">{{ v }}</el-descriptions-item>
</el-descriptions>
</template>
</el-table-column>
<el-table-column label="大批号" prop="batch" min-width="200" fixed="left">
<template #default="scope"><span>{{ scope.row.batch }}</span></template>
</el-table-column>
<el-table-column label="物料" min-width="140">
<template #default="scope"><span>{{ scope.row.material_start_name }}</span></template>
</el-table-column>
<el-table-column label="白料数" class-name="colorheader1">
<template #default="scope"><span>{{ scope.row.data.直通_白料数 }}</span></template>
</el-table-column>
<el-table-column label="直通总合格数" class-name="colorheader1">
<template #default="scope"><span>{{ scope.row.data.直通_总合格数 }}</span></template>
</el-table-column>
<el-table-column label="直通良率(%)" class-name="colorheader5">
<template #default="scope"><span>{{ scope.row.data.直通_良率 }}</span></template>
</el-table-column>
<el-table-column label="白料口径" width="120">
<template #default="scope"><span>{{ scope.row.data.直通_口径 }}</span></template>
</el-table-column>
<el-table-column label="子批数" width="70">
<template #default="scope"><span>{{ subCount(scope.row) }}</span></template>
</el-table-column>
<el-table-column label="子批次" min-width="260" show-overflow-tooltip>
<template #default="scope"><span>{{ scope.row.data.直通_子批次 }}</span></template>
</el-table-column>
<el-table-column label="首次操作" width="100">
<template #default="scope"><span>{{ (scope.row.first_time || '').slice(0, 10) }}</span></template>
</el-table-column>
<el-table-column label="最后操作" width="100">
<template #default="scope"><span>{{ (scope.row.last_time || '').slice(0, 10) }}</span></template>
</el-table-column>
</el-table>
</el-main>
<el-footer>
<el-pagination
background
:small="true"
:total="dataTotal"
layout="total, sizes, prev, pager, next, jumper"
:page-size="params.page_size"
:page-sizes="[20, 50, 100, 200]"
:current-page="params.page"
@current-change="getList"
@update:page-size="getList2"
></el-pagination>
</el-footer>
</el-container>
</template>
<script>
export default {
name: "zt_batch_gx",
data() {
return {
query: {
batch: '',
sub_batch: '',
kind: '',
time_start: '',
time_end: '',
},
params: {
page: 1,
page_size: 20,
ordering: "-last_time",
querys: [
[{field: "data__has_key", compare: "", value: "直通_白料数"}]
],
},
dataTotal: 0,
tableData: [],
};
},
mounted() {
this.getList();
},
methods: {
subCount(row) {
let s = row.data.直通_子批次;
return s ? s.split(";").length : 0;
},
displayData(data) {
// count_* ( _count_ok_full _count_ok )
const map = [
["_count_ok_full", "_完全合格数"],
["_count_notok", "_不合格数"],
["_count_ok", "_合格数"],
["_count_use", "_领用数"],
["_count_real", "_生产数"],
["_count_pn_jgqbl", "_加工前不良"],
];
const out = {};
for (const k in data) {
let nk = k;
for (const [en, cn] of map) {
if (nk.includes(en)) {
nk = nk.replace(en, cn);
break;
}
}
out[nk] = data[k];
}
return out;
},
getList(val) {
let that = this;
that.params.page = val ? val : 1;
that.$API.wpm.batchstquery.req(that.params).then((res) => {
if (res.count > 0) {
that.tableData = res.results;
that.dataTotal = res.count;
} else {
that.tableData = [];
that.dataTotal = 0;
}
});
},
getList2(val) {
let that = this;
that.params.page = 1;
that.params.page_size = val;
that.getList(1);
},
handleQuery() {
let that = this;
that.params.page = 1;
let arr = [{field: "data__has_key", compare: "", value: "直通_白料数"}];
for (let key in that.query) {
if (that.query[key] != "" && that.query[key] != null && that.query[key] != undefined) {
let obj = {};
obj.value = that.query[key];
if (key == 'batch') {
obj.field = "batch";
obj.compare = "contains";
} else if (key == 'sub_batch') {
// JSONField contains JSON , icontains
obj.field = "data__直通_子批次";
obj.compare = "icontains";
} else if (key == 'kind') {
obj.field = "data__直通_口径";
obj.compare = "icontains";
} else if (key == 'time_start') {
obj.compare = "gte";
obj.field = "last_time";
} else if (key == 'time_end') {
obj.compare = "lte";
obj.field = "last_time";
obj.value = that.query[key] + " 23:59:59";
}
arr.push(obj);
}
}
that.params.querys = [arr];
this.getList();
},
getSummaries({ columns, data }) {
const sums = [];
let whiteSum = 0, okSum = 0;
data.forEach((r) => {
whiteSum += Number(r.data.直通_白料数) || 0;
okSum += Number(r.data.直通_总合格数) || 0;
});
columns.forEach((column, index) => {
if (index === 1) {
sums[index] = "合计";
return;
}
if (column.label == "白料数") {
sums[index] = whiteSum ? Number(whiteSum.toFixed(1)) : "";
} else if (column.label == "直通总合格数") {
sums[index] = okSum || "";
} else if (column.label == "直通良率(%)") {
sums[index] = whiteSum ? ((okSum / whiteSum) * 100).toFixed(2) : "";
} else {
sums[index] = "";
}
});
return sums;
},
handleExport() {
this.$XLSX("#exportDiv", "大批直通良率统计");
},
},
};
</script>
<style scoped>
</style>

View File

@ -101,7 +101,6 @@
:step-strictly="true"
style="width: 100%"
placeholder="交接数量"
@change="countChanges($index)"
>
</el-input-number>
</el-form-item>
@ -383,7 +382,6 @@ export default {
route_code: "",
project_code:'',
materials:[],
totalCount: 0,
deptID:'',
bwIndex:0,
checkedStatus:[],
@ -466,6 +464,11 @@ export default {
that.getMgroupOptions();
},
computed: {
// handoverb
totalCount() {
if (!this.form.handoverb || this.form.handoverb.length === 0) return 0;
return this.form.handoverb.reduce((sum, item) => sum + Number(item.count || 0), 0);
},
// state OK(10) defect ""
defectMismatch() {
if (this.mtype !== 30) return false;
@ -490,11 +493,6 @@ export default {
that.checkedStatus.splice(index,1);
this.form.handoverb[this.bwIndex].handoverbw.splice(index,1);
this.form.handoverb[this.bwIndex].count--;
let totalCount = 0;
that.form.handoverb.forEach((item)=>{
totalCount += item.count;
})
that.totalCount = totalCount;
},
showbw(index){
let that = this;
@ -680,7 +678,6 @@ export default {
delMaterial(index){
this.selectItems.splice(index,1);
this.form.handoverb.splice(index,1);
this.countChange();
},
codeTextChange(text){
this.wm_in = text;
@ -701,12 +698,10 @@ export default {
}
that.form.handoverb = data?data:[];
if(data&&data!==''&&data.length>0){
that.totalCount = 0;
data.forEach((item,index)=>{
item.wm = item.id;
that.checkedStatus.push(true)
that.selectItems.push(item.id)
that.totalCount += Number(item.count);
that.getWprList(item.wm,index);
})
if(that.isFeiPinku){
@ -720,7 +715,6 @@ export default {
},
clearSelect(){
let that = this;
that.totalCount = 0;
that.form.handoverb = [];
},
materialChange() {
@ -735,7 +729,7 @@ export default {
});
}
that.form.new_batch = that.route_code=='shangpaipingmo'?that.form.new_batch:"";
let totalCount = 0,data = [];
let data = [];
if(that.selectItems.length>0){
console.log('that.selectItems',that.selectItems)
data = that.materialOptions.filter((item) => {
@ -783,10 +777,8 @@ export default {
// handoverbw
item.handoverbw = scanned.handoverbw;
item.count = scanned.count;
totalCount += Number(scanned.count);
} else {
item.count = item.count_canhandover;
totalCount += Number(item.count_canhandover);
item.handoverbw = [];
that.getWprList(item.id,index);
}
@ -800,7 +792,6 @@ export default {
return true;
});
}
that.totalCount = totalCount;
that.form.handoverb = data;
},
getWprList(id,index){
@ -830,28 +821,6 @@ export default {
}else{
this.checkedStatus[index] = false;
}
this.countChange();
},
countChange(){
let that = this;
let totalCount = 0;
if(this.form.handoverb&&this.form.handoverb.length&&this.form.handoverb.length>0){
this.form.handoverb.forEach(item=>{
totalCount += Number(item.count);
})
that.totalCount = totalCount;
}else{
that.totalCount = 0;
}
},
countChanges(index){
if(this.form.handoverb[index]){}else{}
if(this.form.handoverb.length>0){
this.form.handoverb.forEach(item=>{
totalCount += item.count;
})
that.totalCount = totalCount;
}
},
//
submit() {
@ -910,7 +879,6 @@ export default {
//
setData(data) {
let that = this;
this.totalCount = data.count?data.count:data.handoverb.count;
Object.assign(this.form, data);
this.$API.system.user.list.req({ depts: data.send_dept, page: 0 }).then((res) => {
that.userList = res;
@ -1154,9 +1122,6 @@ export default {
}
}
}
setTimeout(() => {
this.countChange();
}, 500);
},
new_batch(){
let that = this;

View File

@ -60,7 +60,7 @@
</el-descriptions>
<div style="padding: 5px 10px;display: flex;justify-content: end;align-items: center;">
<div v-if="mlogItem.submit_time == null && hasOkBDefect" style="margin-right: 16px;display:flex;align-items:center;">
<span style="margin-right: 6px;">合格B类不拆批</span>
<span style="margin-right: 6px;">合格B类记为合格</span>
<el-switch v-model="mlogItem.clear_defect"></el-switch>
</div>
<el-button

View File

@ -64,7 +64,7 @@
<div style="padding: 5px 10px;display: flex;justify-content: end;align-items: center;">
<!-- hasOkBDefect -->
<div v-if="mlogItem.submit_time == null" style="margin-right: 16px;display:flex;align-items:center;">
<span style="margin-right: 6px;">合格B类不拆批</span>
<span style="margin-right: 6px;">合格B类记为合格</span>
<el-switch v-model="mlogItem.clear_defect"></el-switch>
</div>
<el-button

View File

@ -1,7 +1,7 @@
#!/bin/bash
# 设置默认版本号 (格式: 2.6.YYYYMMDDHH)
DEFAULT_VERSION="2.7.$(date '+%Y%m%d%H')"
DEFAULT_VERSION="3.1.$(date '+%Y%m%d%H')"
# 获取参数 (起始tag)
TARGET_TAG="$1"