一些封装的组件

一些封装的组件,基于element-ui

Dialog(弹窗)


在保留当前页面状态的情况下,告知用户并承载相关操作。

Dialog

亮点功能:

  1. 支持自定义Dialog标题
  2. 支持自定义Dialog宽度
  3. 支持全屏显示(需打开Dialog后点击)
  4. Dialog高度自适应,超出屏幕高度的80%可滚动。
  5. 兼容各种设备。

核心代码:

<template>
    <div id="popup">
        <div class="warp"></div>
        <div :class="{container: true, fullscreen:fullscreen}" ref="popup" :style="{width:width, 'margin-left':marginLeft}">
            <div class="title">
                <h3>{{title}}</h3>
                <div class="titleAction">
                    <el-button @click="fullscreen=!fullscreen" class="fullscreenBtn" :icon="fullscreen? 'el-icon-aim' : 'el-icon-full-screen'" :title="fullscreen?'取消全屏':'全屏'"></el-button>
                    <el-button @click="closePopup" class="closePopup" icon="el-icon-close" title="关闭"></el-button>
                </div>
            </div>
            <div class="content">
                <slot></slot>
            </div>
        </div>
    </div>
</template>

<script>
    export default {
        name: "popup",
        props: {
            title: {type: String, required: true},
            width: {type: String, required: false},
            marginLeft: {type: String, required: false},
        }
        data(){
            return{
                currentScrollTop:'',
                fullscreen: false
            }
        },
        methods:{
            closePopup(){
                this.$emit("closePopup")
            },
            useScroll(){
                setTimeout(()=>{
                    let popHeight = this.$refs.popup.clientHeight;
                    let bodyHeight = document.body.clientHeight
                    if(bodyHeight- popHeight < 180){
                        this.$refs.popup.style["overflow-y"] = "scroll";
                        this.$refs.popup.style.height = "80vh"
                    }
                }, 10)
            }
        },
        watch:{
            popHeight(n, o){
                console.log(n, o)
            }
        },
        mounted() {
            this.useScroll()
        },
        created() {
            this.currentScrollTop = document.documentElement.scrollTop || document.body.scrollTop;
            document.body.style.overflow='hidden';
            document.body.style.position='fixed';
            document.body.style.top= this.currentScrollTop+"px";
        },
        beforeDestroy() {
            document.body.style.removeProperty('overflow');
            document.body.style.removeProperty('position');
            document.body.style.removeProperty('top');
            document.body.scrollTop = this.currentScrollTop;
            document.documentElement.scrollTop = this.currentScrollTop;
        },

    }
</script>

<style scoped>
    #popup, .warp{
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}
#popup{
    z-index: 1000;
}
.warp{
    z-index: 1001;
    background-color: #000;
    opacity: .6;
    transition: .1s ease-out all;
}
.container{
    width: 1100px;
    min-height: 30vh;
    background-color: #fff;
    position: absolute;
    left: 50%;
    top: 10vh;
    z-index: 1002;
    margin-left: -550px;
    transition: .5s ease-out all;
    border-radius: 3px;
    max-height: 80%;
    overflow: auto;
}
.fullscreen{
    width: 100%!important;
    min-height: 100vh!important;
    top:0!important;
    height: 100%!important;
    margin-left: -50% !important;
}
.container .title{
    display: flex;
    align-items: center;
    justify-content: space-between;
    height: 50px;
    border-bottom: 1px solid #999;
    padding: 0 15px;
    position: sticky;
    background: #fff;
}
.container .title h3{
    color: #303133;
    letter-spacing: 2px;
    font-size: 19px;
}
.container .title .titleAction button{
    color: #666;
    font-weight: bold;
    padding: 0;
    font-size: 22px;
    border: none;
    background: none;
    margin-right: 10px;
}
.container .title .titleAction button:first-child{
    font-size: 20px;
}
.container .title .closePopup:hover{
    color: #f00;
}
@media (max-width: 1000px) {
    .container{
        width: 100%;
        /*top: 6vh;*/
        margin-left: 0;
        left: 0;
    }
}

@media (max-width: 800px) {
    .container{
        width: 100%;
        left: 10px;
        overflow: auto;
    }
    .container .title{
        width: 800px;
    }
}


