bigtop-manager-ui/src/components/common/auto-form/index.vue (198 lines of code) (raw):

<!-- ~ Licensed to the Apache Software Foundation (ASF) under one ~ or more contributor license agreements. See the NOTICE file ~ distributed with this work for additional information ~ regarding copyright ownership. The ASF licenses this file ~ to you under the Apache License, Version 2.0 (the ~ "License"); you may not use this file except in compliance ~ with the License. You may obtain a copy of the License at ~ ~ http://www.apache.org/licenses/LICENSE-2.0 ~ ~ Unless required by applicable law or agreed to in writing, ~ software distributed under the License is distributed on an ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY ~ KIND, either express or implied. See the License for the ~ specific language governing permissions and limitations ~ under the License. --> <script lang="ts" setup> import { computed, onMounted, ref, toRaw, toRefs, watch } from 'vue' import { Emits, FormState, Props } from './types' import { useLocaleStore } from '@/store/locale' import { storeToRefs } from 'pinia' const props = withDefaults(defineProps<Props>(), { showButton: true, disabledItems: null, // Has higher priority than controlProps of formItems hiddenItems: () => [], labelCol: () => { return { span: 5 } }, wrapperCol: () => { return { span: 14 } }, formOptions: () => { return { hideOk: false, hideCancel: false, okText: 'Submit', cancelText: 'Reset' } } }) const emits = defineEmits<Emits>() const localeStore = useLocaleStore() const { locale } = storeToRefs(localeStore) const formRef = ref() const formState = ref<FormState>({}) const tmpCacheFormState = ref<FormState>({}) const optionMap = ref<Record<string, unknown>>({}) const formItemEvents = ref<Record<string, unknown>>({}) const { formValue } = toRefs(props) const ruleFormTmpResolve = computed({ get() { const len = Object.keys(formValue.value).length return len ? formValue.value : tmpCacheFormState.value }, set(val) { tmpCacheFormState.value = val || {} emits('update:formValue', val) } }) watch( formState, (newState) => { emits('update:formValue', newState) }, { deep: true } ) watch(locale, () => { formRef.value.clearValidate() }) const initForm = () => { const newForm: FormState = {} props.formItems.forEach((item) => { const { field, defaultValue } = item newForm[field] = defaultValue || undefined }) Object.assign(formState.value, { ...newForm, ...ruleFormTmpResolve.value }) } const getFormValidation = async () => { try { await formRef.value?.validateFields() return Promise.resolve(true) } catch (error) { console.log('Failed:', error) return Promise.resolve(false) } } const setOptions = (field: string, list: unknown[]) => { optionMap.value[field] = list } const setFormItemEvents = (field: string, events: any) => { formItemEvents.value[field] = events } const setFormValue = <T,>(form: FormState<T>) => { const oldForm = { ...toRaw(formState.value) } formState.value = { ...oldForm, ...form } } const resetForm = () => { formRef.value.resetFields() } const onSubmit = async () => { try { await formRef.value.validateFields() emits('onSubmit', true) } catch (error) { console.log('Failed:', error) emits('onSubmit', false) } } const onReset = () => { resetForm() } onMounted(() => { initForm() }) defineExpose({ resetForm, getFormValidation, setOptions, setFormItemEvents, setFormValue }) </script> <template> <div> <a-form ref="formRef" label-align="left" :colon="false" :model="formState" :label-col="props.labelCol" :wrapper-col="props.wrapperCol" :disabled="props.formDisabled" > <div v-for="item in props.formItems" :key="item.field"> <slot v-if="!props.hiddenItems.includes(item.field)" :="{ item, state: formState }" :name="item.slot ?? item.field" > <a-form-item v-bind="item.formItemProps"> <!-- input --> <a-input v-if="item.type == 'input'" v-model:value="formState[item.field]" v-bind="item.controlProps" :disabled="disabledItems ? disabledItems.includes(item.field) : item.controlProps?.disabled" v-on="formItemEvents[item.field] || {}" /> <a-input-password v-if="item.type == 'inputPassword'" v-model:value="formState[item.field]" v-bind="item.controlProps" :disabled="disabledItems ? disabledItems.includes(item.field) : item.controlProps?.disabled" v-on="formItemEvents[item.field] || {}" /> <!-- textarea --> <a-textarea v-if="item.type == 'textarea'" v-model:value="formState[item.field]" v-bind="item.controlProps" :disabled="disabledItems ? disabledItems.includes(item.field) : item.controlProps?.disabled" v-on="formItemEvents[item.field] || {}" /> <!-- select --> <a-select v-if="item.type == 'select'" v-model:value="formState[item.field]" v-bind="item.controlProps" :disabled="disabledItems ? disabledItems.includes(item.field) : item.controlProps?.disabled" v-on="formItemEvents[item.field] || {}" > <a-select-option v-for="(child, childIndex) in optionMap[item.field] || item.defaultOptionsMap || []" :key="childIndex" :value="typeof child === 'string' ? child : child[(item.fieldMap && item.fieldMap.value) || 'value']" > {{ typeof child === 'string' ? child : child[(item.fieldMap && item.fieldMap.label) || 'label'] }} </a-select-option> </a-select> <!-- radio --> <a-radio-group v-if="item.type == 'radio'" v-model:value="formState[item.field]" v-bind="item.controlProps" :disabled="disabledItems ? disabledItems.includes(item.field) : item.controlProps?.disabled" v-on="formItemEvents[item.field] || {}" > <a-radio v-for="(child, childIndex) in optionMap[item.field] || item.defaultOptionsMap || []" :key="childIndex" :value="child[(item.fieldMap && item.fieldMap.value) || 'value']" > {{ child[(item.fieldMap && item.fieldMap.label) || 'label'] }} </a-radio> </a-radio-group> </a-form-item> </slot> </div> <a-form-item v-if="showButton"> <slot name="actions" :on-submit="onSubmit" :on-reset="onReset"> <a-space> <a-button type="primary" @click="onSubmit"> {{ props.formOptions.okText }} </a-button> <a-button @click="onReset"> {{ props.formOptions.hideCancel }} </a-button> </a-space> </slot> </a-form-item> </a-form> </div> </template>