Files
klp-mono/apps/hand-factory/components/klp-ui/k-table/k-table.vue
2025-10-27 13:21:43 +08:00

331 lines
7.8 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.

<!-- components/uni-table/uni-table.vue -->
<template>
<view class="uni-table-container">
<!-- 横向表格模式 -->
<scroll-view
v-if="mode === 'horizontal'"
scroll-x
class="horizontal-table"
:style="{ maxHeight: height }"
>
<view class="table-header">
<view
v-for="(col, index) in columns"
:key="index"
class="header-cell"
:style="getHeaderStyle(col)"
>
{{ col.title }}
</view>
</view>
<view
v-for="(row, rowIndex) in data"
:key="rowIndex"
class="table-row"
:class="{ 'row-hover': rowHover }"
@click="handleRowClick(row, rowIndex)"
>
<view
v-for="(col, colIndex) in columns"
:key="colIndex"
class="body-cell"
:style="getCellStyle(col)"
>
<slot
v-if="col.slot"
:name="col.slot"
:row="row"
:column="col"
></slot>
<text v-else>{{ formatCellValue(row[col.key], col) }}</text>
</view>
</view>
<view v-if="data.length === 0" class="empty-tip">
<u-empty mode="data" icon="/static/empty.png"/>
</view>
</scroll-view>
<!-- 纵向表格模式 -->
<scroll-view
v-else
scroll-y
class="vertical-table"
:style="{ maxHeight: height }"
>
<view
v-for="(row, rowIndex) in data"
:key="rowIndex"
class="vertical-row"
:class="{ 'row-hover': rowHover }"
@click="handleRowClick(row, rowIndex)"
>
<view
v-for="(col, colIndex) in columns"
:key="colIndex"
class="vertical-cell"
>
<view class="cell-label">{{ col.title }}</view>
<view class="cell-value">
<slot
v-if="col.slot"
:name="col.slot"
:row="row"
:column="col"
></slot>
<text v-else>{{ formatCellValue(row[col.key], col) }}</text>
</view>
</view>
</view>
<view v-if="data.length === 0" class="empty-tip">
<u-empty mode="data" icon="/static/empty.png"/>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
name: 'UniTable',
props: {
// 表格模式horizontal横向/vertical纵向
mode: {
type: String,
default: 'horizontal',
validator: value => ['horizontal', 'vertical'].includes(value)
},
// 列配置
columns: {
type: Array,
required: true,
},
// 表格数据
data: {
type: Array,
default: () => []
},
// 固定高度支持px/rpx
height: {
type: String,
default: '1000rpx'
},
// 是否显示边框
border: {
type: Boolean,
default: true
},
// 是否显示行hover效果
rowHover: {
type: Boolean,
default: true
},
// 是否显示斑马纹
stripe: {
type: Boolean,
default: false
},
// 是否显示序号列
showIndex: {
type: Boolean,
default: false
},
// 序号列标题
indexLabel: {
type: String,
default: '序号'
}
},
computed: {
// 处理后的列配置
processedColumns() {
let cols = [...this.columns]
if (this.showIndex) {
cols.unshift({
title: this.indexLabel,
key: '_index',
width: '80rpx',
align: 'center'
})
}
return cols
}
},
methods: {
// 获取表头样式
getHeaderStyle(col) {
return {
width: col.width || '200rpx',
textAlign: col.align || 'left',
flex: col.flex ? `0 0 ${col.width}` : 'none',
backgroundColor: this.border ? '#f5f7fa' : 'transparent'
}
},
// 获取单元格样式
getCellStyle(col) {
return {
width: col.width || '200rpx',
textAlign: col.align || 'left',
flex: col.flex ? `0 0 ${col.width}` : 'none'
}
},
// 行点击事件
handleRowClick(row, index) {
this.$emit('row-click', row, index)
},
// 格式化单元格值
formatCellValue(value, col) {
if (value === undefined || value === null) {
return '--'
}
// 如果列定义了format函数使用format函数处理
if (col.format && typeof col.format === 'function') {
return col.format(value)
}
// 如果是日期类型
if (col.type === 'date') {
return this.formatDate(value, col.dateFormat)
}
// 如果是数字类型
if (col.type === 'number') {
return this.formatNumber(value, col.precision)
}
return value
},
// 格式化日期
formatDate(value, format = 'YYYY-MM-DD') {
if (!value) return '--'
// 这里可以使用日期格式化库如dayjs
return value
},
// 格式化数字
formatNumber(value, precision) {
if (typeof value !== 'number') return value
return precision !== undefined ? value.toFixed(precision) : value
}
}
}
</script>
<style lang="scss" scoped>
.uni-table-container {
background-color: #fff;
border-radius: 12rpx;
overflow: hidden;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
.horizontal-table {
.table-header {
display: flex;
background-color: #f5f7fa;
border-bottom: 2rpx solid #ebeef5;
position: sticky;
top: 0;
z-index: 1;
.header-cell {
padding: 24rpx;
font-weight: 500;
color: #303133;
white-space: nowrap;
transition: background-color 0.3s;
position: relative;
&::after {
content: '';
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
height: 50%;
width: 2rpx;
background-color: #ebeef5;
}
}
}
.table-row {
display: flex;
border-bottom: 1rpx solid #ebeef5;
transition: background-color 0.3s;
&:last-child {
border-bottom: none;
}
&.row-hover:active {
background-color: #f5f7fa;
}
.body-cell {
padding: 24rpx;
color: #606266;
word-break: break-all;
display: flex;
align-items: center;
}
}
}
.vertical-table {
.vertical-row {
padding: 24rpx;
border-bottom: 1rpx solid #ebeef5;
transition: background-color 0.3s;
&:last-child {
border-bottom: none;
}
&.row-hover:active {
background-color: #f5f7fa;
}
.vertical-cell {
display: flex;
margin-bottom: 20rpx;
align-items: flex-start;
&:last-child {
margin-bottom: 0;
}
.cell-label {
width: 200rpx;
color: #909399;
flex-shrink: 0;
font-size: 28rpx;
}
.cell-value {
flex: 1;
color: #303133;
word-break: break-all;
font-size: 28rpx;
padding-left: 20rpx;
}
}
}
}
.empty-tip {
padding: 80rpx 0;
display: flex;
justify-content: center;
align-items: center;
background-color: #fff;
}
}
</style>