Files
klp-oa/klp-ui/src/views/wms/report/template/action.vue

943 lines
43 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="app-container" v-loading="loading">
<!-- all 类型视图选择器 -->
<div v-if="reportType === 'all'" style="margin-bottom: 10px;">
<el-radio-group v-model="viewType" @change="handleViewTypeChange">
<el-radio-button label="day">日视图</el-radio-button>
<el-radio-button label="month">月视图</el-radio-button>
<el-radio-button label="year">年视图</el-radio-button>
<el-radio-button label="custom">自定义</el-radio-button>
</el-radio-group>
<el-date-picker v-if="viewType === 'day'" style="width: 200px; margin-left: 10px;" v-model="dayDate" type="date"
value-format="yyyy-MM-dd" placeholder="选择日期" @change="handleDayDateChange" />
<el-date-picker v-if="viewType === 'month'" style="width: 200px; margin-left: 10px;" v-model="monthDate"
type="month" value-format="yyyy-MM" placeholder="选择月份" @change="handleMonthDateChange" />
<el-date-picker v-if="viewType === 'year'" style="width: 200px; margin-left: 10px;" v-model="yearDate" type="year"
value-format="yyyy" placeholder="选择年份" @change="handleYearDateChange" />
<span v-if="viewType === 'custom'" style="margin-left: 10px;">
<el-date-picker style="width: 200px;" v-model="queryParams.startTime" type="datetime"
value-format="yyyy-MM-dd HH:mm:ss" placeholder="选择开始日期"></el-date-picker>
<el-date-picker style="width: 200px;" v-model="queryParams.endTime" type="datetime"
value-format="yyyy-MM-dd HH:mm:ss" placeholder="选择结束日期"></el-date-picker>
</span>
</div>
<el-row v-else>
<el-form label-width="80px" inline>
<template v-if="['out', 'loss'].includes(reportType)">
<el-form-item label="时间范围" prop="timeRange">
<time-range-picker v-model="queryParams" start-key="startTime" end-key="endTime"
:default-start-time="defaultStartTime" :default-end-time="defaultEndTime" @quick-select="handleQuery" />
</el-form-item>
</template>
<el-form-item v-else-if="reportType === 'day'" label="日期" prop="date">
<el-date-picker style="width: 200px;" v-model="dayDate" type="date" value-format="yyyy-MM-dd"
placeholder="选择日期" @change="handleDateChange"></el-date-picker>
</el-form-item>
<el-form-item v-else-if="reportType === 'month'" label="日期" prop="date">
<el-date-picker style="width: 200px;" v-model="monthDate" type="month" value-format="yyyy-MM"
placeholder="选择月份" @change="handleDateChange"></el-date-picker>
</el-form-item>
<el-form-item v-else-if="reportType === 'year'" label="日期" prop="date">
<el-date-picker style="width: 200px;" v-model="yearDate" type="year" value-format="yyyy" placeholder="选择年份"
@change="handleDateChange"></el-date-picker>
</el-form-item>
<template v-else-if="reportType === 'team'">
<el-form-item label="开始日期">
<el-date-picker style="width: 200px;" v-model="queryParams.byCreateTimeStart" type="date"
value-format="yyyy-MM-dd HH:mm:ss" placeholder="选择日期"></el-date-picker>
</el-form-item>
<el-form-item label="结束日期">
<el-date-picker style="width: 200px;" v-model="queryParams.byCreateTimeEnd" type="date"
value-format="yyyy-MM-dd HH:mm:ss" placeholder="选择日期"></el-date-picker>
</el-form-item>
<el-form-item label="班组">
<el-select v-model="queryParams.team" placeholder="请选择班组" style="width: 200px;">
<el-option label="甲" value="甲" />
<el-option label="乙" value="乙" />
</el-select>
</el-form-item>
</template>
</el-form>
</el-row>
<el-row>
<el-form label-width="80px" inline>
<template v-if="reportType !== 'all'">
<el-form-item label="入场钢卷号" prop="enterCoilNo">
<el-input style="width: 200px; display: inline-block;" v-model="queryParams.enterCoilNo"
placeholder="请输入入场钢卷号" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="当前钢卷号" prop="currentCoilNo">
<el-input style="width: 200px;" v-model="queryParams.currentCoilNo" placeholder="请输入当前钢卷号" clearable
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="产品名称" prop="itemName">
<el-input style="width: 200px;" v-model="queryParams.itemName" placeholder="请输入产品名称" clearable
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="规格" prop="itemSpecification">
<memo-input style="width: 200px;" v-model="queryParams.itemSpecification" storageKey="coilSpec"
placeholder="请选择规格" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="材质" prop="itemMaterial">
<dict-select style="width: 200px;" v-model="queryParams.itemMaterial" dict-type="coil_material"
placeholder="请选择材质" clearable @keyup.enter.native="handleQuery" multiple />
</el-form-item>
<el-form-item label="厂家" prop="itemManufacturer">
<dict-select style="width: 200px;" v-model="queryParams.itemManufacturer" dict-type="coil_manufacturer"
placeholder="请选择厂家" clearable @keyup.enter.native="handleQuery" multiple />
</el-form-item>
<el-form-item label="品质" prop="qualityStatusCsv">
<muti-select v-model="queryParams.qualityStatusCsv" :options="dict.type.coil_quality_status"
placeholder="请选择品质" clearable />
</el-form-item>
<el-form-item label="逻辑库位" prop="warehouseId">
<warehouse-select v-model="queryParams.warehouseId" placeholder="请选择仓库/库区/库位"
style="width: 100%; display: inline-block; width: 200px;" clearable />
</el-form-item>
</template>
<template v-else>
<el-form-item label="入场钢卷号" prop="enterCoilNo">
<el-input style="width: 200px; display: inline-block;" v-model="queryParams.enterCoilNo"
placeholder="请输入入场钢卷号" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="当前钢卷号" prop="currentCoilNo">
<el-input style="width: 200px;" v-model="queryParams.currentCoilNo" placeholder="请输入当前钢卷号" clearable
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="逻辑库位" prop="warehouseIds">
<el-select v-model="warehouseIds" collapse-tags multiple placeholder="请选择逻辑库位" style="width: 200px;">
<el-option v-for="item in warehouseOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="产品名称" prop="itemName">
<el-input style="width: 200px;" v-model="queryParams.itemName" placeholder="请输入产品名称" clearable
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="规格" prop="itemSpecification">
<memo-input style="width: 200px;" v-model="queryParams.itemSpecification" storageKey="coilSpec"
placeholder="请选择规格" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="材质" prop="itemMaterial">
<dict-select style="width: 200px;" v-model="queryParams.itemMaterial" dictType="coil_material"
placeholder="请选择材质" clearable multiple />
</el-form-item>
<el-form-item label="厂家" prop="itemManufacturer">
<dict-select style="width: 200px;" v-model="queryParams.itemManufacturer" dictType="coil_manufacturer"
placeholder="请选择厂家" clearable multiple />
</el-form-item>
<el-form-item label="品质" prop="qualityStatusCsv">
<muti-select v-model="queryParams.qualityStatusCsv" :options="dict.type.coil_quality_status"
placeholder="请选择品质" clearable />
</el-form-item>
</template>
<el-form-item>
<el-button type="primary" @click="handleQuery">查询</el-button>
<el-button v-if="['out', 'team', 'day', 'month', 'year', 'all'].includes(reportType)" type="primary"
@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>
<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>
<!-- out 类型的统计信息 -->
<el-descriptions v-if="reportType === 'out'" title="统计信息" :column="3" border>
<el-descriptions-item label="总钢卷数量">{{ outSummary.totalCount }}</el-descriptions-item>
<el-descriptions-item label="总重">{{ outSummary.totalWeight }}t</el-descriptions-item>
<el-descriptions-item label="均重">{{ outSummary.avgWeight }}t</el-descriptions-item>
</el-descriptions>
<!-- loss 类型的统计信息 -->
<el-descriptions v-else-if="reportType === 'loss'" title="统计信息" :column="3" border>
<el-descriptions-item label="总钢卷数量">{{ lossSummary.totalCount }}</el-descriptions-item>
<el-descriptions-item label="总重">{{ lossSummary.totalWeight }}t</el-descriptions-item>
<el-descriptions-item label="均重">{{ lossSummary.avgWeight }}t</el-descriptions-item>
</el-descriptions>
<!-- all 类型的统计信息comprehensive 风格 -->
<template v-else-if="reportType === 'all'">
<el-descriptions title="统计信息" :column="3" border>
<el-descriptions-item label="产出数量">{{ summary.outCount }}<span v-if="viewType === 'day' && yesterdaySummary.outCount" style="margin-left: 10px; font-size: 12px; color: #999;">(昨日: {{ yesterdaySummary.outCount }})</span></el-descriptions-item>
<el-descriptions-item label="产出总重">{{ summary.outTotalWeight }}t<span v-if="viewType === 'day' && yesterdaySummary.outTotalWeight" style="margin-left: 10px; font-size: 12px; color: #999;">(昨日: {{ yesterdaySummary.outTotalWeight }}t)</span></el-descriptions-item>
<el-descriptions-item label="产出均重">{{ summary.outAvgWeight }}t<span v-if="viewType === 'day' && yesterdaySummary.outAvgWeight" style="margin-left: 10px; font-size: 12px; color: #999;">(昨日: {{ yesterdaySummary.outAvgWeight }}t)</span></el-descriptions-item>
<el-descriptions-item label="消耗数量">{{ summary.lossCount }}<span v-if="viewType === 'day' && yesterdaySummary.lossCount" style="margin-left: 10px; font-size: 12px; color: #999;">(昨日: {{ yesterdaySummary.lossCount }})</span></el-descriptions-item>
<el-descriptions-item label="消耗总重">{{ summary.lossTotalWeight }}t<span v-if="viewType === 'day' && yesterdaySummary.lossTotalWeight" style="margin-left: 10px; font-size: 12px; color: #999;">(昨日: {{ yesterdaySummary.lossTotalWeight }}t)</span></el-descriptions-item>
<el-descriptions-item label="消耗均重">{{ summary.lossAvgWeight }}t<span v-if="viewType === 'day' && yesterdaySummary.lossAvgWeight" style="margin-left: 10px; font-size: 12px; color: #999;">(昨日: {{ yesterdaySummary.lossAvgWeight }}t)</span></el-descriptions-item>
<el-descriptions-item label="数量差值">{{ summary.countDiff }}<span v-if="viewType === 'day' && yesterdaySummary.countDiff" style="margin-left: 10px; font-size: 12px; color: #999;">(昨日: {{ yesterdaySummary.countDiff }})</span></el-descriptions-item>
<el-descriptions-item label="总重差值">{{ summary.weightDiff }}<span v-if="viewType === 'day' && yesterdaySummary.weightDiff" style="margin-left: 10px; font-size: 12px; color: #999;">(昨日: {{ yesterdaySummary.weightDiff }})</span></el-descriptions-item>
<el-descriptions-item label="均重差值">{{ summary.avgWeightDiff }}t<span v-if="viewType === 'day' && yesterdaySummary.avgWeightDiff" style="margin-left: 10px; font-size: 12px; color: #999;">(昨日: {{ yesterdaySummary.avgWeightDiff }}t)</span></el-descriptions-item>
<el-descriptions-item label="成品率">{{ summary.passRate }}<span v-if="viewType === 'day' && yesterdaySummary.passRate" style="margin-left: 10px; font-size: 12px; color: #999;">(昨日: {{ yesterdaySummary.passRate }})</span></el-descriptions-item>
<el-descriptions-item label="损耗率">{{ summary.lossRate }}<span v-if="viewType === 'day' && yesterdaySummary.lossRate" style="margin-left: 10px; font-size: 12px; color: #999;">(昨日: {{ yesterdaySummary.lossRate }})</span></el-descriptions-item>
<el-descriptions-item label="异常率">{{ summary.abRate }}<span v-if="viewType === 'day' && yesterdaySummary.abRate" style="margin-left: 10px; font-size: 12px; color: #999;">(昨日: {{ yesterdaySummary.abRate }})</span></el-descriptions-item>
<el-descriptions-item label="正品率">{{ summary.passRate2 }}<span v-if="viewType === 'day' && yesterdaySummary.passRate2" style="margin-left: 10px; font-size: 12px; color: #999;">(昨日: {{ yesterdaySummary.passRate2 }})</span></el-descriptions-item>
</el-descriptions>
<el-descriptions title="已处理M统计信息" :column="3" border>
<el-descriptions-item label="产出数量">{{ mSummary.outCount }}</el-descriptions-item>
<el-descriptions-item label="产出总重">{{ mSummary.outTotalWeight }}t</el-descriptions-item>
<el-descriptions-item label="产出均重">{{ mSummary.outAvgWeight }}t</el-descriptions-item>
<el-descriptions-item label="消耗数量">{{ mSummary.lossCount }}</el-descriptions-item>
<el-descriptions-item label="消耗总重">{{ mSummary.lossTotalWeight }}t</el-descriptions-item>
<el-descriptions-item label="消耗均重">{{ mSummary.lossAvgWeight }}t</el-descriptions-item>
<el-descriptions-item label="数量差值">{{ mSummary.countDiff }}</el-descriptions-item>
<el-descriptions-item label="总重差值">{{ mSummary.weightDiff }}</el-descriptions-item>
<el-descriptions-item label="均重差值">{{ mSummary.avgWeightDiff }}t</el-descriptions-item>
<el-descriptions-item label="成品率">{{ mSummary.passRate }}</el-descriptions-item>
<el-descriptions-item label="损耗率">{{ mSummary.lossRate }}</el-descriptions-item>
<el-descriptions-item label="异常率">{{ mSummary.abRate }}</el-descriptions-item>
<el-descriptions-item label="正品率">{{ mSummary.passRate2 }}</el-descriptions-item>
</el-descriptions>
<el-descriptions title="异常统计" :column="4" border>
<el-descriptions-item v-for="item in abSummary" :key="item.label" :label="item.label">{{ item.value
}}</el-descriptions-item>
</el-descriptions>
<!-- 月视图日数据折线图 -->
<div v-if="viewType === '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>
<!-- 分条信息统计 -->
<split-summary v-if="productionLine == '分条线'" :origin-outputlist="outList" :origin-loss-list="lossList"
:common-coil-ids="commonCoilIds"></split-summary>
</template>
<!-- team, day, month, year 类型的统计信息 -->
<template v-else>
<el-descriptions title="统计信息" :column="3" border>
<el-descriptions-item label="产出数量">{{ summary.outCount }}</el-descriptions-item>
<el-descriptions-item label="产出总重">{{ summary.outTotalWeight }}t</el-descriptions-item>
<el-descriptions-item label="产出均重">{{ summary.outAvgWeight }}t</el-descriptions-item>
<el-descriptions-item label="消耗数量">{{ summary.lossCount }}</el-descriptions-item>
<el-descriptions-item label="消耗总重">{{ summary.lossTotalWeight }}t</el-descriptions-item>
<el-descriptions-item label="消耗均重">{{ summary.lossAvgWeight }}t</el-descriptions-item>
<el-descriptions-item label="数量差值">{{ summary.countDiff }}</el-descriptions-item>
<el-descriptions-item label="总重差值">{{ summary.weightDiff }}</el-descriptions-item>
<el-descriptions-item label="均重差值">{{ summary.avgWeightDiff }}t</el-descriptions-item>
<el-descriptions-item label="成品率">{{ summary.passRate }}</el-descriptions-item>
<el-descriptions-item label="损耗率">{{ summary.lossRate }}</el-descriptions-item>
<el-descriptions-item label="异常率">{{ summary.abRate }}</el-descriptions-item>
<el-descriptions-item label="正品率">{{ summary.passRate2 }}</el-descriptions-item>
</el-descriptions>
<el-descriptions title="已处理M统计信息" :column="3" border>
<el-descriptions-item label="产出数量">{{ mSummary.outCount }}</el-descriptions-item>
<el-descriptions-item label="产出总重">{{ mSummary.outTotalWeight }}t</el-descriptions-item>
<el-descriptions-item label="产出均重">{{ mSummary.outAvgWeight }}t</el-descriptions-item>
<el-descriptions-item label="消耗数量">{{ mSummary.lossCount }}</el-descriptions-item>
<el-descriptions-item label="消耗总重">{{ mSummary.lossTotalWeight }}t</el-descriptions-item>
<el-descriptions-item label="消耗均重">{{ mSummary.lossAvgWeight }}t</el-descriptions-item>
<el-descriptions-item label="数量差值">{{ mSummary.countDiff }}</el-descriptions-item>
<el-descriptions-item label="总重差值">{{ mSummary.weightDiff }}</el-descriptions-item>
<el-descriptions-item label="均重差值">{{ mSummary.avgWeightDiff }}t</el-descriptions-item>
<el-descriptions-item label="成品率">{{ mSummary.passRate }}</el-descriptions-item>
<el-descriptions-item label="损耗率">{{ mSummary.lossRate }}</el-descriptions-item>
<el-descriptions-item label="异常率">{{ mSummary.abRate }}</el-descriptions-item>
<el-descriptions-item label="正品率">{{ mSummary.passRate2 }}</el-descriptions-item>
</el-descriptions>
<el-descriptions v-if="reportType === 'team'" title="异常统计" :column="4" border>
<el-descriptions-item v-for="item in abSummary" :key="item.label" :label="item.label">{{ item.value
}}</el-descriptions-item>
</el-descriptions>
<template v-if="reportType === 'team'">
<el-descriptions title="班组统计信息" :column="3" border>
</el-descriptions>
<el-table :data="teamSummary" border>
<el-table-column label="班组" align="center" prop="team" />
<el-table-column label="产出数量" align="center" prop="outCount" />
<el-table-column label="产出总重" align="center" prop="outWeight" />
</el-table>
</template>
</template>
<!-- 明细信息和标签页 -->
<!-- all 类型产出在前投入在后 -->
<template v-if="reportType === 'all'">
<el-descriptions title="明细信息" :column="3" border>
</el-descriptions>
<el-tabs v-model="activeTab">
<el-tab-pane label="产出钢卷" name="output">
<coil-table :columns="outputColumns" :data="outList" :highlight-config="{ rows: commonCoilIds }"></coil-table>
</el-tab-pane>
<el-tab-pane label="投入钢卷" name="loss">
<coil-table :columns="lossColumns" :data="lossList" :highlight-config="{ rows: commonCoilIds }"></coil-table>
</el-tab-pane>
</el-tabs>
</template>
<template v-else-if="reportType !== 'out' && reportType !== 'loss'">
<el-descriptions title="明细信息" :column="3" border>
</el-descriptions>
<el-tabs v-model="activeTab">
<el-tab-pane label="投入钢卷" name="loss">
<coil-table :data="lossList" :columns="lossColumns" :loading="loading" height="calc(100vh - 360px)" />
</el-tab-pane>
<el-tab-pane label="产出钢卷" name="output">
<coil-table :data="outList" :columns="outputColumns" :loading="loading" height="calc(100vh - 360px)" />
</el-tab-pane>
</el-tabs>
</template>
<!-- out 类型只显示产出钢卷 -->
<coil-table v-else-if="reportType === 'out'" :columns="outputColumns" :data="outList"></coil-table>
<!-- loss 类型只显示投入钢卷 -->
<coil-table v-else-if="reportType === 'loss'" :columns="lossColumns" :data="lossList"></coil-table>
<el-dialog title="列设置" :visible.sync="settingVisible" width="50%">
<el-radio-group v-model="activeColumnConfig">
<el-radio-button label="coil-report-loss">投入明细配置</el-radio-button>
<el-radio-button label="coil-report-output">产出明细配置</el-radio-button>
</el-radio-group>
<columns-setting :reportType="activeColumnConfig"></columns-setting>
</el-dialog>
</div>
</template>
<script>
import { listCoilWithIds } from "@/api/wms/coil";
import {
listLightPendingAction,
listPendingAction,
} from '@/api/wms/pendingAction';
import MemoInput from "@/components/MemoInput";
import MutiSelect from "@/components/MutiSelect";
import ProductInfo from "@/components/KLPService/Renderer/ProductInfo";
import RawMaterialInfo from "@/components/KLPService/Renderer/RawMaterialInfo";
import CoilNo from "@/components/KLPService/Renderer/CoilNo.vue";
import WarehouseSelect from "@/components/KLPService/WarehouseSelect";
import { calcSummary, calcAbSummary, calcTeamSummary, calcMSummary } from "@/views/wms/report/js/calc";
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 { saveReportFile } from "@/views/wms/report/js/reportFile";
import * as echarts from 'echarts';
export default {
name: 'MergeTemplate',
props: {
actionType: {
type: [Number, String],
required: true
},
productionLine: {
type: String,
default: '',
},
reportType: {
type: String,
default: 'day', // day | month | year | all | team | out | loss
},
warehouseOptions: {
type: Array,
default: () => []
}
},
components: {
MemoInput,
MutiSelect,
ProductInfo,
RawMaterialInfo,
CoilNo,
WarehouseSelect,
CoilTable,
ColumnsSetting,
TimeRangePicker,
SplitSummary,
},
dicts: ['product_coil_status', 'coil_material', 'coil_itemname', 'coil_manufacturer', 'coil_quality_status'],
data() {
const addZero = (num) => num.toString().padStart(2, '0')
const now = new Date()
const currentYear = `${now.getFullYear()}`
const currentMonth = `${now.getFullYear()}-${addZero(now.getMonth() + 1)}`
const currentDay = `${now.getFullYear()}-${addZero(now.getMonth() + 1)}-${addZero(now.getDate())}`
const yesterday = new Date(now)
yesterday.setDate(yesterday.getDate() - 1)
const yesYear = yesterday.getFullYear()
const yesMonth = addZero(yesterday.getMonth() + 1)
const yesDay = addZero(yesterday.getDate())
const defaultStartTime = `${yesYear}-${yesMonth}-${yesDay} 07:00:00`
const defaultEndTime = `${now.getFullYear()}-${addZero(now.getMonth() + 1)}-${addZero(now.getDate())} 07:00:00`
const getDayTimeRange = (dateStr) => {
const yearPattern = /^\d{4}$/;
const monthPattern = /^\d{4}-\d{2}$/;
const dayPattern = /^\d{4}-\d{2}-\d{2}$/;
let startDate, endDate;
if (yearPattern.test(dateStr)) {
startDate = `${dateStr}-01-01`;
endDate = `${dateStr}-12-31`;
} else if (monthPattern.test(dateStr)) {
const [year, month] = dateStr.split('-').map(Number);
startDate = `${dateStr}-01`;
const lastDayOfMonth = new Date(year, month, 0).getDate();
endDate = `${dateStr}-${lastDayOfMonth.toString().padStart(2, '0')}`;
} else if (dayPattern.test(dateStr)) {
startDate = dateStr;
endDate = dateStr;
} else {
throw new Error('输入格式错误,请传入 yyyy、yyyy-MM 或 yyyy-MM-dd 格式的字符串');
}
return {
start: `${startDate} 00:00:00`,
end: `${endDate} 23:59:59`
};
};
const { start, end } = getDayTimeRange(currentMonth);
return {
lossList: [],
outList: [],
activeTab: 'loss',
activeColumnConfig: 'coil-report-loss',
settingVisible: false,
loading: false,
dayDate: currentDay,
monthDate: currentMonth,
yearDate: currentYear,
defaultStartTime,
defaultEndTime,
getDayTimeRange,
// all 类型专用
viewType: 'custom',
yesterdaySummary: {},
monthChart: null,
queryParams: {
startTime: start,
endTime: end,
enterCoilNo: '',
currentCoilNo: '',
warehouseId: '',
itemName: '',
itemSpecification: '',
itemMaterial: '',
itemManufacturer: '',
team: '',
pageSize: 9999,
pageNum: 1,
},
warehouseIds: [
'1988150099140866050',
'1988150263284953089',
'1988150545175736322',
'1988150150521090049',
'2019583656787259393',
'2019583325311414274',
'2019583429955104769',
'2019583137616310273',
],
lossColumns: [],
outputColumns: [],
actionIds: ''
}
},
computed: {
summary() {
return calcSummary(this.outList, this.lossList)
},
mSummary() {
return calcMSummary(this.outList, this.lossList)
},
abSummary() {
return calcAbSummary(this.outList)
},
teamSummary() {
const teamOutSummary = calcTeamSummary(this.outList);
const allTeams = [...new Set(Object.keys(teamOutSummary))];
return allTeams.map(team => {
const outData = teamOutSummary[team] || { count: 0, weight: 0 };
const formatWeight = (weight) => Number(weight || 0).toFixed(2);
return {
team: team,
outCount: outData.count,
outWeight: formatWeight(outData.weight)
};
});
},
outSummary() {
const totalCount = this.outList.length;
const totalWeight = this.outList.reduce((acc, cur) => acc + parseFloat(cur.netWeight), 0);
const avgWeight = totalCount > 0 ? (totalWeight / totalCount).toFixed(2) : 0;
return {
totalCount,
totalWeight: totalWeight.toFixed(2),
avgWeight,
};
},
lossSummary() {
const totalCount = this.lossList.length;
const totalWeight = this.lossList.reduce((acc, cur) => acc + parseFloat(cur.netWeight), 0);
const avgWeight = totalCount > 0 ? (totalWeight / totalCount).toFixed(2) : 0;
return {
totalCount,
totalWeight: totalWeight.toFixed(2),
avgWeight,
};
},
commonCoilIds() {
if (this.productionLine !== '分条线') {
return []
}
const outputCoilIds = new Set(this.outList.map(item => item.coilId))
const lossCoilIds = new Set(this.lossList.map(item => item.coilId))
const commonIds = []
outputCoilIds.forEach(id => {
if (lossCoilIds.has(id)) {
commonIds.push(id)
}
})
return commonIds
},
},
watch: {
warehouseOptions: {
handler(newVal) {
if (newVal) {
this.warehouseIds = newVal.map(item => item.value);
}
},
immediate: true
}
},
created() {
this.initDateByReportType()
this.handleQuery()
this.loadColumns()
},
methods: {
initDateByReportType() {
if (this.reportType === 'out' || this.reportType === 'loss') {
this.queryParams.startTime = this.defaultStartTime;
this.queryParams.endTime = this.defaultEndTime;
} else if (this.reportType === 'team') {
const { start, end } = this.getDayTimeRange(this.dayDate);
this.queryParams.byCreateTimeStart = start;
this.queryParams.byCreateTimeEnd = end;
} else if (this.reportType === 'all') {
// all 类型默认使用当前月份作为自定义时间范围
this.viewType = 'custom';
const { start, end } = this.getDayTimeRange(this.monthDate);
this.queryParams.startTime = start;
this.queryParams.endTime = end;
} else {
let dateStr;
if (this.reportType === 'year') {
dateStr = this.yearDate;
} else if (this.reportType === 'month') {
dateStr = this.monthDate;
} else if (this.reportType === 'day') {
dateStr = this.dayDate;
}
const { start, end } = this.getDayTimeRange(dateStr);
this.queryParams.startTime = start;
this.queryParams.endTime = end;
}
},
handleDateChange() {
let dateStr;
if (this.reportType === 'year') {
dateStr = this.yearDate;
} else if (this.reportType === 'month') {
dateStr = this.monthDate;
} else if (this.reportType === 'day') {
dateStr = this.dayDate;
}
if (dateStr) {
const { start, end } = this.getDayTimeRange(dateStr);
this.queryParams.startTime = start;
this.queryParams.endTime = end;
this.handleQuery();
}
},
// all 类型:视图类型切换
handleViewTypeChange() {
switch (this.viewType) {
case 'day': {
const today = new Date();
const year = today.getFullYear();
const month = String(today.getMonth() + 1).padStart(2, '0');
const day = String(today.getDate()).padStart(2, '0');
this.dayDate = `${year}-${month}-${day}`;
this.handleDayDateChange();
break;
}
case 'month':
this.handleMonthDateChange();
break;
case 'year':
this.handleYearDateChange();
break;
case 'custom':
break;
}
},
// all 类型:日视图日期变更
handleDayDateChange() {
if (this.dayDate) {
const { start, end } = this.getDayTimeRange(this.dayDate);
this.queryParams.startTime = start;
this.queryParams.endTime = end;
this.handleQuery();
}
},
// all 类型:月视图日期变更
handleMonthDateChange() {
if (this.monthDate) {
const { start, end } = this.getDayTimeRange(this.monthDate);
this.queryParams.startTime = start;
this.queryParams.endTime = end;
this.handleQuery();
}
},
// all 类型:年视图日期变更
handleYearDateChange() {
if (this.yearDate) {
this.queryParams.startTime = `${this.yearDate}-01-01 00:00:00`;
this.queryParams.endTime = `${this.yearDate}-12-31 23:59:59`;
this.handleQuery();
}
},
handleQuery() {
this.getList()
},
async getList() {
this.loading = true;
if (this.reportType === 'all') {
// all 类型使用 comprehensive 方式查询
const res = await listLightPendingAction({ ...this.queryParams, 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);
this.actionIds = lossActionIds.join(',')
const outIds = [...new Set(res.data.filter(item => item.processedCoilIds).map(item => item.processedCoilIds))];
if (lossIds.length === 0 || outIds.length === 0) {
this.$message({ message: '查询结果为空', type: 'warning' })
this.loading = false;
return
}
const [lossRes, outRes] = await Promise.all([
listCoilWithIds({ ...this.queryParams, actionIds: lossActionIds.join(',') || '', startTime: '', endTime: '', selectType: 'raw_material' }),
listCoilWithIds({ ...this.queryParams, coilIds: outIds.join(',') || '', startTime: '', endTime: '', selectType: 'product' }),
]);
this.lossList = lossRes.rows.map(item => {
const [thickness, width] = item.specification?.split('*') || []
return { ...item, computedThickness: parseFloat(thickness), computedWidth: parseFloat(width) }
});
this.outList = outRes.rows.map(item => {
const [thickness, width] = item.specification?.split('*') || []
return { ...item, computedThickness: parseFloat(thickness), computedWidth: parseFloat(width) }
});
if (this.viewType === 'day') {
this.getYesterdayData()
}
if (this.viewType === 'month') {
this.$nextTick(() => {
this.initMonthChart()
})
}
this.loading = false;
return
}
const res = await listLightPendingAction({ ...this.queryParams, 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);
this.actionIds = lossActionIds.join(',')
const outIds = [...new Set(res.data.filter(item => item.processedCoilIds).map(item => item.processedCoilIds))];
if (lossIds.length === 0 || outIds.length === 0) {
this.$message({ message: '查询结果为空', type: 'warning' })
this.loading = false;
return
}
const [lossRes, outRes] = await Promise.all([
listCoilWithIds({ ...this.queryParams, actionIds: lossActionIds.join(',') || '', startTime: '', endTime: '', selectType: 'raw_material' }),
listCoilWithIds({ ...this.queryParams, coilIds: outIds.join(',') || '', startTime: '', endTime: '', selectType: 'product' }),
]);
if (this.reportType === 'out') {
this.outList = outRes.rows.map(item => {
const [thickness, width] = item.specification?.split('*') || []
return { ...item, computedThickness: parseFloat(thickness), computedWidth: parseFloat(width) }
});
this.lossList = [];
} else if (this.reportType === 'loss') {
this.lossList = lossRes.rows.map(item => {
const [thickness, width] = item.specification?.split('*') || []
return { ...item, computedThickness: parseFloat(thickness), computedWidth: parseFloat(width) }
});
this.outList = [];
} else {
this.lossList = lossRes.rows.map(item => {
const [thickness, width] = item.specification?.split('*') || []
return { ...item, computedThickness: parseFloat(thickness), computedWidth: parseFloat(width) }
});
this.outList = outRes.rows.map(item => {
const [thickness, width] = item.specification?.split('*') || []
return { ...item, computedThickness: parseFloat(thickness), computedWidth: parseFloat(width) }
});
}
this.loading = false;
},
// all 类型:获取昨日数据
getYesterdayData() {
const yesterday = new Date(this.dayDate)
yesterday.setDate(yesterday.getDate() - 1)
const year = yesterday.getFullYear()
const month = String(yesterday.getMonth() + 1).padStart(2, '0')
const day = String(yesterday.getDate()).padStart(2, '0')
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
})
listPendingAction({
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(',')
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, [])
}
})
})
},
// all 类型:初始化月视图折线图
initMonthChart() {
if (!this.$refs.monthChart) return
if (this.monthChart) {
this.monthChart.dispose()
}
this.monthChart = echarts.init(this.$refs.monthChart)
const dailyData = {}
this.outList.forEach(item => {
const date = item.createTime.split(' ')[0]
if (!dailyData[date]) {
dailyData[date] = { outCount: 0, outTotalWeight: 0, lossCount: 0, lossTotalWeight: 0 }
}
dailyData[date].outCount++
dailyData[date].outTotalWeight += parseFloat(item.netWeight) || 0
})
this.lossList.forEach(item => {
const date = item.actionCompleteTime?.split(' ')[0]
if (!dailyData[date]) {
dailyData[date] = { outCount: 0, outTotalWeight: 0, lossCount: 0, lossTotalWeight: 0 }
}
dailyData[date].lossCount++
dailyData[date].lossTotalWeight += parseFloat(item.netWeight) || 0
})
const dates = Object.keys(dailyData).sort()
const outCountData = dates.map(date => dailyData[date].outCount)
const outTotalWeightData = dates.map(date => dailyData[date].outTotalWeight.toFixed(2))
const lossCountData = dates.map(date => dailyData[date].lossCount)
const lossTotalWeightData = dates.map(date => dailyData[date].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: dates }],
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.monthChart.setOption(option)
window.addEventListener('resize', () => {
this.monthChart.resize()
})
},
exportData() {
if (this.outList.length === 0) {
this.$message.warning('暂无数据可导出')
return
}
let fileName = `materialCoil_${new Date().getTime()}.xlsx`;
if (['day', 'month', 'year', 'all'].includes(this.reportType)) {
fileName = `materialCoil_${this.queryParams.date || ''}_${new Date().getTime()}.xlsx`;
}
this.download('wms/materialCoil/export', {
coilIds: this.outList.map(item => item.coilId).join(',')
}, fileName)
},
exportLossData() {
if (this.lossList.length === 0) {
this.$message.warning('暂无数据可导出')
return
}
let fileName = `materialCoil_${new Date().getTime()}.xlsx`;
if (['day', 'month', 'year', 'all'].includes(this.reportType)) {
fileName = `materialCoil_${this.queryParams.date || ''}_${new Date().getTime()}.xlsx`;
}
this.download('wms/materialCoil/export', {
actionIds: this.actionIds
}, fileName)
},
saveOutputReport() {
this.loading = true
let reportTypeStr = '产出报表';
if (this.reportType === 'day') {
reportTypeStr = '产出报表,日报表';
} else if (this.reportType === 'month') {
reportTypeStr = '产出报表,月报表';
} else if (this.reportType === 'year') {
reportTypeStr = '产出报表,年报表';
} else if (this.reportType === 'team') {
reportTypeStr = '产出报表,班报表';
} else if (this.reportType === 'all') {
reportTypeStr = '产出报表,综合报表';
}
saveReportFile(this.outList.map(item => item.coilId).join(','), {
reportParams: this.queryParams,
reportType: reportTypeStr,
productionLine: this.productionLine,
}).then(res => {
this.$message({ message: '保存成功', type: 'success' })
}).catch(err => {
this.$message({ message: '保存失败', type: 'error' })
}).finally(() => {
this.loading = false
})
},
saveLossReport() {
this.loading = true
let reportTypeStr = '消耗报表';
if (this.reportType === 'day') {
reportTypeStr = '消耗报表,日报表';
} else if (this.reportType === 'month') {
reportTypeStr = '消耗报表,月报表';
} else if (this.reportType === 'year') {
reportTypeStr = '消耗报表,年报表';
} else if (this.reportType === 'team') {
reportTypeStr = '消耗报表,班报表';
} else if (this.reportType === 'all') {
reportTypeStr = '消耗报表,综合报表';
}
saveReportFile(this.lossList.map(item => item.coilId).join(','), {
reportParams: this.queryParams,
reportType: reportTypeStr,
productionLine: this.productionLine,
}).then(res => {
this.$message({ message: '保存成功', type: 'success' })
}).catch(err => {
this.$message({ message: '保存失败', type: 'error' })
}).finally(() => {
this.loading = false
})
},
loadColumns() {
this.lossColumns = JSON.parse(localStorage.getItem('preference-tableColumns-coil-report-loss') || '[]') || []
this.outputColumns = JSON.parse(localStorage.getItem('preference-tableColumns-coil-report-output') || '[]') || []
}
}
}
</script>
<style></style>