在前端开发中,使用 vue + element 封装业务弹框组件是一个比较高频的操作。

随着开发项目的增多,每次遇到这种场景都要去各个项目中翻找以前封装好的弹框组件。

年龄大了,这样找起来比较费脑子,索性记录下来吧,以后直接翻笔记。

Vue2 + Element

使用场景,比如在主业务组件中,一般这样写:

/src/views/xxx/index.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<template>
<div>
<div>这里可能有搜索栏、数据列表等...</div>

<!-- 这里引入业务弹框组件 -->
<SelectDialog :show.sync="businessShow"></SelectDialog>
</div>
</template>

<script>
import SelectDialog from "./select-dialog.vue";

export default {
components: {
SelectDialog, // 注册组件
},
data() {
return {
businessShow: false,
};
},
methods: {},
};
</script>

这个下拉弹框组件的封装方式如下:

/src/views/xxx/select-dialog.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<template>
<div>
<el-dialog :visible="show" title="XX下拉选择弹框" v-loading="dial_loading" width="600px" @close="handleClose">
<div>这里是具体业务组件...</div>

<template slot="footer">
<el-button @click="handleClose" size="small">关 闭</el-button>
<el-button @click="handleConfirm" type="primary" size="small">确 认</el-button>
</template>
</el-dialog>
</div>
</template>

<script>
export default {
props: {
// 外部使用需要加修饰符 show.sync
show: {
type: Boolean,
default: false,
},
},
data() {
return {
dial_loading: false,
};
},
watch: {
show(value) {
if (value) {
console.log("监听到 show 的值有变化:", value);
}
},
},
methods: {
handleConfirm() {
this.dial_loading = true;

// 模拟执行业务
setTimeout(() => {
// 业务执行完关闭弹框
this.dial_loading = false;
this.$modal.msgSuccess("已确认");
this.$emit("confirm");
this.$emit("close");
this.$emit("update:show", false);
this.resetDialog();
}, 3000);
},
handleClose() {
this.$emit("close");
this.$emit("update:show", false);
this.resetDialog();
},

resetDialog() {
// 重置弹框里面的数据,比如有表单、下拉框、筛选框等等
},
},
};
</script>

Vue3 + Element Plus

在 Vue3 + Element Plus 中,稍微有点变化,sync 修饰符没有了。

/src/views/xxx/index.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<div>
<div>这里可能有搜索栏、数据列表等...</div>

<!-- 这里引入业务弹框组件 -->
<SelectDialog v-model="dialog.show"></SelectDialog>
</div>
</template>

<script setup>
import { ref, reactive } from "vue";
import SelectDialog from "./select-dialog.vue";

const dialog = reactive({
show: false,
});
</script>

这个下拉弹框组件的封装方式如下:

/src/views/xxx/select-dialog.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<template>
<div>
<el-dialog :model-value="modelValue" :title="title" @closed="handleClose" width="60vw" top="5vh">
<div>这里是具体业务组件...</div>

<template #footer>
<div class="dialog-footer">
<el-button @click="handleClose">关 闭</el-button>
<el-button @click="handleConfirm" type="primary">确 认</el-button>
</div>
</template>
</el-dialog>
</div>
</template>

<script setup>
import { ref } from "vue";
import { ElMessage } from "element-plus";

const props = defineProps({
modelValue: {
type: Boolean,
default: false,
},
title: {
type: String,
default: "XX下拉选择弹框",
},
});

const emit = defineEmits(["update:modelValue", "confirm", "close"]);

// const dial_loading = ref(false);

// 关闭弹窗
function handleClose() {
emit("update:modelValue", false);
}

function handleConfirm() {
// dial_loading.value = true;

// 模拟执行业务
setTimeout(() => {
// 业务执行完关闭弹框
// dial_loading.value = false;
ElMessage.success("已确认");
emit("confirm");
emit("close");
emit("update:modelValue", false);
resetDialog();
}, 3000);
}

function resetDialog() {
// 重置弹框里面的数据,比如有表单、下拉框、筛选框等等
}
</script>

业务样例(Vue3)

写一个业务样例,方便开发时直接复制:

/src/api/role.api.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import request from "@/utils/request";

const RoleAPI = {
// 查询列表
roleList(query) {
return request({
url: '/api/sys/roles',
method: 'get',
params: query
})
},

// 查询详细
roleInfo(id) {
return request({
url: '/api/sys/role/' + id,
method: 'get'
})
},
// 新增
roleAdd(data) {
return request({
url: '/api/sys/roles',
method: 'post',
data: data
})
},
// 修改
roleUpdate(data) {
return request({
url: '/api/sys/roles',
method: 'put',
data: data
})
},
// 删除
roleRemove(id) {
return request({
url: '/api/sys/role/' + id,
method: 'delete'
})
},
};

export default RoleAPI;

/src/views/system/role-add.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
<template>
<div>
<el-dialog :model-value="modelValue" :title="title" @closed="handleClose"
:width="width" :top="top" :close-on-click-modal="false" append-to-body>
<div>
<el-form ref="form1Ref" :model="form1" :rules="form1Rules" label-width="80px">
<el-form-item label="名称" prop="name">
<el-input v-model="form1.name" placeholder="请输入" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="form1.status">
<el-radio :label="1">启用</el-radio>
<el-radio :label="0">停用</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</div>

<template #footer>
<div class="dialog-footer">
<el-button @click="handleClose">关 闭</el-button>
<el-button @click="handleConfirm" type="primary">确 认</el-button>
</div>
</template>
</el-dialog>
</div>
</template>

<script setup>
import { reactive, ref, watch } from "vue";
import { ElMessage } from "element-plus";
import RoleAPI from '@/api/role.api';

const { proxy } = getCurrentInstance();

const props = defineProps({
modelValue: {
type: Boolean,
default: false,
},
title: {
type: String,
default: "XX下拉选择弹框",
},
width: {
type: String,
default: "60vw",
},
top: {
type: String,
default: "5vh",
},
id: {
type: [String, Number],
},
});

const emit = defineEmits(["update:modelValue", "confirm", "close"]);

// const dial_loading = ref(false);

// 是否为更新模式
const isUpdateMode = ref(false);

const init_form1 = {
name: '',
status: 1
};
const form1Ref = ref();
const form1 = reactive({
id: null,
name: '',
status: 1
});
const form1Rules = reactive({
name: [
{ required: true, message: '名称不能为空', trigger: 'blur' },
// { min: 3, max: 5, message: 'Length should be 3 to 5', trigger: 'blur' },
],
});

watch(() => props.id, (newId) => {
if (newId) {
form1.id = newId;
isUpdateMode.value = true;
} else {
form1.id = null;
isUpdateMode.value = false;
}
});

/** 确认并关闭 */
function confirmClose() {
emit("confirm");
emit("close");
emit("update:modelValue", false);
}

/** 重置弹框里面的数据,比如输入框、下拉框等等 */
function resetDialog() {
Object.assign(form1, init_form1);
form1Ref.value.resetFields();
}

/** 关闭弹窗 */
function handleClose() {
emit("update:modelValue", false);
}

/** 确认按钮 */
function handleConfirm() {
// 验证表单后再提交
form1Ref.value.validate(valid => {
if (valid) {
if (this.isUpdateMode) { // 更新时
RoleAPI.roleUpdate(form1).then(() => {
proxy.$modal.msgSuccess("修改成功");
confirmClose();
resetDialog();
});
} else { // 新增时
RoleAPI.roleAdd(form1).then(() => {
proxy.$modal.msgSuccess("新增成功");
confirmClose();
resetDialog();
});
}
}
});
}
</script>