Skip to content

Commit 2302186

Browse files
committed
refactor: 更新 CheckboxGroupOption 和 RadioGroupOption 接口,支持泛型扩展
1 parent f7789a4 commit 2302186

6 files changed

Lines changed: 92 additions & 88 deletions

File tree

apps/example/src/views/component_example/checkbox_group/_demo3.vue

Lines changed: 35 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -5,64 +5,58 @@ const options = [
55
{
66
label: '专注模式',
77
value: 'focus',
8+
summary: '任务优先',
89
description: '突出主任务,弱化辅助信息,适合录入和审批场景。',
910
},
1011
{
1112
label: '平衡模式',
1213
value: 'balanced',
14+
summary: '默认体验',
1315
description: '信息密度与可读性保持平衡,适合作为默认配置。',
1416
},
1517
{
1618
label: '高密度模式',
1719
value: 'dense',
20+
summary: '信息看板',
1821
description: '在大屏中同时承载更多信息,适合运营与监控看板。',
1922
},
2023
]
21-
22-
const currentText = computed(() =>
23-
options
24-
.filter(option => value.value.includes(option.value))
25-
.map(option => option.label)
26-
.join(''),
27-
)
2824
</script>
2925

3026
<template>
31-
<FaCheckboxGroup
32-
v-model="value"
33-
:options="options"
34-
class="gap-2 md:grid-cols-3"
35-
option-class="rounded-xl border border-transparent px-1 py-1"
36-
>
37-
<template #option="{ option, checked, disabled }">
38-
<div
39-
class="px-4 py-3 border rounded-xl flex gap-3 w-full transition-colors items-start justify-between" :class="[
40-
checked ? 'border-primary bg-primary/5' : 'border-border hover:border-primary/40',
41-
disabled && 'opacity-60',
42-
]"
43-
>
44-
<div class="gap-1 grid min-w-0">
45-
<div class="text-sm font-medium truncate">
46-
{{ option.label }}
47-
</div>
48-
<div
49-
v-if="option.description"
50-
class="text-xs text-muted-foreground leading-5"
51-
>
52-
{{ option.description }}
53-
</div>
54-
</div>
55-
<span
56-
class="text-xs font-medium shrink-0" :class="[
57-
checked ? 'text-primary' : 'text-muted-foreground',
27+
<div class="gap-4 grid">
28+
<FaCheckboxGroup
29+
v-model="value"
30+
:options="options"
31+
class="gap-2 md:grid-cols-3"
32+
option-class="rounded-xl border border-transparent px-1 py-1"
33+
>
34+
<template #option="{ option, checked, disabled }">
35+
<div
36+
class="px-4 py-3 border rounded-xl flex gap-3 w-full transition-colors items-start justify-between" :class="[
37+
checked ? 'border-primary bg-primary/5' : 'border-border hover:border-primary/40',
38+
disabled && 'opacity-60',
5839
]"
5940
>
60-
{{ checked ? '已选中' : '可选择' }}
61-
</span>
62-
</div>
63-
</template>
64-
</FaCheckboxGroup>
65-
<div class="text-sm text-muted-foreground">
66-
当前模式:{{ currentText }}
41+
<div class="gap-1 grid min-w-0">
42+
<div class="flex gap-2 items-center">
43+
<span class="text-sm font-medium truncate">{{ option.label }}</span>
44+
<span class="text-xs text-muted-foreground px-1.5 py-0.5 rounded bg-muted">
45+
{{ option.summary }}
46+
</span>
47+
</div>
48+
<div class="text-xs text-muted-foreground leading-5">
49+
{{ option.description }}
50+
</div>
51+
</div>
52+
<span class="text-xs font-medium shrink-0" :class="checked ? 'text-primary' : 'text-muted-foreground'">
53+
{{ checked ? '已选中' : '可选择' }}
54+
</span>
55+
</div>
56+
</template>
57+
</FaCheckboxGroup>
58+
<div class="text-sm text-muted-foreground">
59+
当前值:{{ value.join('、') }}
60+
</div>
6761
</div>
6862
</template>

apps/example/src/views/component_example/radio_group/_demo3.vue

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,32 +5,30 @@ const options = [
55
{
66
label: '专注模式',
77
value: 'focus',
8+
badge: '录入',
89
description: '突出主任务,弱化辅助信息,适合录入和审批场景。',
910
},
1011
{
1112
label: '平衡模式',
1213
value: 'balanced',
14+
badge: '推荐',
1315
description: '信息密度与可读性保持平衡,适合作为默认配置。',
1416
},
1517
{
1618
label: '高密度模式',
1719
value: 'dense',
20+
badge: '大屏',
1821
description: '在大屏中同时承载更多信息,适合运营与监控看板。',
1922
},
2023
]
21-
22-
const currentLabel = computed(() =>
23-
options.find(option => option.value === value.value)?.label ?? '',
24-
)
2524
</script>
2625

