feat: 机组管理迁移l2代码

This commit is contained in:
砂糖
2025-10-10 14:40:29 +08:00
parent b0b8416e2f
commit 0a366b054c
32 changed files with 5133 additions and 11 deletions

View File

@@ -42,6 +42,7 @@
"bpmn-js-token-simulation": "0.10.0", "bpmn-js-token-simulation": "0.10.0",
"clipboard": "2.0.8", "clipboard": "2.0.8",
"core-js": "3.25.3", "core-js": "3.25.3",
"dayjs": "^1.11.18",
"dom-to-image": "^2.6.0", "dom-to-image": "^2.6.0",
"echarts": "5.4.0", "echarts": "5.4.0",
"element-ui": "2.15.12", "element-ui": "2.15.12",
@@ -72,6 +73,7 @@
"vue-router": "3.4.9", "vue-router": "3.4.9",
"vuedraggable": "2.24.3", "vuedraggable": "2.24.3",
"vuex": "3.6.0", "vuex": "3.6.0",
"xlsx": "^0.18.5",
"xml-js": "1.6.11" "xml-js": "1.6.11"
}, },
"devDependencies": { "devDependencies": {

52
klp-ui/src/api/l2/pdo.js Normal file
View File

@@ -0,0 +1,52 @@
import axios from 'axios'
export default function createFetch(url) {
const l2Request = axios.create({
baseURL: url,
headers: {
'Content-Type': 'application/json'
},
timeout: 10000,
// 自定义响应数据转换处理大整数ID精度问题
transformResponse: [data => {
// 在JSON解析前将所有id数值转换为字符串避免大整数精度丢失
const modifiedData = data.replace(/"id":\s*(\d+)/g, '"id": "$1"');
return JSON.parse(modifiedData);
}]
})
l2Request.interceptors.response.use(response => {
return response.data
})
return {
updatePdo: (data) => l2Request({
method: 'put',
url: '/api/pdo/update',
data
}),
getPdoList: (params) => l2Request({
method: 'post',
url: '/api/pdo/list',
data: params
}),
addPdo: (data) => l2Request({
method: 'post',
url: '/api/pdo/add',
data
}),
getPdo: (excoilid, operid) => l2Request({
method: 'get',
url: `/api/pdo/get/${excoilid}/${operid}`
}),
deletePdo: (excoilid, planId) => l2Request({
method: 'delete',
url: `/api/pdo/delete/${excoilid}/${planId}`
}),
getSegmentList: (query) => l2Request({
method: 'get',
url: '/api/segment/getSegmentHistory',
params: query
}),
}
}

191
klp-ui/src/api/l2/plan.js Normal file
View File

@@ -0,0 +1,191 @@
import axios from 'axios'
// 格式化日期字段
function formatDateFields(data) {
const dateFields = ['timestamp', 'onlineDate', 'startDate', 'endDate', 'furInDate', 'furOutDate', 'createTime', 'updateTime'];
// 对每个日期字段进行格式化
dateFields.forEach(field => {
// 处理空值情况
if (!data[field] || data[field] === '') {
data[field] = null;
return;
}
// 如果已经是ISO格式的日期字符串保持不变
if (typeof data[field] === 'string' && data[field].includes('T')) {
return;
}
// 如果是日期对象转换为ISO格式字符串
if (data[field] instanceof Date) {
data[field] = data[field].toISOString().slice(0, 19);
return;
}
// 如果是其他格式的日期字符串转换为ISO格式
if (typeof data[field] === 'string') {
try {
const date = new Date(data[field]);
if (!isNaN(date.getTime())) {
data[field] = date.toISOString().slice(0, 19);
} else {
data[field] = null;
}
} catch (e) {
console.error(`日期格式化错误 (${field}):`, e);
data[field] = null;
}
}
});
return data;
}
export default function createFetch(url) {
const l2Request = axios.create({
baseURL: url,
headers: {
'Content-Type': 'application/json'
},
timeout: 10000,
// 自定义响应数据转换处理大整数ID精度问题
transformResponse: [data => {
// 在JSON解析前将所有id数值转换为字符串避免大整数精度丢失
const modifiedData = data.replace(/"id":\s*(\d+)/g, '"id": "$1"');
return JSON.parse(modifiedData);
}]
})
l2Request.interceptors.response.use(response => {
return response.data
})
return {
listPlan: (query) => {
// 确保查询参数格式正确
const params = {
coilid: query.coilid || '',
entryThick: query.entryThick || null,
entryWidth: query.entryWidth || null,
status: query.status || '',
// 使用ISO格式的日期时间不添加额外的时间部分
startDate: query.startDate || '',
endDate: query.endDate || ''
};
return l2Request({
url: '/api/pdi/list',
method: 'post',
data: params
})
},
getPlan: (coilId) => l2Request({
url: '/api/pdi/getByCoilId',
method: 'get',
params: {
coilId: coilId
}
}),
addPlan: (data) => {
// 深拷贝数据,避免修改原始对象
const formattedData = JSON.parse(JSON.stringify(data));
// 处理日期字段,确保格式正确
formatDateFields(formattedData);
return l2Request({
url: '/api/pdi/add',
method: 'post',
data: formattedData
})
},
updatePlan: (data) => {
// 深拷贝数据,避免修改原始对象
const formattedData = JSON.parse(JSON.stringify(data));
// 处理日期字段,确保格式正确
formatDateFields(formattedData);
return l2Request({
url: '/api/pdi/update',
method: 'put',
data: formattedData
})
},
delPlan: (data) => {
// 确保ids是数组
const idArray = Array.isArray(ids) ? ids : [ids];
// 始终保持ID为字符串类型避免大整数精度问题
const processedIds = idArray.map(id => String(id));
console.log('删除的ID:', processedIds);
return l2Request({
url: '/api/pdi/delete',
method: 'post',
data: processedIds
})
},
getSetup: (ID) => {
return l2Request({
url: '/business/setup/' + ID,
method: 'get',
})
},
addSetup: (data) => {
return l2Request({
url: '/business/setup',
method: 'post',
data: data
})
},
listSetup: (query) => {
return l2Request({
url: '/business/setup/list',
method: 'post',
data: query
})
},
getBendforce: (query) => {
return l2Request({
url: '/business/bendforce',
method: 'get',
params: query
})
},
getFur: (steelGrade) => {
return l2Request({
url: '/business/fur/' + steelGrade,
method: 'get'
})
},
getMesh: (query) => {
return l2Request({
url: '/business/mesh',
method: 'get',
params: query
})
},
getRollforce: (query) => {
return l2Request({
url: '/business/rollforce',
method: 'get',
params: query
})
},
getTension: (query) => {
return l2Request({
url: '/business/tension',
method: 'get',
params: query
})
},
getTl: (query) => {
return l2Request({
url: '/business/tl',
method: 'get',
params: query
})
}
}
}

View File

@@ -0,0 +1,38 @@
import axios from 'axios'
export default function createFetch(url) {
const l2Request = axios.create({
baseURL: url,
headers: {
'Content-Type': 'application/json'
},
timeout: 10000,
// 自定义响应数据转换处理大整数ID精度问题
transformResponse: [data => {
// 在JSON解析前将所有id数值转换为字符串避免大整数精度丢失
const modifiedData = data.replace(/"id":\s*(\d+)/g, '"id": "$1"');
return JSON.parse(modifiedData);
}]
})
l2Request.interceptors.response.use(response => {
return response.data
})
return {
getReportSummary: (params) => {
return l2Request({
method: 'get',
url: '/api/report/summary',
params
})
},
getReportDetail: (params) => {
return l2Request({
method: 'get',
url: '/api/report/details',
params
})
}
}
}

View File

@@ -0,0 +1,75 @@
import axios from 'axios'
export default function createFetch(url) {
const l2Request = axios.create({
baseURL: url,
headers: {
'Content-Type': 'application/json'
},
timeout: 10000,
// 自定义响应数据转换处理大整数ID精度问题
transformResponse: [data => {
// 在JSON解析前将所有id数值转换为字符串避免大整数精度丢失
const modifiedData = data.replace(/"id":\s*(\d+)/g, '"id": "$1"');
return JSON.parse(modifiedData);
}]
})
l2Request.interceptors.response.use(response => {
return response.data
})
return {
getRollIdList: () => {
return l2Request({
method: 'get',
url: '/api/roller/history/rollid'
})
},
getChangeIdList: () => {
return l2Request({
method: 'get',
url: '/api/roller/history/changeid'
})
},
getRollHistorytList: (params) => {
return l2Request({
method: 'post',
data: params,
url: '/api/roller/history/list'
})
},
backupRoll: (data) => {
return l2Request({
method: 'post',
data,
url: '/api/roller/change/standby'
})
},
onlineRoll: (data) => {
return l2Request({
method: 'post',
data,
url: '/api/roller/change/online'
})
},
getOnlineRollList: () => {
return l2Request({
method: 'get',
url: '/api/roller/data/online'
})
},
getOfflineRollList: (position, type) => {
return l2Request({
method: 'get',
url: `/api/roller/data/ready/${position}/${type}`
})
},
getReadyRollList: () => {
return l2Request({
method: 'get',
url: '/api/roller/data/standby'
})
}
}
}

View File

@@ -0,0 +1,58 @@
import axios from 'axios'
export default function createFetch(url) {
const l2Request = axios.create({
baseURL: url,
headers: {
'Content-Type': 'application/json'
},
timeout: 10000,
// 自定义响应数据转换处理大整数ID精度问题
transformResponse: [data => {
// 在JSON解析前将所有id数值转换为字符串避免大整数精度丢失
const modifiedData = data.replace(/"id":\s*(\d+)/g, '"id": "$1"');
return JSON.parse(modifiedData);
}]
})
l2Request.interceptors.response.use(response => {
return response.data
})
return {
updateSteelGrade: (data) => {
return l2Request({
method: 'put',
url: '/api/steelGrade/update',
data
})
},
addSteelGrade: (data) => {
return l2Request({
method: 'post',
url: '/api/steelGrade/add',
data
})
},
getSteelGradeList: () => {
return l2Request({
method: 'get',
url: '/api/steelGrade/list'
})
},
getSteelGradeInfo: (gradeid) => {
return l2Request({
method: 'get',
url: '/api/steelGrade/info',
params: { gradeid }
})
},
deleteSteelGrade: (id) => {
return l2Request({
method: 'delete',
url: `/l2-api/api/steelGrade/delete/${id}`
})
}
}
}

43
klp-ui/src/api/l2/stop.js Normal file
View File

@@ -0,0 +1,43 @@
import axios from 'axios'
export default function createFetch(url) {
const l2Request = axios.create({
baseURL: url,
headers: {
'Content-Type': 'application/json'
},
timeout: 10000,
// 自定义响应数据转换处理大整数ID精度问题
transformResponse: [data => {
// 在JSON解析前将所有id数值转换为字符串避免大整数精度丢失
const modifiedData = data.replace(/"id":\s*(\d+)/g, '"id": "$1"');
return JSON.parse(modifiedData);
}]
})
l2Request.interceptors.response.use(response => {
return response.data
})
return {
updateStoppage: (params) => l2Request({
method: 'put',
data: params,
url: '/api/stoppage/update'
}),
listStoppage: (data) => l2Request({
method: 'post',
data,
url: '/api/stoppage/list'
}),
deleteStoppage: (stopid) => l2Request({
method: 'delete',
url: `/api/stoppage/delete/${stopid}`
}),
getStoppageSummary: (data) => l2Request({
method: 'post',
data,
url: '/api/stoppage/calc'
})
}
}

View File

@@ -6,7 +6,13 @@ export default function createFetch(url) {
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
timeout: 10000 timeout: 10000,
// 自定义响应数据转换处理大整数ID精度问题
transformResponse: [data => {
// 在JSON解析前将所有id数值转换为字符串避免大整数精度丢失
const modifiedData = data.replace(/"id":\s*(\d+)/g, '"id": "$1"');
return JSON.parse(modifiedData);
}]
}) })
l2Request.interceptors.response.use(response => { l2Request.interceptors.response.use(response => {

View File

@@ -1,5 +1,6 @@
// ====================== 1. 基础颜色变量Sass直接处理====================== // ====================== 1. 基础颜色变量Sass直接处理======================
$primary-base: #5F7BA0; $primary-base: #5F7BA0;
// $primary-base: #2bf;
/* 工业蓝灰(主色原始值) */ /* 工业蓝灰(主色原始值) */
$success-base: #2ECC71; $success-base: #2ECC71;
/* 成功色原始值 */ /* 成功色原始值 */
@@ -1382,7 +1383,7 @@ body {
// ====================== 描述列表组件el-descriptions- 深色模式 ====================== // ====================== 描述列表组件el-descriptions- 深色模式 ======================
.el-descriptions { .el-descriptions {
width: 100%; width: 100%;
background: $--color-background !important; // 黑色背景 // background: $--color-background !important; // 黑色背景
box-shadow: $--metal-shadow; box-shadow: $--metal-shadow;
overflow: hidden; overflow: hidden;
color: $--color-text-primary; // 白色文字 color: $--color-text-primary; // 白色文字
@@ -1391,7 +1392,7 @@ body {
&__header { &__header {
padding: $--spacing-base $--spacing-lg; padding: $--spacing-base $--spacing-lg;
border-bottom: 1px solid $--border-color-lighter; border-bottom: 1px solid $--border-color-lighter;
background: $--metal-gradient-dark; // background: $--metal-gradient-dark;
color: $--color-text-primary; // 白色标题 color: $--color-text-primary; // 白色标题
font-weight: 600; font-weight: 600;
font-size: 14px; font-size: 14px;
@@ -1400,6 +1401,7 @@ body {
// 描述列表主体容器 // 描述列表主体容器
&__body { &__body {
width: 100%; width: 100%;
background-color: transparent;
} }
// 描述列表行 // 描述列表行
@@ -1416,11 +1418,11 @@ body {
// 描述列表标签项(左侧) // 描述列表标签项(左侧)
.el-descriptions-item__label { .el-descriptions-item__label {
padding: $--spacing-lg; // padding: $--spacing-lg;
background: $--color-background !important; // 极浅灰背景区分标签 // background: $--color-background !important; // 极浅灰背景区分标签
color: $--color-text-secondary; // 浅灰文字 color: $--color-text-secondary; // 浅灰文字
font-weight: 500; font-weight: 500;
border-right: 1px solid $--border-color-lighter; // border-right: 1px solid $--border-color-lighter;
box-sizing: border-box; box-sizing: border-box;
white-space: nowrap; white-space: nowrap;
font-size: 13px; font-size: 13px;
@@ -1428,8 +1430,8 @@ body {
// 描述列表内容项(右侧) // 描述列表内容项(右侧)
&-item__content { &-item__content {
padding: $--spacing-lg; // padding: $--spacing-lg;
background: $--color-background-light; // background: $--color-background-light;
color: $--color-text-primary; // 白色文字 color: $--color-text-primary; // 白色文字
flex: 1; flex: 1;
box-sizing: border-box; box-sizing: border-box;
@@ -1472,12 +1474,12 @@ body {
.el-descriptions__label, .el-descriptions__label,
.el-descriptions__content { .el-descriptions__content {
padding: $--spacing-lg $--spacing-lg * 1.5; padding: $--spacing-lg $--spacing-lg;
font-size: 14px; font-size: 14px;
} }
.el-descriptions__header { .el-descriptions__header {
padding: $--spacing-base $--spacing-lg * 1.5; padding: $--spacing-base $--spacing-lg;
font-size: 15px; font-size: 15px;
} }
} }

View File

@@ -5,6 +5,7 @@
/* theme color */ /* theme color */
$--color-primary: #5F7BA0; $--color-primary: #5F7BA0;
// $--color-primary: #2bf;
$--color-success: #13ce66; $--color-success: #13ce66;
$--color-warning: #ffba00; $--color-warning: #ffba00;
$--color-danger: #ff4949; $--color-danger: #ff4949;

View File

@@ -0,0 +1,62 @@
<template>
<div>
<el-tabs v-model="activeName" type="card">
<el-tab-pane label="计划执行" name="Plan"></el-tab-pane>
<el-tab-pane label="过程跟踪" name="Track"></el-tab-pane>
<el-tab-pane label="产出实绩" name="Quality"></el-tab-pane>
<el-tab-pane label="停机明细" name="Stop"></el-tab-pane>
<el-tab-pane label="实绩报表" name="PdoAnalysis"></el-tab-pane>
<el-tab-pane label="停机报表" name="StopAnalysis"></el-tab-pane>
<!-- <el-tab-pane label="换辊报表" name="RollerAnalysis"></el-tab-pane> -->
</el-tabs>
<keep-alive>
<div v-if="ready" class="app-container">
<component :is="activeName" :baseURL="baseURL" />
</div>
</keep-alive>
</div>
</template>
<script>
import { getConfigKey } from '@/api/system/config'
import Plan from './panels/plan/index.vue'
import Track from './panels/track/index.vue'
import Quality from './panels/quality/index.vue'
import Stop from './panels/stop/index.vue'
import PdoAnalysis from './panels/analysis/pdo.vue'
import StopAnalysis from './panels/analysis/stop.vue'
import RollerAnalysis from './panels/analysis/roller.vue'
export default {
name: 'Lines',
components: {
Plan,
Track,
Quality,
Stop,
PdoAnalysis,
StopAnalysis,
RollerAnalysis
},
data() {
return {
activeName: 'Plan',
baseURL: '',
ready: false
}
},
created() {
// 获取路由地址的最后一项
const route = this.$route.path.split('/').pop()
getConfigKey(`line.${route}.baseURL`).then(res => {
this.baseURL = res.msg
this.ready = true
})
}
}
</script>
<style>
</style>

View File

@@ -0,0 +1,112 @@
<template>
<div class="report-body">
<!-- 操作行 -->
<el-row>
<el-button type="primary" @click="exportReport">导出</el-button>
</el-row>
<h1 class="report-body-title" style="text-align: center;">
{{ title }}
</h1>
<div class="summary-cards" v-if="summary.length > 0">
<div v-for="item in summary">
<span class="card-title">{{ item.label }}: </span>
<span class="card-value">{{ item.value }}</span>
</div>
</div>
<el-table :data="dataset" border style="width: 100%;" size="small">
<el-table-column v-for="item in columns" :label="item.label" :prop="item.prop" />
</el-table>
</div>
</template>
<script>
import * as XLSX from 'xlsx';
export default {
name: 'ReportBody',
props: {
title: {
type: String,
required: true
},
summary: {
type: Array,
required: true
},
dataset: {
type: Array,
required: true
},
columns: {
type: Array,
required: true
}
},
methods: {
exportReport() {
// 创建工作簿
const workbook = XLSX.utils.book_new();
// 准备导出数据数组
const exportData = [];
// 处理表格数据确保按columns定义的顺序和标签导出
const tableData = this.dataset.map(row => {
const formattedRow = {};
this.columns.forEach(col => {
formattedRow[col.label] = row[col.prop];
});
return formattedRow;
});
// 添加表格数据
exportData.push(...tableData);
// 创建工作表
const worksheet = XLSX.utils.json_to_sheet(exportData);
// 调整列宽
const columnWidths = this.columns.map(col => ({ wch: Math.max(col.label.length * 1.5, 10) }));
worksheet['!cols'] = columnWidths;
// 添加工作表到工作簿
XLSX.utils.book_append_sheet(workbook, worksheet, '报表数据');
// 生成文件名
const exportFileName = this.fileName || `${this.title.replace(/[\\/*?:"<>|]/g, '')}_${new Date().toLocaleDateString()}.xlsx`;
// 导出文件
XLSX.writeFile(workbook, exportFileName);
}
}
}
</script>
<style scoped lang="scss">
.summary-cards {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 12px;
margin-bottom: 15px;
}
.card-title {
font-size: 13px;
color: #111;
margin-bottom: 6px;
}
.card-value {
font-size: 20px;
font-weight: bold;
color: #222;
margin-bottom: 3px;
}
.card-unit {
font-size: 12px;
color: rgba(255, 255, 255, 0.6);
}
</style>

View File

@@ -0,0 +1,60 @@
<template>
<div class="pdo-report">
<ReportBody v-loading="loading" title="实绩报表" :summary="reportSummary" :dataset="reportDetail" :columns="columns" />
</div>
</template>
<script>
import ReportBody from './body.vue'
import { getReportSummary, getReportDetail } from '@/api/l2/report'
export default {
name: 'PdoReport',
components: {
ReportBody
},
data() {
return {
loading: false,
reportSummary: [],
reportDetail: [],
columns: []
}
},
methods: {
async fetchReportData() {
this.loading = true
const res = await getReportSummary()
this.reportSummary = [
{ label: '成品卷数', value: res.coilCount }, // coilCount=总卷数,结合“成品总重/原料总重”场景,推测为成品卷数
{ label: '平均出口厚度[mm]', value: res.avgExitThickness }, // avgExitThickness=平均出口厚度,厚度用“平均”更符合实际(单卷厚度均匀,无“总厚度”概念)
{ label: '原料总重[t]', value: res.totalEntryWeight }, // totalEntryWeight=总入口重量,入口重量即原料重量
{ label: '总出口长度[mm]', value: res.totalExitLength }, // totalExitLength=总出口长度,补充原“去锌总重”(无对应字段)的空缺,符合卷材加工的核心维度
{ label: '平均出口宽度[mm]', value: res.avgExitWidth }, // avgExitWidth=平均出口宽度,宽度用“平均”更合理(单卷宽度固定,“总宽度”无业务意义)
{ label: '成品总重[t]', value: res.totalActualWeight }, // totalActualWeight=总实际重量,实际重量即成品最终重量
{ label: '总理论重量[t]', value: res.totalTheoryWeight }, // totalTheoryWeight=总理论重量工业场景中常用“理论重量vs实际重量”对比
{ label: '成材率', value: (res.yieldRate * 100).toFixed(2) + '%' } // yieldRate=成材率,原配置正确,保留
];
const res2 = await getReportDetail()
this.reportDetail = res2
this.columns = [
{ label: '出口物料ID', prop: 'exitMatId' },
{ label: '入口物料ID', prop: 'entryMatId' },
{ label: '机组号', prop: 'groupNo' },
{ label: '班次号', prop: 'shiftNo' },
{ label: '钢种', prop: 'steelGrade' },
{ label: '出口厚度(mm)', prop: 'exitThickness' },
{ label: '出口宽度(mm)', prop: 'exitWidth' },
{ label: '出口长度(m)', prop: 'exitLength' },
{ label: '理论重量(kg)', prop: 'theoryWeight' },
{ label: '实际重量(kg)', prop: 'actualWeight' },
{ label: '上线时间', prop: 'onlineTime' },
]
this.loading = false
}
},
mounted() {
this.fetchReportData()
}
}
</script>

View File

@@ -0,0 +1,50 @@
<template>
<div class="roller-report">
<ReportBody v-loading="loading" title="换辊报表" :summary="reportSummary" :dataset="reportDetail" :columns="columns" />
</div>
</template>
<script>
import ReportBody from './body.vue'
import { getRollHistorytList } from '@/api/l2/roller'
export default {
name: 'RollerReport',
components: {
ReportBody
},
data() {
return {
reportSummary: [],
reportDetail: [],
columns: [],
loading: false
}
},
mounted() {
this.fetchReportData()
},
methods: {
async fetchReportData() {
this.loading = true
const res = await getRollHistorytList({ pageNum: 1, pageSize: 1000 })
this.reportDetail = res.data.list
this.columns = [
{ label: '换辊号', prop: 'changeid' },
{ label: '轧辊号', prop: 'rollid' },
{ label: '类型', prop: 'type' },
{ label: '位置', prop: 'position' },
{ label: '直径', prop: 'diameter' },
{ label: '粗糙度', prop: 'rough' },
{ label: '凸度', prop: 'crown' },
{ label: '轧制长度', prop: 'rolledLength' },
{ label: '轧制重量', prop: 'rolledWeight' },
{ label: '轧制次数', prop: 'rolledCount' },
{ label: '上线时间', prop: 'instalTime' },
{ label: '下线时间', prop: 'deinstalTime' },
]
this.loading = false
}
}
}
</script>

View File

@@ -0,0 +1,49 @@
<template>
<div class="stop-report">
<ReportBody v-loading="loading" title="停机报表" :summary="reportSummary" :dataset="reportDetail" :columns="columns" />
</div>
</template>
<script>
import ReportBody from './body.vue'
import { listStoppage } from '@/api/l2/stop';
export default {
name: 'StopReport',
components: {
ReportBody
},
data() {
return {
loading: false,
reportSummary: [],
reportDetail: [],
columns: [
{ label: '钢卷号', prop: 'coilId' },
{ label: '班次号', prop: 'shiftNo' },
{ label: '组', prop: 'groupNo' },
{ label: '开始时间', prop: 'startTime' },
{ label: '结束时间', prop: 'endTime' },
{ label: '持续时间[分钟]', prop: 'duration' },
{ label: '停机类型', prop: 'type' },
{ label: '备注', prop: 'remark' },
]
}
},
mounted() {
this.fetchReportData()
},
methods: {
async fetchReportData() {
this.loading = true
const res = await listStoppage({
// 只保留年月日YYYY-mm-dd
startDate: '2023-08-13',
endDate: '2025-08-20'
})
this.reportDetail = res.data.list
this.loading = false
}
}
}
</script>

View File

@@ -0,0 +1,343 @@
<template>
<div class="report-container">
<!-- <el-row>
<el-select size="mini" v-model="reportType" style="width: 80px;" placeholder="请选择报表类型">
<el-option label="实绩" value="pdo"></el-option>
<el-option label="换辊" value="roller"></el-option>
<el-option label="停机" value="stop"></el-option>
</el-select>
</el-row> -->
<!-- 查询条件区域 -->
<el-row>
<el-form v-if="reportType === 'pdo'" :inline="true" :model="queryParams" class="query-form" size="mini">
<el-form-item label="机组号" prop="groupNo">
<el-input v-model="queryParams.groupNo" placeholder="请输入机组号" clearable style="width: 160px;"></el-input>
</el-form-item>
<el-form-item label="班次号" prop="shiftNo">
<el-input v-model="queryParams.shiftNo" placeholder="请输入班次号" clearable style="width: 160px;"></el-input>
</el-form-item>
<el-form-item label="开始时间" prop="startTime">
<el-date-picker v-model="queryParams.startTime" type="datetime" placeholder="选择开始时间"
value-format="yyyy-MM-dd HH:mm:ss" style="width: 200px;"></el-date-picker>
</el-form-item>
<el-form-item label="结束时间" prop="endTime">
<el-date-picker v-model="queryParams.endTime" type="datetime" placeholder="选择结束时间"
value-format="yyyy-MM-dd HH:mm:ss" style="width: 200px;"></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="fetchReportData" icon="el-icon-search" size="small">查询</el-button>
<el-button @click="resetQuery" icon="el-icon-refresh" size="small">重置</el-button>
<!-- <el-button type="success" @click="exportReport" icon="el-icon-download" size="small">导出</el-button> -->
</el-form-item>
</el-form>
<el-form v-if="reportType === 'roller'" size="mini" inline class="query-form">
<el-form-item label="开始日期">
<el-date-picker v-model="queryParams.startTime" type="datetime" placeholder="选择日期"
value-format="yyyy-MM-dd HH:mm:ss" style="width: 140px"></el-date-picker>
</el-form-item>
<el-form-item label="结束日期">
<el-date-picker v-model="queryParams.endTime" type="datetime" placeholder="选择日期"
value-format="yyyy-MM-dd HH:mm:ss" style="width: 140px"></el-date-picker>
</el-form-item>
</el-form>
<el-form v-if="reportType === 'stop'" size="mini" inline class="query-form">
<el-form-item label="开始时间" prop="startDate">
<el-date-picker v-model="queryParams.startDate" type="date" placeholder="选择开始时间" value-format="yyyy-MM-dd"
:clearable="true"></el-date-picker>
</el-form-item>
<el-form-item label="结束时间" prop="endDate">
<el-date-picker v-model="queryParams.endDate" type="date" placeholder="选择结束时间" value-format="yyyy-MM-dd"
:clearable="true"></el-date-picker>
</el-form-item>
</el-form>
</el-row>
<PdoReport v-if="reportType === 'pdo'" />
<RollerReport v-if="reportType === 'roller'" />
<StopReport v-if="reportType === 'stop'" />
</div>
</template>
<script>
import createFetch from '@/api/l2/report'
import ReportBody from './components/body.vue'
import PdoReport from './components/pdo.vue'
import RollerReport from './components/roller.vue'
import StopReport from './components/stop.vue'
export default {
name: 'Report',
components: {
ReportBody,
PdoReport,
RollerReport,
StopReport
},
data() {
return {
fetchApi: {},
reportType: 'stop',
// 查询参数
queryParams: {
groupNo: '',
shiftNo: '',
startTime: '',
endTime: '',
startDate: '',
endDate: '',
},
// 汇总数据
reportSummary: {
totalExitWidth: 0,
totalExitLength: 0,
totalTheoryWeight: 0,
totalActualWeight: 0,
totalExitThickness: 0,
avgExitWidth: 0,
avgExitLength: 0,
avgTheoryWeight: 0,
avgActualWeight: 0,
avgExitThickness: 0,
coilCount: 0,
totalEntryWeight: 0,
yieldRate: 0
},
columns: [
{ label: '出口物料ID', prop: 'exitMatId' },
{ label: '入口物料ID', prop: 'entryMatId' },
{ label: '机组号', prop: 'groupNo' },
{ label: '班次号', prop: 'shiftNo' },
{ label: '钢种', prop: 'steelGrade' },
{ label: '出口厚度(mm)', prop: 'exitThickness' },
{ label: '出口宽度(mm)', prop: 'exitWidth' },
{ label: '出口长度(m)', prop: 'exitLength' },
{ label: '理论重量(kg)', prop: 'theoryWeight' },
{ label: '实际重量(kg)', prop: 'actualWeight' },
{ label: '上线时间', prop: 'onlineTime' },
],
// 详细数据(无分页,一次性加载所有数据)
reportDetail: [],
// 加载状态
loading: false
}
},
created() {
this.fetchApi = createFetch(this.baseURL)
// 初始化时获取URL参数
this.initQueryParams()
// 加载报表数据
this.fetchReportData()
},
methods: {
// 从URL初始化查询参数
initQueryParams() {
const { groupNo, shiftNo, startTime, endTime } = this.$route.query
if (groupNo) this.queryParams.groupNo = groupNo
if (shiftNo) this.queryParams.shiftNo = shiftNo
if (startTime) this.queryParams.startTime = startTime
if (endTime) this.queryParams.endTime = endTime
},
// 获取报表数据(无分页)
async fetchReportData() {
this.loading = true
try {
// 获取汇总数据
const summaryRes = await this.fetchApi.getReportSummary(this.queryParams)
this.reportSummary = summaryRes || this.reportSummary
// 获取详细数据(不传递分页参数,获取所有数据)
const detailRes = await this.fetchApi.getReportDetail(this.queryParams)
this.reportDetail = detailRes || []
} catch (error) {
this.$message.error('获取报表数据失败:' + (error.message || '未知错误'))
console.error('报表数据获取失败', error)
} finally {
this.loading = false
}
},
// 重置查询条件
resetQuery() {
this.queryParams = {
groupNo: '',
shiftNo: '',
startTime: '',
endTime: '',
startDate: '',
endDate: '',
}
this.fetchReportData() // 重置后重新查询
},
// 导出报表
exportReport() {
this.$confirm('确定要导出当前报表数据吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'info'
}).then(async () => {
try {
// 这里可以实现导出逻辑
this.$message.success('报表导出成功')
} catch (error) {
this.$message.error('报表导出失败:' + (error.message || '未知错误'))
}
}).catch(() => {
this.$message.info('已取消导出')
})
}
}
}
</script>
<style scoped>
.report-container {
min-height: 100vh;
color: #ffffff;
/* 文字设为白色 */
}
.page-header {
margin-bottom: 15px;
}
.page-header h1 {
font-size: 22px;
color: #ffffff;
margin: 0 0 6px 0;
}
.page-description {
font-size: 13px;
color: rgba(255, 255, 255, 0.8);
margin: 0;
}
.filter-card {
margin-bottom: 15px;
border-radius: 4px;
border: 1px solid rgba(255, 255, 255, 0.1);
background: rgba(0, 0, 0, 0.3);
}
.query-form {
padding: 10px 0;
}
/* 紧凑的表单样式 */
.el-form-item {
margin-right: 10px !important;
margin-bottom: 10px !important;
}
.el-form-item__label {
color: rgba(255, 255, 255, 0.9) !important;
padding-right: 5px !important;
}
.el-input,
.el-date-picker {
--el-input-bg-color: rgba(255, 255, 255, 0.1) !important;
--el-input-text-color: #ffffff !important;
--el-input-placeholder-color: rgba(255, 255, 255, 0.6) !important;
border-color: rgba(255, 255, 255, 0.2) !important;
}
.summary-cards {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 12px;
margin-bottom: 15px;
}
.card-title {
font-size: 13px;
color: rgba(255, 255, 255, 0.8);
margin-bottom: 6px;
}
.card-value {
font-size: 20px;
font-weight: bold;
color: #ffffff;
margin-bottom: 3px;
}
.card-unit {
font-size: 12px;
color: rgba(255, 255, 255, 0.6);
}
.detail-table-card {
border: 1px solid rgba(255, 255, 255, 0.1);
background: rgba(0, 0, 0, 0.3);
}
.table-header {
padding: 10px 0;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
margin-bottom: 10px;
}
.table-header h2 {
font-size: 16px;
color: #ffffff;
margin: 0;
}
.el-table {
--el-table-bg-color: transparent !important;
--el-table-text-color: #ffffff !important;
--el-table-border-color: rgba(255, 255, 255, 0.1) !important;
}
.el-table th {
background-color: rgba(255, 255, 255, 0.05) !important;
color: rgba(255, 255, 255, 0.9) !important;
border-bottom-color: rgba(255, 255, 255, 0.1) !important;
}
.el-table tr {
background-color: transparent !important;
}
.el-table tr:nth-child(even) {
background-color: rgba(255, 255, 255, 0.03) !important;
}
.el-table tr:hover>td {
background-color: rgba(255, 255, 255, 0.08) !important;
}
.el-table td {
border-bottom-color: rgba(255, 255, 255, 0.05) !important;
}
.no-data {
padding: 30px 0;
text-align: center;
}
.el-empty__description {
color: rgba(255, 255, 255, 0.6) !important;
}
/* 按钮样式优化 */
.el-button {
margin-left: 5px !important;
}
.el-button--small {
padding: 5px 10px !important;
font-size: 12px !important;
}
</style>

View File

@@ -0,0 +1,98 @@
<template>
<div class="pdo-report">
<el-row>
<el-form :inline="true" :model="queryParams" class="query-form" size="mini">
<el-form-item label="开始日期" prop="startTime">
<el-date-picker v-model="queryParams.startTime" type="datetime" placeholder="选择开始时间"
value-format="yyyy-MM-dd 00:00:00" style="width: 200px;"></el-date-picker>
</el-form-item>
<el-form-item label="结束日期" prop="endTime">
<el-date-picker v-model="queryParams.endTime" type="datetime" placeholder="选择结束时间"
value-format="yyyy-MM-dd 23:59:59" style="width: 200px;"></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="fetchReportData" icon="el-icon-search" size="small">查询</el-button>
</el-form-item>
</el-form>
</el-row>
<ReportBody v-loading="loading" title="实绩报表" :summary="reportSummary" :dataset="reportDetail" :columns="columns" />
</div>
</template>
<script>
import ReportBody from './components/body.vue'
import createFetch from '@/api/l2/report'
export default {
name: 'PdoReport',
components: {
ReportBody
},
props: {
baseURL: {
type: String,
required: true
}
},
data() {
return {
fetchApi: {},
queryParams: {
// startTime: new Date().getFullYear() + '-' + (new Date().getMonth() + 1).toString().padStart(2, '0') + '-01 00:00:00',
// endTime: new Date().getFullYear() + '-' + (new Date().getMonth() + 2).toString().padStart(2, '0') + '-01 00:00:00',
startTime: undefined,
endTime: undefined,
pageNum: 1,
pageSize: 1000
},
loading: false,
reportSummary: [],
reportDetail: [],
columns: []
}
},
created() {
this.fetchApi = createFetch(this.baseURL)
},
methods: {
async fetchReportData() {
this.loading = true
const res = await this.fetchApi.getReportSummary(this.queryParams)
this.reportSummary = [
{ label: '成品卷数', value: res.coilCount }, // coilCount=总卷数,结合“成品总重/原料总重”场景,推测为成品卷数
{ label: '平均出口厚度[mm]', value: res.avgExitThickness }, // avgExitThickness=平均出口厚度,厚度用“平均”更符合实际(单卷厚度均匀,无“总厚度”概念)
{ label: '原料总重[t]', value: res.totalEntryWeight }, // totalEntryWeight=总入口重量,入口重量即原料重量
{ label: '总出口长度[mm]', value: res.totalExitLength }, // totalExitLength=总出口长度,补充原“去锌总重”(无对应字段)的空缺,符合卷材加工的核心维度
{ label: '平均出口宽度[mm]', value: res.avgExitWidth }, // avgExitWidth=平均出口宽度,宽度用“平均”更合理(单卷宽度固定,“总宽度”无业务意义)
{ label: '成品总重[t]', value: res.totalActualWeight }, // totalActualWeight=总实际重量,实际重量即成品最终重量
{ label: '总理论重量[t]', value: res.totalTheoryWeight }, // totalTheoryWeight=总理论重量工业场景中常用“理论重量vs实际重量”对比
{ label: '成材率', value: (res.yieldRate * 100).toFixed(2) + '%' } // yieldRate=成材率,原配置正确,保留
];
const res2 = await this.fetchApi.getReportDetail(this.queryParams)
this.reportDetail = res2.map(item => {
return {
...item,
onlineTime: item.onlineTime.replace('T', ' ')
}
})
this.columns = [
{ label: '出口物料ID', prop: 'exitMatId' },
{ label: '入口物料ID', prop: 'entryMatId' },
{ label: '机组号', prop: 'groupNo' },
{ label: '班次号', prop: 'shiftNo' },
{ label: '钢种', prop: 'steelGrade' },
{ label: '出口厚度(mm)', prop: 'exitThickness' },
{ label: '出口宽度(mm)', prop: 'exitWidth' },
{ label: '出口长度(m)', prop: 'exitLength' },
{ label: '理论重量(kg)', prop: 'theoryWeight' },
{ label: '实际重量(kg)', prop: 'actualWeight' },
{ label: '上线时间', prop: 'onlineTime' },
]
this.loading = false
}
},
mounted() {
this.fetchReportData()
}
}
</script>

View File

@@ -0,0 +1,84 @@
<template>
<div class="roller-report">
<el-row>
<el-form :inline="true" :model="queryParams" class="query-form" size="mini">
<el-form-item label="开始日期" prop="startDate">
<el-date-picker v-model="queryParams.startTime" type="datetime" placeholder="选择开始时间"
value-format="yyyy-MM-dd 00:00:00" style="width: 200px;"></el-date-picker>
</el-form-item>
<el-form-item label="结束日期" prop="endDate">
<el-date-picker v-model="queryParams.endTime" type="datetime" placeholder="选择结束时间"
value-format="yyyy-MM-dd 23:59:59" style="width: 200px;"></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="fetchReportData" icon="el-icon-search" size="small">查询</el-button>
</el-form-item>
</el-form>
</el-row>
<ReportBody v-loading="loading" title="换辊报表" :summary="reportSummary" :dataset="reportDetail" :columns="columns" />
</div>
</template>
<script>
import ReportBody from './components/body.vue'
import createFetch from '@/api/l2/roller'
export default {
name: 'RollerReport',
components: {
ReportBody
},
props: {
baseURL: {
type: String,
required: true
}
},
data() {
return {
fetchApi: {},
queryParams: {
// startTime: new Date().getFullYear() + '-' + (new Date().getMonth() + 1).toString().padStart(2, '0') + '-01 00:00:00',
// endTime: new Date().getFullYear() + '-' + (new Date().getMonth() + 2).toString().padStart(2, '0') + '-01 00:00:00',
startTime: undefined,
endTime: undefined,
endTime: '',
pageNum: 1,
pageSize: 1000
},
reportSummary: [],
reportDetail: [],
columns: [],
loading: false
}
},
created() {
this.fetchApi = createFetch(this.baseURL)
},
mounted() {
this.fetchReportData()
},
methods: {
async fetchReportData() {
this.loading = true
const res = await this.fetchApi.getRollHistorytList(this.queryParams)
this.reportDetail = res.data.list
this.columns = [
{ label: '换辊号', prop: 'changeid' },
{ label: '轧辊号', prop: 'rollid' },
{ label: '类型', prop: 'type' },
{ label: '位置', prop: 'position' },
{ label: '直径', prop: 'diameter' },
{ label: '粗糙度', prop: 'rough' },
{ label: '凸度', prop: 'crown' },
{ label: '轧制长度', prop: 'rolledLength' },
{ label: '轧制重量', prop: 'rolledWeight' },
{ label: '轧制次数', prop: 'rolledCount' },
{ label: '上线时间', prop: 'instalTime' },
{ label: '下线时间', prop: 'deinstalTime' },
]
this.loading = false
}
}
}
</script>

View File

@@ -0,0 +1,89 @@
<template>
<div class="stop-report">
<el-row>
<el-form :inline="true" :model="queryParams" class="query-form" size="mini">
<el-form-item label="开始日期" prop="startDate">
<el-date-picker v-model="queryParams.startDate" type="datetime" placeholder="选择开始时间"
value-format="yyyy-MM-dd" style="width: 200px;"></el-date-picker>
</el-form-item>
<el-form-item label="结束日期" prop="endDate">
<el-date-picker v-model="queryParams.endDate" type="datetime" placeholder="选择结束时间"
value-format="yyyy-MM-dd" style="width: 200px;"></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="fetchReportData" icon="el-icon-search" size="small">查询</el-button>
</el-form-item>
</el-form>
</el-row>
<ReportBody v-loading="loading" title="停机报表" :summary="reportSummary" :dataset="reportDetail" :columns="columns" />
</div>
</template>
<script>
import ReportBody from './components/body.vue'
import createFetch from '@/api/l2/stop';
export default {
name: 'StopReport',
components: {
ReportBody
},
props: {
baseURL: {
type: String,
required: true
}
},
data() {
return {
fetchApi: {},
// 默认值为本月
queryParams: {
// startDate: new Date().getFullYear() + '-' + (new Date().getMonth() + 1).toString().padStart(2, '0') + '-01',
startDate: '2020-01-01',
// 最后一天不一定是31
// endDate: new Date().getFullYear() + '-' + (new Date().getMonth() + 2).toString().padStart(2, '0') + '-01',
// 今天
endDate: new Date().getFullYear() + '-' + (new Date().getMonth() + 1).toString().padStart(2, '0') + '-' + new Date().getDate(),
pageNum: 1,
pageSize: 1000
},
loading: false,
reportSummary: [],
reportDetail: [],
columns: [
{ label: '钢卷号', prop: 'coilid' },
{ label: '班次号', prop: 'shift' },
{ label: '组', prop: 'area' },
{ label: '开始时间', prop: 'startDate' },
{ label: '结束时间', prop: 'endDate' },
{ label: '持续时间[分钟]', prop: 'duration' },
{ label: '停机类型', prop: 'unit' },
{ label: '备注', prop: 'remark' },
]
}
},
created() {
this.fetchApi = createFetch(this.baseURL)
},
mounted() {
this.fetchReportData()
},
methods: {
async fetchReportData() {
this.loading = true
const res = await this.fetchApi.listStoppage(this.queryParams)
this.reportDetail = res.data
const res2 = await this.fetchApi.getStoppageSummary(this.queryParams)
this.reportSummary = [
{ label: '统计区间', value: this.queryParams.startDate + ' 至 ' + this.queryParams.endDate },
{ label: '停机次数', value: res2.data[0] },
{ label: '停机时长[小时]', value: res2.data[1] },
{ label: '作业率', value: res2.data[2] + ' %' },
]
this.loading = false
}
}
}
</script>

View File

@@ -0,0 +1,258 @@
<template>
<div>
<el-form :model="form" label-width="160px" v-loading="loading">
<el-row>
<el-col :span="8">
<el-form-item label="开卷机张力">
<el-input v-model="form.porTension" @change="syncModal" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="入口活套张力">
<el-input v-model="form.celTension" @change="syncModal" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="清洗段张力">
<el-input v-model="form.cleanTension" @change="syncModal" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="炉区张力">
<el-input v-model="form.furTension" @change="syncModal" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="冷却塔张力">
<el-input v-model="form.towerTension" @change="syncModal" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="光整机不投张力">
<el-input v-model="form.tmNoneTension" @change="syncModal" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="光整机入口张力">
<el-input v-model="form.tmEntryTension" @change="syncModal" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="光整机出口张力">
<el-input v-model="form.tmExitTension" @change="syncModal" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item label="光整机轧制力">
<el-input v-model="form.tmRollforce" @change="syncModal" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="光整机弯辊力">
<el-input v-model="form.tmBendforce" @change="syncModal" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="光整机防皱辊插入量">
<el-input v-model="form.tmAcrMesh" @change="syncModal" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="光整机防颤辊插入量">
<el-input v-model="form.tmBrMesh" @change="syncModal" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="拉矫机延伸率">
<el-input v-model="form.tlElong" @change="syncModal" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="拉矫机矫直辊插入量1">
<el-input v-model="form.tlLvlMesh1" @change="syncModal" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="拉矫机矫直辊插入量2">
<el-input v-model="form.tlLvlMesh2" @change="syncModal" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item label="炉火段预热段出口板温">
<el-input v-model="form.preheatingSection" @change="syncModal" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="炉火段加热段出口板温">
<el-input v-model="form.heatingSection" @change="syncModal" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="炉火段冷却段出口板温">
<el-input v-model="form.coolingSection" @change="syncModal" />
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
</template>
<script>
import createFetch from '@/api/l2/plan'
export default {
props: {
income: {
type: Object,
required: true
},
baseURL: {
type: String,
required: true
}
},
data() {
return {
fetchApi: {},
form: {
// 全线张力
porTension: undefined, // 开卷机张力
celTension: undefined, // 入口活套张力
cleanTension: undefined, // 清洗段张力
furTension: undefined, // 炉区张力
towerTension: undefined, // 冷却塔张力
tmNoneTension: undefined, // 光整机不投张力
tmEntryTension: undefined, // 光整机入口张力
tmExitTension: undefined, // 光整机出口张力
tlNoneTension: undefined, // 拉矫机不投张力
tlExitTension: undefined, // 拉矫机出口张力
coatTension: undefined, // 后处理张力
cxlTension: undefined, // 出口活套张力
trTension: undefined, // 卷取机张力
// 光整机
tmRollforce: undefined, // 光整机轧制力
tmBendforce: undefined, // 光整机弯辊力
tmAcrMesh: undefined, // 光整机防皱辊插入量
tmBrMesh: undefined, // 光整机防颤辊插入量
// 拉矫机
tlElong: undefined, // 拉矫机延伸率
tlLvlMesh1: undefined, // 拉矫机矫直辊插入量1
tlLvlMesh2: undefined, // 拉矫机矫直辊插入量2
tlAcbMesh: undefined, // 拉矫机防横弓插入量
// 炉火段
preheatingSection: undefined, // 炉火段预热段出口板温
heatingSection: undefined, // 炉火段加热段出口板温
coolingSection: undefined, // 炉火段冷却段出口板温
},
loading: false
}
},
mounted() {
this.fetchApi = createFetch(this.baseURL)
this.fetchSetup()
},
methods: {
async fetchSetup() {
this.loading = true
const { entryThick, yieldPoint, steelGrade, spmElongation, spmRollforce, entryWidth } = this.income
const res1 = await this.fetchApi.getTension({
thick: entryThick,
yieldStren: yieldPoint
})
if (res1.data) {
// 全线张力的各种参数
this.form.porTension = res1.data.value1;
this.form.celTension = res1.data.value2;
this.form.cleanTension = res1.data.value3;
this.form.furTension = res1.data.value4;
this.form.towerTension = res1.data.value5;
this.form.tmNoneTension = res1.data.value6;
this.form.tmEntryTension = res1.data.value7;
this.form.tmExitTension = res1.data.value8;
this.form.tlNoneTension = res1.data.value9;
this.form.tlExitTension = res1.data.value10;
this.form.coatTension = res1.data.value11;
this.form.cxlTension = res1.data.value12;
this.form.trTension = res1.data.value13;
}
const res2 = await this.fetchApi.getTl({
thick: entryThick,
yieldStren: yieldPoint,
steelGrade: steelGrade
})
if (res2.data) {
this.form.tlElong = res2.data.value1;
this.form.tlLvlMesh1 = res2.data.value2;
this.form.tlLvlMesh2 = res2.data.value3;
this.form.tlAcbMesh = res2.data.value4;
}
const res3 = await this.fetchApi.getRollforce({
thick: entryThick,
yieldStren: yieldPoint,
steelGrade: steelGrade,
elong: spmElongation
})
if (res3.data) {
this.form.tmRollforce = res3.data.value1;
}
const res4 = await this.fetchApi.getBendforce({
width: entryWidth,
rollForce: spmRollforce
})
if (res4.data) {
this.form.tmBendforce = res4.data.value1;
}
const res5 = await this.fetchApi.getMesh({
thick: entryThick,
yieldStren: yieldPoint,
steelGrade: steelGrade
})
if (res5.data) {
this.form.tmAcrMesh = res5.data.value1;
this.form.tmBrMesh = res5.data.value2;
}
const res6 = await this.fetchApi.getFur(steelGrade)
if (res6.data) {
this.form.preheatingSection = res6.data.value1;
this.form.heatingSection = res6.data.value2;
this.form.coolingSection = res6.data.value3;
}
console.log('表单初始化完成', this.form)
this.syncModal()
this.loading = false
},
syncModal() {
this.$emit('input', this.form)
}
}
}
</script>

View File

@@ -0,0 +1,118 @@
<template>
<el-tabs>
<el-tab-pane label="全线张力">
<el-descriptions :column="1">
<el-descriptions-item label="开卷机张力">
{{ setupForm.porTension }}
</el-descriptions-item>
<el-descriptions-item label="入口活套张力">
{{ setupForm.celTension }}
</el-descriptions-item>
<el-descriptions-item label="清洗段张力">
{{ setupForm.cleanTension }}
</el-descriptions-item>
<el-descriptions-item label="炉区张力">
{{ setupForm.furTension }}
</el-descriptions-item>
<el-descriptions-item label="冷却塔张力">
{{ setupForm.towerTension }}
</el-descriptions-item>
<el-descriptions-item label="光整机不投张力">
{{ setupForm.tmNoneTension }}
</el-descriptions-item>
<el-descriptions-item label="光整机入口张力">
{{ setupForm.tmEntryTension }}
</el-descriptions-item>
<el-descriptions-item label="光整机出口张力">
{{ setupForm.tmExitTension }}
</el-descriptions-item>
<el-descriptions-item label="拉矫机不投张力">
{{ setupForm.tlNoneTension }}
</el-descriptions-item>
<el-descriptions-item label="拉矫机出口张力">
{{ setupForm.tlExitTension }}
</el-descriptions-item>
<el-descriptions-item label="后处理张力">
{{ setupForm.coatTension }}
</el-descriptions-item>
<el-descriptions-item label="出口活套张力">
{{ setupForm.cxlTension }}
</el-descriptions-item>
<el-descriptions-item label="卷取机张力">
{{ setupForm.trTension }}
</el-descriptions-item>
</el-descriptions>
</el-tab-pane>
<el-tab-pane label="拉矫机参数">
<el-descriptions :column="1">
<el-descriptions-item label="拉矫机延伸率">
{{ setupForm.tlElong }}
</el-descriptions-item>
<el-descriptions-item label="拉矫机矫直辊插入量1">
{{ setupForm.tlLvlMesh1 }}
</el-descriptions-item>
<el-descriptions-item label="拉矫机矫直辊插入量2">
{{ setupForm.tlLvlMesh2 }}
</el-descriptions-item>
<el-descriptions-item label="拉矫机防横弓插入量">
{{ setupForm.tlAcbMesh }}
</el-descriptions-item>
</el-descriptions>
</el-tab-pane>
<el-tab-pane label="光整机弯辊力">
<el-descriptions :column="1">
<el-descriptions-item label="光整机弯辊力">
{{ setupForm.tmBendforce }}
</el-descriptions-item>
</el-descriptions>
</el-tab-pane>
<el-tab-pane label="光整机插入量">
<el-descriptions :column="1">
<el-descriptions-item label="光整机防皱辊插入量">
{{ setupForm.tmAcrMesh }}
</el-descriptions-item>
<el-descriptions-item label="光整机防颤辊插入量">
{{ setupForm.tmBrMesh }}
</el-descriptions-item>
</el-descriptions>
</el-tab-pane>
<el-tab-pane label="光整机轧制力">
<el-descriptions :column="1">
<el-descriptions-item label="光整机轧制力">
{{ setupForm.tmRollforce }}
</el-descriptions-item>
</el-descriptions>
</el-tab-pane>
<el-tab-pane label="炉火段">
<el-descriptions :column="1">
<el-descriptions-item label="预热段出口板温">
{{ setupForm.preheatingSection }}
</el-descriptions-item>
<el-descriptions-item label="加热段出口板温">
{{ setupForm.heatingSection }}
</el-descriptions-item>
<el-descriptions-item label="冷却段出口板温">
{{ setupForm.coolingSection }}
</el-descriptions-item>
</el-descriptions>
</el-tab-pane>
</el-tabs>
</template>
<script>
// 接收数据渲染为tab
export default {
props: {
setupForm: {
type: Object,
required: true
}
},
}
</script>

View File

@@ -0,0 +1,774 @@
<template>
<div>
<el-button type="success" @click="handleAdd" icon="el-icon-plus" size="mini">新增</el-button>
<!-- 数据表格 -->
<el-table v-loading="loading" :data="planList" border
@row-click="handleRowClick" highlight-current-row size="mini" height="400">
<el-table-column prop="planid" label="计划号" align="center"></el-table-column>
<el-table-column prop="coilid" label="钢卷号" align="center"></el-table-column>
<el-table-column prop="steelGrade" label="钢种" align="center"></el-table-column>
<el-table-column prop="yieldPoint" label="屈服点" align="center"></el-table-column>
<el-table-column prop="entryWeight" label="重量(t)" align="center"></el-table-column>
<el-table-column prop="entryThick" label="厚度(mm)" align="center"></el-table-column>
<el-table-column prop="entryWidth" label="宽度(mm)" align="center"></el-table-column>
<el-table-column prop="entryLength" label="长度(mm)" align="center"></el-table-column>
<el-table-column prop="entryOuterDiameter" label="外径(mm)" align="center"></el-table-column>
<el-table-column prop="spmElongation" label="延伸率(%)" align="center"></el-table-column>
<el-table-column prop="spmRollforce" label="SPM轧制力" align="center"></el-table-column>
<el-table-column label="操作" width="160" align="center">
<template slot-scope="scope">
<el-button size="mini" type="primary" @click.stop="handleUpdate(scope.row)" icon="el-icon-edit">
编辑
</el-button>
<el-button size="mini" type="danger" @click.stop="handleDelete(scope.row)" icon="el-icon-delete"
:loading="scope.row.deleteLoading">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<el-row style="margin-top: 20px;" :gutter="20" v-if="currentRow">
<el-col :span="12">
<el-descriptions size="mini" :column="3">
<el-descriptions-item label="钢卷号">
{{ currentRow.coilid }}
</el-descriptions-item>
<el-descriptions-item label="钢种">
{{ currentRow.steelGrade }}
</el-descriptions-item>
<el-descriptions-item label="屈服点">
{{ currentRow.yieldPoint }}
</el-descriptions-item>
<el-descriptions-item label="重量(t)">
{{ currentRow.entryWeight }}
</el-descriptions-item>
<el-descriptions-item label="厚度(mm)">
{{ currentRow.entryThick }}
</el-descriptions-item>
<el-descriptions-item label="宽度(mm)">
{{ currentRow.entryWidth }}
</el-descriptions-item>
<el-descriptions-item label="长度(mm)">
{{ currentRow.entryLength }}
</el-descriptions-item>
<el-descriptions-item label="外径(mm)">
{{ currentRow.entryOuterDiameter }}
</el-descriptions-item>
<el-descriptions-item label="SPM延伸率(%)">
{{ currentRow.spmElongation }}
</el-descriptions-item>
<el-descriptions-item label="SPM轧制力">
{{ currentRow.spmRollforce }}
</el-descriptions-item>
</el-descriptions>
</el-col>
<el-col :span="12">
<setup-pane v-if="!setupLoading" :setup-form="setupForm" />
<div v-else v-loading="setupLoading" style="height: 200px;">
</div>
</el-col>
</el-row>
<!-- 编辑/新增弹窗 -->
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="80%" :close-on-click-modal="false"
append-to-body>
<el-form :model="form" ref="form" label-width="80px" :rules="rules" size="mini">
<!-- 基础信息区域 -->
<el-divider content-position="left">基础信息</el-divider>
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="计划ID" prop="planid">
<el-input v-model="form.planid" placeholder="请输入计划ID" maxLength="32"></el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="钢卷号" prop="coilid">
<el-input v-model="form.coilid" placeholder="请输入钢卷号" maxLength="32"></el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="钢种" prop="steelGrade">
<el-select v-model="form.steelGrade" placeholder="请选择钢种">
<el-option v-for="item in steelGradeList" :key="item.gradeid" :label="item.name"
:value="item.gradeid"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="厚度(mm)" prop="entryThick">
<el-input v-model="form.entryThick" placeholder="请输入入口厚度" type="number" step="0.01" min="0"></el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="宽度(mm)" prop="entryWidth">
<el-input v-model="form.entryWidth" placeholder="请输入入口宽度" type="number" step="1" min="0"></el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="屈服点" prop="yieldPoint">
<el-input v-model="form.yieldPoint" placeholder="请输入屈服点" type="number" step="0.01" min="0"></el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="重量(t)" prop="entryWeight">
<el-input v-model="form.entryWeight" placeholder="请输入入口重量" type="number" step="0.01" min="0"></el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="长度(mm)" prop="entryLength">
<el-input v-model="form.entryLength" placeholder="请输入入口长度" type="number" step="1" min="0"></el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="外径(mm)" prop="entryOuterDiameter">
<el-input v-model="form.entryOuterDiameter" placeholder="请输入入口外径" type="number" step="1"
min="0"></el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="延伸率(%)" prop="spmElongation">
<el-input v-model="form.spmElongation" placeholder="请输入延伸率" type="number" step="0.01" min="0"></el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="轧制力" prop="spmRollforce">
<el-input v-model="form.spmRollforce" placeholder="请输入SPM轧制力" type="number" step="0.01"
min="0"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-checkbox v-if="!form.id" v-model="spmFlag" label="生成工艺参数(建议先填写完成基础信息再勾选此选项)"></el-checkbox>
<el-divider content-position="left" v-if="spmFlag">工艺参数</el-divider>
<setup-form v-if="spmFlag" :income="form" :baseURL="baseURL" @input="handleSetupInput" />
<!-- 弹窗底部按钮 -->
<div slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitForm" :loading="saveLoading">保存</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import createFetch from '@/api/l2/plan'
import createSteelGradeFetch from '@/api/l2/steelGrade'
import SetupForm from './components/setupForm.vue'
import SetupPane from './components/setupPane.vue'
// 标准日期格式化方法(优化时间处理逻辑,适配接口日期格式)
function parseTime(time, format = "{yyyy}-{mm}-{dd} {hh}:{ii}:{ss}") {
if (!time) return "";
// 处理时间戳10位转13位兼容后端常见时间戳格式
if (typeof time === "number") {
if (time.toString().length === 10) time = time * 1000;
time = new Date(time);
}
// 处理字符串时间标准化ISO格式移除毫秒、多余字符避免解析异常
if (typeof time === "string") {
time = time.split(".")[0]; // 移除毫秒部分(如 "2024-05-01T12:00:00.123" → "2024-05-01T12:00:00"
time = time.replace(/\s+.*$/, ""); // 移除多余空格及后续字符
time = new Date(time);
}
// 校验日期有效性
if (!(time instanceof Date) || isNaN(time.getTime())) return "";
const formatObj = {
y: time.getFullYear(),
m: time.getMonth() + 1,
d: time.getDate(),
h: time.getHours(),
i: time.getMinutes(),
s: time.getSeconds(),
};
// 补零格式化(确保月/日/时/分/秒为两位数)
return format.replace(/{([ymdhisa])+}/g, (result, key) => {
const value = formatObj[key];
return value.toString().length < 2 ? "0" + value : value;
});
}
export default {
name: "PlanManagement",
components: {
SetupForm,
SetupPane
},
props: {
baseURL: {
type: String,
required: true
}
},
data() {
return {
fetchApi: {},
fetchSteelGradeApi: {},
setupLoading: false,
// 查询表单参数(与后端列表接口参数对齐)
// setupForm: {
// // 全线张力
// porTension: undefined, // 开卷机张力
// celTension: undefined, // 入口活套张力
// cleanTension: undefined, // 清洗段张力
// furTension: undefined, // 炉区张力
// towerTension: undefined, // 冷却塔张力
// tmNoneTension: undefined, // 光整机不投张力
// tmEntryTension: undefined, // 光整机入口张力
// tmExitTension: undefined, // 光整机出口张力
// tlNoneTension: undefined, // 拉矫机不投张力
// tlExitTension: undefined, // 拉矫机出口张力
// coatTension: undefined, // 后处理张力
// cxlTension: undefined, // 出口活套张力
// trTension: undefined, // 卷取机张力
// // 光整机
// tmRollforce: undefined, // 光整机轧制力
// tmBendforce: undefined, // 光整机弯辊力
// tmAcrMesh: undefined, // 光整机防皱辊插入量
// tmBrMesh: undefined, // 光整机防颤辊插入量
// // 拉矫机
// tlElong: undefined, // 拉矫机延伸率
// tlLvlMesh1: undefined, // 拉矫机矫直辊插入量1
// tlLvlMesh2: undefined, // 拉矫机矫直辊插入量2
// tlAcbMesh: undefined, // 拉矫机防横弓插入量
// },
// setupForm: undefined,
queryParams: {
coilid: "",
entryThick: null,
entryWidth: null,
status: null,
startDate: "",
endDate: "",
orderNo: "",
steelGrade: "",
unitCode: "",
},
spmFlag: false,
planList: [],
loading: false,
btnLoading: false,
// 弹窗状态(新增/编辑弹窗显隐+标题)
dialogVisible: false,
dialogTitle: "新增计划",
// 表单数据(与后端新增/编辑接口参数完全对齐)
form: {
id: null, // 主键ID组件内部用提交时后端自动处理
seqid: null, // 顺序号number接口要求
coilid: "", // 钢卷号string必填
unitCode: "", // 机组号string接口要求
dummyCoilFlag: 0, // 虚卷标识number默认0非虚卷
planid: "", // 计划IDstring接口要求
planType: "", // 计划类型string接口要求
originCoilid: "", // 原卷号string补充接口缺失字段
yieldPoint: null, // 屈服点number接口要求
entryWeight: null, // 入口重量(t)number接口要求
entryThick: null, // 入口厚度(mm)number接口要求
entryWidth: null, // 入口宽度(mm)number接口要求
entryLength: null, // 入口长度(mm)number接口要求
entryOuterDiameter: null, // 入口外径(mm)number接口要求
entryInnerDiameter: null, // 入口内径(mm)number接口要求
exitInnerDiameter: null, // 出口内径(mm)number接口要求
steelGrade: "", // 钢种string接口要求
exitCoilid: "", // 出口卷号string接口要求
tlvFlag: 0, // 张力平整标识number默认0未启用
orderNo: "", // 订单号string接口要求
custommerCode: "", // 客户代码string接口要求
orderThick: null, // 订单厚度(mm)number接口要求
orderWidth: null, // 订单宽度(mm)number接口要求
orderLenLow: null, // 订单长度下限(mm)number接口要求
orderLenUp: null, // 订单长度上限(mm)number接口要求
orderWt: null, // 订单重量(t)number接口要求
orderWtLow: null, // 订单重量下限(t)number接口要求
orderWtUp: null, // 订单重量上限(t)number接口要求
orderMetCoatTop: null, // 订单镀层上number接口要求
orderMetCoatBot: null, // 订单镀层下number接口要求
finalUse: "", // 最终用途string接口要求
hotCoilCode: "", // 热卷代码string接口要求
exitLengthTar: null, // 出口目标长度(mm)number接口要求
exitLengthTolMax: null, // 出口长度公差最大值(mm)number接口要求
exitLengthTolMin: null, // 出口长度公差最小值(mm)number接口要求
exitThickTar: null, // 出口目标厚度(mm)number接口要求
exitThickTolMax: null, // 出口厚度公差最大值(mm)number接口要求
exitThickTolMin: null, // 出口厚度公差最小值(mm)number接口要求
exitWidthTar: null, // 出口目标宽度(mm)number接口要求
exitWidthTolMax: null, // 出口宽度公差最大值(mm)number接口要求
exitWidthTolMin: null, // 出口宽度公差最小值(mm)number接口要求
nextWholeBacklogCode: "", // 下道全流程积压代码string接口要求
prevWholeBacklogCode: "", // 上道全流程积压代码string接口要求
exitWtMode: "", // 出口重量模式string接口要求
splitNum: null, // 分卷数量number接口要求
exitWeight1: null, // 出口重量1number接口要求
exitWeight2: null, // 出口重量2number接口要求
exitWeight3: null, // 出口重量3number接口要求
exitWeight4: null, // 出口重量4number接口要求
exitWeight5: null, // 出口重量5number接口要求
exitWeight6: null, // 出口重量6number接口要求
headSampleMode: "", // 头部取样模式string接口要求
headSamplePosition: "", // 头部取样位置string接口要求
headSampleLength: null, // 头部取样长度(mm)number接口要求
headSamplePlace: "", // 头部取样地点string接口要求
headSampleNum: null, // 头部取样数量number接口要求
tailSampleMode: "", // 尾部取样模式string接口要求
tailSamplePosition: "", // 尾部取样位置string接口要求
tailSampleLength: null, // 尾部取样长度(mm)number接口要求
tailSamplePlace: "", // 尾部取样地点string接口要求
tailSampleNum: null, // 尾部取样数量number接口要求
timestamp: null, // 时间戳object接口要求后端自动生成时可留空
onlineDate: null, // 上线时间objectDate类型适配接口object要求
startDate: null, // 开始时间objectDate类型适配接口object要求
endDate: null, // 结束时间objectDate类型适配接口object要求
furInDate: null, // 进炉时间objectDate类型适配接口object要求
furOutDate: null, // 出炉时间objectDate类型适配接口object要求
createTime: null, // 创建时间object后端自动生成前端无需修改
updateTime: null, // 更新时间object后端自动生成前端无需修改
surfaceVice: "", // 副面string接口要求
weldCode: "", // 焊缝代码string接口要求
spmFlag: 0, // SPM标识number默认0未启用
spmElongation: null, // SPM延伸率(%)number接口要求
spmRollforce: null, // SPM轧制力number接口要求
spmProcessType: "", // SPM工艺类型string接口要求
spmControlMode: "", // SPM控制模式string接口要求
spmBendingForce: null, // SPM弯曲力number接口要求
spmCrimpingRollMesh: null, // SPM咬入辊目number接口要求
spmBillyRollMesh: null, // SPM精轧辊目number接口要求
trimFlag: 0, // 切边标识number默认0不切边
tlvMode: "", // 张力平整模式string接口要求
tlvElongation: null, // 张力平整延伸率(%)number接口要求
chromating: 0, // 钝化标识number默认0无钝化
coatingCode: "", // 镀层代码string接口要求
oilingFlag: 0, // 涂油标识number默认0不涂油
oilingType: "", // 涂油类型string接口要求
oilingTop: null, // 涂油上number接口要求
oilingBottom: null, // 涂油下number接口要求
heatCycle: "", // 热循环string接口要求
coatingSelection: "", // 镀层选择string接口要求
coatingType: "", // 镀层类型string接口要求
chemTreatMode: "", // 化学处理模式string接口要求
aimWeightTop: null, // 目标重量上number接口要求
aimWeightBottom: null, // 目标重量下number接口要求
maxTolWeightTop: null, // 最大公差重量上number接口要求
maxTolWeightBottom: null, // 最大公差重量下number接口要求
headendGaugeLength: null, // 头端测厚长度(mm)number接口要求
tailendGaugeLength: null, // 尾端测厚长度(mm)number接口要求
},
// 表单验证规则(至少包含必填字段校验)
rules: {
coilid: [
{ required: true, message: "钢卷号不能为空", trigger: "blur" }, // 必填校验
{ max: 32, message: "钢卷号长度不能超过32字符", trigger: "blur" }, // 长度校验
],
// 新增 status 必填校验
status: [
{ required: true, message: "请选择计划状态", trigger: "change" }
]
},
// 保存按钮加载状态(避免重复提交)
saveLoading: false,
// 当前选中行数据(用于行点击、编辑回显)
currentRow: null,
steelGradeList: [],
};
},
created() {
this.fetchApi = createFetch(this.baseURL)
this.fetchSteelGradeApi = createSteelGradeFetch(this.baseURL)
// 组件创建时自动加载计划列表
this.getList();
this.fetchSteelGradeList();
},
methods: {
parseTime, // 挂载日期格式化方法到实例
fetchSteelGradeList() {
this.fetchSteelGradeApi.getSteelGradeList().then(res => {
this.steelGradeList = res.data
})
},
handleSetupInput(data) {
this.setupForm = data
},
// 获取计划列表数据(支持传入清理后的查询参数)
getList(cleanParams) {
this.loading = true; // 开启表格加载
const params = cleanParams || this.queryParams; // 优先使用传入参数(如重置/查询)
this.fetchApi.listPlan(params)
.then((response) => {
// 关闭加载状态
this.loading = false;
this.btnLoading = false;
// 处理列表数据格式转换、避免ID精度丢失
this.planList = response.data.map((item) => {
return {
...item,
deleteLoading: false, // 行删除按钮加载状态
};
});
})
.catch((error) => {
// 异常处理:关闭加载+提示错误
this.loading = false;
this.btnLoading = false;
console.error("获取计划列表失败:", error);
this.$message.error("获取数据失败,请重试");
});
},
// 处理查询操作(校验参数+调用列表接口)
handleQuery() {
// 深拷贝查询参数(避免修改原数据)
const params = JSON.parse(JSON.stringify(this.queryParams));
// 1. 时间范围校验(开始时间不能晚于结束时间)
if (params.startDate && params.endDate) {
const start = new Date(params.startDate);
const end = new Date(params.endDate);
if (start > end) {
this.$message.warning("开始时间不能晚于结束时间");
return;
}
}
// 3. 调用列表接口(带查询参数)
this.btnLoading = true;
this.getList(params);
},
// 重置查询表单(清空参数+重新加载列表)
handleReset() {
// 清空表单字段依赖表单ref="queryForm"
if (this.$refs.queryForm) {
this.$refs.queryForm.resetFields();
}
// 重新加载列表(无查询参数)
this.getList();
},
// 表格行点击事件(记录当前选中行)
handleRowClick(row) {
this.currentRow = { ...row }; // 深拷贝避免引用修改
this.fetchSpmParams()
},
// 新增计划(重置表单+打开弹窗)
handleAdd() {
this.dialogTitle = "新增计划"; // 设置弹窗标题
// 重置表单数据(默认值与接口要求对齐)
this.form = {
dummyCoilFlag: 0,
tlvFlag: 0,
spmFlag: 0,
trimFlag: 0,
chromating: 0,
oilingFlag: 0,
seqid: null,
coilid: "",
unitCode: "",
status: 0,
planid: "",
planType: "",
originCoilid: "",
yieldPoint: null,
entryWeight: null,
entryThick: null,
entryWidth: null,
entryLength: null,
entryOuterDiameter: null,
entryInnerDiameter: null,
exitInnerDiameter: null,
steelGrade: "",
exitCoilid: "",
orderNo: "",
custommerCode: "",
orderThick: null,
orderWidth: null,
orderLenLow: null,
orderLenUp: null,
orderWt: null,
orderWtLow: null,
orderWtUp: null,
orderMetCoatTop: null,
orderMetCoatBot: null,
finalUse: "",
hotCoilCode: "",
exitLengthTar: null,
exitLengthTolMax: null,
exitLengthTolMin: null,
exitThickTar: null,
exitThickTolMax: null,
exitThickTolMin: null,
exitWidthTar: null,
exitWidthTolMax: null,
exitWidthTolMin: null,
nextWholeBacklogCode: "",
prevWholeBacklogCode: "",
exitWtMode: "",
splitNum: null,
exitWeight1: null,
exitWeight2: null,
exitWeight3: null,
exitWeight4: null,
exitWeight5: null,
exitWeight6: null,
headSampleMode: "",
headSamplePosition: "",
headSampleLength: null,
headSamplePlace: "",
headSampleNum: null,
tailSampleMode: "",
tailSamplePosition: "",
tailSampleLength: null,
tailSamplePlace: "",
tailSampleNum: null,
timestamp: null,
onlineDate: null,
startDate: null,
endDate: null,
furInDate: null,
furOutDate: null,
createTime: null,
updateTime: null,
surfaceVice: "",
weldCode: "",
spmElongation: null,
spmRollforce: null,
spmProcessType: "",
spmControlMode: "",
spmBendingForce: null,
spmCrimpingRollMesh: null,
spmBillyRollMesh: null,
tlvMode: "",
tlvElongation: null,
coatingCode: "",
oilingType: "",
oilingTop: null,
oilingBottom: null,
heatCycle: "",
coatingSelection: "",
coatingType: "",
chemTreatMode: "",
aimWeightTop: null,
aimWeightBottom: null,
maxTolWeightTop: null,
maxTolWeightBottom: null,
headendGaugeLength: null,
tailendGaugeLength: null,
};
this.spmFlag = false
// 打开弹窗+清空表单验证状态
this.dialogVisible = true;
this.$nextTick(() => {
if (this.$refs.form) {
this.$refs.form.clearValidate();
}
});
},
// 编辑计划(回显行数据+打开弹窗)
handleUpdate(row) {
this.dialogTitle = "编辑计划"; // 设置弹窗标题
// 深拷贝行数据(避免修改原表格数据)
this.form = JSON.parse(JSON.stringify(row));
this.spmFlag = false
// 1. ID转字符串避免精度丢失
this.form.id = this.form.id !== null && this.form.id !== undefined ? String(this.form.id) : "";
// 2. 数值字段格式化确保为number类型适配接口要求
this.formatFormNumericFields(this.form);
// 3. 日期字段处理后端返回为格式化字符串转为Date对象适配表单组件
const dateFields = ["startDate", "endDate", "onlineDate", "furInDate", "furOutDate"];
dateFields.forEach((field) => {
this.form[field] = this.form[field] ? new Date(this.form[field]) : null;
});
// 4. 打开弹窗+清空验证状态
this.dialogVisible = true;
this.$nextTick(() => {
if (this.$refs.form) {
this.$refs.form.clearValidate();
}
});
},
// 格式化表单数值字段统一转为number/null适配接口类型要求
formatFormNumericFields(formData) {
// 所有需转为number类型的字段与接口参数对齐
const numericFields = [
"seqid", "dummyCoilFlag", "status", "tlvFlag", "splitNum", "headSampleNum", "tailSampleNum",
"spmFlag", "trimFlag", "chromating", "oilingFlag", "yieldPoint", "entryWeight", "entryThick",
"entryWidth", "entryLength", "entryOuterDiameter", "entryInnerDiameter", "exitInnerDiameter",
"orderThick", "orderWidth", "orderLenLow", "orderLenUp", "orderWt", "orderWtLow", "orderWtUp",
"orderMetCoatTop", "orderMetCoatBot", "exitLengthTar", "exitLengthTolMax", "exitLengthTolMin",
"exitThickTar", "exitThickTolMax", "exitThickTolMin", "exitWidthTar", "exitWidthTolMax",
"exitWidthTolMin", "exitWeight1", "exitWeight2", "exitWeight3", "exitWeight4", "exitWeight5",
"exitWeight6", "headSampleLength", "tailSampleLength", "spmElongation", "spmRollforce",
"spmBendingForce", "spmCrimpingRollMesh", "spmBillyRollMesh", "tlvElongation", "oilingTop",
"oilingBottom", "aimWeightTop", "aimWeightBottom", "maxTolWeightTop", "maxTolWeightBottom",
"headendGaugeLength", "tailendGaugeLength"
];
numericFields.forEach((field) => {
// 空值/空字符串转为null非空值转为number
if (formData[field] === "" || formData[field] === null || formData[field] === undefined) {
formData[field] = null;
} else {
formData[field] = Number(formData[field]);
}
});
},
// 删除计划(单个删除,支持批量扩展)
handleDelete(row) {
// 弹窗确认(危险操作二次确认)
this.$confirm(`确定删除ID: ${row.id} 的计划?`, "确认删除", {
type: "danger",
confirmButtonText: "确认",
cancelButtonText: "取消"
})
.then(() => {
row.deleteLoading = true; // 开启行删除按钮加载
// 调用删除接口传入ID数组支持批量扩展
this.fetchApi.delPlan([row.id])
.then((res) => {
row.deleteLoading = false; // 关闭加载
this.$message.success("删除成功");
this.getList(); // 重新加载列表
})
.catch((err) => {
row.deleteLoading = false; // 关闭加载
console.error("删除计划失败:", err);
this.$message.error("删除失败,请重试");
});
})
},
// 保存表单(新增/编辑统一处理)
submitForm() {
// 1. 表单验证依赖表单ref="form"
this.$refs.form.validate((valid) => {
if (!valid) return; // 验证不通过则终止
// 2. 深拷贝表单数据(避免修改原数据)
const formData = JSON.parse(JSON.stringify(this.form));
// 3. 时间范围校验(开始时间不能晚于结束时间)
if (formData.startDate && formData.endDate) {
const start = new Date(formData.startDate);
const end = new Date(formData.endDate);
if (start > end) {
this.$message.warning("开始时间不能晚于结束时间");
return;
}
}
// 4. 数值字段格式化(确保与接口类型一致)
this.formatFormNumericFields(formData);
// 8. 调用新增/编辑接口
this.saveLoading = true; // 开启保存按钮加载
const request = formData.id ? this.fetchApi.updatePlan(formData) : this.fetchApi.addPlan(formData);
request
.then((res) => {
this.saveLoading = false; // 关闭加载
console.log("服务器响应:", res);
if (res.code === 200) {
this.$message.success(formData.id ? "更新计划成功" : "新增计划成功");
this.dialogVisible = false; // 关闭弹窗
this.getList(); // 重新加载列表
} else {
this.$message.error(res.msg || (formData.id ? "更新失败" : "新增失败"));
}
})
.catch((err) => {
this.saveLoading = false; // 关闭加载
console.error(`${formData.id ? "更新" : "新增"}计划失败:`, err);
this.$message.error(`${formData.id ? "更新" : "新增"}失败,请重试`);
});
// 如果是新增计划并且勾选了生成工艺参数,则生成工艺参数
if (!formData.id && this.spmFlag) {
this.generateSpmParams();
}
});
},
fetchSpmParams() {
console.log("获取工艺参数");
this.setupLoading = true
this.fetchApi.listSetup({
coilid: this.currentRow.coilid,
planid: this.currentRow.planid,
}).then(res => {
if (res.rows.length > 0) {
this.setupForm = res.rows[0]
} else {
this.setupForm = {}
}
this.setupLoading = false
})
},
generateSpmParams() {
this.fetchApi.addSetup({
coilid: this.form.coilid,
planid: this.form.planid,
...this.setupForm
}).then(res => {
this.$message.success("工艺参数创建成功");
})
}
},
};
</script>
<style scoped>
.plan-management {
min-height: calc(100vh - 120px);
}
/* 查询表单样式 */
::v-deep .el-form--inline .el-form-item {
margin-right: 16px;
margin-bottom: 16px;
}
/* 表格样式 */
::v-deep .el-table {
margin-top: 16px;
}
/* 弹窗表单样式 */
::v-deep .el-dialog__body {
max-height: 60vh;
overflow-y: auto;
padding: 16px;
}
::v-deep .el-divider--horizontal {
margin: 16px 0;
}
::v-deep .el-form-item {
margin-bottom: 16px;
}
/* 操作列样式 */
.dialog-footer {
text-align: right;
}
</style>

View File

@@ -0,0 +1,310 @@
<template>
<el-form
:model="formData"
ref="correctionForm"
label-width="120px"
:rules="formRules"
>
<el-row :gutter="20">
<!-- 成品卷 -->
<el-col :span="8">
<el-form-item label="成品卷" prop="exitMatId">
<el-input v-model="formData.exitMatId"></el-input>
</el-form-item>
</el-col>
<!-- 来料卷 -->
<el-col :span="8">
<el-form-item label="来料卷" prop="entryMatId">
<el-input v-model="formData.entryMatId"></el-input>
</el-form-item>
</el-col>
<!-- 分切数 -->
<el-col :span="8">
<el-form-item label="分切数" prop="subId">
<el-input-number size="mini" v-model="formData.subId" :min="0" :step="1" style="width: 100%;"></el-input-number>
</el-form-item>
</el-col>
<!-- 开始位置 -->
<el-col :span="8">
<el-form-item label="开始位置" prop="startPosition">
<el-input-number size="mini" v-model="formData.startPosition" :step="0.01" style="width: 100%;"></el-input-number>
</el-form-item>
</el-col>
<!-- 结束位置 -->
<el-col :span="8">
<el-form-item label="结束位置" prop="endPosition">
<el-input-number size="mini" v-model="formData.endPosition" :step="0.01" style="width: 100%;"></el-input-number>
</el-form-item>
</el-col>
<!-- 计划ID -->
<el-col :span="8">
<el-form-item label="计划ID" prop="planId">
<el-input-number size="mini" v-model="formData.planId" :step="1" style="width: 100%;"></el-input-number>
</el-form-item>
</el-col>
<!-- 计划号 -->
<el-col :span="8">
<el-form-item label="计划号" prop="planNo">
<el-input size="mini" v-model="formData.planNo"></el-input>
</el-form-item>
</el-col>
<!-- 产品类型 -->
<el-col :span="8">
<el-form-item label="产品类型" prop="prodCode">
<el-input size="mini" v-model="formData.prodCode"></el-input>
</el-form-item>
</el-col>
<!-- 班号 -->
<el-col :span="8">
<el-form-item label="班号" prop="groupNo">
<el-input size="mini" v-model="formData.groupNo"></el-input>
</el-form-item>
</el-col>
<!-- 组号 -->
<el-col :span="8">
<el-form-item label="组号" prop="shiftNo">
<el-input size="mini" v-model="formData.shiftNo"></el-input>
</el-form-item>
</el-col>
<!-- 状态 -->
<el-col :span="8">
<el-form-item label="状态" prop="status">
<el-input size="mini" v-model="formData.status"></el-input>
</el-form-item>
</el-col>
<!-- 钢种 -->
<el-col :span="8">
<el-form-item label="钢种" prop="steelGrade">
<el-input size="mini" v-model="formData.steelGrade"></el-input>
</el-form-item>
</el-col>
<!-- 来料厚度 -->
<el-col :span="8">
<el-form-item label="来料厚度" prop="entryThick">
<el-input-number size="mini" v-model="formData.entryThick" :step="0.01" style="width: 100%;"></el-input-number>
</el-form-item>
</el-col>
<!-- 来料宽度 -->
<el-col :span="8">
<el-form-item label="来料宽度" prop="entryWidth">
<el-input-number size="mini" v-model="formData.entryWidth" :step="0.01" style="width: 100%;"></el-input-number>
</el-form-item>
</el-col>
<!-- 来料长度 -->
<el-col :span="8">
<el-form-item label="来料长度" prop="entryLength">
<el-input-number size="mini" v-model="formData.entryLength" :step="0.01" style="width: 100%;"></el-input-number>
</el-form-item>
</el-col>
<!-- 来料重量 -->
<el-col :span="8">
<el-form-item label="来料重量" prop="entryWeight">
<el-input-number size="mini" v-model="formData.entryWeight" :step="0.01" style="width: 100%;"></el-input-number>
</el-form-item>
</el-col>
<!-- 上表面镀锌 -->
<el-col :span="8">
<el-form-item label="上表面镀锌" prop="weightTop">
<el-input-number size="mini" v-model="formData.weightTop" :step="0.01" style="width: 100%;"></el-input-number>
</el-form-item>
</el-col>
<!-- 下表面镀锌 -->
<el-col :span="8">
<el-form-item label="下表面镀锌" prop="weightBottom">
<el-input-number size="mini" v-model="formData.weightBottom" :step="0.01" style="width: 100%;"></el-input-number>
</el-form-item>
</el-col>
<!-- 成品长度 -->
<el-col :span="8">
<el-form-item label="成品长度" prop="exitLength">
<el-input-number size="mini" v-model="formData.exitLength" :step="0.01" style="width: 100%;"></el-input-number>
</el-form-item>
</el-col>
<!-- 成品带涂料重量 -->
<el-col :span="8">
<el-form-item label="成品带涂料重量" prop="exitNetWeight">
<el-input-number size="mini" v-model="formData.exitNetWeight" :step="0.01" style="width: 100%;"></el-input-number>
</el-form-item>
</el-col>
<!-- 理论重量 -->
<el-col :span="8">
<el-form-item label="理论重量" prop="theoryWeight">
<el-input-number size="mini" v-model="formData.theoryWeight" :step="0.01" style="width: 100%;"></el-input-number>
</el-form-item>
</el-col>
<!-- 实际重量 -->
<el-col :span="8">
<el-form-item label="实际重量" prop="actualWeight">
<el-input-number size="mini" v-model="formData.actualWeight" :step="0.01" style="width: 100%;"></el-input-number>
</el-form-item>
</el-col>
<!-- 成品外径 -->
<el-col :span="8">
<el-form-item label="成品外径" prop="exitOuterDiameter">
<el-input-number size="mini" v-model="formData.exitOuterDiameter" :step="0.01" style="width: 100%;"></el-input-number>
</el-form-item>
</el-col>
<!-- 成品厚度 -->
<el-col :span="8">
<el-form-item label="成品厚度" prop="exitThickness">
<el-input-number size="mini" v-model="formData.exitThickness" :step="0.01" style="width: 100%;"></el-input-number>
</el-form-item>
</el-col>
<!-- 成品宽度 -->
<el-col :span="8">
<el-form-item label="成品宽度" prop="exitWidth">
<el-input-number size="mini" v-model="formData.exitWidth" :step="0.01" style="width: 100%;"></el-input-number>
</el-form-item>
</el-col>
<!-- 客户 -->
<el-col :span="8">
<el-form-item label="客户" prop="customer">
<el-input size="mini" v-model="formData.customer"></el-input>
</el-form-item>
</el-col>
<!-- 上线时间 -->
<el-col :span="8">
<el-form-item label="上线时间" prop="onlineTime">
<el-date-picker size="mini" v-model="formData.onlineTime" type="datetime" value-format="yyyy-MM-ddTHH:mm:ss" />
</el-form-item>
</el-col>
<!-- 开始时间 -->
<el-col :span="8">
<el-form-item label="开始时间" prop="startTime">
<el-date-picker size="mini" v-model="formData.startTime" type="datetime" value-format="yyyy-MM-ddTHH:mm:ss" />
</el-form-item>
</el-col>
<!-- 结束时间 -->
<el-col :span="8">
<el-form-item label="结束时间" prop="endTime">
<el-date-picker size="mini" v-model="formData.endTime" type="datetime" value-format="yyyy-MM-ddTHH:mm:ss" />
</el-form-item>
</el-col>
<!-- 机组号 -->
<el-col :span="8">
<el-form-item label="机组号" prop="unitCode">
<el-input size="mini" v-model="formData.unitCode"></el-input>
</el-form-item>
</el-col>
<!-- 工序号 -->
<el-col :span="8">
<el-form-item label="工序号" prop="processCode">
<el-input size="mini" v-model="formData.processCode"></el-input>
</el-form-item>
</el-col>
<!-- 是否尾卷 -->
<el-col :span="8">
<el-form-item label="是否尾卷" prop="lastFlag">
<el-switch v-model="formData.lastFlag"></el-switch>
</el-form-item>
</el-col>
<!-- 是否分卷 -->
<el-col :span="8">
<el-form-item label="是否分卷" prop="separateFlag">
<el-switch v-model="formData.separateFlag"></el-switch>
</el-form-item>
</el-col>
<!-- 计划来源 -->
<el-col :span="8">
<el-form-item label="计划来源" prop="planOrigin">
<el-select v-model="formData.planOrigin" placeholder="请选择计划来源">
<el-option label="L3计划" value="L3"></el-option>
<el-option label="人工" value="MANUAL"></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<!-- 保存按钮 -->
<div class="dialog-footer">
<el-button type="primary" @click="handleSave" :loading="saveLoading">保存</el-button>
</div>
</el-form>
</template>
<script>
export default {
name: 'PdoDataCorrection',
props: {
// 接收父组件传递的详情数据
detail: {
type: Object,
required: true,
default: () => ({})
},
// 保存回调函数(父组件处理实际保存逻辑)
saveCallback: {
type: Function,
required: true
},
// 保存按钮loading状态由父组件控制
saveLoading: {
type: Boolean,
required: true,
default: false
}
},
data() {
return {
// 内部表单数据拷贝父组件传递的detail避免直接修改父组件数据
formData: {},
// 表单验证规则
formRules: {
exitMatId: [{ required: true, message: '请输入成品卷号', trigger: 'blur' }],
entryMatId: [{ required: true, message: '请输入来料卷号', trigger: 'blur' }],
planNo: [{ required: true, message: '请输入计划号', trigger: 'blur' }],
steelGrade: [{ required: true, message: '请输入钢种', trigger: 'blur' }]
// 可根据实际需求补充其他字段验证规则
}
}
},
watch: {
// 监听detail变化深拷贝更新内部formData
detail: {
handler(newVal) {
// 深拷贝避免引用类型数据相互影响
this.formData = JSON.parse(JSON.stringify(newVal))
// 初始化数值类型字段防止null/undefined导致输入框异常
this.initNumberFields()
},
immediate: true, // 初始渲染时立即执行
deep: true // 深度监听对象内部变化
}
},
methods: {
// 初始化数值类型字段确保默认值为0而非null/undefined
initNumberFields() {
const numberFields = [
'subId', 'startPosition', 'endPosition', 'planId', 'entryThick',
'entryWidth', 'entryLength', 'entryWeight', 'weightTop', 'weightBottom',
'exitLength', 'exitNetWeight', 'theoryWeight', 'actualWeight',
'exitOuterDiameter', 'exitThickness', 'exitWidth'
]
numberFields.forEach(field => {
if (this.formData[field] === undefined || this.formData[field] === null) {
this.formData[field] = 0
}
})
// 初始化布尔类型字段
this.formData.lastFlag = this.formData.lastFlag ?? false
this.formData.separateFlag = this.formData.separateFlag ?? false
},
// 处理保存逻辑(先验证表单,通过后触发父组件回调)
handleSave() {
this.$refs.correctionForm.validate(valid => {
if (valid) {
// 触发父组件保存回调,传递内部修改后的表单数据
this.saveCallback(this.formData)
}
})
}
}
}
</script>
<style scoped>
.dialog-footer {
text-align: right;
margin-top: 20px;
}
</style>

View File

@@ -0,0 +1,271 @@
<template>
<div class="label-print-container">
<!-- 预览区域与打印内容一致且可编辑 -->
<div class="label-preview" v-if="Object.keys(editableData).length">
<!-- 给打印区域添加一个唯一ID -->
<div id="printContent" class="label-content">
<!-- 公司名称 -->
<div class="company-name">
{{ editableData.companyName }}
</div>
<!-- 内容区域使用Flexbox布局 -->
<div class="content-grid">
<div class="grid-item label">钢卷号</div>
<div class="grid-item value">
<input v-model="editableData.exitMatId" :border="false" class="editable-input"
placeholder="钢卷号"></input>
</div>
<div class="grid-item label">热卷号</div>
<div class="grid-item value">
<input v-model="editableData.entryMatId" :border="false" class="editable-input"
placeholder="热卷号"></input>
</div>
<div class="grid-item label">规格</div>
<div class="grid-item value">
<input v-model="editableData.spec" :border="false" class="editable-input" placeholder="规格"></input>
</div>
<div class="grid-item label">材质</div>
<div class="grid-item value">
<input v-model="editableData.steelGrade" :border="false" class="editable-input"
placeholder="材质"></input>
</div>
<div class="grid-item label">净重</div>
<div class="grid-item value">
<input v-model="editableData.actualWeight" :border="false" class="editable-input"
placeholder="净重"></input>
</div>
<div class="grid-item label">生产班组</div>
<div class="grid-item value">
<input v-model="editableData.groupNo" :border="false" class="editable-input"
placeholder="生产班组"></input>
</div>
<div class="grid-item label">产品名称</div>
<div class="grid-item value">
<input v-model="editableData.prodCode" :border="false" class="editable-input"
placeholder="产品名称"></input>
</div>
<div class="grid-item label">生产日期</div>
<div class="grid-item value">
<input v-model="editableData.productionDate" :border="false" class="editable-input"
placeholder="生产日期"></input>
</div>
</div>
</div>
</div>
<!-- 打印按钮 -->
<div class="print-btn-container">
<el-button type="primary" @click="handlePrint" icon="el-icon-printer"
:disabled="!Object.keys(editableData).length">
打印标签
</el-button>
</div>
</div>
</template>
<script>
import printJS from 'print-js';
import dayjs from 'dayjs';
export default {
name: 'PdoLabelPrint',
props: {
detail: {
type: Object,
required: true,
default: () => ({})
},
},
data() {
return {
// 可编辑的数据对象
editableData: {}
};
},
watch: {
// 监听detail变化初始化可编辑数据
detail: {
handler(newVal) {
if (newVal) {
this.initEditableData(newVal);
}
},
immediate: true,
deep: true
}
},
methods: {
// 初始化可编辑数据
initEditableData(detail) {
const {
exitMatId, entryMatId, exitThickness, exitWidth,
actualWeight, steelGrade, groupNo, prodCode
} = detail;
// 格式化规格
const spec = exitThickness && exitWidth
? `${exitThickness}*${exitWidth}`
: '';
// 格式化生产日期
const productionDate = dayjs().format('M月D日');
// 初始化可编辑数据
this.editableData = {
companyName: '嘉祥科伦普重工有限公司',
exitMatId: exitMatId || '',
entryMatId: entryMatId || '',
spec: spec || '',
steelGrade: steelGrade || '',
actualWeight: actualWeight || '',
groupNo: groupNo || '',
prodCode: prodCode || '',
productionDate: productionDate
};
},
// 处理打印逻辑
handlePrint() {
// 定义打印样式,确保打印效果与预览一致
const printStyle = `
.label-content {
width: 200mm;
height: 160mm;
padding: 0;
box-sizing: border-box;
font-family: SimSun;
}
.company-name {
text-align: center;
padding: 8mm 0;
font-size: 16pt;
font-weight: bold;
border: 1px solid #000;
}
.content-grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
gap: 0;
}
.grid-item {
min-height: 15mm;
display: flex;
border: 1px solid #000;
align-items: center;
box-sizing: border-box;
}
.label {
font-weight: bold;
font-size: 12pt;
padding-left: 2mm;
}
.value {
font-size: 12pt;
}
input {
border: none !important;
width: 100%;
height: 100%;
font-size: 12pt;
padding: 0 2mm;
box-sizing: border-box;
}
`;
// 调用printJS进行打印
printJS({
printable: 'printContent', // 要打印的元素ID
type: 'html',
header: null, // 不显示默认页眉
footer: null, // 不显示默认页脚
style: printStyle, // 应用打印样式
scanStyles: false, // 不扫描页面现有样式
targetStyles: ['*'], // 允许所有目标样式
documentTitle: '标签打印', // 打印文档标题
onPrintDialogClose: () => {
console.log('打印对话框已关闭');
}
});
}
}
}
</script>
<style scoped>
.label-print-container {
padding: 10px;
}
/* 预览区域样式 */
.label-preview {
margin-bottom: 20px;
}
/* 标签内容样式(与打印尺寸等比例) */
.label-content {
width: 500px; /* 200mm × 2.5 = 500px */
height: 400px; /* 160mm × 2.5 = 400px */
border: 1px solid #ccc;
padding: 25px; /* 10mm × 2.5 = 25px */
box-sizing: border-box;
font-family: SimSun;
}
.company-name {
text-align: center;
padding: 20px;
border: 1px solid black;
font-size: 24px;
font-weight: bold;
}
/* 内容网格布局 */
.content-grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
gap: 0;
border: 1px solid black;
}
.grid-item {
border: 1px solid black;
min-height: 40px;
align-items: center;
box-sizing: border-box;
}
.label {
font-weight: bold;
line-height: 60px;
font-size: 16px;
}
/* 无边框输入框样式 */
.editable-input {
width: 100%;
padding: 4px;
text-align: inherit;
background: transparent;
font-size: inherit;
border: none;
outline: none;
height: 60px;
box-sizing: border-box;
line-height: 60px;
}
.editable-input:hover,
.editable-input:focus {
background: rgba(255, 255, 255, 0.8);
outline: none;
}
.print-btn-container {
text-align: left;
margin-top: 10px;
}
</style>

View File

@@ -0,0 +1,68 @@
<template>
<el-descriptions
:column="1"
border
size="mini"
>
<!-- 循环渲染汇总项从计算属性获取数据 -->
<el-descriptions-item
v-for="(item, index) in summaryResult"
:key="index"
:label="item.label"
>
<!-- 根据类型格式化显示重量/百分比/数量 -->
<span :class="item.type === 'rate' ? 'summary-rate' : ''">
{{ item.value }}
{{ item.unit }}
</span>
</el-descriptions-item>
</el-descriptions>
</template>
<script>
export default {
name: 'PdoSummary',
props: {
// 接收表格原始数据(父组件传递)
tableData: {
type: Array,
required: true,
default: () => [] // 默认空数组,避免无数据时报错
}
},
computed: {
// 核心根据tableData计算汇总结果
summaryResult() {
// 1. 基础数值汇总处理字符串转数字避免NaN
const totalEntryWeight = this.tableData.reduce((sum, item) => {
return sum + (Number(item.entryWeight) || 0)
}, 0) // 原料总重(来料重量求和)
const totalExitWeight = this.tableData.reduce((sum, item) => {
return sum + (Number(item.exitNetWeight) || 0)
}, 0) // 产品总重(成品带涂料重量求和)
const totalZincRemoved = totalEntryWeight - totalExitWeight // 去锌总理重(原料总重 - 产品总重)
// 2. 成材率计算避免除数为0
const yieldRate = totalEntryWeight > 0
? ((totalExitWeight / totalEntryWeight) * 100).toFixed(2)
: '0.00'
// 3. 卷数统计(原料/成品卷数默认按表格行数可根据实际业务调整如去重entryMatId
const totalEntryCoils = this.tableData.length // 原料总卷数
const totalExitCoils = this.tableData.length // 成品总卷数
// 4. 返回格式化后的汇总项
return [
{ label: '原料总重', value: totalEntryWeight.toFixed(1), unit: 't', type: 'weight' },
{ label: '去锌总理重', value: totalZincRemoved.toFixed(1), unit: 't', type: 'weight' },
{ label: '产品总重', value: totalExitWeight.toFixed(1), unit: 't', type: 'weight' },
{ label: '成材率', value: yieldRate, unit: '%', type: 'rate' },
{ label: '原料总卷数', value: totalEntryCoils, unit: '卷', type: 'count' },
{ label: '成品总卷数', value: totalExitCoils, unit: '卷', type: 'count' }
]
}
}
}
</script>

View File

@@ -0,0 +1,450 @@
<template>
<el-row :gutter="20" class="monitoring-container">
<!-- 实时数据折线图 -->
<el-col :span="20">
<div class="chart-wrapper">
<!-- 参数选择下拉框 -->
<el-select
v-model="paramField"
class="param-select"
@change="handleParamChange"
size="mini"
>
<el-option
v-for="item in paramFields"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
<!-- 图表容器 -->
<div ref="chart" class="chart-content"></div>
</div>
</el-col>
</el-row>
</template>
<script>
import * as echarts from 'echarts'
import createFetch from '@/api/l2/pdo'
export default {
name: 'DeviceMonitoring',
props: {
enCoilID: {
type: String,
required: true,
},
baseURL: {
type: String,
default: ''
}
},
data() {
return {
fetchApi: {},
paramFields: [
{ label: '带钢速度', value: 'stripSpeed' },
{ label: '张力PorBr1', value: 'tensionPorBr1' },
{ label: '清洗电压', value: 'cleaningVoltage' }
],
paramField: 'stripSpeed',
treeProps: {
children: 'children',
label: 'label'
},
currentParam: null, // 当前选中的参数
chart: null, // 图表实例
chartData: [], // 图表数据
timeStamps: [], // 时间戳
timeRange: '1', // 时间范围(分钟)
socket: null, // 模拟socket
latestValue: 0,
maxValue: 0,
minValue: 0,
avgValue: 0
}
},
watch: {
enCoilID: {
handler(newVal) {
if (newVal) {
this.fetchChartData();
} else {
this.drawEmptyChart();
}
},
immediate: true
},
paramField: {
handler() {
if (this.enCoilID) {
this.fetchChartData();
}
}
}
},
mounted() {
// 初始化图表
this.fetchApi = createFetch(this.baseURL)
this.initChart();
},
beforeDestroy() {
// 销毁图表实例和事件监听
if (this.chart) {
this.chart.dispose();
}
window.removeEventListener('resize', this.handleResize);
},
methods: {
// 初始化图表
initChart() {
if (this.$refs.chart) {
this.chart = echarts.init(this.$refs.chart);
// 监听窗口大小变化,调整图表尺寸
window.addEventListener('resize', this.handleResize);
// 初始显示
if (this.enCoilID) {
this.fetchChartData();
} else {
this.drawEmptyChart();
}
}
},
// 处理窗口大小变化
handleResize() {
if (this.chart) {
this.chart.resize();
}
},
// 获取图表数据
fetchChartData() {
this.fetchApi.getSegmentList({
enCoilID: this.enCoilID,
paramField: this.paramField
}).then(res => {
// 假设res.data格式为[{ segNo: '时间戳', value: '数值' }]
if (res.data && res.data.length) {
this.processChartData(res.data);
this.drawLineChart();
} else {
this.drawNoDataChart();
}
}).catch(err => {
console.error('获取数据失败:', err);
this.drawErrorChart();
});
},
// 处理图表数据
processChartData(rawData) {
// 排序数据按segNo假设是时间戳或序号
rawData.sort((a, b) => a.segNo - b.segNo);
// 提取时间戳和数值
this.timeStamps = rawData.map(item => item.segNo);
this.chartData = rawData.map(item => parseFloat(item.value));
// 计算统计值
if (this.chartData.length) {
this.latestValue = this.chartData[this.chartData.length - 1];
this.maxValue = Math.max(...this.chartData);
this.minValue = Math.min(...this.chartData);
this.avgValue = this.chartData.reduce((sum, val) => sum + val, 0) / this.chartData.length;
}
},
// 绘制折线图
drawLineChart() {
if (!this.chart) return;
const option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
formatter: (params) => {
const param = params[0];
return `
时间: ${param.name}<br>
值: ${param.value.toFixed(2)}<br>
单位: ${this.getParamUnit()}
`;
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
top: '15%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
data: this.timeStamps,
axisLabel: {
rotate: 45,
interval: 0
}
},
yAxis: {
type: 'value',
name: this.getParamUnit(),
nameLocation: 'middle',
nameGap: 30
},
series: [
{
name: this.getParamLabel(),
type: 'line',
data: this.chartData,
smooth: true,
symbol: 'circle',
symbolSize: 6,
markPoint: {
data: [
{ type: 'max', name: '最大值' },
{ type: 'min', name: '最小值' }
]
},
areaStyle: {},
markLine: {
data: [
{ type: 'average', name: '平均值' }
]
}
}
]
};
this.chart.setOption(option);
},
// 绘制空图表(未选择实绩)
drawEmptyChart() {
if (!this.chart) return;
const option = {
graphic: {
elements: [
{
type: 'text',
left: 'center',
top: 'center',
style: {
text: '请选择实绩',
fontSize: 16,
fontWeight: 'bold',
fill: '#999'
}
}
]
},
xAxis: {
type: 'category',
data: []
},
yAxis: {
type: 'value'
},
series: []
};
this.chart.setOption(option);
},
// 绘制无数据图表
drawNoDataChart() {
if (!this.chart) return;
const option = {
graphic: {
elements: [
{
type: 'text',
left: 'center',
top: 'center',
style: {
text: '暂无数据',
fontSize: 16,
fontWeight: 'bold',
fill: '#999'
}
}
]
},
xAxis: {
type: 'category',
data: []
},
yAxis: {
type: 'value'
},
series: []
};
this.chart.setOption(option);
},
// 绘制错误图表
drawErrorChart() {
if (!this.chart) return;
const option = {
graphic: {
elements: [
{
type: 'text',
left: 'center',
top: 'center',
style: {
text: '数据加载失败',
fontSize: 16,
fontWeight: 'bold',
fill: '#f56c6c'
}
}
]
},
xAxis: {
type: 'category',
data: []
},
yAxis: {
type: 'value'
},
series: []
};
this.chart.setOption(option);
},
// 处理参数变更
handleParamChange() {
// 清空现有数据
this.chartData = [];
this.timeStamps = [];
// 重新获取数据
if (this.enCoilID) {
this.fetchChartData();
}
},
// 获取参数标签
getParamLabel() {
const param = this.paramFields.find(item => item.value === this.paramField);
return param ? param.label : this.paramField;
},
// 获取参数单位
getParamUnit() {
// 根据不同参数返回不同单位
switch(this.paramField) {
case 'stripSpeed':
return 'm/s';
case 'tensionPorBr1':
return 'N';
case 'cleaningVoltage':
return 'V';
default:
return '';
}
}
}
}
</script>
<style scoped>
.monitoring-container {
padding: 10px;
height: 100%;
box-sizing: border-box;
}
.chart-wrapper {
position: relative;
width: 100%;
height: 100%;
}
.param-select {
position: absolute;
top: 10px;
left: 10px;
z-index: 10;
width: 150px;
}
.chart-content {
width: 100%;
height: 300px;
}
/* 原有样式保留 */
.tree-container,
.chart-container {
height: 100%;
}
.el-card {
height: 100%;
display: flex;
flex-direction: column;
}
.tree-header,
.chart-header {
display: flex;
align-items: center;
justify-content: space-between;
font-weight: bold;
}
.tree-header i,
.chart-header i {
margin-right: 8px;
color: #409EFF;
}
.device-tree {
overflow-y: auto;
flex: 1;
padding-top: 10px;
}
.custom-tree-node {
display: flex;
align-items: center;
padding: 5px 0;
color: #606266;
}
.custom-tree-node .online-status {
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
background-color: #13ce66;
margin-left: 5px;
}
:deep(.el-tree-node__content:hover) {
background-color: transparent !important;
color: white !important;
}
:deep(.el-tree-node .el-tree-node__content:hover) {
background-color: transparent !important;
}
:deep(.el-tree-indent) {
width: 16px;
}
</style>

View File

@@ -0,0 +1,282 @@
<template>
<div>
<el-row>
<!-- 查询表单保持不变 -->
<el-form :inline="true" :model="queryForm" ref="queryForm" label-width="100px">
<el-form-item label="钢卷号" prop="coilid">
<el-input size="mini" v-model="queryForm.coilid" placeholder="请输入钢卷号"></el-input>
</el-form-item>
<el-form-item label="开始日期" prop="startDate">
<el-date-picker size="mini" v-model="queryForm.startDate" type="date" placeholder="选择开始日期"
value-format="yyyy-MM-dd" clearable></el-date-picker>
</el-form-item>
<el-form-item label="结束日期" prop="endDate">
<el-date-picker size="mini" v-model="queryForm.endDate" type="date" placeholder="选择结束日期"
value-format="yyyy-MM-dd" clearable></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery" :loading="btnLoading" icon="el-icon-search"
size="mini">查询</el-button>
<el-button @click="handleReset" icon="el-icon-refresh" size="mini">重置</el-button>
<el-button type="success" @click="handleAdd" icon="el-icon-plus" size="mini">补录</el-button>
</el-form-item>
</el-form>
<!-- 数据表格保持不变 -->
<el-table v-loading="tableLoading" :data="tableData" border style="width: 100%; margin-top: 20px;"
@row-click="handleRowClick" highlight-current-row>
<el-table-column prop="exitMatId" label="成品卷" width="150" align="center" />
<el-table-column prop="entryMatId" label="来料卷" width="150" align="center" />
<el-table-column prop="subId" label="分切数" width="100" align="center" />
<el-table-column prop="status" label="状态" width="100" align="center" />
<el-table-column prop="startPosition" label="开始位置" width="100" align="center" />
<el-table-column prop="endPosition" label="结束位置" width="100" align="center" />
<el-table-column prop="planId" label="计划ID" width="120" align="center" />
<el-table-column prop="planNo" label="计划号" width="150" align="center" />
<el-table-column prop="prodCode" label="产品类型" width="120" align="center" />
<el-table-column prop="groupNo" label="班号" width="100" align="center" />
<el-table-column prop="shiftNo" label="组号" width="100" align="center" />
<el-table-column prop="steelGrade" label="钢种" width="100" align="center" />
<el-table-column prop="entryThick" label="来料厚度" width="100" align="center" />
<el-table-column prop="entryWidth" label="来料宽带" width="100" align="center" />
<el-table-column prop="entryLength" label="来料长度" width="100" align="center" />
<el-table-column prop="entryWeight" label="来料重量" width="100" align="center" />
<el-table-column prop="weightTop" label="上表面镀锌" width="100" align="center" />
<el-table-column prop="weightBottom" label="下表面镀锌" width="100" align="center" />
<el-table-column prop="exitLength" label="成品长度" width="100" align="center" />
<el-table-column prop="exitNetWeight" label="成品带涂料重量" width="120" align="center" />
<el-table-column prop="theoryWeight" label="理论重量" width="100" align="center" />
<el-table-column prop="actualWeight" label="实际重量" width="100" align="center" />
<el-table-column prop="exitOuterDiameter" label="成品外径" width="100" align="center" />
<el-table-column prop="exitThickness" label="成品厚度" width="100" align="center" />
<el-table-column prop="exitWidth" label="成品宽度" width="100" align="center" />
<el-table-column prop="customer" label="客户" width="120" align="center" />
<el-table-column label="上线时间" prop="onlineTime" width="180" align="center">
<template slot-scope="scope">{{ parseTime(scope.row.onlineTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</template>
</el-table-column>
<el-table-column label="开始时间" prop="startTime" width="180" align="center">
<template slot-scope="scope">{{ parseTime(scope.row.startTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</template>
</el-table-column>
<el-table-column label="结束时间" prop="endTime" width="180" align="center">
<template slot-scope="scope">{{ parseTime(scope.row.endTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</template>
</el-table-column>
<el-table-column prop="unitCode" label="机组号" width="100" align="center" />
<el-table-column prop="processCode" label="工序号" width="100" align="center" />
<el-table-column label="是否尾卷" width="100" align="center">
<template slot-scope="scope">
<span>{{ scope.row.lastFlag ? '是' : '否' }}</span>
</template>
</el-table-column>
<el-table-column label="是否分卷" width="100" align="center">
<template slot-scope="scope">
<span>{{ scope.row.separateFlag ? '是' : '否' }}</span>
</template>
</el-table-column>
<el-table-column label="计划来源" align="center" prop="planOrigin" width="120">
<template slot-scope="scope">
<dict-tag :options="dict.type.pdo_plan_origin" :value="scope.row.planOrigin" />
</template>
</el-table-column>
<el-table-column label="操作" width="200" align="center" fixed="right" prop="action">
<template slot-scope="scope">
<el-button size="mini" type="primary" @click="handleEdit(scope.row)" icon="el-icon-edit">操作</el-button>
<el-button size="mini" type="danger" @click="handleDelete(scope.row)" :loading="scope.row.deleteLoading"
icon="el-icon-delete">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-row>
<el-row>
<el-col :span="3">
<pdo-summary :table-data="tableData" />
</el-col>
<el-col :span="21">
<line-chart :enCoilID="currentRow.exitMatId" :baseURL="baseURL" />
</el-col>
</el-row>
<!-- 编辑/新增弹窗根据isAdd条件渲染内容 -->
<el-dialog
:title="dialogTitle"
:visible.sync="dialogVisible"
width="60%"
:close-on-click-modal="false"
>
<!-- 补录新增仅显示数据修正组件无tab -->
<div v-if="isAdd">
<pdo-data-correction
:detail="formData"
:save-callback="handleSave"
:save-loading="saveLoading"
></pdo-data-correction>
</div>
<!-- 编辑保留原有tab显示数据修正+标签打印 -->
<el-tabs v-else v-model="activeTab">
<el-tab-pane label="数据修正" name="basicInfo">
<pdo-data-correction
:detail="formData"
:save-callback="handleSave"
:save-loading="saveLoading"
></pdo-data-correction>
</el-tab-pane>
<el-tab-pane label="标签打印" name="labelPrint">
<pdo-label-print
:detail="formData"
></pdo-label-print>
</el-tab-pane>
</el-tabs>
</el-dialog>
</div>
</template>
<script>
// import { getPdoList, addPdo, updatePdo, deletePdo } from '@/api/l2/pdo'
import createFetch from '@/api/l2/pdo'
import LineChart from './components/line.vue'
// 引入封装的两个组件
import PdoDataCorrection from './components/DataCorrection.vue'
import PdoLabelPrint from './components/LabelPrint.vue'
import PdoSummary from './components/PdoSummary.vue'
export default {
name: 'PdoManagement',
dicts: ['pdo_plan_origin'],
components: {
LineChart,
PdoDataCorrection, // 注册数据修正组件
PdoLabelPrint, // 注册标签打印组件
PdoSummary
},
props: {
baseURL: {
type: String,
default: ''
}
},
data() {
return {
fetchApi: {},
activeTab: 'basicInfo',
queryForm: { coilid: '', startDate: '', endDate: '' },
tableData: [],
tableLoading: false,
btnLoading: false,
dialogVisible: false,
dialogTitle: '新增实绩',
// 新增:区分「补录(新增)」和「编辑」的状态标识
isAdd: false,
// 传递给子组件的详情数据(父组件仅做数据中转,不直接修改)
formData: {},
saveLoading: false,
currentRow: {},
socketType: [
{ value: 'alarm', label: '报警' },
{ value: 'track_position', label: '跟踪位置' },
{ value: 'track_measure', label: '跟踪测量' },
{ value: 'track_signal', label: '跟踪信号' },
{ value: 'track_matmap', label: '跟踪料号' },
{ value: 'calc_setup_result', label: '计算结果' }
]
}
},
created() {
this.fetchApi = createFetch(this.baseURL)
this.getPdoList()
},
methods: {
// 获取列表数据(保持不变)
getPdoList() {
this.tableLoading = true
this.fetchApi.getPdoList(this.queryForm).then(res => {
this.tableData = res.data.map(item => ({ ...item, deleteLoading: false }))
}).catch(err => {
console.error(err)
this.$message.error('获取数据失败')
}).finally(() => {
this.tableLoading = false
this.btnLoading = false
})
},
// 查询(保持不变)
handleQuery() {
this.btnLoading = true;
this.getPdoList()
},
// 重置(保持不变)
handleReset() {
this.$refs.queryForm.resetFields();
this.getPdoList()
},
// 行点击(保持不变)
handleRowClick(row) {
this.currentRow = row;
},
// 补录新增设置isAdd=true初始化空表单
handleAdd() {
this.isAdd = true; // 标记为「补录」
this.dialogTitle = '新增实绩';
// 初始化空表单数据(传递给子组件)
this.formData = {
subId: 0,
startPosition: 0,
endPosition: 0,
planId: 0,
entryThick: 0,
entryWidth: 0,
entryLength: 0,
entryWeight: 0,
weightTop: 0,
weightBottom: 0,
exitLength: 0,
exitNetWeight: 0,
theoryWeight: 0,
actualWeight: 0,
exitOuterDiameter: 0,
exitThickness: 0,
exitWidth: 0,
lastFlag: false,
separateFlag: false
};
this.dialogVisible = true
},
// 编辑设置isAdd=false赋值行数据
handleEdit(row) {
this.isAdd = false; // 标记为「编辑」
this.dialogTitle = '编辑实绩';
// 深拷贝行数据(避免直接修改表格数据)
this.formData = JSON.parse(JSON.stringify(row));
this.dialogVisible = true
},
// 删除(保持不变)
handleDelete(row) {
this.$confirm(`确定删除成品卷 ${row.exitMatId}?`, '确认删除', { type: 'danger' }).then(() => {
row.deleteLoading = true
this.fetchApi.deletePdo(row.exitMatId, row.planId).then(res => {
if (res.code === 200) {
this.$message.success('删除成功');
this.getPdoList()
} else this.$message.error(res.msg || '删除失败')
}).finally(() => row.deleteLoading = false)
})
},
// 保存回调子组件触发处理实际API调用
handleSave(formData) {
this.saveLoading = true
// 根据是否有id判断是新增还是编辑
const request = formData.id ? this.fetchApi.updatePdo(formData) : this.fetchApi.addPdo(formData)
request.then(res => {
if (res.code === 200) {
this.$message.success('保存成功');
this.dialogVisible = false;
this.getPdoList() // 刷新列表
} else this.$message.error(res.msg || '保存失败')
}).finally(() => {
this.saveLoading = false
})
},
}
}
</script>

View File

@@ -0,0 +1,351 @@
<template>
<div>
<!-- 查询表单 -->
<el-form :inline="true" :model="queryForm" ref="queryForm" label-width="80px">
<el-form-item label="开始时间" prop="startDate">
<el-date-picker v-model="queryForm.startDate" type="date" placeholder="选择开始时间" value-format="yyyy-MM-dd"
:clearable="true"></el-date-picker>
</el-form-item>
<el-form-item label="结束时间" prop="endDate">
<el-date-picker v-model="queryForm.endDate" type="date" placeholder="选择结束时间" value-format="yyyy-MM-dd"
:clearable="true"></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery" :loading="btnLoading">
<i class="el-icon-search"></i> 查询
</el-button>
<el-button @click="handleReset">
<i class="el-icon-refresh"></i> 重置
</el-button>
<!-- <el-button type="success" @click="handleAdd">
<i class="el-icon-plus"></i> 新增
</el-button> -->
</el-form-item>
</el-form>
<!-- 数据表格 -->
<el-table v-loading="tableLoading" :data="tableData" border style="width: 100%; margin-top: 20px;"
@row-click="handleRowClick" highlight-current-row>
<el-table-column prop="coilid" label="钢卷号" width="120" align="center"></el-table-column>
<el-table-column prop="stopType" label="停机类型" width="120" align="center"></el-table-column>
<el-table-column prop="remark" label="停机原因" min-width="200"></el-table-column>
<el-table-column prop="shift" label="班" width="80" align="center"></el-table-column>
<el-table-column prop="crew" label="组" width="80" align="center"></el-table-column>
<el-table-column prop="area" label="区域" width="100" align="center"></el-table-column>
<el-table-column prop="seton" label="设备" width="100" align="center"></el-table-column>
<el-table-column prop="startDate" label="开始时间" width="180" align="center"></el-table-column>
<el-table-column prop="endDate" label="结束时间" width="180" align="center"></el-table-column>
<el-table-column prop="duration" label="停机时长(分)" width="120" align="center"></el-table-column>
<el-table-column prop="insdate" label="更新时间" width="180" align="center"></el-table-column>
<el-table-column label="操作" width="200" align="center" fixed="right">
<template slot-scope="scope">
<el-button size="mini" type="primary" @click="handleEdit(scope.row)" icon="el-icon-edit">
编辑
</el-button>
<el-button size="mini" type="danger" @click="handleDelete(scope.row)" icon="el-icon-delete"
:loading="scope.row.deleteLoading">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 编辑/新增弹窗 -->
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="50%" :close-on-click-modal="false">
<el-form :model="formData" ref="formData" label-width="80px" :rules="formRules">
<el-form-item label="停机ID" prop="stopid" v-if="formData.stopid">
<el-input v-model="formData.stopid" disabled></el-input>
</el-form-item>
<el-form-item label="钢卷号" prop="coilid">
<el-input v-model="formData.coilid" placeholder="请输入钢卷号"></el-input>
</el-form-item>
<el-form-item label="班" prop="shift">
<el-select v-model="formData.shift" placeholder="请选择班次" clearable>
<el-option label="早班" value="早班"></el-option>
<el-option label="中班" value="中班"></el-option>
<el-option label="晚班" value="晚班"></el-option>
</el-select>
</el-form-item>
<el-form-item label="组" prop="crew">
<el-input v-model="formData.crew" placeholder="请输入组号"></el-input>
</el-form-item>
<el-form-item label="区域" prop="area">
<el-input v-model="formData.area" placeholder="请输入区域"></el-input>
</el-form-item>
<el-form-item label="机组" prop="unit">
<el-input v-model="formData.unit" placeholder="请输入机组"></el-input>
</el-form-item>
<el-form-item label="设备" prop="seton">
<el-input v-model="formData.seton" placeholder="请输入设备"></el-input>
</el-form-item>
<el-form-item label="开始时间" prop="startDate">
<el-date-picker v-model="formData.startDate" type="datetime" placeholder="选择开始时间"
value-format="yyyy-MM-dd HH:mm:ss"></el-date-picker>
</el-form-item>
<el-form-item label="结束时间" prop="endDate">
<el-date-picker v-model="formData.endDate" type="datetime" placeholder="选择结束时间"
value-format="yyyy-MM-dd HH:mm:ss"></el-date-picker>
</el-form-item>
<el-form-item label="停机类型" prop="stopType">
<el-select v-model="formData.stopType" placeholder="请选择停机类型" clearable>
<el-option label="计划停机" value="计划停机"></el-option>
<el-option label="故障停机" value="故障停机"></el-option>
<el-option label="维护停机" value="维护停机"></el-option>
<el-option label="其他" value="其他"></el-option>
</el-select>
</el-form-item>
<el-form-item label="停机原因" prop="remark">
<el-input v-model="formData.remark" placeholder="请输入停机原因" type="textarea" rows="4"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSave" :loading="saveLoading">保存</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import createFetch from '@/api/l2/stop'
export default {
name: 'StoppageManagement',
props: {
baseURL: {
type: String,
default: ''
}
},
data() {
return {
fetchApi: {},
// 查询表单数据
queryForm: {
// 只保留年月日YYYY-mm-dd
startDate: '2023-08-13',
endDate: '2025-08-20'
},
// 表格数据
tableData: [],
// 加载状态
tableLoading: false,
btnLoading: false,
// 弹窗状态
dialogVisible: false,
dialogTitle: '新增停机记录',
// 表单数据
formData: {
stopid: '',
coilid: '',
shift: '',
crew: '',
area: '',
unit: '',
seton: '',
startDate: '',
endDate: '',
duration: 0,
insdate: '',
stopType: '',
remark: ''
},
// 表单验证规则
formRules: {
startDate: [
{ required: true, message: '请选择开始时间', trigger: 'blur' }
],
endDate: [
{ required: true, message: '请选择结束时间', trigger: 'blur' }
],
seton: [
{ required: true, message: '请输入设备', trigger: 'blur' }
]
},
// 保存按钮加载状态
saveLoading: false,
// 当前操作的行数据
currentRow: {}
};
},
created() {
// 页面加载时默认查询一次
this.fetchApi = createFetch(this.baseURL)
this.getStoppageList();
},
methods: {
// 获取停机记录列表
getStoppageList() {
this.tableLoading = true;
this.fetchApi.listStoppage(this.queryForm)
.then(response => {
this.tableLoading = false;
this.btnLoading = false;
this.tableData = response.data.map(item => ({
...item,
deleteLoading: false
}));
})
.catch(error => {
this.tableLoading = false;
this.btnLoading = false;
console.error('获取数据失败:', error);
this.$message.error('获取数据失败,请稍后重试');
});
},
// 处理查询
handleQuery() {
// 验证开始时间不能晚于结束时间
if (this.queryForm.startDate && this.queryForm.endDate &&
new Date(this.queryForm.startDate) > new Date(this.queryForm.endDate)) {
this.$message.warning('开始时间不能晚于结束时间');
return;
}
this.btnLoading = true;
this.getStoppageList();
},
// 处理重置
handleReset() {
this.$refs.queryForm.resetFields();
this.getStoppageList();
},
// 行点击事件
handleRowClick(row) {
this.currentRow = row;
},
// 新增
handleAdd() {
this.dialogTitle = '新增停机记录';
this.formData = {
stopid: '',
coilid: '',
shift: '',
crew: '',
area: '',
unit: '',
seton: '',
startDate: '',
endDate: '',
duration: 0,
insdate: '',
stopType: '',
remark: ''
};
this.dialogVisible = true;
},
// 编辑
handleEdit(row) {
this.dialogTitle = '编辑停机记录';
this.formData = { ...row };
this.dialogVisible = true;
},
// 删除
handleDelete(row) {
this.$confirm(`确定要删除ID为 ${row.stopid} 的停机记录吗?`, '确认删除', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'danger'
}).then(() => {
// 设置当前行的删除按钮为加载状态
row.deleteLoading = true;
this.fetchApi.deleteStoppage(row.stopid)
.then(response => {
row.deleteLoading = false;
if (response.code === 200) {
this.$message.success('删除成功');
// 重新查询数据
this.getStoppageList();
} else {
this.$message.error(response.msg || '删除失败');
}
})
.catch(error => {
row.deleteLoading = false;
console.error('删除失败:', error);
this.$message.error('删除失败,请稍后重试');
});
}).catch(() => {
// 用户取消删除
this.$message.info('已取消删除');
});
},
// 保存
handleSave() {
this.$refs.formData.validate(valid => {
if (valid) {
// 验证开始时间不能晚于结束时间
if (new Date(this.formData.startDate) > new Date(this.formData.endDate)) {
this.$message.warning('开始时间不能晚于结束时间');
return;
}
// 计算停机时长(分钟)
const start = new Date(this.formData.startDate).getTime();
const end = new Date(this.formData.endDate).getTime();
this.formData.duration = Math.round((end - start) / (1000 * 60) * 10) / 10;
// 设置当前时间为更新时间
const now = new Date();
this.formData.insdate = now.toISOString().slice(0, 19).replace('T', ' ');
this.saveLoading = true;
// 调用更新接口
this.fetchApi.updateStoppage(this.formData)
.then(response => {
this.saveLoading = false;
if (response.code === 200) {
this.$message.success('保存成功');
this.dialogVisible = false;
// 重新查询数据
this.getStoppageList();
} else {
this.$message.error(response.msg || '保存失败');
}
})
.catch(error => {
this.saveLoading = false;
console.error('保存失败:', error);
this.$message.error('保存失败,请稍后重试');
});
}
});
}
}
};
</script>
<style scoped>
.no-data {
margin-top: 50px;
text-align: center;
}
::v-deep .el-form-item {
margin-bottom: 15px;
}
</style>

View File

@@ -0,0 +1,469 @@
<template>
<div class="graph-container-box">
<el-row>
<el-col :span="16">
<knova-stage @rectClick="selectCard" :matMapList="matMapList" :rects="rects" :lines="lines"></knova-stage>
</el-col>
<el-col :span="8">
<div style="border: 1px solid #000; padding: 10px; border-radius: 10px; margin-bottom: 10px;">
<!-- 调整工具选择两个位置两个下拉选分别双向绑定 -->
<el-form :model="adjustForm" ref="adjustForm" label-width="80px">
<el-form-item label="当前位置" prop="current">
<el-select v-model="adjustForm.current" placeholder="请选择当前位置">
<el-option v-for="item in matMapList" :key="item.positionNameEn" :label="item.positionNameCn" :value="item.positionNameEn"></el-option>
</el-select>
</el-form-item>
<el-form-item label="目标位置" prop="target">
<el-select v-model="adjustForm.target" placeholder="请选择目标位置">
<el-option v-for="item in matMapList" :key="item.positionNameEn" :label="item.positionNameCn" :value="item.positionNameEn"></el-option>
</el-select>
</el-form-item>
</el-form>
<el-button type="primary" :disabled="!adjustForm.current || !adjustForm.target" @click="handleConfirmAdjust">确认调整</el-button>
</div>
<div style="border: 1px solid #000; padding: 10px; border-radius: 10px; margin-bottom: 10px;">
<el-row v-if="selectedCard">
<el-col :span="12">
<div class="detail-item">
<span class="detail-label">位置名称</span>
<span class="detail-value">{{ selectedCard.positionNameCn || '-' }}</span>
</div>
<div class="detail-item">
<span class="detail-label">位置代号</span>
<span class="detail-value">{{ selectedCard.positionNameEn || '-' }}</span>
</div>
<div class="detail-item">
<span class="detail-label">钢卷号</span>
<span class="detail-value">{{ selectedCard.matId || '-' }}</span>
</div>
<div class="detail-item">
<span class="detail-label">计划ID</span>
<span class="detail-value">{{ selectedCard.planId || '-' }}</span>
</div>
<div class="detail-item">
<span class="detail-label">计划号</span>
<span class="detail-value">{{ selectedCard.planNo || '-' }}</span>
</div>
<div class="detail-item">
<span class="detail-label">开卷机编号</span>
<span class="detail-value">{{ selectedCard.porIdx || '-' }}</span>
</div>
<div class="detail-item">
<span class="detail-label">卷取机编号</span>
<span class="detail-value">{{ selectedCard.trIdx || '-' }}</span>
</div>
</el-col>
<el-col :span="12">
<!-- 加载状态 -->
<div class="empty-tip" v-if="isLoadingReturn">加载回退信息中...</div>
<!-- 错误状态 -->
<div class="empty-tip" v-else-if="returnError" style="color: #f56c6c;">
{{ returnError }}
</div>
<!-- 回退信息内容 -->
<div class="detail-list" v-else-if="Object.keys(returnInfo).length > 0">
<div class="detail-item">
<span class="detail-label">回退钢卷号</span>
<span class="detail-value">{{ returnInfo.entryMatId || '-' }}</span>
</div>
<div class="detail-item">
<span class="detail-label">回退计划ID</span>
<span class="detail-value">{{ returnInfo.planId || '-' }}</span>
</div>
<div class="detail-item">
<span class="detail-label">回退计划号</span>
<span class="detail-value">{{ returnInfo.planNo || '-' }}</span>
</div>
<div class="detail-item">
<span class="detail-label">回退类型</span>
<span class="detail-value">{{ returnInfo.returnType || '-' }}</span>
</div>
<div class="detail-item">
<span class="detail-label">回退重量</span>
<span class="detail-value">
{{ returnInfo.returnWeight || '-' }}
{{ returnInfo.returnWeight ? 'kg' : '' }}
</span>
</div>
</div>
<!-- 无回退信息 -->
<div class="empty-tip" v-else>无回退信息</div>
</el-col>
</el-row>
<el-row v-else>
<div class="empty-tip">请选择钢卷卡片查看详情</div>
</el-row>
</div>
<div style="border: 1px solid #000; padding: 10px; border-radius: 10px; margin-bottom: 10px;">
<el-row v-if="selectedCard">
<div class="operation-panel">
<div class="panel-content">
<!-- 非调整模式显示操作按钮 -->
<div class="operation-buttons">
<div class="button-group">
<el-button size="mini" type="primary" @click="handleOperate(selectedCard, 'ONLINE')"
class="btn-block">
钢卷上线
</el-button>
<el-button size="mini" type="warning" @click="handleOperate(selectedCard, 'UNLOAD')"
class="btn-block mt-2">
手动卸卷
</el-button>
<el-button size="mini" type="danger" @click="handleOperate(selectedCard, 'ALL_RETURN')"
class="btn-block mt-2">
整卷回退
</el-button>
<el-button size="mini" type="danger" @click="handleOperate(selectedCard, 'HALF_RETURN')"
class="btn-block mt-2">
半卷回退
</el-button>
<el-button size="mini" type="info" @click="handleOperate(selectedCard, 'BLOCK')"
class="btn-block mt-2">
卸卷并封闭
</el-button>
<!-- <el-button size="mini" type="info" @click="handleOperate(selectedCard, 'THROW_TAIL')" class="btn-block mt-2">
甩尾
</el-button> -->
</div>
</div>
</div>
</div>
</el-row>
<el-row v-else>
<div class="empty-tip">请选择钢卷卡片进行操作</div>
</el-row>
</div>
</el-col>
</el-row>
<el-dialog :visible.sync="operateMatStatus" :title="getOperateTitle" width="50%">
<el-form :model="operateMatForm" :rules="operateRules" ref="operateForm" label-width="120px">
<el-form-item label="开卷机编号" prop="porIdx">
<el-input v-model="operateMatForm.porIdx"></el-input>
</el-form-item>
<el-form-item label="卷取机编号" prop="trIdx">
<el-input v-model="operateMatForm.trIdx"></el-input>
</el-form-item>
<el-form-item label="计划id" prop="planId">
<el-input v-model="operateMatForm.planId" placeholder="请输入计划ID"></el-input>
</el-form-item>
<el-form-item label="钢卷号" prop="entryMatId">
<el-input v-model="operateMatForm.entryMatId" placeholder="请输入钢卷号"></el-input>
</el-form-item>
<!-- <el-form-item label="计划号" prop="planNo">
<el-input v-model="operateMatForm.planNo" placeholder="请输入计划号"></el-input>
</el-form-item> -->
<el-form-item label="操作类型" prop="operation">
<el-select v-model="operateMatForm.operation" disabled>
<el-option label="钢卷上线" value="ONLINE"></el-option>
<el-option label="手动卸卷" value="UNLOAD"></el-option>
<el-option label="整卷回退" value="ALL_RETURN"></el-option>
<el-option label="半卷回退" value="HALF_RETURN"></el-option>
<el-option label="卸卷并封闭" value="BLOCK"></el-option>
<!-- <el-option label="甩尾" value="THROW_TAIL"></el-option> -->
</el-select>
</el-form-item>
<!-- 回退相关字段 -->
<template v-if="['ALL_RETURN', 'HALF_RETURN'].includes(operateMatForm.operation)">
<el-form-item label="回退卷号" prop="returnMatId">
<el-input v-model="operateMatForm.returnMatId" placeholder="请输入回退卷号"></el-input>
</el-form-item>
<el-form-item label="回退重量" prop="returnWeight">
<el-input v-model="operateMatForm.returnWeight" placeholder="请输入回退重量"></el-input>
</el-form-item>
<el-form-item label="回退备注" prop="returnRemark">
<el-input v-model="operateMatForm.returnRemark" rows="3"></el-input>
</el-form-item>
</template>
<!-- 产出长度字段 -->
<template v-if="['PRODUCING', 'PRODUCT'].includes(operateMatForm.operation)">
<el-form-item label="产出钢卷长度" prop="coilLength">
<el-input v-model="operateMatForm.coilLength" type="number" placeholder="请输入产出钢卷长度"></el-input>
</el-form-item>
</template>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="operateMatStatus = false">取消</el-button>
<el-button type="primary" @click="submitOperateForm">确定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import createFetch from '@/api/l2/track'
import KnovaStage from '../../components/knovaStage.vue'
import { rects, lines } from './rects'
export default {
components: {
KnovaStage
},
props: {
baseURL: {
type: String,
default: ''
}
},
data() {
return {
fetchApi: undefined,
rects,
lines,
matMapList: [],
selectedCard: null, // 非调整模式选中的单个卡片
adjustForm: {
current: null,
target: null
}, // 调整模式选中的位置:[当前, 目标],双向绑定
adjustMode: false, // 是否为调整模式
deviceMap: {},
operateMatForm: {
porIdx: null,
trIdx: null,
planId: '',
entryMatId: '',
// planNo: '',
operation: '',
returnMatId: '',
returnWeight: null,
returnRemark: '',
coilLength: null
},
operateRules: {
planId: [{ required: true, message: '请输入计划id', trigger: 'blur' }],
entryMatId: [{ required: true, message: '请输入钢卷号', trigger: 'blur' }],
operation: [{ required: true, message: '请选择操作类型', trigger: 'change' }],
returnMatId: [
{
required: true,
message: '请输入回退卷号',
trigger: 'blur',
validator: (rule, val, cb) => {
if (['ALL_RETURN', 'HALF_RETURN'].includes(this.operateMatForm.operation) && !val) {
cb(new Error('请输入回退卷号'))
} else cb()
}
}
],
returnWeight: [
{
required: true,
message: '请输入回退重量',
trigger: 'blur',
validator: (rule, val, cb) => {
if (['ALL_RETURN', 'HALF_RETURN'].includes(this.operateMatForm.operation) && (val === null || val === '')) {
cb(new Error('请输入回退重量'))
} else cb()
}
}
]
},
operateMatStatus: false, // 操作对话框显示状态
returnInfo: {}, // 存储回退接口返回的数据
isLoadingReturn: false, // 回退信息加载状态
returnError: '' // 回退信息获取失败的提示
}
},
computed: {
// 操作对话框标题
getOperateTitle() {
const titleMap = {
'ONLINE': '钢卷上线',
'UNLOAD': '手动卸卷',
'ALL_RETURN': '整卷回退',
'HALF_RETURN': '半卷回退',
'BLOCK': '卸卷并封闭',
'THROW_TAIL': '甩尾'
}
return titleMap[this.operateMatForm.operation] || '钢卷操作'
}
},
methods: {
// 获取钢卷数据
fetchData() {
this.fetchApi.getTrackMatPosition().then(res => {
this.matMapList = res.data.matMapList || []
// this.deviceMap = res.data.matMapList || {}
}).catch(err => {
console.error('获取钢卷数据失败:', err)
this.$message.error('获取数据失败,请重试')
})
},
/**
* 获取回退信息
* @param {Number} posIdx - 位置索引接口必填query参数
*/
fetchReturnData(posIdx) {
// 1. 校验posIdx
if (!posIdx && posIdx !== 0) {
this.returnInfo = {};
this.returnError = '缺少位置索引posIdx无法获取回退信息';
return;
}
// 2. 加载状态初始化
this.isLoadingReturn = true;
this.returnError = '';
// 3. 调用回退接口posIdx作为query参数传递
this.fetchApi.getBackData({ posIdx })
.then(res => {
this.isLoadingReturn = false;
// 接口成功且有数据
if (res.code === 200 && res.data) {
this.returnInfo = res.data;
} else {
this.returnInfo = {};
this.returnError = res.msg || '获取回退信息失败';
}
})
.catch(err => {
this.isLoadingReturn = false;
this.returnInfo = {};
this.returnError = '获取回退信息出错,请重试';
console.error('回退信息接口异常:', err);
});
},
// 选择卡片(区分调整/非调整模式)
selectCard(item) {
this.selectedCard = this.selectedCard === item ? null : item
// 选中卡片时查询回退信息,取消选中时清空
if (this.selectedCard) {
this.fetchReturnData(this.selectedCard.posIdx);
this.adjustForm.current = this.selectedCard.positionNameEn
} else {
this.returnInfo = {};
this.returnError = '';
this.adjustForm.current = null
}
},
// 确认调整位置
handleConfirmAdjust() {
const { current, target } = this.adjustForm
if (!current || !target) {
this.$message.warning('请选择当前位置和目标位置')
return
}
const params = {
currentPos: current,
targetPos: target,
}
this.$confirm(`确定将 ${current} 的钢卷调整到 ${target}`, '确认调整', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.fetchApi.adjustPosition(params).then(() => {
this.$message.success('调整成功')
this.exitAdjustMode()
this.fetchData()
}).catch(err => {
console.error('调整失败:', err)
this.$message.error('调整失败,请重试')
})
}).catch(() => {
this.$message.info('已取消调整')
})
},
// 打开操作对话框
handleOperate(row, operation) {
this.$refs.operateForm?.resetFields()
this.operateMatForm = {
porIdx: row.posIdx || null,
trIdx: row.posIdx || null,
planId: row.planId || '',
entryMatId: row.matId || '',
planNo: row.planNo || '',
operation: operation,
returnMatId: '',
returnWeight: null,
returnRemark: '',
coilLength: null
}
this.operateMatStatus = true
},
// 提交操作表单
submitOperateForm() {
this.$refs.operateForm.validate(valid => {
if (valid) {
this.fetchApi.operateMat(this.operateMatForm).then(() => {
this.$message.success('操作成功')
this.operateMatStatus = false
this.fetchData()
}).catch(err => {
console.error('操作失败:', err)
this.$message.error('操作失败,请重试')
})
} else {
this.$message.warning('请完善表单信息')
}
})
}
},
mounted() {
this.fetchApi = createFetch(this.baseURL)
this.fetchData()
}
}
</script>
<style scoped lang="scss">
.graph-container-box {
width: 100%;
box-sizing: border-box;
height: calc(100vh - 86px);
padding: 20px;
// background-color: #c0c0c0;
overflow: hidden;
}
.graph-container {
width: 100%;
height: 100%;
position: relative;
img {
width: 1881px;
height: 608px;
}
// 图形元素基础样式
.graph-list>div {
position: absolute;
}
// 文字容器样式
.text-wrapper {
position: relative;
height: 100%;
}
// 文字基础样式(可被配置项覆盖)
.text-wrapper span {
position: absolute;
color: #333;
/* 默认文字颜色 */
font-size: 14px;
/* 默认文字大小 */
}
}
</style>

View File

@@ -0,0 +1,356 @@
export const rects = [
// 左侧:开卷机
{
id: 'POR1',
config: {
x: 40,
y: 110,
width: 200,
height: 50,
fill: '#d3d3d3',
stroke: 'black',
strokeWidth: 1,
cursor: 'pointer'
},
textConfig: { text: '1#开卷机[POR1]' }
},
{
id: 'POR2',
config: {
x: 40,
y: 220,
width: 200,
height: 50,
fill: '#d3d3d3',
stroke: 'black',
strokeWidth: 1,
cursor: 'pointer'
},
textConfig: { text: '2#开卷机[POR2]' }
},
// 中上部:焊机、入口活套
{
id: 'WELDER',
config: {
x: 300,
y: 30,
width: 220,
height: 50,
fill: '#d3d3d3',
stroke: 'black',
strokeWidth: 1,
cursor: 'pointer'
},
textConfig: { text: '焊机[WELDER]' }
},
{
id: 'ENL1',
config: {
x: 300,
y: 110,
width: 220,
height: 50,
fill: '#d3d3d3',
stroke: 'black',
strokeWidth: 1,
cursor: 'pointer'
},
textConfig: { text: '入口活套1[ENL1]' }
},
{
id: 'ENL2',
config: {
x: 300,
y: 160,
width: 220,
height: 50,
fill: '#d3d3d3',
stroke: 'black',
strokeWidth: 1,
cursor: 'pointer'
},
textConfig: { text: '入口活套2[ENL2]' }
},
// 中下部:清洗段
{
id: 'CLEAN',
config: {
x: 300,
y: 240,
width: 220,
height: 50,
fill: '#d3d3d3',
stroke: 'black',
strokeWidth: 1,
cursor: 'pointer'
},
textConfig: { text: '清洗段[CLEAN]' }
},
// 右侧上退火炉1-4
{
id: 'FUR1',
config: {
x: 600,
y: 70,
width: 220,
height: 50,
fill: '#d3d3d3',
stroke: 'black',
strokeWidth: 1,
cursor: 'pointer'
},
textConfig: { text: '退火炉[FUR1]' }
},
{
id: 'FUR2',
config: {
x: 600,
y: 120,
width: 220,
height: 50,
fill: '#d3d3d3',
stroke: 'black',
strokeWidth: 1,
cursor: 'pointer'
},
textConfig: { text: '退火炉[FUR2]' }
},
{
id: 'FUR3',
config: {
x: 600,
y: 170,
width: 220,
height: 50,
fill: '#d3d3d3',
stroke: 'black',
strokeWidth: 1,
cursor: 'pointer'
},
textConfig: { text: '退火炉[FUR3]' }
},
{
id: 'FUR4',
config: {
x: 600,
y: 220,
width: 220,
height: 50,
fill: '#d3d3d3',
stroke: 'black',
strokeWidth: 1,
cursor: 'pointer'
},
textConfig: { text: '退火炉[FUR4]' }
},
// 右侧中:光整机
{
id: 'TM',
config: {
x: 600,
y: 400,
width: 220,
height: 50,
fill: '#d3d3d3',
stroke: 'black',
strokeWidth: 1,
cursor: 'pointer'
},
textConfig: { text: '光整机[TM]' }
},
// 右侧下:拉矫机
{
id: 'TL',
config: {
x: 600,
y: 480,
width: 220,
height: 50,
fill: '#d3d3d3',
stroke: 'black',
strokeWidth: 1,
cursor: 'pointer'
},
textConfig: { text: '拉矫机[TL]' }
},
// 中下:后处理
{
id: 'COAT',
config: {
x: 300,
y: 360,
width: 220,
height: 50,
fill: '#d3d3d3',
stroke: 'black',
strokeWidth: 1,
cursor: 'pointer'
},
textConfig: { text: '后处理[COAT]' }
},
// 中下:出口活套
{
id: 'CXL1',
config: {
x: 300,
y: 440,
width: 220,
height: 50,
fill: '#d3d3d3',
stroke: 'black',
strokeWidth: 1,
cursor: 'pointer'
},
textConfig: { text: '出口活套[CXL1]' }
},
{
id: 'CXL2',
config: {
x: 300,
y: 490,
width: 220,
height: 50,
fill: '#d3d3d3',
stroke: 'black',
strokeWidth: 1,
cursor: 'pointer'
},
textConfig: { text: '出口活套[CXL2]' }
},
// 左下:卷取机、称重位
{
id: 'TR',
config: {
x: 40,
y: 380,
width: 200,
height: 50,
fill: '#d3d3d3',
stroke: 'black',
strokeWidth: 1,
cursor: 'pointer'
},
textConfig: { text: '卷取机[TR]' }
},
{
id: 'WEIT',
config: {
x: 40,
y: 460,
width: 200,
height: 50,
fill: '#d3d3d3',
stroke: 'black',
strokeWidth: 1,
cursor: 'pointer'
},
textConfig: { text: '称重位[WEIT]' }
}
]
export const lines = [
// 1#开卷机 → 焊机
{
id: 'line-por1-welder',
config: {
points: [
40 + 200, 110 + 25,
40 + 200 + 30, 110 + 25,
40 + 200 + 30, 30 + 25,
300, 30 + 25
],
stroke: '#686868',
strokeWidth: 2,
lineCap: 'round',
lineJoin: 'round'
}
},
// 2#开卷机 → 焊机
{
id: 'line-por2-welder',
config: {
points: [
40 + 200, 220 + 25,
40 + 200 + 30, 220 + 25,
40 + 200 + 30, 30 + 25,
300, 30 + 25
],
stroke: '#686868',
strokeWidth: 2,
lineCap: 'round',
lineJoin: 'round'
}
},
// 清洗段 → 退火炉1
{
id: 'line-clean-fur1',
config: {
points: [
300 + 220, 240 + 25,
300 + 220 + 40, 240 + 25,
300 + 220 + 40, 70 + 25,
600, 70 + 25
],
stroke: '#686868',
strokeWidth: 2,
lineCap: 'round',
lineJoin: 'round'
}
},
// 退火炉4 → 光整机
{
id: 'line-fur4-tm',
config: {
points: [
600 + 220, 220 + 25,
600 + 220 + 40, 220 + 25,
600 + 220 + 40, 400 + 25,
600 + 220, 400 + 25
],
stroke: '#686868',
strokeWidth: 2,
lineCap: 'round',
lineJoin: 'round'
}
},
// 拉矫机 → 后处理
{
id: 'line-tl-coat',
config: {
points: [
600, 480 + 25,
600 - 40, 480 + 25,
600 - 40, 360 + 25,
600 - 80, 360 + 25
],
stroke: '#686868',
strokeWidth: 2,
lineCap: 'round',
lineJoin: 'round'
}
},
// 出口活套2 → 卷取机
{
id: 'line-cxl2-tr',
config: {
points: [
300, 490 + 25,
300 - 30, 490 + 25,
300 - 30, 380 + 25,
300 - 60, 380 + 25
],
stroke: '#686868',
strokeWidth: 2,
lineCap: 'round',
lineJoin: 'round'
}
}
]

View File

@@ -202,7 +202,7 @@
import createFetch from '@/api/l2/track' import createFetch from '@/api/l2/track'
import { getConfigKey } from '@/api/system/config' import { getConfigKey } from '@/api/system/config'
import KnovaStage from './components/knovaStage.vue' import KnovaStage from './components/knovaStage.vue'
import { rects, lines } from './rects' import { rects, lines } from './panels/track/rects'
export default { export default {
components: { components: {