Browse Source

更新

master
常熟吴彦祖 3 weeks ago
parent
commit
22c01ecd6b
  1. 3
      .gitignore
  2. 6
      src/api/order/poOrder.js
  3. 96
      src/views/modules/order/poBuyer.vue
  4. 346
      src/views/modules/order/poOrder.vue
  5. 5
      src/views/modules/order/poOrderFieldSchemaHelper.js

3
.gitignore

@ -16,3 +16,6 @@ node_modules/
*.ntvs*
*.njsproj
*.sln
AGENTS.md
CLAUDE.md
/.claude

6
src/api/order/poOrder.js

@ -39,3 +39,9 @@ export const getPoCustomerScope = (data) => createAPI(`/pODetail/customerScope`,
/** 按 customer + scene 获取字段 schema */
export const getPoFieldSchema = (data) => createAPI(`/pODetail/fieldSchema`, 'post', data)
/** 按供应商查询可供物料(SupplierforPart) */
export const listPoSupplierPartOptions = (data) => createAPI(`/supplier/supplierPartMoqPrice/listSupplierPart`, 'post', data)
/** 查询当前生效阶梯价(sp_current_tier_price) */
export const listPoCurrentTierPrice = (data) => createAPI(`/supplier/supplierPartMoqPrice/listCurrentTierPrice`, 'post', data)

96
src/views/modules/order/poBuyer.vue

@ -7,6 +7,15 @@
<el-form-item label="Buyer名称">
<el-input v-model="searchData.buyerName" clearable style="width: 220px" placeholder="请输入名称" />
</el-form-item>
<el-form-item label="所属Customer">
<el-select v-model="searchData.customerCode" clearable filterable style="width: 260px" placeholder="请选择所属Customer">
<el-option
v-for="item in poCustomerOptions"
:key="item.value"
:label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="启用">
<el-select v-model="searchData.active" clearable style="width: 120px">
<el-option label="Y" value="Y" />
@ -67,12 +76,23 @@
:rules="rules"
style="margin-left: 7px; margin-right: 7px;">
<el-row :gutter="24">
<el-col :span="12">
<el-col :span="8">
<el-form-item label="编码" prop="buyerCode">
<el-input v-model.trim="modalData.buyerCode" :disabled="modalDisableFlag" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-col :span="8">
<el-form-item label="所属Customer" prop="customerCode">
<el-select v-model="modalData.customerCode" filterable clearable placeholder="请选择所属Customer" style="width: 100%">
<el-option
v-for="item in poCustomerOptions"
:key="item.value"
:label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="启用" prop="active">
<el-select v-model="modalData.active" style="width: 100%">
<el-option label="Y" value="Y" />
@ -111,6 +131,7 @@ import {
updatePoBuyer,
deletePoBuyer
} from '@/api/order/poBuyer.js'
import { getPoCustomerOptions } from '@/api/order/poOrder.js'
import excel from '@/utils/excel-util.js'
export default {
@ -128,16 +149,19 @@ export default {
site: this.$store.state.user.site,
buyerCode: '',
buyerName: '',
customerCode: '',
active: '',
page: 1,
limit: 10
},
poCustomerOptions: [],
modalFlag: false,
modalDisableFlag: false,
modalData: {
site: this.$store.state.user.site,
buyerCode: '',
buyerName: '',
customerCode: '',
active: 'Y',
remark: '',
flag: '1'
@ -176,6 +200,28 @@ export default {
columnWidth: 260,
showOverflowTooltip: true
},
{
columnProp: 'customerCode',
headerAlign: 'center',
align: 'center',
columnLabel: '所属Customer编码',
columnHidden: false,
columnSortable: false,
fixed: '',
columnWidth: 180,
showOverflowTooltip: true
},
{
columnProp: 'customerName',
headerAlign: 'center',
align: 'left',
columnLabel: '所属Customer名称',
columnHidden: false,
columnSortable: false,
fixed: '',
columnWidth: 220,
showOverflowTooltip: true
},
{
columnProp: 'active',
headerAlign: 'center',
@ -208,6 +254,9 @@ export default {
{ required: true, message: '请输入名称', trigger: 'blur' },
{ max: 300, message: '名称长度不能超过300', trigger: 'blur' }
],
customerCode: [
{ required: true, message: '请选择所属Customer', trigger: 'change' }
],
active: [
{ required: true, message: '请选择启用状态', trigger: 'change' }
],
@ -223,9 +272,49 @@ export default {
})
},
created () {
this.getCustomerOptions()
this.getDataList()
},
methods: {
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
}))
} else {
this.$message.error((data && data.msg) || '获取Customer失败')
}
}).catch(err => {
this.$message.error((err && err.message) || '获取Customer失败')
})
},
ensureCustomerOptionExists (code, name) {
const customerCode = (code || '').trim()
if (!customerCode) {
return
}
const exists = this.poCustomerOptions.some(item => item && item.value === customerCode)
if (exists) {
return
}
this.poCustomerOptions.push({
value: customerCode,
label: this.formatCodeOptionLabel(customerCode, name || '')
})
},
getDataList () {
this.dataListLoading = true
const params = {
@ -268,6 +357,7 @@ export default {
site: this.$store.state.user.site,
buyerCode: '',
buyerName: '',
customerCode: '',
active: 'Y',
remark: '',
flag: '1'
@ -282,10 +372,12 @@ export default {
updateModal (row) {
this.modalDisableFlag = true
this.ensureCustomerOptionExists(row.customerCode, row.customerName)
this.modalData = {
site: row.site,
buyerCode: row.buyerCode,
buyerName: row.buyerName,
customerCode: row.customerCode || '',
active: row.active,
remark: row.remark || '',
flag: '2'

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

@ -162,7 +162,7 @@
<template v-else>
<!-- 下拉框 -->
<el-select
v-if="item.editType === 'select'"
v-if="item.editType === 'select' || item.editType === 'dict-select'"
v-model="scope.row[item.columnProp]"
size="mini"
style="width: 100%"
@ -366,6 +366,35 @@
<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>
@ -380,7 +409,9 @@ import {
createPoOrder,
getPoCustomerOptions,
getPoBuyerOptions,
getPoFieldSchema
getPoFieldSchema,
listPoSupplierPartOptions,
listPoCurrentTierPrice
} from '@/api/order/poOrder.js'
import {
createAdvancedQueryData,
@ -408,6 +439,7 @@ import { PO_ORDER_TYPE_OPTIONS, PO_ORDER_TYPE_FILTER_OPTIONS, formatPoOrderTypeL
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,
@ -490,6 +522,15 @@ export default {
eamTagNo: '',
eamPickField: '',
addForm: {},
supplierPartDialogVisible: false,
supplierPartDialogLoading: false,
supplierPartDialogRows: [],
supplierPartDialogContext: '',
supplierPartDialogTargetRow: null,
supplierPartDialogSupplierNo: '',
supplierPartDialogSite: '',
addPriceLookupTimer: null,
addPriceLookupSeq: 0,
selectedCustomerScope: null,
currentRow: null,
@ -510,6 +551,7 @@ export default {
{ label: 'N', value: 'N' }
]
},
dictSelectOptionsMap: {},
searchData: {
site: this.$store.state.user.site,
customer: '',
@ -1588,6 +1630,10 @@ export default {
},
beforeDestroy () {
window.removeEventListener('resize', this.onWindowResize)
if (this.addPriceLookupTimer) {
clearTimeout(this.addPriceLookupTimer)
this.addPriceLookupTimer = null
}
},
methods: {
canIssuePoOrder,
@ -1782,6 +1828,94 @@ export default {
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) {
@ -1866,6 +2000,13 @@ export default {
* 解析列对应的下拉选项优先列上 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
}
@ -1876,6 +2017,74 @@ export default {
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,
@ -1888,6 +2097,8 @@ export default {
onSessionCustomerChange (customer, customerOption) {
this.searchData.customer = customer
this.selectedCustomerScope = customerOption || null
this.dictSelectOptionsMap = {}
this.getBuyerOptions(customer)
this.pageIndex = 1
this.masterRows = []
this.dataList = []
@ -1895,6 +2106,8 @@ export default {
if (!customer) {
this.selectedCustomerScope = null
this.fieldSchemaFields = []
this.poBuyerOptions = []
this.columnSelectOptions.poBuyerOptions = []
this.refreshTableLayout()
return
}
@ -1914,10 +2127,12 @@ export default {
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 = []
@ -1989,7 +2204,9 @@ export default {
if (tag === SUPPLIER_CHOOSELIST_TAG || tag === 520) {
const { supplierNo, supplierName } = parseSupplierChooselistRow(data)
if (ctx === 'add') {
this.fillSupplierShortFields(this.addForm, supplierNo, supplierName, false)
this.fillSupplierShortFields(this.addForm, supplierNo, supplierName, false).then(() => {
this.scheduleAutoFillAddPriceByTierPrice()
})
} else if (this.currentSelectRow) {
const row = this.currentSelectRow
this.fillSupplierShortFields(row, supplierNo, supplierName, true)
@ -2003,6 +2220,7 @@ export default {
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)
@ -2241,9 +2459,18 @@ export default {
})
},
getBuyerOptions () {
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
site: this.$store.state.user.site,
customer: customerCode
}).then(({ data }) => {
if (data && data.code === 0) {
this.poBuyerOptions = (data.rows || []).map(item => ({
@ -2629,15 +2856,19 @@ export default {
},
fillSupplierShortFields (target, supplierNo, supplierName, markChanged) {
if (!target || !supplierNo) return
fetchSupplierDetail(supplierNo, this.$store.state.user.site).then(detail => {
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)
@ -2649,12 +2880,95 @@ export default {
apply(fromList.item || '', fromList.category || '', fromList.categoryName || '', fromList.sku || '')
return
}
searchPartInfo({ site: this.$store.state.user.site, partNo }).then(({ data }) => {
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
@ -2668,6 +2982,11 @@ export default {
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
@ -2678,9 +2997,7 @@ export default {
if (!this.poCustomerOptions.length) {
this.getCustomerOptions()
}
if (!this.poBuyerOptions.length) {
this.getBuyerOptions()
}
this.getBuyerOptions(this.searchData.customer)
},
loadAddFieldSchema (customer) {
@ -2763,7 +3080,7 @@ export default {
this.openEamList(SUPPLIER_CHOOSELIST_TAG, '', conSql, 'add', 'supplier')
},
openAddPartNoSelect () {
this.openEamList(507, '', '', 'add', 'partNo')
this.openSupplierPartDialog('add')
},
openAddDepartureSelect () {
this.openEamList(2018, '', this.buildDeparturePortConSql(), 'add', 'departure')
@ -2838,8 +3155,7 @@ export default {
},
openPartNoSelect (row) {
this.currentPartNoRow = row
this.openEamList(507, '', '', 'row', 'partNo')
this.openSupplierPartDialog('row', row)
},
openDepartureSelect (row) {

5
src/views/modules/order/poOrderFieldSchemaHelper.js

@ -188,6 +188,7 @@ function buildDynamicColumnBySchema (field, existingProps) {
showOverflowTooltip: true,
editAble: String(field.editableFlag || '').toUpperCase() === 'Y',
editType,
dictType: field && field.dictType ? String(field.dictType).trim() : '',
selectOptionsKey: '',
selectOptions: null,
__sortNo: Number.isFinite(sortNo) ? sortNo : Number.MAX_SAFE_INTEGER
@ -231,9 +232,9 @@ function resolveEditType (field) {
if (editorType === 'select') {
return 'select'
}
// dict-select 在表格批量编辑场景先回落为普通输入,避免无选项导致不可编辑
// dict-select 在编辑场景仍应按下拉处理(选项由页面按 dictType 异步加载)
if (editorType === 'dict-select' || editorType === 'dict_select') {
return 'text'
return 'dict-select'
}
if (editorType) {
return 'text'

Loading…
Cancel
Save