Files
klp-oa/klp-ui/src/views/wms/report/components/coilTable/index.vue
砂糖 fd50118161 refactor(contract/export/preview): 优化合同打印导出功能,添加页码和修复样式
1.  移除coilTable的异常行高亮逻辑
2.  修复导航栏告警badge的显示逻辑
3.  为合同导出和预览添加页码标注,调整打印样式
4.  移除冗余的合同打印预览代码
2026-06-10 11:29:41 +08:00

399 lines
13 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="coil-table">
<div class="table-controls">
<el-pagination layout="total, sizes, prev, pager, next, jumper" :total="paginationTotal" :page-size="effectivePageSize"
:current-page="pageNum" :page-sizes="[10, 20, 50, 100, 200, 500, 1000]" @size-change="handleSizeChange"
@current-change="handleCurrentChange" />
<slot name="settings"></slot>
</div>
<el-table :data="tableData" style="width: 100%" height="calc(100vh - 320px)" border row-key="coilId" :row-class-name="getRowClassName">
<el-table-column v-for="column in columns" :key="column.prop" :prop="column.prop" :label="column.title"
:width="column.width" :align="column.align">
<template slot-scope="scope">
<!-- 特殊 prop 渲染逻辑 -->
<template v-if="column.prop === 'enterCoilNo'">
<coil-no :coil-no="scope.row.enterCoilNo"></coil-no>
</template>
<template v-else-if="column.prop === 'currentCoilNo'">
<current-coil-no :current-coil-no="scope.row.currentCoilNo"></current-coil-no>
</template>
<template v-else-if="column.prop === 'itemId'">
<ProductInfo v-if="scope.row.itemType == 'product'" :product="scope.row" />
<RawMaterialInfo v-else-if="scope.row.itemType === 'raw_material'" :material="scope.row" />
</template>
<template v-else-if="column.prop === 'status'">
{{ scope.row.status === 0 ? '在库' : '已出库' }}
</template>
<!-- 生产耗时单位分钟渲染为xx天xx小时xx分钟 -->
<template v-else-if="column.prop === 'productionDuration'">
{{ formatProductionDuration(scope.row.productionDuration) }}
</template>
<!-- 质量状态点击后会出现弹窗显示详细信息 -->
<template v-else-if="column.prop === 'lengthDiff'">
<span :style="{ color: isLengthAbnormal(scope.row) ? 'red' : '' }">
{{ scope.row.lengthDiff != null ? scope.row.lengthDiff.toFixed(3) : '' }}
<template v-if="scope.row.lengthDiff != null && scope.row.theoreticalLength">
({{ (Math.abs(scope.row.lengthDiff) / scope.row.theoreticalLength).toFixed(3) }})
</template>
</span>
</template>
<template v-else-if="column.prop === 'thicknessDiff'">
<span :style="{ color: isThicknessAbnormal(scope.row) ? 'red' : '' }">
{{ scope.row.thicknessDiff != null ? scope.row.thicknessDiff.toFixed(3) : '' }}
</span>
</template>
<template v-else-if="column.prop === 'qualityStatus'">
<div @click="handleQualityStatusClick(scope.row)" style="cursor: pointer; background-color: #f5f7fa;">
<dict-tag v-if="scope.row.qualityStatus" :options="dict.type.coil_quality_status" :value="scope.row.qualityStatus"></dict-tag>
<span v-else>暂未判级</span>
</div>
</template>
<!-- 默认渲染 -->
<template v-else>
{{ scope.row[column.prop] }}
</template>
</template>
</el-table-column>
</el-table>
<el-dialog title="钢卷异常信息" :visible.sync="abmornal.visible" width="60%">
<abnormal-table ref="abnormalTable" :list="abmornal.data"
:editable="false" :show-coil="false" :coil-info="abmornal.currentCoil" v-loading="abmornal.loading">
</abnormal-table>
</el-dialog>
</div>
</template>
<script>
import ProductInfo from "@/components/KLPService/Renderer/ProductInfo";
import RawMaterialInfo from "@/components/KLPService/Renderer/RawMaterialInfo";
import CoilNo from "@/components/KLPService/Renderer/CoilNo.vue";
import { listCoilAbnormal } from '@/api/wms/coilAbnormal'
import AbnormalTable from '@/views/wms/coil/components/AbnormalTable.vue'
import { getConfigKey } from '@/api/system/config'
export default {
name: 'CoilTable',
components: {
ProductInfo,
RawMaterialInfo,
CoilNo,
AbnormalTable,
},
props: {
columns: {
type: Array,
default: () => [],
},
data: {
type: Array,
default: () => [],
},
// 高亮配置
highlightConfig: {
type: Object,
default: () => ({
// 需要高亮的行的条件函数或行ID数组
rows: [],
// 高亮样式类名
className: 'highlight-row',
// 自定义高亮样式
style: {}
})
},
// 服务端分页总数(>0 时开启服务端分页模式)
total: {
type: Number,
default: 0
},
// 服务端分页每页条数
pageSize: {
type: Number,
default: 0
}
},
dicts: ['coil_quality_status', 'coil_abnormal_code', 'coil_abnormal_degree'],
data() {
return {
pageNum: 1,
innerPageSize: 1000,
// 排序相关
sortField: '',
sortDirection: 'asc',
// 筛选相关
filterKeyword: '',
filterColumn: [],
abmornal: {
visible: false,
data: {},
loading: false,
currentCoil: {},
},
lengthThreshold: 0,
thicknessThreshold: 0,
thicknessMaxThreshold: 0,
thicknessMinThreshold: 0,
}
},
mounted() {
// 默认选中所有列
this.filterColumn = this.columns.map(column => column.prop)
this.getAlarmThreshold()
},
computed: {
isServerPagination() {
return this.total > 0
},
effectivePageSize() {
return this.pageSize > 0 ? this.pageSize : this.innerPageSize
},
enrichedData() {
return this.data.map(row => ({
...row,
lengthDiff: (row.actualLength || 0) - (row.theoreticalLength || 0),
thicknessDiff: (row.theoreticalThickness || 0) - (row.computedThickness || 0)
}))
},
// 处理排序和筛选后的数据(服务端分页模式下跳过筛选排序)
processedData() {
if (this.isServerPagination) {
return this.enrichedData
}
let result = [...this.enrichedData]
// 筛选逻辑
if (this.filterColumn.length > 0 && this.filterKeyword) {
const keyword = this.filterKeyword.toLowerCase()
result = result.filter(item => {
// 只要有一个字段匹配,就保留该记录
return this.filterColumn.some(column => {
const value = item[column]
if (value === null || value === undefined) return false
return String(value).toLowerCase().includes(keyword)
})
})
}
// 排序逻辑
if (this.sortField) {
result.sort((a, b) => {
const aValue = a[this.sortField]
const bValue = b[this.sortField]
// 处理null和undefined
if (aValue === null || aValue === undefined) return 1
if (bValue === null || bValue === undefined) return -1
// 字符串比较
if (typeof aValue === 'string' && typeof bValue === 'string') {
return this.sortDirection === 'asc'
? aValue.localeCompare(bValue)
: bValue.localeCompare(aValue)
}
// 数字比较
if (typeof aValue === 'number' && typeof bValue === 'number') {
return this.sortDirection === 'asc'
? aValue - bValue
: bValue - aValue
}
// 其他类型比较
return this.sortDirection === 'asc'
? String(aValue).localeCompare(String(bValue))
: String(bValue).localeCompare(String(aValue))
})
}
return result
},
// 内部实现前端分页逻辑(服务端分页模式下直接返回数据)
tableData() {
if (this.isServerPagination) {
return this.enrichedData
}
return this.processedData.slice((this.pageNum - 1) * this.effectivePageSize, this.pageNum * this.effectivePageSize)
},
// 计算总页数
totalPage() {
const total = this.isServerPagination ? this.total : this.processedData.length
return Math.ceil(total / this.effectivePageSize)
},
// 计算总条数
pageTotal() {
return this.processedData.length
},
// 分页总条数(服务端优先)
paginationTotal() {
return this.isServerPagination ? this.total : this.pageTotal
},
// 是否展示分页组件
showPagination() {
if (this.isServerPagination) return true
return this.totalPage > 1
}
},
methods: {
// 分页大小改变时触发
handleSizeChange(val) {
if (this.isServerPagination) {
this.$emit('size-change', val)
} else {
this.innerPageSize = val
}
this.pageNum = 1
},
// 分页当前页改变时触发
handleCurrentChange(val) {
this.pageNum = val
if (this.isServerPagination) {
this.$emit('current-change', val)
}
},
// 生产耗时单位分钟渲染为xx天xx小时xx分钟
formatProductionDuration(duration) {
if (!duration || isNaN(duration)) {
return '0分钟'
}
const days = Math.floor(duration / 1440)
const hours = Math.floor((duration - days * 1440) / 60)
const minutes = duration - days * 1440 - hours * 60
let result = ''
if (days > 0) {
result += `${days}`
}
if (hours > 0) {
result += `${hours}小时`
}
if (minutes > 0 || (days === 0 && hours === 0)) {
result += `${minutes}分钟`
}
return result
},
// 处理筛选条件变化
handleFilterChange() {
this.pageNum = 1
},
// 处理排序规则变化
handleSortChange() {
this.pageNum = 1
},
handleQualityStatusClick(row) {
this.abmornal.visible = true
this.abmornal.loading = true
this.abmornal.currentCoil = row
listCoilAbnormal({
coilId: row.coilId
}).then(response => {
this.abmornal.data = response.rows || []
}).catch(error => {
console.error('查询异常记录失败:', error)
this.$message.error('查询异常记录失败: ' + (error.message || error))
}).finally(() => {
this.abmornal.loading = false
})
},
// 获取行类名
getRowClassName({ row, rowIndex }) {
const { rows } = this.highlightConfig
// if (this.isLengthAbnormal(row) || this.isThicknessAbnormal(row)) {
// return 'warning-row'
// }
if (!rows || (Array.isArray(rows) && rows.length === 0)) {
return ''
}
// 检查是否需要高亮
let shouldHighlight = false
if (typeof rows === 'function') {
// 如果是函数,调用函数判断
shouldHighlight = rows(row, rowIndex)
} else if (Array.isArray(rows)) {
// 如果是数组检查row.coilId是否在数组中
shouldHighlight = rows.includes(row.coilId)
console.log('判断是否应该高亮', shouldHighlight, rows, row.coilId)
}
return shouldHighlight ? 'warning-row' : ''
},
// 获取行样式
getRowStyle({ row, rowIndex }) {
const { rows, style } = this.highlightConfig
if (!rows || (Array.isArray(rows) && rows.length === 0)) {
return {}
}
// 检查是否需要高亮
let shouldHighlight = false
if (typeof rows === 'function') {
shouldHighlight = rows(row, rowIndex)
} else if (Array.isArray(rows)) {
shouldHighlight = rows.includes(row.coilId)
}
return shouldHighlight ? style : {}
},
isLengthAbnormal(row) {
const theoreticalLength = row.theoreticalLength || 1
return Math.abs(row.lengthDiff) / theoreticalLength > this.lengthThreshold
},
isThicknessAbnormal(row) {
return row.thicknessDiff > this.thicknessThreshold
},
// 获取报警阈值参数
getAlarmThreshold() {
getConfigKey('material.warning.length').then(response => { this.lengthThreshold = response.msg || 0 })
getConfigKey('material.warning.thickness').then(response => { this.thicknessThreshold = response.msg || 0 })
// getConfigKey('material.warning.Maxthickness').then(response => { this.thicknessMaxThreshold = response.msg || 0 })
// getConfigKey('material.warning.Minthickness').then(response => { this.thicknessMinThreshold = response.msg || 0 })
}
}
}
</script>
<style scoped>
.coil-table {
padding: 10px;
}
.table-controls {
display: flex;
justify-content: flex-end;
align-items: center;
margin-bottom: 15px;
flex-wrap: wrap;
gap: 10px;
}
.filter-section,
.sort-section {
display: flex;
align-items: center;
gap: 10px;
}
.el-pagination {
margin-top: 0px !important;
}
@media screen and (max-width: 768px) {
.table-controls {
flex-direction: column;
align-items: flex-end;
}
.filter-section,
.sort-section {
width: 100%;
}
}
/* 行高亮样式 */
::v-deep .el-table__row.warning-row {
background: oldlace !important;
}
</style>