Skip to content

Commit 9ee9419

Browse files
committed
enhance type safety in Picker component and update interaction utility methods
1 parent 5c25c2b commit 9ee9419

File tree

4 files changed

+49
-46
lines changed

4 files changed

+49
-46
lines changed

frontend/src/components/Picker/index.vue

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,65 @@
1-
<script setup lang="ts">
2-
import { ref, toRaw } from 'vue'
1+
<script setup lang="ts" generic="ValueType = any, PickerType extends 'single' | 'multi' = 'single'">
2+
import { ref, toRaw, type Ref } from 'vue'
33
44
import useI18n from '@/lang'
55
6-
export type PickerItem = {
6+
export type PickerItem<T> = {
77
label: string
8-
value: string
8+
value: T
99
description?: string
1010
background?: string
1111
onSelect?: (args: {
12-
value: PickerItem['value']
13-
option: PickerItem
14-
options: PickerItem[]
15-
selected: PickerItem['value'][]
12+
value: PickerItem<T>['value']
13+
option: PickerItem<T>
14+
options: PickerItem<T>[]
15+
selected: PickerItem<T>['value'][]
1616
}) => void
1717
}
1818
19-
interface Props {
20-
type: 'single' | 'multi'
19+
interface Props<T, K> {
20+
type: K
2121
title: string
22-
options: PickerItem[]
23-
initialValue?: string[]
22+
options: PickerItem<T>[]
23+
initialValue?: T[]
2424
}
2525
26-
const props = withDefaults(defineProps<Props>(), {
26+
const props = withDefaults(defineProps<Props<ValueType, PickerType>>(), {
2727
options: () => [],
2828
initialValue: () => [],
2929
})
3030
31-
const emits = defineEmits(['confirm', 'cancel', 'finish'])
31+
const emit = defineEmits<{
32+
confirm: [val: PickerType extends 'single' ? ValueType : ValueType[]]
33+
cancel: []
34+
finish: []
35+
}>()
3236
3337
const selected = ref(
34-
new Set<string>(
35-
props.initialValue.filter((v) => props.options.find((option) => option.value === v)),
38+
new Set(
39+
props.initialValue.filter((v) => props.options.find((o) => o.value === v)).map((v) => toRaw(v)),
3640
),
37-
)
41+
) as Ref<Set<ValueType>>
3842
3943
const { t } = useI18n.global
4044
4145
const handleConfirm = () => {
42-
let res: any = Array.from(selected.value).map((v) => toRaw(v))
46+
const res: any = Array.from(selected.value).map((v) => toRaw(v))
4347
if (props.type === 'single') {
44-
res = res[0]
48+
emit('confirm', res[0])
49+
} else {
50+
emit('confirm', res)
4551
}
46-
emits('confirm', res)
47-
emits('finish')
52+
emit('finish')
4853
}
4954
5055
const handleCancel = () => {
51-
emits('cancel')
52-
emits('finish')
56+
emit('cancel')
57+
emit('finish')
5358
}
5459
55-
const isSelected = (option: string) => selected.value.has(option)
60+
const isSelected = (option: ValueType) => selected.value.has(option)
5661
57-
const handleSelect = (option: PickerItem) => {
62+
const handleSelect = (option: PickerItem<ValueType>) => {
5863
if (isSelected(option.value)) {
5964
selected.value.delete(option.value)
6065
} else {
@@ -64,7 +69,7 @@ const handleSelect = (option: PickerItem) => {
6469
value: option.value,
6570
option,
6671
options: props.options,
67-
selected: [...selected.value],
72+
selected: Array.from(selected.value).map((v) => toRaw(v)),
6873
})
6974
}
7075
}

frontend/src/utils/interaction.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { render, createVNode, type VNode, h } from 'vue'
1+
import { render, h, type VNode } from 'vue'
22

33
import i18n from '@/lang'
44
import { APP_TITLE, sampleID } from '@/utils'
@@ -72,7 +72,7 @@ class Message {
7272
const initInstance = () => {
7373
dom.style.cssText = 'display: flex; align-items: center; justify-content: center;'
7474

75-
const vnode = createVNode(MessageComp, {
75+
const vnode = h(MessageComp, {
7676
icon,
7777
content,
7878
onClose: () => {
@@ -136,25 +136,25 @@ class Message {
136136
class Picker {
137137
constructor() {}
138138

139-
public single = <T>(title: string, options: PickerItem[], initialValue: string[] = []) => {
140-
return this.buildPicker<T>('single', title, options, initialValue)
139+
public single = <T>(title: string, options: PickerItem<T>[], initialValue: T[] = []) => {
140+
return this.buildPicker('single', title, options, initialValue)
141141
}
142142

143-
public multi = <T>(title: string, options: PickerItem[], initialValue: string[] = []) => {
144-
return this.buildPicker<T>('multi', title, options, initialValue)
143+
public multi = <T>(title: string, options: PickerItem<T>[], initialValue: T[] = []) => {
144+
return this.buildPicker('multi', title, options, initialValue)
145145
}
146146

147-
private buildPicker = <T>(
148-
type: 'single' | 'multi',
147+
private buildPicker = <ValueType, PickerType extends 'single' | 'multi'>(
148+
type: PickerType,
149149
title: string,
150-
options: PickerItem[],
151-
initialValue: string[],
152-
): Promise<T> => {
150+
options: PickerItem<ValueType>[],
151+
initialValue: ValueType[],
152+
): Promise<PickerType extends 'single' ? ValueType : ValueType[]> => {
153153
return new Promise((resolve, reject) => {
154154
const { t } = i18n.global
155155
const dom = document.createElement('div')
156156
dom.style.cssText = ContainerCssText
157-
const vnode = createVNode(PickerComp, {
157+
const vnode = h(PickerComp<ValueType, PickerType>, {
158158
type,
159159
title,
160160
options,
@@ -183,7 +183,7 @@ const buildConfirm = (
183183
const { t } = i18n.global
184184
const dom = document.createElement('div')
185185
dom.style.cssText = ContainerCssText
186-
const vnode = createVNode(ConfirmComp, {
186+
const vnode = h(ConfirmComp, {
187187
title,
188188
message,
189189
options,
@@ -211,7 +211,7 @@ export const prompt = <T>(
211211
return new Promise<T>((resolve, reject) => {
212212
const dom = document.createElement('div')
213213
dom.style.cssText = ContainerCssText
214-
const vnode = createVNode(PromptComp, {
214+
const vnode = h(PromptComp, {
215215
title,
216216
initialValue,
217217
props,

frontend/src/views/HomeView/components/ConnectionsController.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ const menu: Menu[] = [
199199
return {
200200
label,
201201
handler: async (record: Record<string, any>) => {
202-
const options: PickerItem[] = []
202+
const options: PickerItem<string>[] = []
203203
if (record.metadata.host) {
204204
options.push({
205205
label: t('kernel.rules.type.DOMAIN'),
@@ -233,7 +233,7 @@ const menu: Menu[] = [
233233
description: record.metadata.processPath,
234234
})
235235
}
236-
const payloads = await picker.multi<string[]>('rulesets.selectRuleType', options)
236+
const payloads = await picker.multi('rulesets.selectRuleType', options)
237237
try {
238238
await addToRuleSet(ruleset as any, payloads)
239239
await ignoredError(updateProvidersRules, ruleset)

frontend/src/views/HomeView/components/LogsController.vue

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ const menus: Menu[] = [
5252
return
5353
}
5454
55-
const options: PickerItem[] = []
55+
const options: PickerItem<string>[] = []
5656
5757
if (isValidIPv4(matches[1])) {
5858
options.push({
@@ -74,9 +74,7 @@ const menus: Menu[] = [
7474
})
7575
}
7676
77-
const payloads = await picker.multi<string[]>('rulesets.selectRuleType', options, [
78-
options?.[0].value,
79-
])
77+
const payloads = await picker.multi('rulesets.selectRuleType', options, [options?.[0].value])
8078
8179
try {
8280
await addToRuleSet(ruleset as any, payloads)

0 commit comments

Comments
 (0)