import { getForecastList, addForecast, updateForecast } from '@/api/forecast' import { getCustomerList } from '@/api/common' import { getItemList } from '@/api/common' import { APPROVAL_STATUS, APPROVAL_STATUS_CONFIG, APPROVAL_STATUS_OPTIONS, getApprovalStatusLabel, getApprovalStatusType, canEdit, FORECAST_FORM_RULES, DEFAULT_FORECAST_FORM } from '@/constants/forecast' import { mapGetters } from 'vuex' import ForecastFormAvue from '@/components/forecast-form/index' /** * @typedef {import('@/api/forecast/types').ForecastRecord} ForecastRecord * @typedef {import('@/api/forecast/types').ForecastForm} ForecastForm * @typedef {import('./types').CustomerOption} CustomerOption * @typedef {import('./types').ItemOption} ItemOption * @typedef {import('./types').PageConfig} PageConfig * @typedef {import('./types').ForecastComponent} ForecastComponent * @typedef {import('@/api/forecast/types').ForecastForm} ForecastFormModel * @typedef {import('@/api/forecast/types').ForecastRecord} ForecastItem */ /** * 经销商销售预测申报页面业务逻辑混入 * @description 提供预测申报的增删改查、表单验证、远程搜索等功能 * @this {ForecastComponent & Vue} */ export default { /** * 组件注册 */ components: { ForecastFormAvue }, data() { return { /** @type {ForecastForm} 表单数据 */ form: { ...DEFAULT_FORECAST_FORM }, /** @type {Record} 查询参数 */ query: {}, /** @type {boolean} 表格加载状态 */ loading: true, /** @type {boolean} 表单提交状态 */ submitting: false, /** @type {boolean} 保存按钮加载状态 */ saveLoading: false, /** @type {boolean} 添加/编辑弹窗显示状态(保留兼容性) */ dialogVisible: false, /** @type {boolean} 是否为编辑模式(保留兼容性) */ isEdit: false, /** @type {boolean} 表单页面显示状态 */ editVisible: false, /** @type {'add'|'edit'|'view'} 表单模式:'add' | 'edit' | 'view' */ editMode: 'add', /** @type {string} 表单标题 */ editTitle: '', /** @type {string|number|null} 当前编辑的预测记录ID */ currentForecastId: null, /** @type {{pageSize: number, currentPage: number, total: number}} 分页配置 */ page: { pageSize: 10, currentPage: 1, total: 0 }, /** @type {Array} 表格数据 */ data: [], /** @type {Array} 客户选项 */ customerOptions: [], /** @type {boolean} 客户选项加载状态 */ customerLoading: false, /** @type {Array} 物料选项 */ itemOptions: [], /** @type {boolean} 物料选项加载状态 */ itemLoading: false, /** @type {AvueCrudOption} avue表格配置 */ option: { height: 'auto', calcHeight: 30, tip: false, searchShow: true, searchMenuSpan: 6, border: true, index: true, viewBtn: false, selection: false, addBtn: true, editBtn: false, // 禁用内置编辑按钮 delBtn: false, excelBtn: false, columnBtn: false, refreshBtn: true, dialogClickModal: false, addBtnText: '新增预测申报', editBtnText: '编辑', // 添加这个配置来完全隐藏操作列 menu: false, column: [ { label: '年份', prop: 'year', type: 'year', valueFormat: 'yyyy', // 添加这行,确保提交的数据是YYYY格式 search: true, searchSpan: 6, width: 100 }, { label: '月份', prop: 'month', type: 'select', dicData: [ { label: '1月', value: 1 }, { label: '2月', value: 2 }, { label: '3月', value: 3 }, { label: '4月', value: 4 }, { label: '5月', value: 5 }, { label: '6月', value: 6 }, { label: '7月', value: 7 }, { label: '8月', value: 8 }, { label: '9月', value: 9 }, { label: '10月', value: 10 }, { label: '11月', value: 11 }, { label: '12月', value: 12 } ], search: true, searchSpan: 6, width: 100 }, { label: '物料名称', prop: 'itemName', search: false, width: 200, overHidden: true }, { label: '物料编码', prop: 'itemCode', search: false, width: 150, overHidden: true }, { label: '规格', prop: 'specs', search: false, width: 150, overHidden: true }, { label: '预测数量', prop: 'forecastQuantity', type: 'number', precision: 4, search: false, width: 120, align: 'right' }, { label: '当前库存', prop: 'currentInventory', type: 'number', precision: 4, search: false, width: 120, align: 'right' }, { label: '品牌名称', prop: 'brandName', search: false, width: 120, overHidden: true }, { label: '审批状态', prop: 'approvalStatus', type: 'select', dicData: APPROVAL_STATUS_OPTIONS, slot: true, search: true, searchSpan: 6, width: 120 }, { label: '审批人', prop: 'approvedName', search: false, width: 120, overHidden: true }, { label: '审批时间', prop: 'approvedTime', type: 'datetime', format: 'yyyy-MM-dd HH:mm:ss', search: false, width: 160 }, { label: '预测编码', prop: 'forecastCode', search: true, searchSpan: 6, width: 150, overHidden: true }, { label: '客户名称', prop: 'customerName', search: true, searchSpan: 6, width: 180, overHidden: true }, { label: '客户编码', prop: 'customerCode', search: false, width: 150, overHidden: true }, { label: '创建时间', prop: 'createTime', type: 'datetime', format: 'yyyy-MM-dd HH:mm:ss', search: false, width: 160 }, // 添加自定义编辑按钮列 { label: '操作', prop: 'editBtn', slot: true, width: 120, fixed: 'right' } ] }, /** @type {Record>} 表单验证规则 */ formRules: FORECAST_FORM_RULES } }, computed: { ...mapGetters(['permission']), /** * 权限配置 * @this {Vue & ForecastComponent} * @returns {{addBtn: boolean, viewBtn: boolean, delBtn: boolean, editBtn: boolean}} 权限配置对象 */ permissionList() { return { // addBtn: this.vaildData(this.permission.forecast_add, false), // viewBtn: this.vaildData(this.permission.forecast_view, false), // delBtn: false, // 不提供删除功能 // editBtn: this.vaildData(this.permission.forecast_edit, false) addBtn: false, viewBtn: false, delBtn: false, // 不提供删除功能 editBtn: false } }, /** * 弹窗标题 * @this {Vue & ForecastComponent} * @returns {string} 弹窗标题文本 */ dialogTitle() { return this.isEdit ? '编辑预测申报' : '新增预测申报' } }, /** * 组件创建时初始化 * @this {Vue & ForecastComponent} */ created() { this.loadCustomerOptions() this.loadItemOptions() }, methods: { /** * 检查是否可以编辑 * @param {ForecastRecord} row - 预测记录行数据 * @returns {boolean} 是否可以编辑 */ canEdit(row) { return canEdit(row.approvalStatus || APPROVAL_STATUS.PENDING) }, /** * 加载客户选项 * @this {ForecastComponent & Vue} * @returns {Promise} */ async loadCustomerOptions() { try { this.customerLoading = true const res = await getCustomerList(1, 20) if (res.data && res.data.success) { this.customerOptions = res.data.data.records.map(item => ({ value: parseInt(item.Customer_ID.toString()), label: item.Customer_NAME, customerId: parseInt(item.Customer_ID.toString()), customerCode: item.Customer_CODE, customerName: item.Customer_NAME })) } } catch (error) { console.error('加载客户选项失败:', error) this.$message.error('加载客户选项失败') } finally { this.customerLoading = false } }, /** * 远程搜索客户 * @this {ForecastComponent & Vue} * @param {string} query - 搜索关键词 * @returns {Promise} */ async remoteSearchCustomer(query) { if (!query) { this.loadCustomerOptions() return } try { this.customerLoading = true /** @type {import('@/api/types/common').CustomerQueryParams} */ const params = { customerName: query, current: 1, size: 50 } const res = await getCustomerList(1, 50, params) if (res.data && res.data.success) { this.customerOptions = res.data.data.records.map(item => ({ value: parseInt(item.Customer_ID.toString()), label: item.Customer_NAME, customerId: parseInt(item.Customer_ID.toString()), customerCode: item.Customer_CODE, customerName: item.Customer_NAME })) } } catch (error) { console.error('搜索客户失败:', error) } finally { this.customerLoading = false } }, /** * 客户选择变化处理 * @this {ForecastComponent & Vue} * @param {number} customerId - 客户ID * @returns {void} */ handleCustomerChange(customerId) { const customer = this.customerOptions.find(item => item.value === customerId) if (customer) { this.form.customerId = customer.value this.form.customerCode = customer.customerCode this.form.customerName = customer.customerName } }, /** * 加载物料选项 * @this {ForecastComponent & Vue} * @returns {Promise} */ async loadItemOptions() { try { this.itemLoading = true const res = await getItemList(1, 20) if (res.data && res.data.success) { this.itemOptions = res.data.data.records.map(item => ({ value: item.Item_ID, label: item.Item_Name, itemId: item.Item_ID, itemCode: item.Item_Code, itemName: item.Item_Name, specs: item.Item_PECS || '' })) } } catch (error) { console.error('加载物料选项失败:', error) this.$message.error('加载物料选项失败') } finally { this.itemLoading = false } }, /** * 远程搜索物料 * @this {ForecastComponent & Vue} * @param {string} query - 搜索关键词 * @returns {Promise} */ async remoteSearchItem(query) { if (!query) { this.loadItemOptions() return } try { this.itemLoading = true const res = await getItemList(1, 50, { itemName: query }) if (res.data && res.data.success) { this.itemOptions = res.data.data.records.map(item => ({ value: item.Item_ID, label: item.Item_Name, itemId: item.Item_ID, itemCode: item.Item_Code, itemName: item.Item_Name, specs: item.Item_PECS || '' })) } } catch (error) { console.error('搜索物料失败:', error) } finally { this.itemLoading = false } }, /** * 物料选择变化处理 * @this {Vue & ForecastComponent} * @param {number} itemId - 物料ID * @returns {void} */ handleItemChange(itemId) { const item = this.itemOptions.find(option => option.value === itemId) if (item) { this.form.itemId = item.value this.form.itemCode = item.itemCode this.form.itemName = item.itemName this.form.specs = item.specs } }, /** * 获取审批状态标签 * @param {number} status - 审批状态 * @returns {string} 状态标签 */ getApprovalStatusLabel, /** * 获取审批状态类型 * @param {number} status - 审批状态 * @returns {string} 状态类型 */ getApprovalStatusType, /** * 检查是否可以编辑 * @param {ForecastRecord|ForecastItem} row - 行数据 * @returns {boolean} 是否可以编辑 */ canEditRow(row) { return row.approvalStatus !== undefined ? canEdit(row.approvalStatus) : false }, /** * 搜索重置 * @this {Vue & {query: Record, page: {pageSize: number, currentPage: number, total: number}, onLoad: (page: any, params?: any) => Promise}} * @returns {void} */ searchReset() { this.query = {} this.onLoad(this.page) }, /** * 搜索条件变化 * @this {Vue & {query: Record, page: {pageSize: number, currentPage: number, total: number}, onLoad: (page: any, params?: any) => Promise}} * @param {Record} params - 搜索参数 * @param {() => void} done - 完成回调 * @returns {void} */ searchChange(params, done) { this.query = params this.page.currentPage = 1 this.onLoad(this.page, params) done() }, /** * 页码变化 * @this {Vue & {page: {pageSize: number, currentPage: number, total: number}}} * @param {number} currentPage - 当前页码 * @returns {void} */ currentChange(currentPage) { this.page.currentPage = currentPage }, /** * 页大小变化 * @this {Vue & {page: {pageSize: number, currentPage: number, total: number}}} * @param {number} pageSize - 页大小 * @returns {void} */ sizeChange(pageSize) { this.page.pageSize = pageSize }, /** * 刷新数据 * @this {Vue & {page: {pageSize: number, currentPage: number, total: number}, query: Record, onLoad: (page: any, params?: any) => Promise}} * @returns {void} */ refreshChange() { this.onLoad(this.page, this.query) }, /** * 加载数据 * @this {Vue & {loading: boolean, page: {pageSize: number, currentPage: number, total: number}, query: Record, data: Array, $message: any}} * @param {{currentPage: number, pageSize: number}} page - 分页参数 * @param {Record} params - 查询参数 * @returns {Promise} */ async onLoad(page, params = {}) { this.loading = true try { const res = await getForecastList(page.currentPage, page.pageSize, { ...params, ...this.query }) if (res.data && res.data.success) { const data = res.data.data this.page.total = data.total this.data = data.records } else { this.$message.error(res.data?.msg || '加载数据失败') this.data = [] this.page.total = 0 } } catch (error) { console.error('加载数据失败:', error) this.$message.error('加载数据失败,请稍后重试') this.data = [] this.page.total = 0 } finally { this.loading = false } }, /** * 新增按钮点击 * @this {Vue & {isEdit: boolean, currentForecastId: string|number|null, dialogVisible: boolean}} * @returns {void} */ rowAdd() { this.isEdit = false this.currentForecastId = null this.dialogVisible = true }, /** * 新增预测申报(新版本) * @this {Vue & {editMode: string, editTitle: string, currentForecastId: string|number|null, editVisible: boolean, form: ForecastFormModel, isEdit: boolean, dialogVisible: boolean, generateForecastCode: () => void}} * @description 显示新增预测申报表单页面 * @returns {void} */ handleAdd() { console.log('新增预测申报') this.editMode = 'add' this.editTitle = '新增预测申报' this.currentForecastId = null this.editVisible = true // 重置表单数据 this.form = { ...DEFAULT_FORECAST_FORM } // 生成预测编码 this.generateForecastCode() // 保持兼容性 this.isEdit = false this.dialogVisible = true }, /** * 编辑按钮点击 * @this {Vue & {isEdit: boolean, currentForecastId: string|number|null, dialogVisible: boolean, $message: any, canEditRow: (row: ForecastRecord) => boolean}} * @param {ForecastRecord} row - 行数据 * @param {number} index - 行索引 * @returns {void} */ rowEdit(row, index) { if (!this.canEditRow(row)) { this.$message.warning('已审批或已拒绝的记录不能修改') return } this.isEdit = true this.currentForecastId = row.id this.dialogVisible = true }, /** * 编辑按钮点击(新版本) * @this {Vue & {editMode: string, editTitle: string, currentForecastId: string|number|null, editVisible: boolean, $message: any, canEditRow: (row: ForecastItem) => boolean, form: ForecastFormModel, isEdit: boolean, dialogVisible: boolean}} * @param {ForecastItem} row - 行数据 * @param {number} index - 行索引 * @description 显示编辑预测申报表单页面 * @returns {void} */ handleEdit(row, index) { if (!this.canEditRow(row)) { this.$message.warning('已审批或已拒绝的记录不能修改') return } this.editMode = 'edit' this.editTitle = '编辑预测申报' this.currentForecastId = row.id this.editVisible = true // 设置表单数据 this.form = { ...row } // 保持兼容性 this.isEdit = true this.dialogVisible = true }, /** * 返回列表 * @this {Vue & {editVisible: boolean, editMode: string, editTitle: string, currentForecastId: string|number|null, form: ForecastFormModel, dialogVisible: boolean, isEdit: boolean}} * @description 关闭表单页面,返回列表页面 * @returns {void} */ handleBackToList() { this.editVisible = false this.editMode = 'add' this.editTitle = '' this.currentForecastId = null this.form = { ...DEFAULT_FORECAST_FORM } // 保持兼容性 this.dialogVisible = false this.isEdit = false }, /** * 处理保存按钮点击 * @this {Vue & {saveLoading: boolean, $refs: any}} * @returns {void} */ handleSave() { if (this.$refs.forecastForm) { this.saveLoading = true this.$refs.forecastForm.handleSubmit() } }, /** * 处理表单提交成功 * @this {Vue & {saveLoading: boolean, editMode: string, $message: any, handleBackToList: () => void, refreshChange: () => void}} * @param {{msg?: string}} data - 提交成功的数据 * @returns {void} */ handleFormSubmit(data) { console.log('表单提交成功', data) console.log('当前编辑模式:', this.editMode) this.saveLoading = false // 保存当前编辑模式,因为 handleBackToList 会重置它 const currentEditMode = this.editMode // 关闭表单页面,返回列表 this.handleBackToList() // 刷新列表数据 this.refreshChange() // 显示成功消息 - 使用API返回的msg字段 const successMsg = data?.msg || (currentEditMode === 'add' ? '新增成功' : '修改成功') console.log(data) this.$message.success(successMsg) }, /** * 处理表单关闭 * @this {Vue & {saveLoading: boolean, handleBackToList: () => void}} * @returns {void} */ handleFormClose() { console.log('表单关闭') this.saveLoading = false // 返回列表页面 this.handleBackToList() }, /** * 表单提交(兼容旧版本) * @returns {Promise} */ async rowSave() { // 此方法保留用于兼容 avue-crud 的事件绑定 // 实际表单提交逻辑已移至 forecast-form 组件内部 console.warn('rowSave 方法已废弃,请使用组件化表单') }, /** * 取消表单(兼容旧版本) * @this {Vue & {dialogVisible: boolean}} * @returns {void} */ rowCancel() { // 此方法保留用于兼容 avue-crud 的事件绑定 this.dialogVisible = false }, /** * 生成预测编码 * @this {Vue & {form: ForecastFormModel}} * @returns {void} */ generateForecastCode() { const now = new Date() const year = now.getFullYear() const month = String(now.getMonth() + 1).padStart(2, '0') const timestamp = now.getTime().toString().slice(-6) this.form.forecastCode = `FC-${year}-${month}-${timestamp}` } } }