Files
xgy-oa/klp-ui/src/views/lines/panels/pdo/index.vue
砂糖 2773c9e8ae feat(pdo): 重构PDO模块结构并优化功能
将PDO模块拆分为面板组件和页面组件,实现代码复用
新增数据修正、标签打印和统计汇总组件
优化图表显示和数据处理逻辑
2026-01-03 16:04:46 +08:00

592 lines
17 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="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>