|
@@ -59,7 +59,7 @@
|
|
|
|
|
|
// API接口导入
|
|
|
import { addForecast, updateForecast, getForecastDetail } from '@/api/forecast'
|
|
|
-import { addSalesForecastMain } from '@/api/forecast/forecast-summary'
|
|
|
+import { addSalesForecastMain, updateSalesForecastMain } from '@/api/forecast/forecast-summary'
|
|
|
import { getUserLinkGoods } from '@/api/order/sales-order'
|
|
|
|
|
|
// 常量和枚举导入
|
|
@@ -253,7 +253,7 @@ export default {
|
|
|
brandOptions: [],
|
|
|
|
|
|
/** 物料表格数据(来自用户关联商品 pjpfStockDescList),带预测数量字段 */
|
|
|
- /** @type {Array<import('@/api/types/order').PjpfStockDesc & { forecastQuantity: number }>} */
|
|
|
+ /** @type {Array<import('@/api/types/order').PjpfStockDesc & { forecastQuantity: number, brandCode?: string }>} */
|
|
|
stockTableData: [],
|
|
|
|
|
|
/** 表格加载状态 */
|
|
@@ -375,6 +375,29 @@ export default {
|
|
|
...newData,
|
|
|
year: newData.year ? newData.year.toString() : ''
|
|
|
}
|
|
|
+ // 回显子项明细到物料表格:将 pcBladeSalesForecastSummaryList -> stockTableData
|
|
|
+ if (Array.isArray(newData.pcBladeSalesForecastSummaryList)) {
|
|
|
+ try {
|
|
|
+ this.stockTableData = newData.pcBladeSalesForecastSummaryList.map(item => ({
|
|
|
+ // 尽量保持与 PjpfStockDesc 结构一致,便于表格渲染
|
|
|
+ id: item.id != null ? item.id : undefined,
|
|
|
+ goodsId: item.itemId != null ? item.itemId : undefined,
|
|
|
+ code: item.itemCode || '',
|
|
|
+ cname: item.itemName || '',
|
|
|
+ brandId: item.brandId != null ? item.brandId : undefined,
|
|
|
+ brandCode: item.brandCode || '',
|
|
|
+ brandName: item.brandName || '',
|
|
|
+ typeNo: item.specs || '',
|
|
|
+ productDescription: item.pattern || '',
|
|
|
+ brandItem: item.pattern || '',
|
|
|
+ storeInventory: item.storeInventory || '0',
|
|
|
+ // 预测数量用于编辑
|
|
|
+ forecastQuantity: Number(item.forecastQuantity || 0)
|
|
|
+ }))
|
|
|
+ } catch (e) {
|
|
|
+ console.warn('映射回显明细失败:', e)
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
},
|
|
|
immediate: true,
|
|
@@ -549,14 +572,30 @@ export default {
|
|
|
await this.loadItemOption(this.formData.itemId, this.formData.itemName, this.formData.itemCode, this.formData.specs)
|
|
|
}
|
|
|
|
|
|
- // 触发加载完成事件
|
|
|
- this.$emit(FORECAST_FORM_EVENTS.LOADED, this.formData)
|
|
|
- } else {
|
|
|
- this.$message.error(res.data?.message || '获取详情失败')
|
|
|
+ // 映射明细到表格:pcBladeSalesForecastSummaryList -> stockTableData
|
|
|
+ if (Array.isArray(detailData.pcBladeSalesForecastSummaryList)) {
|
|
|
+ try {
|
|
|
+ this.stockTableData = detailData.pcBladeSalesForecastSummaryList.map(item => ({
|
|
|
+ id: item.id != null ? item.id : undefined,
|
|
|
+ goodsId: item.itemId != null ? item.itemId : undefined,
|
|
|
+ code: item.itemCode || '',
|
|
|
+ cname: item.itemName || '',
|
|
|
+ brandId: item.brandId != null ? item.brandId : undefined,
|
|
|
+ brandCode: item.brandCode || '',
|
|
|
+ brandName: item.brandName || '',
|
|
|
+ typeNo: item.specs || '',
|
|
|
+ productDescription: item.pattern || '',
|
|
|
+ brandItem: item.pattern || '',
|
|
|
+ storeInventory: item.storeInventory || '0',
|
|
|
+ forecastQuantity: Number(item.forecastQuantity || 0)
|
|
|
+ }))
|
|
|
+ } catch (e) {
|
|
|
+ console.warn('映射详情明细失败:', e)
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
} catch (error) {
|
|
|
- console.error('获取销售预测详情失败:', error)
|
|
|
- this.$message.error('获取详情失败,请稍后重试')
|
|
|
+ console.error('加载销售预测详情失败:', error)
|
|
|
} finally {
|
|
|
this.formLoading = false
|
|
|
}
|
|
@@ -848,6 +887,8 @@ export default {
|
|
|
if (!valid) {
|
|
|
// 校验失败时,如存在 loading 回调(部分版本提供),尝试恢复按钮状态
|
|
|
if (typeof loading === 'function') loading()
|
|
|
+ // 通知父组件校验失败,便于父侧重置保存按钮loading
|
|
|
+ this.$emit && this.$emit(FORECAST_FORM_EVENTS.SUBMIT_ERROR, { message: '表单校验未通过' })
|
|
|
return
|
|
|
}
|
|
|
// 校验通过后执行提交
|
|
@@ -884,6 +925,8 @@ export default {
|
|
|
// 基础校验(客户必选)
|
|
|
if (!this.formData.customerId) {
|
|
|
this.$message && this.$message.warning('请选择客户')
|
|
|
+ // 通知父组件失败,重置保存按钮loading
|
|
|
+ this.$emit && this.$emit(FORECAST_FORM_EVENTS.SUBMIT_ERROR, { message: '未选择客户' })
|
|
|
return
|
|
|
}
|
|
|
|
|
@@ -892,26 +935,24 @@ export default {
|
|
|
const month = Number(this.formData.month)
|
|
|
|
|
|
// 安全的ID转换:优先使用 BigInt 校验范围,再决定以 number 还是 string 传输
|
|
|
+ /** @param {unknown} val @returns {string|number|''} */
|
|
|
const toIdOutput = (val) => {
|
|
|
if (val == null || val === '') return ''
|
|
|
try {
|
|
|
const bi = BigInt(String(val))
|
|
|
- // 在 JS 中,超过 MAX_SAFE_INTEGER 的数字使用字符串传输,避免精度丢失
|
|
|
const absBi = bi >= 0n ? bi : -bi
|
|
|
const maxSafe = BigInt(Number.MAX_SAFE_INTEGER)
|
|
|
if (absBi <= maxSafe) {
|
|
|
- // 安全范围内,返回 number
|
|
|
return Number(bi)
|
|
|
}
|
|
|
- // 超出安全范围,返回字符串
|
|
|
return String(bi)
|
|
|
} catch (e) {
|
|
|
- // 不是纯数字,兜底返回原字符串
|
|
|
return String(val)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 安全的数值转换(用于数量等非ID字段):若不可安全表示整数,仍以字符串传输
|
|
|
+ /** @param {unknown} val @returns {number|string} */
|
|
|
const toSafeNumberOrString = (val) => {
|
|
|
if (val == null || val === '') return 0
|
|
|
if (typeof val === 'number') {
|
|
@@ -932,46 +973,54 @@ export default {
|
|
|
const brandId = toIdOutput(rawBrandId)
|
|
|
const itemId = toIdOutput(rawItemId)
|
|
|
|
|
|
- return {
|
|
|
- // ID与编码/名称:缺失则置空
|
|
|
- brandId: brandId,
|
|
|
+ const base = {
|
|
|
+ brandId,
|
|
|
brandCode: row.brandCode || '',
|
|
|
brandName: row.brandName || (matchedBrand ? matchedBrand.cname : ''),
|
|
|
- itemId: itemId,
|
|
|
+ itemId,
|
|
|
itemCode: row.code || '',
|
|
|
itemName: row.cname || '',
|
|
|
- // 规格与花纹:无则空
|
|
|
specs: row.typeNo || '',
|
|
|
pattern: row.productDescription || row.brandItem || '',
|
|
|
- // 数量:保留为 number(有限数),否则以字符串兜底
|
|
|
forecastQuantity: toSafeNumberOrString(row.forecastQuantity),
|
|
|
- // 子项审批状态:与顶层保持一致,缺省为待提交
|
|
|
approvalStatus: Number(this.formData.approvalStatus ?? 0)
|
|
|
}
|
|
|
+ // 编辑模式下,如果明细有 id,带上给后端做区分
|
|
|
+ if (this.isEdit && (row.id != null && row.id !== '')) {
|
|
|
+ return { id: toIdOutput(row.id), ...base }
|
|
|
+ }
|
|
|
+ return base
|
|
|
})
|
|
|
|
|
|
if (!items.length) {
|
|
|
this.$message && this.$message.warning('请至少填写一条有效的预测数量')
|
|
|
+ // 通知父组件失败,重置保存按钮loading
|
|
|
+ this.$emit && this.$emit(FORECAST_FORM_EVENTS.SUBMIT_ERROR, { message: '未填写有效的预测明细' })
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- // 组装 main-add 载荷
|
|
|
- const payload = {
|
|
|
+ // 组装载荷
|
|
|
+ const payloadBase = {
|
|
|
year: year || new Date().getFullYear(),
|
|
|
month: month || (new Date().getMonth() + 1),
|
|
|
approvalStatus: Number(this.formData.approvalStatus ?? 0),
|
|
|
pcBladeSalesForecastSummaryList: items
|
|
|
}
|
|
|
|
|
|
- const res = await addSalesForecastMain(payload)
|
|
|
+ let res
|
|
|
+ if (this.isEdit && this.formData.id) {
|
|
|
+ // 更新:需要主表 id
|
|
|
+ res = await updateSalesForecastMain({ id: toIdOutput(this.formData.id), ...payloadBase })
|
|
|
+ } else {
|
|
|
+ // 新增
|
|
|
+ res = await addSalesForecastMain(payloadBase)
|
|
|
+ }
|
|
|
+
|
|
|
if (res && res.data && res.data.success) {
|
|
|
- // this.$message && this.$message.success('提交成功')
|
|
|
this.$emit && this.$emit(FORECAST_FORM_EVENTS.SUBMIT, res.data)
|
|
|
this.$emit && this.$emit(FORECAST_FORM_EVENTS.SUBMIT_SUCCESS, res.data)
|
|
|
} else {
|
|
|
const msg = (res && res.data && (res.data.msg || res.data.message)) || '提交失败'
|
|
|
- // this.$message && this.$message.error(msg)
|
|
|
- // 提交业务失败时,解禁年份与月份两个表单项(直接操作 $refs)
|
|
|
if (typeof this.setYearMonthDisabled === 'function') {
|
|
|
this.setYearMonthDisabled(false)
|
|
|
} else if (this.$refs) {
|
|
@@ -982,13 +1031,10 @@ export default {
|
|
|
} catch (e) { /* 忽略 */ }
|
|
|
})
|
|
|
}
|
|
|
- // 通知父组件:提交失败(业务失败)
|
|
|
this.$emit && this.$emit(FORECAST_FORM_EVENTS.SUBMIT_ERROR, { message: msg, response: res })
|
|
|
}
|
|
|
} catch (error) {
|
|
|
console.error('提交表单失败:', error)
|
|
|
- // this.$message && this.$message.error(error && error.message ? error.message : '操作失败,请重试')
|
|
|
- // 通知父组件:提交失败(异常)
|
|
|
this.$emit && this.$emit(FORECAST_FORM_EVENTS.SUBMIT_ERROR, error)
|
|
|
}
|
|
|
},
|
|
@@ -1005,8 +1051,10 @@ export default {
|
|
|
this.formData.customerId = Number(customerData.customerId)
|
|
|
this.formData.customerCode = customerData.customerCode
|
|
|
this.formData.customerName = customerData.customerName
|
|
|
- // 选中客户后加载该用户关联的品牌与库存物料
|
|
|
- this.loadUserLinkGoods()
|
|
|
+ // 选中客户后加载该用户关联的品牌与库存物料(仅新增模式自动加载,编辑模式不覆盖回显数据)
|
|
|
+ if (!this.isEdit) {
|
|
|
+ this.loadUserLinkGoods()
|
|
|
+ }
|
|
|
} else {
|
|
|
this.formData.customerId = null
|
|
|
this.formData.customerCode = ''
|