156 lines
3.4 KiB
Vue
156 lines
3.4 KiB
Vue
<template>
|
||
<view class="table u-font-28">
|
||
<!-- 表头:根据 columns 渲染 + 全选框 -->
|
||
<view class="u-flex u-row-between no-wrap title">
|
||
<!-- 全选框 -->
|
||
<view class="table-th">
|
||
<up-checkbox @change="handleSelectAll" usedAlone v-model:checked="internalAllChecked" shape="square"
|
||
:size="20" />
|
||
</view>
|
||
|
||
<!-- 动态列标题 -->
|
||
<view class="table-th" v-for="col in columns" :key="col.key" :style="col.style || {}">
|
||
{{ col.title }}
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 有数据 → 渲染列表 -->
|
||
<template v-if="internalData.length > 0">
|
||
<view @click="handleRowCheck(item)" class="u-m-t-12 u-flex u-p-24 u-row-between row"
|
||
v-for="(item, index) in internalData" :key="item.id || index" row-key="id">
|
||
<view class="table-td">
|
||
<up-checkbox @change="handleRowCheck(item)" usedAlone v-model:checked="item._checked" shape="square" :size="20"></up-checkbox>
|
||
</view>
|
||
|
||
<view class="table-td" v-for="col in columns" :key="col.key" :style="col.style || {}">
|
||
<slot v-if="col.slot" :name="col.slot" :row="item" />
|
||
<template v-else>{{ item[col.key] }}</template>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<!-- 无数据 → 显示暂无数据 -->
|
||
<view v-else class="empty-text u-p-t-40 u-p-b-40 u-text-center">
|
||
暂无数据
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import {
|
||
ref,
|
||
computed,
|
||
watch
|
||
} from 'vue';
|
||
|
||
// 完全对齐 u-table2 的 props
|
||
const props = defineProps({
|
||
data: {
|
||
type: Array,
|
||
default: () => []
|
||
},
|
||
columns: {
|
||
type: Array,
|
||
default: () => []
|
||
},
|
||
rowKey: {
|
||
type: String,
|
||
default: 'id'
|
||
}
|
||
});
|
||
|
||
// 抛出和 u-table2 一样的 selection-change 事件
|
||
const emits = defineEmits(['selection-change']);
|
||
|
||
// 内部数据(增加选中状态 _checked)
|
||
const internalData = ref([]);
|
||
|
||
// 全选状态
|
||
const internalAllChecked = ref(false);
|
||
|
||
// 监听外部 data 变化,自动注入选中状态
|
||
watch(
|
||
() => props.data,
|
||
(val) => {
|
||
internalData.value = val.map(item => ({
|
||
...item,
|
||
_checked: item._checked || false
|
||
}));
|
||
}, {
|
||
immediate: true,
|
||
deep: true
|
||
}
|
||
);
|
||
|
||
// 全选切换
|
||
function handleSelectAll() {
|
||
internalAllChecked.value = !internalAllChecked.value
|
||
const checked = internalAllChecked.value;
|
||
internalData.value = internalData.value.map(item => {
|
||
item._checked = checked;
|
||
return item;
|
||
})
|
||
console.log('internalData.value', internalData.value);
|
||
emitChange();
|
||
}
|
||
|
||
// 行选中切换
|
||
function handleRowCheck(row) {
|
||
row._checked = !row._checked;
|
||
emitChange();
|
||
}
|
||
|
||
// 抛出选中结果(和 u-table2 行为一致)
|
||
function emitChange() {
|
||
const selectedRows = internalData.value.filter(item => item._checked);
|
||
emits('selection-change', selectedRows);
|
||
}
|
||
|
||
|
||
// 监听所有行选中状态,自动更新全选框
|
||
watch(
|
||
() => internalData.value,
|
||
() => {
|
||
const total = internalData.value.length;
|
||
const checkedCount = internalData.value.filter(i => i._checked).length;
|
||
internalAllChecked.value = total > 0 && checkedCount === total;
|
||
}, {
|
||
deep: true
|
||
}
|
||
);
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
.table {
|
||
background: #f9f9f9;
|
||
border-radius: 8rpx;
|
||
overflow: hidden;
|
||
|
||
.title {
|
||
padding: 12rpx 24rpx;
|
||
background: #aebad2;
|
||
border-radius: 8rpx 8rpx 0 0;
|
||
color: #fff;
|
||
}
|
||
|
||
.table-th,
|
||
.table-td {
|
||
flex: 1;
|
||
text-align: center;
|
||
}
|
||
|
||
.row {
|
||
cursor: pointer;
|
||
}
|
||
|
||
.row:nth-of-type(2n + 1) {
|
||
background: #f0f0f0;
|
||
}
|
||
|
||
// 空数据样式
|
||
.empty-text {
|
||
font-size: 28rpx;
|
||
color: #999;
|
||
}
|
||
}
|
||
</style> |