diff --git a/src/components/Form/src/components/ApiCheckboxGroup.vue b/src/components/Form/src/components/ApiCheckboxGroup.vue new file mode 100644 index 0000000..de9a799 --- /dev/null +++ b/src/components/Form/src/components/ApiCheckboxGroup.vue @@ -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> diff --git a/src/components/Form/src/types/index.ts b/src/components/Form/src/types/index.ts index 8a2cb25..8ba5cbd 100644 --- a/src/components/Form/src/types/index.ts +++ b/src/components/Form/src/types/index.ts @@ -123,6 +123,7 @@ interface _CustomComponents { FileUpload: ExtractPropTypes<(typeof import('../components/FileUpload.vue'))['default']> Editor: ExtractPropTypes<(typeof import('@/components/Tinymce/src/Editor.vue'))['default']> CronTab: ExtractPropTypes<(typeof import('@/components/CronTab/src/CronTabInput.vue'))['default']> + ApiCheckboxGroup: ExtractPropTypes<(typeof import('../components/ApiCheckboxGroup.vue'))['default']> } type CustomComponents<T = _CustomComponents> = { @@ -171,4 +172,5 @@ export interface ComponentProps { FileUpload: CustomComponents['FileUpload'] & ExtractPropTypes<(typeof import('ant-design-vue/es/upload'))['default']> Editor: CustomComponents['Editor'] CronTab: CustomComponents['CronTab'] + ApiCheckboxGroup: CustomComponents['ApiCheckboxGroup'] & ComponentProps['CheckboxGroup'] }