Skip to content

Commit 7569093

Browse files
author
Tenny
committed
TanstackTable: 多选实现
Signed-off-by: Tenny <tenny.shu@foxmail.com>
1 parent d8498a9 commit 7569093

File tree

8 files changed

+178
-105
lines changed

8 files changed

+178
-105
lines changed

docs/components/checkbox.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,26 @@
55
## 演示
66

77
<script setup lang="ts">
8-
import { ref } from 'vue'
8+
import { ref, watch } from 'vue'
99
import { Checkbox, CheckboxGroup } from '../../src'
1010

1111
const cities= [['CD', '成都'], ['BJ', '北京'], ['SZ', '深圳'], ['HZ', '杭州']]
1212

13+
const isCheckOption = ref(true)
14+
1315
const checkedCities = ref(['CD', 'SZ'])
1416
const checkAll = ref(false)
1517
const isIndeterminate = ref(true)
1618

1719
function handleCheckAllChange(val: boolean) {
18-
checkedCities.value = val ? cities.map(c => c[0]) : []
1920
isIndeterminate.value = false
21+
checkedCities.value = val ? cities.map(c => c[0]) : []
2022
}
2123
function handleGroupChange(val: string[]) {
2224
const checkedCount = val.length
2325
checkAll.value = checkedCount === cities.length
2426
if (checkedCount === 0) {
25-
isIndeterminate.value = 0
27+
isIndeterminate.value = false
2628
} else {
2729
isIndeterminate.value = checkedCount < cities.length
2830
}
@@ -36,8 +38,11 @@
3638
<ClientOnly>
3739
<CodePreview>
3840
<textarea lang="vue-html">
39-
<nt-checkbox label="Option1"></nt-checkbox>
41+
<nt-checkbox label="Option1" v-model="isCheckOption"></nt-checkbox>
4042
</textarea>
43+
<template #preview>
44+
<Checkbox label="Option1" v-model="isCheckOption"></Checkbox>
45+
</template>
4146
</CodePreview>
4247
</ClientOnly>
4348

docs/components/tanstacktable.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,10 @@ npm install @tanstack/vue-table
135135
fixed: 'right'
136136
}
137137
]
138+
139+
const columns2 = [{
140+
type: 'checkbox'
141+
}, ...columns]
138142
</script>
139143

140144
### 基础用法
@@ -301,6 +305,25 @@ npm install @tanstack/vue-table
301305
</CodePreview>
302306
</ClientOnly>
303307

308+
### 选中行
309+
310+
通过将第一列配置 `type=selection` 让行变为可选的。
311+
312+
<ClientOnly>
313+
<CodePreview>
314+
<textarea lang="vue">
315+
<script setup lang="ts">
316+
</script>
317+
<template>
318+
<hr />
319+
</template>
320+
</textarea>
321+
<template #preview>
322+
<TanstackTable :data="data" :columns="columns2"></TanstackTable>
323+
</template>
324+
</CodePreview>
325+
</ClientOnly>
326+
304327
## API
305328

