refactor(wms-report): 统一报表页面模板,提取公共逻辑为action-template
1. 将merge、repair下的多个报表页面重构为使用action-template组件 2. 提取自定义导出功能为公共组件CustomExport并复用至receive.vue和action-template 3. 统一报表页面的查询、导出、列配置等公共逻辑
This commit is contained in:
@@ -137,15 +137,19 @@
|
||||
</template>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleQuery">查询</el-button>
|
||||
<el-button type="primary" @click="settingVisible = true">列配置</el-button>
|
||||
<el-button v-if="['out', 'team', 'day', 'month', 'year', 'all'].includes(reportType)" type="primary"
|
||||
@click="exportData">导出产出钢卷</el-button>
|
||||
@click="exportData">导出产出</el-button>
|
||||
<el-button v-if="['loss', 'team', 'day', 'month', 'year', 'all'].includes(reportType)" type="primary"
|
||||
@click="exportLossData">导出消耗钢卷</el-button>
|
||||
<el-button type="primary" @click="settingVisible = true">列设置</el-button>
|
||||
@click="exportLossData">导出消耗</el-button>
|
||||
<el-button v-if="['out', 'team', 'day', 'month', 'year', 'all'].includes(reportType)" type="primary"
|
||||
@click="saveOutputReport">保存产出报表</el-button>
|
||||
@click="openCustomExport('output')">自定义导出产出</el-button>
|
||||
<el-button v-if="['loss', 'team', 'day', 'month', 'year', 'all'].includes(reportType)" type="primary"
|
||||
@click="saveLossReport">保存消耗报表</el-button>
|
||||
@click="openCustomExport('loss')">自定义导出消耗</el-button>
|
||||
<el-button v-if="['out', 'team', 'day', 'month', 'year', 'all'].includes(reportType)" type="primary"
|
||||
@click="saveOutputReport">保存产出</el-button>
|
||||
<el-button v-if="['loss', 'team', 'day', 'month', 'year', 'all'].includes(reportType)" type="primary"
|
||||
@click="saveLossReport">保存消耗</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-row>
|
||||
@@ -278,6 +282,30 @@
|
||||
<el-table-column label="产出总重" align="center" prop="outWeight" />
|
||||
</el-table>
|
||||
</template>
|
||||
|
||||
<!-- day 类型:小时数据趋势图 -->
|
||||
<div v-if="reportType === 'day'" style="margin-top: 20px; height: 400px;">
|
||||
<el-card>
|
||||
<template slot="header">
|
||||
<div class="clearfix">
|
||||
<span>小时数据趋势</span>
|
||||
</div>
|
||||
</template>
|
||||
<div ref="dayChart" style="width: 100%; height: 350px;"></div>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<!-- month 类型:日数据趋势图 -->
|
||||
<div v-if="reportType === 'month'" style="margin-top: 20px; height: 400px;">
|
||||
<el-card>
|
||||
<template slot="header">
|
||||
<div class="clearfix">
|
||||
<span>日数据趋势</span>
|
||||
</div>
|
||||
</template>
|
||||
<div ref="monthChart" style="width: 100%; height: 350px;"></div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 明细信息和标签页 -->
|
||||
@@ -318,6 +346,14 @@
|
||||
</el-radio-group>
|
||||
<columns-setting :reportType="activeColumnConfig"></columns-setting>
|
||||
</el-dialog>
|
||||
|
||||
<custom-export
|
||||
ref="customExport"
|
||||
:visible.sync="customExportVisible"
|
||||
:storage-key="customExportStorageKey"
|
||||
:column-groups="columnGroups"
|
||||
@export="handleCustomExport"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -337,6 +373,7 @@ import CoilTable from "@/views/wms/report/components/coilTable";
|
||||
import ColumnsSetting from "@/views/wms/report/components/setting/columns";
|
||||
import TimeRangePicker from "@/views/wms/report/components/timeRangePicker.vue";
|
||||
import SplitSummary from "@/views/wms/report/components/summary/splitSummary.vue";
|
||||
import CustomExport from "@/views/wms/report/components/CustomExport.vue";
|
||||
|
||||
import { saveReportFile } from "@/views/wms/report/js/reportFile";
|
||||
import * as echarts from 'echarts';
|
||||
@@ -372,6 +409,7 @@ export default {
|
||||
ColumnsSetting,
|
||||
TimeRangePicker,
|
||||
SplitSummary,
|
||||
CustomExport,
|
||||
},
|
||||
dicts: ['product_coil_status', 'coil_material', 'coil_itemname', 'coil_manufacturer', 'coil_quality_status'],
|
||||
data() {
|
||||
@@ -437,6 +475,19 @@ export default {
|
||||
viewType: 'custom',
|
||||
yesterdaySummary: {},
|
||||
monthChart: null,
|
||||
dayChart: null,
|
||||
customExportVisible: false,
|
||||
customExportType: 'output',
|
||||
columnGroups: {
|
||||
'基本信息': ['itemTypeDesc', 'warehouseName', 'actualWarehouseName', 'dataTypeText'],
|
||||
'钢卷号': ['enterCoilNo', 'supplierCoilNo', 'currentCoilNo'],
|
||||
'时间': ['createTime', 'exportTime', 'exportBy'],
|
||||
'物理属性': ['netWeight', 'length', 'specification', 'actualThickness', 'theoreticalThickness', 'theoreticalLength'],
|
||||
'材质属性': ['material', 'manufacturer', 'surfaceTreatmentDesc', 'zincLayer', 'packingStatus', 'temperGrade', 'coatingType', 'chromePlateCoilNo'],
|
||||
'用途': ['purpose', 'businessPurpose'],
|
||||
'状态': ['qualityStatus', 'statusDesc', 'isRelatedToOrderText'],
|
||||
'其他': ['itemName', 'itemId', 'packagingRequirement', 'trimmingRequirement', 'transferType', 'saleName', 'remark', 'team'],
|
||||
},
|
||||
queryParams: {
|
||||
startTime: start,
|
||||
endTime: end,
|
||||
@@ -523,6 +574,9 @@ export default {
|
||||
})
|
||||
return commonIds
|
||||
},
|
||||
customExportStorageKey() {
|
||||
return `coil-report-action-${this.actionType}`
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
warehouseOptions: {
|
||||
@@ -718,10 +772,21 @@ export default {
|
||||
});
|
||||
}
|
||||
|
||||
if (this.reportType === 'day') {
|
||||
this.$nextTick(() => {
|
||||
this.initDayChart()
|
||||
})
|
||||
}
|
||||
if (this.reportType === 'month') {
|
||||
this.$nextTick(() => {
|
||||
this.initMonthChart()
|
||||
})
|
||||
}
|
||||
|
||||
this.loading = false;
|
||||
},
|
||||
// all 类型:获取昨日数据
|
||||
getYesterdayData() {
|
||||
// all 类型:获取昨日数据(逻辑同 getList,时间向前推一天)
|
||||
async getYesterdayData() {
|
||||
const yesterday = new Date(this.dayDate)
|
||||
yesterday.setDate(yesterday.getDate() - 1)
|
||||
const year = yesterday.getFullYear()
|
||||
@@ -730,66 +795,37 @@ export default {
|
||||
const yesterdayDate = `${year}-${month}-${day}`
|
||||
const { start, end } = this.getDayTimeRange(yesterdayDate)
|
||||
|
||||
Promise.all([
|
||||
listCoilWithIds({
|
||||
selectType: 'raw_material',
|
||||
itemType: 'raw_material',
|
||||
warehouseIds: this.warehouseIds.join(','),
|
||||
...this.queryParams,
|
||||
startTime: start,
|
||||
endTime: end,
|
||||
pageSize: 99999,
|
||||
pageNum: 1,
|
||||
}),
|
||||
listCoilWithIds({
|
||||
selectType: 'product',
|
||||
itemType: 'product',
|
||||
warehouseIds: this.warehouseIds.join(','),
|
||||
...this.queryParams,
|
||||
startTime: start,
|
||||
endTime: end,
|
||||
pageSize: 99999,
|
||||
pageNum: 1,
|
||||
}),
|
||||
]).then((resList) => {
|
||||
const list = resList.flatMap(res => res.rows)
|
||||
const yesterdayList = list.sort(
|
||||
(a, b) => new Date(b.createTime) - new Date(a.createTime)
|
||||
).map(item => {
|
||||
if (!item.selectType) {
|
||||
item.selectType = item.itemType === 'product' ? 'product' : 'raw_material'
|
||||
}
|
||||
return item
|
||||
})
|
||||
const yesterdayParams = {
|
||||
...this.queryParams,
|
||||
startTime: start,
|
||||
endTime: end,
|
||||
}
|
||||
|
||||
listLightPendingAction({
|
||||
actionStatus: 2,
|
||||
actionType: this.actionType,
|
||||
pageSize: 99999,
|
||||
pageNum: 1,
|
||||
startTime: start,
|
||||
endTime: end,
|
||||
}).then((lossRes) => {
|
||||
const lossActions = lossRes.rows
|
||||
const lossCoilIds = lossActions.map(item => item.coilId).join(',')
|
||||
const res = await listLightPendingAction({ ...yesterdayParams, actionTypes: this.actionType, actionStatus: 2 })
|
||||
const lossIds = res.data.filter(item => item.coilId).map(item => item.coilId)
|
||||
const lossActionIds = res.data.filter(item => item.actionId).map(item => item.actionId)
|
||||
const outIds = [...new Set(res.data.filter(item => item.processedCoilIds).map(item => item.processedCoilIds))]
|
||||
|
||||
if (lossCoilIds) {
|
||||
listCoilWithIds({
|
||||
...this.queryParams,
|
||||
startTime: undefined,
|
||||
endTime: undefined,
|
||||
coilIds: lossCoilIds,
|
||||
pageSize: 99999,
|
||||
pageNum: 1,
|
||||
}).then(res => {
|
||||
const yesterdayLossList = res.rows
|
||||
this.yesterdaySummary = calcSummary(yesterdayList, yesterdayLossList)
|
||||
})
|
||||
} else {
|
||||
this.yesterdaySummary = calcSummary(yesterdayList, [])
|
||||
}
|
||||
})
|
||||
if (lossIds.length === 0 || outIds.length === 0) {
|
||||
this.yesterdaySummary = {}
|
||||
return
|
||||
}
|
||||
|
||||
const [lossRes, outRes] = await Promise.all([
|
||||
listCoilWithIds({ ...yesterdayParams, actionIds: lossActionIds.join(',') || '', startTime: '', endTime: '', selectType: 'raw_material' }),
|
||||
listCoilWithIds({ ...yesterdayParams, coilIds: outIds.join(',') || '', startTime: '', endTime: '', byCreateTimeStart: start, byCreateTimeEnd: end, selectType: 'product' }),
|
||||
])
|
||||
|
||||
const yesterdayLossList = lossRes.rows.map(item => {
|
||||
const [thickness, width] = item.specification?.split('*') || []
|
||||
return { ...item, computedThickness: parseFloat(thickness), computedWidth: parseFloat(width) }
|
||||
})
|
||||
const yesterdayOutList = outRes.rows.map(item => {
|
||||
const [thickness, width] = item.specification?.split('*') || []
|
||||
return { ...item, computedThickness: parseFloat(thickness), computedWidth: parseFloat(width) }
|
||||
})
|
||||
|
||||
this.yesterdaySummary = calcSummary(yesterdayOutList, yesterdayLossList)
|
||||
},
|
||||
// all 类型:初始化月视图折线图
|
||||
initMonthChart() {
|
||||
@@ -852,6 +888,97 @@ export default {
|
||||
this.monthChart.resize()
|
||||
})
|
||||
},
|
||||
// day 类型:初始化小时数据折线图
|
||||
initDayChart() {
|
||||
if (!this.$refs.dayChart) return
|
||||
|
||||
if (this.dayChart) {
|
||||
this.dayChart.dispose()
|
||||
}
|
||||
|
||||
this.dayChart = echarts.init(this.$refs.dayChart)
|
||||
|
||||
const hourlyData = {}
|
||||
for (let h = 0; h < 24; h++) {
|
||||
hourlyData[h] = { outCount: 0, outTotalWeight: 0, lossCount: 0, lossTotalWeight: 0 }
|
||||
}
|
||||
|
||||
this.outList.forEach(item => {
|
||||
const timeStr = item.createTime?.split(' ')[1] || '00:00:00'
|
||||
const hour = parseInt(timeStr.split(':')[0])
|
||||
if (hourlyData[hour] !== undefined) {
|
||||
hourlyData[hour].outCount++
|
||||
hourlyData[hour].outTotalWeight += parseFloat(item.netWeight) || 0
|
||||
}
|
||||
})
|
||||
|
||||
this.lossList.forEach(item => {
|
||||
const timeStr = item.actionCompleteTime?.split(' ')[1] || '00:00:00'
|
||||
const hour = parseInt(timeStr.split(':')[0])
|
||||
if (hourlyData[hour] !== undefined) {
|
||||
hourlyData[hour].lossCount++
|
||||
hourlyData[hour].lossTotalWeight += parseFloat(item.netWeight) || 0
|
||||
}
|
||||
})
|
||||
|
||||
const hours = Object.keys(hourlyData).map(Number).sort((a, b) => a - b)
|
||||
const hourLabels = hours.map(h => `${String(h).padStart(2, '0')}:00`)
|
||||
const outCountData = hours.map(h => hourlyData[h].outCount)
|
||||
const outTotalWeightData = hours.map(h => hourlyData[h].outTotalWeight.toFixed(2))
|
||||
const lossCountData = hours.map(h => hourlyData[h].lossCount)
|
||||
const lossTotalWeightData = hours.map(h => hourlyData[h].lossTotalWeight.toFixed(2))
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'cross', label: { backgroundColor: '#6a7985' } }
|
||||
},
|
||||
legend: { data: ['产出数量', '产出总重', '消耗数量', '消耗总重'] },
|
||||
grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
|
||||
xAxis: [{ type: 'category', boundaryGap: false, data: hourLabels }],
|
||||
yAxis: [
|
||||
{ type: 'value', name: '数量', position: 'left' },
|
||||
{ type: 'value', name: '重量(t)', position: 'right' }
|
||||
],
|
||||
series: [
|
||||
{ name: '产出数量', type: 'line', data: outCountData, yAxisIndex: 0 },
|
||||
{ name: '产出总重', type: 'line', data: outTotalWeightData, yAxisIndex: 1 },
|
||||
{ name: '消耗数量', type: 'line', data: lossCountData, yAxisIndex: 0 },
|
||||
{ name: '消耗总重', type: 'line', data: lossTotalWeightData, yAxisIndex: 1 }
|
||||
]
|
||||
}
|
||||
|
||||
this.dayChart.setOption(option)
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
this.dayChart.resize()
|
||||
})
|
||||
},
|
||||
// 自定义导出
|
||||
openCustomExport(type) {
|
||||
if (type === 'output' && this.outList.length === 0) {
|
||||
this.$message({ message: '暂无产出数据可导出', type: 'warning' })
|
||||
return
|
||||
}
|
||||
if (type === 'loss' && this.lossList.length === 0) {
|
||||
this.$message({ message: '暂无消耗数据可导出', type: 'warning' })
|
||||
return
|
||||
}
|
||||
this.customExportType = type
|
||||
this.$refs.customExport.open()
|
||||
},
|
||||
handleCustomExport(orderedColumns) {
|
||||
const { pageNum, pageSize, startTime, endTime, ...filters } = this.queryParams
|
||||
const type = this.customExportType
|
||||
const ids = type === 'loss'
|
||||
? { actionIds: this.actionIds }
|
||||
: { coilIds: this.outList.map(item => item.coilId).join(',') }
|
||||
this.download('wms/materialCoil/exportCustomOrdered', {
|
||||
...filters,
|
||||
...ids,
|
||||
columnsOrdered: orderedColumns.join(','),
|
||||
}, `materialCoil_${new Date().getTime()}.xlsx`)
|
||||
},
|
||||
exportData() {
|
||||
if (this.outList.length === 0) {
|
||||
this.$message.warning('暂无数据可导出')
|
||||
|
||||
Reference in New Issue
Block a user