/*修改滚动条样式*/
.container::-webkit-scrollbar{
    width:9px;
    height:9px;
}
.container::-webkit-scrollbar-track{
    background: rgb(239, 239, 239);
    border-radius:2px;
}
.container::-webkit-scrollbar-thumb{
    background: #bfbfbf;
    border-radius:10px;
}
.container::-webkit-scrollbar-thumb:hover{
    background: #333;
}
.container::-webkit-scrollbar-corner{
    background: #179a16;
}



</style>

Attributes

参数 说明 类型 可选值 默认值
title Dialog的标题 String - -
width Dialog宽度 String - 1100px
marginLeft Dialog左侧便宜量,居中显示的时候需要是 width/2 String - 550px

Events

事件名称 说明 回调参数
closePopup 关闭Dialog弹窗 -

Slot

name 说明
匿名插槽 Dialog核心内容插槽

上传文件


通过点击或拖拽上传文件,不需要重复写获取文件及验证规则

uploadFile

亮点功能

  1. 样式支持各种自定义
  2. 支持是否拖拽上传
  3. 支持多选
  4. 自定义提示语
  5. 符合实际需求的已上传文件列表显示
  6. 不用重复写文件验证

核心代码

<template>
    <div id="uploadDoc">
        <el-upload class="upload-demo" :drag="drag" action="/" :multiple="multiple" :http-request="uploadFile" :show-file-list="false">
            <div v-if="uploadStyle=='default'">
                <i class="el-icon-upload"></i>
                <div class="el-upload__text" v-html="footnote"></div>
                <div class="el-upload__text">只能上传{{backString(allowFileType)}}文件</div>
            </div>
            <div v-else>
                <slot></slot>
            </div>
        </el-upload>
        <div v-if="multiple">
            <p class="fileList" v-for="(item,index) in fileList">
                <span :title="item.name">
                     <i class="el-icon-document"></i>
                    <a href="javascript:void(0)" class="fileName">{{item.name}}</a>
                </span>
                <i class="close el-icon-circle-close" @click="removeFile(index)" title="删除"></i>
            </p>
        </div>
        <div v-else>
            <p class="fileList" v-if="fileList">
                <span :title="fileList.name">
                    <i class="el-icon-document"></i>
                    <a href="javascript:void(0)" class="fileName">{{fileList.name}}</a>
                </span>
                <i class="close el-icon-circle-close" @click="removeFile(-1)" title="删除"></i>
            </p>
        </div>
    </div>
</template>

<script>
    export default {
        name: "uploadFile",
        props:{
            uploadStyle:{
                default: "default", type:String,
            },
            drag:{default:true, type:Boolean},
            multiple:Boolean,
            allowFileType:{
                default: ()=>{
                    return ["doc", "docx", "xlsx", "zip", "rar"]
                }
            },
            allowFileSize:{
                default:5242880
            },
            footnote:{
                default: `将审核通过文件拖到此处,或<em>点击上传</em>`
            },
        },
        data(){
            return{
                fileList:[]
            }
        },
        methods:{
            backString(item){
                let str = ""
                item.forEach(i=>{
                    str = str + i + "、"
                })
                return str.substring(0, str.length-1)
            },
            uploadFile(fileObj){
                let file = fileObj.file;
                // 文件格式
                let allowFleType = this.allowFileType;
                let uploadFileType = file.name.substring(file.name.lastIndexOf('.')+1, file.name.length);
                if(allowFleType.indexOf(uploadFileType) <= -1){
                    this.$message.error(`${uploadFileType}格式的文件不受支持,请上传:${this.allowFileType}文件`);
                    return false
                }
                // 文件大小
                const fileSize = file.size;
                if(fileSize > this.allowFileSize){
                    this.$message.error(`仅支持${this.allowFileSize/1024/1024}MB以内的文件,请重新上传`);
                    return false
                }
                if(this.multiple){
                    this.fileList.push({file:file, name:file.name});
                }else{
                    this.fileList = {file:file, name:file.name};
                }
                // 与父组件同步上传文件信息
                this.$emit("changeUploadEvent", this.fileList)
            },
            // 删除文件
            removeFile(index){
                if(index>=0){
                    this.fileList.splice(index, 1);
                }else {
                    this.fileList = ''
                }
                 // 与父组件同步上传文件信息
                this.$emit("changeUploadEvent", this.fileList)
            }
        },
        mounted() {
            this.fileList = this.multiple ? [] : '';
        }
    }
