2 changed files with 128 additions and 0 deletions
@ -0,0 +1,126 @@
|
||||
<script lang="ts" setup> |
||||
import type { PropType } from 'vue' |
||||
import { computed, ref, unref, watch, watchEffect } from 'vue' |
||||
import { Checkbox } from 'ant-design-vue' |
||||
import { get, omit } from 'lodash-es' |
||||
import type { CheckboxChangeEvent } from 'ant-design-vue/lib/checkbox/interface' |
||||
import { isFunction } from '@/utils/is' |
||||
import { useRuleFormItem } from '@/hooks/component/useFormItem' |
||||
import { useAttrs } from '@/hooks/core/useAttrs' |
||||
import { propTypes } from '@/utils/propTypes' |
||||
|
||||
interface OptionsItem { |
||||
label?: string |
||||
value?: string | number |
||||
disabled?: boolean |
||||
indeterminate?: boolean |
||||
onChange?: (e: CheckboxChangeEvent) => void |
||||
[key: string]: any |
||||
} |
||||
|
||||
defineOptions({ name: 'ApiCheckboxGroup' }) |
||||
|
||||
const props = defineProps({ |
||||
api: { |
||||
type: Function as PropType<(arg?: any) => Promise<OptionsItem[]>>, |
||||
default: null, |
||||
}, |
||||
params: { |
||||
type: [Object, String] as PropType<any | string>, |
||||
default: () => ({}), |
||||
}, |
||||
value: { |
||||
type: Array as PropType<(string | number | boolean)[]>, |
||||
}, |
||||
numberToString: propTypes.bool, |
||||
resultField: propTypes.string.def(''), |
||||
labelField: propTypes.string.def('label'), |
||||
valueField: propTypes.string.def('value'), |
||||
immediate: propTypes.bool.def(true), |
||||
}) |
||||
const emit = defineEmits(['options-change', 'change']) |
||||
|
||||
const options = ref<OptionsItem[]>([]) |
||||
const loading = ref(false) |
||||
const isFirstLoad = ref(true) |
||||
const emitData = ref<any[]>([]) |
||||
const attrs = useAttrs() |
||||
// Embedded in the form, just use the hook binding to perform form verification |
||||
const [state] = useRuleFormItem(props, 'value', 'change', emitData) |
||||
|
||||
// Processing options value |
||||
const getOptions = computed(() => { |
||||
const { labelField, valueField, numberToString } = props |
||||
|
||||
return unref(options).reduce((prev, next: any) => { |
||||
if (next) { |
||||
const value = next[valueField] |
||||
prev.push({ |
||||
label: next[labelField], |
||||
value: numberToString ? `${value}` : value, |
||||
...omit(next, [labelField, valueField]), |
||||
}) |
||||
} |
||||
return prev |
||||
}, [] as (OptionsItem & { value: OptionsItem['label '] })[]) |
||||
}) |
||||
|
||||
watchEffect(() => { |
||||
props.immediate && fetch() |
||||
}) |
||||
|
||||
watch( |
||||
() => props.params, |
||||
() => { |
||||
!unref(isFirstLoad) && fetch() |
||||
}, |
||||
{ deep: true }, |
||||
) |
||||
|
||||
async function fetch() { |
||||
const api = props.api |
||||
if (!api || !isFunction(api)) |
||||
return |
||||
options.value = [] |
||||
try { |
||||
loading.value = true |
||||
const res = await api(props.params) |
||||
if (Array.isArray(res)) { |
||||
options.value = res |
||||
emitChange() |
||||
return |
||||
} |
||||
if (props.resultField) |
||||
options.value = get(res, props.resultField) || [] |
||||
|
||||
emitChange() |
||||
} |
||||
catch (error) { |
||||
console.warn(error) |
||||
} |
||||
finally { |
||||
loading.value = false |
||||
} |
||||
} |
||||
|
||||
function emitChange() { |
||||
emit('options-change', unref(getOptions)) |
||||
} |
||||
|
||||
function handleClick(...args) { |
||||
emitData.value = args |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<Checkbox.Group v-bind="attrs" v-model:value="state"> |
||||
<template v-for="item in getOptions" :key="`${item.value}`"> |
||||
<Checkbox |
||||
v-bind="item" |
||||
@click="handleClick(item)" |
||||
> |
||||
{{ item.label }} |
||||
</Checkbox> |
||||
</template> |
||||
</Checkbox.Group> |
||||
</template> |
Reference in new issue