|
|
@@ -0,0 +1,1156 @@
|
|
|
+<template>
|
|
|
+ <ele-modal
|
|
|
+ v-if="visible"
|
|
|
+ custom-class="ele-dialog-form long-dialog-form"
|
|
|
+ :centered="true"
|
|
|
+ :append-to-body="true"
|
|
|
+ :visible.sync="dialogVisible"
|
|
|
+ :close-on-click-modal="false"
|
|
|
+ width="70%"
|
|
|
+ :maxable="true"
|
|
|
+ :resizable="true"
|
|
|
+ :title="title"
|
|
|
+ @close="onModalClose"
|
|
|
+ >
|
|
|
+ <div class="work-order-handover" v-loading="loading">
|
|
|
+ <div class="handover_content">
|
|
|
+ <!-- 工单信息 -->
|
|
|
+ <div class="card_box">
|
|
|
+ <div class="title_box rx-bc">
|
|
|
+ <div class="name">工单信息</div>
|
|
|
+ </div>
|
|
|
+ <div class="material">
|
|
|
+ <div class="content_table">
|
|
|
+ <div class="item rx-sc">
|
|
|
+ <div class="rx ww16">
|
|
|
+ <div class="lable rx-cc">工单编码</div>
|
|
|
+ <div class="content">{{ workOrderInfo.workOrderCode }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="rx ww16">
|
|
|
+ <div class="lable rx-cc">产品编码</div>
|
|
|
+ <div class="content">{{ workOrderInfo.categoryCode }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="rx ww16">
|
|
|
+ <div class="lable rx-cc">批次号</div>
|
|
|
+ <div class="content">{{ workOrderInfo.batchNo }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="item rx-sc">
|
|
|
+ <div class="rx ww16">
|
|
|
+ <div class="lable rx-cc">产品名称</div>
|
|
|
+ <div class="content">{{ workOrderInfo.categoryName }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="rx ww16">
|
|
|
+ <div class="lable rx-cc">要求生产数量</div>
|
|
|
+ <div class="content"
|
|
|
+ >{{ workOrderInfo.formingNum }}{{ workOrderInfo.unit }}</div
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ <div class="rx ww16">
|
|
|
+ <div class="lable rx-cc">要求生产重量</div>
|
|
|
+ <div class="content"
|
|
|
+ >{{ workOrderInfo.formingWeight
|
|
|
+ }}{{ workOrderInfo.weightUnit }}</div
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="item rx-sc">
|
|
|
+ <div class="rx ww16">
|
|
|
+ <div class="lable rx-cc">生产编号</div>
|
|
|
+ <div class="content">{{ workOrderInfo.productionCodes }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="rx ww16">
|
|
|
+ <div class="lable rx-cc">已生产数量</div>
|
|
|
+ <div class="content"
|
|
|
+ >{{ workOrderInfo.formedNum || 0
|
|
|
+ }}{{ workOrderInfo.unit }}</div
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ <div class="rx ww16">
|
|
|
+ <div class="lable rx-cc">已生产重量</div>
|
|
|
+ <div class="content"
|
|
|
+ >{{ workOrderInfo.formedWeight || 0
|
|
|
+ }}{{ workOrderInfo.weightUnit }}</div
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="item rx-sc">
|
|
|
+ <div class="rx ww16">
|
|
|
+ <div class="lable rx-cc">当前工序</div>
|
|
|
+ <div class="content">{{
|
|
|
+ (workOrderInfo.currentTaskDiagram || {}).taskTypeName ||
|
|
|
+ workOrderInfo.initiateTaskName ||
|
|
|
+ ''
|
|
|
+ }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="rx ww16">
|
|
|
+ <div class="lable rx-cc"></div>
|
|
|
+ <div class="content"></div>
|
|
|
+ </div>
|
|
|
+ <div class="rx ww16">
|
|
|
+ <div class="lable rx-cc"></div>
|
|
|
+ <div class="content"></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 交接信息 -->
|
|
|
+ <div class="card_box">
|
|
|
+ <div class="title_box rx-bc">
|
|
|
+ <div class="name">交接信息</div>
|
|
|
+ </div>
|
|
|
+ <el-form
|
|
|
+ ref="handoverForm"
|
|
|
+ :model="workOrderInfo"
|
|
|
+ :rules="handoverRules"
|
|
|
+ label-width="100px"
|
|
|
+ class="handover_form"
|
|
|
+ size="small"
|
|
|
+ >
|
|
|
+ <div class="form_row">
|
|
|
+ <el-form-item
|
|
|
+ label="交接类型"
|
|
|
+ prop="type"
|
|
|
+ class="form_item"
|
|
|
+ required
|
|
|
+ >
|
|
|
+ <el-select
|
|
|
+ v-model="workOrderInfo.type"
|
|
|
+ placeholder="请选择"
|
|
|
+ style="width: 100%"
|
|
|
+ :disabled="formLocked"
|
|
|
+ >
|
|
|
+ <el-option label="物品移交" :value="1" />
|
|
|
+ <el-option label="班次交接" :value="2" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item
|
|
|
+ label="交接时间"
|
|
|
+ prop="initiateTime"
|
|
|
+ class="form_item"
|
|
|
+ required
|
|
|
+ >
|
|
|
+ <el-date-picker
|
|
|
+ v-model="workOrderInfo.initiateTime"
|
|
|
+ type="datetime"
|
|
|
+ format="yyyy-MM-dd HH:mm:ss"
|
|
|
+ value-format="yyyy-MM-dd HH:mm:ss"
|
|
|
+ placeholder="选择交接时间"
|
|
|
+ style="width: 100%"
|
|
|
+ :disabled="formLocked"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="交接单编码" class="form_item">
|
|
|
+ <el-input
|
|
|
+ v-model="handoverCodeDisplay"
|
|
|
+ disabled
|
|
|
+ placeholder="系统自动生成"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </div>
|
|
|
+ <div class="form_row">
|
|
|
+ <el-form-item
|
|
|
+ label="交接班组"
|
|
|
+ prop="acceptTeamId"
|
|
|
+ class="form_item"
|
|
|
+ required
|
|
|
+ v-if="workOrderInfo.type == 1"
|
|
|
+ >
|
|
|
+ <el-select
|
|
|
+ v-model="workOrderInfo.acceptTeamId"
|
|
|
+ filterable
|
|
|
+ :disabled="formLocked"
|
|
|
+ @change="onHandoverTeamChange"
|
|
|
+ style="width: 100%"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in teamList"
|
|
|
+ :key="item.id"
|
|
|
+ :label="item.name"
|
|
|
+ :value="item.id"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item
|
|
|
+ label="交接人"
|
|
|
+ prop="acceptId"
|
|
|
+ class="form_item"
|
|
|
+ required
|
|
|
+ v-if="workOrderInfo.type == 1"
|
|
|
+ >
|
|
|
+ <el-select
|
|
|
+ v-model="workOrderInfo.acceptId"
|
|
|
+ filterable
|
|
|
+ :disabled="formLocked"
|
|
|
+ @change="onAcceptPersonChange"
|
|
|
+ style="width: 100%"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in personList"
|
|
|
+ :key="item.id"
|
|
|
+ :label="item.name"
|
|
|
+ :value="item.id"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <div class="form_item form_item_empty"></div>
|
|
|
+ </div>
|
|
|
+ <div class="form_row">
|
|
|
+ <el-form-item
|
|
|
+ label="交接数量"
|
|
|
+ prop="handoverQuantity"
|
|
|
+ class="form_item"
|
|
|
+ required
|
|
|
+ >
|
|
|
+ <el-input
|
|
|
+ v-model="workOrderInfo.handoverQuantity"
|
|
|
+ placeholder="请输入"
|
|
|
+ :disabled="formLocked"
|
|
|
+ @input="
|
|
|
+ batchInput(
|
|
|
+ workOrderInfo.handoverQuantity,
|
|
|
+ 'handoverQuantity'
|
|
|
+ )
|
|
|
+ "
|
|
|
+ >
|
|
|
+ <template slot="append">PCS</template>
|
|
|
+ </el-input>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item
|
|
|
+ label="交接重量"
|
|
|
+ prop="handoverWeight"
|
|
|
+ class="form_item"
|
|
|
+ required
|
|
|
+ >
|
|
|
+ <el-input
|
|
|
+ v-model="workOrderInfo.handoverWeight"
|
|
|
+ placeholder="请输入"
|
|
|
+ :disabled="formLocked"
|
|
|
+ @input="
|
|
|
+ batchInput(workOrderInfo.handoverWeight, 'handoverWeight')
|
|
|
+ "
|
|
|
+ >
|
|
|
+ <template slot="append">KG</template>
|
|
|
+ </el-input>
|
|
|
+ </el-form-item>
|
|
|
+ <div class="form_item form_item_empty"></div>
|
|
|
+ </div>
|
|
|
+ <div class="form_row_full">
|
|
|
+ <el-form-item
|
|
|
+ label="交接注意事项"
|
|
|
+ prop="remark"
|
|
|
+ class="form_item_full"
|
|
|
+ >
|
|
|
+ <el-input
|
|
|
+ v-model="workOrderInfo.remark"
|
|
|
+ type="textarea"
|
|
|
+ :rows="3"
|
|
|
+ placeholder="请输入"
|
|
|
+ :disabled="formLocked"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </div>
|
|
|
+ </el-form>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 物料清单 -->
|
|
|
+ <div class="card_box">
|
|
|
+ <div class="title_box rx-bc">
|
|
|
+ <div class="name">物料清单</div>
|
|
|
+ <div class="rx-bc action_btns" v-if="!formLocked">
|
|
|
+ <el-button type="primary" size="small" @click="addMaterial"
|
|
|
+ >添加物料</el-button
|
|
|
+ >
|
|
|
+ <el-button type="danger" size="small" @click="batchDeleteMaterial"
|
|
|
+ >批量删除</el-button
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <ele-pro-table
|
|
|
+ ref="table"
|
|
|
+ :key="'handover-material-' + (formLocked ? 'ro' : 'rw')"
|
|
|
+ :needPage="false"
|
|
|
+ :columns="columns"
|
|
|
+ :datasource="workOrderInfo.detailList"
|
|
|
+ class="time-form"
|
|
|
+ max-height="600"
|
|
|
+ @columns-change="handleColumnChange"
|
|
|
+ :cache-key="cacheKeyUrl"
|
|
|
+ :selection.sync="selection"
|
|
|
+ row-key="id"
|
|
|
+ >
|
|
|
+ <template v-slot:action="{ row }">
|
|
|
+ <el-popconfirm
|
|
|
+ class="ele-action"
|
|
|
+ title="确定要删除此物料吗?"
|
|
|
+ @confirm="remove(row)"
|
|
|
+ >
|
|
|
+ <template v-slot:reference>
|
|
|
+ <el-link type="danger" :underline="false"> 删除 </el-link>
|
|
|
+ </template>
|
|
|
+ </el-popconfirm>
|
|
|
+ </template>
|
|
|
+ </ele-pro-table>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 接收信息:status 为 1(待接收)且非详情时展示 -->
|
|
|
+ <div class="card_box receive_card" v-if="showReceiveBlock">
|
|
|
+ <div class="title_box rx-bc">
|
|
|
+ <div class="name">接收信息</div>
|
|
|
+ </div>
|
|
|
+ <el-form label-width="100px" size="small" class="receive_form">
|
|
|
+ <el-form-item label="接收建议" required>
|
|
|
+ <el-input
|
|
|
+ v-model="workOrderInfo.acceptRemark"
|
|
|
+ type="textarea"
|
|
|
+ :rows="4"
|
|
|
+ placeholder="请输入"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <div class="receive_actions">
|
|
|
+ <el-button
|
|
|
+ type="success"
|
|
|
+ size="small"
|
|
|
+ :loading="receiveSubmitting"
|
|
|
+ @click="handleReceiveDecision(2)"
|
|
|
+ >同意</el-button
|
|
|
+ >
|
|
|
+ <el-button
|
|
|
+ type="danger"
|
|
|
+ size="small"
|
|
|
+ :loading="receiveSubmitting"
|
|
|
+ @click="handleReceiveDecision(3)"
|
|
|
+ >驳回</el-button
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ </el-form>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <pickingList
|
|
|
+ v-if="!formLocked"
|
|
|
+ isType="pick"
|
|
|
+ ref="pickingListRef"
|
|
|
+ @allSelection="allSelection"
|
|
|
+ ></pickingList>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div slot="footer" class="dialog-footer" v-if="showMainFooter">
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ size="small"
|
|
|
+ :loading="submitLoading"
|
|
|
+ @click="submitHandover(1)"
|
|
|
+ >提交</el-button
|
|
|
+ >
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ size="small"
|
|
|
+ :loading="submitLoading"
|
|
|
+ @click="submitHandover(0)"
|
|
|
+ >保存</el-button
|
|
|
+ >
|
|
|
+ <el-button size="small" :disabled="submitLoading" @click="onCancel"
|
|
|
+ >取 消</el-button
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ </ele-modal>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+ import { getteampage } from '@/api/workforceManagement/team';
|
|
|
+ import {
|
|
|
+ getHandoverById,
|
|
|
+ updateHandover,
|
|
|
+ acceptOrRejectHandover
|
|
|
+ } from '@/api/workOrderHandover';
|
|
|
+ import pickingList from '@/views/produce/components/picking/newPickingList.vue';
|
|
|
+ export default {
|
|
|
+ name: 'WorkOrderHandoverEdit',
|
|
|
+
|
|
|
+ components: {
|
|
|
+ pickingList
|
|
|
+ },
|
|
|
+
|
|
|
+ props: {
|
|
|
+ visible: {
|
|
|
+ type: Boolean,
|
|
|
+ default: true
|
|
|
+ },
|
|
|
+ title: {
|
|
|
+ type: String,
|
|
|
+ default: '工单交接'
|
|
|
+ },
|
|
|
+ /** 交接单主键 id,编辑/查看时传入;新增不传或传空 */
|
|
|
+ workOrderId: {
|
|
|
+ type: [String, Number],
|
|
|
+ default: ''
|
|
|
+ },
|
|
|
+ /** 列表行状态:0 待提交 1 待接收 2 已接收 3 已驳回 */
|
|
|
+ status: {
|
|
|
+ type: [Number, String],
|
|
|
+ default: 0
|
|
|
+ },
|
|
|
+ /** 仅查看详情:全部只读且不展示任何操作按钮 */
|
|
|
+ detail: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ loading: false,
|
|
|
+ dialogVisible: false,
|
|
|
+ workOrderInfo: {
|
|
|
+ acceptRemark: '',
|
|
|
+ detailList: []
|
|
|
+ },
|
|
|
+ handoverRules: {
|
|
|
+ type: [
|
|
|
+ { required: true, message: '请选择交接类型', trigger: 'change' }
|
|
|
+ ],
|
|
|
+ initiateTime: [
|
|
|
+ { required: true, message: '请选择交接时间', trigger: 'change' }
|
|
|
+ ],
|
|
|
+ acceptTeamId: [
|
|
|
+ { required: true, message: '请选择交接班组', trigger: 'change' }
|
|
|
+ ],
|
|
|
+ acceptId: [
|
|
|
+ { required: true, message: '请选择交接人', trigger: 'change' }
|
|
|
+ ],
|
|
|
+ handoverQuantity: [
|
|
|
+ { required: true, message: '请选择交接数量', trigger: 'change' }
|
|
|
+ ],
|
|
|
+ handoverWeight: [
|
|
|
+ { required: true, message: '请选择交接重量', trigger: 'change' }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ teamList: [],
|
|
|
+ personList: [],
|
|
|
+ selection: [],
|
|
|
+ cacheKeyUrl: 'mes_workOrderHandover_materialTable',
|
|
|
+ receiveSubmitting: false,
|
|
|
+ submitLoading: false
|
|
|
+ };
|
|
|
+ },
|
|
|
+ watch: {
|
|
|
+ visible: {
|
|
|
+ immediate: true,
|
|
|
+ handler(v) {
|
|
|
+ this.dialogVisible = v;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ /** 与 ele-modal 同步:弹窗真正显示为 true 时再拉取详情(不监听 workOrderId) */
|
|
|
+ dialogVisible(v) {
|
|
|
+ if (v) {
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.bootstrapDialog();
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ computed: {
|
|
|
+ numericStatus() {
|
|
|
+ const s = this.status;
|
|
|
+ if (s === '' || s === null || s === undefined) return 0;
|
|
|
+ const n = Number(s);
|
|
|
+ return Number.isNaN(n) ? 0 : n;
|
|
|
+ },
|
|
|
+ /** 详情 或 待接收:交接信息与物料不可编辑 */
|
|
|
+ formLocked() {
|
|
|
+ if (this.detail) return true;
|
|
|
+ return this.numericStatus === 1;
|
|
|
+ },
|
|
|
+ /** 展示底部 提交 / 保存 / 取消 */
|
|
|
+ showMainFooter() {
|
|
|
+ if (this.detail) return false;
|
|
|
+ return this.numericStatus !== 1;
|
|
|
+ },
|
|
|
+ /** 展示「接收信息」区块(同意 / 驳回) */
|
|
|
+ showReceiveBlock() {
|
|
|
+ if (this.detail) return false;
|
|
|
+ return this.numericStatus === 1;
|
|
|
+ },
|
|
|
+ handoverCodeDisplay() {
|
|
|
+ const w = this.workOrderInfo || {};
|
|
|
+ return w.handoverCode || w.code || '';
|
|
|
+ },
|
|
|
+ columns() {
|
|
|
+ const cols = [];
|
|
|
+ if (!this.formLocked) {
|
|
|
+ cols.push({
|
|
|
+ width: 45,
|
|
|
+ type: 'selection',
|
|
|
+ columnKey: 'selection',
|
|
|
+ align: 'center',
|
|
|
+ fixed: 'left',
|
|
|
+ reserveSelection: true
|
|
|
+ });
|
|
|
+ }
|
|
|
+ cols.push(
|
|
|
+ {
|
|
|
+ width: 55,
|
|
|
+ type: 'index',
|
|
|
+ columnKey: 'index',
|
|
|
+ align: 'center',
|
|
|
+ label: '序号',
|
|
|
+ fixed: 'left'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'categoryCode',
|
|
|
+ label: '编码',
|
|
|
+ align: 'center',
|
|
|
+ showOverflowTooltip: true,
|
|
|
+ sortable: true,
|
|
|
+ minWidth: 110
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'categoryName',
|
|
|
+ label: '名称',
|
|
|
+ align: 'center',
|
|
|
+ showOverflowTooltip: true,
|
|
|
+ minWidth: 110
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'categoryLevelPath',
|
|
|
+ label: '分类',
|
|
|
+ align: 'center',
|
|
|
+ showOverflowTooltip: true
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'brandNum',
|
|
|
+ align: 'center',
|
|
|
+ label: '牌号',
|
|
|
+ showOverflowTooltip: true
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'modelType',
|
|
|
+ label: '型号',
|
|
|
+ align: 'center',
|
|
|
+ showOverflowTooltip: true
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'specification',
|
|
|
+ label: '规格',
|
|
|
+ align: 'center',
|
|
|
+ showOverflowTooltip: true
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'measureUnit',
|
|
|
+ label: '计量单位',
|
|
|
+ showOverflowTooltip: true,
|
|
|
+ minWidth: 90
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'weightUnit',
|
|
|
+ label: '重量单位',
|
|
|
+ showOverflowTooltip: true,
|
|
|
+ minWidth: 90
|
|
|
+ }
|
|
|
+ );
|
|
|
+ if (!this.formLocked) {
|
|
|
+ cols.push({
|
|
|
+ columnKey: 'action',
|
|
|
+ label: '操作',
|
|
|
+ width: 100,
|
|
|
+ align: 'center',
|
|
|
+ resizable: false,
|
|
|
+ slot: 'action',
|
|
|
+ fixed: 'right'
|
|
|
+ });
|
|
|
+ }
|
|
|
+ return cols;
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ methods: {
|
|
|
+ hasValidId(val) {
|
|
|
+ return (
|
|
|
+ val !== '' &&
|
|
|
+ val !== null &&
|
|
|
+ val !== undefined &&
|
|
|
+ String(val).length > 0
|
|
|
+ );
|
|
|
+ },
|
|
|
+ getSafeDetailList() {
|
|
|
+ return Array.isArray(this.workOrderInfo.detailList)
|
|
|
+ ? this.workOrderInfo.detailList
|
|
|
+ : [];
|
|
|
+ },
|
|
|
+ closeDialogWithSuccess(res) {
|
|
|
+ this.dialogVisible = false;
|
|
|
+ this.$emit('update:visible', false);
|
|
|
+ this.$emit('success', res);
|
|
|
+ },
|
|
|
+ closeDialogByCancel() {
|
|
|
+ this.dialogVisible = false;
|
|
|
+ this.$emit('update:visible', false);
|
|
|
+ this.$emit('cancel');
|
|
|
+ },
|
|
|
+ clearAndRestoreTableSelection() {
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.$refs.table && this.$refs.table.clearSelection();
|
|
|
+ this.selection.forEach((item) => {
|
|
|
+ this.$refs.table && this.$refs.table.toggleRowSelection(item, true);
|
|
|
+ });
|
|
|
+ });
|
|
|
+ },
|
|
|
+ formatDateTimeNow() {
|
|
|
+ const d = new Date();
|
|
|
+ const p = (n) => String(n).padStart(2, '0');
|
|
|
+ return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(
|
|
|
+ d.getHours()
|
|
|
+ )}:${p(d.getMinutes())}:${p(d.getSeconds())}`;
|
|
|
+ },
|
|
|
+
|
|
|
+ /** 防止 acceptRemark 未定义导致模板报错 */
|
|
|
+ ensureAcceptRemarkShape() {
|
|
|
+ const w = this.workOrderInfo;
|
|
|
+ if (!w || typeof w !== 'object') return;
|
|
|
+ if (w.acceptRemark === undefined) {
|
|
|
+ this.$set(w, 'acceptRemark', '');
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ pickAcceptRemarkFromRaw(raw) {
|
|
|
+ if (!raw || typeof raw !== 'object') return '';
|
|
|
+ if (raw.acceptRemark !== undefined && raw.acceptRemark !== null) {
|
|
|
+ return raw.acceptRemark;
|
|
|
+ }
|
|
|
+ if (
|
|
|
+ raw.receiveAdvice &&
|
|
|
+ typeof raw.receiveAdvice === 'object' &&
|
|
|
+ raw.receiveAdvice !== null
|
|
|
+ ) {
|
|
|
+ return (
|
|
|
+ raw.receiveAdvice.acceptRemark || raw.receiveAdvice.remark || ''
|
|
|
+ );
|
|
|
+ }
|
|
|
+ if (typeof raw.receiveAdvice === 'string') {
|
|
|
+ return raw.receiveAdvice;
|
|
|
+ }
|
|
|
+ return raw.receiveRemark || '';
|
|
|
+ },
|
|
|
+
|
|
|
+ getEmptyWorkOrderState() {
|
|
|
+ return {
|
|
|
+ type: 1,
|
|
|
+ initiateTime: this.formatDateTimeNow(),
|
|
|
+ handoverCode: '',
|
|
|
+ workOrderCode: '',
|
|
|
+ categoryCode: '',
|
|
|
+ categoryName: '',
|
|
|
+ productCode: '',
|
|
|
+ productName: '',
|
|
|
+ batchNo: '',
|
|
|
+ unit: '',
|
|
|
+ weightUnit: '',
|
|
|
+ formingNum: '',
|
|
|
+ formingWeight: '',
|
|
|
+ productionCodes: '',
|
|
|
+ formedNum: 0,
|
|
|
+ formedWeight: 0,
|
|
|
+ currentTaskDiagram: {},
|
|
|
+ initiateTaskId: '',
|
|
|
+ initiateTaskName: '',
|
|
|
+ acceptTeamId: '',
|
|
|
+ acceptTeamName: '',
|
|
|
+ acceptId: '',
|
|
|
+ acceptName: '',
|
|
|
+ handoverQuantity: '',
|
|
|
+ handoverWeight: '',
|
|
|
+ remark: '',
|
|
|
+ acceptRemark: '',
|
|
|
+ detailList: [],
|
|
|
+ workOrderId: ''
|
|
|
+ };
|
|
|
+ },
|
|
|
+
|
|
|
+ normalizeHandoverDetail(raw) {
|
|
|
+ if (!raw || typeof raw !== 'object') {
|
|
|
+ return this.getEmptyWorkOrderState();
|
|
|
+ }
|
|
|
+ const detailList = Array.isArray(raw.detailList) ? raw.detailList : [];
|
|
|
+ const task = raw.currentTaskDiagram || {};
|
|
|
+ return {
|
|
|
+ ...raw,
|
|
|
+ detailList,
|
|
|
+ categoryCode: raw.categoryCode || raw.productCode || '',
|
|
|
+ categoryName: raw.categoryName || raw.productName || '',
|
|
|
+ workOrderCode:
|
|
|
+ raw.workOrderCode ||
|
|
|
+ raw.workOrderNum ||
|
|
|
+ raw.workOrderEntityCode ||
|
|
|
+ '',
|
|
|
+ handoverCode:
|
|
|
+ raw.handoverCode || raw.passonCode || raw.passonOrderCode || '',
|
|
|
+ acceptRemark: this.pickAcceptRemarkFromRaw(raw),
|
|
|
+ currentTaskDiagram:
|
|
|
+ raw.currentTaskDiagram ||
|
|
|
+ (raw.initiateTaskId || raw.initiateTaskName
|
|
|
+ ? {
|
|
|
+ taskId: raw.initiateTaskId,
|
|
|
+ taskTypeName: raw.initiateTaskName
|
|
|
+ }
|
|
|
+ : task)
|
|
|
+ };
|
|
|
+ },
|
|
|
+
|
|
|
+ async loadTeamListOnly() {
|
|
|
+ const factoryId = this.$store.state.user.info.factoryId;
|
|
|
+ const { list } = await getteampage({
|
|
|
+ pageNum: 1,
|
|
|
+ size: -1,
|
|
|
+ factoryId
|
|
|
+ });
|
|
|
+ this.teamList = Array.isArray(list) ? list : [];
|
|
|
+ if (!this.teamList.length) {
|
|
|
+ this.personList = [];
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /** 弹窗打开(visible 为 true)时统一拉取/初始化,不依赖对 workOrderId 的 watch */
|
|
|
+ async bootstrapDialog() {
|
|
|
+ if (!this.visible) return;
|
|
|
+ this.loading = true;
|
|
|
+ try {
|
|
|
+ const id = this.workOrderId;
|
|
|
+ const hasId = this.hasValidId(id);
|
|
|
+
|
|
|
+ await this.loadTeamListOnly();
|
|
|
+
|
|
|
+ if (hasId) {
|
|
|
+ const res = await getHandoverById(id);
|
|
|
+ this.workOrderInfo = this.normalizeHandoverDetail(res);
|
|
|
+ const preferredAcceptId = this.workOrderInfo.acceptId;
|
|
|
+ if (
|
|
|
+ this.workOrderInfo.acceptTeamId != null &&
|
|
|
+ this.workOrderInfo.acceptTeamId !== ''
|
|
|
+ ) {
|
|
|
+ this.syncTeamUsersAndLeader(
|
|
|
+ this.workOrderInfo.acceptTeamId,
|
|
|
+ preferredAcceptId
|
|
|
+ );
|
|
|
+ } else if (this.teamList.length) {
|
|
|
+ const first = this.teamList[0].id;
|
|
|
+ this.workOrderInfo.acceptTeamId = first;
|
|
|
+ this.syncTeamUsersAndLeader(first);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ this.workOrderInfo = this.getEmptyWorkOrderState();
|
|
|
+ if (this.teamList.length) {
|
|
|
+ const first = this.teamList[0].id;
|
|
|
+ this.workOrderInfo.acceptTeamId = first;
|
|
|
+ this.syncTeamUsersAndLeader(first);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ this.$message.error((e && e.message) || '加载交接单失败');
|
|
|
+ this.workOrderInfo = this.getEmptyWorkOrderState();
|
|
|
+ } finally {
|
|
|
+ this.loading = false;
|
|
|
+ this.ensureAcceptRemarkShape();
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.$refs.handoverForm && this.$refs.handoverForm.clearValidate();
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ onHandoverTeamChange(teamId) {
|
|
|
+ this.syncTeamUsersAndLeader(teamId);
|
|
|
+ },
|
|
|
+ onAcceptPersonChange(personId) {
|
|
|
+ const person =
|
|
|
+ this.personList.find(
|
|
|
+ (item) => String(item.id) === String(personId)
|
|
|
+ ) || {};
|
|
|
+ this.workOrderInfo.acceptName = person.name || '';
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * @param preferredAcceptId 详情接口已返回的交接人 id,若在班组人员中则优先保留
|
|
|
+ */
|
|
|
+ syncTeamUsersAndLeader(teamId, preferredAcceptId) {
|
|
|
+ const currentTeam =
|
|
|
+ this.teamList.find((item) => String(item.id) === String(teamId)) ||
|
|
|
+ {};
|
|
|
+ this.workOrderInfo.acceptTeamName = currentTeam.name || '';
|
|
|
+ const users = Array.isArray(currentTeam.userVOList)
|
|
|
+ ? currentTeam.userVOList
|
|
|
+ : [];
|
|
|
+ this.personList = users.map((user) => ({
|
|
|
+ id: user.id,
|
|
|
+ name: user.name
|
|
|
+ }));
|
|
|
+ if (!this.personList.length) {
|
|
|
+ this.workOrderInfo.acceptId = '';
|
|
|
+ this.workOrderInfo.acceptName = '';
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (
|
|
|
+ preferredAcceptId != null &&
|
|
|
+ preferredAcceptId !== '' &&
|
|
|
+ this.personList.some(
|
|
|
+ (u) => String(u.id) === String(preferredAcceptId)
|
|
|
+ )
|
|
|
+ ) {
|
|
|
+ const pick =
|
|
|
+ this.personList.find(
|
|
|
+ (u) => String(u.id) === String(preferredAcceptId)
|
|
|
+ ) || {};
|
|
|
+ this.workOrderInfo.acceptId = pick.id;
|
|
|
+ this.workOrderInfo.acceptName = pick.name || '';
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const leader =
|
|
|
+ this.personList.find(
|
|
|
+ (user) => String(user.id) === String(currentTeam.leaderUserId)
|
|
|
+ ) || this.personList[0];
|
|
|
+ this.workOrderInfo.acceptId = leader.id;
|
|
|
+ this.workOrderInfo.acceptName = leader.name || '';
|
|
|
+ },
|
|
|
+
|
|
|
+ batchInput(val, type) {
|
|
|
+ let newVal = String(val == null ? '' : val).replace(/[^\d.]/g, '');
|
|
|
+
|
|
|
+ if (newVal.startsWith('.')) {
|
|
|
+ newVal = '';
|
|
|
+ }
|
|
|
+
|
|
|
+ const firstDotIndex = newVal.indexOf('.');
|
|
|
+ if (firstDotIndex !== -1) {
|
|
|
+ newVal =
|
|
|
+ newVal.slice(0, firstDotIndex + 1) +
|
|
|
+ newVal.slice(firstDotIndex + 1).replace(/\./g, '');
|
|
|
+ }
|
|
|
+
|
|
|
+ const match = newVal.match(/^(\d+)(\.\d{0,4})?/);
|
|
|
+
|
|
|
+ if (type === 'handoverQuantity') {
|
|
|
+ this.workOrderInfo.handoverQuantity = match ? match[0] : '';
|
|
|
+ } else if (type === 'handoverWeight') {
|
|
|
+ this.workOrderInfo.handoverWeight = match ? match[0] : '';
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ submitHandover(isRelease) {
|
|
|
+ if (this.formLocked || this.detail || this.submitLoading) return;
|
|
|
+ this.$refs.handoverForm.validate((valid) => {
|
|
|
+ if (!valid) return;
|
|
|
+ const detailList = this.getSafeDetailList().map((row) => {
|
|
|
+ const { id, ...rest } = row;
|
|
|
+ return rest;
|
|
|
+ });
|
|
|
+ const param = {
|
|
|
+ ...this.workOrderInfo,
|
|
|
+ detailList,
|
|
|
+ isRelease
|
|
|
+ };
|
|
|
+
|
|
|
+ this.submitLoading = true;
|
|
|
+ updateHandover(param)
|
|
|
+ .then((res) => {
|
|
|
+ this.$message.success(isRelease === 1 ? '提交成功' : '保存成功');
|
|
|
+ this.closeDialogWithSuccess(res);
|
|
|
+ })
|
|
|
+ .catch((e) => {
|
|
|
+ this.$message.error((e && e.message) || '提交失败');
|
|
|
+ })
|
|
|
+ .finally(() => {
|
|
|
+ this.submitLoading = false;
|
|
|
+ });
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ /** 同意 status=2,驳回 status=3 */
|
|
|
+ async handleReceiveDecision(status) {
|
|
|
+ const advice = (this.workOrderInfo.acceptRemark || '').trim();
|
|
|
+ if (!advice) {
|
|
|
+ this.$message.warning('请填写接收建议');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (!this.hasValidId(this.workOrderInfo.id)) {
|
|
|
+ this.$message.warning('缺少交接单信息');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.receiveSubmitting = true;
|
|
|
+ try {
|
|
|
+ const res = await acceptOrRejectHandover({
|
|
|
+ id: this.workOrderInfo.id,
|
|
|
+ status,
|
|
|
+ acceptRemark: advice
|
|
|
+ });
|
|
|
+ this.$message.success(status === 2 ? '接收成功' : '已驳回');
|
|
|
+ this.closeDialogWithSuccess(res);
|
|
|
+ } catch (e) {
|
|
|
+ this.$message.error((e && e.message) || '操作失败');
|
|
|
+ } finally {
|
|
|
+ this.receiveSubmitting = false;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ onModalClose() {
|
|
|
+ this.closeDialogByCancel();
|
|
|
+ },
|
|
|
+ onCancel() {
|
|
|
+ this.closeDialogByCancel();
|
|
|
+ },
|
|
|
+ handleColumnChange() {},
|
|
|
+ addMaterial() {
|
|
|
+ const pickList = this.getSafeDetailList();
|
|
|
+ this.$refs.pickingListRef.open(
|
|
|
+ '',
|
|
|
+ {
|
|
|
+ pickList
|
|
|
+ },
|
|
|
+ '选择列表'
|
|
|
+ );
|
|
|
+ },
|
|
|
+ batchDeleteMaterial() {
|
|
|
+ const selected = Array.isArray(this.selection) ? this.selection : [];
|
|
|
+ if (!selected.length) {
|
|
|
+ this.$message.warning('请先选择要删除的物料');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.$confirm(
|
|
|
+ `确定删除已选中的 ${selected.length} 条物料吗?`,
|
|
|
+ '提示',
|
|
|
+ {
|
|
|
+ type: 'warning'
|
|
|
+ }
|
|
|
+ )
|
|
|
+ .then(() => {
|
|
|
+ const detailList = this.getSafeDetailList();
|
|
|
+ const selectedIds = new Set(
|
|
|
+ selected
|
|
|
+ .map((item) => (item && item.id != null ? String(item.id) : ''))
|
|
|
+ .filter(Boolean)
|
|
|
+ );
|
|
|
+ let next;
|
|
|
+ if (selectedIds.size) {
|
|
|
+ next = detailList.filter(
|
|
|
+ (item) => !selectedIds.has(String(item.id))
|
|
|
+ );
|
|
|
+ } else {
|
|
|
+ next = detailList.filter((item) => !selected.includes(item));
|
|
|
+ }
|
|
|
+ this.$set(this.workOrderInfo, 'detailList', next);
|
|
|
+ this.selection = [];
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.$refs.table && this.$refs.table.clearSelection();
|
|
|
+ });
|
|
|
+ this.$message.success('删除成功');
|
|
|
+ })
|
|
|
+ .catch(() => {});
|
|
|
+ },
|
|
|
+ allSelection(id, list) {
|
|
|
+ this.$set(this.workOrderInfo, 'detailList', list || []);
|
|
|
+ },
|
|
|
+ /* 删除 */
|
|
|
+ remove(row) {
|
|
|
+ if (!row) return;
|
|
|
+ const detailList = this.getSafeDetailList();
|
|
|
+ const next = detailList.filter((item) => {
|
|
|
+ if (row.id != null && item.id != null) {
|
|
|
+ return String(item.id) !== String(row.id);
|
|
|
+ }
|
|
|
+ return item !== row;
|
|
|
+ });
|
|
|
+ this.$set(this.workOrderInfo, 'detailList', next);
|
|
|
+ this.selection = this.selection.filter((item) => {
|
|
|
+ if (row.id != null && item.id != null) {
|
|
|
+ return String(item.id) !== String(row.id);
|
|
|
+ }
|
|
|
+ return item !== row;
|
|
|
+ });
|
|
|
+ this.clearAndRestoreTableSelection();
|
|
|
+ this.$message.success('删除成功');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+ .work-order-handover {
|
|
|
+ background: #fff;
|
|
|
+ padding: 0;
|
|
|
+ max-height: calc(100vh - 220px);
|
|
|
+ overflow-y: auto;
|
|
|
+ }
|
|
|
+
|
|
|
+ .handover_content {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 16px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .card_box {
|
|
|
+ background: #fff;
|
|
|
+ padding: 12px;
|
|
|
+ border-radius: 2px;
|
|
|
+ border: 1px solid #e3e5e5;
|
|
|
+ }
|
|
|
+
|
|
|
+ .title_box {
|
|
|
+ padding: 10px 0;
|
|
|
+ margin-bottom: 8px;
|
|
|
+
|
|
|
+ .name {
|
|
|
+ font-size: 14px;
|
|
|
+ font-style: normal;
|
|
|
+ font-weight: 400;
|
|
|
+ color: #157a2c;
|
|
|
+ padding-left: 5px;
|
|
|
+ position: relative;
|
|
|
+
|
|
|
+ &::before {
|
|
|
+ position: absolute;
|
|
|
+ content: '';
|
|
|
+ left: 0;
|
|
|
+ top: 0;
|
|
|
+ bottom: 0;
|
|
|
+ width: 2px;
|
|
|
+ height: 14px;
|
|
|
+ background: #157a2c;
|
|
|
+ margin: auto;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 工单信息表格 */
|
|
|
+ .material {
|
|
|
+ margin-top: 6px;
|
|
|
+
|
|
|
+ .content_table {
|
|
|
+ width: 100%;
|
|
|
+ max-width: 100%;
|
|
|
+ box-sizing: border-box;
|
|
|
+ border: 1px solid #e3e5e5;
|
|
|
+ overflow: hidden;
|
|
|
+
|
|
|
+ .item {
|
|
|
+ display: flex;
|
|
|
+ min-width: 0;
|
|
|
+ border-bottom: 1px solid #e3e5e5;
|
|
|
+
|
|
|
+ &:last-child {
|
|
|
+ border-bottom: none;
|
|
|
+ }
|
|
|
+
|
|
|
+ .rx {
|
|
|
+ display: flex;
|
|
|
+ flex: 1;
|
|
|
+ min-width: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 三等分:避免 33.33%×3 舍入与 flex-shrink:0 撑破卡片 */
|
|
|
+ .ww16 {
|
|
|
+ flex: 1 1 0;
|
|
|
+ min-width: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .lable {
|
|
|
+ width: 100px;
|
|
|
+ min-width: 100px;
|
|
|
+ flex-shrink: 0;
|
|
|
+ text-align: center;
|
|
|
+ background-color: #f7f9fa;
|
|
|
+ font-size: 12px;
|
|
|
+ border-right: 1px solid #e3e5e5;
|
|
|
+ padding: 8px 4px;
|
|
|
+ box-sizing: border-box;
|
|
|
+ }
|
|
|
+
|
|
|
+ .content {
|
|
|
+ flex: 1;
|
|
|
+ min-width: 0;
|
|
|
+ min-height: 32px;
|
|
|
+ font-size: 13px;
|
|
|
+ padding: 8px 10px;
|
|
|
+ box-sizing: border-box;
|
|
|
+ background: #fff;
|
|
|
+ border-right: 1px solid #e3e5e5;
|
|
|
+ white-space: nowrap;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+
|
|
|
+ &:last-child {
|
|
|
+ border-right: none;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 交接信息表单:三列等宽网格,与第一行对齐;避免 space-between 拉散两列行 */
|
|
|
+ .handover_form {
|
|
|
+ .form_row_full {
|
|
|
+ margin-bottom: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .form_row {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
|
+ column-gap: 16px;
|
|
|
+ margin-bottom: 12px;
|
|
|
+ align-items: start;
|
|
|
+ }
|
|
|
+
|
|
|
+ .form_item {
|
|
|
+ width: 100%;
|
|
|
+ min-width: 0;
|
|
|
+ margin-bottom: 0;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
+ ::v-deep .el-form-item__label {
|
|
|
+ padding-right: 8px;
|
|
|
+ }
|
|
|
+
|
|
|
+ ::v-deep .el-form-item__content {
|
|
|
+ flex: 1;
|
|
|
+ min-width: 0;
|
|
|
+ margin-left: 0 !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ ::v-deep .el-select,
|
|
|
+ ::v-deep .el-date-editor.el-input {
|
|
|
+ width: 100% !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ ::v-deep .el-select .el-input__inner,
|
|
|
+ ::v-deep .el-date-editor .el-input__inner {
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .form_item_empty {
|
|
|
+ min-width: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .form_item_full {
|
|
|
+ width: 100%;
|
|
|
+ margin-bottom: 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .material_table {
|
|
|
+ margin-top: 8px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .receive_card {
|
|
|
+ .receive_form {
|
|
|
+ margin-top: 4px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .receive_actions {
|
|
|
+ display: flex;
|
|
|
+ gap: 12px;
|
|
|
+ padding-left: 100px;
|
|
|
+ margin-top: 4px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+</style>
|