Files
klp-mono/apps/l2/src/views/index.vue
砂糖 7ba0683fb7 feat: 添加炉火参数设置功能并国际化界面
refactor: 重构工艺参数面板组件结构

style: 更新界面样式和布局

docs: 添加i18n多语言支持

chore: 新增API接口和工具组件

fix: 修复部分组件显示问题

perf: 优化数据加载和渲染性能

test: 更新测试用例

build: 添加依赖配置
2026-01-04 15:07:47 +08:00

710 lines
20 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="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">
<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>
</el-col>
</el-row>
<!-- 数据表格区域 -->
<el-row :gutter="20" class="table-section">
<el-col :span="12">
<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>
<MiniTable
v-loading="tableLoading"
:columns="alarmColumns"
:data="alarmData"
:highlightOnRowClick="true"
tableHeight="280px"
/>
</el-card>
</el-col>
<el-col :span="12">
<el-card>
<div slot="header" class="card-header">
<span>换辊信息</span>
</div>
<MiniTable
v-loading="rollHistoryLoading"
:columns="rollHistoryColumns"
:data="rollHistoryData"
tableHeight="280px"
/>
</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('/plan')">
查看全部
<i class="el-icon-arrow-right"></i>
</el-button>
</div>
<MiniTable
v-loading="planLoading"
:columns="planColumns"
:data="planData"
tableHeight="280px"
/>
</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>
</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>
</div>
</template>
<script>
import CurrentTime from "./components/CurrentTime.vue";
import HomeMain from "./components/HomeMain.vue";
import MiniTable from "./components/MiniTable.vue";
// 引入日志API / 生产相关API
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";
export default {
name: "Index",
components: { CurrentTime, HomeMain, MiniTable, TrackMeasure },
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' },
],
alarmData: [], // 表格数据从API获取
queryForm: { pageNum: 1, pageSize: 10 }, // 分页参数
tableLoading: false, // 加载状态
rollHistoryData: [], // 轧辊历史数据
rollHistoryLoading: false, // 轧辊历史数据加载状态
planData: [], // 生产计划数据
planLoading: false, // 生产计划数据加载状态
measureData: [], // 过程跟踪数据
measureLoading: false, // 过程跟踪加载状态
measureColumns: [] // 过程跟踪列配置
};
},
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);
}
},
methods: {
// 获取日志数据
getLogData() {
this.tableLoading = true;
getLogDataPage(this.queryForm)
.then((response) => {
this.tableLoading = false;
this.alarmData = response.data.list || []; // 赋值表格数据
})
.catch((error) => {
this.tableLoading = false;
console.error("获取日志数据失败:", error);
this.$message.error("获取日志数据失败,请稍后重试");
});
},
// 获取轧辊历史列表
getRollHistorytList() {
this.rollHistoryLoading = true;
getRollHistorytList(this.queryForm)
.then((response) => {
this.rollHistoryLoading = false;
this.rollHistoryData = response.data.list || []; // 赋值表格数据
})
.catch((error) => {
this.rollHistoryLoading = false;
console.error("获取轧辊历史数据失败:", error);
});
},
// 获取生产计划列表
getPlanList() {
this.planLoading = true;
listPlan(this.queryForm)
.then((response) => {
this.planLoading = false;
this.planData = (response.data || []).map(item => {
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';
});
}
},
};
</script>
<style scoped lang="scss">
/* 主题色与布局样式 */
$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;
}
}
}
</style>