vue之动态表单(优化)

代码资源在这儿 ↑

vue之动态表单优化

  • vue2+js动态表单优化
  • vue3+ts动态表单优化

vue2+js动态表单优化

效果图
vue之动态表单(优化)_第1张图片

目录结构
vue之动态表单(优化)_第2张图片
五个文件的完整代码:

以下是App.vue

<template>
  <div>
    <router-view></router-view>
    <Formpage />
  </div>
</template>

<script>
import Formpage from './views/FormPage.vue';

export default {
  components: {
    Formpage
  },
}
</script>

以下是FormPage.vue

<template>
  <div class="container">
    <FormItemComp :formState="root"></FormItemComp>
  </div>
</template>

<script>
import FormItemComp from '../components/FormItemComp.vue';
import root from './FormPageDatas';

export default {
  components: {
    FormItemComp
  },
  data() {
    return {
      root: root
    }
  }
}
</script>

<style scoped>
.container {
  width: 80%;
  margin: 1em auto;
}
</style>

以下是FormItemComp.vue

<template>
    <el-form>
      <template v-if="formState">
        <el-form-item :label="formState.payload.label">
          <template v-if="formState.type === 'input'">
            <el-input v-model="formState.payload.value"/>
          </template>
          <template v-else-if="formState.type === 'checkbox'">
            <el-checkbox-group v-model="formState.payload.value">
              <el-checkbox v-for="option in formState.payload.options" :key="option.value" :label="option.value">{{ option.label }}</el-checkbox>
            </el-checkbox-group>
          </template>
          <template v-else-if="formState.type === 'radio'">
            <el-radio-group v-model="formState.payload.value">
              <el-radio v-for="option in formState.payload.options" :key="option.value" :label="option.value">{{ option.label }}</el-radio>
            </el-radio-group>
          </template>
          <template v-else-if="formState.type === 'select'">
            <el-select v-model="formState.payload.value">
              <el-option v-for="option in formState.payload.options" :key="option.value" :label="option.label" :value="option.value"/>
            </el-select>
          </template>
        </el-form-item>
        <FormItemComp :form-state="getNext()"/>
      </template>
    </el-form>
</template>

<script>
export default {
  name: 'FormItemComp',
  props: {
    formState: {
      type: Object,
      default: null
    }
  },
  methods: {
    getNext() {
      let current = this.formState;
      if (!current) {
        return null;
      }
      const ancestors = [];
      ancestors.unshift(current);
      while ((current = current.parent)) {
        ancestors.unshift(current);
      }
      return this.formState.next(this.formState, ancestors);
    }
  }
}
</script>

<style scoped>
.el-form-item__label {
  padding-right: 10px !important;
}
</style>

以下是FormItem.js

import Vue from 'vue';

/**
 * 创建表单项
 * @param formItemType string 表单项的类型
 * @param payload object 表单项的label、options、value
 * @param next function 当前选择的值
 * @param parent 上一个表当项
 * @return {{next: ((function(*=, *=): (null|null))|*), parent: null, payload, type}}
 */
export function createFormItem(formItemType, payload, next, parent) {
    if (!next) {
        next = () => null;
    }
    if (!parent) {
        parent = null;
    }
    const nextFunc = function(current, acients) {
        let nextItem = next(current, acients);
        if (!nextItem) {
            return null;
        }
        nextItem.parent = current;
        if (!Vue.observable(nextItem)) {
            nextItem = Vue.observable(nextItem);
        }
        return nextItem;
    };
    return Vue.observable({
        type: formItemType,
        payload: payload,
        next: nextFunc,
        parent: parent,
    });
}

以下是FormPageDatas.js

import {createFormItem} from '@/FormItem';

const item1 = createFormItem(
    'select',
    {
        label: 'test1',
        options: [
            {label: 'test1-1', value: 'test1-1'},
            {label: 'test1-2', value: 'test1-2'},
            {label: 'test1-3', value: 'test1-3'},
        ],
        value: 'test1-1',
    },
    (current) => {
        if (current.payload.value === 'test1-2') {
            return item2;
        } else if (current.payload.value === 'test1-3') {
            return item4;
        } else {
            return null;
        }
    }
);

const item2 = createFormItem('input', {
    label: 'test2',
    value: 'test'
}, (current) => (current.payload.value === 'test2' ? item3 : null));

const item3 = createFormItem(
    'checkbox',
    {
        label: 'test3',
        options: [
            {label: 'test3-1', value: 'test3-1'},
            {label: 'test3-2', value: 'test3-2'},
            {label: 'test3-3', value: 'test3-3'},
        ],
        value: ['test3-2', 'test3-3'],
    },
    (current) => (current.payload.value.includes('test3-1') ? item4 : null)
);

const item4 = createFormItem('radio', {
    label: 'test4',
    options: [
        {label: 'test4-1', value: 'test4-1'},
        {label: 'test4-2', value: 'test4-2'},
        {label: 'test4-3', value: 'test4-3'},
        {label: 'test4-4', value: 'test4-4'},
    ],
    value: 'test4-4',
});

