本人是一个web前端开发工程师,主要是vue框架,整理了一些Vue常用的技术,一方面是分享,一方面是做总结,今后也会一直更新,有好建议的同学欢迎评论区分享 ;-)
Vue专栏:点击此处
Vue组件库专栏:点击此处
Vue2 vs Vue3 专栏:点击此处
Typescript专栏:点击此处
Vue组件库专栏会按顺序执行一下流程,不断完善组件库开发流程
环境状态
vue版本:vue3
是否使用 ts:是
后台管理系统的网站,一个页面无非就是4个常用业务块
那咋们是不是可以将其进行封装成组件呢?
只需要传入一个配置文件就可以了~
项目解构如下:
封装后的展示图如下:
效果图如下:
如果能做成这样,是不是页面就会整洁很多?
一个管理系统的新增跟修改弹框,基本上是样式是一致的,不外乎就是弹出了一个Dialog,里面有一个form表单,表单里面有产品需要的一些字段展示,点击确定,验证了没问题,就向后端发起请求。
那要封装的组件需要什么参数呢:
当然还有其他的配置项,按自身需求自己添加即可...
<PenkForm
:title="title"
:formConfig="formConfig"
:formData="formData"
v-model:visible="visible"
append-to-body
@confirm="saveItem"
>
上述的主要配置有了,还有一些额外的,主要用于传递给elementUI的一些配置,在后面的组件interface接口也有详细的描述
主要是将弹框打开,并且传递formData,新建的时候传空对象,修改传递row,即后台返回的对象。
// 添加数据按钮
function handleAddItem() {
visible.value = true;
title.value='新增';
formData = reactive({});
}
// 修改数据按钮
function handleEditItem(index: number, row: any) {
visible.value = true;
title.value='编辑';
formData = reactive(row);
}
监听二次封装的弹框,如果有@confirm 事件,就说明表单内校验没问题,并且第一个参数携带一个新的formData对象,如果该对象有id,就证明是修改数据;没有id,证明是新增 的数据。
// 新增,编辑数据
function saveItem(form: any) {
if (form.id) {
http.update(form).then((res: any) => {
getList();
ElMessage({
type: "success",
message: "修改成功!",
});
visible.value = false;
});
} else {
http.add(form).then((res: any) => {
getList();
ElMessage({
type: "info",
message: "添加成功!",
});
visible.value = false;
});
}
}
const formConfig = {
formItemConfig: [
{
label: "类型名",
prop: "typeName",
type: "input",
},
{
label: "父级类型名",
prop: "parentId",
type: "select",
data: [],
},
],
rules: {
typeName: [
{
required: true,
message: "请输入类型名",
trigger: "blur",
},
],
},
};
父组件=> 封装组件=> elementUI组件
elementUI 组件=> 封装组件=> watch触发update => 父组件
// 子组件的template
<el-dialog
v-model="visible1"
:fullscreen="props.fullscreen"
:draggable="props.draggable"
:title="props.title"
:width="props.width||'50%'"
:before-close="handleClose"
>
<el-form
ref="ruleFormRef"
label-width="120px"
size="large"
:model="form"
:rules="props.formConfig.rules"
>
部分代码讲解,可以先看代码,看不懂再看…
<template
v-for="item in props.formConfig.formItemConfig"
:key="item.prop"
>
<el-form-item
:label="item.label + ':'"
:style="{ display: item.hidden == true ? 'none' : '' }"
:prop="item.prop"
>
<el-input
v-if="item.type == 'input'"
v-model="form[item.prop]"
:placeholder="item.placeholder || ''"
:clearable="item.clearable"
:disabled="item.disabled"
:type="item.inputType"
:row="item.row"
:style="{ width: item.width + 'px' }"
/>
下边注释很详细了,一般是给调用者看的,如果看不懂,估计还需多多努力~...
这边虽说是Form弹框,但是还是用到了elementUI的两个组件:
1个是el-dialog,1个是el-form。
// 单个form item 的属性
interface formItemConfig {
// 必填
// 标签名
label: string;
// 对应的属性名 如: from.name ~
prop: string;
// 组件所需类型,下拉框或者输入框
type: string;
// 选填-通用
// 宽度
width?: number;
// 占位符
placeholder?: string;
// 是否清空
clearable?: boolean;
// 是否使能
disabled?: boolean;
// 是否隱藏
hidden?: boolean;
// 选填
// 特殊组件参数
// 输入框判断是否文本域
inputType?: string;
// 输入框为文本域的时候的行数
row: number;
// 选填-特殊
// 数据,一般是下拉框之类需要可选项的才用到
data?: any;
}
interface formConfig {
// 每一项的配置
formItemConfig: formItemConfig;
// 规则
// https://element-plus.gitee.io/zh-CN/component/form.html#自定义校验规则
rules: any;
}
interface props {
// 以下是el-form 的配置
// 编辑时候的formData数据
formData: any;
// form配置 包括form item 以及 rules
formConfig: formConfig;
// 以下是el-dialog 的配置
visible: boolean;
// dialog配置
width:string|number;
// 弹框标题
title: string;
// 是否为全屏 Dialog
fullscreen?: boolean;
// 为 Dialog 启用可拖拽功能
draggable?: boolean;
// Dialog 自身是否插入至 body 元素上。 嵌套的 Dialog 必须指定该属性并赋值为 true
"append-to-body"?: boolean;
// 是否可以通过点击 modal 关闭 Dialog
"close-on-click-modal"?: boolean;
}
const props = defineProps<props>();
// 生成事件对象,数组中就是对象名
const emit = defineEmits(["update:visible", "confirm"]);
// 监听父组件的visible,用来简介控制el-dialog的弹框开关,一般是用于开
watch(
() => props.visible,
(n, o) => {
visible1.value = n;
}
);
// 监听el-dialog显示状态,再通过@update:visible 通知父组件,一般是用于关
watch(visible1, (n, o) => {
emit("update:visible", n);
});
// 每次触发,就证明父组件点了修改或者添加的按钮,传递了一个新的formData
// 需要重新给form 赋值,并且,对该表单项进行重置
watch(
() => props.formData,
(n, o) => {
resetForm(ruleFormRef.value)
form = reactive(n);
}
);
// 重置表单
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.resetFields();
};
先对其进行校验,通过再出发@confirm事件
// 确定按钮触发
const confirm = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
await formEl.validate((valid, fields) => {
if (valid) {
emit("confirm", form);
} else {
console.log("error submit!", fields);
ElMessage({
showClose: true,
message: "请完善表单信息!",
type: "error",
});
}
});
};
这里用到了 2个elementUI组件,一个el-dialog,一个el-form。
可以先做el-dialog的配置项,实现visible功能,再去实现el-form的表单展示,一步步完善~
<!--
* @Author: Penk
* @LastEditors: Penk
* @LastEditTime: 2022-11-29 18:02:34
* @FilePath: \front-master\src\components\public\PenkForm.vue
* @email: 492934056@qq.com
-->
<template>
<div class="penk-form-container">
<el-dialog
v-model="visible1"
:fullscreen="props.fullscreen"
:draggable="props.draggable"
:title="props.title"
:width="props.width || '50%'"
>
<el-form
ref="ruleFormRef"
label-width="120px"
size="large"
:model="form"
:rules="props.formConfig.rules"
>
<template
v-for="item in props.formConfig.formItemConfig"
:key="item.prop"
>
<el-form-item
:label="item.label + ':'"
:style="{ display: item.hidden == true ? 'none' : '' }"
:prop="item.prop"
>
<!-- 输入框 -->
<el-input
v-if="item.type == 'input'"
v-model="form[item.prop]"
:placeholder="item.placeholder || ''"
:clearable="item.clearable"
:disabled="item.disabled"
:type="item.inputType"
:row="item.row"
:style="{ width: item.width + 'px' }"
/>
<!-- 下拉框 -->
<el-select
v-else-if="item.type == 'select'"
v-model="form[item.prop]"
:placeholder="item.placeholder"
:clearable="item.clearable"
:disabled="item.disabled"
:style="{ width: item.width + 'px' }"
>
<el-option
v-for="option in item.data"
:key="option.value"
:label="option.label"
:value="option.value"
:disabled="option.disabled"
/>
</el-select>
<!-- 多选框 -->
<el-checkbox-group
v-else-if="item.type == 'checkbox'"
v-model="form[item.prop]"
:placeholder="item.placeholder"
:clearable="item.clearable"
:disabled="item.disabled"
:style="{ width: item.width + 'px' }"
>
<el-checkbox
v-for="option in item.data"
:label="option.value"
:disabled="option.disabled"
>{{ option.label }}</el-checkbox
>
</el-checkbox-group>
<!-- 单选框 -->
<el-radio-group
v-else-if="item.type == 'radio'"
v-model="form[item.prop]"
:placeholder="item.placeholder"
:clearable="item.clearable"
:disabled="item.disabled"
:style="{ width: item.width + 'px' }"
>
<el-radio
:label="option.value"
size="large"
v-for="option in item.data"
>{{ option.label }}</el-radio
>
</el-radio-group>
</el-form-item>
</template>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible1 = false">取消</el-button>
<el-button type="primary" @click="confirm(ruleFormRef)">
确定
</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, watch, defineEmits, onMounted } from "vue";
import { ElMessageBox, ElMessage } from "element-plus";
import type { FormInstance, FormRules } from "element-plus";
let form = reactive({});
const visible1 = ref(true);
const ruleFormRef = ref<FormInstance>();
// 单个form item 的属性
interface formItemConfig {
// 必填
// 标签名
label: string;
// 对应的属性名 如: from.name ~
prop: string;
// 组件所需类型,下拉框或者输入框
type: string;
// 选填-通用
// 宽度
width?: number;
// 占位符
placeholder?: string;
// 是否清空
clearable?: boolean;
// 是否使能
disabled?: boolean;
// 是否隱藏
hidden?: boolean;
// 选填
// 特殊组件参数
// 输入框判断是否文本域
inputType?: string;
// 输入框为文本域的时候的行数
row: number;
// 选填-特殊
// 数据,一般是下拉框之类需要可选项的才用到
data?: any;
}
interface formConfig {
// 每一项的配置
formItemConfig: formItemConfig;
// 规则
// https://element-plus.gitee.io/zh-CN/component/form.html#自定义校验规则
rules: any;
}
interface props {
// 以下是el-form 的配置
// 编辑时候的formData数据
formData: any;
// form配置 包括form item 以及 rules
formConfig: formConfig;
// 以下是el-dialog 的配置
visible: boolean;
// dialog配置
width: string | number;
// 弹框标题
title: string;
// 是否为全屏 Dialog
fullscreen?: boolean;
// 为 Dialog 启用可拖拽功能
draggable?: boolean;
// Dialog 自身是否插入至 body 元素上。 嵌套的 Dialog 必须指定该属性并赋值为 true
"append-to-body"?: boolean;
// 是否可以通过点击 modal 关闭 Dialog
"close-on-click-modal"?: boolean;
}
const props = defineProps<props>();
// 生成事件对象,数组中就是对象名
const emit = defineEmits(["update:visible", "confirm"]);
// 监听父组件的visible,用来简介控制el-dialog的弹框开关,一般是用于开
watch(
() => props.visible,
(n, o) => {
visible1.value = n;
}
);
// 监听el-dialog显示状态,再通过@update:visible 通知父组件,一般是用于关
watch(visible1, (n, o) => {
emit("update:visible", n);
});
// 每次触发,就证明父组件点了修改或者添加的按钮,传递了一个新的formData
// 需要重新给form 赋值,并且,对该表单项进行重置
watch(
() => props.formData,
(n, o) => {
resetForm(ruleFormRef.value);
form = reactive(n);
}
);
// 确定按钮触发
const confirm = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
await formEl.validate((valid, fields) => {
if (valid) {
emit("confirm", form);
} else {
console.log("error submit!", fields);
ElMessage({
showClose: true,
message: "请完善表单信息!",
type: "error",
});
}
});
};
// 重置表单
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.resetFields();
};
onMounted(() => {
// 初始化 配置弹框是否可显示
visible1.value = props.visible;
// 初始化 配置formData
form = reactive(props.formData);
});
</script>
<style lang="less" scoped>
.penk-form-container {
.el-table-border {
border: 1px #eee solid;
}
// :deep(.el-scrollbar__view) {
// width: 100%;
// }
:deep(.el-dialog__body) {
border-top: 1px solid #eee;
}
}
</style>