|
|
@@ -0,0 +1,563 @@
|
|
|
+import { getForecastList, updateForecast } from '@/api/forecast'
|
|
|
+import {
|
|
|
+ APPROVAL_STATUS,
|
|
|
+ APPROVAL_STATUS_OPTIONS,
|
|
|
+ getApprovalStatusLabel,
|
|
|
+ getApprovalStatusType,
|
|
|
+ canApprove,
|
|
|
+ canReject,
|
|
|
+ formatNumber
|
|
|
+} from '@/constants/forecast'
|
|
|
+import { mapGetters } from 'vuex'
|
|
|
+
|
|
|
+/**
|
|
|
+ * 预测申报数据项类型定义
|
|
|
+ * @typedef {Object} ForecastItem
|
|
|
+ * @property {string} id - 预测申报ID
|
|
|
+ * @property {string} createUser - 创建用户ID
|
|
|
+ * @property {string} createDept - 创建部门ID
|
|
|
+ * @property {string} createTime - 创建时间
|
|
|
+ * @property {string} updateUser - 更新用户ID
|
|
|
+ * @property {string} updateTime - 更新时间
|
|
|
+ * @property {number} status - 状态
|
|
|
+ * @property {number} isDeleted - 是否删除
|
|
|
+ * @property {string} forecastCode - 预测编码
|
|
|
+ * @property {number} year - 年份
|
|
|
+ * @property {number} month - 月份
|
|
|
+ * @property {number} customerId - 客户ID
|
|
|
+ * @property {string} customerCode - 客户编码
|
|
|
+ * @property {string} customerName - 客户名称
|
|
|
+ * @property {number} brandId - 品牌ID
|
|
|
+ * @property {string} brandCode - 品牌编码
|
|
|
+ * @property {string} brandName - 品牌名称
|
|
|
+ * @property {number} itemId - 物料ID
|
|
|
+ * @property {string} itemCode - 物料编码
|
|
|
+ * @property {string} itemName - 物料名称
|
|
|
+ * @property {string} specs - 规格
|
|
|
+ * @property {number} forecastQuantity - 预测数量
|
|
|
+ * @property {number} currentInventory - 当前库存
|
|
|
+ * @property {number} approvalStatus - 审批状态
|
|
|
+ * @property {number|null} approvedBy - 审批人ID
|
|
|
+ * @property {string|null} approvedName - 审批人姓名
|
|
|
+ * @property {string|null} approvedTime - 审批时间
|
|
|
+ * @property {string|null} approvalComment - 审批意见
|
|
|
+ */
|
|
|
+
|
|
|
+/**
|
|
|
+ * 分页配置类型定义
|
|
|
+ * @typedef {Object} PageConfig
|
|
|
+ * @property {number} pageSize - 每页数量
|
|
|
+ * @property {number} currentPage - 当前页码
|
|
|
+ * @property {number} total - 总数量
|
|
|
+ */
|
|
|
+
|
|
|
+/**
|
|
|
+ * 审批表单数据类型定义
|
|
|
+ * @typedef {Object} ApprovalFormData
|
|
|
+ * @property {string} approvalComment - 审批意见
|
|
|
+ * @property {boolean} isApprove - 是否为通过审批
|
|
|
+ * @property {ForecastItem|null} currentRecord - 当前审批的记录
|
|
|
+ */
|
|
|
+
|
|
|
+/**
|
|
|
+ * 权限配置类型定义
|
|
|
+ * @typedef {Object} PermissionConfig
|
|
|
+ * @property {boolean} addBtn - 添加按钮权限
|
|
|
+ * @property {boolean} viewBtn - 查看按钮权限
|
|
|
+ * @property {boolean} editBtn - 编辑按钮权限
|
|
|
+ * @property {boolean} delBtn - 删除按钮权限
|
|
|
+ */
|
|
|
+
|
|
|
+/**
|
|
|
+ * 销售预测审核页面业务逻辑混入
|
|
|
+ * @description 提供预测申报的审核功能,包括列表展示、详情查看、审批操作等
|
|
|
+ */
|
|
|
+export default {
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ /** @type {Object} 表单数据 */
|
|
|
+ form: {},
|
|
|
+ /** @type {Object} 查询参数 */
|
|
|
+ query: {},
|
|
|
+ /** @type {boolean} 表格加载状态 */
|
|
|
+ loading: true,
|
|
|
+ /** @type {boolean} 表单提交状态 */
|
|
|
+ submitting: false,
|
|
|
+ /** @type {boolean} 审批中状态 */
|
|
|
+ approving: false,
|
|
|
+ /** @type {boolean} 拒绝中状态 */
|
|
|
+ rejecting: false,
|
|
|
+ /** @type {boolean} 详情弹窗显示状态 */
|
|
|
+ detailDialogVisible: false,
|
|
|
+ /** @type {boolean} 审批确认弹窗显示状态 */
|
|
|
+ approvalDialogVisible: false,
|
|
|
+ /** @type {PageConfig} 分页配置 */
|
|
|
+ page: {
|
|
|
+ pageSize: 10,
|
|
|
+ currentPage: 1,
|
|
|
+ total: 0
|
|
|
+ },
|
|
|
+ /** @type {Array<ForecastItem>} 表格数据 */
|
|
|
+ data: [],
|
|
|
+ /** @type {ForecastItem} 详情表单数据 */
|
|
|
+ detailForm: {},
|
|
|
+ /** @type {ApprovalFormData} 审批表单数据 */
|
|
|
+ approvalForm: {
|
|
|
+ /** @type {string} 审批意见 */
|
|
|
+ approvalComment: '',
|
|
|
+ /** @type {boolean} 是否为通过审批 */
|
|
|
+ isApprove: true,
|
|
|
+ /** @type {ForecastItem|null} 当前审批的记录 */
|
|
|
+ currentRecord: null
|
|
|
+ },
|
|
|
+ /** @type {Object} avue表格配置 */
|
|
|
+ option: {
|
|
|
+ height: 'auto',
|
|
|
+ calcHeight: 30,
|
|
|
+ tip: false,
|
|
|
+ searchShow: true,
|
|
|
+ searchMenuSpan: 6,
|
|
|
+ border: true,
|
|
|
+ index: true,
|
|
|
+ viewBtn: false,
|
|
|
+ selection: false,
|
|
|
+ addBtn: false,
|
|
|
+ editBtn: false,
|
|
|
+ delBtn: false,
|
|
|
+ excelBtn: false,
|
|
|
+ columnBtn: false,
|
|
|
+ refreshBtn: true,
|
|
|
+ dialogClickModal: false,
|
|
|
+ menu: true,
|
|
|
+ menuWidth: 280,
|
|
|
+ column: [
|
|
|
+ {
|
|
|
+ label: '预测编码',
|
|
|
+ prop: 'forecastCode',
|
|
|
+ search: true,
|
|
|
+ searchSpan: 6,
|
|
|
+ width: 150,
|
|
|
+ overHidden: 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: 'customerName',
|
|
|
+ search: true,
|
|
|
+ searchSpan: 6,
|
|
|
+ width: 180,
|
|
|
+ overHidden: true
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '客户编码',
|
|
|
+ prop: 'customerCode',
|
|
|
+ search: false,
|
|
|
+ width: 150,
|
|
|
+ overHidden: true
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '品牌名称',
|
|
|
+ prop: 'brandName',
|
|
|
+ search: false,
|
|
|
+ width: 120,
|
|
|
+ overHidden: true
|
|
|
+ },
|
|
|
+ {
|
|
|
+ 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: '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
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ /** @type {Object} 审批表单验证规则 */
|
|
|
+ approvalFormRules: {
|
|
|
+ approvalComment: [
|
|
|
+ {
|
|
|
+ validator: (rule, value, callback) => {
|
|
|
+ if (!this.approvalForm.isApprove && (!value || value.trim() === '')) {
|
|
|
+ callback(new Error('拒绝审批时必须填写拒绝原因'))
|
|
|
+ } else if (value && value.length > 500) {
|
|
|
+ callback(new Error('审批意见不能超过500个字符'))
|
|
|
+ } else {
|
|
|
+ callback()
|
|
|
+ }
|
|
|
+ },
|
|
|
+ trigger: 'blur'
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ computed: {
|
|
|
+ ...mapGetters(['permission']),
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 权限列表
|
|
|
+ * @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
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 审批弹窗标题
|
|
|
+ * @returns {string} 弹窗标题文本
|
|
|
+ */
|
|
|
+ approvalDialogTitle() {
|
|
|
+ return this.approvalForm.isApprove ? '审批通过确认' : '审批拒绝确认'
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ created() {
|
|
|
+ // 初始化时不需要加载额外数据,只等待表格自动加载
|
|
|
+ },
|
|
|
+
|
|
|
+ methods: {
|
|
|
+ /**
|
|
|
+ * 获取审批状态标签
|
|
|
+ * @param {number} status - 审批状态值
|
|
|
+ * @returns {string} 状态标签文本
|
|
|
+ */
|
|
|
+ getApprovalStatusLabel(status) {
|
|
|
+ return getApprovalStatusLabel(status)
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取审批状态类型
|
|
|
+ * @param {number} status - 审批状态值
|
|
|
+ * @returns {string} 状态类型
|
|
|
+ */
|
|
|
+ getApprovalStatusType(status) {
|
|
|
+ return getApprovalStatusType(status)
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断是否可以审批通过
|
|
|
+ * @param {ForecastItem} row - 数据行对象
|
|
|
+ * @returns {boolean} 是否可以审批通过
|
|
|
+ */
|
|
|
+ canApprove(row) {
|
|
|
+ return canApprove(row.approvalStatus)
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断是否可以审批拒绝
|
|
|
+ * @param {ForecastItem} row - 数据行对象
|
|
|
+ * @returns {boolean} 是否可以审批拒绝
|
|
|
+ */
|
|
|
+ canReject(row) {
|
|
|
+ return canReject(row.approvalStatus)
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 格式化数字显示
|
|
|
+ * @param {number|null|undefined} value - 数值
|
|
|
+ * @returns {string} 格式化后的字符串
|
|
|
+ */
|
|
|
+ formatNumber(value) {
|
|
|
+ return formatNumber(value)
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查看详情
|
|
|
+ * @param {ForecastItem} row - 数据行对象
|
|
|
+ * @param {number} index - 行索引
|
|
|
+ * @returns {void}
|
|
|
+ */
|
|
|
+ viewDetail(row, index) {
|
|
|
+ this.detailForm = { ...row }
|
|
|
+ this.detailDialogVisible = true
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 审批通过记录
|
|
|
+ * @param {ForecastItem} row - 数据行对象
|
|
|
+ * @param {number} index - 行索引
|
|
|
+ * @returns {void}
|
|
|
+ */
|
|
|
+ approveRecord(row, index) {
|
|
|
+ this.approvalForm = {
|
|
|
+ approvalComment: '',
|
|
|
+ isApprove: true,
|
|
|
+ currentRecord: { ...row }
|
|
|
+ }
|
|
|
+ this.approvalDialogVisible = true
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 审批拒绝记录
|
|
|
+ * @param {ForecastItem} row - 数据行对象
|
|
|
+ * @param {number} index - 行索引
|
|
|
+ * @returns {void}
|
|
|
+ */
|
|
|
+ rejectRecord(row, index) {
|
|
|
+ this.approvalForm = {
|
|
|
+ approvalComment: '',
|
|
|
+ isApprove: false,
|
|
|
+ currentRecord: { ...row }
|
|
|
+ }
|
|
|
+ this.approvalDialogVisible = true
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从详情页面审批通过
|
|
|
+ * @returns {void}
|
|
|
+ */
|
|
|
+ approveFromDetail() {
|
|
|
+ this.approvalForm = {
|
|
|
+ approvalComment: '',
|
|
|
+ isApprove: true,
|
|
|
+ currentRecord: { ...this.detailForm }
|
|
|
+ }
|
|
|
+ this.detailDialogVisible = false
|
|
|
+ this.approvalDialogVisible = true
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从详情页面审批拒绝
|
|
|
+ * @returns {void}
|
|
|
+ */
|
|
|
+ rejectFromDetail() {
|
|
|
+ this.approvalForm = {
|
|
|
+ approvalComment: '',
|
|
|
+ isApprove: false,
|
|
|
+ currentRecord: { ...this.detailForm }
|
|
|
+ }
|
|
|
+ this.detailDialogVisible = false
|
|
|
+ this.approvalDialogVisible = true
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 确认审批操作
|
|
|
+ * @returns {Promise<void>}
|
|
|
+ * @throws {Error} 当审批操作失败时抛出错误
|
|
|
+ */
|
|
|
+ async confirmApproval() {
|
|
|
+ try {
|
|
|
+ // 验证表单
|
|
|
+ await this.$refs.approvalForm.validate()
|
|
|
+
|
|
|
+ this.submitting = true
|
|
|
+
|
|
|
+ const { currentRecord, isApprove, approvalComment } = this.approvalForm
|
|
|
+
|
|
|
+ // 构建更新数据
|
|
|
+ const updateData = {
|
|
|
+ ...currentRecord,
|
|
|
+ approvalStatus: isApprove ? APPROVAL_STATUS.APPROVED : APPROVAL_STATUS.REJECTED,
|
|
|
+ approvalComment: approvalComment || null,
|
|
|
+ approvedBy: this.$store.getters.userInfo.user_id,
|
|
|
+ approvedName: this.$store.getters.userInfo.real_name,
|
|
|
+ approvedTime: new Date().toISOString().replace('T', ' ').substring(0, 19)
|
|
|
+ }
|
|
|
+
|
|
|
+ const res = await updateForecast(updateData)
|
|
|
+
|
|
|
+ if (res.data && res.data.success) {
|
|
|
+ this.$message.success(`审批${isApprove ? '通过' : '拒绝'}成功`)
|
|
|
+ this.approvalDialogVisible = false
|
|
|
+ this.onLoad(this.page) // 重新加载数据
|
|
|
+ } else {
|
|
|
+ throw new Error(res.data?.msg || '审批操作失败')
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('审批操作失败:', error)
|
|
|
+ if (error.message && error.message !== 'validation failed') {
|
|
|
+ this.$message.error(error.message)
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ this.submitting = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 搜索变化事件
|
|
|
+ * @param {Object} params - 搜索参数
|
|
|
+ * @param {Function} done - 完成回调
|
|
|
+ * @returns {void}
|
|
|
+ */
|
|
|
+ searchChange(params, done) {
|
|
|
+ this.query = params
|
|
|
+ this.onLoad(this.page, params)
|
|
|
+ done()
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 搜索重置事件
|
|
|
+ * @returns {void}
|
|
|
+ */
|
|
|
+ searchReset() {
|
|
|
+ this.query = {}
|
|
|
+ this.onLoad(this.page)
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 刷新事件
|
|
|
+ * @returns {void}
|
|
|
+ */
|
|
|
+ refreshChange() {
|
|
|
+ this.onLoad(this.page, this.query)
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 加载数据
|
|
|
+ * @param {PageConfig} page - 分页参数
|
|
|
+ * @param {Object} [params={}] - 查询参数
|
|
|
+ * @returns {Promise<void>}
|
|
|
+ * @throws {Error} 当数据加载失败时抛出错误
|
|
|
+ */
|
|
|
+ async onLoad(page, params = {}) {
|
|
|
+ try {
|
|
|
+ this.loading = true
|
|
|
+
|
|
|
+ const queryParams = {
|
|
|
+ ...params,
|
|
|
+ // 只显示需要审核的数据(可以根据业务需求调整)
|
|
|
+ // approvalStatus: APPROVAL_STATUS.PENDING
|
|
|
+ }
|
|
|
+
|
|
|
+ const res = await getForecastList(page.currentPage, page.pageSize, queryParams)
|
|
|
+
|
|
|
+ if (res.data && res.data.success) {
|
|
|
+ const { records, total } = res.data.data
|
|
|
+ this.data = records || []
|
|
|
+ this.page.total = total || 0
|
|
|
+ } else {
|
|
|
+ throw new Error(res.data?.msg || '获取数据失败')
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载数据失败:', error)
|
|
|
+ this.$message.error(error.message || '加载数据失败')
|
|
|
+ this.data = []
|
|
|
+ this.page.total = 0
|
|
|
+ } finally {
|
|
|
+ this.loading = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 页码变化
|
|
|
+ * @param {number} currentPage - 当前页码
|
|
|
+ * @returns {void}
|
|
|
+ */
|
|
|
+ currentChange(currentPage) {
|
|
|
+ this.page.currentPage = currentPage
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 页大小变化
|
|
|
+ * @param {number} pageSize - 页大小
|
|
|
+ * @returns {void}
|
|
|
+ */
|
|
|
+ sizeChange(pageSize) {
|
|
|
+ this.page.pageSize = pageSize
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|