|
|
|
@ -1,254 +1,20 @@ |
|
|
|
<template> |
|
|
|
<div class="po-order-detail"> |
|
|
|
<!-- 查看模式 --> |
|
|
|
<!-- |
|
|
|
订单详情动态渲染区(按字段权限 schema 控制) |
|
|
|
规则: |
|
|
|
1) 普通字段固定 1 格(24 栅格中的 4)=> 一行 6 格; |
|
|
|
2) Vendor Name 固定 2 格(8); |
|
|
|
3) Comments 固定全行(24)。 |
|
|
|
--> |
|
|
|
<el-form label-position="top" class="detail-form"> |
|
|
|
<el-row :gutter="20"> |
|
|
|
<el-col :span="8"> |
|
|
|
<el-form-item label="Customer"> |
|
|
|
<el-input v-model="detailData.customer" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="Buyer"> |
|
|
|
<el-input v-model="detailData.buyer" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="8"> |
|
|
|
<el-form-item label="Vendor Name"> |
|
|
|
<el-input v-model="detailData.supplierName" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="V_Short_Office"> |
|
|
|
<el-input v-model="detailData.shortOffice" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
|
|
|
|
</el-row> |
|
|
|
<el-row :gutter="20"> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="V_Short_NPC"> |
|
|
|
<el-input v-model="detailData.shortNpc" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="PO_No"> |
|
|
|
<el-input v-model="detailData.poNo" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="Part No"> |
|
|
|
<el-input v-model="detailData.partNo" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="Item"> |
|
|
|
<el-input v-model="detailData.item" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="Category"> |
|
|
|
<el-input v-model="detailData.category" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="SKU"> |
|
|
|
<el-input v-model="detailData.sku" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="Unit Price"> |
|
|
|
<el-input v-model="detailData.price" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
</el-row> |
|
|
|
|
|
|
|
<el-row :gutter="20"> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="PO Value"> |
|
|
|
<el-input v-model="detailData.value" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="A-Value"> |
|
|
|
<el-input v-model="detailData.sumPrice" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="Departure Port"> |
|
|
|
<el-input v-model="detailData.departure" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="Destination Port"> |
|
|
|
<el-input v-model="detailData.destination" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="Method"> |
|
|
|
<el-input v-model="detailData.shipVia" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="Buyer Release Date"> |
|
|
|
<el-input v-model="detailData.orderDate" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
</el-row> |
|
|
|
|
|
|
|
<el-row :gutter="20"> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="Planner Release Date"> |
|
|
|
<el-input v-model="detailData.enquiry" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="Status"> |
|
|
|
<el-input v-model="detailData.status" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="PI Confirmed Date"> |
|
|
|
<el-input v-model="detailData.confirmed" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="Original Arrival Date"> |
|
|
|
<el-input v-model="detailData.wantReceiveDate" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="Final Confirmed Arrival Date"> |
|
|
|
<el-input v-model="detailData.narrival" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="PI Confirmed CRD"> |
|
|
|
<el-input v-model="detailData.crd" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
</el-row> |
|
|
|
<el-row :gutter="20"> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="Original CRD"> |
|
|
|
<el-input v-model="detailData.crdOri" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
</el-row> |
|
|
|
<el-row :gutter="20"> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="Inspection Date"> |
|
|
|
<el-input v-model="detailData.inspection" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="Actual Completion Date"> |
|
|
|
<el-input v-model="detailData.realCrd" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="ETA"> |
|
|
|
<el-input v-model="detailData.eta" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="Flexport Id"> |
|
|
|
<el-input v-model="detailData.flexid" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="On Board Date"> |
|
|
|
<el-input v-model="detailData.obd" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="Doc Sent"> |
|
|
|
<el-input v-model="detailData.docSent" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
</el-row> |
|
|
|
|
|
|
|
<el-row :gutter="20"> |
|
|
|
|
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="Telex Release"> |
|
|
|
<el-input v-model="detailData.telexRelease" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="Advance Payment"> |
|
|
|
<el-input v-model="detailData.advancePayment" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="Advanc Paid"> |
|
|
|
<el-input v-model="detailData.advancePaid" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="Create Date"> |
|
|
|
<el-input v-model="detailData.createTime" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="M-Planner"> |
|
|
|
<el-input v-model="detailData.planner" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="Edit Date"> |
|
|
|
<el-input v-model="detailData.edit" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
</el-row> |
|
|
|
|
|
|
|
<el-row :gutter="20"> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="Last Editor"> |
|
|
|
<el-input v-model="detailData.endPlanner" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="Remark Delay"> |
|
|
|
<el-input v-model="detailData.remarkDelay" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="8"> |
|
|
|
<el-form-item label="Comments"> |
|
|
|
<el-input v-model="detailData.remark" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label=" "> |
|
|
|
<span></span> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
</el-row> |
|
|
|
|
|
|
|
<el-row :gutter="20"> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="PO Qty"> |
|
|
|
<el-input v-model="detailData.qty" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="A-Qty"> |
|
|
|
<el-input v-model="detailData.shipQty" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="已申请验货数量"> |
|
|
|
<el-input v-model="detailData.inspectingQty" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="4"> |
|
|
|
<el-form-item label="验货完成数量"> |
|
|
|
<el-input v-model="detailData.inspectionCompletedQty" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
<el-col :span="8"> |
|
|
|
<el-form-item label=" "> |
|
|
|
<span></span> |
|
|
|
<el-col |
|
|
|
v-for="field in displayFields" |
|
|
|
:key="getFieldKey(field)" |
|
|
|
:span="getFieldSpan(field)"> |
|
|
|
<el-form-item :label="getFieldLabel(field)"> |
|
|
|
<el-input :value="getFieldDisplayValue(field)" disabled /> |
|
|
|
</el-form-item> |
|
|
|
</el-col> |
|
|
|
</el-row> |
|
|
|
@ -257,18 +23,238 @@ |
|
|
|
</template> |
|
|
|
|
|
|
|
<script> |
|
|
|
import { formatPoOrderTypeLabel } from './poOrderTypeOptions.js' |
|
|
|
|
|
|
|
export default { |
|
|
|
name: 'PoOrderDetail', |
|
|
|
props: { |
|
|
|
detailData: { |
|
|
|
type: Object, |
|
|
|
default: () => ({}) |
|
|
|
}, |
|
|
|
schemaFields: { |
|
|
|
type: Array, |
|
|
|
default: () => [] |
|
|
|
} |
|
|
|
}, |
|
|
|
computed: { |
|
|
|
/** |
|
|
|
* 详情页签显示字段(基于权限 schema 动态决定)。 |
|
|
|
* 处理逻辑: |
|
|
|
* 1) 仅保留 visibleFlag=Y 的字段; |
|
|
|
* 2) 同一 field_code 去重,防止异常配置导致重复渲染; |
|
|
|
* 3) 按 sortNo 升序,保证与列表页/权限配置顺序一致。 |
|
|
|
* 返回值:用于页面渲染的字段数组(已过滤、去重、排序)。 |
|
|
|
* 边界处理:schemaFields 非数组时直接返回空数组,避免页面渲染报错。 |
|
|
|
*/ |
|
|
|
displayFields () { |
|
|
|
if (!Array.isArray(this.schemaFields)) { |
|
|
|
return [] |
|
|
|
} |
|
|
|
const seen = new Set() |
|
|
|
const visible = this.schemaFields.filter(field => { |
|
|
|
if (!field) { |
|
|
|
return false |
|
|
|
} |
|
|
|
const visibleFlag = String(field.visibleFlag || 'Y').toUpperCase() |
|
|
|
if (visibleFlag !== 'Y') { |
|
|
|
return false |
|
|
|
} |
|
|
|
const key = this.getFieldKey(field) |
|
|
|
if (!key || seen.has(key)) { |
|
|
|
return false |
|
|
|
} |
|
|
|
seen.add(key) |
|
|
|
return true |
|
|
|
}) |
|
|
|
return visible.sort((a, b) => { |
|
|
|
const left = Number(a && a.sortNo) |
|
|
|
const right = Number(b && b.sortNo) |
|
|
|
const la = Number.isFinite(left) ? left : Number.MAX_SAFE_INTEGER |
|
|
|
const rb = Number.isFinite(right) ? right : Number.MAX_SAFE_INTEGER |
|
|
|
if (la !== rb) { |
|
|
|
return la - rb |
|
|
|
} |
|
|
|
return this.getFieldKey(a).localeCompare(this.getFieldKey(b)) |
|
|
|
}) |
|
|
|
} |
|
|
|
}, |
|
|
|
methods: { |
|
|
|
/** |
|
|
|
* 生成字段唯一键。 |
|
|
|
* 优先级:fieldCode > fieldProp > fieldLabel。 |
|
|
|
* 入参:field(单个字段 schema 对象,允许为空)。 |
|
|
|
* 返回值:稳定字符串 key;若字段信息缺失则返回空字符串。 |
|
|
|
* 边界处理:字段为空时走空字符串回退,避免 v-for key 异常。 |
|
|
|
*/ |
|
|
|
getFieldKey (field) { |
|
|
|
const code = this.getFieldCode(field) |
|
|
|
if (code) { |
|
|
|
return code |
|
|
|
} |
|
|
|
const prop = this.getFieldProp(field) |
|
|
|
if (prop) { |
|
|
|
return prop |
|
|
|
} |
|
|
|
return String((field && field.fieldLabel) || '').trim() |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* 解析字段编码(snake_case)。 |
|
|
|
* 入参兼容: |
|
|
|
* 1) 后端直接返回 fieldCode; |
|
|
|
* 2) 无 fieldCode 时由 fieldProp 反推 snake_case。 |
|
|
|
* 入参:field(字段 schema 对象)。 |
|
|
|
* 返回值:字段编码字符串;无法解析时返回空字符串。 |
|
|
|
* 边界处理:field 为空或关键属性缺失时不抛错,统一回空串。 |
|
|
|
*/ |
|
|
|
getFieldCode (field) { |
|
|
|
if (!field) { |
|
|
|
return '' |
|
|
|
} |
|
|
|
const code = String(field.fieldCode || '').trim() |
|
|
|
if (code) { |
|
|
|
return code.toLowerCase() |
|
|
|
} |
|
|
|
const prop = String(field.fieldProp || '').trim() |
|
|
|
return prop ? this.camelToSnake(prop) : '' |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* 解析字段属性(camelCase),用于读取当前行对象值。 |
|
|
|
* 入参兼容: |
|
|
|
* 1) 后端直接返回 fieldProp; |
|
|
|
* 2) 无 fieldProp 时由 fieldCode 反推 camelCase。 |
|
|
|
* 入参:field(字段 schema 对象)。 |
|
|
|
* 返回值:字段属性名;无法解析时返回空字符串。 |
|
|
|
* 边界处理:field 为空或编码异常时统一回空串,避免取值报错。 |
|
|
|
*/ |
|
|
|
getFieldProp (field) { |
|
|
|
if (!field) { |
|
|
|
return '' |
|
|
|
} |
|
|
|
const prop = String(field.fieldProp || '').trim() |
|
|
|
if (prop) { |
|
|
|
return prop |
|
|
|
} |
|
|
|
const code = String(field.fieldCode || '').trim() |
|
|
|
return code ? this.snakeToCamel(code) : '' |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* 获取展示标签。 |
|
|
|
* 优先 fieldLabel;若异常缺失则回退字段属性名,避免出现空标签。 |
|
|
|
* 入参:field(字段 schema 对象)。 |
|
|
|
* 返回值:展示标签字符串。 |
|
|
|
* 边界处理:label/prop/code 都缺失时返回空串,不中断渲染流程。 |
|
|
|
*/ |
|
|
|
getFieldLabel (field) { |
|
|
|
const label = String((field && field.fieldLabel) || '').trim() |
|
|
|
if (label) { |
|
|
|
return label |
|
|
|
} |
|
|
|
return this.getFieldProp(field) || this.getFieldCode(field) |
|
|
|
}, |
|
|
|
data() { |
|
|
|
return {} |
|
|
|
|
|
|
|
/** |
|
|
|
* 按业务布局规则计算栅格宽度: |
|
|
|
* - Vendor Name(supplier_name): 2格 => span=8 |
|
|
|
* - Comments(remark): 全行 => span=24 |
|
|
|
* - 其他字段: 1格 => span=4 |
|
|
|
* 入参:field(字段 schema 对象)。 |
|
|
|
* 返回值:Element 栅格 span 数值。 |
|
|
|
* 边界处理:未匹配特殊字段时统一走默认 1 格,保证布局稳定。 |
|
|
|
*/ |
|
|
|
getFieldSpan (field) { |
|
|
|
const code = this.getFieldCode(field) |
|
|
|
const label = this.getFieldLabel(field).trim().toLowerCase() |
|
|
|
if (code === 'supplier_name' || label === 'vendor name') { |
|
|
|
return 8 |
|
|
|
} |
|
|
|
if (code === 'remark' || code === 'comments' || label === 'comments') { |
|
|
|
return 24 |
|
|
|
} |
|
|
|
return 4 |
|
|
|
}, |
|
|
|
methods: {} |
|
|
|
|
|
|
|
/** |
|
|
|
* 读取详情字段原始值。 |
|
|
|
* 读取顺序: |
|
|
|
* 1) detailData[fieldProp] |
|
|
|
* 2) detailData[fieldCode](兼容少量 snake_case 回传) |
|
|
|
* 3) detailData.extValues[fieldCode/fieldProp](兼容扩展字段) |
|
|
|
* 入参:field(字段 schema 对象)。 |
|
|
|
* 返回值:原始字段值;取不到值时返回空字符串。 |
|
|
|
* 边界处理:detailData/extValues 为空或结构异常时安全降级为空串。 |
|
|
|
*/ |
|
|
|
getFieldRawValue (field) { |
|
|
|
const row = this.detailData || {} |
|
|
|
const prop = this.getFieldProp(field) |
|
|
|
const code = this.getFieldCode(field) |
|
|
|
if (prop && row[prop] !== undefined && row[prop] !== null) { |
|
|
|
return row[prop] |
|
|
|
} |
|
|
|
if (code && row[code] !== undefined && row[code] !== null) { |
|
|
|
return row[code] |
|
|
|
} |
|
|
|
const extValues = row.extValues && typeof row.extValues === 'object' ? row.extValues : null |
|
|
|
if (extValues) { |
|
|
|
if (code && extValues[code] !== undefined && extValues[code] !== null) { |
|
|
|
return extValues[code] |
|
|
|
} |
|
|
|
if (prop && extValues[prop] !== undefined && extValues[prop] !== null) { |
|
|
|
return extValues[prop] |
|
|
|
} |
|
|
|
} |
|
|
|
return '' |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* 获取最终显示值。 |
|
|
|
* 特殊处理: |
|
|
|
* 1) order_type/orderType 走统一枚举格式化; |
|
|
|
* 2) 其余字段按原值输出,空值统一返回空字符串。 |
|
|
|
* 入参:field(字段 schema 对象)。 |
|
|
|
* 返回值:可直接绑定到输入框的展示值。 |
|
|
|
* 边界处理:空值统一归一为空串,避免界面出现 undefined/null。 |
|
|
|
*/ |
|
|
|
getFieldDisplayValue (field) { |
|
|
|
const raw = this.getFieldRawValue(field) |
|
|
|
const code = this.getFieldCode(field) |
|
|
|
const prop = this.getFieldProp(field) |
|
|
|
if (raw === null || raw === undefined) { |
|
|
|
return '' |
|
|
|
} |
|
|
|
if (code === 'order_type' || prop === 'orderType') { |
|
|
|
return formatPoOrderTypeLabel(raw) |
|
|
|
} |
|
|
|
return raw |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* snake_case 转 camelCase。 |
|
|
|
* 入参:snake(下划线字段名)。 |
|
|
|
* 返回值:camelCase 字段名;入参为空时原样返回。 |
|
|
|
*/ |
|
|
|
snakeToCamel (snake) { |
|
|
|
if (!snake || !snake.includes('_')) { |
|
|
|
return snake |
|
|
|
} |
|
|
|
return snake.replace(/_([a-z0-9])/g, (_, c) => c.toUpperCase()) |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* camelCase 转 snake_case。 |
|
|
|
* 入参:camel(驼峰字段名)。 |
|
|
|
* 返回值:snake_case 字段名;入参为空时返回空字符串。 |
|
|
|
*/ |
|
|
|
camelToSnake (camel) { |
|
|
|
if (!camel) { |
|
|
|
return '' |
|
|
|
} |
|
|
|
return camel.replace(/([A-Z])/g, '_$1').toLowerCase() |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
</script> |
|
|
|
|
|
|
|
@ -284,6 +270,7 @@ export default { |
|
|
|
.el-form-item { |
|
|
|
margin-bottom: 6px; |
|
|
|
} |
|
|
|
|
|
|
|
/deep/ .el-form-item__label { |
|
|
|
font-size: 14px; |
|
|
|
color: #606266; |
|
|
|
@ -312,5 +299,4 @@ export default { |
|
|
|
border-color: #409eff; |
|
|
|
outline: none; |
|
|
|
} |
|
|
|
|
|
|
|
</style> |