自定义vue指令v-drag & 封装自定义可拖拽弹框 & id定义样式、computed实现动态style动态class & 具名插槽name属性定义slot & 引入全局组件 & 定义全局样式

115 篇文章 8 订阅
47 篇文章 4 订阅

自定义vue指令v-drag & 封装自定义可拖拽弹框 & id定义样式、computed实现动态style动态class & 具名插槽name属性定义slot & 引入全局组件 & 定义全局样式

效果

在这里插入图片描述

代码
1、主页面

index.vue

<template>
  <div>
    <el-button type="primary" size="default" @click="abc">点击</el-button>
    <ModalFrame v-model="isShowModel" v-drag="greet"></ModalFrame>
  </div>
</template>
<script>
export default {    
  data() {
    return {
      isShowModel:false,
    }
  },
  methods: {  
    abc(){
      this.isShowModel = true
    },
  }
}
</script>

2、封装组件

2.1、自定义弹框组件

src\components\modalFrame\modalFrame.vue

<template>
  <div id="shade-model" :class="isMark?'bg':'noBg'" v-show="modelValue">
    <div id="model-frame" :style="frameStyle">
      <div class="model-frame-title" v-show="title">
        <slot name="title">
          <p>提示</p>
        </slot>
        <span class="close-model-button" @click.stop="closeModelFrame" v-show="shut">×</span>
      </div>
      <div class="model-frame-content">
        <slot name="content"></slot>
      </div>
      <div class="model-frame-footer">
        <button class="cancel" @click.stop="closeModelFrame" v-show="cancel">取消</button>&emsp;
        <slot name="footer"></slot>
      </div>
    </div>
  </div>
</template>
<script>
  export default {
    props: {
      modelValue: {
        type: Boolean
      },
      width: {
        type: Number,
        default: 460
      },
      height: {
        type: Number,
        default: 270
      },
      cancel: {
        type: Boolean,
        default: true
      },
      title: {
        type: Boolean,
        default: true
      },
      shut: {
        type: Boolean,
        default: true
      },
      isMark:{
        type: Boolean,
        default: true
      }
    },
    methods: {
      //关闭模态框
      closeModelFrame() {
        this.$emit('update:modelValue',false)
        this.$emit('cancelReply')  //通知父组件调用函数
      }
    },
    computed: {
      frameStyle: function() {
        return {
          width: this.width + "px",
          height: this.height + "px",
          marginLeft: -(this.width / 2) + "px",
          marginTop: -(this.height / 2) + "px"
        }
      }
    }
  }
</script>
<style>


  #shade-model{
    width:100%;
    height:100%;
    z-index: 11;
    top: 0;
    left: 0;
    position:fixed;
  }
  .bg{
    background-color:rgba(0,0,0,0.3);
  }
  .noBg{
    background-color:transparent;
  }
  #model-frame{
    background:white;
    position:absolute;
    left:50%;
    top:50%;
    border: 1px solid #cccccc;
    border-radius: 4px;
  }
  #model-frame .close-model-button{
    position: absolute;
    right: 10px;
    top: 12px;
    cursor: pointer;
    border-radius: 50%;
    width: 32px;
    height: 32px;
    text-align: center;
    line-height: 32px;
    color: #999;
  }
  #model-frame .model-frame-title {
    width: 100%;
    height: 56px;
    border-bottom: 1px solid #d9d9d9;
    font-weight: 700;
    font-style: normal;
    font-size: 16px;
    position: relative;
  }
  #model-frame .model-frame-title p{
    padding-left: 24px;
    color: #000;
    height:56px;
    line-height: 56px;
    text-align: left;
  }
  #model-frame .model-frame-content {
    width: 100%;
    overflow: hidden;
    font-size: 14px;
    color: rgb(51, 51, 51);
    box-sizing: border-box;
    padding: 16px 20px 16px;
  }

  #model-frame .model-frame-footer {
    text-align: right;
    border-top: 1px solid #d9d9d9;
    position: absolute;
    bottom: 0;
    height: 56px;
    line-height: 56px;
    width: 100%;
    padding-right: 20px;
    box-sizing: border-box;
  }
  #model-frame .model-frame-footer>div{
    display: inline-block;
  }
  #model-frame .model-frame-footer .cancel {
    background-color: #fff;
    font-size: 14px;
    color: rgb(74, 74, 74);
    text-decoration: underline;
    cursor: pointer;
  }
  #model-frame .model-frame-footer .ok{
    width: 80px;
    height: 32px;
    line-height: 32px;
    text-align: center;
    color: #fff;
    font-weight: 400;
    font-style: normal;
    font-size: 14px;
    background-color: #4385f4;
    border-radius: 23px;
    margin-left: 20px;
    cursor: pointer;
    margin-right: 20px;
  }
  #model-frame .model-frame-footer .ok:hover{
    transition: all .4s;
    background-color: #6CA0F8;
  }
