我正在参加「掘金·启航计划」, 众所周知,elementUI弹窗没有拖拽效果,想要怎么实现拖拽呢?
解决方案:添加自定义指令;
1.自定义指令,创建文件utils/directive.js
import Vue from 'vue'
// v-dialogDrag: 弹窗拖拽
Vue.directive('dialogDrag', {
bind(el, binding, vnode, oldVnode) {
// const dialogHeaderEl = el.querySelector('.el-dialog__header')
// const dragDom = el.querySelector('.el-dialog')
const dialogHeaderEl = el.querySelector('.htitle')
const dragDom = el.querySelector('.container')
dialogHeaderEl.style.cursor = 'move'
// 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null)
dialogHeaderEl.onmousedown = (e) => {
// 鼠标按下,计算当前元素距离可视区的距离
const disX = e.clientX - dialogHeaderEl.offsetLeft
const disY = e.clientY - dialogHeaderEl.offsetTop
// 获取到的值带px 正则匹配替换
let styL, styT
// 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
if (sty.left.includes('%')) {
styL = +document.body.clientWidth * (+sty.left.replace(/%/g, '') / 100)
styT = +document.body.clientHeight * (+sty.top.replace(/%/g, '') / 100)
} else {
styL = +sty.left.replace(/px/g, '')
styT = +sty.top.replace(/px/g, '')
}
document.onmousemove = function(e) {
// 通过事件委托,计算移动的距离
const l = e.clientX - disX
const t = e.clientY - disY
// 移动当前元素
dragDom.style.left = `${l + styL}px`
dragDom.style.top = `${t + styT}px`
// 将此时的位置传出去
// binding.value({x:e.pageX,y:e.pageY})
}
document.onmouseup = function(e) {
document.onmousemove = null
document.onmouseup = null
}
}
}
})
// v-dialogDragWidth: 弹窗宽度拖大 拖小
Vue.directive('dialogDragWidth', {
bind(el, binding, vnode, oldVnode) {
console.log(el,binding,111111);
const dragDom = binding.value.$el.querySelector('.el-dialog')
// const dragDom = el.querySelector('.container') //改造后
el.onmousedown = (e) => {
// 鼠标按下,计算当前元素距离可视区的距离
const disX = e.clientX - el.offsetLeft
document.onmousemove = function(e) {
e.preventDefault() // 移动时禁用默认事件
// 通过事件委托,计算移动的距离
const l = e.clientX - disX
dragDom.style.width = `${l}px`
}
document.onmouseup = function(e) {
document.onmousemove = null
document.onmouseup = null
}
}
}
})
2.项目main.js文件中全局引入
import './utils/directive' //引入弹窗拖拽指令
3.弹窗组件添加自定义指令
<!-- 弹窗 -->
<mydialog
v-dialogDrag
v-if="isresetPasswordDialog"
ref="resetPassword"
:node-data="multipleSelection"
@toCloseTask="toCloseTask"
@setMultipleSelection="setMultipleSelection"
/>
这样就可以实现拖拽效果啦!
一般elementUI弹窗的样式想要修改比较麻烦,不想修改我们可以自己写一个弹窗
封装弹窗组件
<template>
<!-- 重置密码组件 -->
<div class="mask">
<div class="container">
<!-- 标题 -->
<div class="htitle">
<h3>重置密码</h3>
<div class="el-icon-close" @click="toCloseTask" />
</div>
<div class="form-container">
<el-form
ref="accountFrom"
:model="formData"
:rules="rules"
label-width="100px"
label-position="top"
>
<el-form-item label="已选用户">
<!-- <div class="selected-title">已选用户</div> -->
<template slot-scope="scope">
<div class="selected-users">
<div
v-for="item in formData.userData"
:key="item.id"
class="selected-item"
>
<span>{{ item.personName }}</span>
<span>({{ item.account }})</span>
<span class="el-icon-close" @click="delSelectedItem(item)" />
</div>
</div>
</template>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input
v-model="formData.password"
style="width: 90%"
placeholder="请输入密码,包含数字和英文大小写"
/>
</el-form-item>
<el-form-item label="确认密码" prop="confirmPassword">
<el-input
v-model="formData.confirmPassword"
style="width: 90%"
placeholder="请再次输入密码"
/>
</el-form-item>
</el-form>
</div>
<!-- 提交 取消 -->
<div class="okCancel">
<el-button @click="btnCancel">取 消</el-button>
<el-button
class="button-background-color"
type="primary"
@click="btnOK('ruleForm')"
>提 交</el-button
>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
// 已选行账号信息
nodeData: {
type: Array,
required: true
}
},
data () {
// 校验两次密码是否一致
const validatePass = (rule, value, callback) => {
if (value !== this.formData.password) {
callback(new Error('两次密码不一致'))
} else {
callback()
}
}
return {
// 已选用户数据
// userData: [],
// 表单数据
formData: {
// 已选用户数据
userData: [],
password: '', // 密码
confirmPassword: '' // 确认密码
},
rules: {
password: [{ required: true, message: '请输入新密码', trigger: 'blur' },
{ pattern: /^(?:(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])).{6,18}$/, message: '密码应该为6-18位字母、数字组合', trigger: 'blur' }],
confirmPassword: [{ validator: validatePass, trigger: 'blur' }]
}
}
},
mounted () {
this.setUserData()
},
methods: {
// 关闭弹窗
toCloseTask () {
this.$emit('toCloseTask')
},
// 获取用户数据-回填数据
setUserData () {
this.formData.userData = [...this.nodeData]
// console.log(this.formData.userData, '回填')
},
// 新增 点击确定按钮
btnOK () {
// 手动校验表单
this.$refs.accountFrom.validate(async isOK => {
if (isOK) {
this.$emit('toCloseTask')
}
})
},
// 点击取消按钮 关闭弹窗
btnCancel () {
// this.userData = [],
// 重置数据 因为resetFields 只能重置表单上的数据 编辑中id不能重置
this.formData = {
userData: [],
password: '', // 密码
confirmPassword: '' // 确认密码
}
this.$emit('toCloseTask')
// 清除之前的校验
this.$refs.accountFrom.resetFields()
},
// 删除已选择用户
delSelectedItem (v) {
console.log(v, '删除')
this.formData.userData.forEach((item, index) => {
if (item.id === v.id) { this.formData.userData.splice(index, 1) }
})
this.$emit('setMultipleSelection', [v]) // 取消勾选项
}
}
}
</script>
<style lang="scss" scoped>
.mask {
position: fixed;
z-index: 99;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.3);
.container {
width: 470px;
height: 493px;
// position: fixed;
// right: 428px;
// top: 192px;
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
overflow-y: auto;
background: #fff;
box-shadow: 0px 2px 8px 0px rgba(0, 0, 0, 0.15);
border-radius: 4px 4px 4px 4px;
.htitle {
width: 100%;
background-color: #f5f6f8;
border-radius: 4px 4px 0px 0px;
height: 52px;
display: flex;
justify-content: space-between;
padding: 15px 24px;
h3 {
font-size: 16px;
font-weight: bold;
}
.el-icon-close {
margin-top: 3px;
font-size: 16px;
font-weight: bold;
color: #3372ff;
}
//鼠标悬停变小手
.el-icon-close:hover {
cursor: pointer;
}
}
}
}
.form-container {
padding: 16px 80px 0;
.selected-title {
margin-bottom: 6px;
width: 48px;
height: 17px;
font-size: 12px;
font-family: PingFang SC-Medium, PingFang SC;
font-weight: 500;
color: #262626;
line-height: 17px;
}
.selected-users {
width: 310px;
height: 96px;
background: #ffffff;
border-radius: 4px 4px 4px 4px;
opacity: 1;
border: 1px solid rgba(187, 187, 187, 0.5);
padding: 0 20px;
// display: flex;
overflow-y: auto;
.selected-item {
margin: 6px 6px 6px 0;
padding: 4px 19px 3px 12px;
// width: 97px;
height: 24px;
background: #f2f2f2;
border-radius: 12px 12px 12px 12px;
opacity: 1;
display: inline-block;
font-size: 12px;
font-family: PingFang SC-Medium, PingFang SC;
font-weight: 500;
color: #262626;
line-height: 17px;
.el-icon-close {
margin-left: 3px;
width: 8px;
height: 8px;
color: #1c69d4;
opacity: 1;
}
.el-icon-close:hover {
cursor: pointer;
}
}
}
//表单item样式
.el-form-item {
margin: 24px 0 0 0;
::v-deep .el-form-item__label {
height: 17px;
font-size: 12px;
font-family: PingFang SC-Medium, PingFang SC !important;
font-weight: 500;
color: #262626;
line-height: 17px;
width: 100%;
margin-bottom: 6px;
padding: 0;
}
::v-deep .el-input__inner {
width: 310px;
height: 36px;
background: #ffffff;
border-radius: 4px 4px 4px 4px;
opacity: 1;
border: 1px solid #d9d9d9;
font-size: 13px;
font-family: PingFang SC-Medium, PingFang SC;
font-weight: 500;
color: #bbbbbb;
line-height: 18px;
}
}
}
//按钮样式start
.okCancel {
margin-left: 254px;
margin-top: 50px;
width: 192px;
height: 36px;
}
.el-button {
width: 90px;
height: 36px;
padding: 9px 16px;
}
.button-background-color {
background-color: #3372ff;
margin-left: 12px;
}
//按钮样式end
</style>
主要关注结构和样式的代码:
结构:
<div class="mask">
<div class="container">
<!-- 标题 -->
<div class="htitle">
<h3>重置密码</h3>
<div class="el-icon-close" @click="toCloseTask" />
</div>
<div class="form-container">
</div>
<!-- 提交 取消 -->
<div class="okCancel">
<el-button @click="btnCancel">取 消</el-button>
<el-button
class="button-background-color"
type="primary"
@click="btnOK('ruleForm')"
>提 交</el-button
>
</div>
</div>
</div>
</div>
样式上面代码自行复制。