|
|
<template> <div ref="pageRoot" class="mod-config"> <com-po-order-customer-picker :site="searchData.site" v-model="searchData.customer" @change="onSessionCustomerChange" /> <el-form :inline="true" label-position="top" :model="searchData"> <el-form-item label="PO No"> <el-input v-model="searchData.poNo" clearable style="width: 140px" @keyup.enter.native="handleSearch"></el-input> </el-form-item> <el-form-item label="订单类型"> <el-select v-model="searchData.orderType" placeholder="请选择" style="width: 120px" clearable> <el-option v-for="item in poOrderTypeSearchOptions" :key="item.value" :label="item.label" :value="item.value" /> </el-select> </el-form-item> <el-form-item> <span style="cursor: pointer" slot="label" @click="openSearchBaseList(2017)"><a href="javascript:void(0)">Part No</a></span> <el-input v-model="searchData.partNo" clearable style="width: 120px" @keyup.enter.native="handleSearch"></el-input> </el-form-item> <el-form-item> <span style="cursor: pointer" slot="label" @click="openSearchBaseList(520)"><a href="javascript:void(0)">供应商编码</a></span> <el-input v-model="searchData.supplierNo" clearable style="width: 120px" @keyup.enter.native="handleSearch"></el-input> </el-form-item> <el-form-item label="SKU"> <el-input v-model="searchData.sku" clearable style="width: 120px" @keyup.enter.native="handleSearch"></el-input> </el-form-item> <el-form-item label="Status"> <el-select v-model="searchData.status" placeholder="请选择" style="width:100px" clearable> <el-option label="全部" value=""></el-option> <el-option v-for="item in poOrderStatusOptions" :key="item.value" :label="item.label" :value="item.value" /> </el-select> </el-form-item>
</el-form> <el-form :inline="true" label-position="top" style="margin-top:10px "> <el-form-item> <el-button type="primary" @click="handleSearch()" v-if="!editMode" :disabled="!searchData.customer">查询</el-button> <el-button type="primary" style="margin-left: 2px" @click="exportExcel" :disabled="editMode || !searchData.customer">导出</el-button> <el-button type="warning" plain @click="advancedQueryVisible = true" v-if="!editMode" style="margin-left: 2px">高级查询</el-button> <el-button type="primary" style="margin-left: 2px" @click="toggleEditMode"> {{ editMode ? '确定' : '编辑' }} </el-button> <el-button v-if="editMode" style="margin-left: 2px" @click="cancelEditMode">取消</el-button> <el-button type="primary" style="margin-left: 2px" @click="openAddDialog" v-if="!editMode">新增</el-button><!-- <el-button type="primary" style="margin-left: 2px" @click="exportModal">导入</el-button>--> </el-form-item> </el-form> <el-table ref="poTable" :height="height" :data="masterRows" border highlight-current-row style="width: 100%;" @current-change="onTableCurrentChange"> <el-table-column v-if="!editMode" fixed="left" label="操作" header-align="center" align="center" width="90"> <template slot-scope="scope"> <el-link v-if="canIssuePoOrder(scope.row.status)" type="primary" @click.stop="handleIssuePoOrder(scope.row)">下达</el-link> <el-link v-else-if="canShipPoOrder(scope.row.status)" type="primary" @click.stop="handleShipPoOrder(scope.row)">shipped</el-link> <el-link v-else-if="canClosePoOrder(scope.row.status)" type="primary" @click.stop="handleClosePoOrder(scope.row)">关闭</el-link> <el-link v-else-if="canOpenPoOrder(scope.row.status)" type="primary" @click.stop="handleOpenPoOrder(scope.row)">打开</el-link> </template> </el-table-column> <el-table-column v-for="(item, index) in visibleColumns" :key="item.serialNumber || index" :sortable="item.columnSortable" :prop="item.columnProp" :header-align="item.headerAlign" :show-overflow-tooltip="item.showOverflowTooltip && (!editMode || (!item.editAble && !isChooserEditColumn(item.columnProp)))" :align="item.align" :fixed="item.fixed === '' ? false : item.fixed" :min-width="item.columnWidth" :label="item.columnLabel"> <template slot-scope="scope"> <template v-if="editMode && item.columnProp==='supplierName' && canEditCell(scope.row, item)"> <el-input v-model="scope.row.supplierName" size="mini" style="width: 100%; cursor: pointer" readonly placeholder="请选择供应商" @click.native="openSupplierSelect(scope.row)"> <i slot="suffix" class="el-icon-search" style="cursor: pointer" @click.stop="openSupplierSelect(scope.row)"></i> </el-input> </template> <template v-else-if="editMode && item.columnProp==='partNo' && canEditCell(scope.row, item)"> <el-input v-model="scope.row.partNo" size="mini" style="width: 100%; cursor: pointer" readonly placeholder="请选择物料" @click.native="openPartNoSelect(scope.row)"> <i slot="suffix" class="el-icon-search" style="cursor: pointer" @click.stop="openPartNoSelect(scope.row)"></i> </el-input> </template> <template v-else-if="editMode && item.columnProp==='departure' && canEditCell(scope.row, item)"> <el-input v-model="scope.row.departure" size="mini" style="width: 100%; cursor: pointer" readonly placeholder="请选择始发港" @click.native="openDepartureSelect(scope.row)"> <i slot="suffix" class="el-icon-search" style="cursor: pointer" @click.stop="openDepartureSelect(scope.row)"></i> </el-input> </template> <template v-else-if="editMode && item.columnProp==='destination' && canEditCell(scope.row, item)"> <el-input v-model="scope.row.destination" size="mini" style="width: 100%; cursor: pointer" readonly placeholder="请选择目的港" @click.native="openDestinationSelect(scope.row)"> <i slot="suffix" class="el-icon-search" style="cursor: pointer" @click.stop="openDestinationSelect(scope.row)"></i> </el-input> </template> <template v-else-if="editMode && item.columnProp==='shipVia' && canEditCell(scope.row, item)"> <el-input v-model="scope.row.shipVia" size="mini" style="width: 100%; cursor: pointer" readonly placeholder="请选择运输方式" @click.native="openShipViaSelect(scope.row)"> <i slot="suffix" class="el-icon-search" style="cursor: pointer" @click.stop="openShipViaSelect(scope.row)"></i> </el-input> </template> <template v-else-if="editMode && canEditCell(scope.row, item)"> <!-- 对于 confirmed 和 wantReceiveDate 字段,仅在新增行时可编辑 --> <template v-if="(item.columnProp === 'confirmed' || item.columnProp === 'wantReceiveDate') && !isNewRow(scope.row)"> <span>{{ scope.row[item.columnProp] }}</span> </template> <!-- 对于 inspection 和 realCrd 字段,仅在修改时(非新增行)可编辑 --> <template v-else-if="(item.columnProp === 'inspection' || item.columnProp === 'realCrd') && isNewRow(scope.row)"> <span>{{ scope.row[item.columnProp] }}</span> </template> <template v-else> <!-- 下拉框 --> <el-select v-if="item.editType === 'select' || item.editType === 'dict-select'" v-model="scope.row[item.columnProp]" size="mini" style="width: 100%" clearable @change="onCellChange(scope.row)"> <el-option v-for="opt in getSelectOptionsForColumn(item)" :key="String(opt.value)" :label="opt.label" :value="opt.value"> </el-option> </el-select> <!-- 数字输入框 --> <el-input v-else-if="item.editType === 'num'" v-model="scope.row[item.columnProp]" type="number" size="mini" @change="onCellChange(scope.row)"> </el-input> <!-- 时间控件 --> <el-date-picker v-else-if="item.editType === 'date'" v-model="scope.row[item.columnProp]" type="date" size="mini" style="width: 100%" value-format="yyyy-MM-dd" placeholder="选择日期" @change="onCellChange(scope.row)"> </el-date-picker> <el-date-picker v-else-if="item.editType === 'datetime'" v-model="scope.row[item.columnProp]" type="datetime" size="mini" style="width: 100%" value-format="yyyy-MM-dd HH:mm:ss" placeholder="选择日期时间" @change="onCellChange(scope.row)"> </el-date-picker> <!-- 普通文本输入框 --> <el-input v-else v-model="scope.row[item.columnProp]" size="mini" @change="onCellChange(scope.row)"> </el-input> </template>
</template> <span v-else>{{ formatColumnCell(scope.row, item) }}</span> </template> </el-table-column> </el-table>
<el-pagination ref="mainPager" @size-change="sizeChangeHandle" @current-change="currentChangeHandle" :current-page="pageIndex" :page-sizes="[20, 50, 100, 200, 500]" :page-size="pageSize" :total="totalPage" layout="total, sizes, prev, pager, next, jumper"> </el-pagination>
<!-- 导入弹窗 --> <el-dialog title="PO订单数据导入" :visible.sync="uploadDialogVisible" :height="height" width="800px"> <!-- 选择文件 --> <div style="margin-bottom:15px;display:flex;align-items:center"> <span style="margin-right:10px">请选择文件</span> <el-input v-model="uploadFileName" style="width:400px;margin-right:10px" readonly></el-input> <el-upload ref="upload" action="/api/pODetail/upload" :show-file-list="false" :on-change="handleFileChange" :auto-upload="false"> <el-button type="primary">选择文件</el-button> </el-upload> <el-button type="success" style="margin-left:10px" @click="previewUpload">上传</el-button> <el-button type="primary" @click="downloadTemplate" style="margin-left:10px"> <i class="el-icon-download"></i> 下载模板 </el-button> </div> <!-- 跳过列提示 --> <el-alert v-if="uploadSkippedColumns.length" type="warning" :closable="false" show-icon style="margin-bottom:10px" :title="'已跳过列:' + uploadSkippedColumns.join('、')" /> <div v-if="uploadColumnMappingEntries.length" style="margin-bottom:10px;font-size:12px;color:#606266"> 列映射: <span v-for="item in uploadColumnMappingEntries" :key="item.header" style="margin-right:12px"> {{ item.header }} → {{ item.fieldCode }} </span> </div> <!-- 预览表格 --> <el-table :data="uploadPreviewList" border height="250" style="width:100%;margin-top:10px" > <el-table-column v-for="col in uploadPreviewColumns" :key="col.prop" :prop="col.prop" :label="col.label" align="center" min-width="100" /> </el-table> <!-- 按钮 --> <div style="margin-top:20px;text-align:center"> <el-button type="primary" @click="confirmUpload">保存</el-button> <el-button @click="uploadDialogVisible=false" style="margin-right:30px">关闭</el-button> </div> </el-dialog>
<ChooselistEam ref="eamList" @getBaseData="getEamData" />
<po-order-advanced-query :visible.sync="advancedQueryVisible" :advanced-query-data="advancedQueryData" @query="handleSearch" />
<!-- 新增 PO 订单(主记录 + 明细暂存) --> <el-dialog title="新增 PO 订单" :visible.sync="addDialogVisible" width="1180px" top="4vh" :close-on-click-modal="false" append-to-body> <div class="po-add-form-wrap"> <el-form ref="addMasterFormRef" :model="addForm" label-position="top" size="small"> <el-row :gutter="15"> <el-col v-for="field in addMasterBaseFields" :key="`master-${field.fieldCode}`" :span="getAddFieldSpan(field)"> <el-form-item :label="field.fieldLabel" :required="field.requiredFlag === 'Y'"> <template v-if="isAddChooserField(field)"> <el-input v-model="addForm[getAddFieldProp(field)]" readonly :placeholder="getAddChooserPlaceholder(field)" @click.native="openAddChooserByField(field, 'master')"> <i slot="suffix" class="el-icon-search" style="cursor:pointer" @click.stop="openAddChooserByField(field, 'master')"></i> </el-input> </template> <template v-else-if="isAddSelectField(field)"> <el-select v-model="addForm[getAddFieldProp(field)]" :filterable="isAddFilterableSelectField(field)" clearable style="width:100%" :disabled="isAddFieldDisabled(field)"> <el-option v-for="opt in getAddFieldSelectOptions(field)" :key="opt.value" :label="opt.label" :value="opt.value" /> </el-select> </template> <template v-else-if="isAddDateField(field)"> <el-date-picker v-model="addForm[getAddFieldProp(field)]" type="date" value-format="yyyy-MM-dd" style="width:100%" :disabled="isAddFieldDisabled(field)" /> </template> <template v-else-if="isAddTextareaField(field)"> <el-input v-model="addForm[getAddFieldProp(field)]" type="textarea" :rows="2" :disabled="isAddFieldDisabled(field)" /> </template> <template v-else> <el-input v-model="addForm[getAddFieldProp(field)]" :type="isAddNumberField(field) ? 'number' : 'text'" :disabled="isAddFieldDisabled(field)" @input="onAddFieldInput(field, 'master')" /> </template> </el-form-item> </el-col> </el-row> </el-form>
<div class="po-add-detail-toolbar"> <span class="po-add-detail-title">明细暂存列表</span> <div> <el-button type="primary" size="mini" @click="openAddDetailDialog">新增</el-button> <el-button size="mini" :disabled="!hasSelectedAddDetailRow" @click="openEditAddDetailDialog">修改</el-button> <el-button size="mini" :disabled="!hasSelectedAddDetailRow" @click="removeAddDetailRow">删除</el-button> <el-button size="mini" :disabled="!hasSelectedAddDetailRow" @click="openCopyAddDetailDialog">复制</el-button> </div> </div>
<el-table ref="addDetailTable" :data="addDetailRows" :height="320" border stripe highlight-current-row empty-text="请点击新增维护明细" @current-change="onAddDetailCurrentChange" @row-click="onAddDetailRowClick"> <el-table-column type="index" label="#" width="50" header-align="center" align="center" /> <el-table-column prop="partNo" :label="getAddTableColumnLabel('part_no', '产品编码')" min-width="150" header-align="center" align="center" /> <el-table-column prop="item" :label="getAddTableColumnLabel('item', 'Item')" min-width="120" header-align="center" align="center" /> <el-table-column prop="sku" :label="getAddTableColumnLabel('sku', 'SKU')" min-width="120" header-align="center" align="center" /> <el-table-column prop="qty" :label="getAddTableColumnLabel('qty', 'PO Qty')" min-width="110" header-align="center" align="right" /> <el-table-column prop="price" :label="getAddTableColumnLabel('price', 'Unit Price')" min-width="120" header-align="center" align="right" /> <el-table-column prop="value" :label="getAddTableColumnLabel('value', 'PO Value')" min-width="120" header-align="center" align="right" /> <el-table-column prop="wantReceiveDate" :label="getAddTableColumnLabel('want_receive_date', 'Promise Date')" min-width="130" header-align="center" align="center" /> <el-table-column prop="remark" :label="getAddTableColumnLabel('remark', 'Comments')" min-width="220" header-align="center" align="left" show-overflow-tooltip /> </el-table> </div> <div slot="footer" class="dialog-footer"> <el-button type="primary" :loading="addFormSaving" @click="saveAddPoOrder">保存</el-button> <el-button @click="addDialogVisible = false">取消</el-button> </div> </el-dialog>
<!-- 新增明细弹窗 --> <el-dialog :title="addDetailDialogTitle" :visible.sync="addDetailDialogVisible" width="1000px" top="6vh" append-to-body :close-on-click-modal="false"> <div class="po-add-form-wrap"> <el-form ref="addDetailFormRef" :model="addDetailForm" label-position="top" size="small"> <el-row :gutter="15"> <el-col v-for="field in addDetailBaseFields" :key="`detail-${field.fieldCode}`" :span="getAddFieldSpan(field)"> <el-form-item :label="field.fieldLabel" :required="field.requiredFlag === 'Y'"> <template v-if="isAddChooserField(field)"> <el-input v-model="addDetailForm[getAddFieldProp(field)]" readonly :placeholder="getAddChooserPlaceholder(field)" @click.native="openAddChooserByField(field, 'detail')"> <i slot="suffix" class="el-icon-search" style="cursor:pointer" @click.stop="openAddChooserByField(field, 'detail')"></i> </el-input> </template> <template v-else-if="isAddSelectField(field)"> <el-select v-model="addDetailForm[getAddFieldProp(field)]" :filterable="isAddFilterableSelectField(field)" clearable style="width:100%" :disabled="isAddFieldDisabled(field)"> <el-option v-for="opt in getAddFieldSelectOptions(field)" :key="opt.value" :label="opt.label" :value="opt.value" /> </el-select> </template> <template v-else-if="isAddDateField(field)"> <el-date-picker v-model="addDetailForm[getAddFieldProp(field)]" type="date" value-format="yyyy-MM-dd" style="width:100%" :disabled="isAddFieldDisabled(field)" /> </template> <template v-else-if="isAddTextareaField(field)"> <el-input v-model="addDetailForm[getAddFieldProp(field)]" type="textarea" :rows="2" :disabled="isAddFieldDisabled(field)" /> </template> <template v-else> <el-input v-model="addDetailForm[getAddFieldProp(field)]" :type="isAddNumberField(field) ? 'number' : 'text'" :disabled="isAddFieldDisabled(field)" @input="onAddFieldInput(field, 'detail')" /> </template> </el-form-item> </el-col> </el-row> <el-row v-if="addDetailExtFields.length" :gutter="24"> <el-col v-for="field in addDetailExtFields" :key="`detail-ext-${field.fieldCode}`" :span="6"> <el-form-item :label="field.fieldLabel" :required="field.requiredFlag === 'Y'"> <com-po-order-dynamic-field-renderer :field="field" :site="searchData.site" :value="getAddDetailExtValue(field.fieldCode)" @input="val => setAddDetailExtValue(field.fieldCode, val)" /> </el-form-item> </el-col> </el-row> </el-form> </div> <div slot="footer" class="dialog-footer"> <el-button type="primary" @click="saveAddDetailDraft">暂存</el-button> <el-button @click="addDetailDialogVisible = false">取消</el-button> </div> </el-dialog>
<el-dialog title="选择供应商可供物料" :visible.sync="supplierPartDialogVisible" width="820px" top="6vh" append-to-body :close-on-click-modal="false" @closed="resetSupplierPartDialog"> <div style="margin-bottom: 10px; color: #606266;"> <span>供应商:{{ supplierPartDialogSupplierNo }}</span> </div> <el-table :data="supplierPartDialogRows" :height="430" border stripe v-loading="supplierPartDialogLoading" empty-text="该供应商未维护供应商可供物料" @row-dblclick="handleSupplierPartRowDblClick"> <el-table-column prop="partNo" label="物料编码" min-width="160" header-align="center" align="center" /> <el-table-column prop="partDesc" label="物料描述" min-width="220" header-align="center" align="left" show-overflow-tooltip /> <el-table-column prop="item" label="Item" min-width="120" header-align="center" align="center" show-overflow-tooltip /> <el-table-column prop="sku" label="SKU" min-width="120" header-align="center" align="center" show-overflow-tooltip /> </el-table> <div slot="footer" class="dialog-footer"> <el-button @click="supplierPartDialogVisible = false">关闭</el-button> </div> </el-dialog> </div></template>
<script>import { searchPoDetailPage, searchPoDetailAdvancedPage, batchUpdatePoDetail, closePoDetail, openPoDetail, issuePoDetail, shipPoDetail, createPoOrder, getPoCustomerOptions, getPoBuyerOptions, getPoFieldSchema, listPoSupplierPartOptions, listPoCurrentTierPrice} from '@/api/order/poOrder.js'import { createAdvancedQueryData, createAdvancedQueryDataFromSchema, buildPoOrderQueryParams} from './poOrderAdvancedQueryHelper.js'import { applyFieldSchemaToColumns, getAdvancedQueryFieldsFromSchema, getEditableExtFieldsFromSchema, buildImportPreviewColumns, normalizeSchemaRowValues, buildExtValuesFromRow} from './poOrderFieldSchemaHelper.js'import { PO_ORDER_STATUS_OPTIONS, canIssuePoOrder, canShipPoOrder, canClosePoOrder, canOpenPoOrder, canEditPoColumnByStatus, resolvePoOrderStatusByInspectQty, buildPoDetailActionParams} from './poOrderStatusOptions.js'import { PO_ORDER_TYPE_OPTIONS, PO_ORDER_TYPE_FILTER_OPTIONS, formatPoOrderTypeLabel } from './poOrderTypeOptions.js'import { getPartList, searchPartInfo } from '@/api/part/partInfo.js'import excel from '@/utils/excel-util.js'import { checkSrmSupplierList } from '@/api/srm/srmSupplier.js'import { selectDictDataList } from '@/api/dict'import { SUPPLIER_CHOOSELIST_TAG, parseSupplierChooselistRow, fetchSupplierDetail, resolveSupplierShortFieldsFromDetail} from '@/utils/supplierChooselistHelper.js'import { searchTransportOptions } from '@/api/port/portTransit.js'import ChooselistEam from '@/views/modules/common/Chooselist_eam.vue'import PoOrderAdvancedQuery from './com_poOrderAdvancedQuery.vue'import ComPoOrderCustomerPicker from './com_poOrder_CustomerPicker.vue'import ComPoOrderDynamicFieldRenderer from './com_poOrder_DynamicFieldRenderer.vue'
const ADD_CHOOSER_FIELD_CODES = new Set(['supplier_name', 'part_no', 'departure', 'destination', 'ship_via'])const ADD_DISPLAY_ONLY_FIELD_CODES = new Set(['short_office', 'short_npc', 'item', 'category_name', 'sku', 'value', 'status', 'crd_ori'])const ADD_TEXTAREA_FIELD_CODES = new Set(['remark'])const ADD_FULL_ROW_FIELD_CODES = new Set(['remark'])const ADD_NUMBER_FIELD_CODES = new Set(['price', 'qty', 'ship_qty', 'sum_price'])const ADD_FILTERABLE_SELECT_FIELD_CODES = new Set(['customer', 'buyer'])const ADD_FORCE_DETAIL_FIELD_CODES = new Set(['part_no', 'price', 'qty'])const ADD_READONLY_DEFAULT_TODAY_FIELD_CODES = new Set(['create_date'])const ADD_EDITABLE_DEFAULT_TODAY_FIELD_CODES = new Set(['po_place_date'])const ADD_SELECT_OPTIONS_FIELD_CODE_MAP = { order_type: 'poOrderTypeOptions', customer: 'poCustomerOptions', buyer: 'poBuyerOptions', status: 'poOrderStatusOptions', doc_sent: 'yesNoOptions', telex_release: 'yesNoOptions', advance_paid: 'yesNoOptions'}
export default { name: 'PoOrder', components: { ChooselistEam, PoOrderAdvancedQuery, ComPoOrderCustomerPicker, ComPoOrderDynamicFieldRenderer }, data () { return { loading: false, masterRows: [], /** * 需要单独写编辑 UI 的 columnProp 填在这里,才会走模板里「自定义」分支;不在此数组的仍用默认 select/num/text - rqrq * 示例:['mPlanner', 'comments'] */ rowDeparture: {}, // 存储每行选中的始发港
rowDestination: {}, // 存储每行选中的目的港
rowShipVia: {}, // 存储每行选中的运输方式
transportOptions: [], // 运输方式选项
rowPartNo: {}, //存储每行选中的物料编号
currentSelectRow: null, // 当前正在选择供应商的行
currentPartNoRow: null, // 当前正在选择物料的行
currentDepartureRow: null, // 当前正在选择始发港的行
currentDestinationRow: null, // 当前正在选择目的港的行
currentShipViaRow: null, // 当前正在选择运输方式的行
rowSupplierNo: {}, //存储每行选中的供应商编号
poPartNoOptions: [], supplierOptions: [], poDepartureOptions: [], poDestinationOptions: [], poCustomerOptions: [], poBuyerOptions: [], /** Chooselist 弹窗选择列(editAble 可为 false,如供应商/物料) */ chooserEditColumnProps: ['supplierName', 'partNo', 'departure', 'destination', 'shipVia'], /** 联表展示、不写入 PO 的列 */ displayOnlyColumnProps: ['shortOffice', 'shortNpc', 'item', 'categoryName', 'sku'], customEditColumnProps: [],
// 上传相关
uploadDialogVisible: false, uploadFileName: '', uploadFile: null, uploadPreviewList: [], uploadSkippedColumns: [], uploadColumnMapping: {}, uploadPreviewColumns: [],
addDialogVisible: false, addFormSaving: false, addFormSchemaFields: [], pickContext: '', eamTagNo: '', eamPickField: '', addForm: {}, addDetailDialogVisible: false, addDetailDialogMode: 'create', addDetailForm: {}, addDetailRows: [], addDetailCurrentDraftId: '', addDetailEditingDraftId: '', addDetailDraftSeq: 0, supplierPartDialogVisible: false, supplierPartDialogLoading: false, supplierPartDialogRows: [], supplierPartDialogContext: '', supplierPartDialogTargetRow: null, supplierPartDialogSupplierNo: '', supplierPartDialogSite: '', addPriceLookupTimer: null, addPriceLookupSeq: 0, selectedCustomerScope: null,
currentRow: null, // 各列下拉:select 列通过 selectOptionsKey 指向此处;也可在列定义里写 selectOptions 数组 - rqrq
columnSelectOptions: { poStatusOptions: PO_ORDER_STATUS_OPTIONS, poPartNoOptions: [], poDepartureOptions: [], poCustomerOptions: [], poBuyerOptions: [], transportOptions: [ { label: '海运 SEA', value: 'SEA' }, { label: '空运 AIR', value: 'AIR' }, { label: '陆运 ROAD', value: 'ROAD' } ], yesNoOptions: [ { label: 'Y', value: 'Y' }, { label: 'N', value: 'N' } ] }, dictSelectOptionsMap: {}, searchData: { site: this.$store.state.user.site, customer: '', buyer: '', poNo: '', orderType: '', partNo: '', supplierNo: '', sku: '', status: '', supplierName: '' }, poOrderTypeSearchOptions: [{ label: '全部', value: '' }, ...PO_ORDER_TYPE_OPTIONS], advancedQueryVisible: false, /** 字段 schema(list 场景) */ fieldSchemaFields: [], baseColumnList: null, /** 高级查询条件对象(查询均读取;仅弹窗内「重置」可清空) */ advancedQueryData: createAdvancedQueryData(), poOrderStatusOptions: PO_ORDER_STATUS_OPTIONS, poOrderTypeOptions: PO_ORDER_TYPE_FILTER_OPTIONS, height: 400, pageIndex: 1, pageSize: 20, totalPage: 0, dataList: [], editMode: false, columnList: [ { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1Customer', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'customer', headerAlign: 'center', align: 'center', columnLabel: 'Customer', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 180, showOverflowTooltip: true, editAble: true, editType: 'select', selectOptionsKey: 'poCustomerOptions', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1Buyer', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'buyer', headerAlign: 'center', align: 'center', columnLabel: 'Buyer', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 90, showOverflowTooltip: true, editAble: true, editType: 'select', selectOptionsKey: 'poBuyerOptions', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1VendorName', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'supplierName', headerAlign: 'center', align: 'center', columnLabel: 'Vendor Name', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 180, showOverflowTooltip: true, editAble: false, editType: 'select', selectOptionsKey: 'supplierOptions', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1VShortOffice', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'shortOffice', headerAlign: 'center', align: 'center', columnLabel: 'V_Short_Office', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 110, showOverflowTooltip: true, editAble: false, editType: 'text', selectOptionsKey: '', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1VShortNpc', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'shortNpc', headerAlign: 'center', align: 'center', columnLabel: 'V_Short_NPC', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 100, showOverflowTooltip: true, editAble: false, editType: 'text', selectOptionsKey: '', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1PoNo', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'poNo', headerAlign: 'center', align: 'center', columnLabel: 'PO_No', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 120, showOverflowTooltip: true, editAble: true, editType: 'text', selectOptionsKey: '', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1OrderType', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'orderType', headerAlign: 'center', align: 'center', columnLabel: '订单类型', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 100, showOverflowTooltip: true, editAble: false, editType: 'text', selectOptionsKey: '', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1PartNo', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'partNo', headerAlign: 'center', align: 'center', columnLabel: 'Part No', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 120, showOverflowTooltip: true, editAble: false, editType: 'select', selectOptionsKey: 'poPartNoOptions', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1Item', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'item', headerAlign: 'center', align: 'center', columnLabel: 'Item', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 120, showOverflowTooltip: true, editAble: false, editType: 'text', selectOptionsKey: '', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1Category', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'categoryName', headerAlign: 'center', align: 'center', columnLabel: 'Category', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 90, showOverflowTooltip: true, editAble: false, editType: 'text', selectOptionsKey: '', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1Sku', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'sku', headerAlign: 'center', align: 'center', columnLabel: 'SKU', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 100, showOverflowTooltip: true, editAble: false, editType: 'text', selectOptionsKey: '', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1UnitPrice', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'price', headerAlign: 'center', align: 'center', columnLabel: 'Unit Price', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 90, showOverflowTooltip: true, editAble: true, editType: 'num', selectOptionsKey: '', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1PQty', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'qty', headerAlign: 'center', align: 'center', columnLabel: 'PO Qty', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 80, showOverflowTooltip: true, editAble: true, editType: 'num', selectOptionsKey: '', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1PValue', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'value', headerAlign: 'center', align: 'center', columnLabel: 'PO Value', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 90, showOverflowTooltip: true, editAble: false, editType: 'num', selectOptionsKey: '', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1AQty', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'shipQty', headerAlign: 'center', align: 'center', columnLabel: 'A-Qty', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 80, showOverflowTooltip: true, editAble: true, editType: 'text', selectOptionsKey: '', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1AValue', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'sumPrice', headerAlign: 'center', align: 'center', columnLabel: 'A-Value', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 90, showOverflowTooltip: true, editAble: true, editType: 'text', selectOptionsKey: '', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1PDeparture', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'departure', headerAlign: 'center', align: 'center', columnLabel: 'Departure Port', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 100, showOverflowTooltip: true, editAble: true, editType: 'select', selectOptionsKey: 'departureOptions', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1PDestination', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'destination', headerAlign: 'center', align: 'center', columnLabel: 'Destination Port', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 110, showOverflowTooltip: true, editAble: true, editType: 'select', selectOptionsKey: 'destinationOptions', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1SMethod', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'shipVia', headerAlign: 'center', align: 'center', columnLabel: 'Method', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 90, showOverflowTooltip: true, editAble: true, editType: 'select', selectOptionsKey: 'transportOptions', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1DIssue', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'orderDate', headerAlign: 'center', align: 'center', columnLabel: 'Buyer Release Date', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 130, showOverflowTooltip: true, editAble: false, editType: 'date', selectOptionsKey: '', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1DEnquiry', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'enquiry', headerAlign: 'center', align: 'center', columnLabel: 'Planner Release Date', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 140, showOverflowTooltip: true, editAble: false, editType: 'date', selectOptionsKey: '', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1Status', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'status', headerAlign: 'center', align: 'center', columnLabel: 'Status', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 80, showOverflowTooltip: true, editAble: false, editType: 'select', selectOptionsKey: 'poStatusOptions', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1DConfirmed', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'confirmed', headerAlign: 'center', align: 'center', columnLabel: 'PI Confirmed Date', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 120, showOverflowTooltip: true, editAble: true, editType: 'date', selectOptionsKey: '', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1DNeed', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'wantReceiveDate', headerAlign: 'center', align: 'center', columnLabel: 'Original Arrival Date', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 130, showOverflowTooltip: true, editAble: true, editType: 'date', selectOptionsKey: '', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1DNarrival', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'narrival', headerAlign: 'center', align: 'center', columnLabel: 'Final Confirmed Arrival Date', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 110, showOverflowTooltip: true, editAble: true, editType: 'date', selectOptionsKey: '', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1DCrd', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'crd', headerAlign: 'center', align: 'center', columnLabel: 'PI Confirmed CRD', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 120, showOverflowTooltip: true, editAble: true, editType: 'date', selectOptionsKey: '', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1DCrdOri', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'crdOri', headerAlign: 'center', align: 'center', columnLabel: 'Original CRD', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 120, showOverflowTooltip: true, editAble: false, editType: 'date', selectOptionsKey: '', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1DInspection', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'inspection', headerAlign: 'center', align: 'center', columnLabel: 'Inspection Date', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 100, showOverflowTooltip: true, editAble: true, editType: 'date', selectOptionsKey: '', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1ACrd', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'realCrd', headerAlign: 'center', align: 'center', columnLabel: 'Actual Completion Date', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 150, showOverflowTooltip: true, editAble: true, editType: 'date', selectOptionsKey: '', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1Eta', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'eta', headerAlign: 'center', align: 'center', columnLabel: 'ETA', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 100, showOverflowTooltip: true, editAble: false, editType: 'date', selectOptionsKey: '', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1Flexid', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'flexid', headerAlign: 'center', align: 'center', columnLabel: 'Flexport Id', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 90, showOverflowTooltip: true, editAble: true, editType: 'text', selectOptionsKey: '', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1AObd', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'obd', headerAlign: 'center', align: 'center', columnLabel: 'On Board Date', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 100, showOverflowTooltip: true, editAble: true, editType: 'date', selectOptionsKey: '', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1DocSent', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'docSent', headerAlign: 'center', align: 'center', columnLabel: 'Doc Sent', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 90, showOverflowTooltip: true, editAble: true, editType: 'select', selectOptionsKey: 'yesNoOptions', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1TelexRelease', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'telexRelease', headerAlign: 'center', align: 'center', columnLabel: 'Telex Release', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 100, showOverflowTooltip: true, editAble: true, editType: 'select', selectOptionsKey: 'yesNoOptions', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1AdvancePayment', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'advancePayment', headerAlign: 'center', align: 'center', columnLabel: 'Advance Payment', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 120, showOverflowTooltip: true, editAble: true, editType: 'text', selectOptionsKey: '', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1AdvancePaid', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'advancePaid', headerAlign: 'center', align: 'center', columnLabel: 'Advanc Paid', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 100, showOverflowTooltip: true, editAble: true, editType: 'select', selectOptionsKey: 'yesNoOptions', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1DCreate', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'createTime', headerAlign: 'center', align: 'center', columnLabel: 'Create Date', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 100, showOverflowTooltip: true, editAble: false, editType: 'date', selectOptionsKey: '', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1MPlanner', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'planner', headerAlign: 'center', align: 'center', columnLabel: 'M-Planner', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 100, showOverflowTooltip: true, editAble: true, editType: 'text', selectOptionsKey: '', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1DEdit', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'edit', headerAlign: 'center', align: 'center', columnLabel: 'Edit Date', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 100, showOverflowTooltip: true, editAble: false, editType: 'date', selectOptionsKey: '', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1EPlanner', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'endPlanner', headerAlign: 'center', align: 'center', columnLabel: 'Last Editor', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 100, showOverflowTooltip: true, editAble: false, editType: 'text', selectOptionsKey: '', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1RemarkDelay', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'remarkDelay', headerAlign: 'center', align: 'center', columnLabel: 'Remark Delay', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 110, showOverflowTooltip: true, editAble: true, editType: 'text', selectOptionsKey: '', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1Comments', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'remark', headerAlign: 'center', align: 'center', columnLabel: 'Comments', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 180, showOverflowTooltip: true, editAble: true, editType: 'text', selectOptionsKey: '', selectOptions: null }, { userId: this.$store.state.user.name, functionId: 812003, serialNumber: '812003Table1InspectionCompletedQty', tableId: '812003Table1', tableName: 'PO Order', columnProp: 'inspectionCompletedQty', headerAlign: 'center', align: 'center', columnLabel: 'Inspection Completed Qty', columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 140, showOverflowTooltip: true, editAble: false, editType: 'text', selectOptionsKey: '', selectOptions: null } ] } }, computed: { visibleColumns () { const source = this.baseColumnList || this.columnList const applied = applyFieldSchemaToColumns(source, this.fieldSchemaFields) const cols = applied.filter(c => !c.columnHidden) const leadProps = ['poNo', 'orderType'] const leading = leadProps.map(p => cols.find(c => c.columnProp === p)).filter(Boolean) const rest = cols.filter(c => !leadProps.includes(c.columnProp)) return [...leading, ...rest] }, addExtFields () { return getEditableExtFieldsFromSchema(this.addFormSchemaFields) }, addDetailExtFields () { return this.addExtFields }, addBaseFields () { if (!Array.isArray(this.addFormSchemaFields)) { return [] } const sortedFields = this.addFormSchemaFields .filter(f => f && f.visibleFlag === 'Y' && !this.isExtSchemaField(f)) .slice() .sort((a, b) => { const sa = Number(a && a.sortNo) const sb = Number(b && b.sortNo) const left = Number.isFinite(sa) ? sa : Number.MAX_SAFE_INTEGER const right = Number.isFinite(sb) ? sb : Number.MAX_SAFE_INTEGER return left - right }) const tailFields = [] const normalFields = [] sortedFields.forEach(field => { if (ADD_FULL_ROW_FIELD_CODES.has(this.getAddFieldCode(field))) { tailFields.push(field) return } normalFields.push(field) }) return [...normalFields, ...tailFields] }, addMasterBaseFields () { return this.addBaseFields.filter(field => this.isAddMasterField(field)) }, addDetailBaseFields () { return this.addBaseFields.filter(field => !this.isAddMasterField(field)) }, hasSelectedAddDetailRow () { if (!this.addDetailCurrentDraftId) { return false } return this.addDetailRows.some(row => row && row._draftId === this.addDetailCurrentDraftId) }, addDetailDialogTitle () { if (this.addDetailDialogMode === 'edit') { return '修改明细' } if (this.addDetailDialogMode === 'copy') { return '复制明细' } return '新增明细' }, uploadColumnMappingEntries () { if (!this.uploadColumnMapping || typeof this.uploadColumnMapping !== 'object') { return [] } return Object.keys(this.uploadColumnMapping).map(header => ({ header, fieldCode: this.uploadColumnMapping[header] })) } }, created () { this.baseColumnList = JSON.parse(JSON.stringify(this.columnList)) this.getPartNoOptions() this.getSupplierOptions() this.getCustomerOptions() this.getBuyerOptions() }, mounted () { this.updateTableHeight() window.addEventListener('resize', this.onWindowResize) }, activated () { this.updateTableHeight() this.refreshTableLayout() }, deactivated () { this.refreshTableLayout() }, beforeDestroy () { window.removeEventListener('resize', this.onWindowResize) if (this.addPriceLookupTimer) { clearTimeout(this.addPriceLookupTimer) this.addPriceLookupTimer = null } }, methods: { canIssuePoOrder, canShipPoOrder, canClosePoOrder, canOpenPoOrder, canEditPoColumnByStatus,
onWindowResize () { this.updateTableHeight() }, updateTableHeight () { // 主表按可视区动态高度计算,扣除分页与底部预留,避免超出或留白 - rqrq
this.$nextTick(() => { const pageRootEl = this.$refs.pageRoot || this.$el const tableVm = this.$refs.poTable const tableEl = tableVm && tableVm.$el if (!pageRootEl || !tableEl) { return }
const pagerVm = this.$refs.mainPager const pagerEl = (pagerVm && pagerVm.$el) || pageRootEl.querySelector('.el-pagination') let pagerOuterHeight = 0 if (pagerEl) { const pagerRect = pagerEl.getBoundingClientRect() const pagerStyle = window.getComputedStyle(pagerEl) pagerOuterHeight = pagerRect.height + (parseFloat(pagerStyle.marginTop) || 0) + (parseFloat(pagerStyle.marginBottom) || 0) }
const rootStyle = window.getComputedStyle(pageRootEl) const rootPaddingBottom = parseFloat(rootStyle.paddingBottom) || 0 const tableTop = tableEl.getBoundingClientRect().top const viewportBottomGap = 16 const availableHeight = window.innerHeight - tableTop - pagerOuterHeight - rootPaddingBottom - viewportBottomGap const nextHeight = Number.isFinite(availableHeight) ? Math.floor(availableHeight) : 240 this.height = nextHeight > 240 ? nextHeight : 240 this.refreshTableLayout() }) }, refreshTableLayout () { this.$nextTick(() => { if (this.$refs.poTable && this.$refs.poTable.doLayout) { this.$refs.poTable.doLayout() } }) },
formatColumnCell (row, item) { if (item.columnProp === 'orderType') { return formatPoOrderTypeLabel(row.orderType) } const prop = item && item.columnProp if (!prop || !row) { return '' } if (row[prop] !== undefined && row[prop] !== null) { return row[prop] } if (row.extValues && typeof row.extValues === 'object' && Object.prototype.hasOwnProperty.call(row.extValues, prop)) { return row.extValues[prop] } return '' },
/** Chooselist 选择列 */ isChooserEditColumn (columnProp) { return this.chooserEditColumnProps.includes(columnProp) },
/** * 当前行该列是否可编辑:列配置 editAble + 状态限制(Part No、Vendor Name、Customer、Buyer、PO_No 草稿/已下达) */ canEditCell (row, item) { if (!this.editMode || !item) return false const prop = item.columnProp if (prop === 'customer') return false if (this.displayOnlyColumnProps.includes(prop)) return false if (this.isChooserEditColumn(prop)) { return canEditPoColumnByStatus(row.status, prop) } if (!item.editAble) return false return canEditPoColumnByStatus(row.status, prop) }, getAddFieldCode (field) { if (!field) return '' if (field.fieldCode) { return String(field.fieldCode).trim().toLowerCase() } return this.camelToSnake(this.getAddFieldProp(field)) }, getAddFieldProp (field) { if (!field) return '' if (field.fieldProp) { return String(field.fieldProp).trim() } if (field.fieldCode) { return this.snakeToCamel(field.fieldCode) } return '' }, isExtSchemaField (field) { const code = this.getAddFieldCode(field) return code.indexOf('ext_') === 0 }, normalizeAddFieldLabel (field) { return String((field && field.fieldLabel) || '').trim().toLowerCase().replace(/\s+/g, ' ') }, isAddCreateDateField (field) { const code = this.getAddFieldCode(field) if (ADD_READONLY_DEFAULT_TODAY_FIELD_CODES.has(code)) { return true } return this.normalizeAddFieldLabel(field) === 'create date' }, isAddPoPlaceDateField (field) { const code = this.getAddFieldCode(field) if (ADD_EDITABLE_DEFAULT_TODAY_FIELD_CODES.has(code)) { return true } return this.normalizeAddFieldLabel(field) === 'po place date' }, isAddMasterField (field) { const code = this.getAddFieldCode(field) if (ADD_FORCE_DETAIL_FIELD_CODES.has(code)) { return false } const mandatoryFlag = String((field && field.mandatoryFlag) || '').trim().toUpperCase() return mandatoryFlag === 'Y' }, getSchemaFieldByCode (fieldCode) { const normalizedCode = String(fieldCode || '').trim().toLowerCase() if (!normalizedCode || !Array.isArray(this.addFormSchemaFields)) { return null } return this.addFormSchemaFields.find(field => this.getAddFieldCode(field) === normalizedCode) || null }, getAddTableColumnLabel (fieldCode, fallback) { const field = this.getSchemaFieldByCode(fieldCode) return (field && field.fieldLabel) || fallback || fieldCode }, snakeToCamel (text) { return String(text || '').replace(/_([a-z0-9])/g, (_, c) => c.toUpperCase()) }, camelToSnake (text) { return String(text || '').replace(/([A-Z])/g, '_$1').toLowerCase().replace(/^_/, '') }, isAddChooserField (field) { return ADD_CHOOSER_FIELD_CODES.has(this.getAddFieldCode(field)) }, openAddChooserByField (field, target = 'master') { const code = this.getAddFieldCode(field) if (code === 'supplier_name') { this.openAddSupplierSelect(target) } else if (code === 'part_no') { this.openAddPartNoSelect(target) } else if (code === 'departure') { this.openAddDepartureSelect(target) } else if (code === 'destination') { this.openAddDestinationSelect(target) } else if (code === 'ship_via') { this.openAddShipViaSelect(target) } }, getAddChooserPlaceholder (field) { const code = this.getAddFieldCode(field) if (code === 'supplier_name') return '请选择供应商' if (code === 'part_no') return '请选择物料' if (code === 'departure') return '请选择始发港' if (code === 'destination') return '请选择目的港' if (code === 'ship_via') return '请选择运输方式' return '' }, getAddFieldSelectOptions (field) { const code = this.getAddFieldCode(field) const optionsKey = ADD_SELECT_OPTIONS_FIELD_CODE_MAP[code] if (!optionsKey) { return [] } if (Array.isArray(this[optionsKey])) { return this[optionsKey] } if (this.columnSelectOptions && Array.isArray(this.columnSelectOptions[optionsKey])) { return this.columnSelectOptions[optionsKey] } return [] }, isAddSelectField (field) { return this.getAddFieldSelectOptions(field).length > 0 }, isAddFilterableSelectField (field) { return ADD_FILTERABLE_SELECT_FIELD_CODES.has(this.getAddFieldCode(field)) }, isAddDateField (field) { const editorType = String((field && field.editorType) || '').trim().toLowerCase() const dataType = String((field && field.dataType) || '').trim().toLowerCase() return editorType === 'date' || editorType === 'datetime' || dataType === 'date' || dataType === 'datetime' }, isAddTextareaField (field) { return ADD_TEXTAREA_FIELD_CODES.has(this.getAddFieldCode(field)) }, isAddNumberField (field) { const code = this.getAddFieldCode(field) if (ADD_NUMBER_FIELD_CODES.has(code)) { return true } const dataType = String((field && field.dataType) || '').trim().toLowerCase() return dataType === 'number' || dataType === 'num' || dataType === 'decimal' || dataType === 'int' }, isAddFieldDisabled (field) { const code = this.getAddFieldCode(field) if (code === 'customer') { return true } if (this.isAddCreateDateField(field)) { return true } if (ADD_DISPLAY_ONLY_FIELD_CODES.has(code)) { return true } const editorType = String((field && field.editorType) || '').trim().toLowerCase() return editorType === 'readonly' }, getAddFieldSpan (field) { if (ADD_FULL_ROW_FIELD_CODES.has(this.getAddFieldCode(field))) { return 24 } return this.isAddTextareaField(field) ? 12 : 6 }, onAddFieldInput (field, target = 'master') { const code = this.getAddFieldCode(field) if (code === 'price' || code === 'qty') { this.recalcAddFormValue(target) } if (code === 'qty') { this.scheduleAutoFillAddPriceByTierPrice(target) } }, getAddTargetForm (target = 'master') { return target === 'detail' ? this.addDetailForm : this.addForm }, getSupplierCodeForTargetForm (form, target = 'master') { const formSupplier = String((form && form.supplierNo) || '').trim() if (formSupplier) { return formSupplier } if (target === 'detail') { return String((this.addForm && this.addForm.supplierNo) || '').trim() } return '' }, scheduleAutoFillAddPriceByTierPrice (target = 'master') { if (this.addPriceLookupTimer) { clearTimeout(this.addPriceLookupTimer) this.addPriceLookupTimer = null } this.addPriceLookupTimer = setTimeout(() => { this.addPriceLookupTimer = null this.autoFillAddPriceByTierPrice(target) }, 250) }, autoFillAddPriceByTierPrice (target = 'master') { if (!this.addDialogVisible) { return Promise.resolve() } const targetForm = this.getAddTargetForm(target) if (!targetForm) { return Promise.resolve() } const site = (this.searchData.site || this.$store.state.user.site || '').trim() const supplierCode = this.getSupplierCodeForTargetForm(targetForm, target) const partNo = String(targetForm.partNo || '').trim() const qtyVal = targetForm.qty if (!site || !supplierCode || !partNo || qtyVal === '' || qtyVal === null || qtyVal === undefined) { return Promise.resolve() } const qty = Number(qtyVal) if (Number.isNaN(qty) || qty < 0) { return Promise.resolve() } const seq = ++this.addPriceLookupSeq return listPoCurrentTierPrice({ site, supplierCode, partNo }).then(({ data }) => { if (seq !== this.addPriceLookupSeq) { return } if (!(data && data.code === 0)) { return } const matchedRow = this.findCurrentTierPriceByQty(data.rows || [], qty) if (!matchedRow) { return } const normalizedPrice = this.normalizeAddNumericValue(matchedRow.unitPrice) if (normalizedPrice === '') { return } this.$set(targetForm, 'price', normalizedPrice) this.recalcAddFormValue(target) }).catch(() => {}) }, findCurrentTierPriceByQty (rows, qty) { if (!Array.isArray(rows) || rows.length === 0) { return null } const qtyNum = Number(qty) if (Number.isNaN(qtyNum)) { return null } for (const row of rows) { const start = Number(row.qtyStart) const endRaw = row.qtyEnd const end = (endRaw === '' || endRaw === null || endRaw === undefined) ? Number.POSITIVE_INFINITY : Number(endRaw) if (Number.isNaN(start) || Number.isNaN(end)) { continue } if (qtyNum >= start && qtyNum <= end) { return row } } return null }, normalizeAddNumericValue (value) { if (value === '' || value === null || value === undefined) { return '' } const num = Number(value) if (Number.isNaN(num)) { return '' } if (Number.isInteger(num)) { return String(num) } return String(Number(num.toFixed(4))) },
handleClosePoOrder (row) { const params = buildPoDetailActionParams(row, this.$store.state.user.site) if (!params.id && !(params.orderNo && params.site && params.itemNo != null)) { this.$message.error('无法识别订单行,请刷新后重试') return } const poLabel = row.poNo ? `PO ${row.poNo}` : '该订单' const itemLabel = row.itemNo != null ? ` 行 ${row.itemNo}` : '' this.$confirm(`确定关闭${poLabel}${itemLabel}?`, '提示', { type: 'warning' }).then(() => { closePoDetail(params).then(({ data }) => { if (data && data.code === 0) { this.$message.success('关闭成功') this.getDataList() } else { this.$message.error((data && data.msg) || '关闭失败') } }).catch(err => { this.$message.error((err && err.message) || '关闭失败') }) }).catch(() => {}) },
handleIssuePoOrder (row) { const params = buildPoDetailActionParams(row, this.$store.state.user.site) if (!params.id && !(params.orderNo && params.site && params.itemNo != null)) { this.$message.error('无法识别订单行,请刷新后重试') return } const poLabel = row.poNo ? `PO ${row.poNo}` : '该订单' const itemLabel = row.itemNo != null ? ` 行 ${row.itemNo}` : '' this.$confirm(`确定下达${poLabel}${itemLabel}?`, '提示', { type: 'warning' }).then(() => { issuePoDetail(params).then(({ data }) => { if (data && data.code === 0) { this.$message.success('下达成功') this.getDataList() } else { this.$message.error((data && data.msg) || '下达失败') } }).catch(err => { this.$message.error((err && err.message) || '下达失败') }) }).catch(() => {}) },
handleShipPoOrder (row) { const params = buildPoDetailActionParams(row, this.$store.state.user.site) if (!params.id && !(params.orderNo && params.site && params.itemNo != null)) { this.$message.error('无法识别订单行,请刷新后重试') return } const poLabel = row.poNo ? `PO ${row.poNo}` : '该订单' const itemLabel = row.itemNo != null ? ` 行 ${row.itemNo}` : '' this.$confirm(`确定将${poLabel}${itemLabel}标记为已发货?`, '提示', { type: 'warning' }).then(() => { shipPoDetail(params).then(({ data }) => { if (data && data.code === 0) { this.$message.success('发货成功') this.getDataList() } else { this.$message.error((data && data.msg) || '发货失败') } }).catch(err => { this.$message.error((err && err.message) || '发货失败') }) }).catch(() => {}) },
handleOpenPoOrder (row) { const params = buildPoDetailActionParams(row, this.$store.state.user.site) if (!params.id && !(params.orderNo && params.site && params.itemNo != null)) { this.$message.error('无法识别订单行,请刷新后重试') return } const poLabel = row.poNo ? `PO ${row.poNo}` : '该订单' const itemLabel = row.itemNo != null ? ` 行 ${row.itemNo}` : '' const targetStatus = resolvePoOrderStatusByInspectQty(row) this.$confirm(`确定打开${poLabel}${itemLabel}?恢复状态为:${targetStatus}`, '提示', { type: 'warning' }).then(() => { openPoDetail(params).then(({ data }) => { if (data && data.code === 0) { this.$message.success('打开成功') this.getDataList() } else { this.$message.error((data && data.msg) || '打开失败') } }).catch(err => { this.$message.error((err && err.message) || '打开失败') }) }).catch(() => {}) },
/** * 表格当前行变化:将选中行深拷贝到 currentRow;取消选中时为 null - rqrq */ onTableCurrentChange (row) { if (!row) { this.currentRow = null return } this.currentRow = row },
/** * 解析列对应的下拉选项:优先列上 selectOptions,其次 columnSelectOptions[selectOptionsKey] */ getSelectOptionsForColumn (item) { if (item && item.editType === 'dict-select') { const dictType = this.getSchemaDictType(item) if (!dictType) { return [] } return this.dictSelectOptionsMap[dictType] || [] } if (item.selectOptions && item.selectOptions.length) { return item.selectOptions } const key = item.selectOptionsKey if (key && this.columnSelectOptions[key]) { return this.columnSelectOptions[key] } return [] },
getSchemaDictType (item) { if (!item) { return '' } if (item.dictType) { return String(item.dictType).trim() } const prop = item.columnProp if (!prop) { return '' } const field = (this.fieldSchemaFields || []).find(f => { const code = String((f && f.fieldCode) || '').trim() const fieldProp = String((f && f.fieldProp) || '').trim() return fieldProp === prop || this.snakeToCamel(code) === prop }) return field && field.dictType ? String(field.dictType).trim() : '' },
preloadDictSelectOptions (schemaFields) { const site = this.searchData.site || this.$store.state.user.site if (!site || !Array.isArray(schemaFields) || schemaFields.length === 0) { this.dictSelectOptionsMap = {} return Promise.resolve() } const dictTypeSet = new Set() schemaFields.forEach(field => { const editorType = String((field && field.editorType) || '').trim().toLowerCase() if (editorType !== 'dict-select' && editorType !== 'dict_select') { return } const dictType = String((field && field.dictType) || '').trim() if (dictType) { dictTypeSet.add(dictType) } }) const dictTypes = Array.from(dictTypeSet) if (dictTypes.length === 0) { this.dictSelectOptionsMap = {} return Promise.resolve() } const nextMap = {} const loaders = dictTypes.map(dictType => { return selectDictDataList({ site, dictType, dictTypeList: [], status: 'Y' }).then(({ data }) => { if (data && data.code === 0) { nextMap[dictType] = (data.rows || []).map(item => ({ label: item.dictLabel || item.dictValue, value: item.dictValue })) } else { nextMap[dictType] = [] this.$message.error((data && data.msg) || `字典加载失败: ${dictType}`) } }).catch(() => { nextMap[dictType] = [] this.$message.error(`字典加载失败: ${dictType}`) }) }) return Promise.all(loaders).then(() => { this.dictSelectOptionsMap = nextMap }) },
buildQueryRequest() { return buildPoOrderQueryParams({ searchData: this.searchData, advancedQueryData: this.advancedQueryData, page: this.pageIndex, limit: this.pageSize }) },
onSessionCustomerChange (customer, customerOption) { this.searchData.customer = customer this.selectedCustomerScope = customerOption || null this.dictSelectOptionsMap = {} this.getBuyerOptions(customer) this.pageIndex = 1 this.masterRows = [] this.dataList = [] this.totalPage = 0 if (!customer) { this.selectedCustomerScope = null this.fieldSchemaFields = [] this.poBuyerOptions = [] this.columnSelectOptions.poBuyerOptions = [] this.refreshTableLayout() return } this.loadFieldSchema(customer).then(() => { this.getDataList() }) },
loadFieldSchema (customer) { return getPoFieldSchema({ site: this.searchData.site, customer, scene: 'list' }).then(({ data }) => { if (data && data.code === 0 && data.data) { this.fieldSchemaFields = data.data.fields || [] const queryFields = getAdvancedQueryFieldsFromSchema(this.fieldSchemaFields) const nextAdvanced = createAdvancedQueryDataFromSchema(queryFields) this.advancedQueryData.rows = nextAdvanced.rows return this.preloadDictSelectOptions(this.fieldSchemaFields) } else { this.fieldSchemaFields = [] this.$message.error((data && data.msg) || '加载字段权限失败') } }).then(() => { this.refreshTableLayout() }).catch(() => { this.fieldSchemaFields = [] this.$message.error('加载字段权限失败') this.refreshTableLayout() }) },
handleSearch() { if (!this.searchData.customer) { this.$message.warning('请先选择 Customer') return } this.pageIndex = 1 this.getDataList() },
openEamList (tagNo, strVal = '', conSql = '', context = 'row', pickField = '') { this.eamTagNo = tagNo this.pickContext = context this.eamPickField = pickField this.$nextTick(() => { this.$refs.eamList.init(tagNo, strVal, conSql) }) },
resetEamPick () { this.pickContext = '' this.eamTagNo = '' this.eamPickField = '' this.currentSelectRow = null this.currentPartNoRow = null this.currentDepartureRow = null this.currentDestinationRow = null this.currentShipViaRow = null },
openSearchBaseList (val) { let strVal = '' let conSql = '' if (val === 520) { strVal = this.searchData.supplierNo || '' conSql = " and supplier_no <> '' " } else if (val === 2017) { strVal = this.searchData.partNo || '' conSql = " and part_no <> '' " } this.openEamList(val, strVal, conSql, 'search') },
getEamData (data) { if (!data) { return } const tag = this.eamTagNo const ctx = this.pickContext
if (ctx === 'search') { if (tag === 520) { this.searchData.supplierNo = data.supplier_no || data.code || '' this.searchData.supplierName = data.supplier_name || data.name || '' } else if (tag === 2017) { this.searchData.partNo = data.part_no || data.code || '' } this.resetEamPick() return }
if (tag === SUPPLIER_CHOOSELIST_TAG || tag === 520) { const { supplierNo, supplierName } = parseSupplierChooselistRow(data) if (ctx === 'add') { this.fillSupplierShortFields(this.addForm, supplierNo, supplierName, false).then(() => { this.scheduleAutoFillAddPriceByTierPrice('master') }) } else if (ctx === 'addDetail') { this.fillSupplierShortFields(this.addDetailForm, supplierNo, supplierName, false).then(() => { this.scheduleAutoFillAddPriceByTierPrice('detail') }) } else if (this.currentSelectRow) { const row = this.currentSelectRow this.fillSupplierShortFields(row, supplierNo, supplierName, true) } this.resetEamPick() return }
if (tag === 507 || tag === 2017) { const partNo = data.part_no || data.partNo || data.code || '' if (ctx === 'add') { this.$set(this.addForm, 'partNo', partNo) this.fillPartCategorySku(partNo, this.addForm) this.scheduleAutoFillAddPriceByTierPrice('master') } else if (ctx === 'addDetail') { this.$set(this.addDetailForm, 'partNo', partNo) this.fillPartCategorySku(partNo, this.addDetailForm) this.scheduleAutoFillAddPriceByTierPrice('detail') } else if (this.currentPartNoRow) { this.$set(this.currentPartNoRow, 'partNo', partNo) this.fillPartCategorySku(partNo, this.currentPartNoRow) this.$set(this.currentPartNoRow, 'changeFlag', 1) } this.resetEamPick() return }
if (tag === 2018) { const code = this.eamPickField === 'destination' ? (data.port_code || data.portCode || data.destination || '') : (data.port_code || data.portCode || data.departure || '') const portName = data.port_name || data.portName || '' if (ctx === 'add') { if (this.eamPickField === 'departure') { this.addForm.departure = code } else if (this.eamPickField === 'destination') { this.addForm.destination = code } } else if (ctx === 'addDetail') { if (this.eamPickField === 'departure') { this.addDetailForm.departure = code } else if (this.eamPickField === 'destination') { this.addDetailForm.destination = code } } else if (this.eamPickField === 'departure' && this.currentDepartureRow) { this.$set(this.currentDepartureRow, 'departure', code) this.$set(this.currentDepartureRow, 'departureName', portName) this.$set(this.currentDepartureRow, 'changeFlag', 1) } else if (this.eamPickField === 'destination' && this.currentDestinationRow) { this.$set(this.currentDestinationRow, 'destination', code) this.$set(this.currentDestinationRow, 'destinationName', portName) this.$set(this.currentDestinationRow, 'changeFlag', 1) } this.resetEamPick() return }
if (tag === 2019) { const via = data.method_desc || data.methodDesc || data.shipVia || '' if (ctx === 'add') { this.addForm.shipVia = via } else if (ctx === 'addDetail') { this.addDetailForm.shipVia = via } else if (this.currentShipViaRow) { this.$set(this.currentShipViaRow, 'shipVia', via) this.$set(this.currentShipViaRow, 'shipViaCode', data.method_no || data.methodNo || '') this.$set(this.currentShipViaRow, 'changeFlag', 1) } this.resetEamPick() } },
getDataList() { if (!this.searchData.customer) { this.loading = false this.dataList = [] this.masterRows = [] this.totalPage = 0 this.refreshTableLayout() return } this.loading = true const { params, useAdvanced } = this.buildQueryRequest() const request = useAdvanced ? searchPoDetailAdvancedPage(params) : searchPoDetailPage(params) request.then(({ data }) => { if (data.code === 0) { const list = (data.page && data.page.list ? data.page.list : []).map(row => normalizeSchemaRowValues(row)) this.dataList = list this.masterRows = list.map(row => ({ ...row, changeFlag: 0 }))
this.pageIndex = data.page.currPage this.pageSize = data.page.pageSize this.totalPage = data.page.totalCount } else { this.$message.error((data && data.msg) || '获取列表失败') } this.loading = false this.refreshTableLayout() }).catch(() => { this.loading = false this.$message.error('请求失败') this.refreshTableLayout() }) }, sizeChangeHandle (val) { this.pageSize = val this.pageIndex = 1 this.getDataList() }, currentChangeHandle (val) { this.pageIndex = val this.getDataList() }, onCellChange (row) { if (row.price !== undefined || row.qty !== undefined) { this.$set(row, 'value', this.calcPoValue(row.price, row.qty)) } this.$set(row, 'changeFlag', 1) },
calcPoValue (price, qty) { if (price === '' || price == null || qty === '' || qty == null) { return '' } const p = Number(price) const q = Number(qty) if (isNaN(p) || isNaN(q)) { return '' } const total = p * q return Number.isInteger(total) ? String(total) : String(Number(total.toFixed(4))) },
recalcAddFormValue (target = 'master') { const form = this.getAddTargetForm(target) if (!form) { return } this.$set(form, 'value', this.calcPoValue(form.price, form.qty)) },
validatePriceAndQty (price, qty, label) { if (price === '' || price == null) { this.$message.warning((label ? label + ':' : '') + '请填写 Unit Price') return false } if (qty === '' || qty == null) { this.$message.warning((label ? label + ':' : '') + '请填写 PO Qty') return false } return true },
handleSupplierChange(row, supplierNo) { console.log('供应商变更:', supplierNo, '行ID:', row.id)
const selectedOption = this.supplierOptions.find(opt => opt.value === supplierNo)
if (selectedOption) { this.$set(row, 'supplierName', selectedOption.label) this.$set(row, 'supplierNo', selectedOption.value) this.$set(row, 'shortOffice', selectedOption.shortOffice || '') this.$set(row, 'shortNpc', selectedOption.shortNpc || '') this.$set(row, 'changeFlag', 1) } },
handlePartNoChange(row, partNo) { console.log('物料变更:', partNo, '行ID:', row.id) // 根据 partNo 找到对应的物料信息
const selectedOption = this.poPartNoOptions.find(opt => opt.value === partNo)
if (selectedOption) { // 更新物料相关信息
this.$set(row, 'partNo', selectedOption.value) this.$set(row, 'item', selectedOption.item || '') //同时更新 category 和 sku
this.$set(row, 'category', selectedOption.category || '') this.$set(row, 'categoryName', selectedOption.categoryName || '') this.$set(row, 'sku', selectedOption.sku || '') this.$set(row, 'changeFlag', 1) } }, handlePortChange(row, field, value) { console.log(`${field}变更:`, value, '行ID:', row.id) this.$set(row, field, value) // 直接设置字段值
this.$set(row, 'changeFlag', 1)
// 注意:这里还需要同步更新对应的 rowDeparture/rowDestination/rowShipVia
if (field === 'departure') { this.$set(this.rowDeparture, row.id, value) } else if (field === 'destination') { this.$set(this.rowDestination, row.id, value) } else if (field === 'shipVia') { this.$set(this.rowShipVia, row.id, value) }
// 如果三个字段都填写了,获取运输方式选项
if (row.departure && row.destination && row.shipVia) { this.getTransportOptions(row.departure, row.destination, row.shipVia) } }, getTransportOptions(departure, destination, shipVia) { console.log('获取运输方式选项:', departure, destination, shipVia) //调用接口获取运输方式选项
searchTransportOptions({ departure, destination, shipVia }).then(({ data }) => { if (data && data.code === 0) { this.transportOptions = (data.rows || []).map(item => ({ label: item.shipVia, value: item.shipVia, eta: item.eta })) } else { this.$message.error(data.msg || '获取运输方式选项失败') } }).catch(error => { this.$message.error('获取运输方式选项失败: ' + error.message) }) },
toggleEditMode () { if (!this.editMode) { this.editMode = true return } const changed = this.masterRows.filter(r => r.changeFlag === 1)
this.batchUpdatePoOrder(changed)
},
cancelEditMode () { this.editMode = false this.resetEamPick() this.getDataList() },
formatCodeOptionLabel (code, name) { if (!name) { return code || '' } if (!code) { return name } return `${code} - ${name}` },
getCustomerOptions () { return getPoCustomerOptions({ site: this.$store.state.user.site }).then(({ data }) => { if (data && data.code === 0) { this.poCustomerOptions = (data.rows || []).map(item => ({ label: this.formatCodeOptionLabel(item.code, item.name), value: item.code })) this.columnSelectOptions.poCustomerOptions = this.poCustomerOptions } else { this.$message.error(data.msg || '操作失败') } }).catch(error => { this.$message.error('操作失败: ' + error.message) }) },
getBuyerOptions (customer = this.searchData.customer) { const customerCode = (customer || '').trim() if (!customerCode) { this.poBuyerOptions = [] this.columnSelectOptions.poBuyerOptions = [] return Promise.resolve([]) } this.poBuyerOptions = [] this.columnSelectOptions.poBuyerOptions = [] return getPoBuyerOptions({ site: this.$store.state.user.site, customer: customerCode }).then(({ data }) => { if (data && data.code === 0) { this.poBuyerOptions = (data.rows || []).map(item => ({ label: this.formatCodeOptionLabel(item.code, item.name), value: item.code })) this.columnSelectOptions.poBuyerOptions = this.poBuyerOptions } else { this.$message.error(data.msg || '操作失败') } }).catch(error => { this.$message.error('操作失败: ' + error.message) }) },
getPartNoOptions() { console.log('获取分类选项') getPartList( { site: this.$store.state.user.site } ).then(({data}) => { if (data && data.code === 0) { console.log('接口返回的rows:', data.rows) // 查看返回数据
this.poPartNoOptions = (data.rows || []).map(item => ({ label: item.partNo, value: item.partNo, item: item.item, category: item.category, categoryName: item.categoryName, sku: item.sku }))
} else { this.$message.error(data.msg || '操作失败') } }).catch(error => { this.$message.error('操作失败: ' + error.message) })
}, getSupplierOptions(){ console.log('获取供应商选项') checkSrmSupplierList( { site: this.$store.state.user.site } ).then(({data}) => { if (data && data.code === 0) { console.log('接口返回的rows:', data.rows) // 查看返回数据
this.supplierOptions = (data.rows || []).map(item => ({ label: item.supplierName, value: item.supplierNo, shortOffice: item.cshortOffice, shortNpc: item.cshortNpc }))
} else { this.$message.error(data.msg || '操作失败') } }).catch(error => { this.$message.error('操作失败: ' + error.message) }) }, getPortOptions(){ console.log('获取港口选项') searchTransportOptions( { site: this.$store.state.user.site } ).then(({data}) => { if (data && data.code === 0) { console.log('接口返回的rows:', data.rows) // 查看返回数据
this.transportOptions = (data.rows || []).map(item => ({ label: item.supplierName, value: item.supplierNo, destination: item.destination, departure: item.departure, shipVia: item.shipVia, etc: item.eta }))
} else { this.$message.error(data.msg || '操作失败') } }).catch(error => { this.$message.error('操作失败: ' + error.message) }) }, // 保存订单信息
buildBatchUpdateRow (row) { const value = this.calcPoValue(row.price, row.qty) const extValues = buildExtValuesFromRow(row, this.fieldSchemaFields) return { orderNo: row.orderNo, site: row.site, itemNo: row.itemNo, supplierNo: row.supplierNo, supplierName: row.supplierName, buyer: row.buyer, customer: row.customer, poNo: row.poNo, departure: row.departure, destination: row.destination, shipVia: row.shipVia, orderDate: row.orderDate, enquiry: row.enquiry, confirmed: row.confirmed, partNo: row.partNo, item: row.item, status: row.status, qty: row.qty === '' ? null : Number(row.qty), price: row.price === '' ? null : Number(row.price), value: value === '' ? null : Number(value), shipQty: row.shipQty, sumPrice: row.sumPrice, wantReceiveDate: row.wantReceiveDate, narrival: row.narrival, crd: row.crd, inspection: row.inspection, realCrd: row.realCrd, poPlaceDate: row.poPlaceDate, poPromiseReadyDate: row.poPromiseReadyDate, poActualReadyDate: row.poActualReadyDate, chinaOfficeInspectionDate: row.chinaOfficeInspectionDate, shipToNpcDate: row.shipToNpcDate, inspectionResult: row.inspectionResult, flexid: row.flexid, obd: row.obd, docSent: row.docSent, telexRelease: row.telexRelease, advancePayment: row.advancePayment, advancePaid: row.advancePaid, planner: row.planner, remarkDelay: row.remarkDelay, remark: row.remark, extValues } },
batchUpdatePoOrder(changed) { for (const row of changed) { const label = row.orderNo ? `订单 ${row.orderNo}` : '订单' if (!this.validatePriceAndQty(row.price, row.qty, label)) { return } this.$set(row, 'value', this.calcPoValue(row.price, row.qty)) } const updateData = changed.map(row => this.buildBatchUpdateRow(row))
// 调用批量修改接口
batchUpdatePoDetail(updateData) .then(({ data }) => { if (data && data.code === 0) { this.$message({ message: `已提交 ${updateData.length} 行变更`, type: 'success', duration: 2000 }) changed.forEach(r => { this.$set(r, 'changeFlag', 0) })
this.editMode = false this.getDataList() } else { this.$message.error(data.msg || '操作失败') } }) .catch(error => { this.$message.error('操作失败: ' + error.message) }) },
// ========== 上传功能 ==========
// 打开上传弹窗
exportModal() { if (!this.searchData.customer) { this.$message.warning('请先选择 Customer') return } this.uploadDialogVisible = true this.uploadFileName = '' this.uploadFile = null this.uploadPreviewList = [] this.uploadSkippedColumns = [] this.uploadColumnMapping = {} this.uploadPreviewColumns = [] }, // 文件选择变化
handleFileChange(file) { this.uploadFile = file.raw this.uploadFileName = file.name }, // 预览上传数据
previewUpload() { if (!this.uploadFile) { this.$message.warning('请先选择文件') return } if (!this.searchData.customer) { this.$message.warning('请先选择 Customer') return }
const formData = new FormData() formData.append('file', this.uploadFile) formData.append('site', this.searchData.site || this.$store.state.user.site) formData.append('customer', this.searchData.customer)
this.$http({ url: this.$http.adornUrl('/pODetail/previewUpload'), method: 'post', data: formData }).then(({ data }) => { if (data && data.code === 0) { const preview = data.data || {} this.uploadPreviewList = preview.rows || [] this.uploadSkippedColumns = preview.skippedColumns || [] this.uploadColumnMapping = preview.columnMapping || {} this.refreshUploadPreviewColumns() this.$message.success('文件解析成功,请确认数据后点击保存') } else { this.$message.error(data.msg || '文件解析失败') } }).catch((error) => { console.error('文件解析失败:', error) const errorMsg = (error.response && error.response.data && error.response.data.msg) || error.message || '未知错误' this.$message.error('文件解析失败: ' + errorMsg) }) }, refreshUploadPreviewColumns () { this.uploadPreviewColumns = buildImportPreviewColumns( this.uploadPreviewList, this.fieldSchemaFields, this.uploadColumnMapping ) }, exportExcel () { if (this.editMode) { this.$message.warning('快速编辑模式下不允许导出,请先点击取消或确定') return } if (!this.searchData.customer) { this.$message.warning('请先选择 Customer') return } const now = new Date() const year = now.getFullYear() const month = String(now.getMonth() + 1).padStart(2, '0') const day = String(now.getDate()).padStart(2, '0') const hour = String(now.getHours()).padStart(2, '0') const minute = String(now.getMinutes()).padStart(2, '0') const second = String(now.getSeconds()).padStart(2, '0') const exportName = `PO订单列表${year}${month}${day}${hour}${minute}${second}`
const { params, useAdvanced } = buildPoOrderQueryParams({ searchData: this.searchData, advancedQueryData: this.advancedQueryData, page: 1, limit: 99999 })
try { excel.exportTable({ url: useAdvanced ? 'pODetail/advancedPage' : 'pODetail/page', columnMapping: this.visibleColumns, mergeSetting: [], params, fileName: exportName + '.xlsx', rowFetcher: res => { const page = res.data && res.data.page const rows = ((page && page.list) || []).map(row => normalizeSchemaRowValues(row)) return { rows } }, columnFormatter: [], dropColumns: [] }) } catch (error) { this.$message.error('导出失败: ' + error.message) } }, // 确认上传
confirmUpload() { if (this.uploadPreviewList.length === 0) { this.$message.warning('没有可保存的数据') return }
this.$confirm(`确认保存 ${this.uploadPreviewList.length} 条数据吗?`, '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { const formData = new FormData() formData.append('file', this.uploadFile) formData.append('site', this.searchData.site || this.$store.state.user.site) formData.append('customer', this.searchData.customer)
this.$http({ url: this.$http.adornUrl('/pODetail/batchSave'), method: 'post', data: formData }).then(({ data }) => { if (data && data.code === 0) { const result = data.data || {} const successCount = result.successCount || 0 const failCount = result.failCount || 0 let msg = `导入完成:成功 ${successCount} 条,失败 ${failCount} 条` if (result.rowErrors && result.rowErrors.length) { msg += ';' + result.rowErrors.slice(0, 3).map(e => `第${e.rowNo}行 ${e.message}`).join(';') } if (failCount > 0) { this.$message.warning(msg) } else { this.$message.success(msg) } this.uploadDialogVisible = false this.getDataList() } else { this.$message.error(data.msg || '数据保存失败') } }).catch((error) => { console.error('数据保存失败:', error) const errorMsg = (error.response && error.response.data && error.response.data.msg) || error.message || '未知错误' this.$message.error('数据保存失败: ' + errorMsg) }) }).catch(() => {}) }, // 下载模板
downloadTemplate() { const loading = this.$loading({ lock: true, text: '正在下载模板...', spinner: 'el-icon-loading', background: 'rgba(0, 0, 0, 0.7)' })
this.$http({ url: this.$http.adornUrl('/pODetail/downloadTemplate'), method: 'get', responseType: 'blob' }).then(response => { loading.close()
// 创建下载文件
const blob = new Blob([response.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
const url = window.URL.createObjectURL(blob) const link = document.createElement('a') link.href = url link.download = 'PO订单导入模板.xlsx'
document.body.appendChild(link) link.click() document.body.removeChild(link)
window.URL.revokeObjectURL(url)
this.$message.success('模板下载成功') }).catch(error => { loading.close() console.error('模板下载失败:', error) this.$message.error('模板下载失败,请联系管理员') }) },
// 打开供应商选择弹窗
openSupplierSelect(row) { this.currentSelectRow = row const conSql = " and site = '" + this.$store.state.user.site + "'" this.openEamList(SUPPLIER_CHOOSELIST_TAG, '', conSql, 'row', 'supplier') },
applySupplierSelection (target, supplierNo, supplierName, shortOffice, shortNpc, markChanged) { if (!target) return this.$set(target, 'supplierName', supplierName) this.$set(target, 'supplierNo', supplierNo) this.$set(target, 'shortOffice', shortOffice) this.$set(target, 'shortNpc', shortNpc) if (markChanged) { this.$set(target, 'changeFlag', 1) } },
fillSupplierShortFields (target, supplierNo, supplierName, markChanged) { if (!target || !supplierNo) { return Promise.resolve() } this.applySupplierSelection(target, supplierNo, supplierName, target.shortOffice || '', target.shortNpc || '', markChanged) return fetchSupplierDetail(supplierNo, this.$store.state.user.site).then(detail => { const { shortOffice, shortNpc } = resolveSupplierShortFieldsFromDetail(detail) this.applySupplierSelection(target, supplierNo, supplierName, shortOffice, shortNpc, markChanged) }).catch(() => {}) },
fillPartCategorySku (partNo, target) { if (!partNo || !target) return const targetSite = String(target.site || this.$store.state.user.site || '').trim() const apply = (item, category, categoryName, sku) => { this.$set(target, 'item', item) this.$set(target, 'category', category) this.$set(target, 'categoryName', categoryName) this.$set(target, 'sku', sku) } const fromList = this.poPartNoOptions.find(opt => opt.value === partNo) if (fromList) { apply(fromList.item || '', fromList.category || '', fromList.categoryName || '', fromList.sku || '') return } searchPartInfo({ site: targetSite, partNo }).then(({ data }) => { if (data && data.code === 0 && data.data) { apply(data.data.item || '', data.data.category || '', data.data.categoryName || '', data.data.sku || '') } }).catch(() => {}) }, applySelectedPartToTarget (target, partRow, markChanged) { if (!target || !partRow) { return } const partNo = partRow.partNo || partRow.part_no || '' if (!partNo) { return } this.$set(target, 'partNo', partNo) this.$set(target, 'partDesc', partRow.partDesc || '') this.$set(target, 'item', partRow.item || '') this.$set(target, 'sku', partRow.sku || '') this.fillPartCategorySku(partNo, target) if (markChanged) { this.$set(target, 'changeFlag', 1) } }, openSupplierPartDialog (context, row = null) { const site = String((context === 'row' ? (row && row.site) : (context === 'addDetail' ? this.addDetailForm.site : this.searchData.site)) || this.$store.state.user.site || '').trim() const supplierNo = String((context === 'row' ? (row && (row.supplierNo || row.supplier_no || row.supplierCode || row.supplier_code)) : (context === 'addDetail' ? (this.addDetailForm.supplierNo || this.addForm.supplierNo || '') : this.addForm.supplierNo)) || '').trim() if (!site) { this.$message.warning('请先选择site') return } if (!supplierNo) { this.$message.warning('请先选择供应商') return } this.supplierPartDialogVisible = true this.supplierPartDialogLoading = true this.supplierPartDialogRows = [] this.supplierPartDialogContext = context this.supplierPartDialogTargetRow = context === 'row' ? row : null this.supplierPartDialogSupplierNo = supplierNo this.supplierPartDialogSite = site listPoSupplierPartOptions({ site, supplierCode: supplierNo, active: 'Y' }).then(({ data }) => { if (data && data.code === 0) { this.supplierPartDialogRows = (data.rows || []).map(item => ({ partNo: item.partNo || '', partDesc: item.partDesc || '', item: item.item || '', sku: item.sku || '' })) if (this.supplierPartDialogRows.length === 0) { this.$message.warning('该供应商未维护供应商可供物料') } return } this.supplierPartDialogRows = [] this.$message.error((data && data.msg) || '获取供应商可供物料失败') }).catch(() => { this.supplierPartDialogRows = [] this.$message.error('获取供应商可供物料失败') }).finally(() => { this.supplierPartDialogLoading = false }) }, handleSupplierPartRowDblClick (row) { if (!row) { return } if (this.supplierPartDialogContext === 'add') { this.applySelectedPartToTarget(this.addForm, row, false) this.scheduleAutoFillAddPriceByTierPrice('master') } else if (this.supplierPartDialogContext === 'addDetail') { this.applySelectedPartToTarget(this.addDetailForm, row, false) this.scheduleAutoFillAddPriceByTierPrice('detail') } else if (this.supplierPartDialogContext === 'row' && this.supplierPartDialogTargetRow) { this.applySelectedPartToTarget(this.supplierPartDialogTargetRow, row, true) } this.supplierPartDialogVisible = false }, resetSupplierPartDialog () { this.supplierPartDialogLoading = false this.supplierPartDialogRows = [] this.supplierPartDialogContext = '' this.supplierPartDialogTargetRow = null this.supplierPartDialogSupplierNo = '' this.supplierPartDialogSite = '' },
resolveAddOrderTypeByCustomer () { const customerOrderType = this.selectedCustomerScope && this.selectedCustomerScope.orderType ? this.selectedCustomerScope.orderType : 'PO' return formatPoOrderTypeLabel(customerOrderType) || '采购订单' },
openAddDialog () { if (!this.searchData.customer) { this.$message.warning('请选择customer') return } if (this.addPriceLookupTimer) { clearTimeout(this.addPriceLookupTimer) this.addPriceLookupTimer = null } this.addPriceLookupSeq += 1 this.addForm = this.createEmptyAddForm(this.resolveAddOrderTypeByCustomer()) this.addForm.customer = this.searchData.customer this.addDetailRows = [] this.addDetailCurrentDraftId = '' this.addDetailDialogMode = 'create' this.addDetailDialogVisible = false this.addDetailEditingDraftId = '' this.addDetailForm = this.createEmptyAddDetailForm() this.addDialogVisible = true this.loadAddFieldSchema(this.searchData.customer) if (!this.poPartNoOptions.length) { this.getPartNoOptions() } if (!this.poCustomerOptions.length) { this.getCustomerOptions() } this.getBuyerOptions(this.searchData.customer) },
loadAddFieldSchema (customer) { return getPoFieldSchema({ site: this.searchData.site, customer, scene: 'add' }).then(({ data }) => { if (data && data.code === 0 && data.data) { this.addFormSchemaFields = data.data.fields || [] this.applyAddDefaultDateBySchema() this.addDetailForm = this.createEmptyAddDetailForm() } else { this.addFormSchemaFields = [] } }).catch(() => { this.addFormSchemaFields = [] }) },
getTodayDateYmd () { if (this.dayjs) { return this.dayjs().format('YYYY-MM-DD') } const now = new Date() const pad = (num) => String(num).padStart(2, '0') return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}` },
applyAddDefaultDateBySchema () { this.applyDefaultDateBySchemaToForm(this.addForm) },
applyDefaultDateBySchemaToForm (targetForm) { if (!targetForm || !Array.isArray(this.addFormSchemaFields) || !this.addFormSchemaFields.length) { return } const today = this.getTodayDateYmd() this.addFormSchemaFields.forEach(field => { const prop = this.getAddFieldProp(field) if (!prop) { return } if (this.isAddCreateDateField(field) || this.isAddPoPlaceDateField(field)) { this.$set(targetForm, prop, today) } }) },
getFormExtValue (targetForm, fieldCode) { if (!targetForm || !targetForm.extValues) { return '' } return targetForm.extValues[fieldCode] == null ? '' : targetForm.extValues[fieldCode] },
setFormExtValue (targetForm, fieldCode, val) { if (!targetForm) { return } if (!targetForm.extValues) { this.$set(targetForm, 'extValues', {}) } this.$set(targetForm.extValues, fieldCode, val) },
getAddExtValue (fieldCode) { return this.getFormExtValue(this.addForm, fieldCode) },
setAddExtValue (fieldCode, val) { this.setFormExtValue(this.addForm, fieldCode, val) },
getAddDetailExtValue (fieldCode) { return this.getFormExtValue(this.addDetailForm, fieldCode) },
setAddDetailExtValue (fieldCode, val) { this.setFormExtValue(this.addDetailForm, fieldCode, val) },
createEmptyAddForm (orderType = '采购订单') { const today = this.getTodayDateYmd() return { customer: '', buyer: '', supplierName: '', supplierNo: '', shortOffice: '', shortNpc: '', poNo: '', orderType: orderType || '采购订单', partNo: '', item: '', category: '', categoryName: '', sku: '', price: '', qty: '', value: '', shipQty: '', sumPrice: '', departure: '', destination: '', shipVia: '', orderDate: '', enquiry: '', status: '草稿', confirmed: '', wantReceiveDate: '', narrival: '', crd: '', inspection: '', realCrd: '', poPlaceDate: '', poPromiseReadyDate: '', poActualReadyDate: '', chinaOfficeInspectionDate: '', shipToNpcDate: '', inspectionResult: '', flexid: '', obd: '', docSent: '', telexRelease: '', advancePayment: 'N', advancePaid: '', planner: '', remarkDelay: '', remark: '', extValues: {} } },
createEmptyAddDetailForm () { const form = this.createEmptyAddForm(this.addForm.orderType || this.resolveAddOrderTypeByCustomer()) form.customer = this.searchData.customer || form.customer form.site = this.searchData.site || this.$store.state.user.site this.applyDefaultDateBySchemaToForm(form) return form },
cloneAddDraftRow (row) { const cloned = JSON.parse(JSON.stringify(row || {})) if (!cloned.extValues || typeof cloned.extValues !== 'object') { cloned.extValues = {} } return cloned },
buildAddDetailRowFromForm () { const row = this.cloneAddDraftRow(this.addDetailForm) if (!row._draftId) { this.addDetailDraftSeq += 1 row._draftId = `DRAFT_${Date.now()}_${this.addDetailDraftSeq}` } row.value = this.calcPoValue(row.price, row.qty) return row },
getSelectedAddDetailRow () { if (!this.addDetailCurrentDraftId) { return null } return this.addDetailRows.find(row => row && row._draftId === this.addDetailCurrentDraftId) || null },
onAddDetailCurrentChange (row) { this.addDetailCurrentDraftId = row && row._draftId ? row._draftId : '' },
onAddDetailRowClick (row) { this.addDetailCurrentDraftId = row && row._draftId ? row._draftId : '' if (this.$refs.addDetailTable && this.$refs.addDetailTable.setCurrentRow) { this.$refs.addDetailTable.setCurrentRow(row) } },
openAddDetailDialog () { this.addDetailDialogMode = 'create' this.addDetailEditingDraftId = '' this.addDetailForm = this.createEmptyAddDetailForm() this.addDetailDialogVisible = true },
openEditAddDetailDialog () { const selected = this.getSelectedAddDetailRow() if (!selected) { this.$message.warning('请先选择一条明细') return } this.addDetailDialogMode = 'edit' this.addDetailEditingDraftId = selected._draftId this.addDetailForm = this.cloneAddDraftRow(selected) this.addDetailDialogVisible = true },
openCopyAddDetailDialog () { const selected = this.getSelectedAddDetailRow() if (!selected) { this.$message.warning('请先选择一条明细') return } const copied = this.cloneAddDraftRow(selected) copied._draftId = '' copied.partNo = '' this.addDetailDialogMode = 'copy' this.addDetailEditingDraftId = '' this.addDetailForm = copied this.addDetailDialogVisible = true },
removeAddDetailRow () { const selected = this.getSelectedAddDetailRow() if (!selected) { this.$message.warning('请先选择一条明细') return } this.addDetailRows = this.addDetailRows.filter(row => row && row._draftId !== selected._draftId) this.addDetailCurrentDraftId = '' this.$nextTick(() => { if (this.$refs.addDetailTable && this.$refs.addDetailTable.setCurrentRow) { this.$refs.addDetailTable.setCurrentRow(null) } }) },
isAddFormFieldEmpty (value) { if (value === null || value === undefined) { return true } if (typeof value === 'string') { return value.trim() === '' } return false },
validateRequiredFieldGroup (fields, form, prefixLabel = '') { if (!Array.isArray(fields)) { return true } for (const field of fields) { if (!field || field.requiredFlag !== 'Y') { continue } const prop = this.getAddFieldProp(field) if (!prop) { continue } const value = form ? form[prop] : null if (this.isAddFormFieldEmpty(value)) { this.$message.warning(`${prefixLabel}${field.fieldLabel}不能为空`) return false } } return true },
validateRequiredExtFieldGroup (fields, form, prefixLabel = '') { if (!Array.isArray(fields)) { return true } for (const field of fields) { if (!field || field.requiredFlag !== 'Y') { continue } const fieldCode = String((field && field.fieldCode) || '').trim() if (!fieldCode) { continue } const value = this.getFormExtValue(form, fieldCode) if (this.isAddFormFieldEmpty(value)) { this.$message.warning(`${prefixLabel}${field.fieldLabel}不能为空`) return false } } return true },
saveAddDetailDraft () { if (!this.validateRequiredFieldGroup(this.addDetailBaseFields, this.addDetailForm, '明细:')) { return } if (!this.validateRequiredExtFieldGroup(this.addDetailExtFields, this.addDetailForm, '明细:')) { return } const nextRow = this.buildAddDetailRowFromForm() if (this.addDetailDialogMode === 'edit' && this.addDetailEditingDraftId) { const targetIndex = this.addDetailRows.findIndex(row => row && row._draftId === this.addDetailEditingDraftId) if (targetIndex >= 0) { nextRow._draftId = this.addDetailEditingDraftId this.$set(this.addDetailRows, targetIndex, nextRow) } } else { this.addDetailRows.push(nextRow) } this.addDetailCurrentDraftId = nextRow._draftId this.addDetailDialogVisible = false this.$nextTick(() => { if (this.$refs.addDetailTable && this.$refs.addDetailTable.setCurrentRow) { const selected = this.addDetailRows.find(row => row && row._draftId === nextRow._draftId) || null this.$refs.addDetailTable.setCurrentRow(selected) } }) },
openAddSupplierSelect (target = 'master') { const conSql = " and site = '" + this.$store.state.user.site + "'" const context = target === 'detail' ? 'addDetail' : 'add' this.openEamList(SUPPLIER_CHOOSELIST_TAG, '', conSql, context, 'supplier') }, openAddPartNoSelect (target = 'master') { this.openSupplierPartDialog(target === 'detail' ? 'addDetail' : 'add') }, openAddDepartureSelect (target = 'master') { const context = target === 'detail' ? 'addDetail' : 'add' this.openEamList(2018, '', this.buildDeparturePortConSql(), context, 'departure') }, openAddDestinationSelect (target = 'master') { const context = target === 'detail' ? 'addDetail' : 'add' this.openEamList(2018, '', this.buildDestinationPortConSql(), context, 'destination') }, openAddShipViaSelect (target = 'master') { const context = target === 'detail' ? 'addDetail' : 'add' this.openEamList(2019, '', '', context, 'shipVia') },
buildMergedAddDetailForm (detailRow) { const merged = {} this.addMasterBaseFields.forEach(field => { const prop = this.getAddFieldProp(field) if (!prop) return merged[prop] = this.addForm[prop] }) this.addDetailBaseFields.forEach(field => { const prop = this.getAddFieldProp(field) if (!prop) return merged[prop] = detailRow ? detailRow[prop] : null }) merged.customer = this.searchData.customer || this.addForm.customer || '' merged.extValues = { ...(detailRow && detailRow.extValues ? detailRow.extValues : {}) } return merged },
saveAddPoOrder () { if (!this.validateRequiredFieldGroup(this.addMasterBaseFields, this.addForm, '主记录:')) { return } if (!this.addForm.poNo || String(this.addForm.poNo).trim() === '') { this.$message.warning('主记录:请填写 PO No') return } if ((!this.addForm.supplierName || String(this.addForm.supplierName).trim() === '') && (!this.addForm.supplierNo || String(this.addForm.supplierNo).trim() === '')) { this.$message.warning('主记录:请填写 Vendor Name') return } if (this.addDetailRows.length === 0) { this.$message.warning('请至少新增一条明细') return }
const payloadList = [] for (let i = 0; i < this.addDetailRows.length; i++) { const row = this.addDetailRows[i] if (!this.validateRequiredFieldGroup(this.addDetailBaseFields, row, `第${i + 1}行明细:`)) { return } if (!this.validateRequiredExtFieldGroup(this.addDetailExtFields, row, `第${i + 1}行明细:`)) { return } const mergedForm = this.buildMergedAddDetailForm(row) if (!mergedForm.partNo || String(mergedForm.partNo).trim() === '') { this.$message.warning(`第${i + 1}行明细:请填写 Part No`) return } if (!this.validatePriceAndQty(mergedForm.price, mergedForm.qty, `第${i + 1}行`)) { return } const calculatedValue = this.calcPoValue(mergedForm.price, mergedForm.qty) payloadList.push(this.buildAddPayload(calculatedValue, mergedForm, this.addBaseFields, this.addExtFields)) } if (payloadList.length === 0) { this.$message.warning('请至少新增一条明细') return }
this.addFormSaving = true const runCreate = (idx) => { if (idx >= payloadList.length) { return Promise.resolve() } return createPoOrder(payloadList[idx]).then(({ data }) => { if (data && data.code === 0) { return runCreate(idx + 1) } const errMsg = (data && data.msg) || '新增失败' throw new Error(`第${idx + 1}行:${errMsg}`) }) }
runCreate(0).then(() => { this.$message.success(`新增成功,共 ${payloadList.length} 条`) this.addDialogVisible = false this.addDetailDialogVisible = false this.getDataList() }).catch(err => { this.$message.error((err && err.message) || '新增失败') }).finally(() => { this.addFormSaving = false }) }, buildAddPayload (calculatedValue, sourceForm = this.addForm, baseFields = this.addBaseFields, extFields = this.addExtFields) { const payload = { site: this.$store.state.user.site, customer: this.searchData.customer, extValues: {} } baseFields.forEach(field => { const prop = this.getAddFieldProp(field) const code = this.getAddFieldCode(field) if (!prop || !code) { return } let value = sourceForm[prop] if (code === 'customer') { value = this.searchData.customer || value } else if (code === 'value') { value = calculatedValue === '' ? null : Number(calculatedValue) } else if (code === 'price' || code === 'qty' || code === 'ship_qty' || code === 'sum_price') { value = (value === '' || value === null || value === undefined) ? null : Number(value) } payload[prop] = value }) extFields.forEach(field => { const fieldCode = (field && field.fieldCode) ? String(field.fieldCode).trim() : '' if (!fieldCode) { return } const value = this.getFormExtValue(sourceForm, fieldCode) if (value === '' || value === null || value === undefined) { return } payload.extValues[fieldCode] = value }) return payload },
openPartNoSelect (row) { this.openSupplierPartDialog('row', row) },
openDepartureSelect (row) { this.currentDepartureRow = row this.openEamList(2018, '', this.buildDeparturePortConSql(), 'row', 'departure') },
openDestinationSelect (row) { this.currentDestinationRow = row this.openEamList(2018, '', this.buildDestinationPortConSql(), 'row', 'destination') },
openShipViaSelect (row) { this.currentShipViaRow = row this.openEamList(2019, '', '', 'row', 'shipVia') },
/** * 判断是否为新增行(没有 id 或 id 为空表示新增) */ isNewRow(row) { return !row.id || row.id === '' || row.id === null || row.id === undefined },
buildDeparturePortConSql () { return " and is_departure_port = 'Y'" },
buildDestinationPortConSql () { return " and is_destination_port = 'Y'" }
}}</script>
<style scoped lang="scss">.po-add-form-wrap { max-height: calc(100vh - 210px); overflow-y: scroll; overflow-x: hidden; padding: 0 12px 8px;}
/* 预留滚动槽位,避免滚动条出现/消失导致表单宽度抖动 */@supports (scrollbar-gutter: stable) { .po-add-form-wrap { overflow-y: auto; scrollbar-gutter: stable both-edges; }}
/* 防止滚动条切换时标签换行,进一步降低布局抖动 */.po-add-form-wrap /deep/ .el-form--label-top .el-form-item__label { white-space: nowrap;}
.po-add-detail-toolbar { margin: 10px 0 8px; display: flex; justify-content: space-between; align-items: center;}
.po-add-detail-title { font-size: 13px; color: #606266;}
.el-table /deep/ .cell { height: auto; line-height: 1.5;}</style>
|