Browse Source

feat(forecast-summary): 添加行级导出功能并优化表格列显示

yz 6 days ago
parent
commit
29ec4f5f13
2 changed files with 95 additions and 4 deletions
  1. 20 0
      src/views/forecast-summary/index.vue
  2. 75 4
      src/views/forecast-summary/summaryIndex.js

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

@@ -31,6 +31,16 @@
         </el-button>
       </template>
 
+      <!-- 行级操作列:导出按钮 -->
+      <template slot="menu" slot-scope="{row}">
+        <el-button
+          type="text"
+          size="mini"
+          :loading="rowExporting && rowExporting[String(row.idBigint || row.id)]"
+          @click.stop="handleExportByMainId(row)"
+        >导出</el-button>
+      </template>
+
       <!-- 自定义审批状态列 -->
       <template slot="approvalStatus" slot-scope="{row}">
         <el-tag
@@ -61,6 +71,16 @@
         <span>{{ row.approvedTime ? formatDateTime(row.approvedTime) : '-' }}</span>
       </template>
 
+      <!-- 自定义ID列:兼容 idBigint/id -->
+      <template slot="id" slot-scope="{row}">
+        <span>{{ row.idBigint || row.id }}</span>
+      </template>
+      
+      <!-- 自定义更新时间列 -->
+      <template slot="updateTime" slot-scope="{row}">
+        <span>{{ row.updateTime ? formatDateTime(row.updateTime) : '-' }}</span>
+      </template>
+      
       <!-- 行展开插槽 - 显示主表对应的子表(pcBladeSalesForecastSummaryList) -->
       <template slot="expand" slot-scope="{row}">
         <div class="order-expand-content">

+ 75 - 4
src/views/forecast-summary/summaryIndex.js

@@ -7,7 +7,7 @@
  * @typedef {import('./types').ForecastSummaryComponent} ForecastSummaryComponent
  */
 
-import { getSalesForecastMainList, getForecastSummaryDetail, exportSalesForecastTemplate, exportSalesForecastByMonth } from '@/api/forecast/forecast-summary'
+import { getSalesForecastMainList, getForecastSummaryDetail, exportSalesForecastTemplate, exportSalesForecastByMonth, exportSalesForecastByMainId } from '@/api/forecast/forecast-summary'
 import {
   APPROVAL_STATUS,
   APPROVAL_STATUS_CONFIG,
@@ -88,12 +88,26 @@ import { mapGetters } from 'vuex'
          addBtn: false,
          selection: false,
          dialogClickModal: false,
-         menu: false,
+         //  menu: true,
+         //  menuWidth: 120,
+         menu: true,
+         menuWidth: 160,
+         menuFixed: false,
          // 启用行展开,展示子表格
          expand: true,
          expandRowKeys: [],
          defaultExpandAll: false,
          column: [
+           // 新增:ID 列(放在序号后,年月前)
+           {
+             label: 'ID',
+             prop: 'id',
+             width: 140,
+             overHidden: false,
+             sortable: true,
+             // 兼容 idBigint/id 的展示
+             slot: true
+           },
            {
              label: '年月',
              prop: 'yearMonth',
@@ -172,6 +186,13 @@ import { mapGetters } from 'vuex'
              minWidth: 160,
              sortable: true,
              slot: true
+           },
+           {
+             label: '更新时间',
+             prop: 'updateTime',
+             minWidth: 160,
+             sortable: true,
+             slot: true
            }
          ]
        },
@@ -198,7 +219,6 @@ import { mapGetters } from 'vuex'
            { 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', minWidth: 100, type: 'select', dicData: APPROVAL_STATUS_OPTIONS, slot: true },
            { label: '客户名称', prop: 'customerName', minWidth: 160, overHidden: true },
            { label: '年份', prop: 'year', minWidth: 80 },
@@ -224,7 +244,10 @@ import { mapGetters } from 'vuex'
          month: [ { required: true, message: '请选择月份', trigger: 'change' } ]
        },
        /** 月份选项(用于模板渲染) */
-       monthOptions: MONTH_OPTIONS
+       monthOptions: MONTH_OPTIONS,
+
+       /** 行导出加载状态映射(按主表ID) */
+       rowExporting: {}
      }
    },
 
@@ -572,6 +595,54 @@ import { mapGetters } from 'vuex'
          console.error('获取明细失败:', e)
          return []
        }
+     },
+
+     /**
+      * 按主表ID导出当前行的销售预测汇总
+      * @param {SalesForecastMainRecord} row - 主表行记录
+      * @returns {Promise<void>}
+      */
+     async handleExportByMainId(row) {
+       try {
+         if (!row) return
+         const mainId = row.idBigint || row.id
+         if (!mainId) {
+           this.$message.warning('无法获取该行主键ID,导出失败')
+           return
+         }
+         // 标记该行导出中
+         this.$set(this.rowExporting, String(mainId), true)
+
+         const res = await exportSalesForecastByMainId(mainId)
+
+         // 处理文件名:优先从响应头解析,其次使用“客户名称_yyyy-MM.xlsx”
+         const disposition = res?.headers?.['content-disposition'] || res?.headers?.['Content-Disposition']
+         const ym = this.formatYearMonth(Number(row.year), Number(row.month))
+         const defaultName = `${row.customerName ? row.customerName + '_' : ''}${ym || ''}销售预测汇总.xlsx`
+         let filename = defaultName
+         if (disposition) {
+           const utf8Match = /filename\*=UTF-8''([^;\n]+)/i.exec(disposition)
+           const plainMatch = /filename="?([^;\n"]+)"?/i.exec(disposition)
+           const raw = utf8Match ? decodeURIComponent(utf8Match[1]) : (plainMatch ? plainMatch[1] : '')
+           if (raw) filename = raw
+         }
+
+         const blob = new Blob([res.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
+         const url = window.URL.createObjectURL(blob)
+         const a = document.createElement('a')
+         a.href = url
+         a.download = filename
+         document.body.appendChild(a)
+         a.click()
+         document.body.removeChild(a)
+         window.URL.revokeObjectURL(url)
+       } catch (e) {
+         console.error('按主表ID导出失败:', e)
+         this.$message.error('导出失败,请稍后重试')
+       } finally {
+         const key = String((row && (row.idBigint || row.id)) || '')
+         if (key) this.$set(this.rowExporting, key, false)
+       }
      }
    }
  }