Browse Source

feat(components/Form): add ApiCheckboxGroup component

main
刘凯 1 year ago
parent
commit
3e8f73cc49
  1. 126
      src/components/Form/src/components/ApiCheckboxGroup.vue
  2. 2
      src/components/Form/src/types/index.ts

126
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>

2
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']
}