</style>
2.2、自定义vue指令

src\directive\drag.vue

<script>
  export default{
   mounted(el, binding) {
      let oDiv = el;   //当前元素
      let self = this;  //上下文
      oDiv.onmousedown = function (e) {
        if(e.target.value == undefined){
          //鼠标按下,计算当前元素距离可视区的距离
          let disX = e.clientX - oDiv.offsetLeft;
          let disY = e.clientY - oDiv.offsetTop;

          document.onmousemove = function (e) {
            //通过事件委托,计算移动的距离
            let l = e.clientX - disX;
            let t = e.clientY - disY;
            //移动当前元素
            oDiv.style.left = l + 'px';
            oDiv.style.top = t + 'px';
            //将此时的位置传出去
            binding.value({x:e.pageX,y:e.pageY})
          };
          document.onmouseup = function (e) {

            document.onmousemove = null;
            document.onmouseup = null;
          };
        }
      };
    }

  }
</script>
3、注册全局组件和指令

src\main.ts

import { createApp, ref } from 'vue'
import App from './App.vue'
const app = createApp(App)

/*公共模态框组件*/
app.component('ModalFrame', ModalFrame);
import ModalFrame from './components/modalFrame/modalFrame.vue';

/*拖拽*/
app.directive('drag', Drag)
import Drag from './directive/drag.vue'
实例
效果

在这里插入图片描述

代码

index.vue

<template>
  <div>
    <el-button type="primary" size="default" @click="abc">点击</el-button>
    <ModalFrame v-model="isShowModel" v-drag="greet" :width="380" :height="315">
        <template v-slot:title>
          <p>确认报名</p>
        </template>
        <template v-slot:content>
          <div class="gamePage_content">
            <p><span>姓名</span><span>:&emsp;{{ userInfo.userName}}</span></p>
            <p><span>公司名称</span><span>:&emsp;{{ userInfo.baseOrgName }}</span></p>
            <!-- <p><span>所属部门</span><span>:&emsp;{{ userInfo.orgNameChineseTotal }}</span></p>
            <p><span>邮箱</span><span>:&emsp;{{ idCard(userInfo.email)}}</span></p>
            <p><span>联系方式</span><span>:&emsp;{{ idCard(userInfo.mobilePhone)}}</span></p> -->
            <!-- --------------------------------------------------------------------------------------- -->
            <!-- <p><span>姓名</span><span>:&emsp;{{ userInfo.userName | sm }}</span></p>
            <p><span>公司名称</span><span>:&emsp;{{ userInfo.orgName }}</span></p>
            <p><span>所属部门</span><span>:&emsp;{{ userInfo.orgNameChineseTotal }}</span></p>
            <p><span>邮箱</span><span>:&emsp;{{ userInfo.email | sm | idCard }}</span></p>
            <p><span>联系方式</span><span>:&emsp;{{ userInfo.mobilePhone | sm | idCard }}</span></p> -->
            <div class="gamePage_footer">
              <input type="checkbox" v-model="gamePopingAgreement" />&nbsp;同意<span class="gameKnow"
                @click="application">《报名须知》</span><span v-show="allMessage"
                style="font-size:12px;color:red">请勾选同意按钮</span>
            </div>
          </div>
  </div>
