模仿风宇博客登录蒙层弹窗(vuex+computed实现)

news2024/11/24 14:43:16

效果图

在这里插入图片描述

  • 使用了动态组件做组件切换
  • 使用vue内置的transition组件实现过渡效果,蒙层 和 弹框 都使用了transition,并嵌套
  • vuex + computed计算属性,实现数据 和 方法共享,让其它组件也能够控制到登录弹框
  • 蒙层使用了固定定位
  • 未实现:鼠标滚轮在蒙层上面滚动时,蒙层下面的页面也会滚动。但是,我不想使用body,overflow:hidden,这种方式,这样右侧的滚动条就会消失掉,还没找到方法。(已解决:参考-蒙层禁止页面滚动)
  • 未实现:页面会立即回到顶部(已解决:参考-蒙层禁止页面滚动))
  • 与原博客中有差异,我的是弹出来后,下面的页面静止不动了,仍然有滚动条,但滚动条里的滑块没了
  • 有其他定位相关的问题,影响了其它页面布局(不要用!!!),只是做个效果

代码

App.vue

  • 直接将Login这个组件,放入App.vue中,而Login组件使用固定定位,覆盖掉整个视口,然后,使用flex布局,让弹窗内容在中间。
<template>
  <div id="app">
    <router-view/>
    <Login/>
  </div>
</template>

<script>
import Login from '@/components/model/Login.vue';

  export default {
    name:'App',
    data() {
      return {
        
      }
    },
    components: {
      Login
    }
  }
</script>

store/index.js

  • loginShow控制最外面的蒙层的v-show(即display:none)
  • componentName控制动态组件切换的组件名
  • loginShow 和 componentName都存到vuex中,提供方法入口给外界,去修改这两个值
  • 这两个值,会被Login组件使用,控制展示蒙层 和 切换的动态组件
import Vue from 'vue'
import Vuex from 'vuex'
import router from '@/router'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    loginShow: false,
    componentName: '',
    scrollTop1:0
  },
  getters: {

  },
  mutations: {
    updateComponentName(state, val) {
      state.componentName = val
    },
    updateLoginShow(state, val) {
      state.loginShow = val
    },
    showComponentName(state,componentName){

	  /* 记录此时的滚动距离 */
      let documentTop = document.scrollingElement.scrollTop;
      state.scrollTop1 = documentTop
      
      /* 调用其它mutation中的方法 */
      this.commit('updateComponentName',componentName)
      this.commit('updateLoginShow',true)
		
	  /* 保持滚动距离 */
      this._vm.$nextTick(()=>{
        document.body.style.position = "fixed"
        document.body.style.top = -state.scrollTop1 + "px";
      })
    }
  },
  actions: {
  },
  modules: {
  }
})

Login.vue

  • 给 蒙层(使用了v-show,支持过渡) 和 动态组件(动态组件支持过渡) 添加过渡样式
  • 使用 计算属性 配合使用 vuex
  • 点击蒙层时,调用vuex提供的方法,修改store中的值,然后,计算属性接收到修改的值,从而更新渲染(动态组件消失,蒙层隐藏)
<style lang="scss">
.body-mask {
    position: fixed;
    background-color: rgba(0, 0, 0, .3);
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 999;
}

.mask-content {
    max-height: 90%;
    width: 460px;
    overflow-y: auto;
    overflow-x: hidden;
    background-color: #fff;
    border-radius: 6px;
    padding: 40px 30px 10px;
}


/* 蒙层的动画 */
.mask-enter,
.mask-leave-to {
    opacity: 0;
}

.mask-enter-to,
.mask-leave {
    opacity: 1;

}

.mask-enter-active,
.mask-leave-active {
    transition: all 0.2s;
}

/* 蒙层中的组件切换的动画 */
.v-enter,
.v-leave-to {
    transform: scale(0.8);
    opacity: 0;
}

.v-enter-to,
.v-leave {
    transform: scale(1);
    opacity: 1;

}

.v-enter-active,
.v-leave-active {
    transition: all 0.2s;
}