2726
<template>
2827
<div class="gap-4 grid">
2928
<FaRadioGroup
3029
v-model="value"
31-
orientation="horizontal"
3230
:options="options"
33-
class="gap-4 md:grid-cols-3"
31+
class="gap-2 md:grid-cols-3"
3432
option-class="rounded-xl border border-transparent px-1 py-1"
3533
>
3634
<template #option="{ option, checked, disabled }">
@@ -41,28 +39,24 @@ const currentLabel = computed(() =>
4139
]"
4240
>
4341
<div class="gap-1 grid min-w-0">
44-
<div class="text-sm font-medium truncate">
45-
{{ option.label }}
42+
<div class="flex gap-2 items-center">
43+
<span class="text-sm font-medium truncate">{{ option.label }}</span>
44+
<span class="text-xs text-muted-foreground px-1.5 py-0.5 rounded bg-muted">
45+
{{ option.badge }}
46+
</span>
4647
</div>
47-
<div
48-
v-if="option.description"
49-
class="text-xs text-muted-foreground leading-5"
50-
>
48+
<div class="text-xs text-muted-foreground leading-5">
5149
{{ option.description }}
5250
</div>
5351
</div>
54-
<span
55-
class="text-xs font-medium shrink-0" :class="[
56-
checked ? 'text-primary' : 'text-muted-foreground',
57-
]"
58-
>
52+
<span class="text-xs font-medium shrink-0" :class="checked ? 'text-primary' : 'text-muted-foreground'">
5953
{{ checked ? '已选中' : '可选择' }}
6054
</span>
6155
</div>
6256
</template>
6357
</FaRadioGroup>
6458
<div class="text-sm text-muted-foreground">
65-
当前模式:{{ currentLabel }}
59+
当前值:{{ value }}
6660
</div>
6761
</div>
6862
</template>

packages/components/src/checkbox-group/README.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
### CheckboxGroupOption 接口
2828

