|
|
@@ -0,0 +1,832 @@
|
|
|
+<template>
|
|
|
+ <div>
|
|
|
+ <ele-modal
|
|
|
+ :title="`盘点报工`"
|
|
|
+ append-to-body
|
|
|
+ :visible.sync="visibleDialog"
|
|
|
+ destroy-on-close
|
|
|
+ @close="closeWindows"
|
|
|
+ width="90vw"
|
|
|
+ :maxable="true"
|
|
|
+ :resizable="true"
|
|
|
+ >
|
|
|
+ <div class="dispose-dialog">
|
|
|
+ <HeaderTitle title="基本信息" size="16px"></HeaderTitle>
|
|
|
+ <!-- 搜索表单 -->
|
|
|
+ <el-form
|
|
|
+ label-width="90px"
|
|
|
+ class="ele-form-search"
|
|
|
+ @keyup.enter.native="search"
|
|
|
+ @submit.native.prevent
|
|
|
+ >
|
|
|
+ <el-row :gutter="15">
|
|
|
+ <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
|
|
|
+ <el-form-item label="复盘工单编码" prop="code">
|
|
|
+ <el-input
|
|
|
+ disabled
|
|
|
+ v-model="filterForm.code"
|
|
|
+ placeholder="请输入"
|
|
|
+ ></el-input>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
|
|
|
+ <el-form-item label="关联盘点工单号" prop="name">
|
|
|
+ <el-input
|
|
|
+ disabled
|
|
|
+ v-model="filterForm.name"
|
|
|
+ placeholder="请输入"
|
|
|
+ ></el-input>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
|
|
|
+ <el-form-item label="盘点类型" prop="inventoryType">
|
|
|
+ <el-select
|
|
|
+ v-model="filterForm.inventoryType"
|
|
|
+ class="w100"
|
|
|
+ placeholder="请选择盘点类型"
|
|
|
+ disabled
|
|
|
+ filterable
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in inventoryTypeOp"
|
|
|
+ :key="item.value"
|
|
|
+ :value="item.value"
|
|
|
+ :label="item.label"
|
|
|
+ ></el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
|
|
|
+ <el-form-item label="盘点精度" prop="dimension">
|
|
|
+ <el-select
|
|
|
+ style="width: 100%"
|
|
|
+ disabled
|
|
|
+ v-model="filterForm.dimension"
|
|
|
+ placeholder="请选择维度"
|
|
|
+ >
|
|
|
+ <el-option label="物品维度" :value="1"> </el-option>
|
|
|
+ <el-option label="批次维度" :value="2"> </el-option>
|
|
|
+ <el-option label="包装维度" :value="3"> </el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
|
|
|
+ <el-form-item label="冻结策略" prop="strategy">
|
|
|
+ <el-select
|
|
|
+ v-model="filterForm.strategy"
|
|
|
+ class="w100"
|
|
|
+ placeholder="请选择"
|
|
|
+ disabled
|
|
|
+ filterable
|
|
|
+ >
|
|
|
+ <el-option :value="1" label="库存快照(不锁库)"></el-option>
|
|
|
+ <el-option :value="2" label="仓库锁"></el-option>
|
|
|
+ <el-option :value="3" label="库区锁"></el-option>
|
|
|
+ <el-option :value="4" label="货架锁"></el-option>
|
|
|
+ <el-option :value="5" label="货位锁"></el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
|
|
|
+ <el-form-item label="仓库">
|
|
|
+ <el-input disabled v-model="filterForm.warehouseName" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
|
|
|
+ <el-form-item label="库区">
|
|
|
+ <el-select
|
|
|
+ v-model="filterForm.areaId"
|
|
|
+ class="w100"
|
|
|
+ placeholder="选择库区"
|
|
|
+ filterable
|
|
|
+ @change="changeArea"
|
|
|
+ >
|
|
|
+ <el-option v-for="item in areaList" :key="item.id" :value="item.id" :label="item.name" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
|
|
|
+ <el-form-item label="货架">
|
|
|
+ <el-select
|
|
|
+ v-model="filterForm.shelfId"
|
|
|
+ class="w100"
|
|
|
+ placeholder="选择货架"
|
|
|
+ filterable
|
|
|
+ @change="changeShelf"
|
|
|
+ >
|
|
|
+ <el-option v-for="item in shelfList" :key="item.id" :value="item.id" :label="item.goodsshelvesCode" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
|
|
|
+ <el-form-item label="货架">
|
|
|
+ <el-select
|
|
|
+ v-model="filterForm.shelfId"
|
|
|
+ class="w100"
|
|
|
+ placeholder="选择货架"
|
|
|
+ filterable
|
|
|
+ @change="changeShelf"
|
|
|
+ >
|
|
|
+ <el-option v-for="item in shelfList" :key="item.id" :value="item.id" :label="item.goodsshelvesCode" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
|
|
|
+ <el-form-item label="执行部门" prop="executeGroupId">
|
|
|
+ <deptSelect
|
|
|
+ v-model="filterForm.executeGroupId"
|
|
|
+ @changeGroup="searchDeptNodeClick"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
|
|
|
+ <el-form-item label="执行人员" prop="executorId">
|
|
|
+ <el-select
|
|
|
+ v-model="filterForm.executorId"
|
|
|
+ @change="executorChange"
|
|
|
+ size="small"
|
|
|
+ style="width: 100%"
|
|
|
+ filterable
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in executorList"
|
|
|
+ :key="item.id"
|
|
|
+ :value="item.id"
|
|
|
+ :label="item.name"
|
|
|
+ ></el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col v-bind="styleResponsive ? { lg: 12, md: 12 } : { span: 12 }">
|
|
|
+ <div class="ele-form-actions">
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ icon="el-icon-search"
|
|
|
+ class="ele-btn-icon"
|
|
|
+ @click="search"
|
|
|
+ size="small"
|
|
|
+ >查询</el-button>
|
|
|
+ <el-button
|
|
|
+ @click="resetFilter"
|
|
|
+ icon="el-icon-refresh-left"
|
|
|
+ size="small"
|
|
|
+ type="primary"
|
|
|
+ >重置</el-button>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-form>
|
|
|
+
|
|
|
+ <!-- 差异清单 -->
|
|
|
+ <HeaderTitle title="差异清单" size="16px"></HeaderTitle>
|
|
|
+ <el-form
|
|
|
+ label-width="90px"
|
|
|
+ class="ele-form-search"
|
|
|
+ @keyup.enter.native="search"
|
|
|
+ @submit.native.prevent
|
|
|
+ >
|
|
|
+ <el-row :gutter="15">
|
|
|
+
|
|
|
+ <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
|
|
|
+ <el-form-item label="处置类型" prop="disposeType">
|
|
|
+ <el-select
|
|
|
+ v-model="filterForm.disposeType"
|
|
|
+ class="w100"
|
|
|
+ placeholder="请选择"
|
|
|
+ disabled
|
|
|
+ filterable
|
|
|
+ >
|
|
|
+ <el-option :value="1" label="补录入库"></el-option>
|
|
|
+ <el-option :value="2" label="报损出库"></el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
|
|
|
+ <el-form-item label="处置备注">
|
|
|
+ <el-input disabled v-model="filterForm.remark" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-form>
|
|
|
+ <ele-pro-table
|
|
|
+ ref="table"
|
|
|
+ :columns="columns"
|
|
|
+ height="calc(70vh - 120px)"
|
|
|
+ :page-size="20"
|
|
|
+ :page-sizes="[10, 20, 50, 100]"
|
|
|
+ :datasource="datasource"
|
|
|
+ row-key="rowKey"
|
|
|
+ :where="searchWhere"
|
|
|
+ :needPage="false"
|
|
|
+ :init-load="false"
|
|
|
+ >
|
|
|
+
|
|
|
+ <!-- 表头工具栏 -->
|
|
|
+ <template v-slot:toolbar>
|
|
|
+
|
|
|
+ <!-- <div style="display: flex; justify-content: space-between; align-items: center;">
|
|
|
+ <div>
|
|
|
+ <span class="dispose-stat">
|
|
|
+ 总行数:<b>{{ statistics.total }}</b> |
|
|
|
+ 已盘行数:<b>{{ statistics.confirmed }}</b> |
|
|
|
+ 未盘行数:<b>{{ statistics.unconfirmed }}</b> |
|
|
|
+ 正常:<b>{{ statistics.normal }}</b> |
|
|
|
+ 盘盈:<b>{{ statistics.surplus }}</b> |
|
|
|
+ 盘亏:<b>{{ statistics.lose }}</b> |
|
|
|
+ 无账货:<b>{{ statistics.unlisted }}</b>
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ <div style="padding-right: 10px;">
|
|
|
+ <el-button type="primary" @click="confirmAll">全部确认</el-button>
|
|
|
+ <el-button type="primary" @click="cancelAll">全部取消</el-button>
|
|
|
+ <el-button @click="addUnlisted">新增无账货</el-button>
|
|
|
+ </div>
|
|
|
+ </div> -->
|
|
|
+ </template>
|
|
|
+ <template v-slot:categoryName="{ row }">
|
|
|
+ <template v-if="row._isUnlisted">
|
|
|
+ <el-input v-model="row.categoryName" size="small" placeholder="请输入" />
|
|
|
+ </template>
|
|
|
+ <span v-else>{{ row.categoryName }}</span>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template v-slot:categoryCode="{ row }">
|
|
|
+ <template v-if="row._isUnlisted">
|
|
|
+ <el-input v-model="row.categoryCode" size="small" placeholder="编码" />
|
|
|
+ </template>
|
|
|
+ <span v-else>{{ row.info?.categoryCode || row.categoryCode || '' }}</span>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template v-slot:batchNo="{ row }">
|
|
|
+ <template v-if="row._isUnlisted">
|
|
|
+ <el-input v-model="row.batchNo" size="small" placeholder="请输入" />
|
|
|
+ </template>
|
|
|
+ <span v-else>{{ row.batchNo }}</span>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template v-slot:packageNo="{ row }">
|
|
|
+ <template v-if="row._isUnlisted">
|
|
|
+ <el-input v-model="row.packageNo" size="small" placeholder="包装号" />
|
|
|
+ </template>
|
|
|
+ <span v-else>{{ row.packageNo }}</span>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template v-slot:measureQuantity="{ row }">
|
|
|
+ <span v-if="row._isUnlisted">-</span>
|
|
|
+ <span v-else>{{ formatNumber(row.info?.measureQuantity ?? row.measureQuantity) }}</span>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template v-slot:measureUnit="{ row }">
|
|
|
+ <template v-if="row._isUnlisted">
|
|
|
+ <el-input v-model="row.measureUnit" size="small" placeholder="单位" />
|
|
|
+ </template>
|
|
|
+ <span v-else>{{ row.info?.measureUnit || row.measureUnit || '' }}</span>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template v-slot:actualQuantity="{ row }">
|
|
|
+ <el-input-number
|
|
|
+ v-if="row._status === 0"
|
|
|
+ v-model="row.actualQuantity"
|
|
|
+ :min="0"
|
|
|
+ :precision="2"
|
|
|
+ size="small"
|
|
|
+ controls-position="right"
|
|
|
+ style="width: 100%"
|
|
|
+ @change="onActualChange(row)"
|
|
|
+ />
|
|
|
+ <span v-else>{{ formatNumber(row.actualQuantity) }}</span>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template v-slot:result="{ row }">
|
|
|
+ <el-select
|
|
|
+ v-model="row.result"
|
|
|
+ placeholder="请选择"
|
|
|
+ size="small"
|
|
|
+ clearable
|
|
|
+ :disabled="row._status === 1"
|
|
|
+ @change="onFieldChange(row)"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="opt in resultOptions"
|
|
|
+ :key="opt.value"
|
|
|
+ :label="opt.label"
|
|
|
+ :value="opt.value"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template v-slot:diffQuantity="{ row }">
|
|
|
+ <span>{{ calcDiffQuantity(row) }}</span>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template v-slot:status="{ row }">
|
|
|
+ <el-tag
|
|
|
+ :type="row._status === 1 ? 'success' : 'warning'"
|
|
|
+ size="small"
|
|
|
+ effect="light"
|
|
|
+ >{{ row._status === 1 ? '已盘' : '未盘' }}</el-tag>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template v-slot:description="{ row }">
|
|
|
+ <el-input
|
|
|
+ v-model="row.description"
|
|
|
+ placeholder="盘点说明"
|
|
|
+ size="small"
|
|
|
+ clearable
|
|
|
+ :disabled="row._status === 1"
|
|
|
+ @change="onFieldChange(row)"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template v-slot:diffReason="{ row }">
|
|
|
+ <el-select
|
|
|
+ v-model="row.diffReason"
|
|
|
+ placeholder="请选择"
|
|
|
+ size="small"
|
|
|
+ clearable
|
|
|
+ filterable
|
|
|
+ :disabled="row._status === 1"
|
|
|
+ @change="onFieldChange(row)"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="opt in diffReasonOptions"
|
|
|
+ :key="opt.value"
|
|
|
+ :label="opt.label"
|
|
|
+ :value="opt.value"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template v-slot:diffImages="{ row }">
|
|
|
+ <!-- <file-upload
|
|
|
+ v-model="row.diffImages"
|
|
|
+ :module="'stocktakingReport'"
|
|
|
+ :show-lib="false"
|
|
|
+ :limit="3"
|
|
|
+ :size="5"
|
|
|
+ /> -->
|
|
|
+ <fileMain v-model="row.diffImages"></fileMain>
|
|
|
+
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template v-slot:action="{ row }">
|
|
|
+ <template v-if="row._isUnlisted">
|
|
|
+ <el-button type="text" size="mini" @click="removeUnlisted(row)">删除</el-button>
|
|
|
+ </template>
|
|
|
+ <template v-else>
|
|
|
+ <el-button
|
|
|
+ v-if="row._status === 0"
|
|
|
+ type="text"
|
|
|
+ size="mini"
|
|
|
+ @click="confirmRow(row)"
|
|
|
+ >确认</el-button>
|
|
|
+ <el-button
|
|
|
+ v-else
|
|
|
+ type="text"
|
|
|
+ size="mini"
|
|
|
+ @click="cancelRow(row)"
|
|
|
+ >取消</el-button>
|
|
|
+ </template>
|
|
|
+ </template>
|
|
|
+ </ele-pro-table>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div slot="footer" class="dispose-footer">
|
|
|
+ <el-button @click="closeWindows">关闭</el-button>
|
|
|
+ <el-button @click="onSave">保存</el-button>
|
|
|
+ <el-button type="primary" :loading="reportLoading" @click="onReport">报工</el-button>
|
|
|
+ </div>
|
|
|
+ </ele-modal>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import { getPlanDetailList, reportWork, saveReportWork } from '@/api/inventory';
|
|
|
+import warehouseDefinition from '@/api/warehouseManagement/warehouseDefinition';
|
|
|
+import deptSelect from '@/components/CommomSelect/new-dept-select.vue';
|
|
|
+
|
|
|
+export default {
|
|
|
+ components: { deptSelect },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ inventoryTypeOp: [{
|
|
|
+ label: '库内盘点',
|
|
|
+ value: 1
|
|
|
+ },{
|
|
|
+ label: '库外盘点',
|
|
|
+ value: 2
|
|
|
+ }],
|
|
|
+ executorList: [],
|
|
|
+ visibleDialog: false,
|
|
|
+ planOrderId: '',
|
|
|
+ // 表单查询条件
|
|
|
+ filterForm: {
|
|
|
+ warehouseName: '',
|
|
|
+ dimension: '',
|
|
|
+ areaId: '',
|
|
|
+ shelfId: '',
|
|
|
+ keyWord: '',
|
|
|
+ statusFilter: ''
|
|
|
+ },
|
|
|
+ areaList: [],
|
|
|
+ shelfList: [],
|
|
|
+ locationList: [],
|
|
|
+ reportLoading: false,
|
|
|
+ // 接口返回的原始行(保留编辑状态)
|
|
|
+ serverRows: [],
|
|
|
+ // 无账货行
|
|
|
+ unlistedRows: [],
|
|
|
+ // 差异原因字典
|
|
|
+ diffReasonOptions: [
|
|
|
+ { value: 1, label: '漏记账' },
|
|
|
+ { value: 2, label: '错记账' },
|
|
|
+ { value: 3, label: '丢失' },
|
|
|
+ { value: 4, label: '被盗' },
|
|
|
+ { value: 5, label: '毁损' },
|
|
|
+ { value: 6, label: '串货' },
|
|
|
+ { value: 7, label: '入库未确认' },
|
|
|
+ { value: 8, label: '出库未登记' },
|
|
|
+ { value: 9, label: '其他' }
|
|
|
+ ],
|
|
|
+ // 盘点结果选项
|
|
|
+ resultOptions: [
|
|
|
+ { value: 'normal', label: '正常' },
|
|
|
+ { value: 'surplus', label: '盘盈' },
|
|
|
+ { value: 'lose', label: '盘亏' },
|
|
|
+ { value: 'unlisted', label: '无账货' }
|
|
|
+ ],
|
|
|
+ // 表格列配置
|
|
|
+ columns: [
|
|
|
+ { columnKey: 'index', label: '序号', type: 'index', width: 55, align: 'center', fixed: 'left' },
|
|
|
+ { slot: 'actualQuantity', label: '实盘数', align: 'center', minWidth: 120 },
|
|
|
+ { slot: 'result', label: '盘点结果', align: 'center', minWidth: 100 },
|
|
|
+ { slot: 'diffQuantity', label: '盘点差异数', align: 'center', minWidth: 100 },
|
|
|
+ { slot: 'description', label: '盘点说明', align: 'center', minWidth: 140 },
|
|
|
+ { slot: 'diffReason', label: '差异原因', align: 'center', minWidth: 160 },
|
|
|
+ { slot: 'diffImages', label: '差异拍照', align: 'center', minWidth: 150 },
|
|
|
+ { slot: 'status', label: '盘点状态', align: 'center', minWidth: 90 },
|
|
|
+ { prop: 'categoryName', label: '物品名称', align: 'center', minWidth: 160, showOverflowTooltip: true },
|
|
|
+ { prop: 'info.categoryCode', label: '物品编码', align: 'center', minWidth: 130, showOverflowTooltip: true },
|
|
|
+ { prop: 'batchNo', label: '批次号', align: 'center', minWidth: 120, showOverflowTooltip: true },
|
|
|
+ { prop: 'info.categoryModel', label: '型号', align: 'center', minWidth: 120, showOverflowTooltip: true },
|
|
|
+ { prop: 'info.specification', label: '规格', align: 'center', minWidth: 120, showOverflowTooltip: true },
|
|
|
+ { prop: 'info.measureQuantity', label: '计量数量', align: 'center', minWidth: 110, showOverflowTooltip: true },
|
|
|
+ { prop: 'info.measureUnit', label: '计量单位', align: 'center', minWidth: 100, showOverflowTooltip: true },
|
|
|
+ { prop: 'info.brandNum', label: '牌号', align: 'center', minWidth: 120, showOverflowTooltip: true },
|
|
|
+ { prop: 'info.colorKey', label: '颜色', align: 'center', minWidth: 120, showOverflowTooltip: true },
|
|
|
+ { prop: 'info.weight', label: '重量', align: 'center', minWidth: 110, showOverflowTooltip: true },
|
|
|
+ { prop: 'info.weightUnit', label: '重量单位', align: 'center', minWidth: 100, showOverflowTooltip: true },
|
|
|
+ { prop: 'info.warehouseName', label: '仓库名称', align: 'center', minWidth: 110, showOverflowTooltip: true },
|
|
|
+ { prop: 'info.warehouseArea', label: '库区', align: 'center', minWidth: 100, showOverflowTooltip: true },
|
|
|
+ { prop: 'info.shelf', label: '货架', align: 'center', minWidth: 110, showOverflowTooltip: true },
|
|
|
+ { prop: 'info.location', label: '货位', align: 'center', minWidth: 100, showOverflowTooltip: true },
|
|
|
+ { prop: 'info.reportTime', label: '报告时间', align: 'center', minWidth: 180, showOverflowTooltip: true },
|
|
|
+ { slot: 'action', label: '操作', align: 'center', width: 80, fixed: 'right', resizable: false }
|
|
|
+ ]
|
|
|
+ };
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ styleResponsive() {
|
|
|
+ return this.$store.state.theme.styleResponsive;
|
|
|
+ },
|
|
|
+ searchWhere() {
|
|
|
+ const w = {};
|
|
|
+ if (this.filterForm.keyWord) w.keyWord = this.filterForm.keyWord;
|
|
|
+ return w;
|
|
|
+ },
|
|
|
+ statistics() {
|
|
|
+ const list = this.serverRows;
|
|
|
+ const unlisted = this.unlistedRows;
|
|
|
+ const total = list.length + unlisted.length;
|
|
|
+ const confirmed = list.filter((r) => r._status === 1).length;
|
|
|
+ const unconfirmed = list.filter((r) => r._status === 0).length;
|
|
|
+ const normal = list.filter((r) => r.result === 'normal').length;
|
|
|
+ const surplus = list.filter((r) => r.result === 'surplus').length;
|
|
|
+ const lose = list.filter((r) => r.result === 'lose').length;
|
|
|
+ return { total, confirmed, unconfirmed, normal, surplus, lose, unlisted: unlisted.length };
|
|
|
+ }
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ //选择部门(搜索)
|
|
|
+ searchDeptNodeClick(info, row) {
|
|
|
+ this.formData.executorId = '';
|
|
|
+ if (info) {
|
|
|
+ const params = { groupId: info };
|
|
|
+ this.getUserList(params);
|
|
|
+ this.formData.executeGroupName = row.name;
|
|
|
+ } else {
|
|
|
+ this.formData.executeGroupId = null;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 获取审核人列表、巡点检人员
|
|
|
+ async getUserList(params) {
|
|
|
+ try {
|
|
|
+ let data = { pageNum: 1, size: -1 };
|
|
|
+ if (params) {
|
|
|
+ data = Object.assign(data, params);
|
|
|
+ }
|
|
|
+ const res = await getUserPage(data);
|
|
|
+ this.executorList = res.list;
|
|
|
+ } catch (error) {}
|
|
|
+ },
|
|
|
+ executorChange(val) {
|
|
|
+ this.formData.executorName = this.executorList.filter(
|
|
|
+ (item) => item.id == val
|
|
|
+ )[0].name;
|
|
|
+ },
|
|
|
+ async getAreaList(warehouseId) {
|
|
|
+ const res = await warehouseDefinition.getListByWarehouseId(warehouseId);
|
|
|
+ this.areaList = res || [];
|
|
|
+ },
|
|
|
+ async getShelfList(areaId) {
|
|
|
+ const rep = await warehouseDefinition.getListByAreaId(areaId);
|
|
|
+ this.shelfList = rep || [];
|
|
|
+ },
|
|
|
+ changeArea(val) {
|
|
|
+ this.filterForm.shelfId = '';
|
|
|
+ this.getShelfList(val);
|
|
|
+ },
|
|
|
+ changeShelf() {},
|
|
|
+ // ============= 工具方法 =============
|
|
|
+ formatNumber(val) {
|
|
|
+ if (val == null || val === '') return '';
|
|
|
+ const n = parseFloat(val);
|
|
|
+ if (isNaN(n)) return val;
|
|
|
+ return n.toFixed(2).replace(/\.?0+$/, '') || '0';
|
|
|
+ },
|
|
|
+ // 盘点差异数 = 实盘数 - 计量数量
|
|
|
+ calcDiffQuantity(row) {
|
|
|
+ const actual = parseFloat(row.actualQuantity);
|
|
|
+ const expect = parseFloat(row._measure);
|
|
|
+ if (isNaN(actual) || isNaN(expect)) return '-';
|
|
|
+ const diff = actual - expect;
|
|
|
+ return (diff >= 0 ? '+' : '') + diff.toFixed(2).replace(/\.?0+$/, '') || '0';
|
|
|
+ },
|
|
|
+ onFieldChange(row) { row._dirty = true; },
|
|
|
+ // 实盘数变化时,自动计算盘点结果
|
|
|
+ onActualChange(row) {
|
|
|
+ row._dirty = true;
|
|
|
+ if (row._isUnlisted) {
|
|
|
+ row.result = 'unlisted';
|
|
|
+ } else if (row.actualQuantity != null && row.actualQuantity !== '') {
|
|
|
+ row.result = this.autoCalcResult(row);
|
|
|
+ }
|
|
|
+ this.$forceUpdate();
|
|
|
+ },
|
|
|
+ // 根据实盘数与账面数自动计算结果
|
|
|
+ autoCalcResult(row) {
|
|
|
+ const actual = parseFloat(row.actualQuantity);
|
|
|
+ const expect = parseFloat(row._measure);
|
|
|
+ if (isNaN(actual) || isNaN(expect)) return null;
|
|
|
+ if (actual > expect) return 'surplus';
|
|
|
+ if (actual < expect) return 'lose';
|
|
|
+ return 'normal';
|
|
|
+ },
|
|
|
+ confirmRow(row) {
|
|
|
+ if (row.actualQuantity == null || row.actualQuantity === '') {
|
|
|
+ this.$message.warning('请先填写实盘数'); return;
|
|
|
+ }
|
|
|
+ const key = row._isUnlisted ? 'unlisted' : row.result;
|
|
|
+ const map = { normal: '正常', surplus: '盘盈', lose: '盘亏', unlisted: '无账货' };
|
|
|
+ if (key && key !== 'normal' && !row.diffReason) {
|
|
|
+ this.$message.warning(`盘点结果为${map[key]},请选择差异原因`); return;
|
|
|
+ }
|
|
|
+ row._status = 1;
|
|
|
+ row._dirty = true;
|
|
|
+ this.$forceUpdate();
|
|
|
+ },
|
|
|
+ cancelRow(row) {
|
|
|
+ row._status = 0;
|
|
|
+ row._dirty = true;
|
|
|
+ this.$forceUpdate();
|
|
|
+ },
|
|
|
+ confirmAll() {
|
|
|
+ const allRows = [...this.serverRows, ...this.unlistedRows];
|
|
|
+ const emptyRows = allRows.filter((r) => r.actualQuantity == null || r.actualQuantity === '');
|
|
|
+ if (emptyRows.length > 0) {
|
|
|
+ const nums = emptyRows.map((r, i) => r._index || i + 1).join('、');
|
|
|
+ this.$message.warning(`序号:${nums},未盘,请录入实盘数。`); return;
|
|
|
+ }
|
|
|
+ const missingReason = this.serverRows.filter((r) => {
|
|
|
+ if (r._status === 1) return false;
|
|
|
+ const key = r.result;
|
|
|
+ return key && key !== 'normal' && !r.diffReason;
|
|
|
+ });
|
|
|
+ if (missingReason.length > 0) {
|
|
|
+ this.$message.warning('存在差异行未填写差异原因,请先确认或补录'); return;
|
|
|
+ }
|
|
|
+ this.serverRows.forEach((r) => { if (r._status === 0) r._status = 1; });
|
|
|
+ this.unlistedRows.forEach((r) => { r._status = 1; });
|
|
|
+ this.$message.success('全部确认成功');
|
|
|
+ this.$forceUpdate();
|
|
|
+ },
|
|
|
+ cancelAll() {
|
|
|
+ this.serverRows.forEach((r) => { r._status = 0; r._dirty = true; });
|
|
|
+ this.unlistedRows.forEach((r) => { r._status = 0; r._dirty = true; });
|
|
|
+ this.$message.success('已全部取消确认');
|
|
|
+ this.$forceUpdate();
|
|
|
+ },
|
|
|
+ addUnlisted() {
|
|
|
+ const row = {
|
|
|
+ rowKey: `unlisted-${Date.now()}-${Math.random()}`,
|
|
|
+ categoryName: '', categoryCode: '', batchNo: '', packageNo: '', measureUnit: '',
|
|
|
+ actualQuantity: null, result: 'unlisted', description: '', diffReason: null, diffImages: [],
|
|
|
+ _isUnlisted: true, _status: 0, _dirty: true, _measure: 0
|
|
|
+ };
|
|
|
+ this.unlistedRows.push(row);
|
|
|
+ // 触发 ele-pro-table 重载让无账货行显示
|
|
|
+ this._reloadTable();
|
|
|
+ },
|
|
|
+ removeUnlisted(row) {
|
|
|
+ const idx = this.unlistedRows.indexOf(row);
|
|
|
+ if (idx > -1) {
|
|
|
+ this.unlistedRows.splice(idx, 1);
|
|
|
+ this._reloadTable();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // ============= 数据加载 =============
|
|
|
+ datasource({ page, limit, where }) {
|
|
|
+ return getPlanDetailList({
|
|
|
+ pageNum: page,
|
|
|
+ size: limit,
|
|
|
+ planOrderId: this.planOrderId,
|
|
|
+ ...where
|
|
|
+ }).then((res) => {
|
|
|
+ const list = (res.list || []).map((item, idx) => {
|
|
|
+ const existing = this.serverRows.find((r) => r.id === item.id && !r._isUnlisted);
|
|
|
+ const row = existing ? { ...item, ...existing } : { ...item };
|
|
|
+ row.rowKey = item.id || `server-${idx}`;
|
|
|
+ row._status = row._status != null ? row._status : 0;
|
|
|
+ row._measure = parseFloat(item.info?.measureQuantity ?? item.measureQuantity) || 0;
|
|
|
+ if (!existing && row.actualQuantity != null && row.actualQuantity !== '') {
|
|
|
+ row.result = this.autoCalcResult(row);
|
|
|
+ }
|
|
|
+ return row;
|
|
|
+ });
|
|
|
+ this.serverRows = list;
|
|
|
+ const merged = [...list];
|
|
|
+ if (page === 1 && this.unlistedRows.length) {
|
|
|
+ merged.push(...this.unlistedRows);
|
|
|
+ }
|
|
|
+ return { list: merged, count: res.count || 0 };
|
|
|
+ });
|
|
|
+ },
|
|
|
+ search() {
|
|
|
+ this.$nextTick(() => this._reloadTable());
|
|
|
+ },
|
|
|
+ resetFilter() {
|
|
|
+ this.filterForm.keyWord = '';
|
|
|
+ this.filterForm.statusFilter = '';
|
|
|
+ this.search();
|
|
|
+ },
|
|
|
+ onFilterChange() {
|
|
|
+ this.search();
|
|
|
+ },
|
|
|
+ _reloadTable() {
|
|
|
+ if (this.$refs.table && this.$refs.table.reload) {
|
|
|
+ this.$refs.table.reload({ page: 1 });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // ============= 保存 / 报工 =============
|
|
|
+ buildReportPayload() {
|
|
|
+ const payload = [];
|
|
|
+ this.serverRows.forEach((row) => {
|
|
|
+ if (row._status !== 1) return;
|
|
|
+ const key = row.result;
|
|
|
+ if (!key) return;
|
|
|
+ const item = { id: row.id, planOrderId: this.planOrderId, planDetailId: row.id,
|
|
|
+ categoryId: row.categoryId, categoryName: row.categoryName, batchNo: row.batchNo,
|
|
|
+ packageNo: row.packageNo,
|
|
|
+ status: key === 'surplus' ? 2 : key === 'lose' ? 3 : 1,
|
|
|
+ description: row.description || '', diffReason: row.diffReason, diffImages: this.toImageUrls(row.diffImages) };
|
|
|
+ if (key === 'surplus') item.surplusQuantity = row.actualQuantity;
|
|
|
+ else if (key === 'lose') item.loseQuantity = row.actualQuantity;
|
|
|
+ else item.normalQuantity = row.actualQuantity;
|
|
|
+ payload.push(item);
|
|
|
+ });
|
|
|
+ this.unlistedRows.forEach((row) => {
|
|
|
+ if (!row.categoryName || row.actualQuantity == null) return;
|
|
|
+ payload.push({
|
|
|
+ planOrderId: this.planOrderId, categoryName: row.categoryName, categoryCode: row.categoryCode,
|
|
|
+ batchNo: row.batchNo, packageNo: row.packageNo, measureUnit: row.measureUnit,
|
|
|
+ status: 4, surplusQuantity: row.actualQuantity,
|
|
|
+ description: row.description || '', diffReason: row.diffReason, diffImages: this.toImageUrls(row.diffImages)
|
|
|
+ });
|
|
|
+ });
|
|
|
+ return payload;
|
|
|
+ },
|
|
|
+ buildSavePayload() {
|
|
|
+ const payload = [];
|
|
|
+ this.serverRows
|
|
|
+ .filter((r) => r._dirty && r.actualQuantity != null && r.actualQuantity !== '')
|
|
|
+ .forEach((r) => {
|
|
|
+ payload.push({ id: r.id, planOrderId: this.planOrderId, planDetailId: r.id,
|
|
|
+ actualQuantity: r.actualQuantity, status: r._status, description: r.description || '', diffReason: r.diffReason,
|
|
|
+ diffImages: this.toImageUrls(r.diffImages) });
|
|
|
+ });
|
|
|
+ return payload;
|
|
|
+ },
|
|
|
+ toImageUrls(images) {
|
|
|
+ if (!Array.isArray(images)) return [];
|
|
|
+ return images.map((i) => (typeof i === 'string' ? i : i.url || i.storePath || '')).filter(Boolean);
|
|
|
+ },
|
|
|
+ onSave() {
|
|
|
+ const payload = this.buildSavePayload();
|
|
|
+ if (payload.length === 0) { this.$message.info('没有需要保存的数据'); return; }
|
|
|
+ saveReportWork(payload, { planOrderId: this.planOrderId })
|
|
|
+ .then(() => { this.$message.success('保存成功'); this.serverRows.forEach((r) => (r._dirty = false)); })
|
|
|
+ .catch((e) => { this.$message.error(e?.message || '保存失败'); });
|
|
|
+ },
|
|
|
+ onReport() {
|
|
|
+ const allRows = [...this.serverRows, ...this.unlistedRows];
|
|
|
+ const noActual = allRows.filter((r) => r.actualQuantity == null || r.actualQuantity === '');
|
|
|
+ if (noActual.length > 0) {
|
|
|
+ const nums = noActual.map((r, i) => r._index || i + 1).join('、');
|
|
|
+ this.$message.warning(`序号:${nums},未盘,请录入实盘数。`); return;
|
|
|
+ }
|
|
|
+ const notConfirmed = allRows.filter((r) => r._status !== 1);
|
|
|
+ if (notConfirmed.length > 0) {
|
|
|
+ const nums = notConfirmed.map((r, i) => r._index || i + 1).join('、');
|
|
|
+ this.$message.warning(`序号:${nums},未盘,请先确认。`); return;
|
|
|
+ }
|
|
|
+ const noReason = this.serverRows.filter((r) => {
|
|
|
+ const key = r.result;
|
|
|
+ return key && key !== 'normal' && !r.diffReason;
|
|
|
+ });
|
|
|
+ if (noReason.length > 0) { this.$message.warning('存在盘点差异未填写差异原因,请补充'); return; }
|
|
|
+ const invalidUnlisted = this.unlistedRows.filter((r) => !r.categoryName || r.actualQuantity == null || !r.diffReason);
|
|
|
+ if (invalidUnlisted.length > 0) { this.$message.warning('请完善无账货行:物品名称、实盘数、差异原因'); return; }
|
|
|
+ const payload = this.buildReportPayload();
|
|
|
+ if (payload.length === 0) { this.$message.info('没有需要报工的数据'); return; }
|
|
|
+ this.reportLoading = true;
|
|
|
+ reportWork(payload, { planOrderId: this.planOrderId })
|
|
|
+ .then(() => { this.$message.success('报工成功'); this.$emit('flushed'); this.closeWindows(); })
|
|
|
+ .catch((e) => { this.$message.error(e?.message || '报工失败'); })
|
|
|
+ .finally(() => { this.reportLoading = false; });
|
|
|
+ },
|
|
|
+ open(id) {
|
|
|
+ this.planOrderId = id;
|
|
|
+ this.serverRows = [];
|
|
|
+ this.unlistedRows = [];
|
|
|
+ this.filterForm = {
|
|
|
+ warehouseName: '',
|
|
|
+ dimension: '',
|
|
|
+ areaId: '',
|
|
|
+ shelfId: '',
|
|
|
+ keyWord: '',
|
|
|
+ statusFilter: ''
|
|
|
+ };
|
|
|
+ this.areaList = [];
|
|
|
+ this.shelfList = [];
|
|
|
+ this.visibleDialog = true;
|
|
|
+ this.$nextTick(() => this._reloadTable());
|
|
|
+ },
|
|
|
+ closeWindows() {
|
|
|
+ this.visibleDialog = false;
|
|
|
+ this.serverRows = [];
|
|
|
+ this.unlistedRows = [];
|
|
|
+ this.planOrderId = '';
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+:deep(.el-checkbox__input.is-disabled .el-checkbox__inner) {
|
|
|
+ background-color: #eee !important;
|
|
|
+}
|
|
|
+
|
|
|
+.dispose-dialog {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+}
|
|
|
+
|
|
|
+.ele-form-actions {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: flex-end;
|
|
|
+}
|
|
|
+
|
|
|
+.dispose-stat {
|
|
|
+ flex: 1;
|
|
|
+ min-width: 580px;
|
|
|
+ margin-left: 8px;
|
|
|
+ font-size: 13px;
|
|
|
+ color: #606266;
|
|
|
+ white-space: nowrap;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+
|
|
|
+ b {
|
|
|
+ color: #409eff;
|
|
|
+ font-weight: 600;
|
|
|
+ margin: 0 2px;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+.dispose-footer {
|
|
|
+ text-align: right;
|
|
|
+ margin-top: 12px;
|
|
|
+}
|
|
|
+</style>
|