feat(pdo): 重构PDO模块结构并优化功能
将PDO模块拆分为面板组件和页面组件,实现代码复用 新增数据修正、标签打印和统计汇总组件 优化图表显示和数据处理逻辑
This commit is contained in:
591
klp-ui/src/views/lines/panels/pdo/index.vue
Normal file
591
klp-ui/src/views/lines/panels/pdo/index.vue
Normal file
@@ -0,0 +1,591 @@
|
||||
<template>
|
||||
<div class="pdo-container">
|
||||
<!-- 查询表单区域 -->
|
||||
<div class="pdo-header">
|
||||
<el-form :inline="true" :model="queryForm" ref="queryForm" label-width="100px" size="small">
|
||||
<el-form-item label="钢卷号" prop="coilid">
|
||||
<el-input v-model="queryForm.coilid" placeholder="请输入钢卷号"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="开始日期" prop="startDate">
|
||||
<el-date-picker 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 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">查询</el-button>
|
||||
<el-button @click="handleReset" icon="el-icon-refresh">重置</el-button>
|
||||
<el-button type="success" @click="handleAdd" icon="el-icon-plus">补录</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- 卡片网格布局 -->
|
||||
<div v-loading="tableLoading" class="card-grid-container">
|
||||
<el-row :gutter="10">
|
||||
<el-col v-for="(item, index) in tableData" :key="index" :xs="24" :sm="12" :md="8" :lg="6" :xl="5"
|
||||
class="card-col">
|
||||
<el-card class="parameter-card" shadow="never" :body-style="{ padding: '8px' }"
|
||||
:class="{ 'card-selected': currentRow.exitMatId === item.exitMatId }" @click.native="handleRowClick(item)">
|
||||
<div slot="header" class="card-header">
|
||||
<div class="card-title">成品卷: {{ item.exitMatId || '-' }}</div>
|
||||
<div class="card-subtitle">{{ item.entryMatId || '-' }} | {{ item.planNo || '-' }}</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="param-groups-row">
|
||||
<!-- 基本信息 -->
|
||||
<div class="param-group">
|
||||
<div class="group-title">基本信息</div>
|
||||
<div class="param-list">
|
||||
<div class="param-line">
|
||||
<span class="param-label">状态:</span>
|
||||
<span class="param-value">{{ item.status || '-' }}</span>
|
||||
</div>
|
||||
<div class="param-line">
|
||||
<span class="param-label">钢种:</span>
|
||||
<span class="param-value">{{ item.steelGrade || '-' }}</span>
|
||||
</div>
|
||||
<div class="param-line">
|
||||
<span class="param-label">产品类型:</span>
|
||||
<span class="param-value">{{ item.prodCode || '-' }}</span>
|
||||
</div>
|
||||
<div class="param-line">
|
||||
<span class="param-label">客户:</span>
|
||||
<span class="param-value">{{ item.customer || '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 来料信息 -->
|
||||
<div class="param-group">
|
||||
<div class="group-title">来料信息</div>
|
||||
<div class="param-list">
|
||||
<div class="param-line">
|
||||
<span class="param-label">厚度:</span>
|
||||
<span class="param-value">{{ item.entryThick || '-' }}</span>
|
||||
</div>
|
||||
<div class="param-line">
|
||||
<span class="param-label">宽度:</span>
|
||||
<span class="param-value">{{ item.entryWidth || '-' }}</span>
|
||||
</div>
|
||||
<div class="param-line">
|
||||
<span class="param-label">长度:</span>
|
||||
<span class="param-value">{{ item.entryLength || '-' }}</span>
|
||||
</div>
|
||||
<div class="param-line">
|
||||
<span class="param-label">重量:</span>
|
||||
<span class="param-value">{{ item.entryWeight || '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 成品信息 -->
|
||||
<div class="param-group">
|
||||
<div class="group-title">成品信息</div>
|
||||
<div class="param-list">
|
||||
<div class="param-line">
|
||||
<span class="param-label">厚度:</span>
|
||||
<span class="param-value">{{ item.exitThickness || '-' }}</span>
|
||||
</div>
|
||||
<div class="param-line">
|
||||
<span class="param-label">宽度:</span>
|
||||
<span class="param-value">{{ item.exitWidth || '-' }}</span>
|
||||
</div>
|
||||
<div class="param-line">
|
||||
<span class="param-label">长度:</span>
|
||||
<span class="param-value">{{ item.exitLength || '-' }}</span>
|
||||
</div>
|
||||
<div class="param-line">
|
||||
<span class="param-label">重量:</span>
|
||||
<span class="param-value">{{ item.exitNetWeight || '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<el-button size="mini" type="text" icon="el-icon-view" @click.stop="handlePrint(item)">打印</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click.stop="handleEdit(item)">操作</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" :loading="item.deleteLoading"
|
||||
@click.stop="handleDelete(item)">删除</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div v-if="tableData.length === 0 && !tableLoading" class="empty-data">
|
||||
<el-empty description="暂无数据"></el-empty>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 统计汇总和图表区域 -->
|
||||
<div class="statistics-container">
|
||||
<div class="statistics-header">
|
||||
<div class="selected-info" v-if="currentRow && currentRow.entryMatId">
|
||||
<i class="el-icon-check"></i>
|
||||
<span class="selected-label">已选中:</span>
|
||||
<span class="selected-value">成品卷 {{ currentRow.exitMatId }}</span>
|
||||
<span class="selected-detail" v-if="currentRow.entryMatId">(来料卷: {{ currentRow.entryMatId }})</span>
|
||||
</div>
|
||||
<div class="selected-info empty" v-else>
|
||||
<i class="el-icon-info"></i>
|
||||
<span>请选择上方卡片查看详情</span>
|
||||
</div>
|
||||
</div>
|
||||
<el-row :gutter="15" class="statistics-content">
|
||||
<el-col :span="4" class="summary-col">
|
||||
<div class="summary-wrapper">
|
||||
<div class="summary-header">统计汇总</div>
|
||||
<pdo-summary :table-data="tableData" />
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="20" class="chart-col">
|
||||
<div class="chart-wrapper">
|
||||
<line-chart :enCoilID="currentRow.entryMatId" :url="activeUrl" />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<!-- 编辑/新增弹窗:根据isAdd条件渲染内容 -->
|
||||
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="80%" :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>
|
||||
|
||||
|
||||
<pdo-data-correction :detail="formData" :save-callback="handleSave"
|
||||
:save-loading="saveLoading"></pdo-data-correction>
|
||||
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="标签打印" :visible.sync="printOpen" width="600px" :close-on-click-modal="false">
|
||||
<div style="display: flex; justify-content: center; align-items: center;">
|
||||
<pdo-label-print :detail="formData"></pdo-label-print>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
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: '140.143.206.120:18081'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeUrl: '140.143.206.120:18081',
|
||||
queryForm: { coilid: '', startDate: '', endDate: '' },
|
||||
printOpen: false,
|
||||
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: '计算结果' }
|
||||
]
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
baseURL: {
|
||||
handler(newVal) {
|
||||
this.activeUrl = newVal
|
||||
this.pdoApi = createFetch(this.activeUrl)
|
||||
this.getPdoList()
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 获取列表数据(保持不变)
|
||||
getPdoList() {
|
||||
this.tableLoading = true
|
||||
this.pdoApi.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;
|
||||
},
|
||||
// 格式化时间
|
||||
formatTime(time) {
|
||||
if (!time) return '-';
|
||||
return this.parseTime(time, '{y}-{m}-{d} {h}:{i}:{s}');
|
||||
},
|
||||
// 补录(新增):设置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
|
||||
},
|
||||
handlePrint(row) {
|
||||
// 深拷贝行数据(避免直接修改表格数据)
|
||||
this.formData = JSON.parse(JSON.stringify(row));
|
||||
this.printOpen = true
|
||||
},
|
||||
// 删除(保持不变)
|
||||
handleDelete(row) {
|
||||
this.$confirm(`确定删除成品卷 ${row.exitMatId}?`, '确认删除', { type: 'danger' }).then(() => {
|
||||
row.deleteLoading = true
|
||||
this.pdoApi.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.pdoApi.updatePdo(formData) : this.pdoApi.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>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.pdo-container {
|
||||
height: calc(100vh - 84px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 15px;
|
||||
background: #f5f5f5;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.pdo-header {
|
||||
background: #ffffff;
|
||||
border: 1px solid #d4d4d4;
|
||||
border-radius: 2px;
|
||||
padding: 15px;
|
||||
margin-bottom: 15px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.card-grid-container {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
padding-right: 5px;
|
||||
margin-bottom: 15px;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #c0c0c0;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
}
|
||||
|
||||
.card-col {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.parameter-card {
|
||||
border: 1px solid #d4d4d4;
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
|
||||
&.card-selected {
|
||||
border-color: #999;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
::v-deep .el-card__header {
|
||||
padding: 6px 8px;
|
||||
background: #f8f8f8;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: #999;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
.card-header {
|
||||
.card-title {
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
color: #333;
|
||||
margin-bottom: 2px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.card-subtitle {
|
||||
font-size: 11px;
|
||||
color: #888;
|
||||
line-height: 1.3;
|
||||
}
|
||||
}
|
||||
|
||||
.card-body {
|
||||
.param-groups-row {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
|
||||
.param-group {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
||||
.group-title {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: #555;
|
||||
padding: 3px 0;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
margin-bottom: 4px;
|
||||
background: #f5f5f5;
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
margin-left: -4px;
|
||||
margin-right: -4px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.param-list {
|
||||
.param-line {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 3px 0;
|
||||
border-bottom: 1px dotted #e8e8e8;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.param-label {
|
||||
color: #777;
|
||||
font-size: 11px;
|
||||
flex-shrink: 0;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.param-value {
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
font-size: 11px;
|
||||
text-align: right;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 6px;
|
||||
padding-top: 6px;
|
||||
border-top: 1px solid #e0e0e0;
|
||||
margin-top: 6px;
|
||||
|
||||
.el-button {
|
||||
font-size: 11px;
|
||||
padding: 2px 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.empty-data {
|
||||
text-align: center;
|
||||
padding: 40px 0;
|
||||
}
|
||||
|
||||
.statistics-container {
|
||||
flex: 0 0 auto;
|
||||
background: #ffffff;
|
||||
border: 1px solid #d4d4d4;
|
||||
border-radius: 2px;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 280px;
|
||||
}
|
||||
|
||||
.statistics-header {
|
||||
padding: 8px 15px;
|
||||
background: #f8f8f8;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
flex-shrink: 0;
|
||||
|
||||
.selected-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 13px;
|
||||
|
||||
&.empty {
|
||||
color: #999;
|
||||
|
||||
i {
|
||||
color: #bbb;
|
||||
margin-right: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
i {
|
||||
color: #67c23a;
|
||||
margin-right: 6px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.selected-label {
|
||||
color: #666;
|
||||
margin-right: 6px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.selected-value {
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.selected-detail {
|
||||
color: #888;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.statistics-content {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
padding: 12px 15px;
|
||||
}
|
||||
|
||||
.summary-col {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.summary-wrapper {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-right: 1px solid #e8e8e8;
|
||||
padding-right: 15px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.summary-header {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 10px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
}
|
||||
|
||||
.chart-col {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.chart-wrapper {
|
||||
height: 100%;
|
||||
padding-left: 15px;
|
||||
}
|
||||
</style>
|
||||
699
klp-ui/src/views/lines/panels/roller/index.vue
Normal file
699
klp-ui/src/views/lines/panels/roller/index.vue
Normal file
@@ -0,0 +1,699 @@
|
||||
<template>
|
||||
<div class="roller-page" v-loading="loading">
|
||||
<el-row :gutter="12" style="height: 90vh;">
|
||||
<!-- 历史记录表格区域 -->
|
||||
<el-col :span="18" class="panel-col" style="height: 100%;">
|
||||
<div class="panel-card">
|
||||
<div class="history-container">
|
||||
<div class="history-table" ref="historyWrapper" @mouseleave="handleTableLeave">
|
||||
<el-table
|
||||
:data="tableData"
|
||||
border
|
||||
stripe
|
||||
class="compact-table"
|
||||
height="70vh"
|
||||
:show-overflow-tooltip="true"
|
||||
@cell-mouse-enter="handleCellEnter"
|
||||
@row-mouseleave="handleRowLeave"
|
||||
>
|
||||
<el-table-column prop="changeid" label="换辊号" align="center" show-overflow-tooltip />
|
||||
<el-table-column prop="rollid" label="轧辊号" align="center" show-overflow-tooltip />
|
||||
<el-table-column prop="standid" label="机架号" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="位置" align="center" prop="position">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.main_roll_position" :value="scope.row.position" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="类型" align="center" prop="type">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.main_roll_type" :value="scope.row.type" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="changeType" label="换辊类型" align="center" show-overflow-tooltip />
|
||||
<el-table-column prop="changeTime" label="换辊时间" align="center" show-overflow-tooltip />
|
||||
<el-table-column prop="instalTime" label="安装时间" align="center" show-overflow-tooltip />
|
||||
<el-table-column prop="deinstalTime" label="拆卸时间" align="center" show-overflow-tooltip />
|
||||
</el-table>
|
||||
<transition name="el-fade-in-linear">
|
||||
<div
|
||||
v-if="tooltipVisible && hoveredRow"
|
||||
class="row-tooltip"
|
||||
:style="tooltipStyle"
|
||||
ref="rowTooltip"
|
||||
>
|
||||
<div class="tooltip-title">详细信息</div>
|
||||
<div class="tooltip-list">
|
||||
<div
|
||||
class="tooltip-item"
|
||||
v-for="field in detailFields"
|
||||
:key="field.prop"
|
||||
>
|
||||
<span class="label">{{ field.label }}:</span>
|
||||
<span class="value">{{ formatTooltipValue(hoveredRow, field) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
<el-pagination
|
||||
v-show="pagination.total>0"
|
||||
:total="pagination.total"
|
||||
v-model:current-page="pagination.currentPage"
|
||||
v-model:page-size="pagination.pageSize"
|
||||
@current-change="getList"
|
||||
@size-change="handleSizeChange"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<!-- 筛选条件区域 -->
|
||||
<el-col :span="6" class="panel-col" style="height: 100%;">
|
||||
<div class="panel-card">
|
||||
<div class="filter-container">
|
||||
<div class="filter-panel">
|
||||
<div class="panel-header">
|
||||
<h4>历史检索</h4>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<div class="filter-label">• 按时间</div>
|
||||
<div class="filter-content time-range">
|
||||
<div class="time-label">开始时间:</div>
|
||||
<el-date-picker
|
||||
v-model="startDate"
|
||||
type="date"
|
||||
placeholder="选择日期"
|
||||
value-format="yyyy-MM-dd"
|
||||
style="width: 140px"
|
||||
></el-date-picker>
|
||||
<el-time-picker
|
||||
v-model="startTime"
|
||||
format="HH:mm"
|
||||
placeholder="选择时间"
|
||||
value-format="HH:mm"
|
||||
style="width: 140px; margin-left: 5px"
|
||||
></el-time-picker>
|
||||
|
||||
<div class="time-label" style="margin-top: 10px">结束时间:</div>
|
||||
<el-date-picker
|
||||
v-model="endDate"
|
||||
type="date"
|
||||
placeholder="选择日期"
|
||||
value-format="yyyy-MM-dd"
|
||||
style="width: 140px"
|
||||
></el-date-picker>
|
||||
<el-time-picker
|
||||
v-model="endTime"
|
||||
format="HH:mm"
|
||||
placeholder="选择时间"
|
||||
value-format="HH:mm"
|
||||
style="width: 140px; margin-left: 5px"
|
||||
></el-time-picker>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="filter-item">
|
||||
<div class="filter-label">• 按换辊号</div>
|
||||
<div class="filter-content">
|
||||
<div class="input-label">换辊号:</div>
|
||||
<el-select filterable v-model="queryParams.changeId" placeholder="请选择" clearable style="width: 240px">
|
||||
<el-option
|
||||
v-for="item in changeIdOptions"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="filter-item">
|
||||
<div class="filter-label">• 按轧辊号</div>
|
||||
<div class="filter-content">
|
||||
<div class="input-label">轧辊号:</div>
|
||||
<el-select filterable v-model="queryParams.rollId" placeholder="请选择" clearable style="width: 240px">
|
||||
<el-option
|
||||
v-for="item in rollIdOptions"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="filter-buttons">
|
||||
<el-button type="primary" @click="handleSearch">查 询</el-button>
|
||||
<el-button @click="resetQuery">重 置</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import createFetch from '@/api/l2/roller'
|
||||
|
||||
export default {
|
||||
name: 'Roller',
|
||||
dicts: ['main_roll_type','main_roll_position'],
|
||||
props: {
|
||||
baseURL: {
|
||||
type: String,
|
||||
default: '140.143.206.120:18081'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 原有父组件数据
|
||||
onlineData: [],
|
||||
loading: false,
|
||||
historyParams: {},
|
||||
|
||||
// FilterVue 组件数据
|
||||
activeUrl: '140.143.206.120:18081',
|
||||
queryParams: {
|
||||
changeId: '',
|
||||
rollId: ''
|
||||
},
|
||||
startDate: '',
|
||||
startTime: '',
|
||||
endDate: '',
|
||||
endTime: '',
|
||||
changeIdOptions: [],
|
||||
rollIdOptions: [],
|
||||
rollerApi: undefined,
|
||||
|
||||
// History 组件数据
|
||||
tableData: [],
|
||||
pagination: {
|
||||
currentPage: 1,
|
||||
pageSize: 15,
|
||||
total: 0
|
||||
},
|
||||
tooltipVisible: false,
|
||||
tooltipStyle: {
|
||||
top: '0px',
|
||||
left: '0px'
|
||||
},
|
||||
hoveredRow: null,
|
||||
searching: false,
|
||||
searchForm: {
|
||||
changeId: '',
|
||||
rollId: '',
|
||||
changeTimeRange: [],
|
||||
instalTimeRange: [],
|
||||
deinstalTimeRange: []
|
||||
},
|
||||
detailFields: [
|
||||
{ label: '换辊号', prop: 'changeid' },
|
||||
{ label: '轧辊号', prop: 'rollid' },
|
||||
{ label: '机架号', prop: 'standid' },
|
||||
{ label: '位置', prop: 'position', dict: 'main_roll_position' },
|
||||
{ label: '类型', prop: 'type', dict: 'main_roll_type' },
|
||||
{ label: '换辊类型', prop: 'changeType' },
|
||||
{ label: '换辊时间', prop: 'changeTime' },
|
||||
{ label: '安装时间', prop: 'instalTime' },
|
||||
{ label: '拆卸时间', prop: 'deinstalTime' },
|
||||
{ label: '直径', prop: 'diameter' },
|
||||
{ label: '粗糙度', prop: 'rough' },
|
||||
{ label: '凸度', prop: 'crown' },
|
||||
{ label: '成分', prop: 'composition' },
|
||||
{ label: '磨削次数', prop: 'grindCount' },
|
||||
{ label: '轧制重量', prop: 'rolledWeight' },
|
||||
{ label: '轧制数量', prop: 'rolledCount' },
|
||||
{ label: '轧制长度', prop: 'rolledLength' },
|
||||
{ label: '总轧制重量', prop: 'totalRolledWeight' },
|
||||
{ label: '总轧制长度', prop: 'totalRolledLength' },
|
||||
{ label: '总轧制数量', prop: 'totalRolledCount' }
|
||||
]
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
baseURL: {
|
||||
handler(newVal, oldVal) {
|
||||
if (newVal !== oldVal) {
|
||||
this.rollerApi = createFetch(newVal)
|
||||
this.getChangeIdOptions()
|
||||
this.getRollIdOptions()
|
||||
this.setDefaultTimeRange()
|
||||
this.fetchHistoryData()
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setHistoryParams(params) {
|
||||
console.log(params, 'params,setHistoryParams');
|
||||
this.historyParams = params
|
||||
// 触发数据查询
|
||||
this.pagination.currentPage = 1
|
||||
this.fetchHistoryData()
|
||||
},
|
||||
|
||||
// ========== FilterVue 组件方法 ==========
|
||||
getChangeIdOptions() {
|
||||
this.rollerApi.getChangeIdList().then(res => {
|
||||
console.log('换辊号列表响应:', res)
|
||||
if (res.code === 200 && res.data) {
|
||||
this.changeIdOptions = res.data
|
||||
} else {
|
||||
this.$message.error(res.msg || '获取换辊号列表失败')
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('获取换辊号列表失败', error)
|
||||
this.$message.error('获取换辊号列表失败:' + (error.message || '未知错误'))
|
||||
})
|
||||
},
|
||||
|
||||
getRollIdOptions() {
|
||||
this.rollerApi.getRollIdList().then(res => {
|
||||
console.log('轧辊号列表响应:', res)
|
||||
if (res.code === 200 && res.data) {
|
||||
this.rollIdOptions = res.data
|
||||
} else {
|
||||
this.$message.error(res.msg || '获取轧辊号列表失败')
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('获取轧辊号列表失败', error)
|
||||
this.$message.error('获取轧辊号列表失败:' + (error.message || '未知错误'))
|
||||
})
|
||||
},
|
||||
|
||||
formatDateStr(date) {
|
||||
const year = date.getFullYear()
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
return `${year}-${month}-${day}`
|
||||
},
|
||||
formatTimeStr(date) {
|
||||
const hours = String(date.getHours()).padStart(2, '0')
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||||
return `${hours}:${minutes}`
|
||||
},
|
||||
setDefaultTimeRange() {
|
||||
this.startDate = '2020-01-01'
|
||||
this.startTime = '10:00'
|
||||
this.setEndToNow()
|
||||
},
|
||||
setEndToNow() {
|
||||
const now = new Date()
|
||||
this.endDate = this.formatDateStr(now)
|
||||
this.endTime = this.formatTimeStr(now)
|
||||
},
|
||||
|
||||
handleSearch() {
|
||||
this.setEndToNow()
|
||||
// 构建搜索参数
|
||||
const params = {
|
||||
startTime: `${this.startDate} ${this.startTime}:00`,
|
||||
endTime: `${this.endDate} ${this.endTime}:00`,
|
||||
changeId: this.queryParams.changeId || '',
|
||||
rollid: this.queryParams.rollId || ''
|
||||
}
|
||||
|
||||
console.log('搜索参数:', params)
|
||||
this.historyParams = params
|
||||
this.pagination.currentPage = 1
|
||||
this.fetchHistoryData()
|
||||
},
|
||||
|
||||
resetQuery() {
|
||||
this.queryParams = {
|
||||
changeId: '',
|
||||
rollId: ''
|
||||
}
|
||||
this.setDefaultTimeRange()
|
||||
this.historyParams = {}
|
||||
this.pagination.currentPage = 1
|
||||
this.fetchHistoryData()
|
||||
},
|
||||
|
||||
// ========== History 组件方法 ==========
|
||||
fetchHistoryData() {
|
||||
const requestBody = {
|
||||
...this.historyParams,
|
||||
...this.buildSearchParams(),
|
||||
pageNum: this.pagination.currentPage,
|
||||
pageSize: this.pagination.pageSize
|
||||
}
|
||||
|
||||
const loading = this.$loading({
|
||||
lock: true,
|
||||
text: '加载中...',
|
||||
spinner: 'el-icon-loading',
|
||||
background: 'rgba(0, 0, 0, 0.7)'
|
||||
})
|
||||
|
||||
return this.rollerApi.getRollHistorytList(requestBody)
|
||||
.then(res => {
|
||||
if (res.code === 200 && res.data) {
|
||||
this.tableData = res.data.list || []
|
||||
this.pagination.total = res.data.total || 0
|
||||
this.pagination.currentPage = res.data.pageNum || 1
|
||||
|
||||
this.tableData.forEach(item => {
|
||||
if (item.changeTime) item.changeTime = this.formatDate(item.changeTime)
|
||||
if (item.instalTime) item.instalTime = this.formatDate(item.instalTime)
|
||||
if (item.deinstalTime) item.deinstalTime = this.formatDate(item.deinstalTime)
|
||||
})
|
||||
} else {
|
||||
this.$message.error(res.msg || '获取数据失败')
|
||||
this.tableData = []
|
||||
this.pagination.total = 0
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('获取轧辊历史数据失败', error)
|
||||
this.$message.error('获取数据失败:' + (error.message || '未知错误'))
|
||||
this.tableData = []
|
||||
this.pagination.total = 0
|
||||
})
|
||||
.finally(() => {
|
||||
loading.close()
|
||||
})
|
||||
},
|
||||
|
||||
formatDate(dateStr) {
|
||||
if (!dateStr) return ''
|
||||
|
||||
try {
|
||||
const date = new Date(dateStr)
|
||||
const year = date.getFullYear()
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
const hours = String(date.getHours()).padStart(2, '0')
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0')
|
||||
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
||||
} catch (error) {
|
||||
return dateStr
|
||||
}
|
||||
},
|
||||
|
||||
getList() {
|
||||
this.fetchHistoryData()
|
||||
},
|
||||
|
||||
handleSizeChange(pageSize) {
|
||||
this.pagination.pageSize = pageSize
|
||||
this.fetchHistoryData()
|
||||
},
|
||||
|
||||
buildSearchParams() {
|
||||
const params = {}
|
||||
if (this.searchForm.changeId) {
|
||||
params.changeId = this.searchForm.changeId
|
||||
}
|
||||
if (this.searchForm.rollId) {
|
||||
params.rollid = this.searchForm.rollId
|
||||
}
|
||||
|
||||
const [changeStart, changeEnd] = this.searchForm.changeTimeRange || []
|
||||
if (changeStart && changeEnd) {
|
||||
params.changeTimeBegin = changeStart
|
||||
params.changeTimeEnd = changeEnd
|
||||
}
|
||||
const [instalStart, instalEnd] = this.searchForm.instalTimeRange || []
|
||||
if (instalStart && instalEnd) {
|
||||
params.instalTimeBegin = instalStart
|
||||
params.instalTimeEnd = instalEnd
|
||||
}
|
||||
const [deinstalStart, deinstalEnd] = this.searchForm.deinstalTimeRange || []
|
||||
if (deinstalStart && deinstalEnd) {
|
||||
params.deinstalTimeBegin = deinstalStart
|
||||
params.deinstalTimeEnd = deinstalEnd
|
||||
}
|
||||
return params
|
||||
},
|
||||
|
||||
handleCellEnter(row, column, cell, event) {
|
||||
if (!row || !event) return
|
||||
this.hoveredRow = row
|
||||
this.tooltipVisible = true
|
||||
this.updateTooltipPosition(event)
|
||||
},
|
||||
|
||||
handleRowLeave() {
|
||||
this.tooltipVisible = false
|
||||
this.hoveredRow = null
|
||||
},
|
||||
|
||||
handleTableLeave() {
|
||||
this.tooltipVisible = false
|
||||
this.hoveredRow = null
|
||||
},
|
||||
|
||||
updateTooltipPosition(event) {
|
||||
this.$nextTick(() => {
|
||||
const wrapper = this.$refs.historyWrapper
|
||||
const tooltipEl = this.$refs.rowTooltip
|
||||
if (!wrapper || !tooltipEl) return
|
||||
|
||||
const wrapperRect = wrapper.getBoundingClientRect()
|
||||
const tooltipRect = tooltipEl.getBoundingClientRect()
|
||||
let left = event.clientX - wrapperRect.left + 16
|
||||
let top = event.clientY - wrapperRect.top + 12
|
||||
|
||||
if (left + tooltipRect.width > wrapperRect.width) {
|
||||
left = wrapperRect.width - tooltipRect.width - 8
|
||||
}
|
||||
if (left < 8) left = 8
|
||||
if (top + tooltipRect.height > wrapperRect.height) {
|
||||
top = wrapperRect.height - tooltipRect.height - 8
|
||||
}
|
||||
if (top < 8) top = 8
|
||||
|
||||
this.tooltipStyle = {
|
||||
top: `${top}px`,
|
||||
left: `${left}px`
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
formatTooltipValue(row, field) {
|
||||
const value = row[field.prop]
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return '-'
|
||||
}
|
||||
if (field.dict && this.dict && this.dict.type && this.dict.type[field.dict]) {
|
||||
const match = this.dict.type[field.dict].find(item => item.value === value)
|
||||
return match ? match.label : value
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.roller-page {
|
||||
padding: 15px 20px 20px;
|
||||
background: #f5f5f5;
|
||||
min-height: calc(100vh - 84px);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.panel-row {
|
||||
margin-bottom: 12px;
|
||||
align-items: stretch;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.panel-col {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.panel-card {
|
||||
background: #ffffff;
|
||||
border: 1px solid #dcdcdc;
|
||||
border-radius: 4px;
|
||||
padding: 12px 0;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.tall-panel {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.bottom-row .panel-card {
|
||||
min-height: 360px;
|
||||
}
|
||||
|
||||
// Filter 组件样式
|
||||
.filter-container {
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.filter-panel {
|
||||
flex: 1;
|
||||
padding: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: 4px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.panel-header {
|
||||
margin-bottom: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
h4 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-item {
|
||||
margin-bottom: 20px;
|
||||
|
||||
.filter-label {
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.filter-content {
|
||||
padding-left: 15px;
|
||||
|
||||
.input-label {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.time-range {
|
||||
.time-label {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filter-buttons {
|
||||
text-align: center;
|
||||
|
||||
.el-button {
|
||||
width: 100px;
|
||||
margin: 0 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// History 组件样式
|
||||
.history-container {
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
|
||||
.history-search-panel {
|
||||
margin-bottom: 8px;
|
||||
background: #ffffff;
|
||||
|
||||
.history-form {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-end;
|
||||
gap: 8px 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.history-table {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
margin-bottom: 10px;
|
||||
background: #ffffff;
|
||||
|
||||
.compact-table {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
margin: 10px 0;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.button-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 10px 0;
|
||||
|
||||
.el-button + .el-button {
|
||||
margin-left: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .el-table th,
|
||||
::v-deep .el-table td {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.row-tooltip {
|
||||
position: absolute;
|
||||
background: #ffffff;
|
||||
border: 1px solid #dcdcdc;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
padding: 12px 14px;
|
||||
pointer-events: none;
|
||||
z-index: 5;
|
||||
max-height: 70%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.tooltip-title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tooltip-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 6px 12px;
|
||||
}
|
||||
|
||||
.tooltip-item {
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
line-height: 1.5;
|
||||
|
||||
.label {
|
||||
color: #666;
|
||||
margin-right: 4px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.value {
|
||||
flex: 1;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -181,10 +181,14 @@ export default {
|
||||
currentRow: {}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
// 页面加载时默认查询一次
|
||||
this.fetchApi = createFetch(this.baseURL)
|
||||
this.getStoppageList();
|
||||
watch: {
|
||||
baseURL: {
|
||||
handler(newVal) {
|
||||
this.fetchApi = createFetch(newVal)
|
||||
this.getStoppageList();
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 获取停机记录列表
|
||||
|
||||
@@ -1,579 +1,20 @@
|
||||
<template>
|
||||
<div class="pdo-container">
|
||||
<!-- 查询表单区域 -->
|
||||
<div class="pdo-header">
|
||||
<el-form :inline="true" :model="queryForm" ref="queryForm" label-width="100px" size="small">
|
||||
<el-form-item label="钢卷号" prop="coilid">
|
||||
<el-input v-model="queryForm.coilid" placeholder="请输入钢卷号"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="开始日期" prop="startDate">
|
||||
<el-date-picker 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 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">查询</el-button>
|
||||
<el-button @click="handleReset" icon="el-icon-refresh">重置</el-button>
|
||||
<el-button type="success" @click="handleAdd" icon="el-icon-plus">补录</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- 卡片网格布局 -->
|
||||
<div v-loading="tableLoading" class="card-grid-container">
|
||||
<el-row :gutter="10">
|
||||
<el-col v-for="(item, index) in tableData" :key="index" :xs="24" :sm="12" :md="8" :lg="6" :xl="5"
|
||||
class="card-col">
|
||||
<el-card class="parameter-card" shadow="never" :body-style="{ padding: '8px' }"
|
||||
:class="{ 'card-selected': currentRow.exitMatId === item.exitMatId }" @click.native="handleRowClick(item)">
|
||||
<div slot="header" class="card-header">
|
||||
<div class="card-title">成品卷: {{ item.exitMatId || '-' }}</div>
|
||||
<div class="card-subtitle">{{ item.entryMatId || '-' }} | {{ item.planNo || '-' }}</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="param-groups-row">
|
||||
<!-- 基本信息 -->
|
||||
<div class="param-group">
|
||||
<div class="group-title">基本信息</div>
|
||||
<div class="param-list">
|
||||
<div class="param-line">
|
||||
<span class="param-label">状态:</span>
|
||||
<span class="param-value">{{ item.status || '-' }}</span>
|
||||
</div>
|
||||
<div class="param-line">
|
||||
<span class="param-label">钢种:</span>
|
||||
<span class="param-value">{{ item.steelGrade || '-' }}</span>
|
||||
</div>
|
||||
<div class="param-line">
|
||||
<span class="param-label">产品类型:</span>
|
||||
<span class="param-value">{{ item.prodCode || '-' }}</span>
|
||||
</div>
|
||||
<div class="param-line">
|
||||
<span class="param-label">客户:</span>
|
||||
<span class="param-value">{{ item.customer || '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 来料信息 -->
|
||||
<div class="param-group">
|
||||
<div class="group-title">来料信息</div>
|
||||
<div class="param-list">
|
||||
<div class="param-line">
|
||||
<span class="param-label">厚度:</span>
|
||||
<span class="param-value">{{ item.entryThick || '-' }}</span>
|
||||
</div>
|
||||
<div class="param-line">
|
||||
<span class="param-label">宽度:</span>
|
||||
<span class="param-value">{{ item.entryWidth || '-' }}</span>
|
||||
</div>
|
||||
<div class="param-line">
|
||||
<span class="param-label">长度:</span>
|
||||
<span class="param-value">{{ item.entryLength || '-' }}</span>
|
||||
</div>
|
||||
<div class="param-line">
|
||||
<span class="param-label">重量:</span>
|
||||
<span class="param-value">{{ item.entryWeight || '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 成品信息 -->
|
||||
<div class="param-group">
|
||||
<div class="group-title">成品信息</div>
|
||||
<div class="param-list">
|
||||
<div class="param-line">
|
||||
<span class="param-label">厚度:</span>
|
||||
<span class="param-value">{{ item.exitThickness || '-' }}</span>
|
||||
</div>
|
||||
<div class="param-line">
|
||||
<span class="param-label">宽度:</span>
|
||||
<span class="param-value">{{ item.exitWidth || '-' }}</span>
|
||||
</div>
|
||||
<div class="param-line">
|
||||
<span class="param-label">长度:</span>
|
||||
<span class="param-value">{{ item.exitLength || '-' }}</span>
|
||||
</div>
|
||||
<div class="param-line">
|
||||
<span class="param-label">重量:</span>
|
||||
<span class="param-value">{{ item.exitNetWeight || '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<el-button size="mini" type="text" icon="el-icon-view" @click.stop="handlePrint(item)">打印</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click.stop="handleEdit(item)">操作</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" :loading="item.deleteLoading"
|
||||
@click.stop="handleDelete(item)">删除</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div v-if="tableData.length === 0 && !tableLoading" class="empty-data">
|
||||
<el-empty description="暂无数据"></el-empty>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 统计汇总和图表区域 -->
|
||||
<div class="statistics-container">
|
||||
<div class="statistics-header">
|
||||
<div class="selected-info" v-if="currentRow && currentRow.entryMatId">
|
||||
<i class="el-icon-check"></i>
|
||||
<span class="selected-label">已选中:</span>
|
||||
<span class="selected-value">成品卷 {{ currentRow.exitMatId }}</span>
|
||||
<span class="selected-detail" v-if="currentRow.entryMatId">(来料卷: {{ currentRow.entryMatId }})</span>
|
||||
</div>
|
||||
<div class="selected-info empty" v-else>
|
||||
<i class="el-icon-info"></i>
|
||||
<span>请选择上方卡片查看详情</span>
|
||||
</div>
|
||||
</div>
|
||||
<el-row :gutter="15" class="statistics-content">
|
||||
<el-col :span="4" class="summary-col">
|
||||
<div class="summary-wrapper">
|
||||
<div class="summary-header">统计汇总</div>
|
||||
<pdo-summary :table-data="tableData" />
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="20" class="chart-col">
|
||||
<div class="chart-wrapper">
|
||||
<line-chart :enCoilID="currentRow.entryMatId" :url="activeUrl" />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<!-- 编辑/新增弹窗:根据isAdd条件渲染内容 -->
|
||||
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="80%" :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>
|
||||
|
||||
|
||||
<pdo-data-correction :detail="formData" :save-callback="handleSave"
|
||||
:save-loading="saveLoading"></pdo-data-correction>
|
||||
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="标签打印" :visible.sync="printOpen" width="600px" :close-on-click-modal="false">
|
||||
<div style="display: flex; justify-content: center; align-items: center;">
|
||||
<pdo-label-print :detail="formData"></pdo-label-print>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<div>
|
||||
<PdoPage :baseURL="baseURL" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
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'
|
||||
import PdoPage from '../panels/pdo/index.vue'
|
||||
|
||||
export default {
|
||||
name: 'PdoManagement',
|
||||
dicts: ['pdo_plan_origin'],
|
||||
components: {
|
||||
LineChart,
|
||||
PdoDataCorrection, // 注册数据修正组件
|
||||
PdoLabelPrint, // 注册标签打印组件
|
||||
PdoSummary
|
||||
PdoPage
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeUrl: '140.143.206.120:18081',
|
||||
queryForm: { coilid: '', startDate: '', endDate: '' },
|
||||
printOpen: false,
|
||||
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: '计算结果' }
|
||||
]
|
||||
baseURL: '140.143.206.120:18081'
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.pdoApi = createFetch(this.activeUrl)
|
||||
this.getPdoList()
|
||||
},
|
||||
methods: {
|
||||
// 获取列表数据(保持不变)
|
||||
getPdoList() {
|
||||
this.tableLoading = true
|
||||
this.pdoApi.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;
|
||||
},
|
||||
// 格式化时间
|
||||
formatTime(time) {
|
||||
if (!time) return '-';
|
||||
return this.parseTime(time, '{y}-{m}-{d} {h}:{i}:{s}');
|
||||
},
|
||||
// 补录(新增):设置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
|
||||
},
|
||||
handlePrint(row) {
|
||||
// 深拷贝行数据(避免直接修改表格数据)
|
||||
this.formData = JSON.parse(JSON.stringify(row));
|
||||
this.printOpen = true
|
||||
},
|
||||
// 删除(保持不变)
|
||||
handleDelete(row) {
|
||||
this.$confirm(`确定删除成品卷 ${row.exitMatId}?`, '确认删除', { type: 'danger' }).then(() => {
|
||||
row.deleteLoading = true
|
||||
this.pdoApi.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.pdoApi.updatePdo(formData) : this.pdoApi.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>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.pdo-container {
|
||||
height: calc(100vh - 84px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 15px;
|
||||
background: #f5f5f5;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.pdo-header {
|
||||
background: #ffffff;
|
||||
border: 1px solid #d4d4d4;
|
||||
border-radius: 2px;
|
||||
padding: 15px;
|
||||
margin-bottom: 15px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.card-grid-container {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
padding-right: 5px;
|
||||
margin-bottom: 15px;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #c0c0c0;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
}
|
||||
|
||||
.card-col {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.parameter-card {
|
||||
border: 1px solid #d4d4d4;
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
|
||||
&.card-selected {
|
||||
border-color: #999;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
::v-deep .el-card__header {
|
||||
padding: 6px 8px;
|
||||
background: #f8f8f8;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: #999;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
.card-header {
|
||||
.card-title {
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
color: #333;
|
||||
margin-bottom: 2px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.card-subtitle {
|
||||
font-size: 11px;
|
||||
color: #888;
|
||||
line-height: 1.3;
|
||||
}
|
||||
}
|
||||
|
||||
.card-body {
|
||||
.param-groups-row {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
|
||||
.param-group {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
||||
.group-title {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: #555;
|
||||
padding: 3px 0;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
margin-bottom: 4px;
|
||||
background: #f5f5f5;
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
margin-left: -4px;
|
||||
margin-right: -4px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.param-list {
|
||||
.param-line {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 3px 0;
|
||||
border-bottom: 1px dotted #e8e8e8;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.param-label {
|
||||
color: #777;
|
||||
font-size: 11px;
|
||||
flex-shrink: 0;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.param-value {
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
font-size: 11px;
|
||||
text-align: right;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 6px;
|
||||
padding-top: 6px;
|
||||
border-top: 1px solid #e0e0e0;
|
||||
margin-top: 6px;
|
||||
|
||||
.el-button {
|
||||
font-size: 11px;
|
||||
padding: 2px 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.empty-data {
|
||||
text-align: center;
|
||||
padding: 40px 0;
|
||||
}
|
||||
|
||||
.statistics-container {
|
||||
flex: 0 0 auto;
|
||||
background: #ffffff;
|
||||
border: 1px solid #d4d4d4;
|
||||
border-radius: 2px;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 280px;
|
||||
}
|
||||
|
||||
.statistics-header {
|
||||
padding: 8px 15px;
|
||||
background: #f8f8f8;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
flex-shrink: 0;
|
||||
|
||||
.selected-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 13px;
|
||||
|
||||
&.empty {
|
||||
color: #999;
|
||||
|
||||
i {
|
||||
color: #bbb;
|
||||
margin-right: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
i {
|
||||
color: #67c23a;
|
||||
margin-right: 6px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.selected-label {
|
||||
color: #666;
|
||||
margin-right: 6px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.selected-value {
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.selected-detail {
|
||||
color: #888;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.statistics-content {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
padding: 12px 15px;
|
||||
}
|
||||
|
||||
.summary-col {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.summary-wrapper {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-right: 1px solid #e8e8e8;
|
||||
padding-right: 15px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.summary-header {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 10px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
}
|
||||
|
||||
.chart-col {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.chart-wrapper {
|
||||
height: 100%;
|
||||
padding-left: 15px;
|
||||
}
|
||||
</style>
|
||||
</script>
|
||||
@@ -1,687 +1,20 @@
|
||||
<template>
|
||||
<div class="roller-page" v-loading="loading">
|
||||
<el-row :gutter="12" style="height: 90vh;">
|
||||
<!-- 历史记录表格区域 -->
|
||||
<el-col :span="18" class="panel-col" style="height: 100%;">
|
||||
<div class="panel-card">
|
||||
<div class="history-container">
|
||||
<div class="history-table" ref="historyWrapper" @mouseleave="handleTableLeave">
|
||||
<el-table
|
||||
:data="tableData"
|
||||
border
|
||||
stripe
|
||||
class="compact-table"
|
||||
height="70vh"
|
||||
:show-overflow-tooltip="true"
|
||||
@cell-mouse-enter="handleCellEnter"
|
||||
@row-mouseleave="handleRowLeave"
|
||||
>
|
||||
<el-table-column prop="changeid" label="换辊号" align="center" show-overflow-tooltip />
|
||||
<el-table-column prop="rollid" label="轧辊号" align="center" show-overflow-tooltip />
|
||||
<el-table-column prop="standid" label="机架号" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="位置" align="center" prop="position">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.main_roll_position" :value="scope.row.position" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="类型" align="center" prop="type">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.main_roll_type" :value="scope.row.type" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="changeType" label="换辊类型" align="center" show-overflow-tooltip />
|
||||
<el-table-column prop="changeTime" label="换辊时间" align="center" show-overflow-tooltip />
|
||||
<el-table-column prop="instalTime" label="安装时间" align="center" show-overflow-tooltip />
|
||||
<el-table-column prop="deinstalTime" label="拆卸时间" align="center" show-overflow-tooltip />
|
||||
</el-table>
|
||||
<transition name="el-fade-in-linear">
|
||||
<div
|
||||
v-if="tooltipVisible && hoveredRow"
|
||||
class="row-tooltip"
|
||||
:style="tooltipStyle"
|
||||
ref="rowTooltip"
|
||||
>
|
||||
<div class="tooltip-title">详细信息</div>
|
||||
<div class="tooltip-list">
|
||||
<div
|
||||
class="tooltip-item"
|
||||
v-for="field in detailFields"
|
||||
:key="field.prop"
|
||||
>
|
||||
<span class="label">{{ field.label }}:</span>
|
||||
<span class="value">{{ formatTooltipValue(hoveredRow, field) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
<el-pagination
|
||||
v-show="pagination.total>0"
|
||||
:total="pagination.total"
|
||||
v-model:current-page="pagination.currentPage"
|
||||
v-model:page-size="pagination.pageSize"
|
||||
@current-change="getList"
|
||||
@size-change="handleSizeChange"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<!-- 筛选条件区域 -->
|
||||
<el-col :span="6" class="panel-col" style="height: 100%;">
|
||||
<div class="panel-card">
|
||||
<div class="filter-container">
|
||||
<div class="filter-panel">
|
||||
<div class="panel-header">
|
||||
<h4>历史检索</h4>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<div class="filter-label">• 按时间</div>
|
||||
<div class="filter-content time-range">
|
||||
<div class="time-label">开始时间:</div>
|
||||
<el-date-picker
|
||||
v-model="startDate"
|
||||
type="date"
|
||||
placeholder="选择日期"
|
||||
value-format="yyyy-MM-dd"
|
||||
style="width: 140px"
|
||||
></el-date-picker>
|
||||
<el-time-picker
|
||||
v-model="startTime"
|
||||
format="HH:mm"
|
||||
placeholder="选择时间"
|
||||
value-format="HH:mm"
|
||||
style="width: 140px; margin-left: 5px"
|
||||
></el-time-picker>
|
||||
|
||||
<div class="time-label" style="margin-top: 10px">结束时间:</div>
|
||||
<el-date-picker
|
||||
v-model="endDate"
|
||||
type="date"
|
||||
placeholder="选择日期"
|
||||
value-format="yyyy-MM-dd"
|
||||
style="width: 140px"
|
||||
></el-date-picker>
|
||||
<el-time-picker
|
||||
v-model="endTime"
|
||||
format="HH:mm"
|
||||
placeholder="选择时间"
|
||||
value-format="HH:mm"
|
||||
style="width: 140px; margin-left: 5px"
|
||||
></el-time-picker>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="filter-item">
|
||||
<div class="filter-label">• 按换辊号</div>
|
||||
<div class="filter-content">
|
||||
<div class="input-label">换辊号:</div>
|
||||
<el-select filterable v-model="queryParams.changeId" placeholder="请选择" clearable style="width: 240px">
|
||||
<el-option
|
||||
v-for="item in changeIdOptions"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="filter-item">
|
||||
<div class="filter-label">• 按轧辊号</div>
|
||||
<div class="filter-content">
|
||||
<div class="input-label">轧辊号:</div>
|
||||
<el-select filterable v-model="queryParams.rollId" placeholder="请选择" clearable style="width: 240px">
|
||||
<el-option
|
||||
v-for="item in rollIdOptions"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="filter-buttons">
|
||||
<el-button type="primary" @click="handleSearch">查 询</el-button>
|
||||
<el-button @click="resetQuery">重 置</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div>
|
||||
<RollerPage :baseURL="baseURL" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import createFetch from '@/api/l2/roller'
|
||||
import RollerPage from '../panels/roller/index.vue'
|
||||
|
||||
export default {
|
||||
name: 'Roller',
|
||||
dicts: ['main_roll_type','main_roll_position'],
|
||||
components: {
|
||||
RollerPage
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 原有父组件数据
|
||||
onlineData: [],
|
||||
loading: false,
|
||||
historyParams: {},
|
||||
|
||||
// FilterVue 组件数据
|
||||
activeUrl: '140.143.206.120:18081',
|
||||
queryParams: {
|
||||
changeId: '',
|
||||
rollId: ''
|
||||
},
|
||||
startDate: '',
|
||||
startTime: '',
|
||||
endDate: '',
|
||||
endTime: '',
|
||||
changeIdOptions: [],
|
||||
rollIdOptions: [],
|
||||
rollerApi: undefined,
|
||||
|
||||
// History 组件数据
|
||||
tableData: [],
|
||||
pagination: {
|
||||
currentPage: 1,
|
||||
pageSize: 15,
|
||||
total: 0
|
||||
},
|
||||
tooltipVisible: false,
|
||||
tooltipStyle: {
|
||||
top: '0px',
|
||||
left: '0px'
|
||||
},
|
||||
hoveredRow: null,
|
||||
searching: false,
|
||||
searchForm: {
|
||||
changeId: '',
|
||||
rollId: '',
|
||||
changeTimeRange: [],
|
||||
instalTimeRange: [],
|
||||
deinstalTimeRange: []
|
||||
},
|
||||
detailFields: [
|
||||
{ label: '换辊号', prop: 'changeid' },
|
||||
{ label: '轧辊号', prop: 'rollid' },
|
||||
{ label: '机架号', prop: 'standid' },
|
||||
{ label: '位置', prop: 'position', dict: 'main_roll_position' },
|
||||
{ label: '类型', prop: 'type', dict: 'main_roll_type' },
|
||||
{ label: '换辊类型', prop: 'changeType' },
|
||||
{ label: '换辊时间', prop: 'changeTime' },
|
||||
{ label: '安装时间', prop: 'instalTime' },
|
||||
{ label: '拆卸时间', prop: 'deinstalTime' },
|
||||
{ label: '直径', prop: 'diameter' },
|
||||
{ label: '粗糙度', prop: 'rough' },
|
||||
{ label: '凸度', prop: 'crown' },
|
||||
{ label: '成分', prop: 'composition' },
|
||||
{ label: '磨削次数', prop: 'grindCount' },
|
||||
{ label: '轧制重量', prop: 'rolledWeight' },
|
||||
{ label: '轧制数量', prop: 'rolledCount' },
|
||||
{ label: '轧制长度', prop: 'rolledLength' },
|
||||
{ label: '总轧制重量', prop: 'totalRolledWeight' },
|
||||
{ label: '总轧制长度', prop: 'totalRolledLength' },
|
||||
{ label: '总轧制数量', prop: 'totalRolledCount' }
|
||||
]
|
||||
baseURL: '140.143.206.120:18081'
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// 初始化筛选组件数据
|
||||
this.rollerApi = createFetch(this.activeUrl);
|
||||
this.getChangeIdOptions()
|
||||
this.getRollIdOptions()
|
||||
this.setDefaultTimeRange()
|
||||
this.fetchHistoryData()
|
||||
},
|
||||
methods: {
|
||||
setHistoryParams(params) {
|
||||
console.log(params, 'params,setHistoryParams');
|
||||
this.historyParams = params
|
||||
// 触发数据查询
|
||||
this.pagination.currentPage = 1
|
||||
this.fetchHistoryData()
|
||||
},
|
||||
|
||||
// ========== FilterVue 组件方法 ==========
|
||||
getChangeIdOptions() {
|
||||
this.rollerApi.getChangeIdList().then(res => {
|
||||
console.log('换辊号列表响应:', res)
|
||||
if (res.code === 200 && res.data) {
|
||||
this.changeIdOptions = res.data
|
||||
} else {
|
||||
this.$message.error(res.msg || '获取换辊号列表失败')
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('获取换辊号列表失败', error)
|
||||
this.$message.error('获取换辊号列表失败:' + (error.message || '未知错误'))
|
||||
})
|
||||
},
|
||||
|
||||
getRollIdOptions() {
|
||||
this.rollerApi.getRollIdList().then(res => {
|
||||
console.log('轧辊号列表响应:', res)
|
||||
if (res.code === 200 && res.data) {
|
||||
this.rollIdOptions = res.data
|
||||
} else {
|
||||
this.$message.error(res.msg || '获取轧辊号列表失败')
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('获取轧辊号列表失败', error)
|
||||
this.$message.error('获取轧辊号列表失败:' + (error.message || '未知错误'))
|
||||
})
|
||||
},
|
||||
|
||||
formatDateStr(date) {
|
||||
const year = date.getFullYear()
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
return `${year}-${month}-${day}`
|
||||
},
|
||||
formatTimeStr(date) {
|
||||
const hours = String(date.getHours()).padStart(2, '0')
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||||
return `${hours}:${minutes}`
|
||||
},
|
||||
setDefaultTimeRange() {
|
||||
this.startDate = '2020-01-01'
|
||||
this.startTime = '10:00'
|
||||
this.setEndToNow()
|
||||
},
|
||||
setEndToNow() {
|
||||
const now = new Date()
|
||||
this.endDate = this.formatDateStr(now)
|
||||
this.endTime = this.formatTimeStr(now)
|
||||
},
|
||||
|
||||
handleSearch() {
|
||||
this.setEndToNow()
|
||||
// 构建搜索参数
|
||||
const params = {
|
||||
startTime: `${this.startDate} ${this.startTime}:00`,
|
||||
endTime: `${this.endDate} ${this.endTime}:00`,
|
||||
changeId: this.queryParams.changeId || '',
|
||||
rollid: this.queryParams.rollId || ''
|
||||
}
|
||||
|
||||
console.log('搜索参数:', params)
|
||||
this.historyParams = params
|
||||
this.pagination.currentPage = 1
|
||||
this.fetchHistoryData()
|
||||
},
|
||||
|
||||
resetQuery() {
|
||||
this.queryParams = {
|
||||
changeId: '',
|
||||
rollId: ''
|
||||
}
|
||||
this.setDefaultTimeRange()
|
||||
this.historyParams = {}
|
||||
this.pagination.currentPage = 1
|
||||
this.fetchHistoryData()
|
||||
},
|
||||
|
||||
// ========== History 组件方法 ==========
|
||||
fetchHistoryData() {
|
||||
const requestBody = {
|
||||
...this.historyParams,
|
||||
...this.buildSearchParams(),
|
||||
pageNum: this.pagination.currentPage,
|
||||
pageSize: this.pagination.pageSize
|
||||
}
|
||||
|
||||
const loading = this.$loading({
|
||||
lock: true,
|
||||
text: '加载中...',
|
||||
spinner: 'el-icon-loading',
|
||||
background: 'rgba(0, 0, 0, 0.7)'
|
||||
})
|
||||
|
||||
return this.rollerApi.getRollHistorytList(requestBody)
|
||||
.then(res => {
|
||||
if (res.code === 200 && res.data) {
|
||||
this.tableData = res.data.list || []
|
||||
this.pagination.total = res.data.total || 0
|
||||
this.pagination.currentPage = res.data.pageNum || 1
|
||||
|
||||
this.tableData.forEach(item => {
|
||||
if (item.changeTime) item.changeTime = this.formatDate(item.changeTime)
|
||||
if (item.instalTime) item.instalTime = this.formatDate(item.instalTime)
|
||||
if (item.deinstalTime) item.deinstalTime = this.formatDate(item.deinstalTime)
|
||||
})
|
||||
} else {
|
||||
this.$message.error(res.msg || '获取数据失败')
|
||||
this.tableData = []
|
||||
this.pagination.total = 0
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('获取轧辊历史数据失败', error)
|
||||
this.$message.error('获取数据失败:' + (error.message || '未知错误'))
|
||||
this.tableData = []
|
||||
this.pagination.total = 0
|
||||
})
|
||||
.finally(() => {
|
||||
loading.close()
|
||||
})
|
||||
},
|
||||
|
||||
formatDate(dateStr) {
|
||||
if (!dateStr) return ''
|
||||
|
||||
try {
|
||||
const date = new Date(dateStr)
|
||||
const year = date.getFullYear()
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
const hours = String(date.getHours()).padStart(2, '0')
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0')
|
||||
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
||||
} catch (error) {
|
||||
return dateStr
|
||||
}
|
||||
},
|
||||
|
||||
getList() {
|
||||
this.fetchHistoryData()
|
||||
},
|
||||
|
||||
handleSizeChange(pageSize) {
|
||||
this.pagination.pageSize = pageSize
|
||||
this.fetchHistoryData()
|
||||
},
|
||||
|
||||
buildSearchParams() {
|
||||
const params = {}
|
||||
if (this.searchForm.changeId) {
|
||||
params.changeId = this.searchForm.changeId
|
||||
}
|
||||
if (this.searchForm.rollId) {
|
||||
params.rollid = this.searchForm.rollId
|
||||
}
|
||||
|
||||
const [changeStart, changeEnd] = this.searchForm.changeTimeRange || []
|
||||
if (changeStart && changeEnd) {
|
||||
params.changeTimeBegin = changeStart
|
||||
params.changeTimeEnd = changeEnd
|
||||
}
|
||||
const [instalStart, instalEnd] = this.searchForm.instalTimeRange || []
|
||||
if (instalStart && instalEnd) {
|
||||
params.instalTimeBegin = instalStart
|
||||
params.instalTimeEnd = instalEnd
|
||||
}
|
||||
const [deinstalStart, deinstalEnd] = this.searchForm.deinstalTimeRange || []
|
||||
if (deinstalStart && deinstalEnd) {
|
||||
params.deinstalTimeBegin = deinstalStart
|
||||
params.deinstalTimeEnd = deinstalEnd
|
||||
}
|
||||
return params
|
||||
},
|
||||
|
||||
handleCellEnter(row, column, cell, event) {
|
||||
if (!row || !event) return
|
||||
this.hoveredRow = row
|
||||
this.tooltipVisible = true
|
||||
this.updateTooltipPosition(event)
|
||||
},
|
||||
|
||||
handleRowLeave() {
|
||||
this.tooltipVisible = false
|
||||
this.hoveredRow = null
|
||||
},
|
||||
|
||||
handleTableLeave() {
|
||||
this.tooltipVisible = false
|
||||
this.hoveredRow = null
|
||||
},
|
||||
|
||||
updateTooltipPosition(event) {
|
||||
this.$nextTick(() => {
|
||||
const wrapper = this.$refs.historyWrapper
|
||||
const tooltipEl = this.$refs.rowTooltip
|
||||
if (!wrapper || !tooltipEl) return
|
||||
|
||||
const wrapperRect = wrapper.getBoundingClientRect()
|
||||
const tooltipRect = tooltipEl.getBoundingClientRect()
|
||||
let left = event.clientX - wrapperRect.left + 16
|
||||
let top = event.clientY - wrapperRect.top + 12
|
||||
|
||||
if (left + tooltipRect.width > wrapperRect.width) {
|
||||
left = wrapperRect.width - tooltipRect.width - 8
|
||||
}
|
||||
if (left < 8) left = 8
|
||||
if (top + tooltipRect.height > wrapperRect.height) {
|
||||
top = wrapperRect.height - tooltipRect.height - 8
|
||||
}
|
||||
if (top < 8) top = 8
|
||||
|
||||
this.tooltipStyle = {
|
||||
top: `${top}px`,
|
||||
left: `${left}px`
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
formatTooltipValue(row, field) {
|
||||
const value = row[field.prop]
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return '-'
|
||||
}
|
||||
if (field.dict && this.dict && this.dict.type && this.dict.type[field.dict]) {
|
||||
const match = this.dict.type[field.dict].find(item => item.value === value)
|
||||
return match ? match.label : value
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.roller-page {
|
||||
padding: 15px 20px 20px;
|
||||
background: #f5f5f5;
|
||||
min-height: calc(100vh - 84px);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.panel-row {
|
||||
margin-bottom: 12px;
|
||||
align-items: stretch;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.panel-col {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.panel-card {
|
||||
background: #ffffff;
|
||||
border: 1px solid #dcdcdc;
|
||||
border-radius: 4px;
|
||||
padding: 12px 0;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.tall-panel {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.bottom-row .panel-card {
|
||||
min-height: 360px;
|
||||
}
|
||||
|
||||
// Filter 组件样式
|
||||
.filter-container {
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.filter-panel {
|
||||
flex: 1;
|
||||
padding: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: 4px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.panel-header {
|
||||
margin-bottom: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
h4 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-item {
|
||||
margin-bottom: 20px;
|
||||
|
||||
.filter-label {
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.filter-content {
|
||||
padding-left: 15px;
|
||||
|
||||
.input-label {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.time-range {
|
||||
.time-label {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filter-buttons {
|
||||
text-align: center;
|
||||
|
||||
.el-button {
|
||||
width: 100px;
|
||||
margin: 0 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// History 组件样式
|
||||
.history-container {
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
|
||||
.history-search-panel {
|
||||
margin-bottom: 8px;
|
||||
background: #ffffff;
|
||||
|
||||
.history-form {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-end;
|
||||
gap: 8px 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.history-table {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
margin-bottom: 10px;
|
||||
background: #ffffff;
|
||||
|
||||
.compact-table {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
margin: 10px 0;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.button-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 10px 0;
|
||||
|
||||
.el-button + .el-button {
|
||||
margin-left: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .el-table th,
|
||||
::v-deep .el-table td {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.row-tooltip {
|
||||
position: absolute;
|
||||
background: #ffffff;
|
||||
border: 1px solid #dcdcdc;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
padding: 12px 14px;
|
||||
pointer-events: none;
|
||||
z-index: 5;
|
||||
max-height: 70%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.tooltip-title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tooltip-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 6px 12px;
|
||||
}
|
||||
|
||||
.tooltip-item {
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
line-height: 1.5;
|
||||
|
||||
.label {
|
||||
color: #666;
|
||||
margin-right: 4px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.value {
|
||||
flex: 1;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</script>
|
||||
@@ -1,609 +1,20 @@
|
||||
<template>
|
||||
<div class="stoppage-management">
|
||||
<!-- 查询表单区域 -->
|
||||
<div class="stop-header">
|
||||
<el-form :inline="true" :model="queryForm" ref="queryForm" label-width="80px" size="small">
|
||||
<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" icon="el-icon-search">查询</el-button>
|
||||
<el-button @click="handleReset" icon="el-icon-refresh">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- 卡片网格布局 -->
|
||||
<div v-loading="tableLoading" class="card-grid-container">
|
||||
<el-row :gutter="10">
|
||||
<el-col
|
||||
v-for="(item, index) in tableData"
|
||||
:key="index"
|
||||
:xs="24"
|
||||
:sm="12"
|
||||
:md="8"
|
||||
:lg="6"
|
||||
:xl="5"
|
||||
class="card-col"
|
||||
>
|
||||
<el-card
|
||||
class="parameter-card"
|
||||
shadow="never"
|
||||
:body-style="{ padding: '8px' }"
|
||||
:class="{ 'card-selected': currentRow.stopid === item.stopid }"
|
||||
@click.native="handleRowClick(item)"
|
||||
>
|
||||
<div slot="header" class="card-header">
|
||||
<div class="card-title">停机ID: {{ item.stopid || '-' }}</div>
|
||||
<div class="card-subtitle">钢卷号: {{ item.coilid || '-' }}</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="param-groups-row">
|
||||
<!-- 基本信息 -->
|
||||
<div class="param-group">
|
||||
<div class="group-title">基本信息</div>
|
||||
<div class="param-list">
|
||||
<div class="param-line">
|
||||
<span class="param-label">停机类型:</span>
|
||||
<span class="param-value">{{ item.stopType || '-' }}</span>
|
||||
</div>
|
||||
<div class="param-line">
|
||||
<span class="param-label">班:</span>
|
||||
<span class="param-value">{{ item.shift || '-' }}</span>
|
||||
</div>
|
||||
<div class="param-line">
|
||||
<span class="param-label">组:</span>
|
||||
<span class="param-value">{{ item.crew || '-' }}</span>
|
||||
</div>
|
||||
<div class="param-line">
|
||||
<span class="param-label">区域:</span>
|
||||
<span class="param-value">{{ item.area || '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 设备信息 -->
|
||||
<div class="param-group">
|
||||
<div class="group-title">设备信息</div>
|
||||
<div class="param-list">
|
||||
<div class="param-line">
|
||||
<span class="param-label">设备:</span>
|
||||
<span class="param-value">{{ item.seton || '-' }}</span>
|
||||
</div>
|
||||
<div class="param-line">
|
||||
<span class="param-label">机组:</span>
|
||||
<span class="param-value">{{ item.unit || '-' }}</span>
|
||||
</div>
|
||||
<div class="param-line">
|
||||
<span class="param-label">停机时长:</span>
|
||||
<span class="param-value">{{ item.duration || '-' }} 分钟</span>
|
||||
</div>
|
||||
<div class="param-line">
|
||||
<span class="param-label">更新时间:</span>
|
||||
<span class="param-value">{{ formatTime(item.insdate) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 时间信息 -->
|
||||
<div class="param-group">
|
||||
<div class="group-title">时间信息</div>
|
||||
<div class="param-list">
|
||||
<div class="param-line">
|
||||
<span class="param-label">开始时间:</span>
|
||||
<span class="param-value">{{ formatTime(item.startDate) }}</span>
|
||||
</div>
|
||||
<div class="param-line">
|
||||
<span class="param-label">结束时间:</span>
|
||||
<span class="param-value">{{ formatTime(item.endDate) }}</span>
|
||||
</div>
|
||||
<div class="param-line full-width" v-if="item.remark">
|
||||
<span class="param-label">停机原因:</span>
|
||||
<span class="param-value">{{ item.remark }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="primary"
|
||||
icon="el-icon-edit"
|
||||
@click.stop="handleEdit(item)"
|
||||
>编辑</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
:loading="item.deleteLoading"
|
||||
@click.stop="handleDelete(item)"
|
||||
>删除</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div v-if="tableData.length === 0 && !tableLoading" class="empty-data">
|
||||
<el-empty description="暂无数据"></el-empty>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 编辑/新增弹窗 -->
|
||||
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="60%" :close-on-click-modal="false">
|
||||
<el-form :model="formData" ref="formData" label-width="120px" :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 class="app-container">
|
||||
<StopPage :baseURL="baseURL" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import createFetch from '@/api/l2/stop'; // 导入接口
|
||||
import StopPage from '../panels/stop/index.vue'
|
||||
|
||||
export default {
|
||||
name: 'StoppageManagement',
|
||||
components: {
|
||||
StopPage
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 查询表单数据
|
||||
queryForm: {
|
||||
// 只保留年月日YYYY-mm-dd
|
||||
startDate: '2023-08-13',
|
||||
endDate: '2025-08-20'
|
||||
},
|
||||
activeUrl: '140.143.206.120:18081',
|
||||
// 表格数据
|
||||
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: {},
|
||||
stopApi: undefined,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
// 页面加载时默认查询一次
|
||||
this.stopApi = createFetch(this.activeUrl);
|
||||
this.getStoppageList();
|
||||
},
|
||||
methods: {
|
||||
// 获取停机记录列表
|
||||
getStoppageList() {
|
||||
this.tableLoading = true;
|
||||
this.stopApi.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;
|
||||
},
|
||||
// 格式化时间
|
||||
formatTime(time) {
|
||||
if (!time) return '-';
|
||||
// 如果是日期时间格式,直接返回
|
||||
if (time.includes(' ')) {
|
||||
return time;
|
||||
}
|
||||
// 如果是日期格式,返回日期
|
||||
return time;
|
||||
},
|
||||
|
||||
// 新增
|
||||
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.stopApi.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.stopApi.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('保存失败,请稍后重试');
|
||||
});
|
||||
}
|
||||
});
|
||||
baseURL: '140.143.206.120:18081'
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.stoppage-management {
|
||||
height: calc(100vh - 84px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 15px;
|
||||
background: #f5f5f5;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.stop-header {
|
||||
background: #ffffff;
|
||||
border: 1px solid #d4d4d4;
|
||||
border-radius: 2px;
|
||||
padding: 15px;
|
||||
margin-bottom: 15px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.card-grid-container {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
padding-right: 5px;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #c0c0c0;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
}
|
||||
|
||||
.card-col {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.parameter-card {
|
||||
border: 1px solid #d4d4d4;
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
|
||||
&.card-selected {
|
||||
border-color: #999;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
::v-deep .el-card__header {
|
||||
padding: 6px 8px;
|
||||
background: #f8f8f8;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: #999;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
.card-header {
|
||||
.card-title {
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
color: #333;
|
||||
margin-bottom: 2px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.card-subtitle {
|
||||
font-size: 11px;
|
||||
color: #888;
|
||||
line-height: 1.3;
|
||||
}
|
||||
}
|
||||
|
||||
.card-body {
|
||||
.param-groups-row {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.param-group {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
||||
.group-title {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: #555;
|
||||
padding: 3px 0;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
margin-bottom: 4px;
|
||||
background: #f5f5f5;
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
margin-left: -4px;
|
||||
margin-right: -4px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.param-list {
|
||||
.param-line {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 3px 0;
|
||||
border-bottom: 1px dotted #e8e8e8;
|
||||
|
||||
&.full-width {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
|
||||
.param-label {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.param-value {
|
||||
text-align: left;
|
||||
word-break: break-word;
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.param-label {
|
||||
color: #777;
|
||||
font-size: 11px;
|
||||
flex-shrink: 0;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.param-value {
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
font-size: 11px;
|
||||
text-align: right;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 6px;
|
||||
padding-top: 6px;
|
||||
border-top: 1px solid #e0e0e0;
|
||||
margin-top: 6px;
|
||||
|
||||
.el-button {
|
||||
font-size: 11px;
|
||||
padding: 2px 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.empty-data {
|
||||
text-align: center;
|
||||
padding: 40px 0;
|
||||
}
|
||||
|
||||
::v-deep .el-form-item {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user