Files
klp-mono/apps/l2/src/views/index.vue

710 lines
20 KiB
Vue
Raw Normal View History

2025-10-10 16:47:38 +08:00
<template>
<div class="industrial-dashboard">
<!-- Header: 系统标题和当前时间 -->
<div class="dashboard-header">
<div class="header-left">
<h1 class="system-title">生产监控大屏</h1>
<p class="system-subtitle">实时生产数据与设备状态</p>
</div>
<div class="header-right">
2025-10-10 16:47:38 +08:00
<CurrentTime />
</div>
</div>
<!-- KPI指标区域 -->
<el-row :gutter="20" class="kpi-section">
<el-col :span="6" v-for="(kpi, index) in kpiData" :key="index">
<el-card class="kpi-card" :class="`kpi-${index}`">
<div class="kpi-content">
<div class="kpi-icon">
<i :class="kpi.icon"></i>
</div>
<div class="kpi-info">
<div class="kpi-label">{{ kpi.label }}</div>
<div class="kpi-value">{{ kpi.value }}</div>
<div class="kpi-unit">{{ kpi.unit }}</div>
</div>
<div class="kpi-trend" :class="kpi.trend">
<i :class="kpi.trend === 'up' ? 'el-icon-top' : 'el-icon-bottom'"></i>
<span>{{ kpi.change }}</span>
</div>
</div>
</el-card>
</el-col>
</el-row>
<!-- 生产状态区域 -->
<el-row :gutter="20" class="status-section">
<el-col :span="12">
<el-card class="status-card">
<div slot="header" class="card-header">
<span>生产状态</span>
<el-tag :type="productionStatus.type" size="small">{{ productionStatus.text }}</el-tag>
</div>
<div class="status-content">
<div class="status-item">
<span class="status-label">当前钢卷号</span>
<span class="status-value">{{ productionStatus.currentCoilId || '无' }}</span>
</div>
<div class="status-item">
<span class="status-label">带钢速度</span>
<span class="status-value">{{ productionStatus.stripSpeed || '0' }} m/min</span>
</div>
<div class="status-item">
<span class="status-label">产线效率</span>
<span class="status-value">{{ productionStatus.efficiency || '0' }}%</span>
</div>
</div>
</el-card>
</el-col>
<el-col :span="12">
<el-card class="status-card">
<div slot="header" class="card-header">
<span>设备状态</span>
</div>
<div class="equipment-grid">
<div class="equipment-item" v-for="(equipment, index) in equipmentStatus" :key="index">
<div class="equipment-name">{{ equipment.name }}</div>
<el-tag :type="equipment.status === 'running' ? 'success' : equipment.status === 'warning' ? 'warning' : 'danger'" size="mini">
{{ equipment.statusText }}
</el-tag>
</div>
</div>
</el-card>
2025-10-10 16:47:38 +08:00
</el-col>
</el-row>
<!-- 数据表格区域 -->
<el-row :gutter="20" class="table-section">
<el-col :span="12">
2025-10-10 16:47:38 +08:00
<el-card>
<div slot="header" class="card-header">
<span>系统告警信息</span>
<el-badge :value="alarmData.length" class="alarm-badge" v-if="alarmData.length > 0"></el-badge>
</div>
2025-10-10 16:47:38 +08:00
<MiniTable
v-loading="tableLoading"
:columns="alarmColumns"
:data="alarmData"
:highlightOnRowClick="true"
tableHeight="280px"
2025-10-10 16:47:38 +08:00
/>
</el-card>
</el-col>
<el-col :span="12">
2025-10-10 16:47:38 +08:00
<el-card>
<div slot="header" class="card-header">
<span>换辊信息</span>
</div>
2025-10-10 16:47:38 +08:00
<MiniTable
v-loading="rollHistoryLoading"
:columns="rollHistoryColumns"
:data="rollHistoryData"
tableHeight="280px"
2025-10-10 16:47:38 +08:00
/>
</el-card>
</el-col>
</el-row>
<el-row :gutter="20" class="table-section">
2025-10-10 16:47:38 +08:00
<el-col :span="24">
<el-card>
<div slot="header" class="card-header">
<span>生产计划</span>
<el-button type="text" size="small" @click="$router.push('/plan')">
查看全部
<i class="el-icon-arrow-right"></i>
</el-button>
</div>
2025-10-10 16:47:38 +08:00
<MiniTable
v-loading="planLoading"
:columns="planColumns"
:data="planData"
tableHeight="280px"
2025-10-10 16:47:38 +08:00
/>
</el-card>
</el-col>
</el-row>
<!-- 过程跟踪区域 -->
<el-row :gutter="20" class="table-section">
<el-col :span="24">
<el-card>
<div slot="header" class="card-header">
<span>过程跟踪</span>
<el-button type="text" size="small" @click="$router.push('/track')">
查看详情
<i class="el-icon-arrow-right"></i>
</el-button>
</div>
<TrackMeasure
v-loading="measureLoading"
:columns="measureColumns"
:data="measureData"
tableHeight="280px"
/>
</el-card>
</el-col>
2025-10-10 16:47:38 +08:00
</el-row>
<!-- 快速访问菜单 -->
<el-row :gutter="20" class="quick-access-section">
<el-col :span="24">
<el-card>
<div slot="header" class="card-header">
<span>快速访问</span>
</div>
<div class="quick-access-grid">
<div
class="access-item"
v-for="(card, index) in featureCards"
:key="index"
@click="$router.push(card.path)"
>
<div class="access-icon">
<i :class="`el-icon-${card.icon}`"></i>
</div>
<div class="access-info">
<div class="access-title">{{ card.title }}</div>
<div class="access-desc">{{ card.desc }}</div>
</div>
<div class="access-arrow">
<i class="el-icon-arrow-right"></i>
</div>
</div>
</div>
</el-card>
</el-col>
</el-row>
2025-10-10 16:47:38 +08:00
</div>
</template>
2025-10-10 16:47:38 +08:00
<script>
import CurrentTime from "./components/CurrentTime.vue";
import HomeMain from "./components/HomeMain.vue";
import MiniTable from "./components/MiniTable.vue";
// 引入日志API / 生产相关API
2025-10-10 16:47:38 +08:00
import { getLogDataPage } from "@/api/l2/log";
import { getRollHistorytList } from '@/api/l2/roller'
import { listPlan } from "@/api/l2/plan";
import TrackMeasure from "@/components/TrackMeasure/index.vue";
import { getCurrentProducingPlan, getCurrentProcessParams } from "@/api/business/dashboard";
2025-10-10 16:47:38 +08:00
export default {
name: "Index",
components: { CurrentTime, HomeMain, MiniTable, TrackMeasure },
2025-10-10 16:47:38 +08:00
data() {
return {
// KPI指标数据
kpiData: [
{
label: '日产量',
value: '0',
unit: 't',
icon: 'el-icon-data-line',
trend: 'up',
change: '+5.2%'
},
{
label: '成材率',
value: '0',
unit: '%',
icon: 'el-icon-pie-chart',
trend: 'up',
change: '+1.8%'
},
{
label: '成品卷数',
value: '0',
unit: '卷',
icon: 'el-icon-box',
trend: 'up',
change: '+12'
},
{
label: '产线效率',
value: '0',
unit: '%',
icon: 'el-icon-cpu',
trend: 'down',
change: '-2.1%'
}
],
// 生产状态
productionStatus: {
type: 'success',
text: '运行中',
currentCoilId: '',
stripSpeed: '0',
efficiency: '0'
},
// 设备状态
equipmentStatus: [
{ name: '入口段', status: 'running', statusText: '运行中' },
{ name: '炉区', status: 'running', statusText: '运行中' },
{ name: '镀层段', status: 'running', statusText: '运行中' },
{ name: '出口段', status: 'running', statusText: '运行中' },
{ name: '张力控制', status: 'running', statusText: '运行中' },
{ name: '温度控制', status: 'running', statusText: '运行中' }
],
// 快速访问功能卡片
featureCards: [
{ title: "生产计划", desc: "生产计划管理", icon: "s-order", path: "/plan" },
{ title: "日志记录", desc: "日志记录管理", icon: "document", path: "/log" },
{ title: "轧辊管理", desc: "轧辊管理", icon: "setting", path: "/roller" },
{ title: "停机管理", desc: "停机管理", icon: "warning", path: "/stop" },
{ title: "过程跟踪", desc: "实时过程监控", icon: "monitor", path: "/track" },
{ title: "实绩数据", desc: "查看生产实绩数据", icon: "data-analysis", path: "/pdo" }
],
// 表格列配置(与日志字段对应)
alarmColumns: [
{ label: "发生时间", prop: "timestamp", width: "200px" },
{ label: "报警模块", prop: "module", width: "120px" },
{ label: "报警类型", prop: "logtype", width: "120px" },
{ label: "警报内容", prop: "logtext" },
],
rollHistoryColumns: [
{ label: "换辊号", prop: "changeid", width: "120px" },
{ label: "轧辊号", prop: "rollid", width: "120px" },
{ label: "机组", prop: "seton", width: "80px" },
{ label: "班次", prop: "shift", width: "80px" },
{ label: "班组", prop: "crew", width: "80px" },
{ label: "机架号", prop: "standid", width: "100px" },
{ label: "位置", prop: "position", width: "80px" },
{ label: '直径', prop: 'diameter', width: '100px' },
{ label: '粗糙度', prop: 'rough', width: '100px' },
{ label: '凸度', prop: 'crown', width: '100px' },
{ label: '成分', prop: 'composition', width: '100px' },
],
planColumns: [
{ label: '顺序号', prop: 'seqid', width: '80px' },
{ label: '钢卷号', prop: 'coilid', width: '120px' },
{ label: '机组号', prop: 'unitCode', width: '100px' },
{ label: '计划号', prop: 'planid', width: '120px' },
{ label: '计划类型', prop: 'planType', width: '100px' },
{ label: '钢种', prop: 'steelGrade', width: '120px' },
{ label: '出口卷号', prop: 'exitCoilid', width: '120px' },
{ label: '订单号', prop: 'orderNo', width: '120px' },
{ label: '客户代码', prop: 'custommerCode', width: '120px' },
{ label: '上线时间', prop: 'onlineDate', width: '160px' },
{ label: '开始时间', prop: 'startDate', width: '160px' },
{ label: '结束时间', prop: 'endDate', width: '160px' },
],
2025-10-10 16:47:38 +08:00
alarmData: [], // 表格数据从API获取
queryForm: { pageNum: 1, pageSize: 10 }, // 分页参数
tableLoading: false, // 加载状态
rollHistoryData: [], // 轧辊历史数据
rollHistoryLoading: false, // 轧辊历史数据加载状态
planData: [], // 生产计划数据
planLoading: false, // 生产计划数据加载状态
measureData: [], // 过程跟踪数据
measureLoading: false, // 过程跟踪加载状态
measureColumns: [] // 过程跟踪列配置
2025-10-10 16:47:38 +08:00
};
},
created() {
// 页面加载时调用API获取数据
this.getLogData();
this.getRollHistorytList();
this.getPlanList();
this.refreshDashboard();
// 定期刷新数据
this.refreshTimer = setInterval(() => {
this.getLogData();
this.getRollHistorytList();
this.getPlanList();
this.refreshDashboard();
}, 30000); // 每30秒刷新一次
},
beforeDestroy() {
// 组件销毁时清除定时器
if (this.refreshTimer) {
clearInterval(this.refreshTimer);
}
2025-10-10 16:47:38 +08:00
},
methods: {
// 获取日志数据
2025-10-10 16:47:38 +08:00
getLogData() {
this.tableLoading = true;
getLogDataPage(this.queryForm)
.then((response) => {
this.tableLoading = false;
this.alarmData = response.data.list || []; // 赋值表格数据
2025-10-10 16:47:38 +08:00
})
.catch((error) => {
this.tableLoading = false;
console.error("获取日志数据失败:", error);
this.$message.error("获取日志数据失败,请稍后重试");
2025-10-10 16:47:38 +08:00
});
},
// 获取轧辊历史列表
2025-10-10 16:47:38 +08:00
getRollHistorytList() {
this.rollHistoryLoading = true;
2025-10-10 16:47:38 +08:00
getRollHistorytList(this.queryForm)
.then((response) => {
this.rollHistoryLoading = false;
this.rollHistoryData = response.data.list || []; // 赋值表格数据
2025-10-10 16:47:38 +08:00
})
.catch((error) => {
this.rollHistoryLoading = false;
console.error("获取轧辊历史数据失败:", error);
});
2025-10-10 16:47:38 +08:00
},
// 获取生产计划列表
2025-10-10 16:47:38 +08:00
getPlanList() {
this.planLoading = true;
2025-10-10 16:47:38 +08:00
listPlan(this.queryForm)
.then((response) => {
this.planLoading = false;
this.planData = (response.data || []).map(item => {
2025-10-10 16:47:38 +08:00
return {
...item,
onlineDate: item.onlineDate?.replace('T', ' '),
startDate: item.startDate?.replace('T', ' '),
endDate: item.endDate?.replace('T', ' '),
furInDate: item.furInDate?.replace('T', ' '),
furOutDate: item.furOutDate?.replace('T', ' '),
}
}); // 赋值表格数据
})
.catch((error) => {
this.planLoading = false;
console.error("获取计划数据失败:", error);
});
},
// 从后端刷新首页仪表板数据
refreshDashboard() {
// 1) 当前生产计划
getCurrentProducingPlan().then(res => {
const data = res.data || {};
// 更新生产状态中的卷号等信息
this.productionStatus.currentCoilId = data.coilid || '';
// 这里可以根据需要扩展更多字段,例如钢种、入口规格等
});
// 2) 当前生产卷的工艺参数
getCurrentProcessParams().then(res => {
const data = res.data || {};
// 带钢速度示例优先取出口段TR的speedExitSection其次取入口段POR1/POR2的stripSpeed
let stripSpeed = 0;
const exit = data.exitSection || {};
const entry = data.entrySection || {};
if (exit.TR && (exit.TR.speedExitSection || exit.TR.stripSpeed)) {
stripSpeed = exit.TR.speedExitSection || exit.TR.stripSpeed;
} else if (entry.POR1 && entry.POR1.stripSpeed) {
stripSpeed = entry.POR1.stripSpeed;
} else if (entry.POR2 && entry.POR2.stripSpeed) {
stripSpeed = entry.POR2.stripSpeed;
}
this.productionStatus.stripSpeed = stripSpeed || 0;
// 产线效率目前后端没有直接指标可根据需要后续扩展这里先保持0或从其他接口获取
// this.productionStatus.efficiency = ...;
// KPI 区域暂时用真实卷号和速度填充一部分,其他可根据后端需要扩展
this.kpiData[0].value = (stripSpeed || 0).toFixed(1); // 用带钢速度临时占位,后续可改为产量
this.kpiData[1].value = '0';
this.kpiData[2].value = '0';
this.kpiData[3].value = this.productionStatus.efficiency || '0';
});
2025-10-10 16:47:38 +08:00
}
},
};
</script>
<style scoped lang="scss">
/* 主题色与布局样式 */
2025-10-10 16:47:38 +08:00
$theme-primary: #a7acb4;
$theme-light: rgba(167, 172, 180, 0.8);
$theme-dark: rgba(140, 145, 153, 0.8);
$theme-bg1: #454c51;
$theme-bg2: #454c51;
$theme-bg3: #1E2227;
$theme-text-light: #f0f0f0;
$theme-text-gray: #c9cdcf;
.industrial-dashboard {
padding: 20px;
background: #f2f3f5; // 工业风浅灰背景,避免过深色
min-height: calc(100vh - 60px);
}
/* 仪表板头部 */
.dashboard-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding: 20px;
background: #ffffff; // 简洁白色背景,避免低级渐变色
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
.header-left {
.system-title {
color: #000;
font-size: 28px;
font-weight: 600;
margin: 0 0 8px 0;
}
.system-subtitle {
color: #000;
font-size: 14px;
margin: 0;
}
}
}
/* KPI区域 */
.kpi-section {
margin-bottom: 20px;
.kpi-card {
border-radius: 8px;
transition: all 0.3s ease;
cursor: pointer;
&:hover {
transform: translateY(-4px);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15);
}
.kpi-content {
display: flex;
align-items: center;
padding: 10px 0;
.kpi-icon {
font-size: 40px;
margin-right: 16px;
color: #409eff;
}
.kpi-info {
flex: 1;
.kpi-label {
font-size: 14px;
color: #909399;
margin-bottom: 8px;
}
.kpi-value {
font-size: 28px;
font-weight: 600;
color: #303133;
line-height: 1;
}
.kpi-unit {
font-size: 12px;
color: #909399;
margin-top: 4px;
}
}
.kpi-trend {
display: flex;
align-items: center;
font-size: 12px;
padding: 4px 8px;
border-radius: 4px;
&.up {
color: #67c23a;
background: rgba(103, 194, 58, 0.1);
}
&.down {
color: #f56c6c;
background: rgba(245, 108, 108, 0.1);
}
i {
margin-right: 4px;
}
}
}
&.kpi-0 .kpi-icon { color: #409eff; }
&.kpi-1 .kpi-icon { color: #67c23a; }
&.kpi-2 .kpi-icon { color: #e6a23c; }
&.kpi-3 .kpi-icon { color: #f56c6c; }
}
}
/* 状态区域 */
.status-section {
margin-bottom: 20px;
.status-card {
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
font-weight: 600;
}
.status-content {
padding: 10px 0;
.status-item {
display: flex;
justify-content: space-between;
padding: 12px 0;
border-bottom: 1px solid #ebeef5;
&:last-child {
border-bottom: none;
}
.status-label {
color: #909399;
font-size: 14px;
}
.status-value {
color: #303133;
font-size: 16px;
font-weight: 600;
}
}
}
.equipment-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
padding: 10px 0;
.equipment-item {
display: flex;
flex-direction: column;
align-items: center;
padding: 12px;
background: #f5f7fa;
border-radius: 6px;
transition: all 0.3s ease;
&:hover {
background: #ecf5ff;
}
.equipment-name {
font-size: 13px;
color: #606266;
margin-bottom: 8px;
text-align: center;
}
}
}
}
}
/* 表格区域 */
.table-section {
margin-bottom: 20px;
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
font-weight: 600;
.alarm-badge {
margin-left: 10px;
}
}
}
/* 快速访问区域 */
.quick-access-section {
.quick-access-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
padding: 10px 0;
.access-item {
display: flex;
align-items: center;
padding: 16px;
background: #fff;
border: 1px solid #ebeef5;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
&:hover {
border-color: #409eff;
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.15);
transform: translateY(-2px);
}
.access-icon {
font-size: 32px;
color: #409eff;
margin-right: 16px;
}
.access-info {
flex: 1;
.access-title {
font-size: 16px;
font-weight: 600;
color: #303133;
margin-bottom: 4px;
}
.access-desc {
font-size: 12px;
color: #909399;
}
}
.access-arrow {
color: #c0c4cc;
font-size: 18px;
}
}
}
}
/* 响应式设计 */
@media (max-width: 1200px) {
.quick-access-grid {
grid-template-columns: repeat(2, 1fr) !important;
}
}
@media (max-width: 768px) {
.kpi-section .el-col {
margin-bottom: 16px;
}
.status-section .el-col {
margin-bottom: 16px;
}
.equipment-grid {
grid-template-columns: repeat(2, 1fr) !important;
}
.quick-access-grid {
grid-template-columns: 1fr !important;
}
.dashboard-header {
flex-direction: column;
align-items: flex-start;
.header-right {
margin-top: 16px;
}
}
}
2025-10-10 16:47:38 +08:00
</style>