151 lines
3.2 KiB
Vue
151 lines
3.2 KiB
Vue
<template>
|
||
<view class="table u-font-28">
|
||
<!-- 表头:根据 columns 渲染 + 全选框 -->
|
||
<view class="u-flex u-row-between no-wrap title">
|
||
<!-- 全选框 -->
|
||
<view class="table-th">
|
||
<my-radio
|
||
@change="handleSelectAll"
|
||
v-model="internalAllChecked"
|
||
shape="square"
|
||
:size="20"
|
||
/>
|
||
</view>
|
||
|
||
<!-- 动态列标题 -->
|
||
<view class="table-th" v-for="col in columns" :key="col.key" :style="col.style || {}">
|
||
{{ col.label }}
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 表格行 -->
|
||
<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">
|
||
<my-radio
|
||
v-model="item._checked"
|
||
shape="square"
|
||
:size="20"
|
||
@change="handleRowCheck(item)"
|
||
/>
|
||
</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>
|
||
</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() {
|
||
const checked = internalAllChecked.value;
|
||
internalData.value.forEach(item => {
|
||
item._checked = checked;
|
||
});
|
||
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;
|
||
}
|
||
}
|
||
</style> |