| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657 |
- import { approveSalesForecastSummary, getSalesForecastMainPage, approveSalesForecastSummaryParticulars } from '@/api/forecast/forecast-summary'
- import { mapGetters } from 'vuex'
- import {
- APPROVAL_STATUS,
- APPROVAL_STATUS_OPTIONS,
- getApprovalStatusLabel,
- getApprovalStatusType,
- canApprove as canApproveStatus,
- canReject as canRejectStatus,
- formatNumber,
- formatDateTime as formatDateTimeUtil
- } from '@/constants/forecast'
- import { safeBigInt } from '@/util/util'
- /**
- * @typedef {import('./types').ForecastRecord} ForecastRecord
- * @typedef {import('./types').PageConfig} PageConfig
- * @typedef {import('./types').ApprovalFormData} ApprovalFormData
- * @typedef {import('./types').PermissionConfig} PermissionConfig
- * @typedef {import('./types').ForecastAuditComponent} ForecastAuditComponent
- */
- /**
- * 销售预测审核页面业务逻辑混入
- * @description 提供预测申报的审核功能,包括列表展示、详情查看、审批操作等
- */
- export default {
- /**
- * @this {Vue & ForecastAuditComponent}
- */
- data() {
- return {
- /** @type {Record<string, any>} 表单数据 */
- form: {},
- /** @type {Record<string, any>} 查询参数 */
- query: {},
- /** @type {boolean} 表格加载状态 */
- loading: true,
- /** @type {boolean} 提交状态 */
- submitting: false,
- /** @type {boolean} 详情弹窗显示状态 */
- detailDialogVisible: false,
- /** @type {boolean} 审批弹窗显示状态 */
- approvalDialogVisible: false,
- /** @type {PageConfig} 分页配置 */
- page: {
- pageSize: 10,
- currentPage: 1,
- total: 0
- },
- /** @type {Array<ForecastRecord>} 表格数据 */
- data: [],
- /** @type {Record<string, any>} 详情表单数据 */
- detailForm: {},
- /** 当前正在审批的主表记录(用于批量审批子表) */
- selectedMainRecord: null,
- /** @type {ApprovalFormData} 审批表单数据 */
- approvalForm: {
- id: null,
- isApprove: true
- },
- option: {
- height: 'auto',
- calcHeight: 30,
- searchShow: true,
- searchMenuSpan: 18,
- searchIndex: 3,
- border: true,
- index: false,
- viewBtn: false,
- selection: false,
- addBtn: false,
- editBtn: false,
- delBtn: false,
- excelBtn: false,
- columnBtn: false,
- refreshBtn: true,
- dialogClickModal: false,
- menu: true,
- menuWidth: 280,
- menuFixed: false,
- // 行展开配置,展示子表明细
- expand: true,
- expandRowKeys: [],
- defaultExpandAll: false,
- column: [
- { label: 'ID', prop: 'id', width: 200, overHidden: false },
- { label: '序号', prop: 'seq', width: 70, align: 'center', slot: true },
- {
- label: '年份',
- prop: 'year',
- type: 'year',
- valueFormat: '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: 'customerCode',
- search: true,
- searchSpan: 6,
- width: 150,
- overHidden: true
- },
- {
- label: '客户名称',
- prop: 'customerName',
- search: true,
- searchSpan: 6,
- width: 180,
- overHidden: true
- },
- {
- label: '审批状态',
- prop: 'approvalStatus',
- type: 'select',
- dicData: APPROVAL_STATUS_OPTIONS,
- search: true,
- searchSpan: 6,
- width: 100,
- slot: true
- },
- {
- label: '审批人',
- prop: 'approvedName',
- search: false,
- width: 120,
- overHidden: true
- },
- {
- label: '审批时间',
- prop: 'approvedTime',
- type: 'datetime',
- search: false,
- width: 160,
- overHidden: true
- },
- {
- label: '创建时间',
- prop: 'createTime',
- type: 'datetime',
- search: false,
- width: 160,
- overHidden: true
- },
- { label: '更新时间', prop: 'updateTime', type: 'datetime', search: false, width: 160, overHidden: true }
- ]
- },
- // 子表(展开区)配置,展示明细列表
- childOption: {
- // width: '100%',
- // height: 'auto',
- // calcHeight: 0,
- tip: false,
- searchShow: false,
- border: true,
- index: true,
- viewBtn: false,
- editBtn: false,
- delBtn: false,
- addBtn: false,
- selection: false,
- menu: true,
- menuWidth: 160,
- menuFixed: false,
- expand: false,
- column: [
- { label: '物料编码', prop: 'itemCode', minWidth: 120 },
- { label: '物料名称', prop: 'itemName', minWidth: 180, overHidden: true },
- { label: '规格型号', prop: 'specs', minWidth: 140, overHidden: true },
- { label: '花型/图案', prop: 'pattern', minWidth: 120, overHidden: true },
- { label: '品牌名称', prop: 'brandName', minWidth: 120, overHidden: true },
- { label: '预测数量', prop: 'forecastQuantity', minWidth: 120, align: 'right', slot: true },
- { label: '审核状态', prop: 'approvalStatus', type: 'select', dicData: APPROVAL_STATUS_OPTIONS, minWidth: 100, slot: true },
- // { label: '客户名称', prop: 'customerName', minWidth: 160, overHidden: true },
- // { label: '年份', prop: 'year', minWidth: 100 },
- // { label: '月份', prop: 'month', minWidth: 100 },
- { label: '审批人', prop: 'approvedName', minWidth: 120, overHidden: true },
- { label: '审批时间', prop: 'approvedTime', type: 'datetime', minWidth: 160, overHidden: true, slot: true }
- ]
- }
- }
- },
- computed: {
- ...mapGetters(['permission', 'userInfo']),
- /**
- * 权限列表
- * @this {Vue & ForecastAuditComponent}
- * @returns {PermissionConfig} 权限配置对象
- */
- permissionList() {
- return {
- // addBtn: this.vaildData(this.permission.forecast_audit_add, false),
- // viewBtn: this.vaildData(this.permission.forecast_audit_view, false),
- // editBtn: this.vaildData(this.permission.forecast_audit_edit, false),
- // delBtn: this.vaildData(this.permission.forecast_audit_delete, false)
- addBtn: false,
- viewBtn: false,
- editBtn: false,
- delBtn: false
- }
- },
- /**
- * 审批弹窗标题
- * @this {Vue & ForecastAuditComponent}
- * @returns {string} 弹窗标题文本
- */
- approvalDialogTitle() {
- return this.approvalForm.isApprove ? '审批通过确认' : '审批拒绝确认'
- }
- },
- created() {
- // 初始化时不需要加载额外数据,只等待表格自动加载
- },
- methods: {
- /**
- * 获取审批状态标签
- * @param {import('@/constants/forecast').ApprovalStatus} status - 审批状态值
- * @returns {string} 状态标签文本
- */
- getApprovalStatusLabel(status) {
- return getApprovalStatusLabel(status)
- },
- /**
- * 获取审批状态类型
- * @param {import('@/constants/forecast').ApprovalStatus} status - 审批状态值
- * @returns {string} 状态类型
- */
- getApprovalStatusType(status) {
- return getApprovalStatusType(status)
- },
- /**
- * 判断是否可以审批通过
- * @this {Vue & ForecastAuditComponent}
- * @param {ForecastRecord | {approvalStatus: number}} row - 数据行对象
- * @returns {boolean} 是否可以审批通过
- */
- canApprove(row) {
- return canApproveStatus(/** @type {import('@/constants/forecast').ApprovalStatus} */ (row.approvalStatus ?? APPROVAL_STATUS.PENDING))
- },
- /**
- * 判断是否可以审批拒绝
- * @this {Vue & ForecastAuditComponent}
- * @param {ForecastRecord | {approvalStatus: number}} row - 数据行对象
- * @returns {boolean} 是否可以审批拒绝
- */
- canReject(row) {
- return canRejectStatus(/** @type {import('@/constants/forecast').ApprovalStatus} */ (row.approvalStatus ?? APPROVAL_STATUS.PENDING))
- },
- /**
- * 格式化数字显示
- * @this {Vue & ForecastAuditComponent}
- * @param {number|null|undefined} value - 数值
- * @returns {string} 格式化后的字符串
- */
- formatNumber(value) {
- return formatNumber(value)
- },
- // 新增:格式化日期时间显示,供子表“审批时间”列使用
- /**
- * @param {string|null|undefined} dateTime - 日期时间值
- * @returns {string} 格式化后的日期时间字符串
- */
- formatDateTime(dateTime) {
- return formatDateTimeUtil(dateTime)
- },
- /**
- * 查看详情
- * @this {Vue & ForecastAuditComponent}
- * @param {ForecastRecord} row - 数据行对象
- * @param {number} index - 行索引
- * @returns {void}
- */
- viewDetail(row, index) {
- this.detailForm = { ...row }
- this.detailDialogVisible = true
- },
- /**
- * 审批通过记录(主表)
- * @this {Vue & ForecastAuditComponent}
- * @param {ForecastRecord} row - 数据行对象
- * @param {number} index - 行索引
- * @returns {Promise<void>}
- */
- async approveRecord(row, index) {
- /** @type {Vue & ForecastAuditComponent} */
- const self = /** @type {any} */ (this)
- try {
- await self.$confirm('确认通过此预测申报吗?', '审批确认', {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- type: 'success'
- })
- self.performApproval(row, true)
- } catch (e) {
- // 用户取消或错误
- }
- },
- /**
- * 审批拒绝记录(主表)
- * @this {Vue & ForecastAuditComponent}
- * @param {ForecastRecord} row - 数据行对象
- * @param {number} index - 行索引
- * @returns {Promise<void>}
- */
- async rejectRecord(row, index) {
- /** @type {Vue & ForecastAuditComponent} */
- const self = /** @type {any} */ (this)
- try {
- await self.$confirm('确认拒绝此预测申报吗?', '审批确认', {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- type: 'warning'
- })
- self.performApproval(row, false)
- } catch (e) {
- // 用户取消或错误
- }
- },
- /**
- * 执行审批(主表)
- * @this {Vue & ForecastAuditComponent}
- * @param {ForecastRecord} row - 主表记录
- * @param {boolean} isApprove - 是否通过
- * @returns {Promise<void>}
- */
- async performApproval(row, isApprove) {
- /** @type {Vue & ForecastAuditComponent} */
- const self = /** @type {any} */ (this)
- if (!row) {
- self.$message.error('记录不存在,无法审批')
- return
- }
- const id = (row && row.id != null && row.id !== '')
- ? String(row.id)
- : (row && row.idBigint != null ? String(row.idBigint) : '')
- if (!id) {
- self.$message.error('主表ID缺失,无法提交审批')
- return
- }
- self.submitting = true
- try {
- const payload = {
- id,
- approvalStatus: isApprove ? APPROVAL_STATUS.APPROVED : APPROVAL_STATUS.REJECTED
- }
- await approveSalesForecastSummary(payload)
- self.$message.success(isApprove ? '审批通过成功' : '审批拒绝成功')
- await self.onLoad(self.page, self.query)
- } catch (e) {
- self.$message.error('审批操作失败')
- } finally {
- self.submitting = false
- }
- },
- /**
- * 子表明细审批通过
- * @param {import('./types').ForecastChildRecord} sub - 子项记录
- * @returns {Promise<void>}
- */
- async approveChildRecord(sub) {
- /** @type {Vue & ForecastAuditComponent} */
- const self = /** @type {any} */ (this)
- try {
- await self.$confirm('确认通过此明细吗?', '审批确认', {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- type: 'success'
- })
- await self.submitChildApproval(sub, true)
- } catch (e) {
- // 用户取消或错误
- }
- },
- /**
- * 子表明细审批拒绝
- * @param {import('./types').ForecastChildRecord} sub - 子项记录
- * @returns {Promise<void>}
- */
- async rejectChildRecord(sub) {
- /** @type {Vue & ForecastAuditComponent} */
- const self = /** @type {any} */ (this)
- try {
- await self.$confirm('确认拒绝此明细吗?', '审批确认', {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- type: 'warning'
- })
- await self.submitChildApproval(sub, false)
- } catch (e) {
- // 用户取消或错误
- }
- },
- /**
- * 执行子表明细审批
- * @param {import('./types').ForecastChildRecord} sub - 子项记录
- * @param {boolean} isApprove - 是否通过
- * @returns {Promise<void>}
- */
- async submitChildApproval(sub, isApprove) {
- /** @type {Vue & ForecastAuditComponent} */
- const self = /** @type {any} */ (this)
- self.submitting = true
- const id = /** @type {string|number} */ (sub && sub.id)
- const forecastMainId = /** @type {string|number} */ (sub && sub.forecastMainId)
- if (id === '' || id === null || id === undefined || forecastMainId === '' || forecastMainId === null || forecastMainId === undefined) {
- self.submitting = false
- self.$message.error('子表或主表ID缺失,无法提交审批')
- return
- }
- const payload = {
- id,
- forecastMainId,
- approvalStatus: isApprove ? APPROVAL_STATUS.APPROVED : APPROVAL_STATUS.REJECTED
- }
- try {
- await approveSalesForecastSummaryParticulars(payload)
- self.$message.success('明细审批成功')
- await self.onLoad(self.page, self.query)
- } catch (e) {
- self.$message.error('明细审批失败')
- } finally {
- self.submitting = false
- }
- },
- /**
- * 从详情页面审批通过
- * @this {Vue & ForecastAuditComponent}
- * @returns {Promise<void>}
- */
- async approveFromDetail() {
- /** @type {Vue & ForecastAuditComponent} */
- const self = /** @type {any} */ (this)
- try {
- await self.$confirm('确认通过此预测申报吗?', '审批确认', {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- type: 'success'
- })
- self.detailDialogVisible = false
- self.performApproval(/** @type {ForecastRecord} */ (self.detailForm), true)
- } catch (e) {
- // 用户取消或错误
- }
- },
- /**
- * 从详情页面审批拒绝
- * @this {Vue & ForecastAuditComponent}
- * @returns {Promise<void>}
- */
- async rejectFromDetail() {
- /** @type {Vue & ForecastAuditComponent} */
- const self = /** @type {any} */ (this)
- try {
- await self.$confirm('确认拒绝此预测申报吗?', '审批确认', {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- type: 'warning'
- })
- self.detailDialogVisible = false
- self.performApproval(/** @type {ForecastRecord} */ (self.detailForm), false)
- } catch (e) {
- // 用户取消或错误
- }
- },
- /**
- * 提交审批(主表)
- * @this {Vue & ForecastAuditComponent}
- * @returns {Promise<void>}
- */
- async submitApproval() {
- /** @type {Vue & ForecastAuditComponent} */
- const self = /** @type {any} */ (this)
- self.submitting = true
- const approvalData = {
- id: /** @type {string|number} */ (self.approvalForm.id),
- approvalStatus: self.approvalForm.isApprove ? APPROVAL_STATUS.APPROVED : APPROVAL_STATUS.REJECTED
- }
- // id 为空保护
- if (approvalData.id === null || approvalData.id === undefined || approvalData.id === '') {
- self.submitting = false
- self.$message.error('审批记录ID缺失,无法提交')
- return
- }
- try {
- await approveSalesForecastSummary(approvalData)
- self.$message.success('审批操作成功')
- self.approvalDialogVisible = false
- await self.onLoad(self.page)
- } catch (e) {
- self.$message.error('审批操作失败')
- } finally {
- self.submitting = false
- }
- },
- /**
- * 取消审批
- * @this {Vue & ForecastAuditComponent}
- * @returns {void}
- */
- cancelApproval() {
- this.approvalDialogVisible = false
- this.approvalForm = {
- id: null,
- isApprove: true,
- }
- },
- /**
- * 搜索变化事件
- * @this {Vue & ForecastAuditComponent}
- * @param {Record<string, any>} params - 搜索参数
- * @param {() => void} done - 完成回调
- * @returns {void}
- */
- searchChange(params, done) {
- this.query = params
- this.onLoad(this.page, params)
- done()
- },
- /**
- * 搜索重置事件
- * @this {Vue & ForecastAuditComponent}
- * @returns {void}
- */
- searchReset() {
- this.query = {}
- this.onLoad(this.page)
- },
- /**
- * 刷新事件
- * @this {Vue & ForecastAuditComponent}
- * @returns {void}
- */
- refreshChange() {
- this.onLoad(this.page, this.query)
- },
- /**
- * 加载数据(使用销售预测主表分页)
- * @this {Vue & ForecastAuditComponent}
- * @param {import('./types').PageConfig} page - 分页配置
- * @param {Partial<{year: number, month: number, customerName: string}>} [params={}] - 查询参数
- */
- async onLoad(page, params = {}) {
- try {
- this.loading = true
- const queryParams = {
- year: params.year ?? this.query.year,
- month: params.month ?? this.query.month,
- customerName: params.customerName ?? this.query.customerName,
- // 补充:将“客户编码、审批状态”一并传给接口
- customerCode: params.customerCode ?? this.query.customerCode,
- approvalStatus: params.approvalStatus ?? this.query.approvalStatus
- }
- // 使用新分页接口(主表分页)
- const res = await getSalesForecastMainPage(
- page.currentPage,
- page.pageSize,
- queryParams
- )
- const { records = [], total = 0, current = 1, size = 10 } = (res.data && res.data.data) || {}
- // 处理ID为 BigInt 字符串,同时保留字符串ID以便传输
- const mapped = (records || []).map(rec => {
- const idStr = rec && rec.id != null ? String(rec.id) : ''
- const idBigint = safeBigInt(idStr)
- const subList = Array.isArray(rec.pcBladeSalesForecastSummaryList)
- ? rec.pcBladeSalesForecastSummaryList.map(sub => {
- const subIdStr = sub && sub.id != null ? String(sub.id) : ''
- const subIdBigint = safeBigInt(subIdStr)
- const fmIdStr = sub && sub.forecastMainId != null ? String(sub.forecastMainId) : idStr
- const fmIdBigint = safeBigInt(fmIdStr)
- return {
- ...sub,
- id: subIdStr,
- forecastMainId: fmIdStr,
- year: Number(sub.year),
- month: Number(sub.month),
- forecastQuantity: typeof sub.forecastQuantity === 'string' ? Number(sub.forecastQuantity) : Number(sub.forecastQuantity),
- idBigint: subIdBigint,
- forecastMainIdBigint: fmIdBigint,
- // 继承父级字段,保证子表新增列有值可展示
- approvalStatus: sub && sub.approvalStatus != null ? sub.approvalStatus : (rec && rec.approvalStatus != null ? rec.approvalStatus : APPROVAL_STATUS.PENDING),
- customerName: sub && sub.customerName != null ? sub.customerName : rec && rec.customerName,
- approvedName: sub && sub.approvedName != null ? sub.approvedName : rec && rec.approvedName,
- approvedTime: sub && sub.approvedTime != null ? sub.approvedTime : rec && rec.approvedTime
- }
- })
- : []
- const approvalStatus = /** @type {import('@/constants/forecast').ApprovalStatus} */ (rec.approvalStatus ?? APPROVAL_STATUS.PENDING)
- return { ...rec, id: idStr, idBigint, year: Number(rec.year), month: Number(rec.month), approvalStatus, pcBladeSalesForecastSummaryList: subList }
- })
- this.data = /** @type {import('./types').ForecastRecord[]} */ (mapped)
- this.page.total = Number(total)
- this.page.currentPage = Number(current)
- this.page.pageSize = Number(size)
- } catch (error) {
- // eslint-disable-next-line no-console
- console.error('加载列表失败', error)
- } finally {
- this.loading = false
- }
- }
- }
- }
|