You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

3274 lines
110 KiB

<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="1000px" top="4vh"
:close-on-click-modal="false" append-to-body>
<div class="po-add-form-wrap">
<el-form ref="addFormRef" :model="addForm" label-position="top" size="small">
<el-row :gutter="15">
<el-col v-for="field in addBaseFields" :key="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)">
<i slot="suffix" class="el-icon-search" style="cursor:pointer" @click.stop="openAddChooserByField(field)"></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)" />
</template>
</el-form-item>
</el-col>
</el-row>
<el-row v-if="addExtFields.length" :gutter="24">
<el-col v-for="field in addExtFields" :key="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="getAddExtValue(field.fieldCode)"
@input="val => setAddExtValue(field.fieldCode, val)"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
</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="选择供应商可供物料"
: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_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: {},
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)
},
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]
},
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
},
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) {
const code = this.getAddFieldCode(field)
if (code === 'supplier_name') {
this.openAddSupplierSelect()
} else if (code === 'part_no') {
this.openAddPartNoSelect()
} else if (code === 'departure') {
this.openAddDepartureSelect()
} else if (code === 'destination') {
this.openAddDestinationSelect()
} else if (code === 'ship_via') {
this.openAddShipViaSelect()
}
},
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 (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) {
const code = this.getAddFieldCode(field)
if (code === 'price' || code === 'qty') {
this.recalcAddFormValue()
}
if (code === 'qty') {
this.scheduleAutoFillAddPriceByTierPrice()
}
},
scheduleAutoFillAddPriceByTierPrice () {
if (this.addPriceLookupTimer) {
clearTimeout(this.addPriceLookupTimer)
this.addPriceLookupTimer = null
}
this.addPriceLookupTimer = setTimeout(() => {
this.addPriceLookupTimer = null
this.autoFillAddPriceByTierPrice()
}, 250)
},
autoFillAddPriceByTierPrice () {
if (!this.addDialogVisible) {
return Promise.resolve()
}
const site = (this.searchData.site || this.$store.state.user.site || '').trim()
const supplierCode = String(this.addForm.supplierNo || '').trim()
const partNo = String(this.addForm.partNo || '').trim()
const qtyVal = this.addForm.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(this.addForm, 'price', normalizedPrice)
this.recalcAddFormValue()
}).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()
})
} 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()
} 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 (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 (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 () {
this.addForm.value = this.calcPoValue(this.addForm.price, this.addForm.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) : 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))
: 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()
} 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.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 || []
} else {
this.addFormSchemaFields = []
}
}).catch(() => {
this.addFormSchemaFields = []
})
},
getAddExtValue (fieldCode) {
if (!this.addForm.extValues) {
return ''
}
return this.addForm.extValues[fieldCode] == null ? '' : this.addForm.extValues[fieldCode]
},
setAddExtValue (fieldCode, val) {
if (!this.addForm.extValues) {
this.$set(this.addForm, 'extValues', {})
}
this.$set(this.addForm.extValues, fieldCode, val)
},
createEmptyAddForm (orderType = '采购订单') {
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: {}
}
},
openAddSupplierSelect () {
const conSql = " and site = '" + this.$store.state.user.site + "'"
this.openEamList(SUPPLIER_CHOOSELIST_TAG, '', conSql, 'add', 'supplier')
},
openAddPartNoSelect () {
this.openSupplierPartDialog('add')
},
openAddDepartureSelect () {
this.openEamList(2018, '', this.buildDeparturePortConSql(), 'add', 'departure')
},
openAddDestinationSelect () {
this.openEamList(2018, '', this.buildDestinationPortConSql(), 'add', 'destination')
},
openAddShipViaSelect () {
this.openEamList(2019, '', '', 'add', 'shipVia')
},
saveAddPoOrder () {
if (!this.addForm.poNo || !this.addForm.supplierName || !this.addForm.partNo) {
this.$message.warning('请填写 PO No、Vendor Name、Part No')
return
}
if (!this.validatePriceAndQty(this.addForm.price, this.addForm.qty)) {
return
}
this.recalcAddFormValue()
this.addFormSaving = true
const calculatedValue = this.calcPoValue(this.addForm.price, this.addForm.qty)
const payload = this.buildAddPayload(calculatedValue)
createPoOrder(payload).then(({ data }) => {
if (data && data.code === 0) {
this.$message.success('新增成功')
this.addDialogVisible = false
this.getDataList()
} else {
this.$message.error((data && data.msg) || '新增失败')
}
}).catch(err => {
this.$message.error((err && err.message) || '新增失败')
}).finally(() => {
this.addFormSaving = false
})
},
buildAddPayload (calculatedValue) {
const payload = {
site: this.$store.state.user.site,
customer: this.searchData.customer,
extValues: {}
}
this.addBaseFields.forEach(field => {
const prop = this.getAddFieldProp(field)
const code = this.getAddFieldCode(field)
if (!prop || !code) {
return
}
let value = this.addForm[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
})
this.addExtFields.forEach(field => {
const fieldCode = (field && field.fieldCode) ? String(field.fieldCode).trim() : ''
if (!fieldCode) {
return
}
const value = this.getAddExtValue(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;
}
.el-table /deep/ .cell {
height: auto;
line-height: 1.5;
}
</style>