</style>

<template>
    <transition name="mask">
        <div class="body-mask" v-show="loginShow" ref="bodyMask">
            <transition appear mode="out-in">
                <component :is="componentName" />
            </transition>
        </div>
    </transition>
</template>

<script>
import LoginBox from '@/components/model/LoginBox'
import RegisterBox from '@/components/model/RegisterBox'
import ForgetPwd from '@/components/model/ForgetPwd'
export default {
    name: 'Login',
    data() {
        return {
        }
    },
    computed: {
        componentName: {
            get() {
                return this.$store.state.componentName
            }
        },
        loginShow: {
            get() {
                return this.$store.state.loginShow
            }
        }
    },

    mounted() {
        this.$refs['bodyMask'].onclick = (e) => {
            if (e.target === this.$refs['bodyMask']) {
            
                this.$store.commit('updateComponentName', '')
                this.$store.commit('updateLoginShow', false)
                
                /* 恢复之前的滚动距离 */
                this.$nextTick(()=>{

                    document.body.style.position = "static";
                    document.body.style.top = "auto";
                    
                    /* 始终保留右边的滚动条 */
                    document.body.style.overflowY = 'scroll'
                    document.scrollingElement.scrollTop = this.$store.state.scrollTop1;
                })
            }
        }
    },
    methods: {
        
    },
    components: {
        LoginBox,
        RegisterBox,
        ForgetPwd
    }
}
</script>

LoginBox.vue

直接调用vuex暴露出来的方法,来修改store中的值,Login组件接收到修改的值,从而更新渲染(动态组件更新)

<style lang="scss" scoped>
.mask-content {
        max-height: 90%;
        width: 460px;
        overflow-y: auto;
        overflow-x: hidden;
        background-color: #fff;
        border-radius: 6px;
        // box-sizing: border-box;
        padding: 40px 30px 10px;
    }
    input {
        border: none;
        outline: none;
        border-bottom: 1px solid #9e9e9e;
        padding: 5px 0;
        width: 100%;

        font-size: 16px;

        &::placeholder {
            color: #9f9f9f;
        }
    }

    .form-item {
        margin-bottom: 40px;
        &:last-child {
            margin-bottom: 15px;
        }
    }
    .form-item-name {
        color: #696969;
        font-size: 13px;
        margin-bottom: 2px;
    }

    button {
        border:none;
        cursor: pointer;
        background-color: #2196f3;
        color: #fff;
        width: 100%;
        font-size: 16px;
        padding: 8px;
        border-radius: 4px;
        box-shadow: 0px 1px 2px 1px rgb(0 0 0 / 23%);
    }

    .entrance-tip {
        font-size: 15px;
        display: flex;
        align-items: center;
        justify-content: space-between;
    }

    .entrance-social {
        position: relative;
        display: flex;
        align-items: center;
        justify-content: center;
        height: 20px;

        &::before {
            content: '';
            display: block;
            position: absolute;
            height: 1px;
            background-color: #d8d8d8;
            width: 60%;
        }
        span {
            position: absolute;
            font-size: 13px;
            color: #b5b5b5;
            z-index: 10;
            background-color: #fff;
            padding: 0 10px;
        }
    }

    .social-login {
        display: flex;
        justify-content: center;
    }

    span.iconfont {
        cursor: pointer;
        margin: 0 8px;
        width: 28px;
        height: 28px;
        display: flex;
        align-items: center;
        justify-content: center;
        border-radius: 50%;
        color: #fff;
        font-size: 18px;
        &.icon-weibo {
            background-color: #e05244;
        }
        &.icon-qq {
            background-color: #00aaee;
        }
    }

</style>

