【Exception】Modal中Select组件的下拉选项随页面滚动与Select框分离异常

本文解决AntDesignVue中Modal内的Select组件在页面滚动时出现下拉选项与Select框分离的问题。通过理解Select组件的getPopupContainer属性,设置:getPopupContainer=triggerNode=>triggerNode.parentNode,使下拉菜单正确定位。

案发现场

使用Ant Design Vue,在Modal组件中使用Select组件,

当点击Select组件出现下拉选项后滚动原始页面,出现下拉选项和Select框分离的异常。
在这里插入图片描述
异常代码
在这里插入图片描述

原因分析

Select组件有个API getPopupContainer

官方文档给出的用途解释为:“菜单渲染父节点。默认渲染到 body 上,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位”。

同时官网也强调一个特殊情况需要注意:“如果发现下拉菜单跟随页面滚动,或者需要在其他弹层中触发 Select,请尝试使用 :getPopupContainer="triggerNode => triggerNode.parentNode" 将下拉弹层渲染节点固定在触发器的父元素中”。

官方文档
在这里插入图片描述

解决方案

Select组件中添加:getPopupContainer="triggerNode => triggerNode.parentNode"即可。

在这里插入图片描述

<template> <view class="container"> <view class="header-section"> <cu-button class="btn-add" @click="addMore">添加</cu-button> </view> <view class="data-list"> <view v-for="(item, index) in dataList" :key="index" class="data-item"> <button class="btn-close" @click="deleteRow(index)"> <text class="icon">✕</text> </button> <view class="main-content"> <!-- 合并的标签行和管号信息 --> <view class="header-row"> <view class="info-group"> <view class="label-row"> <text>项目号:{{ item.projectNo }}</text> <text>最新版:{{ item.isTopVersion }}</text> <text>删除:{{ item.isDelete }}</text> <text>暂停:{{ item.isPause }}</text> </view> <view class="info-row"> <text>管号: {{ item.pipeNo }}</text> <text>版本号: {{ item.pipeVersion }}</text> </view> </view> <!-- 操作按钮 --> <view class="btn-pipe-group"> <uni-button size="mini" type="warn" @click="reportAbnormal(index)">异常上报</uni-button> <uni-button size="mini" type="primary" @click="viewInfo(index)">查看详情</uni-button> </view> </view> </view> </view> <view class="bottom-btn-group"> <cu-button style="margin-left: 145px;" @click="confirm">确认</cu-button> </view> </view> <cu-modal :visible="isShowModal" title="异常上报" @cancel="hideModal" @ok="submitModal"> <uni-forms ref="baseForm" :modelValue="exceptionParams" :rules="rules"> <uni-forms-item label="异常类型" required name="exceptionType"> <uni-data-select v-model="item.name" :localdata="exceptionTypeList" placeholder="请选择异常类型" /> </uni-forms-item> <uni-forms-item label="异常描述" required name="exceptionMsg"> <uni-easyinput v-model="exceptionParams.exceptionMsg"/> </uni-forms-item> </uni-forms> </cu-modal> </view> </template> <script setup> import { onMounted, ref } from 'vue' import { useRoute } from 'vue-router' import * as serve from '@/api/modules/piping/pre/prePipeFeedBack' import {getDislist} from "@/api/modules/piping/handover"; const route = useRoute() const dataList = ref([]) const isShowModal = ref(false) const exceptionParams = ref({ exceptionType: '', exceptionMsg: '' }) const rules = ref({ exceptionType: [ { required: true, message: '请选择异常类型' } ], exceptionMsg: [ { required: true, message: '请填写异常描述' } ] }) const exceptionTypeList = ref([]) onMounted(() => { uni.$on('selectedPipesToAdd', (data) => { const newItems = data.map(p => ({ // 保持原有的所有字段 ...p, // 添加缺失的字段 pipeNo: p.bomNo, version: p.bomVersion, isCompleted:false })) dataList.value.push(...newItems) uni.removeStorageSync('selectedPipesToAdd') }) const initialSelectedPipes = uni.getStorageSync('initialSelectedPipes') || [] console.log('初始选中的管段:', initialSelectedPipes) if (initialSelectedPipes.length===0){ serve.workOrderPipeDetail({ orderNo: route.query.orderNo }).then((res) => { if (res.status === 200) { dataList.value = res.data || [] } }) }else { const initPipes= initialSelectedPipes.map(p => ({ projectNo: p.projectNo, // 兼容确认时使用的字段 isTopVersion: p.isTopVersion, isDelete: p.isDelete, isPause: p.isPause, bomNo: p.pipeNo, bomVersion: p.version, processIndex:p.processIndex, isCompleted: p.isCompleted !== undefined ? p.isCompleted : false })) dataList.value=initPipes } getDislist({ code: "CP_EXCEPTION_TYPE" }).then((res) => { if (res.status === 200) { exceptionTypeList.value = res.data || [] } }) }) const exceptionTypeChange = (index, event) => { exceptionTypeList.value[index].name = event.detail.value } // 删除条目 const deleteRow = (index) => { dataList.value.splice(index, 1) } // 异常上报 const reportAbnormal = (index) => { console.log('异常上报:', index) isShowModal.value = true } const hideModal = () => { isShowModal.value = false } // 查看信息 const viewInfo = (index) => { console.log('查看信息:', index) } const confirm = () => { // 校验小工序是否选择 let hasUnselectedProcess = false dataList.value.forEach((item, index) => { if (item.processIndex === undefined || item.processIndex === null) { hasUnselectedProcess = true } }) if (hasUnselectedProcess) { uni.showToast({ title: '请为所有管段选择小工序', icon: 'none' }) return } const data = dataList.value.map(item => ({ pipeNo: item.pipeNo || item.bomNo, version: item.version || item.bomVersion, processIndex: item.processIndex, isCompleted: item.isCompleted, isTopVersion: item.isTopVersion, isDelete: item.isDelete, isPause: item.isPause, projectNo: item.projectNo, })) uni.$emit('selectedPipes', data) uni.navigateBack() } const addMore = () => { // 打开选择弹窗 uni.navigateTo({ url: '/pages/piping/install/selectPipe' }) } </script> <style scoped lang="scss"> .btn-pipe-container { display: flex; justify-content: flex-end; } .btn-pipe-group{ display: flex; flex-direction: column; align-items: flex-end; gap: 8px; } .btn-group{ display: flex; } .data-list { background-color: #f9f9f9; border: 1px solid #ddd; border-radius: 8rpx; padding: 10rpx; margin-bottom: 40rpx; } .data-item { margin-bottom: 20rpx; background-color: white; border-radius: 6rpx; padding: 20rpx; position: relative; /* 确保相对定位,使绝对定位的叉号正确显示 */ } .main-content { padding-top: 40rpx; } .header-row { display: flex; justify-content: space-between; align-items: flex-start; /* 顶部对齐 */ position: relative; } .info-group { flex: 1; } .label-row { display: flex; gap: 20rpx; font-size: 24rpx; color: #666; flex-wrap: wrap; margin-bottom: 5rpx; } .info-row { display: flex; gap: 20rpx; font-size: 24rpx; color: #333; } .form-item { display: flex; align-items: center; margin-bottom: 10rpx; } .label { width: 120rpx; font-size: 24rpx; color: #333; } .switch-item { display: flex; align-items: center; margin-bottom: 10rpx; } .bottom-btn-group { bottom: 40rpx; left: 0; right: 0; display: flex; justify-content: space-between; padding: 0 20rpx; box-sizing: border-box; } .header-section { display: flex; justify-content: flex-end; padding: 20rpx; width: 100%; box-sizing: border-box; } .btn-confirm { color: white; border: none; border-radius: 6rpx; padding: 16rpx 30rpx; font-size: 28rpx; flex: 1; margin-right: 20rpx; } .btn-add { width: auto; height: 60rpx; font-size: 28rpx; display: flex; align-items: center; justify-content: center; } .btn-close { position: absolute; top: 0; right: 0; width: 30rpx; height: 30rpx; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 20rpx; color: #999; border: none; margin: 5rpx; } .icon { font-size: 28rpx; color: #999; } </style> <uni-data-select v-model="item.name" :localdata="exceptionTypeList" placeholder="请选择异常类型" />不现实下拉; 另外异常类型和异常描述换行了,不能换行
09-20
<template> <c-search-panel ref="searchPanelRef" :columns="tableState.columns.concat(extraColumns)" @search="onSearch"> </c-search-panel> <c-table :columns="tableState.columns" :toolbar="tableState.toolbar" :proxy-config="tableState.proxyConfig" @toolbar-button-click="onToolbarClick" :sortConfig="{ showIcon: false }" :pagination="tableState.pagination" ref="tableRef" > <template #action="{ record }"> <a-button type="link" @click="feedBack(record)">反馈</a-button> </template> </c-table> <c-modal :title="'反馈'" v-model:open="open" destroyOnClose width="60%" :footer="null" > <a-card> <a-form ref="formRef" :model="formState" :label-col="{ span: 6 }" :wrapper-col="{ span: 16 }"> <a-row> <a-col :span="12"> <a-form-item label="作业日期" name="workDate" :rules="[{ required: true, message: '请选择作业日期' }]" > <a-date-picker v-model:value="formState.workDate"/> </a-form-item> </a-col> <a-col :span="12"> <a-form-item label="备注" name="remark" > <a-textarea :rows="4" style="width: 300px" v-model:value="formState.remark" /> </a-form-item> </a-col> </a-row> <a-row v-if="process == '焊接'"> <a-col :span="12"> <a-form-item label="协助人" name="helperNo" > <c-user-input v-model:value="formState.helperNo"></c-user-input> </a-form-item> </a-col> </a-row> </a-form> </a-card> <a-card v-if="process == '下料'"> <c-table :columns="pipeTableState.columns.concat(actionColumns)" :pagination="false" :dataSource="pipeTableState.dataSource" height="300" :toolbar="pipeTableState.toolbar" :sortConfig="{ showIcon: false }" @toolbar-button-click="onPipeToolbarClick" ref="pipeTableRef" > <template #action="{ record }"> <a-button type="link" @click="deletePipe(record)">删除</a-button> <a-button type="link" @click="exceptionSubmit(record)">异常上报</a-button> </template> </c-table> </a-card> <a-card v-if="process == '装配'"> <c-table :columns="partTableState.columns" :pagination="false" :dataSource="partTableState.dataSource" height="130" :toolbar="pipeTableState.toolbar" :sortConfig="{ showIcon: false }" @toolbar-button-click="onPipeToolbarClick" ref="partTableRef" > <template #action="{ record }"> <a-button type="link" @click="deletePart(record)">删除</a-button> <a-button type="link" @click="exceptionSubmit(record)">异常上报</a-button> </template> <template #heatNo="{ record }"> <a-select showSearch allow-clear v-model:value="record.heatNo" @click="getHeatNoDropList(record.matNo)" @click.stop > <a-select-option v-for="(item, index) in heatBatchList" :key="index" :value="item.heatNo" > {{ item.heatNo }} </a-select-option> </a-select> </template> d <template #batchNo="{ record }"> <a-select showSearch allow-clear v-model:value="record.batchNo" @click="getBatchNoDropList(record.matNo)" @click.stop > <a-select-option v-for="(item, index) in heatBatchList" :key="index" :value="item.batchNo" > {{ item.batchNo }} </a-select-option> </a-select> </template> </c-table> </a-card> <a-card v-if="process == '焊接' || process == '装配'"> <c-table :columns="weldTableState.columns" :pagination="false" :dataSource="weldTableState.dataSource" :height="process == '焊接' ? 300 : 130" :toolbar="process === '焊接' ? weldTableState.toolbar : {}" :sortConfig="{ showIcon: false }" @toolbar-button-click="onWeldToolbarClick" ref="pipeTableRef" > <template #wpsNo="{ record }"> <a-button type="link" @click="selectWpsNo(record)"> {{ record.wpsNo || '无' }} </a-button> </template> <template #action="{ record }"> <a-button type="link" @click="deleteWeld(record)">删除</a-button> <a-button type="link" @click="exceptionSubmit(record)">异常上报</a-button> </template> <template #rootBatchNo="{ record }"> <a-select showSearch allow-clear v-model:value="record.rootBatchNo" @change="changeRootBatchNo($event, record)" @click="getRootBatchNoDropList(record.rootGrade)" @click.stop > <a-select-option v-for="(item, index) in rootBatchNoList" :key="index" :value="item.batchNo" > {{ item.batchNo }} </a-select-option> </a-select> </template> <template #fillBatchNo="{ record }"> <a-select showSearch allow-clear v-model:value="record.fillBatchNo" @change="changeFillBatchNo($event, record)" @click="getFillBatchNoDropList(record.fillGrade)" @click.stop > <a-select-option v-for="(item, index) in fillBatchNoList" :key="index" :value="item.batchNo" > {{ item.batchNo }} </a-select-option> </a-select> </template> </c-table> </a-card> <div class="footer-buttons"> <a-button type="primary" @click="submitModal">提交</a-button> </div> </c-modal> <c-modal :title="'选择工序'" v-model:open="processVisible" destroyOnClose @ok="handleProcessOk" width="15%" > <a-form ref="processFormRef" :model="processFormState" :label-col="{ span: 6 }" :wrapper-col="{ span: 16 }"> <a-row :gutter="24"> <a-col :span="24"> <a-form-item label="工序" name="process" :rules="[{ required: true, message: '请选择工序' }]" > <a-select v-model:value="processFormState.process" style="width: 100px;"> <a-select-option value='下料'>下料</a-select-option> <a-select-option value='装配'>装配</a-select-option> <a-select-option value='焊接'>焊接</a-select-option> </a-select> </a-form-item> </a-col> </a-row> </a-form> </c-modal> <c-modal v-model:open="addPipeVisible" title="添加物量" width="60%" :footer="null"> <c-table :columns="addPipeTableState.columns" :dataSource="addPipeTableState.dataSource" :toolbar="addPipeTableState.toolbar" :sortConfig="{ showIcon: false }" :rowSelection="addPipeRowSelection" @toolbar-button-click="onAddPipeToolbarClick" :rowKey="record => `${record.projectNo}_${record.pipeNo}_${record.pipeVersion}`" height="400" > </c-table> </c-modal> <c-modal v-model:open="addWeldVisible" title="添加焊缝" width="60%" :footer="null"> <c-table :columns="addWeldTableState.columns" :dataSource="addWeldTableState.dataSource" :toolbar="addWeldTableState.toolbar" :sortConfig="{ showIcon: false }" :rowSelection="addWeldRowSelection" @toolbar-button-click="onAddWeldToolbarClick" :rowKey="record => `${record.projectNo}_${record.pipeNo}_${record.pipeVersion}_${record.weldNo}`" height="400" > </c-table> </c-modal> <c-modal :title="'异常上报'" v-model:open="exceptionVisible" destroyOnClose okText="确认" cancelText="取消" @ok="submitException" width="20%" > <a-form ref="exceptionFormRef" :model="exceptionFormState" :label-col="{ span: 6 }" :wrapper-col="{ span: 16 }"> <a-form-item label="异常类型" name="exceptionType" :rules="[{ required: true, message: '请选择异常类型' }]" > <a-select showSearch allow-clear v-model:value="exceptionFormState.exceptionType"> <a-select-option v-for="(item, index) in exceptionTypeList" :key="index" :value="item.name"> {{ item.name }} </a-select-option> </a-select> </a-form-item> <a-form-item label="异常描述" name="exceptionMsg" :rules="[{ required: true, message: '请输入异常描述' }]" > <a-textarea :rows="4" v-model:value="exceptionFormState.exceptionMsg" /> </a-form-item> </a-form> </c-modal> <c-modal :title="'人员配置'" v-model:open="personnelModalVisible" width="60%" @ok="handlePersonnelOk" > <div> <a-button style="margin-right: 10px" type="primary" :icon="h(PlusOutlined)" @click="addPersonnel" >新增 </a-button> <a-button type="primary" danger :icon="h(DeleteOutlined)" @click="deletePersonnel" >删除 </a-button> </div> <c-table :columns="personnelState.columns" :data-source="personnelState.personnelDataSource" :pagination="false" :sortConfig="{ showIcon: false }" :height="400" :row-selection="personnelRowSelection" rowKey="userNo" ref="personnelTableRef" > <template #user="{ record }"> <c-user-input v-model:value="record['user']"></c-user-input> </template> </c-table> </c-modal> <c-modal :title="'WPS NO'" v-model:open="wpsNoVisible" width="40%" @ok="handleWpsNoOk" > <c-table :columns="wpsNoState.columns" :data-source="wpsNoState.wpsNoDataSource" :pagination="false" :sortConfig="{ showIcon: false }" :height="400" :row-selection="wpsNoRowSelection" :rowKey="record => `${record.wpsNo}_${record.process}_${record.grade}_${record.type}`" > </c-table> </c-modal> </template> <script setup> import * as server from "@/packages/piping/api/cppf" import {computed, createVNode, h, onMounted, reactive, ref} from "vue" import {user, validateSearchFields} from "@/utils"; import {DeleteOutlined, ExclamationCircleOutlined, PlusOutlined} from "@ant-design/icons-vue"; import {getDislist} from "@/packages/oem/api/co"; import {message, Modal} from "ant-design-vue"; import {postExceptionManage} from "@/packages/piping/api/exceptionManage"; import {useI18n} from "vue-i18n" import dayjs from "dayjs"; const {t} = useI18n() const tableRef = ref(null) const pipeTableRef = ref(null) const searchPanelRef = ref(null) const ctable = computed(() => tableRef.value?.getTable()) const process = ref("") const orderNo = ref("") let conditionData = {} const heatBatchList = ref([]) const rootBatchNoList = ref([]) const fillBatchNoList = ref([]) const isVisibleComputed = computed(() => { return process.value == '焊接' }) const submitting = ref(false) const tableState = reactive({ toolbar: { buttons: [ { code: "addPersonQuotaWorkhour", status: 'primary', icon: 'ClockCircleOutlined', name: "工时反馈" } ], showFilter: false }, selectedRowKeys: [], pagination: {pageSize: 20}, proxyConfig: { autoLoad: false, ajax: { query: async (pagination) => { conditionData = await validateSearchFields(searchPanelRef, conditionData) return server.workOrderPage({...pagination, ...conditionData}) } } }, columns: [ { title: "派工单号", dataIndex: "orderNo", width: 120, condition: true, conditionNotice: t("pipeControll.search.content") }, { title: "基地", dataIndex: "orgNo", type: "buildCase", width: 80, condition: true }, { title: "总长度", dataIndex: "totalLength", width: 70 }, { title: "总重量", dataIndex: "totalWeight", width: 70 }, { title: "总寸口数", dataIndex: "totalInch", width: 80 }, { title: "物量统计", dataIndex: "pipeCount", width: 80 }, { title: "下料工位号", dataIndex: "cutWorkSpaceNo", width: 80, }, { title: "下料开始日期", dataIndex: "cutStartDate", type: "date", width: 100, }, { title: "下料结束日期", dataIndex: "cutEndDate", type: "date", width: 100, }, { title: "下料状态", dataIndex: "cutStatus", width: 80, type: "select", options: { options: [ {value: "Y", label: "Y"}, {value: "N", label: "N"} ] } }, { title: "装配工位号", dataIndex: "assyWorkSpaceNo", width: 80, }, { title: "装配工位类型", dataIndex: "assyWorkSpaceType", width: 100, }, { title: "装配开始日期", dataIndex: "assyStartDate", type: "date", width: 100, }, { title: "装配结束日期", dataIndex: "assyEndDate", type: "date", width: 100, }, { title: "装配状态", dataIndex: "assyStatus", width: 80, type: "select", options: { options: [ {value: "Y", label: "Y"}, {value: "N", label: "N"} ] } }, { title: "焊接工位号", dataIndex: "weldingWorkSpaceNo", width: 80, }, { title: "焊接工位类型", dataIndex: "weldingWorkSpaceType", width: 100 }, { title: "焊接开始日期", dataIndex: "weldingStartDate", type: "date", width: 100 }, { title: "焊接结束日期", dataIndex: "weldingEndDate", type: "date", width: 100 }, { title: "焊接状态", dataIndex: "weldingStatus", width: 80, type: "select", options: { options: [ {value: "Y", label: "Y"}, {value: "N", label: "N"} ] } }, { title: "派工单状态", dataIndex: "status", width: 100, type: "select", formatter: (data) => data.cellValue == -1 ? "作废" : data.cellValue == 0 ? "新建" : data.cellValue == 1 ? "作业中" : data.cellValue == 2 ? "已完成" : "", options: { options: [ {value: -1, label: "作废"}, {value: 0, label: "新建"}, {value: 1, label: "作业中"}, {value: 2, label: "已完成"} ] } }, { title: "下料领料单号", dataIndex: "cutMatListNo", width: 120 }, { title: "装配领料单号", dataIndex: "assyMatListNo", width: 120 }, { title: "创建人", dataIndex: "createUserNo", type: "employeeDescription", options: { fieldNames: { label: "createUserName", value: "createUserNo" } }, width: 110 }, { title: "创建时间", dataIndex: "createDataDate", formatter: ({row}) => { return row.createDate ? dayjs(row.createDate).format('YYYY-MM-DD') : '-'; }, width: 80, invisible: false, type: "date" }, { title: "操作", key: "action", scopedSlots: {customRender: "action"}, width: 80, fixed: "right", formInvisible: true } ] }) const extraColumns = ref([ { title: "项目", dataIndex: "projectNo", width: 80, condition: true, type: "project", options: { options: [], fieldNames: {label: "projId", value: "projId"} }, } ]) const onToolbarClick = (target) => { switch (target.code) { case "addPersonQuotaWorkhour": addPersonQuotaWorkhour() break default: break } } const commonPipeColumns = ref([ { title: "项目", dataIndex: "projectNo", width: 50, type: "project", condition: true, options: { options: [], fieldNames: {label: "projId", value: "projId"} } }, { title: "小票号", dataIndex: "pipeNo", condition: true, conditionNotice: t("pipecontroll.search.content"), width: 150 }, { title: "版本号", condition: true, dataIndex: "pipeVersion", width: 60 } ]) const pipeTableState = reactive({ selectedRowKeys: [], selectedRows: [], toolbar: { buttons: [ { code: "insertPipe", status: "primary", icon: "PlusOutlined", name: "添加物量" } ] }, columns: commonPipeColumns, dataSource: [] }) const currentWeldRecord = ref(null) const weldTableState = reactive({ dataSource: [], toolbar: { buttons: [ { code: "insertWeld", status: "primary", icon: "PlusOutlined", name: "添加焊缝" } ] }, columns: [ { title: "项目", dataIndex: "projectNo", width: 50, type: "project", options: { options: [], fieldNames: {label: "projId", value: "projId"} } }, { title: "小票号", dataIndex: "pipeNo", conditionNotice: t("pipecontroll.search.content"), width: 150 }, { title: "版本号", dataIndex: "pipeVersion", width: 60 }, { title: "焊缝号", dataIndex: "weldNo", width: 170 }, { title: "WPS NO", dataIndex: "wpsNo", width: 80, scopedSlots: {customRender: "wpsNo"}, visible: isVisibleComputed }, { title: "焊接方法", dataIndex: "weldProcess", width: 100, visible: isVisibleComputed }, { title: "焊材规格", dataIndex: "weldingMatSpec", width: 150, visible: isVisibleComputed }, { title: "rootGrade", dataIndex: "rootGrade", width: 80, visible: false }, { title: "fillGrade", dataIndex: "fillGrade", width: 80, visible: false }, { title: "批号", dataIndex: "batchNo", children: [ { title: 'root', dataIndex: 'rootBatchNo', scopedSlots: { customRender: "rootBatchNo" }, width: 120 }, { title: 'fill', dataIndex: 'fillBatchNo', scopedSlots: { customRender: "fillBatchNo" }, width: 120 } ], visible: isVisibleComputed }, { title: "证书", dataIndex: "certNo", children: [ {title: 'root', dataIndex: 'rootWeldCert', width: 120}, {title: 'fill', dataIndex: 'fillWeldCert', width: 120} ], width: 80, visible: isVisibleComputed }, { title: "操作", key: "action", width: 140, scopedSlots: {customRender: "action"}, fixed: "right", } ] }) const partTableState = reactive({ dataSource: [], columns: [ { title: "项目", dataIndex: "projectNo", width: 50, type: "project", options: { options: [], fieldNames: {label: "projId", value: "projId"} } }, { title: "小票号", dataIndex: "pipeNo", conditionNotice: t("pipecontroll.search.content"), width: 150 }, { title: "版本号", dataIndex: "pipeVersion", width: 60 }, { title: "零件号", dataIndex: "partNo", width: 100 }, { title: "物资编码", dataIndex: "matNo", width: 100 }, { title: "零件规格", dataIndex: "partSize", width: 100 }, { title: "炉号", dataIndex: "heatNo", scopedSlots: { customRender: "heatNo" }, width: 120 }, { title: "批号", dataIndex: "batchNo", scopedSlots: { customRender: "batchNo" }, width: 120 }, { title: "操作", key: "action", width: 140, scopedSlots: {customRender: "action"}, fixed: "right", } ] }) const addPipeTableState = reactive({ selectedRowKeys: [], selectedRows: [], dataSource: [], toolbar: { buttons: [ { code: "addPipe", status: 'primary', icon: 'PlusOutlined', name: "添加" } ], }, columns: commonPipeColumns }) const addWeldTableState = reactive({ selectedRowKeys: [], selectedRows: [], dataSource: [], toolbar: { buttons: [ { code: "addWeld", status: 'primary', icon: 'PlusOutlined', name: "添加" } ], }, columns: [ { title: "项目", dataIndex: "projectNo", width: 50, type: "project", options: { options: [], fieldNames: {label: "projId", value: "projId"} } }, { title: "小票号", dataIndex: "pipeNo", conditionNotice: t("pipecontroll.search.content"), width: 150 }, { title: "版本号", dataIndex: "pipeVersion", width: 60 }, { title: "焊缝号", dataIndex: "weldNo", width: 170 } ] }) const personnelTableRef = ref(null) const cPersonnelTable = computed(() => personnelTableRef.value?.getTable()) const personnelState = reactive({ selectedRowKeys: [], selectedRows: [], personnelDataSource: [], columns: [ { title: "姓名", dataIndex: "userName", scopedSlots: {customFormRender: "user"}, type: "employeeDescription", options: { fieldNames: { label: "userName", value: "userNo" } }, editable: true, rules: [{required: true, message: "必填项!"}], width: 100 }, { title: "实动工时", dataIndex: "workHours", width: 100, editable: true, type: "inputNumber", rules: [{required: true, message: "必填项!"}] }, { title: "作业时间", dataIndex: "workDate", width: 100, editable: true, type: "date", rules: [{required: true, message: "必填项!"}] }, { title: "异常工时", dataIndex: "exceptionHours", type: "inputNumber", editable: true, width: 100 }, { title: "异常类型", dataIndex: "exceptionType", editable: true, type: "select", options: { options: [], fieldNames: {label: "name", value: "name"}, ajax: getDislist({code: "CP_EXCEPTION_TYPE"}).then((res) => { return res }) }, width: 100 }, { title: "异常描述", dataIndex: "exceptionMsg", editable: true, width: 100 }, { title: "参考工时", dataIndex: "cumulativeWorkHours", width: 100 }, { title: "累计工时", dataIndex: "cumulativeWorkHours", width: 100 } ] }) const wpsNoState = reactive({ selectedRowKeys: [], selectedRows: [], wpsNoDataSource: [], columns: [ { title: "WPS NO", dataIndex: "wpsNo", width: 80, }, { title: "焊接方法", dataIndex: "process", width: 80, }, { title: "焊材规格", dataIndex: "grade", width: 80, }, { title: "焊接类型", dataIndex: "type", width: 80, } ] }) const actionColumns = ref([ { title: "操作", key: "action", width: 140, scopedSlots: {customRender: "action"}, fixed: "right", } ]) const formState = ref({ workDate: "", remark: "", helperNo: {label: "", value: ""} }) const processFormState = ref({ process: "" }) const exceptionFormState = ref({ exceptionType: "", exceptionMsg: "" }) const exceptionTypeList = ref([]) const formRef = ref() const processFormRef = ref() const exceptionFormRef = ref() const open = ref(false) const processVisible = ref(false) const wpsNoVisible = ref(false) const addPipeVisible = ref(false) const addWeldVisible = ref(false) const exceptionVisible = ref(false) const personnelModalVisible = ref(false) const personSaveDataList = ref([]) const projectNo = ref("") const pipeNo = ref("") const pipeVersion = ref(null) const orgNo = ref("") onMounted(() => { getDislist({code: "CP_EXCEPTION_TYPE"}).then((res) => { exceptionTypeList.value = res.data }) }) //搜索 const onSearch = () => { ctable.value.commitProxy("reload") } const addPipeRowSelection = computed(() => { return { selectedRowKeys: addPipeTableState.selectedRowKeys, onChange: (selectedRowKeys, selectedRows) => { addPipeTableState.selectedRowKeys = selectedRowKeys addPipeTableState.selectedRows = selectedRows } } }) const addWeldRowSelection = computed(() => { return { selectedRowKeys: addWeldTableState.selectedRowKeys, onChange: (selectedRowKeys, selectedRows) => { addWeldTableState.selectedRowKeys = selectedRowKeys addWeldTableState.selectedRows = selectedRows } } }) const personnelRowSelection = computed(() => { return { selectedRowKeys: personnelState.selectedRowKeys, onChange: (selectedRowKeys, selectedRows) => { personnelState.selectedRowKeys = selectedRowKeys personnelState.selectedRows = selectedRows } } }) const wpsNoRowSelection = computed(() => { return { selectedRowKeys: weldTableState.selectedRowKeys, onChange: (selectedRowKeys, selectedRows) => { weldTableState.selectedRowKeys = selectedRowKeys weldTableState.selectedRows = selectedRows } } }) const onPipeToolbarClick = (target) => { switch (target.code) { case "insertPipe": insertPipe() break default: break } } const onWeldToolbarClick = (target) => { switch (target.code) { case "insertWeld": insertWeld() break default: break } } const onAddPipeToolbarClick = (target) => { switch (target.code) { case "addPipe": addPipe() break default: break } } const onAddWeldToolbarClick = (target) => { switch (target.code) { case "addWeld": addWeld() break default: break } } const feedBack = (record) => { processVisible.value = true processFormState.value = {process: ""} orgNo.value = record.orgNo orderNo.value = record.orderNo projectNo.value = record.projectNo } const handleProcessOk = () => { processFormRef.value.validate().then(() => { process.value = processFormState.value.process open.value = true formState.value = {workDate: "", remark: ""} pipeTableState.dataSource = [] partTableState.dataSource = [] weldTableState.dataSource = [] processVisible.value = false }) } const insertPipe = () => { if (process.value == '下料') { server.findCutNotFeedbackPipe({orderNo: orderNo.value}).then((res) => { addPipeTableState.dataSource = res.data }) } else if (process.value == '装配') { server.findAssyNotFeedbackPipe({orderNo: orderNo.value}).then((res) => { addPipeTableState.dataSource = res.data }) } addPipeVisible.value = true } const insertWeld = () => { server.findWeldingNotFeedbackPipe({orderNo: orderNo.value}).then((res) => { addWeldTableState.dataSource = res.data addWeldVisible.value = true }) } const deletePipe = (record) => { pipeTableState.dataSource = pipeTableState.dataSource.filter((x) => { return !( x.projectNo === record.projectNo && x.pipeNo === record.pipeNo && x.pipeVersion === record.pipeVersion ); }); } const deletePart = (record) => { Modal.confirm({ title: "请确认是否要进行删除?", icon: createVNode(ExclamationCircleOutlined), content: "该操作一旦执行无法撤回,并将删除该小票的所有零件", okText: "确认", okType: "danger", cancelText: "取消", onOk() { partTableState.dataSource = partTableState.dataSource.filter((x) => { return !( x.projectNo === record.projectNo && x.pipeNo === record.pipeNo && x.pipeVersion === record.pipeVersion ); }); }, onCancel() { console.log("Cancel") } }) } const deleteWeld = (record) => { weldTableState.dataSource = weldTableState.dataSource.filter((x) => { return !( x.projectNo === record.projectNo && x.pipeNo === record.pipeNo && x.pipeVersion === record.pipeVersion && x.weldNo === record.weldNo ); }); } const addPipe = () => { addPipeTableState.selectedRows.forEach((item) => { let dataSource = [] if (process.value == '下料') { dataSource = pipeTableState.dataSource } else if (process.value == '装配') { dataSource = partTableState.dataSource } const isExist = dataSource.some( (dataItem) => dataItem.projectNo === item.projectNo && dataItem.pipeNo === item.pipeNo && dataItem.pipeVersion === item.pipeVersion ); if (!isExist) { if (process.value == '下料') { pipeTableState.dataSource.push(item); } else if (process.value == '装配') { if (item.preFeedbackPartList.length > 0) { partTableState.dataSource.push(...item.preFeedbackPartList) } if (item.preFeedbackWeldList.length > 0) { weldTableState.dataSource.push(...item.preFeedbackWeldList) } } } addPipeVisible.value = false addPipeTableState.selectedRowKeys = []; addPipeTableState.selectedRows = []; }) } const addWeld = () => { addWeldTableState.selectedRows.forEach((item) => { const isExist = weldTableState.dataSource.some( (dataItem) => dataItem.projectNo === item.projectNo && dataItem.pipeNo === item.pipeNo && dataItem.pipeVersion === item.pipeVersion && dataItem.weldNo === item.weldNo ); if (!isExist) { weldTableState.dataSource.push(item); addWeldVisible.value = false } }) } const exceptionSubmit = (record) => { exceptionFormState.value = { exceptionType: "", exceptionMsg: "" } projectNo.value = record.projectNo pipeNo.value = record.pipeNo pipeVersion.value = record.pipeVersion exceptionVisible.value = true; } const submitException = () => { exceptionFormRef.value .validate().then(() => { const submitData = [{ projectNo: projectNo.value, bomNo: pipeNo.value, bomVersion: pipeVersion.value, orgNo: orgNo.value, exceptionType: exceptionFormState.value.exceptionType, exceptionMsg: exceptionFormState.value.exceptionMsg }] postExceptionManage(submitData).then(() => { message.success("上报成功") exceptionVisible.value = false }) }) } const addPersonQuotaWorkhour = () => { personnelModalVisible.value = true personnelState.personnelDataSource = [] personnelState.personnelDataSource.push({ userNo: user().number, userName: user().nickname, workDate: dayjs(new Date()).subtract(1, 'day').format("YYYY-MM-DD") }) } const addPersonnel = () => { cPersonnelTable.value.insert({}) } const deletePersonnel = () => { if (personnelState.selectedRowKeys.length == 0) { message.error("请选择至少一条数据") return } personnelState.selectedRows.forEach((item) => { personnelState.dataSource = personnelState.dataSource.filter((x) => { return !( x.userNo == item.userNo ); }) }) } const handlePersonnelOk = () => { cPersonnelTable.value.validateEditFields().then(() => { personSaveDataList.value = personnelState.dataSource }) } const getHeatNoDropList = (matNo) => { server.findHeatBatchByMatNo({matNo: matNo}).then((res) => { heatBatchList.value = res.data }) } const getRootBatchNoDropList = (rootGrade) => { server.getQcWeldMatSnDropList({grade: rootGrade}).then((res) => { rootBatchNoList.value = res.data }) } const getFillBatchNoDropList = (fillGrade) => { server.getQcWeldMatSnDropList({grade: fillGrade}).then((res) => { fillBatchNoList.value = res.data }) } const changeRootBatchNo = (rootBatchNo, record) => { const foundItem = rootBatchNoList.value.find(item => item.batchNo == rootBatchNo); if (foundItem) { record.rootWeldCert = foundItem.certNo; } } const changeFillBatchNo = (fillBatchNo, record) => { const foundItem = fillBatchNoList.value.find(item => item.batchNo == fillBatchNo); if (foundItem) { record.fillWeldCert = foundItem.certNo; } } const selectWpsNo = (record) => { currentWeldRecord.value = record server.getWldPiPeiWpsList({weldId: record.id}).then((res) => { wpsNoState.wpsNoDataSource = res.data wpsNoVisible.value = true }) } const handleWpsNoOk = () => { if (weldTableState.selectedRowKeys.length != 1) { message.error("请选择一条数据") return } const selectedWps = weldTableState.selectedRows[0] if (currentWeldRecord.value) { currentWeldRecord.value.wpsNo = selectedWps.wpsNo currentWeldRecord.value.weldProcess = selectedWps.process currentWeldRecord.value.weldingMatSpec = selectedWps.grade currentWeldRecord.value.rootGrade = selectedWps.rootGrade currentWeldRecord.value.fillGrade = selectedWps.fillGrade wpsNoVisible.value = false } } const submitModal = () => { if (submitting.value) return formRef.value.validate().then(() => { submitting.value = true const submitData = { orderNo: orderNo.value, process: process.value, workDate: formState.value.workDate, remark: formState.value.remark, helperNo: formState.value.helperNo?.value || "", pipeList: pipeTableState.dataSource, partList: partTableState.dataSource, weldList: weldTableState.dataSource, } let apiPromise if (process.value == "下料") { if (pipeTableState.dataSource.length == 0) { message.error("请选择下料数据") submitting.value = false return } apiPromise = server.cutFeedback(submitData) } else if (process.value == "装配") { if (partTableState.dataSource.length == 0) { message.error("请选择零件数据") submitting.value = false return } if (weldTableState.dataSource.length == 0) { message.error("请选择焊接数据") submitting.value = false return } apiPromise = server.assyFeedback(submitData) } else if (process.value == "焊接") { if (weldTableState.dataSource.length == 0) { message.error("请选择焊接数据") submitting.value = false return } apiPromise = server.weldingFeedback(submitData) } apiPromise .then(() => { message.success("反馈成功") open.value = false }) .finally(() => { submitting.value = false }) }) } </script> <style lang="less"> .footer-buttons { display: flex; justify-content: flex-end; gap: 10px; padding: 10px; border-top: 1px solid #f0f0f0; flex: 0 0 auto } </style> 其中<template #heatNo="{ record }"> <a-select showSearch allow-clear v-model:value="record.heatNo" @click="getHeatNoDropList(record.matNo)" @click.stop > <a-select-option v-for="(item, index) in heatBatchList" :key="index" :value="item.heatNo" > {{ item.heatNo }} </a-select-option> </a-select> </template> 要求因为开启了showSearch,意味着可以在选择上输入信息,当输入的时候(不管输入多少字符,输入一个字符也查),才调用getHeatNoDropList(record.matNo)
最新发布
10-22
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值