</script>

<style scoped>
    #uploadDoc{
        width: 360px;
    }
    .fileList{
        width: 96%;
        display: flex;
        align-items: center;
        justify-content: space-between;
        padding: 3px 5px;
        font-size: 14px;
    }
    .fileList > span {
        display: flex;
        align-items: center;
    }
    .fileList> i{
        color: #999;
    }
    .fileName{
        margin-left: 5px;
        color: #606266;
        letter-spacing: .5px;
        overflow: hidden;
        text-wrap: none;
        white-space: nowrap;
        text-overflow: ellipsis;
        width: 320px;
        display: inline-block;
    }
    .close{
        display: none;
        cursor: pointer;
        font-size: 15px;
    }
    .close:hover{
        color: #67c23a;
    }
    .fileList:hover {
        background-color: #f6f6f6;
        border-radius: 2px;
    }
    .close{
        display: inline-block;
    }
    .close > span .fileName{
        color: #0755a2;
    }
    .uploadBlock .upload-demo >>> .el-upload{
        width: 100%;
        height: 36px;
        border: 1px solid #DCDFE6;
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 12px;
        border-radius: 3px;
    }
    .uploadBlock .upload-demo >>> .el-upload:focus{
        color: #000;
    }
</style>

Attributes

参数 说明 类型 可选值 默认值
uploadStyle 上传文件样式 String 任意 default
drag 是否支持拖拽上传文件 Boolean true/false true
multiple 是否支持上传多个文件(多选) Boolean true/false fasle
allowFileType 允许上传的文件类型 Array - [“doc”, “docx”, “xlsx”, “zip”, “rar”]
allowFileSize 允许上传的文件大小(b) Number - 5242880(5Mb)

Events

事件名称 说明 回调参数
changeUploadEvent 与父组件同步上传文件列表 fileList

Slot

name 说明
匿名插槽 自定义上传文件内容及样式

2021.1.13

回到顶部


亮点功能

自动根据终端屏幕高度及滚动高度显示隐藏回到顶部

核心代码

<template>
    <button v-if="display" class="backTop" title="回到顶部" @click="back()">
        <svg t="1604475304961" class="icon" viewBox="0 0 1025 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2661" width="23" height="23"><path d="M533.204945 247.840277c-11.435995-13.314876-30.184888-13.314876-41.620883 0l-342.378861 399.573165c-11.435995 13.319992-6.434858 24.25659 11.19346 24.25659l128.001279 0c17.621155 0 31.995203 14.379165 31.995203 32.007483l0 256.048609c0 17.626271 14.378142 32.00339 32.001343 32.00339l320.002175 0c17.628318 0 31.996226-14.378142 31.996226-32.00339L704.394887 703.678539c0-17.629342 14.376095-32.007483 32.005437-32.007483l127.999232 0c17.621155 0 22.624339-10.937621 11.185273-24.25659L533.204945 247.840277z" p-id="2662"></path><path d="M192.395911 159.573836l640.002303 0c35.375346 0 64.00064-28.633481 64.00064-64.011897 0-35.382509-28.625294-64.011897-64.00064-64.011897l-640.002303 0c-35.371252 0-63.99757 28.629387-63.99757 64.011897C128.398341 130.940356 157.023635 159.573836 192.395911 159.573836z" p-id="2663"></path></svg>
    </button>
</template>