<template>
    <div class="mask-content ">
        <div class="form-item">
            <div class="form-item-name">邮箱号</div>
            <input placeholder="请输入您的邮箱"/>
        </div>
        <div class="form-item">
            <div class="form-item-name">密码</div>
            <input placeholder="请输入您的密码"/>
        </div>
        <div class="form-item">
            <button>登录</button>
        </div>
        <div class="form-item">
            <div class="entrance-tip">
                <a href="#" @click="changeComponent('RegisterBox')">立即注册</a>
                <a href="#" @click="changeComponent('ForgetPwd')">忘记密码?</a>
            </div>
        </div>
        <div class="form-item">
            <div class="entrance-social">
                <span>社交账号登录</span>
            </div>
        </div>
        <div class="form-item">
            <div class="social-login">
                <span class="iconfont icon-weibo"></span>
                <span class="iconfont icon-qq"></span>
            </div>
        </div>
    </div>
</template>

<script>

export default {
    name: 'LoginBox',
    methods: {
        changeComponent(name) {
            this.$store.commit('updateComponentName',name)
        }
    },
    components: {
    }
}
</script>

RegisterBox.vue

直接调用vuex暴露出来的方法,来修改store中的值,Login组件接收到修改的值,从而更新渲染(动态组件更新)

<style lang="scss">
.mask-content {
        max-height: 90%;
        width: 460px;
        overflow-y: auto;
        overflow-x: hidden;
        background-color: #fff;
        border-radius: 6px;
        // box-sizing: border-box;
        padding: 40px 30px 10px;
    }
input {
    border: none;
    outline: none;
    border-bottom: 1px solid #9e9e9e;
    padding: 5px 0;
    width: 100%;

    font-size: 16px;

    &::placeholder {
        color: #9f9f9f;
    }
}

.form-item {
    margin-bottom: 40px;

    &:last-child {
        margin-bottom: 15px;
    }
}

.form-item-name {
    color: #696969;
    font-size: 13px;
    margin-bottom: 2px;
}

button {
    border: none;
    cursor: pointer;
    background-color: #f44336;
    color: #fff;
    width: 100%;
    font-size: 16px;
    padding: 8px;
    border-radius: 4px;
    box-shadow: 0px 1px 2px 1px rgb(0 0 0 / 23%);
}

.entrance-tip {
    font-size: 15px;
    align-items: center;
    font-size: 14px;
    color: #333333;

    span {
        margin-right: 5px;
    }
}

.entrance-social {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    height: 20px;

    &::before {
        content: '';
        display: block;
        position: absolute;
        height: 1px;
        background-color: #d8d8d8;
        width: 60%;
    }

    span {
        position: absolute;
        font-size: 13px;
        color: #b5b5b5;
        z-index: 10;
        background-color: #fff;
        padding: 0 10px;
    }
}

.social-login {
    display: flex;
    justify-content: center;
}

span.iconfont {
    cursor: pointer;
    margin: 0 8px;
    width: 28px;
    height: 28px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    color: #fff;
    font-size: 18px;

    &.icon-weibo {
        background-color: #e05244;
    }

    &.icon-qq {
        background-color: #00aaee;
    }
}

.check-code {
    display: flex;

    a {
        word-break: keep-all;
        padding: 5px 10px;
        color: #bdbdbd;
        /* padding-left: 10px; */
        font-size: 13px;
    }
}
</style>

<template>
    <div class="mask-content">
        <div class="form-item">
            <div class="form-item-name">邮箱号</div>
            <input placeholder="请输入您的邮箱" />
        </div>
        <div class="form-item">
            <div class="form-item-name">验证码</div>
            <div class="check-code">
                <input placeholder="请输入位数验证码" />
                <a href="#">发送</a>
            </div>
        </div>
        <div class="form-item">
            <div class="form-item-name">密码</div>
            <input placeholder="请输入您的密码" />
        </div>
        <div class="form-item">
            <button>注册</button>
        </div>
        <div class="form-item">
            <div class="entrance-tip">
                <span>已有账号?</span>
                <a href="#" @click="changeComponent('LoginBox')">登录</a>
            </div>
        </div>
    </div>
</template>

<script>

export default {
    name: 'RegisterBox',
    methods: {
        changeComponent(name) {
            this.$store.commit('updateComponentName',name)
        }
    },
    components: {
    }
}
</script>

