feat: 新增锌线生产监控模块及相关API和组件
refactor(auth): 增加锌线系统token管理功能 feat(api): 添加锌线停机记录、生产报表和设备快照API feat(views): 实现锌线实时监控、生产统计和停机统计页面 feat(components): 开发锌线生产报表、停机统计和班组绩效组件 feat(utils): 新增锌线专用请求工具zinc1Request chore(vue.config): 配置锌线API代理
This commit is contained in:
155
klp-ui/src/views/lines/zinc/components/product-statistic.vue
Normal file
155
klp-ui/src/views/lines/zinc/components/product-statistic.vue
Normal file
@@ -0,0 +1,155 @@
|
||||
<template>
|
||||
<div class="report-page" style="padding: 20px;">
|
||||
<!-- 汇总统计信息 el-descriptions 展示 -->
|
||||
<el-card shadow="hover" class="summary-card" style="margin-bottom: 20px;">
|
||||
<div slot="header" class="clearfix">
|
||||
<span style="font-size: 16px; font-weight: bold;">生产报表-数据汇总</span>
|
||||
</div>
|
||||
<el-descriptions
|
||||
:data="productStatistic"
|
||||
title=""
|
||||
border
|
||||
column="4"
|
||||
size="middle"
|
||||
label-width="140px"
|
||||
>
|
||||
<el-descriptions-item label="总出口宽度(mm)">{{ productStatistic.totalExitWidth || 0 }}</el-descriptions-item>
|
||||
<el-descriptions-item label="总出口长度(m)">{{ formatNum(productStatistic.totalExitLength) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="总理论重量(kg)">{{ formatNum(productStatistic.totalTheoryWeight) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="总实际重量(kg)">{{ formatNum(productStatistic.totalActualWeight) }}</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item label="总出口厚度(mm)">{{ formatNum(productStatistic.totalExitThickness) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="平均出口宽度(mm)">{{ formatNum(productStatistic.avgExitWidth) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="平均出口长度(m)">{{ formatNum(productStatistic.avgExitLength) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="平均理论重量(kg)">{{ formatNum(productStatistic.avgTheoryWeight) }}</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item label="平均实际重量(kg)">{{ formatNum(productStatistic.avgActualWeight) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="平均出口厚度(mm)">{{ formatNum(productStatistic.avgExitThickness) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="总来料重量(kg)">{{ formatNum(productStatistic.totalEntryWeight) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="总卷数">{{ productStatistic.coilCount || 0 }}</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item label="成材率" span="4" label-width="140px">
|
||||
<span style="color: #1890ff;">{{ formatRate(productStatistic.yieldRate) }}</span>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-card>
|
||||
|
||||
<!-- 明细数据 el-table 展示 -->
|
||||
<el-card shadow="hover" class="detail-card">
|
||||
<div slot="header" class="clearfix">
|
||||
<span style="font-size: 16px; font-weight: bold;">生产报表-明细数据</span>
|
||||
</div>
|
||||
<el-table
|
||||
:data="reportDetails"
|
||||
border
|
||||
stripe
|
||||
size="small"
|
||||
v-loading="tableLoading"
|
||||
element-loading-text="加载中..."
|
||||
style="width: 100%;"
|
||||
highlight-current-row
|
||||
>
|
||||
<el-table-column prop="exitMatId" label="出口物料编码" align="center" />
|
||||
<el-table-column prop="entryMatId" label="入口物料编码" align="center" />
|
||||
<el-table-column prop="groupNo" label="班组号" align="center" width="60" />
|
||||
<el-table-column prop="shiftNo" label="班次号" align="center" width="60" />
|
||||
<el-table-column prop="steelGrade" label="钢种" align="center" width="80" />
|
||||
<el-table-column prop="exitWidth" label="出口宽度(mm)" align="center">
|
||||
<template #default="scope">{{ formatNum(scope.row.exitWidth) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="exitLength" label="出口长度(m)" align="center">
|
||||
<template #default="scope">{{ formatNum(scope.row.exitLength) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="exitThickness" label="出口厚度(mm)" align="center">
|
||||
<template #default="scope">{{ formatNum(scope.row.exitThickness) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="theoryWeight" label="理论重量(kg)" align="center">
|
||||
<template #default="scope">{{ formatNum(scope.row.theoryWeight) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="actualWeight" label="实际重量(kg)" align="center">
|
||||
<template #default="scope">{{ formatNum(scope.row.actualWeight) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="onlineTime" label="上线时间" align="center" width="190">
|
||||
<template #default="scope">{{ formatTime(scope.row.onlineTime) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="endTime" label="结束时间" align="center" width="190">
|
||||
<template #default="scope">{{ formatTime(scope.row.endTime) }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getReportSummary, getReportDetails } from '@/api/lines/zinc/report'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
productStatistic: {}, // 汇总数据
|
||||
reportDetails: [], // 明细数据
|
||||
tableLoading: false, // 表格加载状态
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 数字格式化:保留2位小数,空值显示0
|
||||
formatNum(num) {
|
||||
return num ? Number(num).toFixed(2) : '0.00'
|
||||
},
|
||||
// 成材率格式化:保留2位小数+百分比
|
||||
formatRate(rate) {
|
||||
return rate ? (Number(rate) * 100).toFixed(2) + '%' : '0.00%'
|
||||
},
|
||||
// 时间格式化:处理ISO时间/空值/null,统一格式
|
||||
formatTime(time) {
|
||||
if (!time) return '-'
|
||||
const date = new Date(time)
|
||||
const year = date.getFullYear()
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0')
|
||||
const day = date.getDate().toString().padStart(2, '0')
|
||||
const hh = date.getHours().toString().padStart(2, '0')
|
||||
const mm = date.getMinutes().toString().padStart(2, '0')
|
||||
const ss = date.getSeconds().toString().padStart(2, '0')
|
||||
return `${year}-${month}-${day} ${hh}:${mm}:${ss}`
|
||||
},
|
||||
// 获取汇总数据
|
||||
async getReportSummary() {
|
||||
try {
|
||||
const res = await getReportSummary()
|
||||
this.productStatistic = res || {}
|
||||
} catch (err) {
|
||||
this.$message.error('获取汇总数据失败!')
|
||||
console.error(err)
|
||||
}
|
||||
},
|
||||
// 获取明细数据
|
||||
async getReportDetails() {
|
||||
this.tableLoading = true
|
||||
try {
|
||||
const res = await getReportDetails()
|
||||
this.reportDetails = res || []
|
||||
} catch (err) {
|
||||
this.$message.error('获取明细数据失败!')
|
||||
console.error(err)
|
||||
} finally {
|
||||
this.tableLoading = false
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 页面加载时调用两个接口
|
||||
this.getReportSummary()
|
||||
this.getReportDetails()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.report-page {
|
||||
background: #f5f7fa;
|
||||
min-height: calc(100vh - 120px);
|
||||
}
|
||||
.summary-card, .detail-card {
|
||||
background: #fff;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
实时监控
|
||||
</div>
|
||||
</template>
|
||||
165
klp-ui/src/views/lines/zinc/components/shutdown-statistic.vue
Normal file
165
klp-ui/src/views/lines/zinc/components/shutdown-statistic.vue
Normal file
@@ -0,0 +1,165 @@
|
||||
<template>
|
||||
<div class="stoppage-page" style="padding: 20px;">
|
||||
<!-- 月份筛选查询区域 -->
|
||||
<div class="search-box" style="margin: 16px 0; display: flex; align-items: center;">
|
||||
<el-date-picker
|
||||
v-model="month"
|
||||
type="month"
|
||||
placeholder="请选择月份"
|
||||
value-format="yyyy-MM"
|
||||
style="width: 200px;"
|
||||
@change="handleMonthChange"
|
||||
/>
|
||||
<el-button type="primary" icon="el-icon-search" style="margin-left: 10px;" @click="handleQuery">查询</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 停机统计表格 ✅【全部修改】适配真实JSON数据字段 -->
|
||||
<el-table
|
||||
:data="stoppageList"
|
||||
border
|
||||
stripe
|
||||
size="small"
|
||||
v-loading="loading"
|
||||
element-loading-text="数据加载中..."
|
||||
style="width: 100%;"
|
||||
highlight-current-row
|
||||
>
|
||||
<el-table-column prop="stopType" label="停机类型" align="center" width="140" />
|
||||
<el-table-column prop="startDate" label="停机开始时间" align="center" width="200" />
|
||||
<el-table-column prop="endDate" label="停机结束时间" align="center" width="200" />
|
||||
<el-table-column prop="duration" label="停机时长" align="center" width="120">
|
||||
<template #default="scope">
|
||||
{{ formatDuration(scope.row.duration) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="insdate" label="数据录入时间" align="center" width="200" />
|
||||
<!-- 生产相关附属字段 -->
|
||||
<el-table-column prop="coilid" label="钢卷号" align="center" width="120" />
|
||||
<el-table-column prop="shift" label="班次" align="center" width="100" />
|
||||
<!-- <el-table-column prop="crew" label="班组人员" align="center" width="120" />
|
||||
<el-table-column prop="area" label="区域" align="center" width="100" />
|
||||
<el-table-column prop="unit" label="机组" align="center" width="100" />
|
||||
<el-table-column prop="seton" label="开机人" align="center" width="100" /> -->
|
||||
<!-- 备注字段 -->
|
||||
<el-table-column prop="remark" label="备注" align="center" min-width="220" show-overflow-tooltip />
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listStoppage } from '@/api/lines/zinc/stoppage'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
stoppageList: [], // 停机统计列表
|
||||
total: 0, // 数据总条数
|
||||
loading: false, // 加载状态
|
||||
month: '', // 选中的月份 yyyy-MM格式
|
||||
queryParams: {
|
||||
startDate: '', // 开始时间 yyyy-MM-dd✅ 与后端字段名一致
|
||||
endDate: '' // 结束时间 yyyy-MM-dd✅ 与后端字段名一致
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// 初始化:默认选中当前月份,并生成当月的起止时间
|
||||
this.initDefaultMonth()
|
||||
},
|
||||
mounted() {
|
||||
// 页面加载时,默认查询当月停机数据
|
||||
this.listStoppage()
|
||||
},
|
||||
methods: {
|
||||
// 格式化持续时间:将分钟数转换为"X天X小时X分钟"格式
|
||||
formatDuration(minutes) {
|
||||
if (minutes === null || minutes === undefined || minutes === '') return '—'
|
||||
const totalMinutes = Math.floor(Number(minutes))
|
||||
if (isNaN(totalMinutes) || totalMinutes < 0) return '—'
|
||||
|
||||
const days = Math.floor(totalMinutes / (24 * 60))
|
||||
const hours = Math.floor((totalMinutes % (24 * 60)) / 60)
|
||||
const mins = Math.floor(totalMinutes % 60)
|
||||
|
||||
let result = ''
|
||||
if (days > 0) {
|
||||
result += `${days}天`
|
||||
}
|
||||
if (hours > 0 || days > 0) {
|
||||
result += `${hours}小时`
|
||||
}
|
||||
if (mins > 0 || result === '') {
|
||||
result += `${mins}分钟`
|
||||
}
|
||||
|
||||
return result || '0分钟'
|
||||
},
|
||||
/** 初始化默认月份和起止时间 */
|
||||
initDefaultMonth() {
|
||||
const now = new Date()
|
||||
const year = now.getFullYear()
|
||||
const month = (now.getMonth() + 1).toString().padStart(2, '0')
|
||||
this.month = `${year}-${month}`
|
||||
// 根据当前月份生成时间范围
|
||||
this.setMonthTimeRange(this.month)
|
||||
},
|
||||
|
||||
/** 选中月份 → 生成 当月1号00:00:00 至 下个月1号00:00:00 的时间格式 */
|
||||
setMonthTimeRange(month) {
|
||||
if (!month) return
|
||||
// 开始时间:选中月份的 1号 00:00:00
|
||||
this.queryParams.startDate = `${month}-01`
|
||||
// 解析选中的月份为日期对象
|
||||
const selectMonth = new Date(month + '-01')
|
||||
// 下个月1号:月份+1,日期为1号
|
||||
const nextMonth = new Date(selectMonth.setMonth(selectMonth.getMonth() + 1))
|
||||
const nextYear = nextMonth.getFullYear()
|
||||
const nextMonthNum = (nextMonth.getMonth() + 1).toString().padStart(2, '0')
|
||||
// 结束时间:下个月 1号 00:00:00
|
||||
this.queryParams.endDate = `${nextYear}-${nextMonthNum}-01`
|
||||
},
|
||||
|
||||
/** 月份选择器切换事件 */
|
||||
handleMonthChange(val) {
|
||||
this.setMonthTimeRange(val)
|
||||
},
|
||||
|
||||
/** 查询按钮点击事件 */
|
||||
handleQuery() {
|
||||
this.listStoppage()
|
||||
},
|
||||
|
||||
/** 核心:查询停机统计列表数据,带时间筛选参数 */
|
||||
async listStoppage() {
|
||||
this.loading = true
|
||||
try {
|
||||
const params = { ...this.queryParams }
|
||||
const res = await listStoppage(params)
|
||||
// 适配后端返回格式:数组/分页对象都兼容
|
||||
this.stoppageList = res.data || res || []
|
||||
this.total = res.total || this.stoppageList.length
|
||||
} catch (err) {
|
||||
this.$message.error('查询停机统计数据失败!')
|
||||
console.error('停机统计查询异常:', err)
|
||||
this.stoppageList = []
|
||||
this.total = 0
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.stoppage-page {
|
||||
background: #f5f7fa;
|
||||
min-height: calc(100vh - 40px);
|
||||
}
|
||||
.search-box {
|
||||
background: #fff;
|
||||
padding: 10px 16px;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
||||
}
|
||||
</style>
|
||||
56
klp-ui/src/views/lines/zinc/index.vue
Normal file
56
klp-ui/src/views/lines/zinc/index.vue
Normal file
@@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-tabs v-model="activeTab" type="card">
|
||||
<el-tab-pane label="实时监控" name="1">
|
||||
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="生产统计" name="2">
|
||||
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="停机统计" name="3">
|
||||
|
||||
</el-tab-pane>
|
||||
<!-- <el-tab-pane label="班组绩效" name="4">
|
||||
|
||||
</el-tab-pane> -->
|
||||
</el-tabs>
|
||||
|
||||
<div>
|
||||
<div v-if="activeTab === '1'">
|
||||
<realTimeMonitor></realTimeMonitor>
|
||||
</div>
|
||||
<div v-if="activeTab === '2'">
|
||||
<productStatistic></productStatistic>
|
||||
</div>
|
||||
<div v-if="activeTab === '3'">
|
||||
<shutdownStatistic></shutdownStatistic>
|
||||
</div>
|
||||
<!-- <div v-if="activeTab === '4'">
|
||||
<TeamPerformance></TeamPerformance>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// import TeamPerformance from './components/team-performance.vue'
|
||||
import ShutdownStatistic from './components/shutdown-statistic.vue';
|
||||
import ProductStatistic from './components/product-statistic.vue';
|
||||
import RealTimeMonitor from './components/real-time-monitoring.vue';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
activeTab: '1'
|
||||
}
|
||||
},
|
||||
components: {
|
||||
// TeamPerformance,
|
||||
ShutdownStatistic,
|
||||
ProductStatistic,
|
||||
RealTimeMonitor
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
Reference in New Issue
Block a user