306329
### TanstackTable Props
Lines changed: 92 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,100 @@
1-
<template>
2-
<label
3-
:class="[
4-
'nt-checkbox',
5-
indeterminate ? 'nt-checkbox--indeterminate' : '',
6-
disabled ? 'nt-checkbox--disabled' : '',
7-
type === 'button' ? 'nt-checkbox--button' : '',
8-
]"
9-
>
10-
<input
11-
type="checkbox"
12-
class="nt-checkbox__input"
13-
:name="name"
14-
:checked="isChecked"
15-
@change="handleChange"
16-
:disabled="disabled"
17-
:value="value"
18-
/>
19-
<span v-if="type !== 'button'" class="nt-checkbox__inner"></span>
20-
<span class="nt-checkbox__label">{{ label }}</span>
21-
</label>
22-
</template>
23-
<script setup lang="ts">
24-
import { ref, inject } from 'vue';
1+
<template></template>
2+
<script lang="ts">
3+
import { inject, computed, defineComponent, h } from 'vue';
4+
import type { ModelRef, PropType } from 'vue';
255
26-
const checkedModel = defineModel();
27-
28-
const props = withDefaults(
29-
defineProps<{
6+
export default defineComponent({
7+
props: {
308
/** 设置不确定状态,仅负责样式控制 */
31-
indeterminate?: boolean;
32-
/** 原生 name 属性 */
33-
name?: string;
9+
indeterminate: {
10+
type: Boolean,
11+
default: false,
12+
},
13+
disabled: {
14+
type: Boolean,
15+
default: false,
16+
},
3417
/** 显示的 label 文本 */
35-
label?: string;
36-
disabled?: boolean;
37-
value?: string | number | boolean;
38-
type?: 'button';
39-
}>(),
40-
{
41-
indeterminate: false,
42-
disabled: false,
43-
value: undefined,
18+
label: String,
19+
value: {
20+
type: [String, Number, Boolean],
21+
default: undefined,
22+
},
23+
type: String as PropType<'button'>,
24+
checked: { type: Boolean, default: undefined },
25+
/** 原生 name 属性 */
26+
name: String,
27+
modelValue: {
28+
type: Boolean,
29+
default: false,
30+
},
4431
},
45-
);
32+
emits: ['change', 'update:modelValue'],
33+
setup(props, { emit, slots }) {
34+
const { checkList, updateCheck } = inject<{
35+
checkList: null | ModelRef<(string | number)[], string>;
36+
updateCheck: null | ((value: any) => void);
37+
}>('nt-checkbox-group-check', {
38+
checkList: null,
39+
updateCheck: null,
40+
});
4641
47-
const emits = defineEmits(['change']);
42+
const isChecked = computed(() => {
43+
if (checkList != null) {
44+
return checkList.value.includes(props.value as string);
45+
}
46+
if (props.value != null) {
47+
return props.modelValue === props.value;
48+
}
49+
if (props.checked != null) {
50+
return props.checked;
51+
}
52+
return props.modelValue as boolean;
53+
});
4854
49-
const { checkList, updateCheck } = inject<{
50-
checkList: null | (string | number)[];
51-
updateCheck: null | ((value: any) => void);
52-
}>('nt-checkbox-group-check', {
53-
checkList: null,
54-
updateCheck: null,
55+
function handleChange(e: Event) {
56+
const target = e.target as HTMLInputElement;
57+
const checked = target.checked;
58+
if (updateCheck != null) {
59+
updateCheck(props.value);
60+
}
61+
const value = props.value == null ? checked : props.value;
62+
emit('update:modelValue', value);
63+
emit('change', value);
64+
}
65+
return () =>
66+
h(
67+
'label',
68+
{
69+
class: [
70+
'nt-checkbox',
71+
props.indeterminate ? 'nt-checkbox--indeterminate' : '',
72+
props.disabled ? 'nt-checkbox--disabled' : '',
73+
props.type === 'button' ? 'nt-checkbox--button' : '',
74+
],
75+
},
76+
[
77+
h('input', {
78+
type: 'checkbox',
79+
class: 'nt-checkbox__input',
80+
name: props.name,
81+
checked: isChecked.value,
82+
onChange: handleChange,
83+
disabled: props.disabled,
84+
value: props.value,
85+
}),
86+
props.type !== 'button'
87+
? h('span', { class: 'nt-checkbox__inner' })
88+
: null,
89+
slots.default != null || props.label != null
90+
? h(
91+
'span',
92+
{ class: 'nt-checkbox__label' },
93+
slots.default != null ? slots.default() : props.label,
94+
)
95+
: null,
96+
],
97+
);
98+
},
5599
});
56-
57-
function initIsChecked(): boolean {
58-
if (checkList != null) {
59-
return checkList.includes(props.value as string);
60-
}
61-
if (props.value != null) {
62-
return checkedModel.value === props.value;
63-
}
64-
return checkedModel.value as boolean;
65-
}
66-
const isChecked = ref<boolean>(initIsChecked());
67-
68-
function handleChange(e: Event) {
69-
const target = e.target as HTMLInputElement;
70-
const checked = target.checked;
71-
if (updateCheck != null) {
72-
updateCheck(props.value);
73-
}
74-
const value = props.value == null ? checked : props.value;
75-
checkedModel.value = value;
76-
emits('change', value);
77-
}
78100
</script>
Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,33 @@
11
<template>
22
<div
33
:class="{
4-
'nt-checkbox-group': true
4+
'nt-checkbox-group': true,
55
}"
66
>
77
<slot></slot>
88
</div>
99
</template>
1010

1111
<script setup lang="ts">
12-
import { provide } from 'vue'
12+
import { provide } from 'vue';
1313
14-
const props = withDefaults(
15-
defineProps<{
16-
modelValue?: (string | number)[]
17-
}>(),
18-
{
19-
modelValue: () => []
20-
}
21-
)
22-
23-
// const checkList = defineModel({ default: () => [] as any[] })
24-
const emits = defineEmits(['change', 'update:modelValue'])
14+
const checkList = defineModel({ default: () => [] as any[] });
15+
const emits = defineEmits(['change', 'update:modelValue']);
2516
2617
function updateCheck(val: any) {
27-
const oldValues = props.modelValue
28-
let index = oldValues.indexOf(val)
18+
const oldValues = checkList.value;
19+
let index = oldValues.indexOf(val);
2920
if (index === -1) {
30-
oldValues.push(val)
21+
oldValues.push(val);
3122
} else {
32-
oldValues.splice(index, 1)
23+
oldValues.splice(index, 1);
3324
}
34-
emits('update:modelValue', [...oldValues])
35-
emits('change', [...oldValues])
25+
checkList.value = [...oldValues];
26+
emits('change', [...oldValues]);
3627
}
3728
3829
provide('nt-checkbox-group-check', {
39-
checkList: props.modelValue,
40-
updateCheck
41-
})
30+
checkList: checkList,
31+
updateCheck,
32+
});
4233
</script>

