废话少说先上图
基本使用
image.png
可以使用输入框 editable设置为true即可
image.png
直接拷贝AlertDialog.ts和alert-dialog.vue,稍微更改一下导入路径及修改css少量样式就可以使用了
使用方式也非常简单 ,一看就会
坐标不传的话 默认居中 ,传了坐标 组件有算法 设置弹窗跟随鼠标位置,不需要自己计算弹窗位置
AlertDialog.show({
title: '提示',
content: '确定要作废该条订单吗?',
left: event.x,
top: event.y,
editable: false,
canceledOnTouchOutside: true,
result: async (res: ResultParam) => {
if (res.confirm) {
console.log('web', '用户确定')
} else if (res.cancle) {
console.log('web', '用户取消')
}
}
})
// 如果不喜欢回调的方式 可以使用await ,即showByAwait方法
// await用法
let res = await AlertDialog.showByAwait({
title: '提示',
content: '确定要作废该条订单吗?',
})
if (res.confirm) {
} else if (res.cancle) {
console.log('web', '用户取消')
}
vue3中已经不再支持用vue.extend来创建组件,那全局弹窗的代替方案是啥呢?这里推荐createApp来创建
下面是完整代码 唯一不同就是你需要更换一下路径,下面看关键类(AlertDialog.ts) createApp方法主要在show里面调用
import {createApp} from 'vue'
import AlertDialogCom from '@assets/components/dialog/alert-dialog.vue'
// prettier-ignore
interface DialogParam {
title?: string
content?: string
left?: number // 弹窗距离左侧的距离 不传则默认居中
top?: number // 弹窗距离顶侧的距离 不传则默认居中
cancelText?: string // 传入空字符串则不会显示取消按钮
sureText?: string // 传入空字符串则不会显示确认按钮
editable?: boolean // 是否显示输入框
canceledOnTouchOutside?: boolean // 点击空白处关闭弹窗
result?: Function // 回调用户操作结果
}
// prettier-ignore
export interface ResultParam {
content: string // editable 为 true 时,用户输入的文本
confirm: boolean // 为 true 时,表示用户点击了确定按钮
cancle: boolean // 为 true 时,表示用户点击了取消
}
export default class AlertDialog{
private static mountNode: HTMLElement | null = null
public static show(param: DialogParam) {
let app = createApp(AlertDialogCom, {...param})
this.mountNode = document.createElement('div')
app.mount(this.mountNode)
document.body.appendChild(this.mountNode)
}
// await用法
// let res = await DialogAialog.showByAwait({
// title: '提示',
// content: '确定要作废该条订单吗?',
// })
// if (res.confirm) {
//
// } else if (res.cancle) {
// console.log('web', '用户取消')
// }
public static showByAwait(param: DialogParam): Promise<ResultParam> {
return new Promise((resolve, reject) => {
param.result = (res: ResultParam) => resolve(res)
this.show(param)
})
}
public static close() {
if (!this.mountNode) return
document.body.removeChild(this.mountNode)
this.mountNode = null
// console.log('close', 'DialogAialog已销毁')
}
}
弹窗组件(alert-dialog.vue)
1.关闭按钮是使用阿里巴巴图标库 可以自己使用图片代替
2.css样式很多都是引用var引用项目配置,可自行修改
<template>
<div class="dialog w-h-100 flex-row flex-center" @click="touchOutside">
<div ref="refContent" class="dialog-content flex-column pd-10-15 relative" :style="style">
<div class="dialog-title flex-row flex-between">
<span>{{ title }}</span>
<i class="dialog-icon-close t1font t1-close pointer" @click="close"></i>
</div>
<div class="content text-secondary">{{ content }}</div>
<div v-if="editable" class="t-input">
<input ref="tInput" v-model="inputContent" type="text" placeholder="" class="t-input__inner" />
</div>
<div class="footer w-100 flex-row flex-end">
<button v-if="sureText.length > 0" @click="result(true)" class="btn-confirm">{{ sureText }}</button>
<button v-if="cancelText.length > 0" @click="result(false)" class="btn-cancel">{{ cancelText }}</button>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
/**
* @用途
* @author Pino
* @创建时间 2023-01-11 15:19
**/
import DialogAialog from '@assets/components/dialog/AlertDialog'
import {computed, onMounted, ref, StyleValue} from 'vue'
const props = defineProps({
title: {type: String, default: '提示'},
content: {type: String, default: '确定要删除该数据吗?'},
left: {type: Number, default: 0},
top: {type: Number, default: 0},
cancelText: {type: String, default: '取消'},
sureText: {type: String, default: '确定'},
editable: {type: Boolean, default: false},
canceledOnTouchOutside: {type: Boolean, default: true},
result: {type: Function}
})
let refContent = ref<HTMLElement | null>(null)
let style = computed(() => {
let style: Partial<StyleValue> = {}
if (props.left || props.top) {
style.position = 'absolute'
let cw = document.documentElement.clientWidth
let ch = document.documentElement.clientHeight
// 弹窗的宽高
let contentH = refContent.value?.offsetHeight || 0
let contentW = refContent.value?.offsetWidth || 0
let offset = 30 // 调整间隔
if (props.left < (cw * 1) / 3) {
// 鼠标点击在屏幕左侧,弹窗要显示在右侧
style.left = props.left + offset + 'px'
let top = props.top
if (ch - top < contentH) top = ch - contentH // 如果鼠标距离最底部 的距离小于弹窗高度
style.top = top + 'px'
} else if (props.left > (cw * 1) / 3 && props.left < (cw * 2) / 3) {
if (props.top < ch / 2) {
// 鼠标在中间上方点击 则弹窗在正下方显示
style.top = props.top + offset + 'px'
} else {
// 鼠标在中间下方点击 则弹窗在正上方显示
style.top = props.top - contentH - offset + 'px'
}
style.left = props.left - contentW / 2 + 'px'
} else {
// 点击屏幕右侧
style.left = props.left - contentW - offset + 'px'
let top = props.top
if (ch - top < contentH) top = ch - contentH // 如果鼠标距离最底部 的距离小于弹窗高度
style.top = top + 'px'
}
}
return style
})
let inputContent = ref('')
let tInput = ref<HTMLElement | null>(null)
onMounted(() => {
if (props.editable) setTimeout(() => tInput.value?.focus(), 100)
})
let touchOutside = (e: any) => {
if (!props.canceledOnTouchOutside) return
if (e.target?.classList?.length && e.target.classList[0] == 'dialog') close()
}
const close = () => DialogAialog.close()
let result = (isSure: boolean) => {
if (props.result) props.result({content: inputContent.value, confirm: isSure == true, cancle: isSure == false})
close()
}
</script>
<style scoped>
@import '@assets/ff-ui/icon-font/t1-icon.css';
.dialog {
position: fixed;
top: 0;
right: 0;
left: 0;
bottom: 0;
z-index: 1024;
background-color: rgba(0, 0, 0, 0.5);
}
.dialog-content {
border-radius: 5px;
box-shadow: 0 1px 3px rgb(0 0 0 / 30%);
width: 400px;
height: max-content;
background-color: var(--bg-2);
box-sizing: border-box;
}
.dialog-title {
color: white;
font-size: 16px;
}
.content {
font-size: 14px;
}
.btn-confirm,
.btn-cancel {
width: 60px;
height: 30px;
border: none;
outline: none;
color: #fff;
background-color: #1664ff;
margin-left: 10px;
border-radius: 3px;
cursor: pointer;
}
.btn-cancel {
color: #409eff;
border: 1px solid #1664ff;
background-color: #fff;
}
.btn-cancel:hover {
background: #ecf5ff;
color: var(--txt-secondary);
}
.btn-confirm:hover {
opacity: 0.8;
}
.dialog-content > div {
margin-top: 10px;
}
.t-input {
font-size: 14px;
display: inline-block;
width: 100%;
}
.t-input__inner {
-webkit-appearance: none;
background-color: #fff;
background-image: none;
border-radius: 4px;
border: 1px solid #dcdfe6;
box-sizing: border-box;
color: #606266;
display: inline-block;
font-size: inherit;
height: 40px;
line-height: 40px;
outline: none;
padding: 0 15px;
transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
width: 100%;
}
.dialog-icon-close {
color: #909399;
line-height: 15px;
font-size: 15px;
}
.dialog-icon-close:hover {
color: var(--color-link);
}
</style>
网友评论