background
Recently, the company is making a component library. I was assigned to the Dialog component and received the task. Originally, I wanted to use the CV method to “learn” from the dialog component on the market, but the UI student was too imaginative and wanted to fill the dialog with some strange things like this
such
And this…
I’ll write a bee myself
start
The idea is to give the component different styles by passing in different props values
This article refers to two basic components, Overlay and Icon. The code is not posted, and these parts can be implemented by themselves
Template section, the page layout is divided into three parts: header, middle part content, button area button
<template>
<Overlay :show="visible">
<div class="my-dialog-wrapper">
<transition name="my-dialog-bounce">
<div :class="classes">
<Icon
@click="handleAction('close')"
v-if="showClose"
name="extra"
class="my-icon my-icon-quxiao my-dialog-box-close"
/>
<div v-if="imageUrl" class="my-dialog-box-img">
<img :src="imageUrl" alt="" />
</div>
<div v-if="avatar" class="my-dialog-box-avatar">
<img :src="avatar" alt="" />
</div>
<div class="my-dialog-box-title">{{ title }}</div>
<div class="my-dialog-box-content">{{ message }}</div>
<div class="my-dialog-box-tags">
<div
v-for="(item, index) in tags"
:key="`tag${index}`"
class="my-dialog-box-tags-item"
>
{{ item }}
</div>
</div>
<slot name="content"></slot>
<div :class="buttonClasses">
<div
v-if="showCancelButton"
@click="handleAction('cancel')"
:class="cancelBtnCls"
>
{{ cancelText }}
</div>
<div
v-if="showConfirmButton"
@click="handleAction('confirm')"
:class="confirmBtnCls"
>
{{ confirmText }}
</div>
</div>
</div>
</transition>
</div>
</Overlay>
</template>
Copy the code
Js part
Support to use v-model to control dialog hide and display. For details about the passed props modelValue, see the vue3 official documentation
<script>
import Overlay from ".. /overlay";
import Icon from ".. /icon";
import {
defineComponent,
computed,
ref,
watch,
} from "vue";
const prefixCls = "my-dialog";
export default defineComponent({
name: "my-dialog".inheritAttrs: false.props: {
avatar: {
type: String.default: "",},imageUrl: {
type: String.default: "",},theme: {
type: String.default: "",},showConfirmButton: {
type: Boolean.default: true,},showCancelButton: {
type: Boolean.default: false,},buttonSize: {
type: String.default: "normal",},confirmText: {
type: String.default: "Confirm",},cancelText: {
type: String.default: "Cancel",},message: {
type: String.default: "",},tags: {
type: Array.default: () = >[],},showClose: {
type: Boolean.default: false,},modelValue: {
type: null.default: false,},title: {
type: String.default: "",}},components: {
Overlay,
Icon
},
emits: ["update:modelValue"."action"].setup(props, { emit }) {
letvisible = ref(!! props.modelValue);const classes = computed(() = > {
return [
`${prefixCls}-box`,
props.showClose ? `${prefixCls}-box-with-close` : "",
props.imageUrl ? `${prefixCls}-box-no-image` : "",
(props.buttonSize === "large"&& props.showCancelButton) || ! props.theme ?`${prefixCls}-box-no-padding-botm`
: "",]; });const buttonClasses = computed(() = > {
return props.theme === "round"
? [
`${prefixCls}-box-btns`,
props.buttonSize === "large" ? `${prefixCls}-box-btns-large` : ""] : [`${prefixCls}-box-btns-line`,
props.buttonSize === "large" ? `${prefixCls}-box-btns-large` : "",]; });const confirmBtnCls = computed(() = > {
return props.theme === "round"
? [
`${prefixCls}-box-btns-normal`.`${prefixCls}-box-btns-normal-confirm`,
props.buttonSize === "large"
? `${prefixCls}-box-btns-large-confirm`
: ""] : ["line-button".`${prefixCls}-box-btns-line-confirm`];
});
const cancelBtnCls = computed(() = > {
return props.theme === "round"
? [
`${prefixCls}-box-btns-normal`.`${prefixCls}-box-btns-normal-cancel`,
props.buttonSize === "large"
? `${prefixCls}-box-btns-large-cancel`
: ""] : ["line-button".`${prefixCls}-box-btns-line-cancel`];
});
const closeDialog = () = > {
if(! visible)return;
visible.value = false;
emit("update:modelValue", !props.modelValue);
};
const handleAction = (action) = > {
emit('action',action);
closeDialog();
};
/ / listen v - model
watch(() = > props.modelValue,(val) = >{
visible.value = val
})
return{ visible, classes, buttonClasses, cancelBtnCls, confirmBtnCls, closeDialog, handleAction, }; }}); </script>Copy the code
CSS part is not said, you can customize CSS according to their own needs
use
At this point, we can import the components to use, passing in the props we defined
<my-dialog
:title="My first Nuggets article."
:buttonSize="large"
:message="Give me a lot of advice."
:theme="round"
v-model="dialogShow"
showClose
showCancelButton
>
Copy the code
A function call
Do we normally call dialogs like this, and how does this work in VuE3?
this.$dialog
.confirm({
title: this.title,
message: this.content,
theme: "round",
})
.catch(() = > {
console.log("Cancel");
});
Copy the code
Based on the component implementation above, we can create a div element on the page and mount our component to it by dynamically mounting the Component when we call the this.$dialog
Vue.extend is no longer supported in Vue3, and there is no constructor anymore. We use the official render function to implement it
import DialogConstructor from "./Dialog.vue";
import { h, render } from "vue";
const dialogInstance = new Map(a);const initInstance = (props, container) = > {
/ / generated vnode
const vnode = h(DialogConstructor, props);
render(vnode, container);
/ / a mount
document.body.appendChild(container);
// Returns the component instance
return vnode.component;
};
const getContainer = () = > {
return document.createElement("div");
};
const showDialog = (options) = > {
options.onAction = (action) = > {
const currentDialog = dialogInstance.get(vm);
let resolve = action;
if (options.callback) {
options.callback(resolve, instance.proxy);
} else {
if (action === "cancel" || action === "close") {
currentDialog.reject("cancel");
} else{ currentDialog.resolve(resolve); }}};const container = getContainer(); // Get the component container
const instance = initInstance(options, container);
const vm = instance.proxy;
for (const prop in options) {
if (options.hasOwnProperty(prop) && !vm.$props.hasOwnProperty(prop)) {
vm[prop] = options[prop];
}
}
/ / display the dialog
vm.visible = true;
return vm;
};
function Dialog(options) {
let callback;
if (typeof options === "string") {
options = {
message: options,
};
} else {
callback = options.callback;
}
return new Promise((resolve, reject) = > {
const vm = showDialog(options);
dialogInstance.set(vm, {
options,
callback,
resolve,
reject,
});
});
}
Dialog.alert = (options) = > {
return Dialog(Object.assign({}, options, { boxType: "alert" }));
};
Dialog.confirm = (options) = > {
return Dialog(
Object.assign({ showCancelButton: true }, options, { boxType: "confirm"})); }; Dialog.close =() = > {
dialogInstance.forEach((_, vm) = > {
vm.closeDialog();
});
dialogInstance.clear();
};
DialogConstructor.install = (app) = >{
const { name } = DialogConstructor;
app.component(name, DialogConstructor)
}
Dialog.Component = DialogConstructor;
// Register the component
Dialog.install = (app) = > {
app.use(Dialog.Component);
app.config.globalProperties.$dialog = Dialog;
};
export default Dialog;
Copy the code
complete
The above is just some of my output in the learning process, I hope to share with you, if there is a better plan, welcome to discuss ~