Files
klp-oa/klp-ui/src/views/wms/report/components/coilTable/index.vue
砂糖 fa198181ee feat(wms): 新增报表透视表组件并优化操作状态显示
refactor: 替换select为tag显示操作状态
feat: 添加今天按钮到时间范围选择器
fix: 移除调试用的console.log
style: 调整按钮间距样式
feat: 新增厂家材质透视表和宽度厚度统计表
refactor: 优化导出功能去除orderBy参数
feat: 添加表面处理等查询条件
feat: 在coilTable中添加settings插槽
feat: 使用下拉菜单整合报表操作按钮
2026-05-05 14:52:24 +08:00

335 lines
10 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">
<div class="filter-section">
<el-input v-model="filterKeyword" placeholder="输入关键词筛选" clearable @input="handleFilterChange"
style="width: 200px; margin-right: 10px" />
<el-select v-model="filterColumn" placeholder="选择筛选字段" @change="handleFilterChange"
style="width: 200px; margin-right: 10px" multiple collapse-tags>
<el-option v-for="column in columns" :key="column.prop" :label="column.title" :value="column.prop" />
</el-select>
</div>
<div class="sort-section">
<el-select v-model="sortField" placeholder="选择排序字段" @change="handleSortChange"
style="width: 150px; margin-right: 10px">
<el-option v-for="column in columns" :key="column.prop" :label="column.title" :value="column.prop" />
</el-select>
<el-select v-model="sortDirection" placeholder="选择排序方向" @change="handleSortChange" style="width: 100px">
<el-option label="升序" value="asc" />
<el-option label="降序" value="desc" />
</el-select>
</div>
<el-pagination layout="total, sizes, prev, pager, next, jumper" :total="total" :page-size.sync="pageSize"
: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 === 'qualityStatus'">
<div @click="handleQualityStatusClick(scope.row)" style="cursor: pointer; background-color: #f5f7fa;">
<dict-tag :options="dict.type.coil_quality_status" :value="scope.row.qualityStatus"></dict-tag>
</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" 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'
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: {}
})
}
},
dicts: ['coil_quality_status', 'coil_abnormal_code', 'coil_abnormal_degree'],
data() {
return {
pageNum: 1,
pageSize: 1000,
// 排序相关
sortField: '',
sortDirection: 'asc',
// 筛选相关
filterKeyword: '',
filterColumn: [],
abmornal: {
visible: false,
data: {},
loading: false,
}
}
},
mounted() {
// 默认选中所有列
this.filterColumn = this.columns.map(column => column.prop)
},
computed: {
// 处理排序和筛选后的数据
processedData() {
let result = [...this.data]
// 筛选逻辑
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() {
return this.processedData.slice((this.pageNum - 1) * this.pageSize, this.pageNum * this.pageSize)
},
// 计算总页数
totalPage() {
return Math.ceil(this.processedData.length / this.pageSize)
},
// 计算总条数
total() {
return this.processedData.length
},
// 是否展示分页组件
showPagination() {
return this.totalPage > 1
}
},
methods: {
// 分页大小改变时触发
handleSizeChange(val) {
this.pageSize = val
this.pageNum = 1
},
// 分页当前页改变时触发
handleCurrentChange(val) {
this.pageNum = 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
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 (!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 : {}
}
}
}
</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>