pet/uni_modules/uview-next/components/u-table-column/u-table-column.vue

386 lines
12 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>
<view v-if="show" class="u-table__column" :style="[columnStyle]">
<block v-for="(row, index) in tableData" :key="index">
<view class="u-table__cell" :style="[cellStyles(row, index)]" :class="[cellClasses(row, index)]"
@click="handleCellClick(row, index)" v-if="!isCellHidden(row, index)">
<view class="u-table__value" :class="{ 'is-ellipsis': isEllipsis }">
<slot :row="row" :column="columnConfig" :index="index" :value="row">
<template v-if="type === 'index'">
{{ index + 1 }}
</template>
<template v-else-if="type === 'selection'">
<u-checkbox :checked="isSelectionSelected(row)"
@change="(checked) => handleSelectionChange(row, checked)"></u-checkbox>
</template>
<template v-else>
{{ getCellValue(row) }}
</template>
</slot>
</view>
</view>
</block>
</view>
</template>
<script>
import props from './props.js';
import mixin from '../../libs/mixin/mixin'
import mpMixin from '../../libs/mixin/mpMixin';
/**
* table-col 表格列
* @description 表格列组件需要与u-table组件配合使用
* @tutorial https://www.uviewui.com/components/table.html
* @property {String} prop 字段名称,对应列内容的字段名 (默认 '' )
* @property {String} label 显示的标题 (默认 '' )
* @property {String|Number} width 列宽度 (默认 '' )
* @property {String|Number} minWidth 列最小宽度 (默认 '' )
* @property {Boolean} sortable 是否可排序 (默认 false )
* @property {Boolean} fixed 是否固定列 (默认 false )
* @property {String} align 对齐方式 (默认 'left' )
* @property {String} type 列类型selection/index (默认 '' )
* @property {Function} formatter 格式化函数 (默认 null )
* @property {Boolean} show 是否显示 (默认 true )
* @property {String} headerAlign 表头对齐方式 (默认 'left' )
* @property {Boolean} headerEllipsis 表头超出1行隐藏 (默认 false )
* @property {String} columnKey 列的key (默认 '' )
* @property {String} className 列的类名 (默认 '' )
* @property {Object} customStyle 定义需要用到的外部样式
* @example <u-table-column prop="name" label="姓名" width="100"></u-table-column>
*/
export default {
name: 'u-table-column',
mixins: [mpMixin, mixin, props],
data() {
return {
parentData: {
data: [],
border: false,
stripe: false,
rowHeight: null,
ellipsis: true,
cellStyle: {},
selectedRowKeys: [],
toggleRowSelection: null,
mergeInfo: {}
},
sortDirection: 0,
innerFormatter: (value) => value
};
},
computed: {
columnConfig() {
return {
prop: this.prop,
type: this.type,
label: this.label,
width: this.width,
minWidth: this.minWidth,
sortable: this.sortable,
align: this.align,
type: this.type,
formatter: this.formatter,
sortDirection: this.sortDirection
};
},
tableData() {
return this.parentData.data || [];
},
isEllipsis() {
return this.parentData.ellipsis;
},
columnStyle() {
const style = {};
if (this.width) {
style.width = this.$u.addUnit(this.width);
} else if (this.minWidth) {
style.minWidth = this.$u.addUnit(this.minWidth);
} else {
style.flex = 1;
}
return style;
},
// 响应式的行选中状态映射
isSelectionSelected() {
return (row) => {
if (this.parentData.selectedRowKeys && this.parentData.selectedRowKeys.length > 0) {
const rowKey = this.getRowKey(row);
return this.parentData.selectedRowKeys.includes(rowKey);
}
return false;
}
},
cellStyles(row, index) {
return (row, index) => {
const style = {};
if (this.parentData.rowHeight) {
style.height = this.$u.addUnit(this.parentData.rowHeight);
}
// 处理合并单元格样式
if (this.parentData.mergeInfo && this.prop) {
const mergeKey = `${index}-${this.prop}`;
const mergeData = this.parentData.mergeInfo[mergeKey];
if (mergeData) {
// 设置合并单元格的高度
if (mergeData.rowspan > 1) {
const rowHeight = parseInt(this.parentData.rowHeight);
style.height = this.$u.addUnit(rowHeight * mergeData.rowspan + 1);
}
// 设置显示状态
if (mergeData.display === 'none') {
style.display = 'none';
}
}
}
return uni.$u.deepMerge(style, uni.$u.addStyle(this.parentData.cellStyle));
}
},
cellClasses(row, index) {
return (row, index) => {
let classes = [
this.className,
`is-${this.align || 'left'}`,
];
if (this.parentData.border) {
classes.push('is-border');
}
if (this.parentData.fixed) {
classes.push('is-fixed');
}
if (this.parentData.stripe && index % 2 === 1) {
classes.push('is-stripe');
}
// 添加合并单元格的样式类
if (this.parentData.mergeInfo && this.prop) {
const mergeKey = `${index}-${this.prop}`;
const mergeData = this.parentData.mergeInfo[mergeKey];
if (mergeData && mergeData.rowspan > 1) {
classes.push('is-merged');
}
}
// #ifdef MP-ALIPAY || MP-TOUTIAO
classes = classes.join(' ')
// #endif
return classes;
}
},
},
mounted() {
this.init()
},
// #ifdef VUE3
emits: ['cellClick'],
// #endif
methods: {
init() {
this.updateParentData();
if (!this.parent) {
uni.$u.error('u-table-column必须搭配u-table组件使用');
return;
}
},
updateParentData() {
this.getParentData('u-table');
},
// 获取行的唯一标识(与父组件保持一致)
getRowKey(row) {
if (!row) return '';
// 这里需要与父组件的rowKey属性保持一致
const rowKeyProp = this.parent?.rowKey || 'id';
return this.getValueByPath(row, rowKeyProp) || JSON.stringify(row);
},
// 获取嵌套属性值
getValueByPath(obj, path) {
if (!path || !obj) return '';
const keys = path.split('.');
let value = obj;
for (const key of keys) {
if (value && typeof value === 'object') {
value = value[key];
} else {
value = '';
break;
}
}
return value;
},
// 处理选择状态变化
handleSelectionChange(row, checked) {
if (typeof this.parentData.toggleRowSelection === 'function') {
this.parentData.toggleRowSelection(row, checked);
}
},
// 处理展开点击
handleExpandClick(row) {
if (typeof this.parentData.toggleRowExpansion === 'function') {
this.parentData.toggleRowExpansion(row);
}
},
handleCellClick(row, rowIndex) {
// 如果是selection或expand类型不触发cell-click事件
if (this.type === 'selection' || this.type === 'expand') {
return;
}
const cellData = {
row,
column: this.columnConfig,
rowIndex,
columnIndex: this.getColumnIndex(),
value: this.getCellValue(row)
};
this.$emit('cellClick', cellData);
if (this.parent) {
this.parent.$emit('cellClick', cellData);
}
},
getCellValue(row) {
if (!this.prop) return '';
const keys = this.prop.split('.');
let value = row;
for (const key of keys) {
if (value && typeof value === 'object') {
value = value[key];
} else {
value = '';
break;
}
}
const formatter = this.formatter || this.innerFormatter;
return formatter(value);
},
getColumnIndex() {
if (!this.parent || !this.parent.children) return 0;
return this.parent.children.findIndex(col => col.prop === this.prop);
},
isCellHidden(row, index) {
// 检查合并信息,如果该单元格被合并到其他单元格中,则隐藏
if (this.parentData.mergeInfo && this.prop) {
const mergeKey = `${index}-${this.prop}`;
const mergeData = this.parentData.mergeInfo[mergeKey];
if (mergeData && mergeData.display === 'none') {
return true;
}
}
return false;
}
}
};
</script>
<style lang="scss" scoped>
@import '../../libs/css/components.scss';
.u-table {
&__column {
@include flex(column);
flex-shrink: 0;
&:last-child .is-border {
border-right: none;
}
}
&__cell {
@include flex(column);
padding: 0px 10px;
align-items: center;
justify-content: center;
position: relative;
&.is-left {
align-items: flex-start;
}
&.is-center {
align-items: center;
}
&.is-right {
align-items: flex-end;
}
&.is-border {
border-right: 1px solid $u-border-color;
border-bottom: 1px solid $u-border-color;
&:last-child {
border-bottom: none;
}
}
&.is-stripe {
background-color: $u-bg-color;
}
&.is-merged {
position: relative;
z-index: 1;
background-color: inherit;
}
}
&__value {
font-size: 13px;
line-height: 15px;
&.is-ellipsis {
word-break: break-all;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
}
&__expand-icon {
@include flex(row);
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
cursor: pointer;
transition: transform 0.2s ease;
&.is-expanded {
transform: rotate(90deg);
}
&:hover {
background-color: rgba(0, 0, 0, 0.05);
border-radius: 2px;
}
}
}
</style>