Files
klp-oa/klp-ui/src/views/wms/report/components/coilTable/index.vue

399 lines
13 KiB
Vue
Raw Normal View History

<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>