2 Commits

  1. 21
      src/views/modules/order/com_poOrderAdvancedQuery.vue
  2. 5
      src/views/modules/order/com_poOrderDetail.vue
  3. 16
      src/views/modules/order/poOrder.vue
  4. 16
      src/views/modules/order/poOrderAdvancedQueryHelper.js
  5. 71
      src/views/modules/order/poOrderStatusMappingMixin.js
  6. 12
      src/views/modules/order/poOrder_search.vue
  7. 12
      src/views/modules/order/poOrder_search_en.vue
  8. 154
      src/views/modules/quickQuery/快捷查询模块说明.md

21
src/views/modules/order/com_poOrderAdvancedQuery.vue

@ -49,7 +49,17 @@
min-width="220">
<template slot-scope="{ row }">
<el-select
v-if="row.dataType === 'select'"
v-if="row.field === 'status' && row.operator === 'eq'"
v-model="row.value"
clearable
filterable
placeholder="请选择"
style="width: 100%;"
@visible-change="visible => visible && $emit('refresh-status-options')">
<el-option v-for="opt in statusOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
</el-select>
<el-select
v-else-if="row.dataType === 'select'"
v-model="row.value"
clearable
placeholder="请选择"
@ -81,6 +91,11 @@ import { PO_ORDER_TYPE_FILTER_OPTIONS } from './poOrderTypeOptions.js'
export default {
name: 'PoOrderAdvancedQuery',
props: {
// rqrq - 与顶部下拉共享去重后的英文状态。
statusOptions: {
type: Array,
default: () => []
},
visible: {
type: Boolean,
default: false
@ -120,6 +135,10 @@ export default {
.filter(Boolean)
},
getOperatorOptions (row) {
// rqrq - 状态没有大小比较语义,只允许精确选择或英文包含查询。
if (row && row.field === 'status') {
return this.getOperatorsByDataType('string').filter(op => ['eq', 'contains'].includes(op.value))
}
const baseOptions = this.getOperatorsByDataType(row && row.dataType)
const operatorSet = this.normalizeOperatorSet(row && row.operatorSet)
if (!operatorSet.length) {

5
src/views/modules/order/com_poOrderDetail.vue

@ -309,6 +309,11 @@ export default {
const raw = this.getFieldRawValue(field)
const code = this.getFieldCode(field)
const prop = this.getFieldProp(field)
// rqrq - 详情仅展示英文,保留 detailData.status 原值供业务使用。
if (code === 'status' || prop === 'status') {
const row = this.detailData || {}
return row.statusDisplay != null ? row.statusDisplay : raw
}
if (raw === null || raw === undefined) {
return ''
}

16
src/views/modules/order/poOrder.vue

@ -29,7 +29,7 @@
<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-select v-model="searchData.status" placeholder="请选择" style="width:180px" clearable filterable @visible-change="onPoStatusDropdownVisible">
<el-option label="全部" value=""></el-option>
<el-option
v-for="item in poOrderStatusOptions"
@ -403,6 +403,8 @@
<po-order-advanced-query
:visible.sync="advancedQueryVisible"
:advanced-query-data="advancedQueryData"
:status-options="poOrderStatusOptions"
@refresh-status-options="loadPoStatusMappings"
@query="handleSearch" />
<!--
@ -713,7 +715,6 @@ import {
buildExtValuesFromRow
} from './poOrderFieldSchemaHelper.js'
import {
PO_ORDER_STATUS_OPTIONS,
canIssuePoOrder,
canShipPoOrder,
canReceivePoOrder,
@ -724,6 +725,7 @@ import {
buildPoDetailActionParams
} from './poOrderStatusOptions.js'
import { PO_ORDER_TYPE_OPTIONS, PO_ORDER_TYPE_FILTER_OPTIONS, formatPoOrderTypeLabel } from './poOrderTypeOptions.js'
import poOrderStatusMappingMixin from './poOrderStatusMappingMixin.js'
import { getPartList, searchPartInfo } from '@/api/part/partInfo.js'
import excel from '@/utils/excel-util.js'
import { checkSrmSupplierList } from '@/api/srm/srmSupplier.js'
@ -753,7 +755,7 @@ const ADD_SELECT_OPTIONS_FIELD_CODE_MAP = {
order_type: 'poOrderTypeOptions',
customer: 'poCustomerOptions',
buyer: 'poBuyerOptions',
status: 'poOrderStatusOptions',
status: 'poOrderStatusValueOptions', // rqrq - 新增状态显示英文,值保留中文
doc_sent: 'yesNoOptions',
telex_release: 'yesNoOptions',
advance_paid: 'yesNoOptions'
@ -862,6 +864,7 @@ const ADD_SELECT_OPTIONS_FIELD_CODE_MAP = {
* - formatCodeOptionLabel, buildDeparturePortConSql, buildDestinationPortConSql
*/
export default {
mixins: [poOrderStatusMappingMixin], // rqrq - 列表、下拉和导出共用状态映射
name: 'PoOrder',
components: {
ChooselistEam,
@ -973,7 +976,7 @@ export default {
currentRow: null,
// 各列下拉:select 列通过 selectOptionsKey 指向此处;也可在列定义里写 selectOptions 数组 - rqrq
columnSelectOptions: {
poStatusOptions: PO_ORDER_STATUS_OPTIONS,
poStatusOptions: [], // rqrq - 从全局状态对应表读取
poPartNoOptions: [],
poDepartureOptions: [],
poCustomerOptions: [],
@ -1009,7 +1012,6 @@ export default {
baseColumnList: null,
/** 高级查询条件对象(查询均读取;仅弹窗内「重置」可清空) */
advancedQueryData: createAdvancedQueryData(),
poOrderStatusOptions: PO_ORDER_STATUS_OPTIONS,
poOrderTypeOptions: PO_ORDER_TYPE_FILTER_OPTIONS,
height: 400,
pageIndex: 1,
@ -2202,6 +2204,8 @@ export default {
},
formatColumnCell (row, item) {
// rqrq - 仅改变显示,原始 row.status 继续供按钮和保存逻辑使用。
if (item.columnProp === 'status') return this.formatPoStatus(row)
if (item.columnProp === 'orderType') {
return formatPoOrderTypeLabel(row.orderType)
}
@ -3689,7 +3693,7 @@ export default {
try {
excel.exportTable({
url: useAdvanced ? 'pODetail/advancedPage' : 'pODetail/page',
columnMapping: this.visibleColumns,
columnMapping: this.poStatusExportColumns(this.visibleColumns), // rqrq - 导出英文状态
mergeSetting: [],
params,
fileName: exportName + '.xlsx',

16
src/views/modules/order/poOrderAdvancedQueryHelper.js

@ -38,8 +38,9 @@ export function createAdvancedQueryDataFromSchema (schemaFields) {
field: item.field,
label: item.label,
dataType: item.dataType,
operatorSet: normalizeOperatorSet(item.operatorSet),
operator: resolveDefaultOperator(item.dataType, item.operatorSet),
// rqrq - 状态等于时使用英文下拉,包含时按英文名称匹配。
operatorSet: item.field === 'status' ? ['eq', 'contains'] : normalizeOperatorSet(item.operatorSet),
operator: item.field === 'status' ? 'eq' : resolveDefaultOperator(item.dataType, item.operatorSet),
value: ''
}))
}
@ -55,8 +56,8 @@ export function createAdvancedQueryData () {
label: item.label,
dataType: item.dataType,
sqlColumn: item.sqlColumn,
operatorSet: [],
operator: resolveDefaultOperator(item.dataType, []),
operatorSet: item.field === 'status' ? ['eq', 'contains'] : [], // rqrq
operator: item.field === 'status' ? 'eq' : resolveDefaultOperator(item.dataType, []),
value: ''
}))
}
@ -74,7 +75,7 @@ export function resetAdvancedQueryData (advancedQueryData) {
advancedQueryData.rows = advancedQueryData.rows.map(row => ({
...row,
operatorSet: normalizeOperatorSet(row && row.operatorSet),
operator: resolveDefaultOperator(row && row.dataType, row && row.operatorSet),
operator: row && row.field === 'status' ? 'eq' : resolveDefaultOperator(row && row.dataType, row && row.operatorSet), // rqrq
value: ''
}))
}
@ -112,7 +113,8 @@ export function buildPoOrderQueryParams ({ searchData, advancedQueryData, page,
buyer: searchData.buyer || '',
poNo: searchData.poNo || '',
sku: searchData.sku || '',
status: searchData.status || '',
// rqrq - 英文筛选独立传参,禁止把显示值送入原始业务 status。
statusDisplayFilter: searchData.status || '',
orderType: searchData.orderType || '',
supplierNo: searchData.supplierNo || '',
supplierName: searchData.supplierName || '',
@ -125,7 +127,7 @@ export function buildPoOrderQueryParams ({ searchData, advancedQueryData, page,
}
}
return {
params: { ...searchData, page, limit },
params: { ...searchData, status: '', statusDisplayFilter: searchData.status || '', page, limit },
useAdvanced: false
}
}

71
src/views/modules/order/poOrderStatusMappingMixin.js

@ -0,0 +1,71 @@
import { createAPI } from '@/utils/httpRequest.js'
/** rqrq - 订单页面共用全局映射;查询值为去重英文,业务值仍为原始状态。 */
export default {
data () {
return {
poStatusMappings: [],
poStatusMappingsLoading: false
}
},
computed: {
poOrderStatusOptions () {
const seen = new Set()
return this.poStatusMappings.reduce((options, row) => {
const label = row.displayStatus
const key = label.toLowerCase()
if (!seen.has(key)) {
seen.add(key)
options.push({ label, value: label })
}
return options
}, [])
},
// rqrq - 编辑/新增中的状态下拉显示英文,但必须提交原始业务值。
poOrderStatusValueOptions () {
return this.poStatusMappings.map(row => ({ label: row.displayStatus, value: row.sourceStatus }))
}
},
created () {
this.loadPoStatusMappings()
},
activated () {
this.loadPoStatusMappings()
},
methods: {
loadPoStatusMappings () {
// rqrq - 首次 created/activated 可能同时触发,避免重复请求;重新打开下拉会刷新配置。
if (this.poStatusMappingsLoading) return
this.poStatusMappingsLoading = true
return createAPI('/poStatusMapping/options', 'post', {}).then(({ data }) => {
if (!data || data.code !== 0) {
throw new Error((data && data.msg) || '加载订单状态映射失败')
}
this.poStatusMappings = (data.rows || []).filter(row => row.sourceStatus && row.displayStatus)
if (this.columnSelectOptions) {
this.$set(this.columnSelectOptions, 'poStatusOptions', this.poOrderStatusValueOptions)
}
}).catch(error => {
this.$message.error(error.message || '加载订单状态映射失败')
}).finally(() => {
this.poStatusMappingsLoading = false
})
},
onPoStatusDropdownVisible (visible) {
if (visible) this.loadPoStatusMappings()
},
formatPoStatus (row) {
if (!row) return ''
// rqrq - 查询结果使用后端同一次查询的映射;新增草稿等本地行使用已加载的字典。
if (row.statusDisplay != null) return row.statusDisplay
const mapping = this.poStatusMappings.find(item => item.sourceStatus === row.status)
return mapping ? mapping.displayStatus : (row.status || '')
},
poStatusExportColumns (columns) {
// rqrq - 导出工具直接读取分页行,单独替换导出列,保持屏幕列字段和业务 status 不变。
return columns.map(column => column.columnProp === 'status'
? { ...column, columnProp: 'statusDisplay' }
: column)
}
}
}

12
src/views/modules/order/poOrder_search.vue

@ -30,7 +30,7 @@
<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-select v-model="searchData.status" placeholder="请选择" style="width:180px" clearable filterable @visible-change="onPoStatusDropdownVisible">
<el-option label="全部" value=""></el-option>
<el-option
v-for="item in poOrderStatusOptions"
@ -113,6 +113,8 @@
<po-order-advanced-query
:visible.sync="advancedQueryVisible"
:advanced-query-data="advancedQueryData"
:status-options="poOrderStatusOptions"
@refresh-status-options="loadPoStatusMappings"
@query="handleSearch" />
<ChooselistEam ref="eamList" @getBaseData="getEamData" />
@ -139,11 +141,12 @@ import PoOrderDetail from './com_poOrderDetail.vue'
import PoOrderAdvancedQuery from './com_poOrderAdvancedQuery.vue'
import ChooselistEam from '@/views/modules/common/Chooselist_eam.vue'
import ComPoOrderCustomerPicker from './com_poOrder_CustomerPicker.vue'
import { PO_ORDER_STATUS_OPTIONS } from './poOrderStatusOptions.js'
import poOrderStatusMappingMixin from './poOrderStatusMappingMixin.js'
import { PO_ORDER_TYPE_OPTIONS, formatPoOrderTypeLabel } from './poOrderTypeOptions.js'
import { searchPartInfo } from '@/api/part/partInfo.js'
export default {
mixins: [poOrderStatusMappingMixin], // rqrq - 列表、下拉和导出共用状态映射
components: {
LogisticsPoList,
InspectionDetailList,
@ -172,7 +175,6 @@ export default {
advancedQueryData: createAdvancedQueryData(),
fieldSchemaFields: [],
baseColumnList: null,
poOrderStatusOptions: PO_ORDER_STATUS_OPTIONS,
searchData: {
site: this.$store.state.user.site,
customer: '',
@ -589,6 +591,8 @@ export default {
})
},
formatColumnCell (row, item) {
// rqrq - 仅改变显示,原始 row.status 继续供按钮和保存逻辑使用。
if (item.columnProp === 'status') return this.formatPoStatus(row)
if (item.columnProp === 'orderType') {
return formatPoOrderTypeLabel(row.orderType)
}
@ -785,7 +789,7 @@ export default {
try {
excel.exportTable({
url: useAdvanced ? 'pODetail/advancedPage' : 'pODetail/page',
columnMapping: this.visibleColumns,
columnMapping: this.poStatusExportColumns(this.visibleColumns), // rqrq - 导出英文状态
mergeSetting: [],
params,
fileName: exportName + '.xlsx',

12
src/views/modules/order/poOrder_search_en.vue

@ -30,7 +30,7 @@
<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="Please select" style="width:100px" clearable>
<el-select v-model="searchData.status" placeholder="Please select" style="width:180px" clearable filterable @visible-change="onPoStatusDropdownVisible">
<el-option label="All" value=""></el-option>
<el-option
v-for="item in poOrderStatusOptions"
@ -114,6 +114,8 @@
<po-order-advanced-query
:visible.sync="advancedQueryVisible"
:advanced-query-data="advancedQueryData"
:status-options="poOrderStatusOptions"
@refresh-status-options="loadPoStatusMappings"
@query="handleSearch" />
<ChooselistEam ref="eamList" @getBaseData="getEamData" />
@ -140,11 +142,12 @@ import PoOrderDetail from './com_poOrderDetail.vue'
import PoOrderAdvancedQuery from './com_poOrderAdvancedQuery.vue'
import ChooselistEam from '@/views/modules/common/Chooselist_eam.vue'
import ComPoOrderCustomerPicker from './com_poOrder_CustomerPicker.vue'
import { PO_ORDER_STATUS_OPTIONS } from './poOrderStatusOptions.js'
import poOrderStatusMappingMixin from './poOrderStatusMappingMixin.js'
import { PO_ORDER_TYPE_OPTIONS, formatPoOrderTypeLabel } from './poOrderTypeOptions.js'
import { searchPartInfo } from '@/api/part/partInfo.js'
export default {
mixins: [poOrderStatusMappingMixin], // rqrq - 列表、下拉和导出共用状态映射
components: {
LogisticsPoList,
InspectionDetailList,
@ -173,7 +176,6 @@ export default {
advancedQueryData: createAdvancedQueryData(),
fieldSchemaFields: [],
baseColumnList: null,
poOrderStatusOptions: PO_ORDER_STATUS_OPTIONS,
searchData: {
site: this.$store.state.user.site,
customer: '',
@ -590,6 +592,8 @@ export default {
})
},
formatColumnCell (row, item) {
// rqrq - 仅改变显示,原始 row.status 继续供按钮和保存逻辑使用。
if (item.columnProp === 'status') return this.formatPoStatus(row)
if (item.columnProp === 'orderType') {
return formatPoOrderTypeLabel(row.orderType)
}
@ -786,7 +790,7 @@ export default {
try {
excel.exportTable({
url: useAdvanced ? 'pODetail/advancedPage' : 'pODetail/page',
columnMapping: this.visibleColumns,
columnMapping: this.poStatusExportColumns(this.visibleColumns), // rqrq - 导出英文状态
mergeSetting: [],
params,
fileName: exportName + '.xlsx',

154
src/views/modules/quickQuery/快捷查询模块说明.md

@ -0,0 +1,154 @@
# 快捷查询模块说明(前端组件 + 后端接口 + 数据库)- rqrq
## 1. 功能概述
- 在库中维护「某业务表格(`table_id`)」下**允许参与快捷查询**的字段:字段名、中文/英文描述、数据类型。
- 前端公共组件 **`QuickQueryFilter`**:传入 **`tableId`**,打开弹窗后从后端拉取字段定义;用户「新增」多行条件,按类型展示**运算符 + 输入框/日期选择器**。
- 用户点击「应用」后,组件将条件拼成 **JSON 字符串**,调用业务页传入的 **`applyHandler(jsonStr, parsedArray)`**(并 `$emit('apply', ...)`),由业务方决定:前端过滤、或把字符串交给后端解析执行 SQL。
## 2. 数据库表 SQL(SQL Server)
完整脚本见:`krd-srm-java/scripts/srm_quick_query_field.sql`。
### 2.1 表结构摘要
| 列名 | 说明 |
|------|------|
| `id` | 主键,自增 |
| `table_id` | 业务表格编码,与前端列表 `tableId`、如 `812003Table1` 一致 |
| `field_name` | 行数据上的属性名(与接口/实体字段一致,如 `status`、`unitPrice`) |
| `field_label` | 界面展示描述(弹窗第一列下拉显示) |
| `data_type` | `NUMBER` / `TEXT` / `DATETIME`(大写) |
| `sort_order` | 排序 |
| `site` | 工厂;空串 `''` 表示全局配置;与具体工厂并存时可加行 |
| `status` | `ACTIVE` / `INACTIVE` |
| `remark` | 备注 |
唯一约束:`(table_id, field_name, site)`。
### 2.2 数据类型与前端行为
| data_type | 条件运算符 | 第三列控件 |
|-----------|------------|------------|
| `NUMBER` | 大于(GT)、小于(LT)、等于(EQ) | `el-input type="number"` |
| `TEXT` | 等于(EQ)、包含(CONTAINS) | 文本 `el-input` |
| `DATETIME` | 大于(GT)、小于(LT) | `el-date-picker`,`value-format="yyyy-MM-dd"` |
### 2.3 应用时生成的 JSON 格式
为**对象数组**的 JSON 字符串,每项:
```json
{
"fieldName": "status",
"fieldLabel": "Status",
"dataType": "TEXT",
"operator": "CONTAINS",
"value": "Open"
}
```
`operator` 取值:`GT` | `LT` | `EQ` | `CONTAINS`。
业务后端若要根据该 JSON 拼 SQL,请使用**白名单**仅允许 `fieldName` 落在配置表中出现过的字段,避免注入。
## 3. 后端模块(Java)
目录:`krd-srm-java/src/main/java/com/xujie/modules/quickQuery/`
| 类型 | 路径 |
|------|------|
| Controller | `controller/QuickQueryFieldController.java` |
| Service | `service/QuickQueryFieldService.java`、`service/impl/QuickQueryFieldServiceImpl.java` |
| Entity | `entity/QuickQueryField.java` |
| Data | `data/QuickQueryFieldData.java` |
| Mapper | `mapper/QuickQueryFieldMapper.java` |
| XML | `resources/mapper/quickQuery/QuickQueryFieldMapper.xml` |
### 3.1 接口列表
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | `/quickQuery/field/listByTableId` | 入参 JSON:`{ "tableId": "812003Table1", "site": "" }`,返回 `rows` |
| POST | `/quickQuery/field/save` | 维护字段配置(新增 `id` 空,修改带 `id`) |
| POST | `/quickQuery/field/delete` | `{ "id": 1 }` |
均需登录(OAuth2),与其它业务接口一致。
### 3.2 listByTableId 站点规则
返回满足:`status = ACTIVE` 且 `(site = '' OR site = 请求.site)`,即**全局配置**与**当前工厂配置**同时生效。
## 4. 前端 API
文件:`krd-srm-vue/src/api/quickQuery/quickQueryField.js`
- `listQuickQueryFieldsByTableId(data)`
- `saveQuickQueryField(data)`
- `deleteQuickQueryField(data)`
## 5. 职责拆分(解构)
| 部分 | 作用 |
|------|------|
| **`QuickQueryFilter.vue`** | UI:按钮 + 弹窗 + 调 `/quickQuery/field/listByTableId` + 拼 JSON + 调用 `applyHandler` / `emit('apply')`。不关心业务数据怎么查。 |
| **`utils/quickQueryMatch.js`** | 可选:前端本地过滤时,用 **`parseQuickQueryJson`** 解析字符串、**`matchQuickQueryRow(row, conditions)`** 判断一行是否满足全部条件(AND)。与页面解耦,其它列表页可直接引用。 |
| **业务页** | 维护 `quickQueryConditions`(可选);在 `applyHandler` 里写入解析结果并 `getDataList`;在 `filter` 里调用 `matchQuickQueryRow`。若改走后端查询,可只把 JSON 塞进接口参数。`poOrder.vue` 当前未内置快捷查询,需时按 6.4 自行挂载。 |
## 6. 公共组件用法
文件:`krd-srm-vue/src/views/modules/quickQuery/components/QuickQueryFilter.vue`
### 6.1 Props
| 属性 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `tableId` | String | 是 | 与库表 `table_id` 一致 |
| `site` | String | 否 | 当前工厂,默认 `''` |
| `applyHandler` | Function | 是 | `(conditionJsonString, parsedArray) => void` |
| `buttonText` | String | 否 | 默认 `快捷查询` |
| `dialogTitle` | String | 否 | 默认 `快捷查询` |
### 6.2 事件
- `apply`:`(conditionJsonString, parsedArray)`,与 `applyHandler` 同时触发。
### 6.3 引用示例
```vue
<template>
<quick-query-filter
table-id="812003Table1"
:site="$store.state.user.site || ''"
:apply-handler="handleQuickQuery"
/>
</template>
<script>
import QuickQueryFilter from '@/views/modules/quickQuery/components/QuickQueryFilter.vue'
export default {
components: { QuickQueryFilter },
methods: {
handleQuickQuery (jsonStr, conditions) {
// 示例1:前端过滤
this.advancedFilter = JSON.parse(jsonStr)
this.loadList()
// 示例2:交给后端
// this.searchForm.quickQueryJson = jsonStr
// this.loadListFromApi()
}
}
}
</script>
```
### 6.4 在列表页集成示例(如 `poOrder.vue`)
在表单工具栏引入 `QuickQueryFilter`,`tableId` 与库表 `srm_quick_query_field.table_id` 一致;`applyHandler` 内用 `parseQuickQueryJson` 得到 `conditions` 写入 `quickQueryConditions`,`getDataList` 的 `filter` 中对每行调用 `matchQuickQueryRow(row, this.quickQueryConditions)`。接真实接口时,可把 `jsonStr` 放入查询 DTO 由后端解析,无需本地 `matchQuickQueryRow`。
## 7. 部署检查清单
1. 执行 `scripts/srm_quick_query_field.sql`。
2. 确认 `812003Table1`(或你的业务表)在 `srm_quick_query_field` 中有对应 `field_name` 与行数据字段一致。
3. 前端菜单/路由如需单独「维护页」,可后续增加仅调用 `save` / `delete` 的页面;当前可通过 Postman 维护。
Loading…
Cancel
Save