|
@@ -0,0 +1,526 @@
|
|
|
|
|
+<template>
|
|
|
|
|
+ <n-form
|
|
|
|
|
+ ref="formRef"
|
|
|
|
|
+ class="form-spacing"
|
|
|
|
|
+ :label-width="80"
|
|
|
|
|
+ :model="formValue"
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ >
|
|
|
|
|
+ <n-collapse>
|
|
|
|
|
+ <n-collapse-item
|
|
|
|
|
+ :title="config[index].name"
|
|
|
|
|
+ v-for="(node, index) in vnodes"
|
|
|
|
|
+ :key="index"
|
|
|
|
|
+ >
|
|
|
|
|
+ <div style="width: 100%; display: inline-flex; align-items: flex-start; align-content: space-around;" >
|
|
|
|
|
+ <component :is="node" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </n-collapse-item>
|
|
|
|
|
+ </n-collapse>
|
|
|
|
|
+ </n-form>
|
|
|
|
|
+</template>
|
|
|
|
|
+
|
|
|
|
|
+<script setup lang="tsx">
|
|
|
|
|
+import { ref, reactive, Reactive, VNode, computed, onMounted, watch } from 'vue'
|
|
|
|
|
+import type { FormInst } from 'naive-ui'
|
|
|
|
|
+import {
|
|
|
|
|
+ NCollapse,
|
|
|
|
|
+ NCollapseItem,
|
|
|
|
|
+ NForm,
|
|
|
|
|
+ NFormItem,
|
|
|
|
|
+ NInput,
|
|
|
|
|
+ NInputNumber,
|
|
|
|
|
+ NRadio,
|
|
|
|
|
+ NRadioGroup,
|
|
|
|
|
+ NCheckbox,
|
|
|
|
|
+ NCheckboxGroup,
|
|
|
|
|
+ NRadioButton,
|
|
|
|
|
+ NSelect,
|
|
|
|
|
+ NSwitch,
|
|
|
|
|
+ NDatePicker,
|
|
|
|
|
+ NTimePicker
|
|
|
|
|
+} from 'naive-ui'
|
|
|
|
|
+
|
|
|
|
|
+interface FormField {
|
|
|
|
|
+ value: NonNullable<any>;
|
|
|
|
|
+ enable: boolean;
|
|
|
|
|
+ child?: FormValueType;
|
|
|
|
|
+ options?: Record<string, FormValueType>;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+export interface FormValueType extends Record<string, Reactive<FormField>> {}
|
|
|
|
|
+
|
|
|
|
|
+const formRef = ref<FormInst | undefined>(undefined)
|
|
|
|
|
+// const formValue = reactive<FormValueType>({})
|
|
|
|
|
+
|
|
|
|
|
+type SupportItemType = 'Input'
|
|
|
|
|
+| 'InputNumber'
|
|
|
|
|
+| 'RadioGroup'
|
|
|
|
|
+| 'Checkbox'
|
|
|
|
|
+| 'CheckboxGroup'
|
|
|
|
|
+| 'ButtonGroup'
|
|
|
|
|
+| 'Select'
|
|
|
|
|
+| 'Switch'
|
|
|
|
|
+| 'DatePicker'
|
|
|
|
|
+| 'TimePicker'
|
|
|
|
|
+
|
|
|
|
|
+export type SupportItemConfig = InputConfig
|
|
|
|
|
+| InputNumberConfig
|
|
|
|
|
+| RadioGroupConfig
|
|
|
|
|
+| CheckboxConfig
|
|
|
|
|
+| CheckboxGroupConfig
|
|
|
|
|
+| ButtonGroupConfig
|
|
|
|
|
+| SelectConfig
|
|
|
|
|
+| SwitchConfig
|
|
|
|
|
+| DatePickerConfig
|
|
|
|
|
+| TimePickerConfig
|
|
|
|
|
+
|
|
|
|
|
+type OptionsType = string | OptionsExt
|
|
|
|
|
+
|
|
|
|
|
+interface OptionsExt {
|
|
|
|
|
+ name: string;
|
|
|
|
|
+ ext: SupportItemConfig[];
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+interface ItemBaseConfig {
|
|
|
|
|
+ name: string;
|
|
|
|
|
+ type: SupportItemType;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+interface InputConfig extends ItemBaseConfig {
|
|
|
|
|
+ default?: string;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+interface InputNumberConfig extends ItemBaseConfig {
|
|
|
|
|
+ default?: number;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+interface RadioGroupConfig extends ItemBaseConfig {
|
|
|
|
|
+ options: OptionsType[];
|
|
|
|
|
+ default: string;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+interface CheckboxConfig extends ItemBaseConfig {
|
|
|
|
|
+ default: boolean;
|
|
|
|
|
+ child?: SupportItemConfig[];
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+interface CheckboxGroupConfig extends ItemBaseConfig {
|
|
|
|
|
+ options: OptionsType[];
|
|
|
|
|
+ default: string[];
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+interface ButtonGroupConfig extends ItemBaseConfig {
|
|
|
|
|
+ options: OptionsType[];
|
|
|
|
|
+ default?: string;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+interface SelectConfig extends ItemBaseConfig {
|
|
|
|
|
+ options: OptionsType[];
|
|
|
|
|
+ default?: string | string[];
|
|
|
|
|
+ multiple: boolean;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+interface SwitchConfig extends ItemBaseConfig {
|
|
|
|
|
+ default?: boolean;
|
|
|
|
|
+ child?: SupportItemConfig[];
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+interface DatePickerConfig extends ItemBaseConfig {
|
|
|
|
|
+ default?: number | [number, number];
|
|
|
|
|
+ formatter?: string; // 时间格式化字符串,详情见 format https://date-fns.org/v2.23.0/docs/format
|
|
|
|
|
+ dateType?: 'date' | 'datetime' | 'daterange' | 'datetimerange' | 'month' | 'monthrange' | 'year' | 'yearrange' | 'quarter' | 'quarterrange' | 'week';
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+interface TimePickerConfig extends ItemBaseConfig {
|
|
|
|
|
+ default?: number;
|
|
|
|
|
+ formatter?: string; // 时间格式化字符串,详情见 format https://date-fns.org/v2.23.0/docs/format
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// interface ItemConfig {
|
|
|
|
|
+// config: SupportItemConfig;
|
|
|
|
|
+// childConfig?: ItemConfig[];
|
|
|
|
|
+// }
|
|
|
|
|
+
|
|
|
|
|
+type FormConfig = SupportItemConfig[];
|
|
|
|
|
+type childConfig = SupportItemConfig[];
|
|
|
|
|
+
|
|
|
|
|
+interface Props {
|
|
|
|
|
+ config?: FormConfig;
|
|
|
|
|
+ formValue?: FormValueType;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const props = withDefaults(defineProps<Props>(), {
|
|
|
|
|
+ config: () => [],
|
|
|
|
|
+ formValue: () => ({})
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const praseInputConfig = (config: InputConfig, model: { value: FormValueType }, enable: boolean): (() => VNode) => {
|
|
|
|
|
+ model.value[config.name] = reactive({
|
|
|
|
|
+ value: config.default ?? "",
|
|
|
|
|
+ enable
|
|
|
|
|
+ })
|
|
|
|
|
+ return () => (
|
|
|
|
|
+ <>
|
|
|
|
|
+ {
|
|
|
|
|
+ model.value[config.name].enable &&
|
|
|
|
|
+ <NFormItem label={config.name} >
|
|
|
|
|
+ <NInput v-model:value={model.value[config.name].value} />
|
|
|
|
|
+ </NFormItem>
|
|
|
|
|
+ }
|
|
|
|
|
+ </>
|
|
|
|
|
+ )
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const praseInputNumberConfig = (config: InputNumberConfig, model: { value: FormValueType }, enable: boolean): (() => VNode) => {
|
|
|
|
|
+ model.value[config.name] = reactive({
|
|
|
|
|
+ value: config.default ?? 0,
|
|
|
|
|
+ enable
|
|
|
|
|
+ })
|
|
|
|
|
+ return () => (
|
|
|
|
|
+ <>
|
|
|
|
|
+ {
|
|
|
|
|
+ model.value[config.name].enable &&
|
|
|
|
|
+ <NFormItem label={config.name} >
|
|
|
|
|
+ <NInputNumber v-model:value={model.value[config.name].value} />
|
|
|
|
|
+ </NFormItem>
|
|
|
|
|
+ }
|
|
|
|
|
+ </>
|
|
|
|
|
+ )
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const praseRadioGroupConfig = (config: RadioGroupConfig, model: { value: FormValueType }, enable: boolean): (() => VNode) => {
|
|
|
|
|
+ model.value[config.name] = reactive({
|
|
|
|
|
+ value: config.default ?? "",
|
|
|
|
|
+ enable
|
|
|
|
|
+ })
|
|
|
|
|
+ const renderFn = optionsExtRender(config, model)
|
|
|
|
|
+ return () => (
|
|
|
|
|
+ <>
|
|
|
|
|
+ {
|
|
|
|
|
+ model.value[config.name].enable &&
|
|
|
|
|
+ <>
|
|
|
|
|
+ <NFormItem label={config.name} >
|
|
|
|
|
+ <NRadioGroup v-model:value={model.value[config.name].value} name={config.name}>
|
|
|
|
|
+ {config.options.map((radioOption, index) => {
|
|
|
|
|
+ const optionName = typeof radioOption === 'string' ? radioOption : radioOption.name
|
|
|
|
|
+ return (
|
|
|
|
|
+ <NRadio key={optionName} value={optionName}>
|
|
|
|
|
+ { optionName }
|
|
|
|
|
+ </NRadio>
|
|
|
|
|
+ )
|
|
|
|
|
+ })}
|
|
|
|
|
+ </NRadioGroup>
|
|
|
|
|
+ </NFormItem>
|
|
|
|
|
+ {
|
|
|
|
|
+ renderFn && renderFn.map((vnode) => (vnode()))
|
|
|
|
|
+ }
|
|
|
|
|
+ </>
|
|
|
|
|
+ }
|
|
|
|
|
+ </>
|
|
|
|
|
+ )
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const praseCheckboxConfig = (config: CheckboxConfig, model: { value: FormValueType }, enable: boolean): (() => VNode) => {
|
|
|
|
|
+ model.value[config.name] = reactive({
|
|
|
|
|
+ value: config.default ?? false,
|
|
|
|
|
+ enable
|
|
|
|
|
+ })
|
|
|
|
|
+ const renderFn = optionsExtRender(config, model)
|
|
|
|
|
+ return () => (
|
|
|
|
|
+ <>
|
|
|
|
|
+ {
|
|
|
|
|
+ model.value[config.name].enable &&
|
|
|
|
|
+ <>
|
|
|
|
|
+ <NFormItem>
|
|
|
|
|
+ <NCheckbox v-model:checked={model.value[config.name].value}>
|
|
|
|
|
+ { config.name }
|
|
|
|
|
+ </NCheckbox>
|
|
|
|
|
+ </NFormItem>
|
|
|
|
|
+ {
|
|
|
|
|
+ renderFn && renderFn.map((vnode) => (vnode()))
|
|
|
|
|
+ }
|
|
|
|
|
+ </>
|
|
|
|
|
+ }
|
|
|
|
|
+ </>
|
|
|
|
|
+ )
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const praseCheckboxGroupConfig = (config: CheckboxGroupConfig, model: { value: FormValueType }, enable: boolean): (() => VNode) => {
|
|
|
|
|
+ model.value[config.name] = reactive({
|
|
|
|
|
+ value: config.default ?? [],
|
|
|
|
|
+ enable
|
|
|
|
|
+ })
|
|
|
|
|
+ const renderFn = optionsExtRender(config, model)
|
|
|
|
|
+ return () => (
|
|
|
|
|
+ <>
|
|
|
|
|
+ {
|
|
|
|
|
+ model.value[config.name].enable &&
|
|
|
|
|
+ <>
|
|
|
|
|
+ <NFormItem label={config.name} >
|
|
|
|
|
+ <NCheckboxGroup v-model:value={model.value[config.name].value} onUpdate:value={(value: (string|number)[]) => {
|
|
|
|
|
+ const valueArray = value as string[]
|
|
|
|
|
+ config.options.map(opt => {
|
|
|
|
|
+ if (typeof opt === 'string') return
|
|
|
|
|
+ const optName = opt.name
|
|
|
|
|
+ console.log("config.name: ", config.name)
|
|
|
|
|
+ console.log("optName: ", optName)
|
|
|
|
|
+ Object.keys(model.value[config.name].options![optName]).map((key) => {
|
|
|
|
|
+ model.value[config.name].options![optName][key].enable = valueArray.includes(optName)
|
|
|
|
|
+ })
|
|
|
|
|
+ console.log("model.value: ", model.value)
|
|
|
|
|
+ })
|
|
|
|
|
+ }} >
|
|
|
|
|
+ {config.options.map((checkboxOption, index) => {
|
|
|
|
|
+ const optionName = typeof checkboxOption === 'string' ? checkboxOption : checkboxOption.name
|
|
|
|
|
+ return (
|
|
|
|
|
+ <NCheckbox value={optionName} key={optionName} label={optionName} />
|
|
|
|
|
+ )
|
|
|
|
|
+ })}
|
|
|
|
|
+ </NCheckboxGroup>
|
|
|
|
|
+ </NFormItem>
|
|
|
|
|
+ {
|
|
|
|
|
+ renderFn && renderFn.map((vnode) => (vnode()))
|
|
|
|
|
+ }
|
|
|
|
|
+ </>
|
|
|
|
|
+ }
|
|
|
|
|
+ </>
|
|
|
|
|
+ )
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const praseButtonGroupConfig = (config: ButtonGroupConfig, model: { value: FormValueType }, enable: boolean): (() => VNode) => {
|
|
|
|
|
+ model.value[config.name] = reactive({
|
|
|
|
|
+ value: config.default ?? "",
|
|
|
|
|
+ enable
|
|
|
|
|
+ })
|
|
|
|
|
+ const renderFn = optionsExtRender(config, model)
|
|
|
|
|
+ return () => (
|
|
|
|
|
+ <>
|
|
|
|
|
+ {
|
|
|
|
|
+ model.value[config.name].enable &&
|
|
|
|
|
+ <>
|
|
|
|
|
+ <NFormItem label={config.name} >
|
|
|
|
|
+ <NRadioGroup v-model:value={model.value[config.name].value} name={config.name}>
|
|
|
|
|
+ {config.options.map((radioOption, index) => {
|
|
|
|
|
+ const optionName = typeof radioOption === 'string' ? radioOption : radioOption.name
|
|
|
|
|
+ return (
|
|
|
|
|
+ <NRadioButton key={optionName} value={optionName}>
|
|
|
|
|
+ { optionName }
|
|
|
|
|
+ </NRadioButton>
|
|
|
|
|
+ )
|
|
|
|
|
+ })}
|
|
|
|
|
+ </NRadioGroup>
|
|
|
|
|
+ </NFormItem>
|
|
|
|
|
+ {
|
|
|
|
|
+ renderFn && renderFn.map((vnode) => (vnode()))
|
|
|
|
|
+ }
|
|
|
|
|
+ </>
|
|
|
|
|
+ }
|
|
|
|
|
+ </>
|
|
|
|
|
+ )
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const praseSelectConfig = (config: SelectConfig, model: { value: FormValueType }, enable: boolean): (() => VNode) => {
|
|
|
|
|
+ model.value[config.name] = reactive({
|
|
|
|
|
+ value: config.default ?? (config.multiple ? [] : ""),
|
|
|
|
|
+ enable
|
|
|
|
|
+ })
|
|
|
|
|
+ const renderFn = optionsExtRender(config, model)
|
|
|
|
|
+ return () => (
|
|
|
|
|
+ <>
|
|
|
|
|
+ {
|
|
|
|
|
+ model.value[config.name].enable &&
|
|
|
|
|
+ <>
|
|
|
|
|
+ <NFormItem label={config.name} >
|
|
|
|
|
+ <NSelect v-model:value={model.value[config.name].value} options={config.options.map(a => {
|
|
|
|
|
+ const optionName = typeof a === 'string' ? a : a.name
|
|
|
|
|
+ return {label: optionName, value: optionName}
|
|
|
|
|
+ })} multiple={config.multiple} />
|
|
|
|
|
+ </NFormItem>
|
|
|
|
|
+ {
|
|
|
|
|
+ renderFn && renderFn.map((vnode) => (vnode()))
|
|
|
|
|
+ }
|
|
|
|
|
+ </>
|
|
|
|
|
+ }
|
|
|
|
|
+ </>
|
|
|
|
|
+ )
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const praseSwitchConfig = (config: SwitchConfig, model: { value: FormValueType }, enable: boolean): (() => VNode) => {
|
|
|
|
|
+ model.value[config.name] = reactive({
|
|
|
|
|
+ value: config.default ?? false,
|
|
|
|
|
+ enable
|
|
|
|
|
+ })
|
|
|
|
|
+ const renderFn = optionsExtRender(config, model)
|
|
|
|
|
+ return () => (
|
|
|
|
|
+ <>
|
|
|
|
|
+ {
|
|
|
|
|
+ model.value[config.name].enable &&
|
|
|
|
|
+ <>
|
|
|
|
|
+ <NFormItem label={config.name} >
|
|
|
|
|
+ <NSwitch v-model:value={model.value[config.name].value} onUpdate:value={(value: boolean) => {
|
|
|
|
|
+ if (model.value[config.name].child) {
|
|
|
|
|
+ Object.keys(model.value[config.name].child!).map((key) => {
|
|
|
|
|
+ model.value[config.name].child![key].enable = value
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ }} />
|
|
|
|
|
+ </NFormItem>
|
|
|
|
|
+ {
|
|
|
|
|
+ renderFn && renderFn.map((vnode) => (vnode()))
|
|
|
|
|
+ }
|
|
|
|
|
+ </>
|
|
|
|
|
+ }
|
|
|
|
|
+ </>
|
|
|
|
|
+ )
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const praseDatePickerConfig = (config: DatePickerConfig, model: { value: FormValueType }, enable: boolean): (() => VNode) => {
|
|
|
|
|
+ model.value[config.name] = reactive({
|
|
|
|
|
+ value: config.default,
|
|
|
|
|
+ enable
|
|
|
|
|
+ })
|
|
|
|
|
+ return () => (
|
|
|
|
|
+ <>
|
|
|
|
|
+ {
|
|
|
|
|
+ model.value[config.name].enable &&
|
|
|
|
|
+ <NFormItem label={config.name} >
|
|
|
|
|
+ <NDatePicker v-model:value={model.value[config.name].value} type={config.dateType} format={config.formatter} />
|
|
|
|
|
+ </NFormItem>
|
|
|
|
|
+ }
|
|
|
|
|
+ </>
|
|
|
|
|
+ )
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const praseTimePickerConfig = (config: TimePickerConfig, model: { value: FormValueType }, enable: boolean): (() => VNode) => {
|
|
|
|
|
+ model.value[config.name] = reactive({
|
|
|
|
|
+ value: config.default,
|
|
|
|
|
+ enable
|
|
|
|
|
+ })
|
|
|
|
|
+ return () => (
|
|
|
|
|
+ <>
|
|
|
|
|
+ {
|
|
|
|
|
+ model.value[config.name].enable &&
|
|
|
|
|
+ <NFormItem label={config.name} >
|
|
|
|
|
+ <NTimePicker v-model:value={model.value[config.name].value} format={config.formatter} />
|
|
|
|
|
+ </NFormItem>
|
|
|
|
|
+ }
|
|
|
|
|
+ </>
|
|
|
|
|
+ )
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+type RenderMap = {
|
|
|
|
|
+ Input: (config: InputConfig, model: { value: FormValueType }, enable: boolean) => (() => VNode);
|
|
|
|
|
+ InputNumber: (config: InputNumberConfig, model: { value: FormValueType }, enable: boolean) => (() => VNode);
|
|
|
|
|
+ RadioGroup: (config: RadioGroupConfig, model: { value: FormValueType }, enable: boolean) => (() => VNode);
|
|
|
|
|
+ Checkbox: (config: CheckboxConfig, model: { value: FormValueType }, enable: boolean) => (() => VNode);
|
|
|
|
|
+ CheckboxGroup: (config: CheckboxGroupConfig, model: { value: FormValueType }, enable: boolean) => (() => VNode);
|
|
|
|
|
+ ButtonGroup: (config: ButtonGroupConfig, model: { value: FormValueType }, enable: boolean) => (() => VNode);
|
|
|
|
|
+ Select: (config: SelectConfig, model: { value: FormValueType }, enable: boolean) => (() => VNode);
|
|
|
|
|
+ Switch: (config: SwitchConfig, model: { value: FormValueType }, enable: boolean) => (() => VNode);
|
|
|
|
|
+ DatePicker: (config: DatePickerConfig, model: { value: FormValueType }, enable: boolean) => (() => VNode);
|
|
|
|
|
+ TimePicker: (config: TimePickerConfig, model: { value: FormValueType }, enable: boolean) => (() => VNode);
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const itemConfigMap: RenderMap = {
|
|
|
|
|
+ 'Input': praseInputConfig,
|
|
|
|
|
+ 'InputNumber': praseInputNumberConfig,
|
|
|
|
|
+ 'RadioGroup': praseRadioGroupConfig,
|
|
|
|
|
+ 'Checkbox': praseCheckboxConfig,
|
|
|
|
|
+ 'CheckboxGroup': praseCheckboxGroupConfig,
|
|
|
|
|
+ 'ButtonGroup': praseButtonGroupConfig,
|
|
|
|
|
+ 'Select': praseSelectConfig,
|
|
|
|
|
+ 'Switch': praseSwitchConfig,
|
|
|
|
|
+ 'DatePicker': praseDatePickerConfig,
|
|
|
|
|
+ 'TimePicker': praseTimePickerConfig,
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function optionsExtRender(config: SupportItemConfig, model: { value: FormValueType }) {
|
|
|
|
|
+ if ('options' in config) {
|
|
|
|
|
+ if (model.value[config.name].options === undefined) {
|
|
|
|
|
+ model.value[config.name].options = {} as Record<string, FormValueType>
|
|
|
|
|
+ }
|
|
|
|
|
+ return config.options.map((itemOption, index) => {
|
|
|
|
|
+ if (typeof itemOption === 'string') {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ const enable = model.value[config.name].value.includes(itemOption.name)
|
|
|
|
|
+ if (model.value[config.name].options![itemOption.name] === undefined) {
|
|
|
|
|
+ model.value[config.name].options![itemOption.name] = {} as FormValueType
|
|
|
|
|
+ }
|
|
|
|
|
+ return configRender(itemOption.ext, { value: model.value[config.name].options![itemOption.name] }, enable)
|
|
|
|
|
+ }
|
|
|
|
|
+ }).flat().filter(Boolean) as (() => VNode)[] // 移除 null
|
|
|
|
|
+ } else if ('child' in config && config.child) { // boolean 类型选项
|
|
|
|
|
+ const enable = model.value[config.name].value as boolean
|
|
|
|
|
+ if (model.value[config.name].child === undefined) {
|
|
|
|
|
+ model.value[config.name].child = {} as FormValueType
|
|
|
|
|
+ }
|
|
|
|
|
+ return configRender(config.child, { value: model.value[config.name].child! }, enable)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function configRender(config: SupportItemConfig[], model: { value: FormValueType }, enable: boolean) {
|
|
|
|
|
+ return config.map(cfg => {
|
|
|
|
|
+ const type = cfg.type;
|
|
|
|
|
+ const renderFn = itemConfigMap[type as SupportItemType];
|
|
|
|
|
+ if (renderFn) {
|
|
|
|
|
+ return renderFn(cfg as any, { value: model.value }, enable);
|
|
|
|
|
+ }
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }).filter(Boolean) as (() => VNode)[]; // 移除 null
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function praseFormConfig(config: FormConfig) {
|
|
|
|
|
+ return configRender(config, { value: props.formValue }, true)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const formRender = praseFormConfig(props.config)
|
|
|
|
|
+const vnodes = computed(() => formRender.map(fn => fn()))
|
|
|
|
|
+
|
|
|
|
|
+onMounted(() => {})
|
|
|
|
|
+
|
|
|
|
|
+function isStringArray(data: any): data is string[] {
|
|
|
|
|
+ return Array.isArray(data) && data.every(item => typeof item === 'string')
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const getStringList = (v: FormValueType): string[] => {
|
|
|
|
|
+ let stringList: string[] = []
|
|
|
|
|
+ Object.entries(v).forEach(([key, field]) => {
|
|
|
|
|
+ if (typeof field.value === 'boolean' && field.value === true) {
|
|
|
|
|
+ stringList.push(key)
|
|
|
|
|
+ } else if (typeof field.value === 'string' || isStringArray(field.value)) {
|
|
|
|
|
+ stringList.push(...field.value)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (field.child) {
|
|
|
|
|
+ const childList = getStringList(field.child)
|
|
|
|
|
+ stringList.push(...childList)
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ return stringList
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+const getSelectTags = (): string[] => {
|
|
|
|
|
+ return getStringList(props.formValue)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 暴露方法给父组件
|
|
|
|
|
+defineExpose({
|
|
|
|
|
+ getSelectTags
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<style lang="css" scoped>
|
|
|
|
|
+.form-spacing {
|
|
|
|
|
+ margin-top: 16px; /* 上间距 */
|
|
|
|
|
+ margin-bottom: 16px; /* 下间距 */
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.form-spacing n-checkbox-group {
|
|
|
|
|
+ margin-right: 16px;
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|