ForgetPwd.vue

直接调用vuex暴露出来的方法,来修改store中的值,Login组件接收到修改的值,从而更新渲染(动态组件更新)

<style lang="scss">
.mask-content {
        max-height: 90%;
        width: 460px;
        overflow-y: auto;
        overflow-x: hidden;
        background-color: #fff;
        border-radius: 6px;
        // box-sizing: border-box;
        padding: 40px 30px 10px;
    }
input {
    border: none;
    outline: none;
    border-bottom: 1px solid #9e9e9e;
    padding: 5px 0;
    width: 100%;

    font-size: 16px;

    &::placeholder {
        color: #9f9f9f;
    }
}

.form-item {
    margin-bottom: 40px;

    &:last-child {
        margin-bottom: 15px;
    }
}

.form-item-name {
    color: #696969;
    font-size: 13px;
    margin-bottom: 2px;
}

button {
    border: none;
    cursor: pointer;
    background-color: #4caf50;
    color: #fff;
    width: 100%;
    font-size: 16px;
    padding: 8px;
    border-radius: 4px;
    box-shadow: 0px 1px 2px 1px rgb(0 0 0 / 23%);
}

.entrance-tip {
    font-size: 15px;
    align-items: center;
    font-size: 14px;
    color: #333333;

    span {
        margin-right: 5px;
    }
}

.entrance-social {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    height: 20px;

    &::before {
        content: '';
        display: block;
        position: absolute;
        height: 1px;
        background-color: #d8d8d8;
        width: 60%;
    }

    span {
        position: absolute;
        font-size: 13px;
        color: #b5b5b5;
        z-index: 10;
        background-color: #fff;
        padding: 0 10px;
    }
}

.social-login {
    display: flex;
    justify-content: center;
}

span.iconfont {
    cursor: pointer;
    margin: 0 8px;
    width: 28px;
    height: 28px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    color: #fff;
    font-size: 18px;

    &.icon-weibo {
        background-color: #e05244;
    }

    &.icon-qq {
        background-color: #00aaee;
    }
}

.check-code {
    display: flex;

    a {
        word-break: keep-all;
        padding: 5px 10px;
        color: #bdbdbd;
        /* padding-left: 10px; */
        font-size: 13px;
    }
}
</style>

<template>
    <div class="mask-content ">
        <div class="form-item">
            <div class="form-item-name">邮箱号</div>
            <input placeholder="请输入您的邮箱" />
        </div>
        <div class="form-item">
            <div class="form-item-name">验证码</div>
            <div class="check-code">
                <input placeholder="请输入位数验证码" />
                <a href="#">发送</a>
            </div>
        </div>
        <div class="form-item">
            <div class="form-item-name">密码</div>
            <input placeholder="请输入您的密码" />
        </div>
        <div class="form-item">
            <button>确定</button>
        </div>
        <div class="form-item">
            <div class="entrance-tip">
                <span>已有账号?</span>
                <a href="#" @click="changeComponent('LoginBox')">登录</a>
            </div>
        </div>
    </div>
</template>

<script>

export default {
    name: 'ForgetPwd',
    methods: {
        changeComponent(name) {
            this.$store.commit('updateComponentName',name)
        }
    },
    components: {
    }
}
</script>

NavBar.vue

直接调用vuex暴露出来的方法,来修改store中的值,Login组件接收到修改的值,从而更新渲染(动态组件更新,并展示蒙层)

<li>
    <a class="menu-a" href="#">
        <i class="iconfont icon-denglu"></i>
        <span @click="$store.commit('showComponentName', 'LoginBox')">登录</span>
    </a>
</li>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/448874.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

读取注册表中的REG_QWORD和REG_BINARY(二进制)类型数据

读取注册表中的REG_QWORD和REG_BINARY二进制类型数据 发现的问题注册表中的一些概念&#xff08;统一认识&#xff09;读取代码&#xff08;字节数据大于8的会显示f开头的前缀&#xff09;说明&#xff08;备注&#xff09;改进代码参考链接 发现的问题 首先我们要明确&#x…