export default item1;

vue3+ts动态表单优化

效果图
vue之动态表单(优化)_第3张图片

目录结构
vue之动态表单(优化)_第4张图片

五个文件的完整代码:

以下是App.vue

<template>
  <div>
    <Formpage />
  </div>
</template>

<script setup lang="ts">
import Formpage from './views/Formpage.vue';
</script>

以下是FormPage.vue

<template>
  <div class="container">
    <FormItemComp :form-state="root"></FormItemComp>
  </div>
</template>

<script setup lang="ts">
import FormItemComp from '../components/FormItemComp.vue';
import root from './FormPageDatas';
</script>

<style scoped>
.container {
  width: 80%;
  margin: 1em auto;
}
</style>

以下是FormItemComp.vue

<template>
  <template v-if="formState">
    <a-form-item :label="formState.payload.label">
      <template v-if="formState.type === 'input'">
        <a-input v-model:value="formState.payload.value" />
      </template>
      <template v-else-if="formState.type === 'checkbox'">
        <a-checkbox-group v-model:value="formState.payload.value">
          <a-checkbox v-for="option in formState.payload.options" :value="option.value">{{ option.label }}</a-checkbox>
        </a-checkbox-group>
      </template>
      <template v-else-if="formState.type === 'radio'">
        <a-radio-group v-model:value="formState.payload.value">
          <a-radio v-for="option in formState.payload.options" :value="option.value">{{ option.label }}</a-radio>
        </a-radio-group>
      </template>
      <template v-else-if="formState.type === 'select'">
        <a-select v-model:value="formState.payload.value">
          <a-select-option v-for="option in formState.payload.options" :value="option.value">{{ option.label }}</a-select-option>
        </a-select>
      </template>
    </a-form-item>
    <FormItemComp :form-state="getNext()"></FormItemComp>
  </template>
</template>

<script setup lang="ts">
import { FormItem } from '../FormItem';

const props = defineProps<{
  formState: FormItem | null;
}>();

function getNext(): FormItem | null {
  let current: FormItem | null = props.formState;
  if (!current) {
    return null;
  }
  const acients = [];
  acients.unshift(current);
  while ((current = current.parent)) {
    acients.unshift(current);
  }
  return props.formState!.next(props.formState!, acients);
}
</script>

<style>
.ant-form-item-label {
  padding-right: 10px !important;
}
</style>

以下是FormItem.ts

import { isReactive, reactive } from 'vue';

export type FormItemType = 'input' | 'select' | 'checkbox' | 'radio';

export interface FormItem {
  type: FormItemType;
  payload: any;
  next: (current: FormItem, acients: FormItem[]) => FormItem | null;
  parent: FormItem | null;
}

export function createFormItem(
  formItemType: FormItem['type'],
  payload: FormItem['payload'],
  next?: FormItem['next'],
  parent?: FormItem['parent']
): FormItem {
  if (!next) {
    next = () => null;
  }
  if (!parent) {
    parent = null;
  }
  const nextFunc: FormItem['next'] = (current, acients) => {
    let nextItem = next!(current, acients);
    if (!nextItem) {
      return null;
    }
    nextItem.parent = current;
    if (!isReactive(nextItem)) {
      nextItem = reactive(nextItem);
    }
    return nextItem;
  };
  const formItem: FormItem = reactive({
    type: formItemType,
    payload,
    next: nextFunc,
    parent,
  });

  return formItem;
}

以下是FormPageDatas.ts

import { createFormItem } from '../FormItem';

const item1 = createFormItem(
  'select',
  {
    label: 'test1',
    options: [
      { label: 'test1-1', value: 'test1-1' },
      { label: 'test1-2', value: 'test1-2' },
      { label: 'test1-3', value: 'test1-3' },
    ],
    value: 'test1-1',
  },
  (current) => {
    if (current.payload.value === 'test1-2') {
      return item2;
    } else if (current.payload.value === 'test1-3') {
      return item4;
    } else {
      return null;
    }
  }
);

const item2 = createFormItem(
    'input',
    { label: 'test2', value: 'test' },
    (current) => (current.payload.value === 'test2' ? item3 : null)
);

const item3 = createFormItem(
  'checkbox',
  {
    label: 'test3',
    options: [
      { label: 'test3-1', value: 'test3-1' },
      { label: 'test3-2', value: 'test3-2' },
      { label: 'test3-3', value: 'test3-3' },
    ],
    value: ['test3-2', 'test3-3'],
  },
  (current) => (current.payload.value.includes('test3-1') ? item4 : null)
);

const item4 = createFormItem('radio', {
  label: 'test4',
  options: [
    { label: 'test4-1', value: 'test4-1' },
    { label: 'test4-2', value: 'test4-2' },
    { label: 'test4-3', value: 'test4-3' },
    { label: 'test4-4', value: 'test4-4' },
  ],
  value: 'test4-4',
});

export default item1;

你可能感兴趣的:(vue.js,前端,javascript)