</template>
<script>
import { activityEnrollApi  } from '@/api/activity'    
export default {    
  data() {
    return {
      isShowModel:false,
      gamePopingAgreement: false,
    }
  },
  methods: {  
    abc(){
      this.isShowModel = true
    },
    //报名须知
    application() {
      this.gameInformation = true;
    },
    //确定报名
    async gamePopingAgree() {
      try {
        let result = await activityEnrollApi({activityId: this.$route.query.id})
        if(result.data.code == 200){
          this.message.success('活动报名成功,可前往个人中心查看我参与的活动')
          this.showPersonalApply = false
          // this.gonuew = true;
          // if (this.questionType == "0" || this.questionType == "1") {
          //   // 0   1 自抽 null 不抽提
          //   window.sessionStorage.setItem('actId', this.$route.query.id);
          //   this.$router.push("/activity/Randomquestions")
          // }
        }
      } catch (error) {
        console.log(error)
      }
    },
  }
}
</script>
<style lang="scss" >
/*马上报名弹框*/
.modalFrame {
  .gamePage_content {
    p {
      line-height: 28px;

      span:first-child {
        width: 60px;
        display: inline-block;
        text-align: justify;
        text-align-last: justify;
        margin-right: 4px;
      }
    }
  }

  .gamePage_footer {
    margin-top: 10px;
  }
</style>

src\components\constant.js

export default {
    ieopActivity: "/ieop-mtg-activity", // 申请表网关标识
}

src\api\activity\index.js

import axios from '@/utils/request'
import { getApiUrl } from "@/utils/tool";
import  constant  from "@/components/constant";
const baseUrl = getApiUrl();

// 活动报名
export const activityEnrollApi = (params)=>{
    return axios.post(baseUrl + constant.ieopActivity + '/activity/compete/enroll',params)
}

src\utils\tool.js

export function getApiUrl(v) {
  return process.env.VUE_APP_API_HOST
}

.env.dev

VUE_APP_API_HOST = '/gp'

vue.config.js

const { defineConfig } = require('@vue/cli-service')

module.exports = defineConfig({
  transpileDependencies: true,
  lintOnSave: false,

  devServer: {
    proxy: {
      '/gp': {
        target: 'http://27.196.111.15:18080',
        changeOrigin: true,
        pathRewrite: {
          '^/gp': '/'
        }
      },
      // '/img': {
      //   target: 'http://192.168.67.86:9595',
      //   changeOrigin: true,
      //   pathRewrite: {
      //     '^/img': '/'
      //   }
      // },
    },
    port: 9000,
    // host:'27.196.104.110',
    host:'0.0.0.0',
    open:true
  },
})
定义公共样式

public\css\CommonStyle.css

/*列表标题hover效果*/
.listHover:hover{
  color:#4385f4;
}

/* element plus 分页器 位置 */
.pagination_center{
  justify-content: center;
}
.pagination_right{
  justify-content: right;
}

/*
  阴影
*/
.Public-Shaow{
  color: #DCDFE6;
  box-shadow: 0 0 18px 0 rgba(0, 0, 0, 0.15);
}

.mb20{
  margin-bottom: 20px;
}
.ml20{
  margin-left: 20px;
}
.mt20{
  margin-top: 20px;
}
.mr20{
  margin-right: 20px;
}

/* element plus 分页器 位置 */
.pagination_center{
  justify-content: center;
}
.pagination_right{
  justify-content: right;
}

/* div p span 英文换行 */
div,p,span{
  word-break: break-all;
}

.el-popper:focus{
  outline: none !important;
}
.el-input__wrapper:focus{
  outline: none !important;
}
.swiper-button-prev:focus{
  outline: none !important;
}
.swiper-button-next:focus{
  outline: none !important;
}

.el-input-number  .el-input__inner{
  text-align: left !important;
}

src\App.vue

<template>
  <div id="app" >
      <my-header></my-header>
   </div>
</template>
<script setup>
    import "../public/css/CommonStyle.css"
</script>
<style lang="scss" scoped>
#app {
  background: #fff;
  width: 100%;
  height: 100%;
}
</style>
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值