|
@@ -58,6 +58,8 @@
|
|
|
|
|
|
// API接口导入
|
|
|
import { addForecast, updateForecast, getForecastDetail } from '@/api/forecast'
|
|
|
+import { batchSaveSalesForecastSummary } from '@/api/forecast/forecast-summary'
|
|
|
+import { getUserLinkGoods } from '@/api/order/sales-order'
|
|
|
|
|
|
// 常量和枚举导入
|
|
|
import {
|
|
@@ -248,6 +250,17 @@ export default {
|
|
|
*/
|
|
|
brandOptions: [],
|
|
|
|
|
|
+ /** 物料表格数据(来自用户关联商品 pjpfStockDescList),带预测数量字段 */
|
|
|
+ /** @type {Array<import('@/api/types/order').PjpfStockDesc & { forecastQuantity: number }>} */
|
|
|
+ stockTableData: [],
|
|
|
+
|
|
|
+ /** 表格加载状态 */
|
|
|
+ tableLoading: false,
|
|
|
+
|
|
|
+ /** 品牌描述列表(用于品牌信息匹配) */
|
|
|
+ /** @type {Array<import('@/api/types/order').PjpfBrandDesc>} */
|
|
|
+ brandDescList: [],
|
|
|
+
|
|
|
/** 当前库存 */
|
|
|
currentInventory: null
|
|
|
}
|
|
@@ -584,6 +597,7 @@ export default {
|
|
|
|
|
|
/**
|
|
|
* 加载当前登录客户信息并填充表单
|
|
|
+ * @this {ForecastFormMixinComponent & Vue}
|
|
|
* @returns {Promise<void>}
|
|
|
*/
|
|
|
async loadCurrentCustomerInfo() {
|
|
@@ -596,6 +610,9 @@ export default {
|
|
|
this.formData.customerId = data.Customer_ID ? Number(data.Customer_ID) : null
|
|
|
this.formData.customerCode = data.Customer_CODE || ''
|
|
|
this.formData.customerName = data.Customer_NAME || ''
|
|
|
+
|
|
|
+ // 成功填充客户信息后,自动加载用户关联的品牌与库存物料,用于渲染下方表格
|
|
|
+ await this.loadUserLinkGoods()
|
|
|
}
|
|
|
} catch (e) {
|
|
|
console.error('获取客户信息失败:', e)
|
|
@@ -804,10 +821,39 @@ export default {
|
|
|
this.formData.customerId = Number(customerData.customerId)
|
|
|
this.formData.customerCode = customerData.customerCode
|
|
|
this.formData.customerName = customerData.customerName
|
|
|
+ // 选中客户后加载该用户关联的品牌与库存物料
|
|
|
+ this.loadUserLinkGoods()
|
|
|
} else {
|
|
|
this.formData.customerId = null
|
|
|
this.formData.customerCode = ''
|
|
|
this.formData.customerName = ''
|
|
|
+ // 清空表格
|
|
|
+ this.stockTableData = []
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 加载用户关联商品(品牌与库存)
|
|
|
+ * @returns {Promise<void>}
|
|
|
+ * @this {ForecastFormMixinComponent & Vue}
|
|
|
+ */
|
|
|
+ async loadUserLinkGoods() {
|
|
|
+ try {
|
|
|
+ this.tableLoading = true
|
|
|
+ this.stockTableData = []
|
|
|
+ this.brandDescList = []
|
|
|
+ const res = await getUserLinkGoods()
|
|
|
+ const payload = res && res.data && res.data.data ? res.data.data : null
|
|
|
+ const brandList = (payload && payload.pjpfBrandDescList) || []
|
|
|
+ const stockList = (payload && payload.pjpfStockDescList) || []
|
|
|
+ this.brandDescList = brandList
|
|
|
+ // 将库存列表转为表格数据,并默认预测数量为1
|
|
|
+ this.stockTableData = stockList.map(row => ({ ...row, forecastQuantity: 1 }))
|
|
|
+ } catch (e) {
|
|
|
+ console.error('加载用户关联商品失败:', e)
|
|
|
+ this.$message.error(e.message || '加载用户关联商品失败')
|
|
|
+ } finally {
|
|
|
+ this.tableLoading = false
|
|
|
}
|
|
|
},
|
|
|
|
|
@@ -906,17 +952,68 @@ export default {
|
|
|
*/
|
|
|
async submitForm() {
|
|
|
try {
|
|
|
- // 提交数据
|
|
|
- const submitData = { ...this.formData }
|
|
|
+ // 校验基础表单(年份、月份、客户等)
|
|
|
+ const year = typeof this.formData.year === 'string' ? parseInt(this.formData.year, 10) : this.formData.year
|
|
|
+ const month = this.formData.month
|
|
|
+
|
|
|
+ // 组装批量保存载荷
|
|
|
+ /** @type {import('@/api/forecast/types').SalesForecastSummaryBatchSaveRequest} */
|
|
|
+ const payload = this.stockTableData
|
|
|
+ .filter(row => Number(row.forecastQuantity) > 0)
|
|
|
+ .map(row => {
|
|
|
+ // 使用品牌描述列表按名称匹配品牌,匹配不到则留空/置0
|
|
|
+ const matchedBrand = this.brandDescList.find(b => b.cname === row.brandName)
|
|
|
+
|
|
|
+ // 原始 id 值(可能为字符串或数字,且可能超出 JS 安全整数范围)
|
|
|
+ const brandIdRaw = row.brandId != null && row.brandId !== ''
|
|
|
+ ? row.brandId
|
|
|
+ : (matchedBrand ? matchedBrand.id : null)
|
|
|
+ const itemIdRaw = row.goodsId
|
|
|
+
|
|
|
+ // 将可能超出安全整数范围的数值以字符串形式透传,避免 Number 精度丢失
|
|
|
+ /**
|
|
|
+ * 将可能超出安全整数范围的 id 值转换为安全的 number 或保留为 string
|
|
|
+ * @param {string|number|null|undefined} val
|
|
|
+ * @returns {string|number}
|
|
|
+ */
|
|
|
+ const toSafeNumberOrString = (val) => {
|
|
|
+ if (val == null || val === '') return 0
|
|
|
+ if (typeof val === 'number') {
|
|
|
+ return Number.isSafeInteger(val) ? val : String(val)
|
|
|
+ }
|
|
|
+ // 字符串:尝试转为数字,若安全则用数字,否则保留为原字符串
|
|
|
+ const parsed = Number(val)
|
|
|
+ return Number.isSafeInteger(parsed) ? parsed : String(val)
|
|
|
+ }
|
|
|
|
|
|
- let res
|
|
|
- if (this.isEdit) {
|
|
|
- res = await updateForecast(submitData)
|
|
|
- } else {
|
|
|
- res = await addForecast(submitData)
|
|
|
+ const brandId = toSafeNumberOrString(brandIdRaw)
|
|
|
+ const itemId = toSafeNumberOrString(itemIdRaw)
|
|
|
+
|
|
|
+ /** @type {import('@/api/forecast/types').SalesForecastSummaryBatchSaveItem} */
|
|
|
+ const item = {
|
|
|
+ year: year || new Date().getFullYear(),
|
|
|
+ month: month || (new Date().getMonth() + 1),
|
|
|
+ brandId: brandId,
|
|
|
+ brandCode: '', // 接口未返回品牌编码,按要求匹配不到留空
|
|
|
+ brandName: row.brandName || (matchedBrand ? matchedBrand.cname : ''),
|
|
|
+ itemId: itemId,
|
|
|
+ itemCode: row.code || '',
|
|
|
+ itemName: row.cname || '',
|
|
|
+ specs: row.typeNo || '',
|
|
|
+ pattern: row.productDescription || '',
|
|
|
+ forecastQuantity: Number(row.forecastQuantity) || 0,
|
|
|
+ approvalStatus: this.formData.approvalStatus || 0
|
|
|
+ }
|
|
|
+ return item
|
|
|
+ })
|
|
|
+
|
|
|
+ if (!payload.length) {
|
|
|
+ this.$message.warning('请至少填写一条有效的预测数量')
|
|
|
+ return
|
|
|
}
|
|
|
|
|
|
- // 触发提交成功事件,传递API响应数据
|
|
|
+ // 提交批量保存
|
|
|
+ const res = await batchSaveSalesForecastSummary(payload)
|
|
|
this.$emit(FORECAST_FORM_EVENTS.SUBMIT, res.data)
|
|
|
} catch (error) {
|
|
|
console.error('提交表单失败:', error)
|