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

699 lines
20 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

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

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