src/components/table/TanstackTable.vue

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,23 @@
5656
</template>
5757
<script setup lang="ts" generic="T">
5858
import { useVueTable, getCoreRowModel, FlexRender } from '@tanstack/vue-table';
59-
import type { CellContext, ColumnDef, Column } from '@tanstack/vue-table';
59+
import type {
60+
CellContext,
61+
ColumnDef,
62+
Column,
63+
Table,
64+
} from '@tanstack/vue-table';
6065
import type { VNode, CSSProperties } from 'vue';
61-
import { computed, shallowRef } from 'vue';
66+
import { computed, shallowRef, h } from 'vue';
67+
import Radio from '../radio/Radio.vue';
68+
import Checkbox from '../checkbox/Checkbox.vue';
6269
6370
export type ColumnDict<T> = {
6471
id?: string;
6572
/** 标题 */
6673
title?: string;
6774
/** 标题 */
68-
header?: string;
75+
header?: string | ((opts: { table: Table<T> }) => VNode | string);
6976
key?: string;
7077
accessorKey?: string;
7178
cell?: (
@@ -76,6 +83,8 @@ export type ColumnDict<T> = {
7683
/** 列宽 */
7784
size?: number;
7885
fixed?: 'left' | 'right';
86+
/** 设置列为可选择 */
87+
type?: 'radio' | 'checkbox';
7988
};
8089
export type ColumnOption<T> = ColumnDict<T> & ColumnDef<T>;
8190
@@ -116,6 +125,26 @@ function createColumns(columns: Array<ColumnOption<T>>) {
116125
} else if (colOpts.fixed === 'right') {
117126
rightFixed.push(colOpts.id as string);
118127
}
128+
if (colOpts.type != null) {
129+
colOpts.id = colOpts.type;
130+
if (colOpts.type === 'checkbox') {
131+
colOpts.header = ({ table }: { table: Table<T> }) => {
132+
return h(Checkbox, {
133+
checked: table.getIsAllRowsSelected(),
134+
indeterminate: table.getIsSomeRowsSelected(),
135+
onChange: (v) => table.toggleAllRowsSelected(v),
136+
});
137+
};
138+
colOpts.cell = (({ row }: CellContext<T, unknown>) => {
139+
return h(Checkbox, {
140+
checked: row.getIsSelected(),
141+
disabled: !row.getCanSelect(),
142+
indeterminate: row.getIsSomeSelected(),
143+
onChange: (v) => row.toggleSelected(v),
144+
});
145+
}) as any;
146+
}
147+
}
119148
delete colOpts.title;
120149
delete colOpts.key;
121150
delete colOpts.fixed;
@@ -158,12 +187,14 @@ const props = withDefaults(
158187
columns: Array<ColumnOption<T>>;
159188
data: Array<T>;
160189
fixedHead?: boolean;
190+
multiSelection?: boolean;
161191
}>(),
162192
{
163193
stripe: true,
164194
border: false,
165195
tableLayout: 'auto',
166196
fixedHead: false,
197+
multiSelection: true,
167198
},
168199
);
169200
@@ -179,13 +210,12 @@ const isFixedLayout = computed(() => {
179210
return props.tableLayout === 'fixed';
180211
});
181212
182-
console.log(rightFixed);
183-
184213
const table = useVueTable<T>({
185214
get data() {
186215
return props.data;
187216
},
188217
columns: cols.value,
218+
enableMultiRowSelection: props.multiSelection === true,
189219
getCoreRowModel: getCoreRowModel(),
190220
initialState: {
191221
columnPinning: {

0 commit comments

Comments
 (0)