Sfoglia il codice sorgente

feat(forecast-summary): 新增销售预测汇总页面功能

yz 3 mesi fa
parent
commit
02dd2b4f3c

+ 146 - 0
src/api/forecast/forecast-summary.js

@@ -0,0 +1,146 @@
+import request from '@/router/axios'
+
+/**
+ * 预测汇总查询参数类型定义
+ * @typedef {Object} ForecastSummaryQueryParams
+ * @property {number} [current=1] - 当前页码
+ * @property {number} [size=10] - 每页数量
+ * @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 {number} [approvalStatus] - 审批状态 0未审批 1已通过 2已拒绝
+ */
+
+/**
+ * 预测汇总数据项类型定义
+ * @typedef {Object} ForecastSummaryItem
+ * @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 {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 {string} pattern - 花纹
+ * @property {string} forecastQuantity - 预测数量
+ * @property {number} approvalStatus - 审批状态 0未审批 1已通过 2已拒绝
+ * @property {number|null} approvedBy - 审批人ID
+ * @property {string|null} approvedName - 审批人姓名
+ * @property {string|null} approvedTime - 审批时间
+ */
+
+/**
+ * 预测汇总分页响应数据类型定义
+ * @typedef {Object} ForecastSummaryPageResponse
+ * @property {ForecastSummaryItem[]} records - 预测汇总数据记录
+ * @property {number} total - 总记录数
+ * @property {number} size - 每页数量
+ * @property {number} current - 当前页码
+ * @property {Array} orders - 排序信息
+ * @property {boolean} optimizeCountSql - 是否优化count查询
+ * @property {boolean} hitCount - 是否命中count缓存
+ * @property {string|null} countId - count查询ID
+ * @property {number|null} maxLimit - 最大限制
+ * @property {boolean} searchCount - 是否查询count
+ * @property {number} pages - 总页数
+ */
+
+/**
+ * API响应数据类型定义
+ * @template T
+ * @typedef {Object} ApiResponse
+ * @property {ApiResponseData<T>} data
+ * @property {ApiResponseData<T>} data
+ * @property {string} statusText - HTTP状态文本 (来自AxiosResponse)
+ * @property {Object} headers - 响应头信息 (来自AxiosResponse)
+ * @property {string} [headers.content-type] - 内容类型
+ * @property {Object} config - 请求配置信息 (来自AxiosResponse)
+ * @property {string} [config.method] - 请求方法
+ * @property {string} [config.url] - 请求URL
+ * @property {Record<string, any>} [config.params] - 请求参数
+ * @property {Record<string, any>} [config.headers] - 请求头
+ */
+
+/**
+ * API响应内层data类型定义
+ * @template T
+ * @typedef {Object} ApiResponseData
+ * @property {number} code - 响应码
+ * @property {boolean} success - 是否成功
+ * @property {T} data - 响应数据
+ * @property {string} msg - 响应消息
+ */
+
+/**
+ * 获取预测汇总列表
+ * @param {number} [current=1] - 当前页码
+ * @param {number} [size=10] - 每页数量
+ * @param {ForecastSummaryQueryParams} [params={}] - 查询参数
+ * @returns {Promise<ApiResponse<ForecastSummaryPageResponse>>} 预测汇总列表响应
+ * @description 获取经销商销售预测汇总列表,支持多条件查询和分页
+ * @example
+ * // 获取第一页10条数据
+ * const result = await getForecastSummaryList(1, 10)
+ *
+ * // 按年月查询
+ * const result = await getForecastSummaryList(1, 10, { year: 2023, month: 8 })
+ *
+ * // 按客户查询
+ * const result = await getForecastSummaryList(1, 10, { customerId: 1002 })
+ *
+ * // 按审批状态查询
+ * const result = await getForecastSummaryList(1, 10, { approvalStatus: 2 })
+ */
+export const getForecastSummaryList = async (current = 1, size = 10, params = {}) => {
+  return request({
+    url: '/api/blade-factory/api/factory/forecast-summary',
+    method: 'get',
+    params: {
+      current,
+      size,
+      ...params
+    }
+  })
+}
+
+/**
+ * 获取预测汇总详情
+ * @param {string|number} forecastSummaryId - 预测汇总ID
+ * @returns {Promise<ApiResponse<ForecastSummaryItem>>} 预测汇总详情响应
+ * @description 根据ID获取预测汇总的详细信息
+ * @example
+ * const result = await getForecastSummaryDetail('1954819531796865026')
+ *
+ * // 处理响应数据
+ * if (result.success && result.code === 200) {
+ *   const summaryData = result.data
+ *   console.log('预测汇总详情:', summaryData)
+ * }
+ */
+export const getForecastSummaryDetail = async (forecastSummaryId) => {
+  return request({
+    url: `/api/blade-factory/api/factory/forecast-summary/${forecastSummaryId}`,
+    method: 'get'
+  })
+}