<script>
    export default {
        name: "back_top",
        data(){
            return{
                backTimer: '',
                scrollHeight: 0,   // 滚轮高度
                clientHeight: 0,   // 当前屏幕可视高度
                scroll: 0,          // 滚轮实时高度
                display: false,     // 默认不显示回到顶部
            }
        },
        methods:{
            back(){
                var self = this;
                this.backTimer = setInterval(function () {
                    this.scrollHeight = document.body.scrollTop || document.documentElement.scrollTop;
                    var speed = this.scrollHeight / 3;
                    if (document.body.scrollTop!=0) {
                        document.body.scrollTop -= speed;
                    }else {
                        document.documentElement.scrollTop -= speed;
                    }
                    if(this.scrollHeight === 0){
                        clearInterval(self.backTimer)
                    }
                }, 30);
            },
            handleScroll(){
                this.scroll = document.body.scrollTop || document.documentElement.scrollTop;
                this.display = this.scroll >= this.clientHeight*1.2 ? true : false;
            },
            // 节流
            throttle (fn, delay = 200) {
                // 设置变量默认为true
                let flag = true;
                // 为了保证this指向,返回一个箭头函数
                return (...args) => {
                    // 判断如果已经在执行就直接return
                    if (!flag) return;
                    // 否则就是没有执行,将状态赋值为false
                    flag = false;
                    // 设置定时器,设置时间
                    setTimeout(() => {
                        // 调用apply方法确保this指向问题
                        fn.apply(this, args);
                        // 最后将状态重新更改为true,以便程序下次执行
                        flag = true;
                    }, delay)
                };
            },
        },
        mounted() {
            this.clientHeight = document.documentElement.clientHeight;
            window.addEventListener('scroll',this.throttle(this.handleScroll))
        }
    }
</script>

<style scoped>
    button.backTop{
        display: block;
        background: #fff;
        border-radius: 4px;
        width: 40px;
        height: 40px;
        box-shadow: 0 1px 3px rgba(20,20,20,.2);
        position: fixed;
        right: 50px;
        bottom: 30px;
        cursor: pointer;
        border: none;
        animation: buttonAnimation .3s;
        transition: all linear .2s;
        z-index: 99;
    }
    button.backTop > i{
        font-size: 24px;
        color: #333333;
    }
    @keyframes buttonAnimation
    {
        from {bottom: 0;}
        to {bottom: 30px;}
    }
</style>

无需传值;无触发其它组件事件


骨架屏


在此标签还未有内容时,先占位;即骨架屏

核心代码:

在全局配置:

// main.js
    import Vue from 'vue';
    Vue.directive('placeholder', {
        inserted(el, binding){
            let {data, config} = binding.value;
            console.log(data, config)
            if(data){
                el.innerText = data;
                return
            }
            // TODO:根据传的配置来显示样式,没有传则如下使用默认样式
            let placeholder = "<span style='display: inline-block;width: 100%;height: 24px;background-color: #ddd;'></span>";
            el.innerHTML = placeholder;
        },
        update(el, binding){
            let {data, config} = binding.value;
            if(data){
                console.log(el.children[0].style)
                el.children[0].style.opacity = "0";
                el.children[0].style.width = "0";
                el.children[0].style.height = "0";
                el.children[0].style.transition = ".2s all";
                setTimeout(()=>{
                    el.removeChild(el.children[0])
                    el.innerHTML = data;
                }, 200)
            }
        }
    })

在组件内配置和使用:

<template>
    <div id="power">
        <span v-placeholder="{data: title, config: {}}" style="color:red"></span>
    </div>
</template>

<script>
    export default {
        name: "power",
        data(){
            return{
                title: ""
            }
        },
        mounted() {
            setTimeout(()=>{
                this.title = "123456"
            }, 1000)
        },
        directives:{
            placeholder: {
                inserted(el, binding){
                    let {data, config} = binding.value;
                    console.log(data, config)
                    if(data){
                        el.innerText = data;
                        return
                    }
                    // TODO:根据传的配置来显示样式,没有传则如下使用默认样式
                    let placeholder = "<span style='display: inline-block;width: 100%;height: 24px;background-color: #ddd;'></span>";
                    el.innerHTML = placeholder;
                },
                update(el, binding){
                    let {data, config} = binding.value;
                    if(data){
                        console.log(el.children[0].style)
                        el.children[0].style.opacity = "0";
                        el.children[0].style.width = "0";
                        el.children[0].style.height = "0";
                        el.children[0].style.transition = ".2s all";
                        setTimeout(()=>{
                            el.removeChild(el.children[0])
                            el.innerHTML = data;
                        }, 200)
                    }
                }
            }
        }
    }
</script>

wait update!!