【Jetpack】DataBinding 架构组件 ⑥ ( RecyclerView 数据绑定 )

文章目录 一、RecyclerView 数据绑定核心要点1、启用数据绑定 / 导入依赖2、RecyclerView 条目 DataBinding 布局3、自定义 RecyclerView.Adapter 适配器要点 ( 本博客重点 ★ ) 二、RecyclerView 数据绑定源码示例1、build.gradle 构建脚本 ( 启用数据绑定 / 导入依赖 )2、主界…

ggplot中的注释图层annotate

文章目录 介绍利用注释层添加图形利用注释层添加文本利用注释层添加公式 介绍 ggplot作图包中除了常见的geom图层外&#xff0c;还有一个annotate的注释图层&#xff0c;实现对作图对象的额外添加&#xff0c;其添加要素不在ggplot()所接受的数据框中。 利用注释层添加图形 …

家政服务APP小程序开发功能详解

随着人们生活水平的提高&#xff0c;对家政服务的要求也越来越高。而传统的到家政公司寻找服务人员的方法显然已经无法满足人们需求&#xff0c;取而代之的是线上预约家政服务。家政服务App小程序软件可以满足用户在线预约&#xff0c;还可以根据自己的需求定制家政服务、选择家…

【Hadoop-CosDistcp】通过CosDistcp的方式迁移Cos中的数据至HDFS

【Hadoop-CosDistcp】通过CosDistcp的方式迁移Cos中的数据至HDFS 1&#xff09;功能说明2&#xff09;使用环境3&#xff09;下载与安装4&#xff09;原理说明5&#xff09;参数说明6&#xff09;使用示例7&#xff09;迁移 Cos 中的数据至 HDFS 及数据校验7.1.数据迁移7.2.数据…

【Unity入门】16.脚本引用组件

【Unity入门】脚本引用组件 大家好&#xff0c;我是Lampard~~ 欢迎来到Unity入门系列博客&#xff0c;所学知识来自B站阿发老师~感谢 &#xff08;一&#xff09;脚本引用普通组件 &#xff08;1&#xff09;点击控制音频播放 还记得我们的车载音乐AudioSource吗&#xff1f;…

zabbix搭建

1.环境 本实验使用一台centos7主机&#xff0c;关闭了firewalld和selinux服务&#xff0c;zabbix版本为5.0版本&#xff0c;mysql使用版本为5.7版本 若要搭建6.0以上版本的zabbix&#xff0c;则需要使用mysql 8.0以上的版本 其它版本的zabbix可参考zabbix官网:Download and…

YOLOv5+单目实现三维跟踪(python)

YOLOv5单目跟踪&#xff08;python&#xff09; 1. 目标跟踪2. 测距模块2.1 设置测距模块2.2 添加测距 3. 主代码4. 实验效果 相关链接 1. YOLOv5单目测距&#xff08;python&#xff09; 2. YOLOv7单目测距&#xff08;python&#xff09; 3. YOLOv7单目跟踪&#xff08;pytho…

C++练级之初级:第四篇

C练级之初级&#xff1a;第四篇 引用 C练级之初级&#xff1a;第四篇1.引用1.1引用的介绍1.2引用的使用场景1.3常引用 2.引用的底层3.引用的与指针的比较 总结 1.引用 1.1引用的介绍 &#x1f914;首先还是一个问题&#xff0c;引用是解决C语言什么不足&#xff1f; 指针在&am…

Python自动化sql注入:布尔盲注

在sql注入时&#xff0c;使用python脚本可以大大提高注入效率&#xff0c;这里演示一下编写python脚本实现布尔盲注的基本流程&#xff1a; 演示靶场&#xff1a;sqli-labs 布尔盲注 特点&#xff1a;没有回显没有报错&#xff0c;但根据sql语句正常与否返回不同结果&#x…

新手做电商直播带货怎么和快递合作谈价格

