|
@@ -2,6 +2,7 @@ import { getForecastList, addForecast, updateForecast } from '@/api/forecast'
|
|
|
import { getCustomerList } from '@/api/common'
|
|
|
import { getItemList } from '@/api/common'
|
|
|
import { getSalesForecastMainList } from '@/api/forecast/forecast-summary'
|
|
|
+import { exportUserSalesForecastByMonth } from '@/api/forecast/forecast-summary'
|
|
|
import {
|
|
|
APPROVAL_STATUS,
|
|
|
APPROVAL_STATUS_CONFIG,
|
|
@@ -214,6 +215,35 @@ export default {
|
|
|
{ label: '预测数量', prop: 'forecastQuantity', minWidth: 120, align: 'right', slot: true }
|
|
|
]
|
|
|
},
|
|
|
+ // 导出(按年月)选择弹层状态与表单
|
|
|
+ exportDialogVisible: false,
|
|
|
+ exportLoading: false,
|
|
|
+ exportForm: {
|
|
|
+ year: '',
|
|
|
+ month: ''
|
|
|
+ },
|
|
|
+ exportFormRules: {
|
|
|
+ year: [
|
|
|
+ { required: true, message: '请选择年份', trigger: 'change' }
|
|
|
+ ],
|
|
|
+ month: [
|
|
|
+ { required: true, message: '请选择月份', trigger: 'change' }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ monthOptions: [
|
|
|
+ { 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 }
|
|
|
+ ],
|
|
|
/** @type {Record<string, Array<ValidationRule>>} 表单验证规则 */
|
|
|
formRules: FORECAST_FORM_RULES
|
|
|
}
|
|
@@ -260,6 +290,7 @@ export default {
|
|
|
},
|
|
|
|
|
|
methods: {
|
|
|
+
|
|
|
/**
|
|
|
* 检查是否可以编辑
|
|
|
* @param {ForecastRecord} row - 预测记录行数据
|
|
@@ -747,6 +778,92 @@ export default {
|
|
|
this.dialogVisible = false
|
|
|
},
|
|
|
|
|
|
+ /** 打开导出弹窗(按年月) */
|
|
|
+ openExportDialog() {
|
|
|
+ const now = new Date()
|
|
|
+ // 仅在为空时设置默认年/月,避免覆盖用户选择
|
|
|
+ if (!this.exportForm.year) this.exportForm.year = String(now.getFullYear())
|
|
|
+ if (!this.exportForm.month) this.exportForm.month = now.getMonth() + 1
|
|
|
+ this.exportDialogVisible = true
|
|
|
+ },
|
|
|
+
|
|
|
+ /** 确认导出(按年月) */
|
|
|
+ async confirmExportByMonth() {
|
|
|
+ if (!this.$refs || !this.$refs.exportFormRef) {
|
|
|
+ // 若未使用校验表单,直接做基础校验
|
|
|
+ if (!this.exportForm.year || !this.exportForm.month) {
|
|
|
+ this.$message.warning('请选择年份和月份')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const doExport = async () => {
|
|
|
+ try {
|
|
|
+ this.exportLoading = true
|
|
|
+ const year = this.exportForm.year
|
|
|
+ const month = this.exportForm.month
|
|
|
+ const res = await exportUserSalesForecastByMonth(year, month)
|
|
|
+ const blob = res && (res.data || res)
|
|
|
+ const filename = this.getExportFilenameFromResponse(res, year, month)
|
|
|
+ this.downloadBlob(blob, filename)
|
|
|
+ this.$message.success('导出成功')
|
|
|
+ this.exportDialogVisible = false
|
|
|
+ } catch (e) {
|
|
|
+ console.error('导出失败:', e)
|
|
|
+ this.$message.error('导出失败,请稍后重试')
|
|
|
+ } finally {
|
|
|
+ this.exportLoading = false
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this.$refs && this.$refs.exportFormRef && this.$refs.exportFormRef.validate) {
|
|
|
+ this.$refs.exportFormRef.validate(valid => {
|
|
|
+ if (valid) doExport()
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ await doExport()
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /** 从响应头解析导出文件名,失败则回退默认名 */
|
|
|
+ getExportFilenameFromResponse(response, year, month) {
|
|
|
+ try {
|
|
|
+ const headers = response && response.headers ? response.headers : {}
|
|
|
+ const cd = headers['content-disposition'] || headers['Content-Disposition'] || ''
|
|
|
+ // 优先解析 RFC 5987 扩展写法
|
|
|
+ const starMatch = /filename\*=([^']*)''([^;\n]+)/i.exec(cd)
|
|
|
+ if (starMatch && starMatch[2]) {
|
|
|
+ return decodeURIComponent(starMatch[2])
|
|
|
+ }
|
|
|
+ // 常规 filename="..."
|
|
|
+ const normalMatch = /filename=\"?([^\";\n]+)\"?/i.exec(cd)
|
|
|
+ if (normalMatch && normalMatch[1]) {
|
|
|
+ return decodeURI(normalMatch[1])
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ // ignore
|
|
|
+ }
|
|
|
+ const m = String(month).padStart(2, '0')
|
|
|
+ return `销售预测提报_用户_${year}-${m}.xlsx`
|
|
|
+ },
|
|
|
+
|
|
|
+ /** 触发下载 */
|
|
|
+ downloadBlob(blob, filename) {
|
|
|
+ try {
|
|
|
+ const url = window.URL.createObjectURL(blob)
|
|
|
+ const a = document.createElement('a')
|
|
|
+ a.href = url
|
|
|
+ a.download = filename || '导出文件.xlsx'
|
|
|
+ document.body.appendChild(a)
|
|
|
+ a.click()
|
|
|
+ document.body.removeChild(a)
|
|
|
+ window.URL.revokeObjectURL(url)
|
|
|
+ } catch (e) {
|
|
|
+ console.error('下载失败:', e)
|
|
|
+ this.$message.error('浏览器下载失败')
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
/**
|
|
|
* 生成预测编码
|
|
|
* @this {Vue & {form: ForecastFormModel}}
|