1 场景
一般表单我们直接默认布局,也就是单列布局,突然有个人员信息表单,需要双列布局的需求,简单实现并拓展下
2 思路
直接无脑div+flex布局实现
3 代码
<template>
<el-form ref="formRef" :model="formData" label-width="80px">
<div class="form-top">
<div class="form-top--left">
<el-form-item label="姓名" prop="name">
<el-input v-model="formData.name" placeholder="请输入姓名" />
</el-form-item>
<el-form-item label="年龄" prop="age">
<el-input v-model="formData.age" placeholder="请输入年龄" />
</el-form-item>
<el-form-item label="性别" prop="sex">
<el-radio-group v-model="formData.sex">
<el-radio :label="1">男</el-radio>
<el-radio :label="2">女</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="formData.email" placeholder="请输入邮箱" />
</el-form-item>
<el-form-item label="排序" prop="sort" placeholder="请输入排序号">
<el-input-number v-model="formData.sort" :min="1" />
</el-form-item>
</div>
<div class="form-top--right">
<el-form-item label="备注" prop="remark">
<el-input
v-model="formData.remark"
type="textarea"
placeholder="请输入备注"
maxlength="100"
show-word-limit
resize="none"
/>
</el-form-item>
<el-form-item label="说明" prop="config">
<el-input
v-model="formData.config"
placeholder="请输入说明"
type="textarea"
show-word-limit
maxlength="200"
:autosize="{ minRows: 6, maxRows: 6 }"
/>
</el-form-item>
</div>
</div>
<div class="form-bottom">
<el-form-item>
<el-button type="primary" @click="handleSubmit">确 定</el-button>
<el-button type="warning" @click="handleClose">取 消</el-button>
</el-form-item>
</div>
</el-form>
</template>
<script setup lang="ts">
const formRef = ref();
const formData = reactive({
remark: "",
age: 10,
config: "",
email: "",
sex: "",
id: null,
name: "",
sort: 0,
});
const handleClose = () => {};
const handleSubmit = () => {};
</script>
<style lang="scss" scoped>
.container {
width: 600px;
margin-top: 100px;
.form-top {
display: flex;
justify-content: space-between;
.form-top--right {
flex: 1;
}
}
.form-bottom {
display: flex;
justify-content: flex-end;
}
}
</style>
这样的无脑实现实在是对不起付出的时间,不嫩复用是最大问题
4 拓展
封装el-form,增加slot
// Form.vue
<template>
<el-form>
<slot></slot>
<div class="form-top" v-if="!slot.default">
<div class="form-top--left" v-if="slot.left">
<slot name="left"></slot>
</div>
<div class="form-top--right" v-if="slot.right">
<slot name="right"></slot>
</div>
</div>
<div class="form-bottom" v-if="slot.bottom">
<slot name="bottom"></slot>
</div>
</el-form>
</template>
<script setup lang="ts">
const slot = useSlots();
</script>
<style lang="scss" scoped>
.form-top {
display: flex;
justify-content: space-between;
.form-top--left {
flex: 1;
}
.form-top--right {
flex: 1;
}
}
.form-bottom {
display: flex;
justify-content: flex-end;
}
</style>
// index.vue
<template>
<Form ref="formRef" :model="formData" label-width="80px">
<template #left>
<el-form-item label="姓名" prop="name">
<el-input v-model="formData.name" placeholder="请输入姓名" />
</el-form-item>
<el-form-item label="年龄" prop="age">
<el-input v-model="formData.age" placeholder="请输入年龄" />
</el-form-item>
<el-form-item label="性别" prop="sex">
<el-radio-group v-model="formData.sex">
<el-radio :label="1">男</el-radio>
<el-radio :label="2">女</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="formData.email" placeholder="请输入邮箱" />
</el-form-item>
<el-form-item label="排序" prop="sort" placeholder="请输入排序号">
<el-input-number v-model="formData.sort" :min="1" />
</el-form-item>
</template>
<template #right>
<el-form-item label="备注" prop="remark">
<el-input
v-model="formData.remark"
type="textarea"
placeholder="请输入备注"
maxlength="100"
show-word-limit
resize="none"
/>
</el-form-item>
<el-form-item label="说明" prop="config">
<el-input
v-model="formData.config"
placeholder="请输入说明"
type="textarea"
show-word-limit
maxlength="200"
:autosize="{ minRows: 6, maxRows: 6 }"
/>
</el-form-item>
</template>
<template #bottom>
<el-form-item>
<el-button type="primary" @click="handleSubmit">确 定</el-button>
<el-button type="warning" @click="handleClose">取 消</el-button>
</el-form-item>
</template>
</Form>
</template>
<script setup lang="ts">
import Form from "./Form.vue";
const formRef = ref();
//添加人员表单
const formData = reactive({
remark: "",
age: 10,
config: "",
email: "",
sex: "",
id: null,
name: "",
sort: 0,
});
const handleClose = () => {};
const handleSubmit = () => {};
</script>
<style lang="scss" scoped>
.form-top {
display: flex;
justify-content: space-between;
.form-top--right {
flex: 1;
}
}
.form-bottom {
display: flex;
justify-content: flex-end;
}
</style>
依然不够通用,因为布局是固定的,如果想要其他布局,要么修改Form.vue,要么重新封装
5 继续拓展
抽出layout,形成Layout.vue组件,拿出祖传技艺slot传递
// Layout.vue
<template>
<div>
<slot></slot>
<div class="form-top" v-if="!slot.default">
<div class="form-top--left" v-if="slot.left">
<slot name="left"></slot>
</div>
<div class="form-top--right" v-if="slot.right">
<slot name="right"></slot>
</div>
</div>
<div class="form-bottom" v-if="slot.bottom">
<slot name="bottom"></slot>
</div>
</div>
</template>
<script setup lang="ts">
const slot = useSlots();
</script>
<style lang="scss" scoped>
.form-top {
display: flex;
justify-content: space-between;
.form-top--left {
flex: 1;
}
.form-top--right {
flex: 1;
}
}
.form-bottom {
display: flex;
justify-content: flex-end;
}
</style>
// Form.vue
<template>
<el-form>
<Layout>
<template v-for="item in Object.keys(slot)" :key="item" #[item]>
<slot :name="item"></slot>
</template>
</Layout>
</el-form>
</template>
<script setup lang="ts">
import Layout from "./Layout.vue";
const slot = useSlots();
</script>
<style lang="scss" scoped></style>
// index.vue
<template>
<Form ref="formRef" :model="formData" label-width="80px">
<template #left>
<el-form-item label="姓名" prop="name">
<el-input v-model="formData.name" placeholder="请输入姓名" />
</el-form-item>
<el-form-item label="年龄" prop="age">
<el-input v-model="formData.age" placeholder="请输入年龄" />
</el-form-item>
<el-form-item label="性别" prop="sex">
<el-radio-group v-model="formData.sex">
<el-radio :label="1">男</el-radio>
<el-radio :label="2">女</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="formData.email" placeholder="请输入邮箱" />
</el-form-item>
<el-form-item label="排序" prop="sort" placeholder="请输入排序号">
<el-input-number v-model="formData.sort" :min="1" />
</el-form-item>
</template>
<template #right>
<el-form-item label="备注" prop="remark">
<el-input
v-model="formData.remark"
type="textarea"
placeholder="请输入备注"
maxlength="100"
show-word-limit
resize="none"
/>
</el-form-item>
<el-form-item label="说明" prop="config">
<el-input
v-model="formData.config"
placeholder="请输入说明"
type="textarea"
show-word-limit
maxlength="200"
:autosize="{ minRows: 6, maxRows: 6 }"
/>
</el-form-item>
</template>
<template #bottom>
<el-form-item>
<el-button type="primary" @click="handleSubmit">确 定</el-button>
<el-button type="warning" @click="handleClose">取 消</el-button>
</el-form-item>
</template>
</Form>
</template>
<script setup lang="ts">
import Form from "./Form.vue";
const formRef = ref();
//添加人员表单
const formData = reactive({
remark: "",
age: 10,
config: "",
email: "",
sex: "",
id: null,
name: "",
sort: 0,
});
const handleClose = () => {};
const handleSubmit = () => {};
</script>
<style lang="scss" scoped>
.form-top {
display: flex;
justify-content: space-between;
.form-top--right {
flex: 1;
}
}
.form-bottom {
display: flex;
justify-content: flex-end;
}
</style>
6 总结
-
布局和数据分离
-
灵活扩展layout
2.1 可以建立多个layout文件,批量引入(或动态引入)Form.vue中 ,Form.vue中利用动态组件component加载各个layout文件
2.2 可以建立多个layout文件,批量引入(或动态引入)LayoutIndex.vue文件中,LayoutIndex.vue文件利用动态组件component加载各个layout文件,然后Form.vue中只渲染LayoutIndex,同时通过传参决定渲染那个layout
-
新增layout时,只需要新增文件(所有layout文件已经被批量引入<或动态引入>),符合“开闭”原则
tip:
批量引入(vite):import.meta.glob(‘./layout/**/*.vue’);
动态引入:defineAsyncComponent(() => import(
./components/${layoutName}.vue
));