|
@@ -7,7 +7,7 @@
|
|
|
* @typedef {import('./types').ForecastSummaryComponent} ForecastSummaryComponent
|
|
|
*/
|
|
|
|
|
|
-import { getSalesForecastMainList, getForecastSummaryDetail } from '@/api/forecast/forecast-summary'
|
|
|
+import { getSalesForecastMainList, getForecastSummaryDetail, exportSalesForecastTemplate } from '@/api/forecast/forecast-summary'
|
|
|
import {
|
|
|
APPROVAL_STATUS,
|
|
|
APPROVAL_STATUS_CONFIG,
|
|
@@ -27,450 +27,510 @@ import { mapGetters } from 'vuex'
|
|
|
* @description 提供预测汇总的查询、分页、表格配置等功能
|
|
|
* @this {ForecastSummaryComponent & Vue}
|
|
|
*/
|
|
|
-export default {
|
|
|
- data() {
|
|
|
- return {
|
|
|
- /**
|
|
|
- * 表单数据
|
|
|
- * @type {Record<string, any>}
|
|
|
- */
|
|
|
- form: {},
|
|
|
-
|
|
|
- /**
|
|
|
- * 查询参数
|
|
|
- * @type {typeof DEFAULT_FORECAST_SUMMARY_QUERY}
|
|
|
- */
|
|
|
- query: { ...DEFAULT_FORECAST_SUMMARY_QUERY },
|
|
|
-
|
|
|
- /**
|
|
|
- * 加载状态
|
|
|
- * @type {boolean}
|
|
|
- */
|
|
|
- loading: true,
|
|
|
-
|
|
|
- /**
|
|
|
- * 分页配置
|
|
|
- * @type {PageConfig}
|
|
|
- */
|
|
|
- page: {
|
|
|
- pageSize: 10,
|
|
|
- currentPage: 1,
|
|
|
- total: 0
|
|
|
- },
|
|
|
-
|
|
|
- /**
|
|
|
- * 表格数据(主表记录)
|
|
|
- * @type {Array<SalesForecastMainRecord>}
|
|
|
- */
|
|
|
- data: [],
|
|
|
-
|
|
|
- /**
|
|
|
- * 选中的行数据(主表记录)
|
|
|
- * @type {Array<SalesForecastMainRecord>}
|
|
|
- */
|
|
|
- selectionList: [],
|
|
|
-
|
|
|
- /**
|
|
|
- * avue表格配置
|
|
|
- * @type {AvueCrudOption}
|
|
|
- */
|
|
|
- option: {
|
|
|
- height: 'auto',
|
|
|
- calcHeight: 30,
|
|
|
- tip: false,
|
|
|
- searchShow: true,
|
|
|
- searchMenuSpan: 6,
|
|
|
- border: true,
|
|
|
- index: true,
|
|
|
- viewBtn: false,
|
|
|
- editBtn: false,
|
|
|
- delBtn: false,
|
|
|
- addBtn: false,
|
|
|
- selection: false,
|
|
|
- dialogClickModal: false,
|
|
|
- menu: false,
|
|
|
- // 启用行展开,展示子表格
|
|
|
- expand: true,
|
|
|
- expandRowKeys: [],
|
|
|
- defaultExpandAll: false,
|
|
|
- column: [
|
|
|
- {
|
|
|
- label: '年月',
|
|
|
- prop: 'yearMonth',
|
|
|
- minWidth: 100,
|
|
|
- sortable: true,
|
|
|
- slot: true,
|
|
|
- search: true,
|
|
|
- searchSpan: 6,
|
|
|
- type: 'month',
|
|
|
- format: 'yyyy-MM',
|
|
|
- valueFormat: 'yyyy-MM',
|
|
|
- searchslot: false,
|
|
|
- hide: false
|
|
|
- },
|
|
|
- {
|
|
|
- label: '年份',
|
|
|
- prop: 'year',
|
|
|
- minWidth: 80,
|
|
|
- type: 'year',
|
|
|
- format: 'yyyy',
|
|
|
- valueFormat: 'yyyy',
|
|
|
- search: true,
|
|
|
- searchSpan: 6,
|
|
|
- hide: true
|
|
|
- },
|
|
|
- {
|
|
|
- label: '月份',
|
|
|
- prop: 'month',
|
|
|
- minWidth: 80,
|
|
|
- type: 'select',
|
|
|
- dicData: MONTH_OPTIONS,
|
|
|
- search: true,
|
|
|
- searchSpan: 6,
|
|
|
- hide: true
|
|
|
- },
|
|
|
- {
|
|
|
- label: '客户编码',
|
|
|
- prop: 'customerCode',
|
|
|
- minWidth: 120,
|
|
|
- search: true,
|
|
|
- searchSpan: 6
|
|
|
- },
|
|
|
- {
|
|
|
- label: '客户名称',
|
|
|
- prop: 'customerName',
|
|
|
- minWidth: 200,
|
|
|
- search: true,
|
|
|
- searchSpan: 6
|
|
|
- },
|
|
|
- {
|
|
|
- label: '审批状态',
|
|
|
- prop: 'approvalStatus',
|
|
|
- minWidth: 100,
|
|
|
- type: 'select',
|
|
|
- dicData: APPROVAL_STATUS_OPTIONS,
|
|
|
- search: true,
|
|
|
- searchSpan: 6,
|
|
|
- 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
|
|
|
- }
|
|
|
- ]
|
|
|
- },
|
|
|
-
|
|
|
- // 子表(展开区)表格配置:展示 pcBladeSalesForecastSummaryList
|
|
|
- childOption: {
|
|
|
- height: 'auto',
|
|
|
- calcHeight: 0,
|
|
|
- tip: false,
|
|
|
- searchShow: false,
|
|
|
- border: true,
|
|
|
- index: true,
|
|
|
- viewBtn: false,
|
|
|
- editBtn: false,
|
|
|
- delBtn: false,
|
|
|
- addBtn: false,
|
|
|
- selection: false,
|
|
|
- menu: 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 }
|
|
|
- ]
|
|
|
- }
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- 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 {{label: string, type: string}} 状态配置对象
|
|
|
- */
|
|
|
- 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)
|
|
|
- },
|
|
|
-
|
|
|
- /**
|
|
|
- * 搜索条件变化处理
|
|
|
- * @this {ForecastSummaryComponent & Vue}
|
|
|
- * @param {Record<string, any>} params - 搜索参数
|
|
|
- * @param {() => void} done - 完成回调
|
|
|
- */
|
|
|
- searchChange(params, done) {
|
|
|
- // 处理年月字段拆分
|
|
|
- const processedParams = { ...params }
|
|
|
-
|
|
|
- // 如果有yearMonth字段,拆分为year和month
|
|
|
- if (processedParams.yearMonth) {
|
|
|
- const yearMonthValue = processedParams.yearMonth
|
|
|
- if (yearMonthValue) {
|
|
|
- // 解析年月格式 (yyyy-MM)
|
|
|
- const [year, month] = yearMonthValue.split('-')
|
|
|
- if (year) {
|
|
|
- processedParams.year = parseInt(year, 10)
|
|
|
- }
|
|
|
- if (month) {
|
|
|
- processedParams.month = parseInt(month, 10)
|
|
|
- }
|
|
|
- }
|
|
|
- // 删除原始的yearMonth字段,避免传递到后端
|
|
|
- delete processedParams.yearMonth
|
|
|
- }
|
|
|
-
|
|
|
- this.query = {
|
|
|
- ...this.query,
|
|
|
- ...processedParams
|
|
|
- }
|
|
|
- this.onLoad(this.page, processedParams)
|
|
|
- done()
|
|
|
- },
|
|
|
-
|
|
|
- /**
|
|
|
- * 搜索重置处理
|
|
|
- * @this {ForecastSummaryComponent & Vue}
|
|
|
- */
|
|
|
- searchReset() {
|
|
|
- this.query = { ...DEFAULT_FORECAST_SUMMARY_QUERY }
|
|
|
- this.onLoad(this.page)
|
|
|
- },
|
|
|
-
|
|
|
- /**
|
|
|
- * 选择变化处理
|
|
|
- * @this {ForecastSummaryComponent & Vue}
|
|
|
- * @param {Array<SalesForecastMainRecord>} selection - 选中的行数据
|
|
|
- */
|
|
|
- selectionChange(selection) {
|
|
|
- this.selectionList = selection
|
|
|
- },
|
|
|
-
|
|
|
- /**
|
|
|
- * 当前页变化处理
|
|
|
- * @this {ForecastSummaryComponent & Vue}
|
|
|
- * @param {number} currentPage - 当前页码
|
|
|
- */
|
|
|
- currentChange(currentPage) {
|
|
|
- this.page.currentPage = currentPage
|
|
|
- },
|
|
|
-
|
|
|
- /**
|
|
|
- * 页大小变化处理
|
|
|
- * @this {ForecastSummaryComponent & Vue}
|
|
|
- * @param {number} pageSize - 页大小
|
|
|
- */
|
|
|
- sizeChange(pageSize) {
|
|
|
- this.page.pageSize = pageSize
|
|
|
- },
|
|
|
-
|
|
|
- /**
|
|
|
- * 刷新处理
|
|
|
- * @this {ForecastSummaryComponent & Vue}
|
|
|
- */
|
|
|
- refreshChange() {
|
|
|
- this.onLoad(this.page, this.query)
|
|
|
- },
|
|
|
-
|
|
|
- /**
|
|
|
- * 弹窗打开前处理
|
|
|
- * @param {Function} done - 完成回调
|
|
|
- * @param {string} type - 操作类型
|
|
|
- */
|
|
|
- beforeOpen(done, type) {
|
|
|
- // 由于是只读模式,不需要处理弹窗
|
|
|
- done()
|
|
|
- },
|
|
|
-
|
|
|
- /**
|
|
|
- * 加载数据
|
|
|
- * @this {ForecastSummaryComponent & Vue}
|
|
|
- * @param {PageConfig} page - 分页参数
|
|
|
- * @param {Record<string, any>} params - 查询参数
|
|
|
- * @returns {Promise<void>}
|
|
|
- */
|
|
|
- async onLoad(page, params = {}) {
|
|
|
- this.loading = true
|
|
|
- try {
|
|
|
- // 仅挑选 main-list 支持的查询参数,避免无效字段影响
|
|
|
- const merged = { ...this.query, ...params }
|
|
|
- const safeParams = {
|
|
|
- year: merged.year,
|
|
|
- month: merged.month,
|
|
|
- customerName: merged.customerName
|
|
|
- }
|
|
|
-
|
|
|
- const response = await getSalesForecastMainList(
|
|
|
- page.currentPage || 1,
|
|
|
- page.pageSize || 10,
|
|
|
- safeParams
|
|
|
- )
|
|
|
-
|
|
|
- if (response && response.data && response.data.code === 200 && response.data.data) {
|
|
|
- const pageData = response.data.data
|
|
|
- const records = Array.isArray(pageData.records) ? pageData.records : []
|
|
|
- // 字段类型转换与BigInt处理
|
|
|
- this.data = records.map(r => {
|
|
|
- const children = Array.isArray(r.pcBladeSalesForecastSummaryList)
|
|
|
- ? r.pcBladeSalesForecastSummaryList.map(it => ({
|
|
|
- ...it,
|
|
|
- idBigint: this.safeBigInt(it.id),
|
|
|
- forecastMainIdBigint: it.forecastMainId != null ? this.safeBigInt(it.forecastMainId) : null,
|
|
|
- year: Number(it.year),
|
|
|
- month: Number(it.month),
|
|
|
- forecastQuantity: typeof it.forecastQuantity === 'string' ? Number(it.forecastQuantity) : Number(it.forecastQuantity)
|
|
|
- }))
|
|
|
- : []
|
|
|
- return {
|
|
|
- ...r,
|
|
|
- idBigint: this.safeBigInt(r.id),
|
|
|
- year: Number(r.year),
|
|
|
- month: Number(r.month),
|
|
|
- pcBladeSalesForecastSummaryList: children
|
|
|
- }
|
|
|
- })
|
|
|
- this.page.total = Number(pageData.total) || 0
|
|
|
- this.page.currentPage = Number(pageData.current) || 1
|
|
|
- this.page.pageSize = Number(pageData.size) || 10
|
|
|
- } else {
|
|
|
- this.$message.error(response?.data?.message || '获取数据失败')
|
|
|
- this.data = []
|
|
|
- this.page.total = 0
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- console.error('获取预测主表分页列表失败:', error)
|
|
|
- this.$message.error('获取数据失败,请稍后重试')
|
|
|
- this.data = []
|
|
|
- this.page.total = 0
|
|
|
- } finally {
|
|
|
- this.loading = false
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- /**
|
|
|
- * 安全将值转换为BigInt
|
|
|
- * @param {string|number|null|undefined} v
|
|
|
- * @returns {bigint|null}
|
|
|
- */
|
|
|
- safeBigInt(v) {
|
|
|
- try {
|
|
|
- if (v === null || v === undefined || v === '') return null
|
|
|
- return BigInt(v)
|
|
|
- } catch (e) {
|
|
|
- return null
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- /**
|
|
|
- * 获取预测汇总详情
|
|
|
- * @this {ForecastSummaryComponent & Vue}
|
|
|
- * @param {string|number} id - 预测汇总ID
|
|
|
- * @returns {Promise<import('@/api/forecast/types').ForecastSummaryRecord|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.message || '获取详情失败')
|
|
|
- return null
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- console.error('获取预测汇总详情失败:', error)
|
|
|
- this.$message.error('获取详情失败,请稍后重试')
|
|
|
- return null
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
+ export default {
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ /**
|
|
|
+ * 表单数据
|
|
|
+ * @type {Record<string, any>}
|
|
|
+ */
|
|
|
+ form: {},
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询参数
|
|
|
+ * @type {typeof DEFAULT_FORECAST_SUMMARY_QUERY}
|
|
|
+ */
|
|
|
+ query: { ...DEFAULT_FORECAST_SUMMARY_QUERY },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 加载状态
|
|
|
+ * @type {boolean}
|
|
|
+ */
|
|
|
+ loading: true,
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 分页配置
|
|
|
+ * @type {PageConfig}
|
|
|
+ */
|
|
|
+ page: {
|
|
|
+ pageSize: 10,
|
|
|
+ currentPage: 1,
|
|
|
+ total: 0
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 表格数据(主表记录)
|
|
|
+ * @type {Array<SalesForecastMainRecord>}
|
|
|
+ */
|
|
|
+ data: [],
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 选中的行数据(主表记录)
|
|
|
+ * @type {Array<SalesForecastMainRecord>}
|
|
|
+ */
|
|
|
+ selectionList: [],
|
|
|
+
|
|
|
+ /**
|
|
|
+ * avue表格配置
|
|
|
+ * @type {AvueCrudOption}
|
|
|
+ */
|
|
|
+ option: {
|
|
|
+ height: 'auto',
|
|
|
+ calcHeight: 30,
|
|
|
+ tip: false,
|
|
|
+ searchShow: true,
|
|
|
+ searchMenuSpan: 6,
|
|
|
+ border: true,
|
|
|
+ index: true,
|
|
|
+ viewBtn: false,
|
|
|
+ editBtn: false,
|
|
|
+ delBtn: false,
|
|
|
+ addBtn: false,
|
|
|
+ selection: false,
|
|
|
+ dialogClickModal: false,
|
|
|
+ menu: false,
|
|
|
+ // 启用行展开,展示子表格
|
|
|
+ expand: true,
|
|
|
+ expandRowKeys: [],
|
|
|
+ defaultExpandAll: false,
|
|
|
+ column: [
|
|
|
+ {
|
|
|
+ label: '年月',
|
|
|
+ prop: 'yearMonth',
|
|
|
+ minWidth: 100,
|
|
|
+ sortable: true,
|
|
|
+ slot: true,
|
|
|
+ search: true,
|
|
|
+ searchSpan: 6,
|
|
|
+ type: 'month',
|
|
|
+ format: 'yyyy-MM',
|
|
|
+ valueFormat: 'yyyy-MM',
|
|
|
+ searchslot: false,
|
|
|
+ hide: false
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '年份',
|
|
|
+ prop: 'year',
|
|
|
+ minWidth: 80,
|
|
|
+ type: 'year',
|
|
|
+ format: 'yyyy',
|
|
|
+ valueFormat: 'yyyy',
|
|
|
+ search: true,
|
|
|
+ searchSpan: 6,
|
|
|
+ hide: true
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '月份',
|
|
|
+ prop: 'month',
|
|
|
+ minWidth: 80,
|
|
|
+ type: 'select',
|
|
|
+ dicData: MONTH_OPTIONS,
|
|
|
+ search: true,
|
|
|
+ searchSpan: 6,
|
|
|
+ hide: true
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '客户编码',
|
|
|
+ prop: 'customerCode',
|
|
|
+ minWidth: 120,
|
|
|
+ search: true,
|
|
|
+ searchSpan: 6
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '客户名称',
|
|
|
+ prop: 'customerName',
|
|
|
+ minWidth: 200,
|
|
|
+ search: true,
|
|
|
+ searchSpan: 6
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '审批状态',
|
|
|
+ prop: 'approvalStatus',
|
|
|
+ minWidth: 100,
|
|
|
+ type: 'select',
|
|
|
+ dicData: APPROVAL_STATUS_OPTIONS,
|
|
|
+ search: true,
|
|
|
+ searchSpan: 6,
|
|
|
+ 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
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+
|
|
|
+ // 子表(展开区)表格配置:展示 pcBladeSalesForecastSummaryList
|
|
|
+ childOption: {
|
|
|
+ height: 'auto',
|
|
|
+ calcHeight: 0,
|
|
|
+ tip: false,
|
|
|
+ searchShow: false,
|
|
|
+ border: true,
|
|
|
+ index: true,
|
|
|
+ viewBtn: false,
|
|
|
+ editBtn: false,
|
|
|
+ delBtn: false,
|
|
|
+ addBtn: false,
|
|
|
+ selection: false,
|
|
|
+ menu: 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 }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ 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 {{label: string, type: string}} 状态配置对象
|
|
|
+ */
|
|
|
+ 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(/** @type {number} */ (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)
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 导出“销售预测汇总”模板(使用当前查询条件)
|
|
|
+ * @this {ForecastSummaryComponent & Vue}
|
|
|
+ * @returns {Promise<void>}
|
|
|
+ */
|
|
|
+ async onExportTemplate() {
|
|
|
+ try {
|
|
|
+ this.loading = true
|
|
|
+ const page = this.page || { currentPage: 1, pageSize: 10 }
|
|
|
+ const merged = { ...this.query }
|
|
|
+ const safeParams = {
|
|
|
+ year: merged.year,
|
|
|
+ month: merged.month,
|
|
|
+ customerName: merged.customerName
|
|
|
+ }
|
|
|
+ const res = await exportSalesForecastTemplate(
|
|
|
+ page.currentPage || 1,
|
|
|
+ page.pageSize || 10,
|
|
|
+ safeParams
|
|
|
+ )
|
|
|
+ // 处理文件名
|
|
|
+ const disposition = res?.headers?.['content-disposition'] || res?.headers?.['Content-Disposition']
|
|
|
+ let filename = '销售预测汇总模板.xlsx'
|
|
|
+ // attachment; filename*=UTF-8''xxxx.xlsx 或 filename="xxxx.xlsx"
|
|
|
+ const plainMatch = /filename="?([^;\n"]+)"?/i.exec(disposition)
|
|
|
+ if (disposition) {
|
|
|
+ // attachment; filename*=UTF-8''xxxx.xlsx 或 filename="xxxx.xlsx"
|
|
|
+ const utf8Match = /filename\*=UTF-8''([^;\n]+)/i.exec(disposition)
|
|
|
+ const raw = utf8Match ? decodeURIComponent(utf8Match[1]) : (plainMatch ? plainMatch[1] : '')
|
|
|
+ if (raw) filename = raw
|
|
|
+ // 将下载日期追加到文件名(YYYYMMDD),便于区分下载时间
|
|
|
+ const now = new Date()
|
|
|
+ const y = now.getFullYear()
|
|
|
+ const m = String(now.getMonth() + 1).padStart(2, '0')
|
|
|
+ const d = String(now.getDate()).padStart(2, '0')
|
|
|
+ const ymd = `${y}${m}${d}`
|
|
|
+ const dotIndex = filename.lastIndexOf('.')
|
|
|
+ if (dotIndex > 0) {
|
|
|
+ filename = `${filename.slice(0, dotIndex)}_${ymd}${filename.slice(dotIndex)}`
|
|
|
+ } else {
|
|
|
+ filename = `${filename}_${ymd}`
|
|
|
+ }
|
|
|
+ }
|
|
|
+ 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('导出模板失败:', e)
|
|
|
+ this.$message.error('导出失败,请稍后重试')
|
|
|
+ } finally {
|
|
|
+ this.loading = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 搜索条件变化处理
|
|
|
+ * @this {ForecastSummaryComponent & Vue}
|
|
|
+ * @param {Record<string, any>} params - 搜索参数
|
|
|
+ * @param {() => void} done - 完成回调
|
|
|
+ */
|
|
|
+ searchChange(params, done) {
|
|
|
+ // 处理年月字段拆分
|
|
|
+ const processedParams = { ...params }
|
|
|
+
|
|
|
+ // 如果有yearMonth字段,拆分为year和month
|
|
|
+ if (processedParams.yearMonth) {
|
|
|
+ const yearMonthValue = processedParams.yearMonth
|
|
|
+ if (yearMonthValue) {
|
|
|
+ // 解析年月格式 (yyyy-MM)
|
|
|
+ const [year, month] = yearMonthValue.split('-')
|
|
|
+ if (year) {
|
|
|
+ processedParams.year = parseInt(year, 10)
|
|
|
+ }
|
|
|
+ if (month) {
|
|
|
+ processedParams.month = parseInt(month, 10)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 删除原始的yearMonth字段,避免传递到后端
|
|
|
+ delete processedParams.yearMonth
|
|
|
+ }
|
|
|
+
|
|
|
+ this.query = {
|
|
|
+ ...this.query,
|
|
|
+ ...processedParams
|
|
|
+ }
|
|
|
+ this.onLoad(this.page, processedParams)
|
|
|
+ done()
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 搜索重置处理
|
|
|
+ * @this {ForecastSummaryComponent & Vue}
|
|
|
+ */
|
|
|
+ searchReset() {
|
|
|
+ this.query = { ...DEFAULT_FORECAST_SUMMARY_QUERY }
|
|
|
+ this.onLoad(this.page)
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 选择变化处理
|
|
|
+ * @this {ForecastSummaryComponent & Vue}
|
|
|
+ * @param {Array<SalesForecastMainRecord>} selection - 选中的行数据
|
|
|
+ */
|
|
|
+ selectionChange(selection) {
|
|
|
+ this.selectionList = selection
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 当前页变化处理
|
|
|
+ * @this {ForecastSummaryComponent & Vue}
|
|
|
+ * @param {number} currentPage - 当前页码
|
|
|
+ */
|
|
|
+ currentChange(currentPage) {
|
|
|
+ this.page.currentPage = currentPage
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 页大小变化处理
|
|
|
+ * @this {ForecastSummaryComponent & Vue}
|
|
|
+ * @param {number} pageSize - 页大小
|
|
|
+ */
|
|
|
+ sizeChange(pageSize) {
|
|
|
+ this.page.pageSize = pageSize
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 刷新处理
|
|
|
+ * @this {ForecastSummaryComponent & Vue}
|
|
|
+ */
|
|
|
+ refreshChange() {
|
|
|
+ this.onLoad(this.page, this.query)
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 弹窗打开前处理
|
|
|
+ * @param {Function} done - 完成回调
|
|
|
+ * @param {string} type - 操作类型
|
|
|
+ */
|
|
|
+ beforeOpen(done, type) {
|
|
|
+ // 由于是只读模式,不需要处理弹窗
|
|
|
+ done()
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 加载数据
|
|
|
+ * @this {ForecastSummaryComponent & Vue}
|
|
|
+ * @param {PageConfig} page - 分页参数
|
|
|
+ * @param {Record<string, any>} params - 查询参数
|
|
|
+ * @returns {Promise<void>}
|
|
|
+ */
|
|
|
+ async onLoad(page, params = {}) {
|
|
|
+ this.loading = true
|
|
|
+ try {
|
|
|
+ // 仅挑选 main-list 支持的查询参数,避免无效字段影响
|
|
|
+ const merged = { ...this.query, ...params }
|
|
|
+ const safeParams = {
|
|
|
+ year: merged.year,
|
|
|
+ month: merged.month,
|
|
|
+ customerName: merged.customerName
|
|
|
+ }
|
|
|
+
|
|
|
+ const response = await getSalesForecastMainList(
|
|
|
+ page.currentPage || 1,
|
|
|
+ page.pageSize || 10,
|
|
|
+ safeParams
|
|
|
+ )
|
|
|
+
|
|
|
+ if (response && response.data && response.data.code === 200 && response.data.data) {
|
|
|
+ const pageData = response.data.data
|
|
|
+ const records = Array.isArray(pageData.records) ? pageData.records : []
|
|
|
+ // 字段类型转换与BigInt处理
|
|
|
+ this.data = records.map(r => {
|
|
|
+ const children = Array.isArray(r.pcBladeSalesForecastSummaryList)
|
|
|
+ ? r.pcBladeSalesForecastSummaryList.map(it => ({
|
|
|
+ ...it,
|
|
|
+ idBigint: this.safeBigInt(it.id),
|
|
|
+ forecastMainIdBigint: it.forecastMainId != null ? this.safeBigInt(it.forecastMainId) : null,
|
|
|
+ year: Number(it.year),
|
|
|
+ month: Number(it.month),
|
|
|
+ forecastQuantity: typeof it.forecastQuantity === 'string' ? Number(it.forecastQuantity) : Number(it.forecastQuantity)
|
|
|
+ }))
|
|
|
+ : []
|
|
|
+ return {
|
|
|
+ ...r,
|
|
|
+ idBigint: this.safeBigInt(r.id),
|
|
|
+ year: Number(r.year),
|
|
|
+ month: Number(r.month),
|
|
|
+ pcBladeSalesForecastSummaryList: children
|
|
|
+ }
|
|
|
+ })
|
|
|
+ this.page.total = Number(pageData.total) || 0
|
|
|
+ this.page.currentPage = Number(pageData.current) || 1
|
|
|
+ this.page.pageSize = Number(pageData.size) || 10
|
|
|
+ } else {
|
|
|
+ this.$message.error(response?.data?.message || '获取数据失败')
|
|
|
+ this.data = []
|
|
|
+ this.page.total = 0
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取预测主表分页列表失败:', error)
|
|
|
+ this.$message.error('获取数据失败,请稍后重试')
|
|
|
+ this.data = []
|
|
|
+ this.page.total = 0
|
|
|
+ } finally {
|
|
|
+ this.loading = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 安全将值转换为BigInt
|
|
|
+ * @param {string|number|null|undefined} v
|
|
|
+ * @returns {bigint|null}
|
|
|
+ */
|
|
|
+ safeBigInt(v) {
|
|
|
+ try {
|
|
|
+ if (v === null || v === undefined || v === '') return null
|
|
|
+ return BigInt(v)
|
|
|
+ } catch (e) {
|
|
|
+ return null
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取预测汇总详情
|
|
|
+ * @this {ForecastSummaryComponent & Vue}
|
|
|
+ * @param {string|number} id - 预测汇总ID
|
|
|
+ * @returns {Promise<import('@/api/forecast/types').ForecastSummaryRecord|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.message || '获取详情失败')
|
|
|
+ return null
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取预测汇总详情失败:', error)
|
|
|
+ this.$message.error('获取详情失败,请稍后重试')
|
|
|
+ return null
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|