2929
```ts
30-
interface CheckboxGroupOption {
30+
export interface CheckboxGroupOption {
3131
label: string
3232
value: AcceptableValue
3333
description?: string
@@ -40,7 +40,7 @@ interface CheckboxGroupOption {
4040

4141
| 名称 | 说明 |
4242
|------|------|
43-
| `option` | 自定义选项内容,slot props: `{ option, checked, disabled, id }` |
43+
| `option` | 自定义选项内容,slot props: `{ option, checked, disabled, id }``option` 会保留 `options` 中的扩展字段类型 |
4444

4545
## Events
4646

@@ -173,16 +173,19 @@ const options = [
173173
{
174174
label: '专注模式',
175175
value: 'focus',
176+
summary: '任务优先',
176177
description: '突出主任务,弱化辅助信息。',
177178
},
178179
{
179180
label: '平衡模式',
180181
value: 'balanced',
182+
summary: '默认体验',
181183
description: '信息密度与可读性保持平衡。',
182184
},
183185
{
184186
label: '高密度模式',
185187
value: 'dense',
188+
summary: '信息看板',
186189
description: '适合大屏运营与监控看板。',
187190
},
188191
]
@@ -202,8 +205,13 @@ const options = [
202205
]"
203206
>
204207
<div class="min-w-0">
205-
<div class="truncate font-medium">{{ option.label }}</div>
206-
<div v-if="option.description" class="text-xs text-muted-foreground leading-5">
208+
<div class="flex items-center gap-2">
209+
<span class="truncate font-medium">{{ option.label }}</span>
210+
<span class="rounded bg-muted px-1.5 py-0.5 text-xs text-muted-foreground">
211+
{{ option.summary }}
212+
</span>
213+
</div>
214+
<div class="text-xs text-muted-foreground leading-5">
207215
{{ option.description }}
208216
</div>
209217
</div>

packages/components/src/checkbox-group/index.vue

100644100755
Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
<script setup lang="ts">
1+
<script setup lang="ts" generic="TOption extends CheckboxGroupOption">
22
import type { AcceptableValue } from 'reka-ui'
33
import type { HTMLAttributes } from 'vue'
44
import { cn } from '../../utils'
55
import { Checkbox } from '../checkbox/checkbox'
66
import { Label } from '../label/label'
77
8-
interface CheckboxGroupOption {
8+
export interface CheckboxGroupOption {
99
label: string
1010
value: AcceptableValue
1111
description?: string
@@ -18,7 +18,7 @@ defineOptions({
1818
})
1919
2020
const props = defineProps<{
21-
options: CheckboxGroupOption[]
21+
options: TOption[]
2222
disabled?: boolean
2323
min?: number
2424
max?: number
@@ -35,7 +35,7 @@ const emit = defineEmits<{
3535
3636
const slots = defineSlots<{
3737
option?: (props: {
38-
option: CheckboxGroupOption
38+
option: TOption
3939
checked: boolean
4040
disabled: boolean
4141
id: string
@@ -53,11 +53,11 @@ watch(value, (newValue) => {
5353
emit('change', newValue)
5454
})
5555
56-
function getOptionId(option: CheckboxGroupOption, index: number) {
56+
function getOptionId(option: TOption, index: number) {
5757
return option.id || `${baseId}-${index}`
5858
}
5959
60-
function getOptionKey(option: CheckboxGroupOption, index: number) {
60+
function getOptionKey(option: TOption, index: number) {
6161
if (option.id) {
6262
return option.id
6363
}
@@ -67,7 +67,7 @@ function getOptionKey(option: CheckboxGroupOption, index: number) {
6767
: index
6868
}
6969
70-
function isOptionDisabled(option: CheckboxGroupOption) {
70+
function isOptionDisabled(option: TOption) {
7171
if (props.disabled || option.disabled) {
7272
return true
7373
}
@@ -85,15 +85,15 @@ function isOptionDisabled(option: CheckboxGroupOption) {
8585
return false
8686
}
8787
88-
function findOptionIndex(option: CheckboxGroupOption) {
88+
function findOptionIndex(option: TOption) {
8989
return value.value.findIndex(item => Object.is(item, option.value))
9090
}
9191
92-
function isOptionChecked(option: CheckboxGroupOption) {
92+
function isOptionChecked(option: TOption) {
9393
return findOptionIndex(option) > -1
9494
}
9595
96-
function updateOptionChecked(option: CheckboxGroupOption, checked: boolean) {
96+
function updateOptionChecked(option: TOption, checked: boolean) {
9797
const nextValue = [...value.value]
9898
const optionIndex = nextValue.findIndex(item => Object.is(item, option.value))
9999
@@ -109,23 +109,23 @@ function updateOptionChecked(option: CheckboxGroupOption, checked: boolean) {
109109
value.value = nextValue
110110
}
111111
112-
function handleOptionModelValueChange(option: CheckboxGroupOption, checked: boolean | 'indeterminate') {
112+
function handleOptionModelValueChange(option: TOption, checked: boolean | 'indeterminate') {
113113
if (isOptionDisabled(option)) {
114114
return
115115
}
116116
117117
updateOptionChecked(option, checked === true)
118118
}
119119
120-
function handleCustomOptionClick(option: CheckboxGroupOption) {
120+
function handleCustomOptionClick(option: TOption) {
121121
if (!slots.option || isOptionDisabled(option)) {
122122
return
123123
}
124124
125125
updateOptionChecked(option, !isOptionChecked(option))
126126
}
127127
128-
function handleCustomOptionKeydown(option: CheckboxGroupOption) {
128+
function handleCustomOptionKeydown(option: TOption) {
129129
if (!slots.option || isOptionDisabled(option)) {
130130
return
131131
}

packages/components/src/radio-group/README.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
### RadioGroupOption 接口
2525

2626
```ts
27-
interface RadioGroupOption {
27+
export interface RadioGroupOption {
2828
label: string
2929
value: AcceptableValue
3030
description?: string
@@ -37,7 +37,7 @@ interface RadioGroupOption {
3737

3838
| 名称 | 说明 |
3939
|------|------|
40-
| `option` | 自定义选项内容,slot props: `{ option, checked, disabled, id }` |
40+
| `option` | 自定义选项内容,slot props: `{ option, checked, disabled, id }``option` 会保留 `options` 中的扩展字段类型 |
4141

4242
## Events
4343

@@ -147,16 +147,19 @@ const options = [
147147
{
148148
label: '专注模式',
149149
value: 'focus',
150+
badge: '录入',
150151
description: '突出主任务,弱化辅助信息。',
151152
},
152153
{
153154
label: '平衡模式',
154155
value: 'balanced',
156+
badge: '推荐',
155157
description: '信息密度与可读性保持平衡。',
156158
},
157159
{
158160
label: '高密度模式',
159161
value: 'dense',
162+
badge: '大屏',
160163
description: '适合大屏运营与监控看板。',
161164
},
162165
]
@@ -176,8 +179,13 @@ const options = [
176179
]"
177180
>
178181
<div class="min-w-0">
179-
<div class="truncate font-medium">{{ option.label }}</div>
180-
<div v-if="option.description" class="text-xs text-muted-foreground leading-5">
182+
<div class="flex items-center gap-2">
183+
<span class="truncate font-medium">{{ option.label }}</span>
184+
<span class="rounded bg-muted px-1.5 py-0.5 text-xs text-muted-foreground">
185+
{{ option.badge }}
186+
</span>
187+
</div>
188+
<div class="text-xs text-muted-foreground leading-5">
181189
{{ option.description }}
182190
</div>
183191
</div>

0 commit comments

Comments
 (0)