|
|
@@ -1,5 +1,7 @@
|
|
|
<template>
|
|
|
- <el-dialog
|
|
|
+ <component
|
|
|
+ :is="standalone ? 'div' : 'el-dialog'"
|
|
|
+ class="batch-change-dialog"
|
|
|
:title="dialogTitle"
|
|
|
:visible.sync="visible"
|
|
|
width="92%"
|
|
|
@@ -9,12 +11,12 @@
|
|
|
<template v-if="mode === 'records'">
|
|
|
<el-form
|
|
|
size="mini"
|
|
|
- label-width="72px"
|
|
|
- class="record-search"
|
|
|
+ label-width="78px"
|
|
|
+ class="record-search ele-form-search"
|
|
|
@submit.native.prevent="searchRecords"
|
|
|
>
|
|
|
<el-row :gutter="16">
|
|
|
- <el-col :xs="24" :md="12" :lg="5">
|
|
|
+ <el-col :xs="24" :sm="12" :md="8" :lg="6">
|
|
|
<el-form-item label="审核代码">
|
|
|
<el-input
|
|
|
v-model="recordQuery.changeNo"
|
|
|
@@ -23,7 +25,7 @@
|
|
|
/>
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
- <el-col :xs="24" :md="12" :lg="5">
|
|
|
+ <el-col :xs="24" :sm="12" :md="8" :lg="6">
|
|
|
<el-form-item label="审批人">
|
|
|
<el-input
|
|
|
v-model="recordQuery.approverName"
|
|
|
@@ -32,7 +34,7 @@
|
|
|
/>
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
- <el-col :xs="24" :md="12" :lg="5">
|
|
|
+ <el-col :xs="24" :sm="12" :md="8" :lg="6">
|
|
|
<el-form-item label="仓库名称">
|
|
|
<el-input
|
|
|
v-model="recordQuery.warehouseName"
|
|
|
@@ -41,7 +43,7 @@
|
|
|
/>
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
- <el-col :xs="24" :md="12" :lg="5">
|
|
|
+ <el-col :xs="24" :sm="12" :md="8" :lg="6">
|
|
|
<el-form-item label="批次号">
|
|
|
<el-input
|
|
|
v-model="recordQuery.batchNo"
|
|
|
@@ -50,7 +52,7 @@
|
|
|
/>
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
- <el-col :xs="24" :md="12" :lg="5">
|
|
|
+ <el-col :xs="24" :sm="12" :md="8" :lg="6">
|
|
|
<el-form-item label="物品编码">
|
|
|
<el-input
|
|
|
v-model="recordQuery.categoryCode"
|
|
|
@@ -59,7 +61,7 @@
|
|
|
/>
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
- <el-col :xs="24" :md="12" :lg="5">
|
|
|
+ <el-col :xs="24" :sm="12" :md="8" :lg="6">
|
|
|
<el-form-item label="类型">
|
|
|
<el-select
|
|
|
v-model="recordQuery.changeType"
|
|
|
@@ -71,7 +73,7 @@
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
- <el-col :xs="24" :md="12" :lg="5">
|
|
|
+ <el-col :xs="24" :sm="12" :md="8" :lg="6">
|
|
|
<el-form-item label="审批状态">
|
|
|
<el-select
|
|
|
v-model="recordQuery.verifyStatus"
|
|
|
@@ -87,9 +89,29 @@
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
- <el-col :xs="24" :lg="4" class="record-search-actions">
|
|
|
- <el-button type="primary" @click="searchRecords">查询</el-button>
|
|
|
- <el-button @click="resetRecordQuery">重置</el-button>
|
|
|
+ <el-col
|
|
|
+ :xs="24"
|
|
|
+ :sm="12"
|
|
|
+ :md="8"
|
|
|
+ :lg="6"
|
|
|
+ :sm-offset="0"
|
|
|
+ :md-offset="16"
|
|
|
+ :lg-offset="18"
|
|
|
+ class="record-search-actions"
|
|
|
+ >
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ icon="el-icon-search"
|
|
|
+ class="ele-btn-icon"
|
|
|
+ @click="searchRecords"
|
|
|
+ >查询</el-button
|
|
|
+ >
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ icon="el-icon-refresh-left"
|
|
|
+ @click="resetRecordQuery"
|
|
|
+ >重置</el-button
|
|
|
+ >
|
|
|
</el-col>
|
|
|
</el-row>
|
|
|
</el-form>
|
|
|
@@ -129,13 +151,14 @@
|
|
|
<el-alert
|
|
|
v-if="mode === 'merge'"
|
|
|
title="仅允许同仓库、批次号、物品属性、供应商和价格完全一致的库存合批,审批通过后生成新系统批次。"
|
|
|
- style="color: red"
|
|
|
+ style="color:red"
|
|
|
type="info"
|
|
|
:closable="false"
|
|
|
/>
|
|
|
<el-alert
|
|
|
v-else
|
|
|
title="包装明细必须整行分配;锁定明细按锁定单分配,关联包装时自动跟随包装目标。"
|
|
|
+ style="color:red"
|
|
|
type="info"
|
|
|
:closable="false"
|
|
|
/>
|
|
|
@@ -261,6 +284,18 @@
|
|
|
@click="assignSelectedPackingTarget"
|
|
|
>批量分配</el-button
|
|
|
>
|
|
|
+ <el-button
|
|
|
+ size="mini"
|
|
|
+ :disabled="Boolean(pendingApproveId)"
|
|
|
+ @click="assignAllPackingTargets"
|
|
|
+ >一键分配</el-button
|
|
|
+ >
|
|
|
+ <el-button
|
|
|
+ size="mini"
|
|
|
+ :disabled="Boolean(pendingApproveId)"
|
|
|
+ @click="clearPackingTargets"
|
|
|
+ >清空分配</el-button
|
|
|
+ >
|
|
|
</div>
|
|
|
<el-table
|
|
|
ref="packingTable"
|
|
|
@@ -274,6 +309,8 @@
|
|
|
v-if="mode === 'split'"
|
|
|
type="selection"
|
|
|
width="45"
|
|
|
+ align="center"
|
|
|
+ header-align="center"
|
|
|
:selectable="packingRowSelectable"
|
|
|
/>
|
|
|
<el-table-column
|
|
|
@@ -354,7 +391,7 @@
|
|
|
/>
|
|
|
</div>
|
|
|
|
|
|
- <span slot="footer">
|
|
|
+ <span v-if="!standalone" slot="footer">
|
|
|
<el-button @click="visible = false">取 消</el-button>
|
|
|
<el-button
|
|
|
v-if="mode !== 'records'"
|
|
|
@@ -542,7 +579,7 @@
|
|
|
</el-tab-pane>
|
|
|
</el-tabs>
|
|
|
</el-dialog>
|
|
|
- </el-dialog>
|
|
|
+ </component>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
@@ -579,6 +616,12 @@
|
|
|
|
|
|
export default {
|
|
|
components: { bpmDetail },
|
|
|
+ props: {
|
|
|
+ standalone: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false
|
|
|
+ }
|
|
|
+ },
|
|
|
data() {
|
|
|
return {
|
|
|
visible: false,
|
|
|
@@ -607,6 +650,9 @@
|
|
|
}
|
|
|
};
|
|
|
},
|
|
|
+ mounted() {
|
|
|
+ if (this.standalone) this.open('records');
|
|
|
+ },
|
|
|
computed: {
|
|
|
detailProcessInstanceId() {
|
|
|
return (
|
|
|
@@ -965,6 +1011,87 @@
|
|
|
});
|
|
|
});
|
|
|
},
|
|
|
+ /** 按所有目标子批次的计量数量依次匹配整包装。 */
|
|
|
+ assignAllPackingTargets() {
|
|
|
+ if (this.pendingApproveId) return;
|
|
|
+ const availableRows = this.packingRows.filter(
|
|
|
+ (row) => !row.targetDetailId
|
|
|
+ );
|
|
|
+ const assignments = [];
|
|
|
+ for (const target of this.targets) {
|
|
|
+ const assignedMeasureQuantity = this.packingRows
|
|
|
+ .filter((row) => row.targetDetailId === target.targetDetailId)
|
|
|
+ .reduce(
|
|
|
+ (sum, row) => sum.plus(decimal(row.measureQuantity)),
|
|
|
+ new BigNumber(0)
|
|
|
+ );
|
|
|
+ const remainingMeasureQuantity = decimal(
|
|
|
+ target.measureQuantity
|
|
|
+ ).minus(assignedMeasureQuantity);
|
|
|
+ if (remainingMeasureQuantity.lte(0)) continue;
|
|
|
+
|
|
|
+ const selectedRows = this.findPackingCombination(
|
|
|
+ availableRows,
|
|
|
+ remainingMeasureQuantity
|
|
|
+ );
|
|
|
+ if (!selectedRows.length) {
|
|
|
+ return this.$message.warning(
|
|
|
+ `${this.targetLabel(
|
|
|
+ target.targetDetailId
|
|
|
+ )}没有可按计量数量分配的整包装`
|
|
|
+ );
|
|
|
+ }
|
|
|
+ assignments.push({
|
|
|
+ target,
|
|
|
+ selectedRows
|
|
|
+ });
|
|
|
+ selectedRows.forEach((packing) => {
|
|
|
+ availableRows.splice(availableRows.indexOf(packing), 1);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ assignments.forEach(({ target, selectedRows }) => {
|
|
|
+ selectedRows.forEach((packing) => {
|
|
|
+ packing.targetDetailId = target.targetDetailId;
|
|
|
+ this.assignPackingTarget(packing, target.targetDetailId, false);
|
|
|
+ });
|
|
|
+ });
|
|
|
+ this.syncTargetPackingMinimums();
|
|
|
+ this.selectedPackingRows = [];
|
|
|
+ if (this.$refs.packingTable) this.$refs.packingTable.clearSelection();
|
|
|
+ if (assignments.some(({ selectedRows }) => selectedRows.length)) {
|
|
|
+ this.$message.success('已按所有目标计量数量自动分配整包装');
|
|
|
+ }
|
|
|
+ },
|
|
|
+ /** 清除所有包装和锁定单的目标分配,保留目标数量输入。 */
|
|
|
+ clearPackingTargets() {
|
|
|
+ if (this.pendingApproveId) return;
|
|
|
+ this.packingRows.forEach((packing) => {
|
|
|
+ packing.targetDetailId = null;
|
|
|
+ });
|
|
|
+ this.lockRows.forEach((lock) => {
|
|
|
+ lock.targetDetailId = null;
|
|
|
+ lock.targetConflict = false;
|
|
|
+ });
|
|
|
+ this.selectedPackingRows = [];
|
|
|
+ if (this.$refs.packingTable) this.$refs.packingTable.clearSelection();
|
|
|
+ this.$message.success('已清空全部分配');
|
|
|
+ },
|
|
|
+ findPackingCombination(rows, targetQuantity) {
|
|
|
+ const selected = [];
|
|
|
+ const search = (start, quantity) => {
|
|
|
+ if (quantity.eq(0)) return true;
|
|
|
+ for (let index = start; index < rows.length; index += 1) {
|
|
|
+ const packingQuantity = decimal(rows[index].measureQuantity);
|
|
|
+ if (packingQuantity.gt(quantity)) continue;
|
|
|
+ selected.push(rows[index]);
|
|
|
+ if (search(index + 1, quantity.minus(packingQuantity))) return true;
|
|
|
+ selected.pop();
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ };
|
|
|
+ return search(0, targetQuantity) ? selected.slice() : [];
|
|
|
+ },
|
|
|
/** 包装分配变化时,同步其关联锁定单的全部明细和包装。 */
|
|
|
assignPackingTarget(packing, targetDetailId, shouldSync = true) {
|
|
|
const relatedLocks = this.lockRows.filter((lock) =>
|
|
|
@@ -1439,7 +1566,7 @@
|
|
|
|
|
|
<style scoped>
|
|
|
.record-search {
|
|
|
- padding: 14px 16px 2px;
|
|
|
+ padding: 12px 14px 2px;
|
|
|
margin-bottom: 12px;
|
|
|
background-color: #f5f7fa;
|
|
|
border: 1px solid #ebeef5;
|
|
|
@@ -1448,17 +1575,27 @@
|
|
|
.record-search :deep(.el-select) {
|
|
|
width: 100%;
|
|
|
}
|
|
|
+ .record-search :deep(.el-form-item) {
|
|
|
+ margin-bottom: 12px;
|
|
|
+ }
|
|
|
.record-search-actions {
|
|
|
display: flex;
|
|
|
justify-content: flex-end;
|
|
|
- padding-top: 1px;
|
|
|
+ align-items: flex-start;
|
|
|
+ gap: 8px;
|
|
|
+ padding-top: 0;
|
|
|
}
|
|
|
@media (max-width: 1199px) {
|
|
|
.record-search-actions {
|
|
|
- justify-content: flex-start;
|
|
|
+ justify-content: flex-end;
|
|
|
padding: 0 8px 12px;
|
|
|
}
|
|
|
}
|
|
|
+ @media (max-width: 767px) {
|
|
|
+ .record-search-actions {
|
|
|
+ justify-content: flex-start;
|
|
|
+ }
|
|
|
+ }
|
|
|
.section-title {
|
|
|
margin: 14px 0 8px;
|
|
|
font-weight: 600;
|