+ 152 - 0
src/constants/forecast.js

@@ -354,3 +354,155 @@ export function formatQuantity(value) {
 export function formatCurrency(value) {
   return formatNumber(value, NUMBER_FORMAT_OPTIONS.currency)
 }
+
+// ==================== 汇总模块相关常量 ====================
+
+/**
+ * 预测汇总查询参数类型定义
+ * @typedef {Object} ForecastSummaryQueryParams
+ * @property {number} [current=1] - 当前页码
+ * @property {number} [size=10] - 每页数量
+ * @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 {number} [approvalStatus] - 审批状态
+ */
+
+/**
+ * 预测汇总数据项类型定义
+ * @typedef {Object} ForecastSummaryItem
+ * @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 {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 {string} pattern - 花纹
+ * @property {string} forecastQuantity - 预测数量
+ * @property {number} approvalStatus - 审批状态 0未审批 1已通过 2已拒绝
+ * @property {number|null} approvedBy - 审批人ID
+ * @property {string|null} approvedName - 审批人姓名
+ * @property {string|null} approvedTime - 审批时间
+ */
+
+/**
+ * 预测汇总默认查询参数
+ * @readonly
+ * @type {ForecastSummaryQueryParams}
+ */
+export const DEFAULT_FORECAST_SUMMARY_QUERY = {
+  current: 1,
+  size: 10,
+  year: null,
+  month: null,
+  customerId: null,
+  customerCode: '',
+  customerName: '',
+  brandId: null,
+  brandCode: '',
+  brandName: '',
+  itemId: null,
+  itemCode: '',
+  itemName: '',
+  approvalStatus: null
+}
+
+/**
+ * 月份选项数组
+ * @readonly
+ * @type {Array<{label: string, value: number}>}
+ */
+export const MONTH_OPTIONS = [
+  { 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 }
+]
+
+/**
+ * 获取月份标签
+ * @param {number} month - 月份值
+ * @returns {string} 月份标签
+ */
+export function getMonthLabel(month) {
+  const monthOption = MONTH_OPTIONS.find(option => option.value === month)
+  return monthOption ? monthOption.label : `${month}月`
+}
+
+/**
+ * 格式化日期时间显示
+ * @param {string|null|undefined} dateTime - 日期时间字符串
+ * @returns {string} 格式化后的日期时间
+ */
+export function formatDateTime(dateTime) {
+  if (!dateTime) {
+    return '-'
+  }
+  try {
+    const date = new Date(dateTime)
+    if (isNaN(date.getTime())) {
+      return '-'
+    }
+    return date.toLocaleString('zh-CN', {
+      year: 'numeric',
+      month: '2-digit',
+      day: '2-digit',
+      hour: '2-digit',
+      minute: '2-digit',
+      second: '2-digit'
+    })
+  } catch (error) {
+    return '-'
+  }
+}
+
+/**
+ * 格式化年月显示
+ * @param {number} year - 年份
+ * @param {number} month - 月份
+ * @returns {string} 格式化后的年月
+ */
+export function formatYearMonth(year, month) {
+  if (!year || !month) {
+    return '-'
+  }
+  return `${year}年${month}月`
+}
+
+/**
+ * 判断是否为只读模式(汇总模块只展示,不允许编辑)
+ * @returns {boolean} 始终返回true,表示只读
+ */
+export function isReadOnlyMode() {
+  return true
+}

+ 28 - 1
src/router/views/index.js

@@ -343,7 +343,34 @@ export default [
                 },
                 component: () =>
                     import(
-                        /* webpackChunkName: "forecast-audit" */ "@/views/forecast-audit/index"
+                        /* webpackChunkName: "views" */ "@/views/forecast-audit/index"
+                    )
+            }
+        ]
+    },
+    {
+        path: "/forecast-summary",
+        component: Layout,
+        redirect: "/forecast-summary/index",
+        meta: {
+            icon: "el-icon-data-analysis",
+            title: "销售预测汇总",
+            keepAlive: true,
+            isAuth: true
+        },
+        children: [
+            {
+                path: "index",
+                name: "销售预测汇总",
+                meta: {
+                    keepAlive: true,
+                    isAuth: true,
+                    title: "销售预测汇总",
+                    icon: "el-icon-data-analysis"
+                },
+                component: () =>
+                    import(
+                        /* webpackChunkName: "views" */ "@/views/forecast-summary/index"
                     )
             }
         ]

+ 180 - 0
src/views/forecast-summary/index.scss

@@ -0,0 +1,180 @@
+/**
+ * 经销商销售预测汇总页面样式
+ * @description 定义预测汇总页面的样式规则
+ * @author System
+ * @since 2025-01-08
+ */
+
+.forecast-summary {
+  .el-tag {
+    &.el-tag--success {
+      background-color: #f0f9ff;
+      border-color: #67c23a;
+      color: #67c23a;
+    }
+
+    &.el-tag--warning {
+      background-color: #fdf6ec;
+      border-color: #e6a23c;
+      color: #e6a23c;
+    }
+
+    &.el-tag--danger {
+      background-color: #fef0f0;
+      border-color: #f56c6c;
+      color: #f56c6c;
+    }
+
+    &.el-tag--info {
+      background-color: #f4f4f5;
+      border-color: #909399;
+      color: #909399;
+    }
+  }
+
+  // 表格行样式
+  .avue-crud {
+    .el-table {
+      .el-table__row {
+        &:hover {
+          background-color: #f5f7fa;
+        }
+      }
+
+      // 表头样式
+      .el-table__header-wrapper {
+        .el-table__header {
+          th {
+            background-color: #fafafa;
+            color: #606266;
+            font-weight: 500;
+          }
+        }
+      }
+
+      // 单元格样式
+      .el-table__body-wrapper {
+        .el-table__body {
+          td {
+            padding: 8px 0;
+
+            .cell {
+              padding: 0 10px;
+              line-height: 1.5;
+            }
+          }
+        }
+      }
+    }
+
+    // 分页样式
+    .el-pagination {
+      margin-top: 20px;
+      text-align: right;
+
+      .el-pagination__total {
+        color: #606266;
+        font-weight: normal;
+      }
+
+      .el-pager {
+        .number {
+          &.active {
+            background-color: #409eff;
+            color: #fff;
+          }
+        }
+      }
+    }
+
+    // 搜索区域样式
+    .avue-crud__search {
+      background-color: #f8f9fa;
+      border: 1px solid #e9ecef;
+      border-radius: 4px;
+      padding: 15px;
+      margin-bottom: 15px;
+
+      .el-form-item {
+        margin-bottom: 10px;
+
+        .el-form-item__label {
+          color: #606266;
+          font-weight: 500;
+        }
+
+        .el-input,
+        .el-select {
+          width: 100%;
+        }
+      }
+
+      .avue-crud__search-btn {
+        text-align: right;
+        margin-top: 10px;
+
+        .el-button {
+          margin-left: 10px;
+        }
+      }
+    }
+  }
+
+  // 数字右对齐
+  .text-right {
+    text-align: right;
+  }
+
+  // 文本省略
+  .text-ellipsis {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+
+  // 状态标签样式增强
+  .status-tag {
+    font-weight: 500;
+    border-radius: 12px;
+    padding: 2px 8px;
+    font-size: 12px;
+  }
+
+  // 响应式设计
+  @media (max-width: 768px) {
+    .avue-crud {
+      .avue-crud__search {
+        .el-form-item {
+          .el-form-item__label {
+            width: 100% !important;
+            text-align: left !important;
+          }
+
+          .el-form-item__content {
+            margin-left: 0 !important;
+          }
+        }
+      }
+    }
+  }
+}
+
+// 全局样式覆盖
+.el-table {
+  .forecast-quantity-cell {
+    font-family: 'Courier New', monospace;
+    font-weight: 500;
+    color: #409eff;
+  }
+
+  .approval-status-cell {
+    .el-tag {
+      font-weight: 500;
+    }
+  }
+
+  .datetime-cell {
+    color: #909399;
+    font-size: 13px;
+  }
+}

+ 70 - 0
src/views/forecast-summary/index.vue

@@ -0,0 +1,70 @@
+<template>
+  <basic-container class="forecast-summary">
+    <avue-crud
+      ref="crud"
+      v-model="form"
+      :option="option"
+      :table-loading="loading"
+      :data="data"
+      :page.sync="page"
+      :permission="permissionList"
+      :before-open="beforeOpen"
+      @search-change="searchChange"
+      @search-reset="searchReset"
+      @selection-change="selectionChange"
+      @current-change="currentChange"
+      @size-change="sizeChange"
+      @refresh-change="refreshChange"
+      @on-load="onLoad"
+    >
+      <!-- 自定义审批状态列 -->
+      <template slot="approvalStatus" slot-scope="{row}">
+        <el-tag
+          :type="getApprovalStatusConfig(row.approvalStatus).type"
+          size="small"
+        >
+          {{ getApprovalStatusConfig(row.approvalStatus).label }}
+        </el-tag>
+      </template>
+
+      <!-- 自定义预测数量列 -->
+      <template slot="forecastQuantity" slot-scope="{row}">
+        <span>{{ formatNumber(row.forecastQuantity) }}</span>
+      </template>
+
+      <!-- 自定义年月列 -->
+      <template slot="yearMonth" slot-scope="{row}">
+        <span>{{ formatYearMonth(row.year, row.month) }}</span>
+      </template>
+
+      <!-- 自定义创建时间列 -->
+      <template slot="createTime" slot-scope="{row}">
+        <span>{{ formatDateTime(row.createTime) }}</span>
+      </template>
+
+      <!-- 自定义审批时间列 -->
+      <template slot="approvedTime" slot-scope="{row}">
+        <span>{{ row.approvedTime ? formatDateTime(row.approvedTime) : '-' }}</span>
+      </template>
+    </avue-crud>
+  </basic-container>
+</template>
+
+<script>
+import summaryIndex from './summaryIndex'
+
+/**
+ * 经销商销售预测汇总页面组件
+ * @description 展示经销商销售预测汇总数据,支持查询、分页等功能
+ * @author System
+ * @since 2025-01-08
+ */
+export default {
+  name: 'ForecastSummary',
+  mixins: [summaryIndex]
+}
+</script>
+
+<style lang="scss">
+@import './index.scss';
+</style>

+ 425 - 0
src/views/forecast-summary/summaryIndex.js

@@ -0,0 +1,425 @@
+/**
+ * @typedef {import('@/api/forecast/forecast-summary').ForecastSummaryItem} ForecastSummaryItem
+ * @typedef {import('@/api/forecast/forecast-summary').ApiResponse} ApiResponse
+ * @typedef {import('@/api/forecast/forecast-summary').ForecastSummaryQueryParams} ForecastSummaryQueryParams
+ * @typedef {import('@/api/forecast/forecast-summary').ForecastSummaryPageResponse} ForecastSummaryPageResponse
+ */
+
+import { getForecastSummaryList, getForecastSummaryDetail } from '@/api/forecast/forecast-summary'
+import {
+  APPROVAL_STATUS,
+  APPROVAL_STATUS_CONFIG,
+  APPROVAL_STATUS_OPTIONS,
+  DEFAULT_FORECAST_SUMMARY_QUERY,
+  MONTH_OPTIONS,
+  getMonthLabel,
+  formatDateTime,
+  formatYearMonth,
+  formatNumber,
+  isReadOnlyMode
+} from '@/constants/forecast'
+import { mapGetters } from 'vuex'
+
+/**
+ * 经销商销售预测汇总页面业务逻辑混入
+ * @description 提供预测汇总的查询、分页、表格配置等功能
+ * @author System
+ * @since 2025-01-08
+ */
+export default {
+  data() {
+    return {
+      /**
+       * 表单数据
+       * @type {Object}
+       */
+      form: {},
+
+      /**
+       * 查询参数
+       * @type {Object}
+       */
+      query: { ...DEFAULT_FORECAST_SUMMARY_QUERY },
+
+      /**
+       * 加载状态
+       * @type {boolean}
+       */
+      loading: true,
+
+      /**
+       * 分页配置
+       * @type {Object}
+       */
+      page: {
+        pageSize: 10,
+        currentPage: 1,
+        total: 0
+      },
+
+      /**
+       * 表格数据
+       * @type {Array<Object>}
+       */
+      data: [],
+
+      /**
+       * 选中的行数据
+       * @type {Array<Object>}
+       */
+      selectionList: [],
+
+      /**
+       * avue表格配置
+       * @type {Object}
+       */
+      option: {
+        height: 'auto',
+        calcHeight: 30,
+        tip: false,
+        searchShow: true,
+        searchMenuSpan: 6,
+        border: true,
+        index: true,
+        viewBtn: false,
+        editBtn: false,
+        delBtn: false,
+        addBtn: false,
+        selection: true,
+        dialogClickModal: false,
+        column: [
+          {
+            label: '年月',
+            prop: 'yearMonth',
+            minWidth: 100,
+            sortable: true,
+            slot: true,
+            search: true,
+            searchSpan: 12,
+            searchslot: true,
+            hide: false
+          },
+          {
+            label: '年份',
+            prop: 'year',
+            minWidth: 80,
+            type: 'number',
+            search: true,
+            searchSpan: 12,
+            hide: true
+          },
+          {
+            label: '月份',
+            prop: 'month',
+            minWidth: 80,
+            type: 'select',
+            dicData: MONTH_OPTIONS,
+            search: true,
+            searchSpan: 12,
+            hide: true
+          },
+          {
+            label: '客户编码',
+            prop: 'customerCode',
+            minWidth: 120,
+            search: true,
+            searchSpan: 12
+          },
+          {
+            label: '客户名称',
+            prop: 'customerName',
+            minWidth: 180,
+            search: true,
+            searchSpan: 12,
+            overHidden: true
+          },
+          {
+            label: '品牌编码',
+            prop: 'brandCode',
+            minWidth: 120,
+            search: true,
+            searchSpan: 12
+          },
+          {
+            label: '品牌名称',
+            prop: 'brandName',
+            minWidth: 120,
+            search: true,
+            searchSpan: 12
+          },
+          {
+            label: '物料编码',
+            prop: 'itemCode',
+            minWidth: 140,
+            search: true,
+            searchSpan: 12
+          },
+          {
+            label: '物料名称',
+            prop: 'itemName',
+            minWidth: 200,
+            search: true,
+            searchSpan: 12,
+            overHidden: true
+          },
+          {
+            label: '规格',
+            prop: 'specs',
+            minWidth: 120,
+            overHidden: true
+          },
+          {
+            label: '花纹',
+            prop: 'pattern',
+            minWidth: 100,
+            overHidden: true
+          },
+          {
+            label: '预测数量',
+            prop: 'forecastQuantity',
+            minWidth: 120,
+            sortable: true,
+            slot: true,
+            align: 'right'
+          },
+          {
+            label: '审批状态',
+            prop: 'approvalStatus',
+            minWidth: 100,
+            type: 'select',
+            dicData: APPROVAL_STATUS_OPTIONS,
+            search: true,
+            searchSpan: 12,
+            slot: true
+          },
+          {
+            label: '审批人',
+            prop: 'approvedName',
+            minWidth: 100,
+            overHidden: true
+          },
+          {
+            label: '审批时间',
+            prop: 'approvedTime',
+            minWidth: 160,
+            sortable: true,
+            slot: true
+          },
+          {
+            label: '创建时间',
+            prop: 'createTime',
+            minWidth: 160,
+            sortable: true,
+            slot: true
+          }
+        ]
+      }
+    }
+  },
+
+  computed: {
+    ...mapGetters(['permission']),
+
+    /**
+     * 权限列表
+     * @returns {Object} 权限配置对象
+     */
+    permissionList() {
+      return {
+        addBtn: false,
+        viewBtn: true,
+        editBtn: false,
+        delBtn: false
+      }
+    },
+
+    /**
+     * 是否为只读模式
+     * @returns {boolean} 只读模式标识
+     */
+    readOnlyMode() {
+      return isReadOnlyMode()
+    }
+  },
+
+  /**
+   * 组件创建时初始化数据
+   */
+  created() {
+    this.onLoad(this.page)
+  },
+
+  methods: {
+    /**
+     * 获取审批状态配置
+     * @param {number} status - 审批状态值
+     * @returns {Object} 状态配置对象
+     */
+    getApprovalStatusConfig(status) {
+      const config = APPROVAL_STATUS_CONFIG[status]
+      if (!config) {
+        return {
+          label: '未知状态',
+          type: 'info'
+        }
+      }
+      return {
+        label: config.label,
+        type: config.type
+      }
+    },
+
+    /**
+     * 格式化数字显示
+     * @param {string|number} value - 数字值
+     * @returns {string} 格式化后的数字字符串
+     */
+    formatNumber(value) {
+      return formatNumber(value)
+    },
+
+    /**
+     * 格式化日期时间
+     * @param {string} dateTime - 日期时间字符串
+     * @returns {string} 格式化后的日期时间
+     */
+    formatDateTime(dateTime) {
+      return formatDateTime(dateTime)
+    },
+
+    /**
+     * 格式化年月显示
+     * @param {number} year - 年份
+     * @param {number} month - 月份
+     * @returns {string} 格式化后的年月字符串
+     */
+    formatYearMonth(year, month) {
+      return formatYearMonth(year, month)
+    },
+
+    /**
+     * 搜索条件变化处理
+     * @param {Object} params - 搜索参数
+     * @param {Function} done - 完成回调
+     */
+    searchChange(params, done) {
+      this.query = {
+        ...this.query,
+        ...params
+      }
+      this.onLoad(this.page, params)
+      done()
+    },
+
+    /**
+     * 搜索重置处理
+     */
+    searchReset() {
+      this.query = { ...DEFAULT_FORECAST_SUMMARY_QUERY }
+      this.onLoad(this.page)
+    },
+
+    /**
+     * 选择变化处理
+     * @param {Array<Object>} selection - 选中的行数据
+     */
+    selectionChange(selection) {
+      this.selectionList = selection
+    },
+
+    /**
+     * 当前页变化处理
+     * @param {number} currentPage - 当前页码
+     */
+    currentChange(currentPage) {
+      this.page.currentPage = currentPage
+    },
+
+    /**
+     * 页大小变化处理
+     * @param {number} pageSize - 页大小
+     */
+    sizeChange(pageSize) {
+      this.page.pageSize = pageSize
+    },
+
+    /**
+     * 刷新处理
+     */
+    refreshChange() {
+      this.onLoad(this.page, this.query)
+    },
+
+    /**
+     * 弹窗打开前处理
+     * @param {Function} done - 完成回调
+     * @param {string} type - 操作类型
+     */
+    beforeOpen(done, type) {
+      // 由于是只读模式,不需要处理弹窗
+      done()
+    },
+
+    /**
+     * 加载数据
+     * @param {Object} page - 分页参数
+     * @param {Object} params - 查询参数
+     * @returns {Promise<void>}
+     */
+    async onLoad(page, params = {}) {
+      this.loading = true
+      try {
+        const queryParams = {
+          ...this.query,
+          ...params
+        }
+
+        const response = await getForecastSummaryList(
+          page.currentPage || 1,
+          page.pageSize || 10,
+          queryParams
+        )
+
+        if (response && response.data && response.data.code === 200) {
+          const { records, total, current, size } = response.data.data
+          this.data = records || []
+          this.page.total = total || 0
+          this.page.currentPage = current || 1
+          this.page.pageSize = size || 10
+        } else {
+          this.$message.error(response?.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
+      }
+    },
+
+    /**
+     * 获取预测汇总详情
+     * @param {string|number} id - 预测汇总ID
+     * @returns {Promise<ForecastSummaryItem|null>} 详情数据
+     */
+    async getForecastSummaryDetail(id) {
+      try {
+        const response = await getForecastSummaryDetail(id)
+        if (response && response.data && response.data.code === 200) {
+          return response.data.data
+        } else {
+          this.$message.error(response.data.msg || '获取详情失败')
+          return null
+        }
+      } catch (error) {
+        console.error('获取预测汇总详情失败:', error)
+        this.$message.error('获取详情失败,请稍后重试')
+        return null
+      }
+    }
+  }
+}