新手做电商直播带货怎么和快递合作谈价格达人带货一般怎样的合作模式&#xff1f;#达人带货 #直播带货 #红人 #百收网 跟快递谈价其实是有方法的&#xff0c;快递的价格不是说不能打下来&#xff0c;就是需要你们多一点点的心机。这个视频我就再给你们补充三个方法&#xff0c;…

HCIP之STP

企业网三层架构 线路冗余—二层网络桥接环路 因为路由器的路由表是由相对完善的计算所得&#xff0c;且存在防环规则&#xff1b;故路由器物理链路上实施备份时&#xff0c;一般不会出现环路&#xff1b;但交换转发数据依赖MAC表&#xff08;CAM表&#xff09;&#xff0c;该表…

【模式识别4】YOLO目标检测数据集xml格式转txt格式

YOLO目标检测数据集xml格式转txt格式 1. 转换前的xml格式2. xml格式转txt格式代码2.1 源代码2.2 需要修改的地方 3. 转换后的txt格式 代码资源&#xff1a;voc2txt.py 1. 转换前的xml格式 如果我们使用LabelImg工具标注数据集&#xff0c;生成的xml文件如下&#xff1a; xml…

Linux运维:推荐八款Linux远程连接工具

目录 2、XShell 3、SecureCRT 4、PuTTY 5、WindTerm 6、iTerm2 7、MobaXterm 8、Termius 今天给大家推荐八款Linux远程连接工具&#xff0c;非常实用&#xff0c;希望对大家能有所帮助&#xff01; 1、NxShell NxShell是一款开源的Linux远程管理工具&#xff0c;是我日…

Spring Boot 接口加解密

1. 介绍 在我们日常的Java开发中&#xff0c;免不了和其他系统的业务交互&#xff0c;或者微服务之间的接口调用 如果我们想保证数据传输的安全&#xff0c;对接口出参加密&#xff0c;入参解密。 但是不想写重复代码&#xff0c;我们可以提供一个通用starter&#xff0c;提…

【python数据可视化】基于networkx的10个绘图技巧

一、说明 networkx在02年5月产生&#xff0c;是用python语言编写的软件包&#xff0c;便于用户对复杂网络进行创建、操作和学习。利用networkx可以以标准化和非标准化的数据格式存储网络、生成多种随机网络和经典网络、分析网络结构、建立网络模型、设计新的网络算法、进行网络…

解决java.lang.IllegalStateException: Failed to load ApplicationContext错误~

错误描述&#xff1a; Caused by: org.springframework.beans.factory.BeanDefinitionStoreException: Failed to read candidate component class: file [D:\SSM\SSM_3.22\spring_transaction\target\classes\spring_txAnnotation\Controller\BookController.class]; nested …

装饰器模式C++用法示例

六.装饰器模式 一.装饰器模式1.原理2.适用场景 二.C程序示例1.语法组成2.C示例 一.装饰器模式 1.原理 装饰器模式是一种结构性设计模式&#xff0c;其目的是通过包装对象来扩展其行为而不改变其接口。该模式可以动态地将责任附加到对象上&#xff0c;从而提供一种灵活的替代方…

SQL Server基础 第三章 数据表基本操作(增删改查,不允许保存更改异常!)

往表里插数据我们现在有两种方式第一种是编辑直接修改&#xff0c;第二种是通过查询来修改数据 两种方法的区别 第一种更直接&#xff0c;如果数据量小那么直接改就好了&#xff0c;那如果数据量稍微庞大我们就需要用新建查询来进行表内容的修改了&#xff01;&#xff01;&a…

【Linux】进程信号 --- 信号的产生 保存 捕捉递达

被爱情困住的是傻子 文章目录 一、信号的预备知识1.通过生活例子来理解信号2.迁移到进程上来理解信号 二、信号的发送&#xff08;修改PCB的信号位图&#xff09;1.通过键盘发送信号&#xff08;kill指令 和 热键&#xff09;2.通过系统调用发送信号&#xff08;kill系统调用 和…