代码资源在这儿 ↑
以下是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;
五个文件的完整代码:
以下是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;