7 changed files with 594 additions and 85 deletions
-
9src/api/order/poOrder.js
-
172src/views/modules/order/com_poOrderAdvancedQuery.vue
-
144src/views/modules/order/poOrder.vue
-
89src/views/modules/order/poOrderAdvancedQueryConfig.js
-
72src/views/modules/order/poOrderAdvancedQueryHelper.js
-
57src/views/modules/order/poOrderStatusOptions.js
-
136src/views/modules/order/poOrder_search.vue
@ -0,0 +1,172 @@ |
|||
<template> |
|||
<!-- |
|||
【AI 维护】高级查询弹窗(poOrder / poOrder_search 共用) |
|||
样式参考:com_part_SpecParams.vue 属性填写表格 + .rq 紧凑行高 |
|||
--> |
|||
<el-dialog |
|||
title="高级查询" |
|||
:visible.sync="dialogVisible" |
|||
width="900px" |
|||
top="3vh" |
|||
:close-on-click-modal="false" |
|||
v-drag |
|||
append-to-body |
|||
custom-class="po-advanced-query-dialog" |
|||
@open="handleOpen" |
|||
@close="handleClose"> |
|||
<div class="rq po-advanced-query-body"> |
|||
<el-table |
|||
:height="tableHeight" |
|||
:data="advancedQueryData.rows" |
|||
border |
|||
style="width: 100%;"> |
|||
<el-table-column |
|||
prop="label" |
|||
label="字段" |
|||
header-align="center" |
|||
align="left" |
|||
min-width="200" |
|||
show-overflow-tooltip /> |
|||
<el-table-column |
|||
label="运算符" |
|||
header-align="center" |
|||
align="center" |
|||
width="120"> |
|||
<template slot-scope="{ row }"> |
|||
<el-select v-model="row.operator" style="width: 100%;"> |
|||
<el-option |
|||
v-for="op in getOperatorsByDataType(row.dataType)" |
|||
:key="op.value" |
|||
:label="op.label" |
|||
:value="op.value" /> |
|||
</el-select> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column |
|||
label="值" |
|||
header-align="center" |
|||
align="left" |
|||
min-width="220"> |
|||
<template slot-scope="{ row }"> |
|||
<el-input v-model="row.value" clearable placeholder="留空则不参与筛选" /> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
<el-footer style="height:25px;margin-top: 10px;text-align:center"> |
|||
<el-button @click="handleReset">重置</el-button> |
|||
<el-button @click="dialogVisible = false">取消</el-button> |
|||
<el-button type="primary" @click="handleQuery">查询</el-button> |
|||
</el-footer> |
|||
</div> |
|||
</el-dialog> |
|||
</template> |
|||
|
|||
<script> |
|||
import { getOperatorsByDataType } from './poOrderAdvancedQueryConfig.js' |
|||
import { resetAdvancedQueryData } from './poOrderAdvancedQueryHelper.js' |
|||
|
|||
export default { |
|||
name: 'PoOrderAdvancedQuery', |
|||
props: { |
|||
visible: { |
|||
type: Boolean, |
|||
default: false |
|||
}, |
|||
advancedQueryData: { |
|||
type: Object, |
|||
required: true |
|||
} |
|||
}, |
|||
data () { |
|||
return { |
|||
tableHeight: 400 |
|||
} |
|||
}, |
|||
computed: { |
|||
dialogVisible: { |
|||
get () { |
|||
return this.visible |
|||
}, |
|||
set (val) { |
|||
this.$emit('update:visible', val) |
|||
} |
|||
} |
|||
}, |
|||
beforeDestroy () { |
|||
window.removeEventListener('resize', this.calcTableHeight) |
|||
}, |
|||
methods: { |
|||
getOperatorsByDataType, |
|||
handleOpen () { |
|||
this.calcTableHeight() |
|||
window.addEventListener('resize', this.calcTableHeight) |
|||
}, |
|||
calcTableHeight () { |
|||
this.$nextTick(() => { |
|||
const vh = window.innerHeight || 800 |
|||
// 预留:dialog 顶距 + 标题栏 + 底栏按钮 + 内边距 |
|||
const reserved = 130 |
|||
this.tableHeight = Math.max(260, Math.min(Math.floor(vh * 0.72) - reserved, 560)) |
|||
}) |
|||
}, |
|||
handleReset () { |
|||
resetAdvancedQueryData(this.advancedQueryData) |
|||
this.$emit('reset') |
|||
}, |
|||
handleQuery () { |
|||
window.removeEventListener('resize', this.calcTableHeight) |
|||
this.dialogVisible = false |
|||
this.$emit('query') |
|||
}, |
|||
handleClose () { |
|||
window.removeEventListener('resize', this.calcTableHeight) |
|||
this.$emit('update:visible', false) |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped lang="scss"> |
|||
.po-advanced-query-body { |
|||
padding: 0 2px; |
|||
} |
|||
|
|||
.po-advanced-query-footer { |
|||
height: 40px; |
|||
margin-top: 12px; |
|||
padding: 0; |
|||
text-align: center; |
|||
line-height: 40px; |
|||
} |
|||
|
|||
/* 与产品清单属性填写 com_part_SpecParams 一致的紧凑表格行高 */ |
|||
.rq.po-advanced-query-body /deep/ .el-table .cell { |
|||
line-height: 28px; |
|||
font-size: 12px; |
|||
height: 28px; |
|||
padding: 2px 4px; |
|||
} |
|||
|
|||
.rq.po-advanced-query-body /deep/ .el-input__inner { |
|||
height: 26px; |
|||
line-height: 26px; |
|||
font-size: 12px; |
|||
} |
|||
|
|||
.rq.po-advanced-query-body /deep/ .el-input__icon { |
|||
line-height: 26px; |
|||
} |
|||
|
|||
.rq.po-advanced-query-body /deep/ .el-table td, |
|||
.rq.po-advanced-query-body /deep/ .el-table th { |
|||
padding: 2px 0; |
|||
} |
|||
</style> |
|||
|
|||
<style lang="scss"> |
|||
/* 弹窗 body 不出现双滚动条,表格区域单独滚动 */ |
|||
.po-advanced-query-dialog .el-dialog__body { |
|||
padding: 10px 16px 12px; |
|||
overflow: hidden; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,89 @@ |
|||
/** |
|||
* ============================================================================= |
|||
* 【高级查询静态配置 - AI 维护说明】 |
|||
* |
|||
* ⚠️ 修改以下任一内容时,必须同步重新生成 PO_ADVANCED_QUERY_FIELD_CONFIG: |
|||
* 1. poOrder_search.vue → columnList(columnProp / columnLabel / 顺序) |
|||
* 2. PODetailMapper.xml → myPage / myPageAdvanced 的 SELECT 字段及别名 |
|||
* 3. 运算符规则(ADVANCED_QUERY_OPERATOR_OPTIONS / getOperatorsByDataType) |
|||
* |
|||
* 生成规则: |
|||
* - field = columnProp(与 columnList 完全一致) |
|||
* - label = columnLabel |
|||
* - sortOrder = columnList 顺序(从 1 开始) |
|||
* - sqlColumn = myPage SELECT 中可用于 WHERE 的完整 SQL 表达式(含表别名、函数) |
|||
* 例:poh.c_enquiry、ss.supplier_name、COALESCE(pod.inspect_approve_qty, 0) |
|||
* - dataType = string | number | date |
|||
* |
|||
* 同步修改后端白名单(必须与 sqlColumn 完全一致): |
|||
* PoDetailAdvancedQuerySupport.java → FIELD_SQL_MAP |
|||
* |
|||
* 最后更新:2026-06-25 |
|||
* 对照:poOrder_search.vue columnList + PODetailMapper.xml#myPage SELECT |
|||
* ============================================================================= |
|||
*/ |
|||
|
|||
/** 运算符:前端 value → 后端 operator(条件之间固定 AND) */ |
|||
export const ADVANCED_QUERY_OPERATOR_OPTIONS = [ |
|||
{ label: '等于', value: 'eq' }, |
|||
{ label: '包含', value: 'contains' }, |
|||
{ label: '大于', value: 'gt' }, |
|||
{ label: '小于', value: 'lt' }, |
|||
{ label: '大于等于', value: 'gte' }, |
|||
{ label: '小于等于', value: 'lte' } |
|||
] |
|||
|
|||
/** 按 dataType 过滤可用运算符(number/date 不提供「包含」) */ |
|||
export function getOperatorsByDataType (dataType) { |
|||
if (dataType === 'string') { |
|||
return ADVANCED_QUERY_OPERATOR_OPTIONS |
|||
} |
|||
return ADVANCED_QUERY_OPERATOR_OPTIONS.filter(o => o.value !== 'contains') |
|||
} |
|||
|
|||
/** |
|||
* 高级查询字段配置 — 顺序与 poOrder_search.vue / poOrder.vue 表格 columnList 一致 |
|||
* sqlColumn:多表关联完整表达式,计算列写函数表达式 |
|||
*/ |
|||
export const PO_ADVANCED_QUERY_FIELD_CONFIG = [ |
|||
{ sortOrder: 1, field: 'customer', label: 'Customer', sqlColumn: 'poh.c_customer', dataType: 'string' }, |
|||
{ sortOrder: 2, field: 'buyer', label: 'Buyer', sqlColumn: 'poh.buyer', dataType: 'string' }, |
|||
{ sortOrder: 3, field: 'supplierName', label: 'Vendor Name', sqlColumn: 'ss.supplier_name', dataType: 'string' }, |
|||
{ sortOrder: 4, field: 'shortOffice', label: 'V_Short_Office', sqlColumn: 'ss.c_short_office', dataType: 'string' }, |
|||
{ sortOrder: 5, field: 'shortNpc', label: 'V_Short_NPC', sqlColumn: 'ss.c_short_npc', dataType: 'string' }, |
|||
{ sortOrder: 6, field: 'poNo', label: 'PO_No', sqlColumn: 'poh.po_no', dataType: 'string' }, |
|||
{ sortOrder: 7, field: 'partNo', label: 'Item', sqlColumn: 'pod.part_no', dataType: 'string' }, |
|||
{ sortOrder: 8, field: 'category', label: 'Category', sqlColumn: 'p.category', dataType: 'string' }, |
|||
{ sortOrder: 9, field: 'sku', label: 'SKU', sqlColumn: 'p.sku', dataType: 'string' }, |
|||
{ sortOrder: 10, field: 'price', label: 'Unit Price', sqlColumn: 'pod.price', dataType: 'number' }, |
|||
{ sortOrder: 11, field: 'qty', label: 'PO Qty', sqlColumn: 'pod.qty', dataType: 'number' }, |
|||
{ sortOrder: 12, field: 'value', label: 'PO Value', sqlColumn: 'pod.c_value', dataType: 'number' }, |
|||
{ sortOrder: 13, field: 'shipQty', label: 'A-Qty', sqlColumn: 'pod.c_ship_qty', dataType: 'number' }, |
|||
{ sortOrder: 14, field: 'sumPrice', label: 'A-Value', sqlColumn: 'pod.c_sum_price', dataType: 'number' }, |
|||
{ sortOrder: 15, field: 'departure', label: 'Departure Port', sqlColumn: 'poh.c_departure', dataType: 'string' }, |
|||
{ sortOrder: 16, field: 'destination', label: 'Destination Port', sqlColumn: 'poh.c_destination', dataType: 'string' }, |
|||
{ sortOrder: 17, field: 'shipVia', label: 'Method', sqlColumn: 'poh.ship_via', dataType: 'string' }, |
|||
{ sortOrder: 18, field: 'orderDate', label: 'Buyer Release Date', sqlColumn: 'poh.order_date', dataType: 'date' }, |
|||
{ sortOrder: 19, field: 'enquiry', label: 'Planner Release Date', sqlColumn: 'poh.c_enquiry', dataType: 'date' }, |
|||
{ sortOrder: 20, field: 'status', label: 'Status', sqlColumn: 'pod.status', dataType: 'string' }, |
|||
{ sortOrder: 21, field: 'confirmed', label: 'PI Confirmed Date', sqlColumn: 'poh.c_confirmed', dataType: 'date' }, |
|||
{ sortOrder: 22, field: 'wantReceiveDate', label: 'Original Arrival Date', sqlColumn: 'pod.want_receive_date', dataType: 'date' }, |
|||
{ sortOrder: 23, field: 'narrival', label: 'Final Confirmed Arrival Date', sqlColumn: 'pod.c_narrival', dataType: 'date' }, |
|||
{ sortOrder: 24, field: 'crd', label: 'PI Confirmed CRD', sqlColumn: 'pod.c_crd', dataType: 'date' }, |
|||
{ sortOrder: 25, field: 'inspection', label: 'Inspection Date', sqlColumn: 'pod.c_inspection', dataType: 'date' }, |
|||
{ sortOrder: 26, field: 'realCrd', label: 'Actual Completion Date', sqlColumn: 'pod.c_real_crd', dataType: 'date' }, |
|||
{ sortOrder: 27, field: 'eta', label: 'ETA', sqlColumn: 'pod.c_eta', dataType: 'date' }, |
|||
{ sortOrder: 28, field: 'flexid', label: 'Flexport Id', sqlColumn: 'pod.c_flexid', dataType: 'string' }, |
|||
{ sortOrder: 29, field: 'obd', label: 'On Board Date', sqlColumn: 'pod.c_obd', dataType: 'date' }, |
|||
{ sortOrder: 30, field: 'docSent', label: 'Doc Sent', sqlColumn: 'pod.c_doc_sent', dataType: 'string' }, |
|||
{ sortOrder: 31, field: 'telexRelease', label: 'Telex Release', sqlColumn: 'pod.c_telex_release', dataType: 'string' }, |
|||
{ sortOrder: 32, field: 'advancePayment', label: 'Advance Payment', sqlColumn: 'pod.c_advance_payment', dataType: 'string' }, |
|||
{ sortOrder: 33, field: 'advancePaid', label: 'Advanc Paid', sqlColumn: 'pod.c_advance_paid', dataType: 'string' }, |
|||
{ sortOrder: 34, field: 'createTime', label: 'Create Date', sqlColumn: 'pod.create_time', dataType: 'date' }, |
|||
{ sortOrder: 35, field: 'planner', label: 'M-Planner', sqlColumn: 'pod.c_planner', dataType: 'string' }, |
|||
{ sortOrder: 36, field: 'edit', label: 'Edit Date', sqlColumn: 'pod.c_edit', dataType: 'string' }, |
|||
{ sortOrder: 37, field: 'endPlanner', label: 'Last Editor', sqlColumn: 'pod.c_end_planner', dataType: 'string' }, |
|||
{ sortOrder: 38, field: 'remarkDelay', label: 'Remark Delay', sqlColumn: 'pod.c_remark_delay', dataType: 'string' }, |
|||
{ sortOrder: 39, field: 'remark', label: 'Comments', sqlColumn: 'pod.remark', dataType: 'string' }, |
|||
{ sortOrder: 40, field: 'inspectionCompletedQty', label: 'Inspection Completed Qty', sqlColumn: 'COALESCE(pod.inspect_approve_qty, 0)', dataType: 'number' } |
|||
] |
|||
@ -0,0 +1,72 @@ |
|||
import { PO_ADVANCED_QUERY_FIELD_CONFIG } from './poOrderAdvancedQueryConfig.js' |
|||
|
|||
/** |
|||
* 【AI 维护】高级查询状态对象工厂 |
|||
* advancedQueryData 为页面级唯一对象,查询/导出均读取此对象 |
|||
*/ |
|||
export function createAdvancedQueryData () { |
|||
return { |
|||
rows: PO_ADVANCED_QUERY_FIELD_CONFIG.map(item => ({ |
|||
field: item.field, |
|||
label: item.label, |
|||
dataType: item.dataType, |
|||
sqlColumn: item.sqlColumn, |
|||
operator: item.dataType === 'string' ? 'contains' : 'eq', |
|||
value: '' |
|||
})) |
|||
} |
|||
} |
|||
|
|||
/** 重置高级查询(仅弹窗内「重置」按钮调用,外部重置不清空) */ |
|||
export function resetAdvancedQueryData (advancedQueryData) { |
|||
advancedQueryData.rows = createAdvancedQueryData().rows |
|||
} |
|||
|
|||
/** 收集有效高级条件(AND,空 value 跳过) */ |
|||
export function getActiveAdvancedConditions (advancedQueryData) { |
|||
if (!advancedQueryData || !advancedQueryData.rows) { |
|||
return [] |
|||
} |
|||
return advancedQueryData.rows |
|||
.filter(r => r.value != null && String(r.value).trim() !== '') |
|||
.map(r => ({ |
|||
field: r.field, |
|||
operator: r.operator, |
|||
value: String(r.value).trim() |
|||
})) |
|||
} |
|||
|
|||
export function hasActiveAdvancedConditions (advancedQueryData) { |
|||
return getActiveAdvancedConditions(advancedQueryData).length > 0 |
|||
} |
|||
|
|||
/** |
|||
* 组装查询/导出参数 |
|||
* - 有高级条件时走 advancedPage,并附带 searchData 基础字段 |
|||
* - 无高级条件时走原 page |
|||
*/ |
|||
export function buildPoOrderQueryParams ({ searchData, advancedQueryData, page, limit }) { |
|||
const conditions = getActiveAdvancedConditions(advancedQueryData) |
|||
const baseParams = { |
|||
site: searchData.site, |
|||
page, |
|||
limit, |
|||
customer: searchData.customer || '', |
|||
buyer: searchData.buyer || '', |
|||
poNo: searchData.poNo || '', |
|||
sku: searchData.sku || '', |
|||
status: searchData.status || '', |
|||
supplierName: searchData.supplierName || '', |
|||
partNo: searchData.partNo || '' |
|||
} |
|||
if (conditions.length > 0) { |
|||
return { |
|||
params: { ...baseParams, conditions }, |
|||
useAdvanced: true |
|||
} |
|||
} |
|||
return { |
|||
params: { ...searchData, page, limit }, |
|||
useAdvanced: false |
|||
} |
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
/** |
|||
* PO 订单行状态选项(静态数据,查询下拉用,不查库) |
|||
*/ |
|||
export const PO_ORDER_STATUS_OPTIONS = [ |
|||
{ label: '已下达', value: '已下达' }, |
|||
{ label: '待验货', value: '待验货' }, |
|||
{ label: '验货中', value: '验货中' }, |
|||
{ label: '验货完成', value: '验货完成' }, |
|||
{ label: '已取消', value: '已取消' }, |
|||
{ label: '已关闭', value: '已关闭' } |
|||
] |
|||
|
|||
/** 是否可「关闭」 */ |
|||
export function canClosePoOrder (status) { |
|||
return !!status && status !== '已关闭' && status !== '已取消' |
|||
} |
|||
|
|||
/** 是否可「打开」(仅已关闭) */ |
|||
export function canOpenPoOrder (status) { |
|||
return status === '已关闭' |
|||
} |
|||
|
|||
/** |
|||
* 打开时根据验货数量推算恢复状态(与后端 PoDetailStatusSupport 规则一致) |
|||
*/ |
|||
export function resolvePoOrderStatusByInspectQty (row) { |
|||
const toInspect = Number(row.toInspectQty != null ? row.toInspectQty : 0) |
|||
const approve = Number(row.inspectApproveQty != null ? row.inspectApproveQty : (row.inspectionCompletedQty != null ? row.inspectionCompletedQty : 0)) |
|||
const qty = Number(row.qty != null ? row.qty : 0) |
|||
|
|||
if (toInspect === 0) { |
|||
return '已下达' |
|||
} |
|||
if (toInspect > 0 && approve === 0) { |
|||
return '待验货' |
|||
} |
|||
if (qty > 0 && approve === qty) { |
|||
return '验货完成' |
|||
} |
|||
if (approve > 0 && (qty === 0 || approve < qty)) { |
|||
return '验货中' |
|||
} |
|||
if (approve > 0 && qty > 0 && approve >= qty) { |
|||
return '验货完成' |
|||
} |
|||
return '待验货' |
|||
} |
|||
|
|||
/** 关闭/打开接口入参(id 为空时用 orderNo+site+itemNo 定位) */ |
|||
export function buildPoDetailActionParams (row, site) { |
|||
return { |
|||
id: row.id != null && row.id !== '' ? row.id : undefined, |
|||
orderNo: row.orderNo, |
|||
site: row.site || site, |
|||
itemNo: row.itemNo |
|||
} |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue