全局el-modal弹窗封装

文章介绍了如何在Vue项目中使用自定义的modal组件,包括引入、混合方法、拖拽指令的实现以及如何调用和销毁弹框。作者展示了如何在main.js中引入并配置该插件,以及在组件中使用示例。

1.主文件

import Vue from 'vue'
import mixins from './mixins'
import store from '@/store'
import router from '@/router'

function getModalInstance(component) {
  if (!component.methods) {
    component.methods = {}
  }
  component.methods.destroy = mixins.methods.destroy
  component.directives = mixins.directives
  const ModalInstanceConstructor = Vue.extend(component)
  return new ModalInstanceConstructor()
}
export const modalObj = {
  show(cmp, payload, callback) {
    return new Promise((resolve, reject) => {
      if (cmp) {
        const Instance = getModalInstance(cmp.default)
        Instance.payload = payload
        Instance.resolve = resolve
        Instance.callback = callback
        // 注入store,router
        Instance.$$store = Instance.$store = store
        Instance.$$router = router
        const component = Instance.$mount()
        document.body.appendChild(component.$el)
        const modal = Instance.$children[0]
        modal.hide = () => {
          Instance.visible = false
          Instance.destroy()
        }
        Instance.visible = true
        // modal.$emit('update:visible', false)
      } else {
        reject(new Error('modal组件未找到'))
      }
    })
  }
}

export default {
  install: vue => {
    vue.prototype.$modal = modalObj
  }
}

2.混合引入

export default {
  methods: {
    data() {
      return {
        visible: false,
        isDestroy: false
      }
    },
    destroy() {
      if (!this.isDestroy) {
        this.visible = false
        this.isDestroy = true
        setTimeout(() => {
          this.$destroy()
          document.body.removeChild(this.$el)
        }, 300)
      }
    }
  },
  directives: {
    drag: {
      bind(el, binding, vnode) {
        const dialogHeaderEl = el.querySelector('.el-dialog__header')
        const dragDom = el.querySelector('.el-dialog')
        dialogHeaderEl.style.cssText += ';cursor:move;'
        dragDom.style.cssText += ';top:0px;'

        // 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
        const getStyle = (() => {
          if (window.document.currentStyle) {
            return (dom, attr) => dom.currentStyle[attr]
          }
          return (dom, attr) => getComputedStyle(dom, false)[attr]
        })()

        dialogHeaderEl.onmousedown = eD => {
          // 鼠标按下,计算当前元素距离可视区的距离
          const disX = eD.clientX - dialogHeaderEl.offsetLeft
          const disY = eD.clientY - dialogHeaderEl.offsetTop

          const dragDomWidth = dragDom.offsetWidth
          const dragDomHeight = dragDom.offsetHeight

          const screenWidth = document.body.clientWidth
          const screenHeight = document.body.clientHeight

          const minDragDomLeft = dragDom.offsetLeft
          const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth

          const minDragDomTop = dragDom.offsetTop
          const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomHeight

          // 获取到的值带px 正则匹配替换
          let styL = getStyle(dragDom, 'left')
          let styT = getStyle(dragDom, 'top')

          if (styL.includes('%')) {
            // eslint-disable-next-line no-useless-escape
            styL = +document.body.clientWidth * (+styL.replace(/\%/g, '') / 100)
            // eslint-disable-next-line no-useless-escape
            styT = +document.body.clientHeight * (+styT.replace(/\%/g, '') / 100)
          } else {
            styL = +styL.replace(/\px/g, '')
            styT = +styT.replace(/\px/g, '')
          }

          document.onmousemove = eM => {
            // 通过事件委托,计算移动的距离
            let left = eM.clientX - disX
            let top = eM.clientY - disY

            // 边界处理
            if (-left > minDragDomLeft) {
              left = -minDragDomLeft
            } else if (left > maxDragDomLeft) {
              left = maxDragDomLeft
            }

            if (-top > minDragDomTop) {
              top = -minDragDomTop
            } else if (top > maxDragDomTop) {
              top = maxDragDomTop
            }

            // 移动当前元素
            dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`

            // emit onDrag event
            vnode.child.$emit('dragDialog')
          }

          document.onmouseup = () => {
            document.onmousemove = null
            document.onmouseup = null
          }
        }
      }
    }
  }
}

3.使用

# 弹框插件使用

调用弹框

```js
// 一、调用弹框
this.$modal.show(require('./modals/openModel'), { openData: row.id }).then((data)=>{})

// 二、弹框获取参数
this.payload.openData

// 三、执行回调
//多类型回调
this.resolve('setting')
//单个回调
this.resolve()
//销毁弹框
this.destroy()
```

4.main.js-------------引入

import modal from '@/utils/Modal'

Element.Dialog.props.closeOnClickModal.default = false

Vue.use(Element, { size: 'mini', zIndex: 3000 })

Vue.use(modal)

<template> <div style=" display: flex; justify-content: center; align-items: center; height: 100%; width: 100%; background-color: #fff; "> <el-dialog :title="dialogMode === 'create' ? '新建' : dialogMode === 'edit' ? '修改' : '查看'" :visible.sync="dialogVisible" :modal-append-to-body="true" append-to-body :close-on-click-modal="false" class="fixed-dialog" width="60%"> <el-form label-width="80px" ref="formRef" :model="currentForm"> <div class="formBorder"> <el-row :gutter="10"> <el-col :span="6"> <el-form-item size="mini" label="项目名称"> <el-input v-model="currentForm.projectName" clearable style="width:100%" size="mini"></el-input> </el-form-item> </el-col> <el-col :span="6"> <el-form-item size="mini" label="项目编号"> <el-input v-model="currentForm.projectCode" clearable style="width:100%" size="mini"></el-input> </el-form-item> </el-col> <el-col :span="12"> <el-form-item size="mini" label="项目周期"> <el-date-picker v-model="projectDate" range-separator="→" start-placeholder="请选择开始日期" end-placeholder="请选择结束日期" type="daterange" size="mini" style="width: 100%;" unlink-panels value-format="yyyy-MM-dd"> </el-date-picker> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="6"> <el-form-item label="负责人" size="mini" style="width: fit-content;"> <el-input v-model="currentForm.projectUser" clearable style="width:100%" size="mini"></el-input> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="24"> <el-form-item label="项目概述"> <el-input v-model="currentForm.remark" :rows="2"></el-input> </el-form-item> </el-col> </el-row> </div> <div class="formBorder2"> <el-container style="height: 100%;"> <el-header> <el-row :gutter="10" type="flex"> <el-col :span="5"><el-select v-model="MaintenanceUnit" placeholder="请选择管养单位" size="mini" clearable> <el-option v-for="item in MaintenanceUnitoptions" :key="item.value" :label="item.label" :value="item.value"></el-option> </el-select> </el-col> <el-col :span="5"><el-select v-model="routeCode" placeholder="请选择路线编号" size="mini" clearable> <el-option v-for="item in routeCodeOptions" :key="item.value" :label="item.label" :value="item.value"></el-option></el-select></el-col> <el-col :span="5"><el-input v-model="formSearch" placeholder="请输入边坡编号或名称" size="mini" clearable> <i slot="suffix" class="el-input__icon el-icon-search"></i> </el-input></el-col> <el-col :span="5"><el-select v-model="evaluateLevel" placeholder="请选择技术状态等级" size="mini" clearable> <el-option v-for="item in evaluateLeveloptions" :key="item.value" :label="item.label" :value="item.value" filterable></el-option></el-select></el-col> <el-col :span="2" :offset="2"><el-button type="primary" size="mini" style="width:100%" icon="el-icon-search" @click="searchForm">搜索</el-button></el-col> </el-row> </el-header> <el-main> <el-table style="width: 100%; overflow-y: auto;" border :data='formTabledata' :header-row-style="{ height: '40px' }" :header-cell-style="{ padding: '0', height: '40px', lineHeight: '40px', textAlign: 'center' }" class="scrollable-table" :cell-style="{ textAlign: 'center' }" @selection-change="handleSelectionChange" :model="sideSlopeDetailList"> <el-table-column type="selection"></el-table-column> <el-table-column label="管养单位" prop="maintenanceCompanyName" width="290"></el-table-column> <el-table-column label="路线编号" prop="routeCode" width="100"></el-table-column> <el-table-column label="边坡编号" prop="sideSlopeCode" width="240"></el-table-column> <el-table-column label="边坡名称" prop="sideSlopeName" width="267"></el-table-column> <el-table-column label="技术状态等级" width="110"> <template slot-scope="scope"> {{ mapEvaluateLevel(scope.row.evaluateLevel) }} </template></el-table-column> </el-table> </el-main> <el-footer> <el-pagination background @size-change="handleSizeChange" @current-change="handleCurrentChange" layout="total, prev, pager, next" :current-page="pageParams.pageNo" :total="total"> </el-pagination> </el-footer> </el-container> </div> </el-form> <!-- 弹窗底部按钮 --> <div slot="footer" class="dialog-footer" v-if="dialogMode === 'create' || dialogMode === 'edit'"> <el-button @click="dialogVisible = false">取消</el-button> <el-button type="primary" @click="submitForm">提交</el-button> </div> </el-dialog> <el-container style="height: 100%;"> <el-header> <el-row :gutter="10" type="flex"> <el-col :span="11"> <el-col :span="6"><el-input v-model="Ceshibianhao" placeholder="项目名称" size="mini"></el-input></el-col> <el-col :span="6"><el-select v-model="selectStatus" placeholder="项目状态" size="mini" clearable> <el-option v-for="item in StatusOptions" :key="item.value" :label="item.label" :value="item.value" filterable></el-option> </el-select></el-col> <el-col :span="11"><el-date-picker v-model="searchProjectDate" range-separator="→" start-placeholder="请选择开始日期" end-placeholder="请选择结束日期" type="daterange" size="mini" style="width: 100%;"> </el-date-picker> </el-col> <el-col :span="1"><el-button type="primary" size="mini">查询</el-button></el-col> </el-col> <el-col :span="1" :offset="1"><el-button type="success" size="mini" style="width:100%" @click="openCreateDialog">新建</el-button></el-col> </el-row> </el-header> <el-main> <el-table :data="tableData" border style="width: 100%;" :main-height="400" :header-row-style="() => { return 'line-height:15px'; }" :cell-style="{ textAlign: 'center' }" :header-cell-style="{ textAlign: 'center' }"> <el-table-column label="序号" type="index" width="120"></el-table-column> <el-table-column label="项目名称" width="250"></el-table-column> <el-table-column label="项目编号" width="150"></el-table-column> <el-table-column label="项目周期" width="250"></el-table-column> <el-table-column label="项目状态" width="200"></el-table-column> <el-table-column label="边坡总数" width="150"></el-table-column> <el-table-column label="已完成边坡数" width="194"></el-table-column> <el-table-column label="完成率" width="150"></el-table-column> <el-table-column label="操作" width="200"> <template slot-scope="scope"> <el-button @click="handleClick(scope.row)" type="text" size="small">查看</el-button> <el-button type="text" size="small">编辑</el-button> <el-button type="text" size="small">删除</el-button> </template> </el-table-column> </el-table> </el-main> <el-footer> <!-- 分页 --> <div class="pagination" style="margin-top:20px;text-align:center;"> <el-pagination background @size-change="handleSizeChange" @current-change="handleCurrentChange" layout="total,prev, sizes, pager, next,jumper" :current-page.sync="pageParams.pageNo" :page-size="pageParams.pageSize" :page-sizes="[10, 40, 60, 100]" :total="pageParams.total"> </el-pagination> </div> </el-footer> </el-container> </div> </template> <script> import { mapCfg } from "@/utils"; import { getPeriodicInspectionPageList, addPeriodicInspection, modifyPeriodicInspection, deletePeriodicInspection, getPeriodicInspectionSideSlopePageList, completePeriodicInspection } from '../../api/testProject'; export default { name: "_Blank", maxins: [], data() { return { Ceshibianhao: '', xiangmuzhuangtai: '', total: 0, pageParams: { pageNo: 1, pageSize: 10, }, dialogpageParams: { pageNo: 1, pageSize: 20 }, tableSelection: [], StatusOptions: [], selectStatus: [], tableData: [], MaintenanceUnitoptions: [], routeCodeOptions: [], checkList: {}, // 页面查询条件专用 searchProjectDate: [], // 弹窗表单专用 dialogProjectDate: [], dialogMode: "create", dialogVisible: false, projectName: '',//项目名称 projectCode: '',//项目编码 projectManager: '',//项目负责人 projectDescription: '',//项目概述 formLevellist: '', formTabledata: [], orgId: '', routeCode: '',//路线 evaluateLeveloptions: [],//技术等级状态 evaluateLevel: '', formSearch: '',////搜索数据 filterForm: { MaintenanceUnit: '', routeCode: '' }, MaintenanceUnit: '',//管养单位 //表单数据 currentForm: { projectCode: '', projectName: '', projectStartDate: '', projectEndDate: '', projectUser: '', remark: '', sideSlopeDetailList: [], }, sideSlopeDetailList: [], } }, computed: { projectDate: { get() { return this.dialogProjectDate; }, set(value) { this.dialogProjectDate = value; if (value && value.length === 2) { this.currentForm.projectStartDate = value[0]; this.currentForm.projectEndDate = value[1]; } } } }, async created() { await this.getEvaluateLevel(); await this.loadAllOptions(); // 先加载所有选项 this.LoadListData(); // 再加载表格数据 this.getStatus(); this.loadTableData(); }, methods: { async loadAllOptions() { // 请求所有管养单位和路线编号(不分页) const params = { orgId: '', routeCode: this.filterForm.routeCode, evaluateLevel: '', SearchKey: '', sideSlopeName: '', pageSize: 2500, pageNo: this.pageParams.pageNo, maintenanceCompanyName: this.filterForm.maintenanceCompanyName }; const res = await getPeriodicInspectionSideSlopePageList(params); // 处理管养单位选项(去重) const maintenanceCompanies = res.entities.map(item => ({ value: item.maintenanceCompanyId, label: item.maintenanceCompanyName })); this.MaintenanceUnitoptions = maintenanceCompanies.reduce((acc, current) => { const exists = acc.some(item => item.value === current.value); if (!exists) acc.push(current); return acc; }, []); // 处理路线编号选项(去重) const routeCodes = res.entities.map(item => item.routeCode); this.routeCodeOptions = [...new Set(routeCodes)].map(code => ({ value: code, label: code })); }, handleSelectionChange(val) { this.tableSelection = val; console.log(this.tableSelection); }, searchForm() { this.filterForm = { maintenanceCompanyName: this.MaintenanceUnit, routeCode: this.routeCode, searchKey: this.sideSlopeCode, }; this.pageParams.pageNo = 1; this.LoadListData(); }, // 映射技术状态等级 mapEvaluateLevel(level) { const option = this.evaluateLeveloptions.find(item => item.value === level); return option ? option.label : level; }, async LoadListData() { const params = { orgId: '', routeCode: this.filterForm.routeCode, evaluateLevel: '', SearchKey: '', sideSlopeName: '', pageSize: this.pageParams.pageSize, pageNo: this.pageParams.pageNo, maintenanceCompanyName: this.filterForm.maintenanceCompanyName } const res = await getPeriodicInspectionSideSlopePageList(params); this.formTabledata = res.entities.map(item => { return { ...item, } }); this.formTabledata = res.entities; this.total = res.entityCount; }, /**分页 */ handleSizeChange(val) { this.pageParams.pageNo = 1; this.pageParams.pageSize = val; this.LoadListData() }, handleCurrentChange(val) { this.pageParams.pageNo = val; this.LoadListData() }, //评定等级 async getEvaluateLevel() { const levelList = await mapCfg('Inspection.Regular.RegularEvaluateLevel')(); this.evaluateLeveloptions = levelList.map(item => ({ value: item.key, label: item.value, })) }, //项目状态字典 async getStatus() { try { const dictList = await mapCfg("Inspection.Periodic.PeriodicInspectionStatus")(); this.StatusOptions = dictList.map(item => ({ value: item.key, label: item.value })); } catch (error) { console.error('获取失败', error); } }, openCreateDialog() { this.dialogMode = 'create'; this.dialogVisible = true; }, async loadTableData() { // const periodicId = '' // const res = await getPeriodicInspectionPageList({ periodicId }); // console.log(res) }, async submitForm() { this.$refs.formRef.validate(async (valid) => { if (valid) { this.submitting = true; try { // 检查边坡选择 if (this.tableSelection.length === 0) { this.$message.warning('请至少选择一个边坡'); return; } // 准备参数 const params = { projectCode: this.currentForm.projectCode, projectName: this.currentForm.projectName, projectStartDate: this.currentForm.projectStartDate, projectEndDate: this.currentForm.projectEndDate, projectUser: this.currentForm.projectUser, remark: this.currentForm.remark, sideSlopeDetailList: this.tableSelection.map(item => ({ sideSlopeUniqueCode: item.sideSlopeCode, // 使用边坡编号作为唯一标识 last_evaluate_level: item.evaluateLevel })) }; console.log(params) // 调用接口 let res; if (this.dialogMode === 'create') { res = await addPeriodicInspection(params); } else if (this.dialogMode === 'edit') { res = await modifyPeriodicInspection(params); } // 处理响应 if (res && res.success) { this.$message.success('操作成功'); this.dialogVisible = false; this.loadTableData(); // 刷新列表 } else { this.$message.error(res.message || '操作失败'); } } catch (error) { console.error('提交失败', error); this.$message.error('提交失败'); } finally { this.submitting = false; } } }); }, } } </script> <style lang="scss" scoped> .el-header { color: #333; text-align: center; line-height: 60px; } .el-main { color: #333; text-align: center; line-height: 100%; padding-left: 5px; padding-top: 0px; } .formBorder { position: relative; /* 添加相对定位 */ border: thin dotted black; padding: 10px; /* 添加上边框文字效果 */ &::before { content: "项目信息"; /* 要显示的文字 */ position: absolute; top: -10px; /* 向上移动一半高度 */ left: 40px; /* 距离左边距 */ background-color: #fff; /* 背景色与页面一致 */ padding: 0 10px; font-size: 14px; color: #606266; } } .formBorder2 { margin-top: 15px; position: relative; /* 添加相对定位 */ border: thin dotted black; padding: 10px; /* 添加上边框文字效果 */ &::before { content: "待检边坡"; /* 要显示的文字 */ position: absolute; top: -10px; /* 向上移动一半高度 */ left: 40px; /* 距离左边距 */ background-color: #fff; /* 背景色与页面一致 */ padding: 0 10px; font-size: 14px; color: #606266; } .el-main { display: flex; } } .dialog-footer { padding: 10px 20px; border-top: 1px solid #ebeef5; text-align: center; } .formBorder2::v-deep .el-main { display: flex; overflow-y: auto; height: 100% !important; } </style> 封装dialog组件 命名为sideSlopeDialog 并给我两个文件的所有代码
最新发布
08-15
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值