Vue 管理系统——数据可视化大屏之活动看板

目录

        1.需求背景及重点需求和解决方式

         2.效果实现

         3.遇到的问题以及解决思路


需求背景:      

        需求的最终目的是为了让门店更了解参加活动的实时数据,通过可视化大屏的形式进行展现。此需求在两个项目上开发,唯一的区别就是门店端只会显示当前门店的数据信息,这篇博客我们主要介绍门店端的开发历程。

重点需求:

        1.为了高效还原ui图,手写倒计时正计时组件,Switch组件;

        2.订单金额没隔3位数加,号(可以参考我之前的博客);

        3.二次封装组件,与后端建立长链接保持数据的实时通讯(大屏金额发生变化时数字跳         动效果&&实时弹幕);

        4.使用rem布局适配大屏;

        5.使用原生js实现屏幕百分百显示效果;

        6.使用js完成一些特效;

 

效果实现:

1.手写组件

倒计时正计时组件:

            <div class="active_nam">距离活动结束还剩</div>
            <div class="countdown">
                <div class="div-con">{{days.toString().padStart(2,0)}}</div><div class="div-dw">天</div>
                <div class="div-con">{{hours.toString().padStart(2,0)}}</div><div class="div-dw">时</div>
                <div class="div-con">{{minutes.toString().padStart(2,0)}}</div><div class="div-dw">分</div>
                <div class="div-con">{{seconds.toString().padStart(2,0)}}</div><div class="div-dw">秒</div>
            </div>
            <div class="diachronic">
                活动历时 : {{day.toString().padStart(2,0)}}:{{hour.toString().padStart(2,0)}}:{{minute.toString().padStart(2,0)}}:{{second.toString().padStart(2,0)}}
            </div>

js:

            calculateTheTime(row) {
                // 此处获取时间信息完成倒计时单位转换
                let start = new Date() ? dayjs(new Date()).format("YYYY/MM/DD HH:mm:ss") :""; // 当前最新的时间
				let begin = row.startTime ? dayjs(row.startTime).format("YYYY/MM/DD HH:mm:ss") : "" // 开始时间
				let end = row.endTime ? dayjs(row.endTime).format("YYYY/MM/DD HH:mm:ss") : "" // 结束时间
				let start_num = new Date(start.replace(/-/g, "/"))
				let end_num = new Date(end.replace(/-/g, "/"))
				let begin_num = new Date(begin.replace(/-/g, "/"))
                if(end_num.getTime() > start_num.getTime()){
                    this.countdown = parseInt(end_num.getTime() - start_num.getTime())
                    this.formatDuring(this.countdown)
                    this.diachronic = parseInt(start_num.getTime() - begin_num.getTime())
                    const Duration = parseInt(end_num.getTime() - begin_num.getTime())
                    this.elapsedTime(this.diachronic,Duration)
                }else{
                    this.diachronic = parseInt(end_num.getTime() - begin_num.getTime())
                    const Duration = parseInt(end_num.getTime() - begin_num.getTime())
                    this.elapsedTime(this.diachronic,Duration)
                }
            },
            formatDuring(millisecond) { // 手写倒计时 倒计时时间为0000的时候清除计时器
                this.days = parseInt(millisecond / (1000 * 60 * 60 * 24));
                this.hours = parseInt((millisecond % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
                this.minutes = parseInt((millisecond % (1000 * 60 * 60)) / (1000 * 60));
                this.seconds = ((millisecond % (1000 * 60)) / 1000);
                this.timeID = setInterval(()=>{
                    if(this.seconds>0){
                        this.seconds --
                    }else if(this.minutes>0){
                        this.seconds = 59
                        this.minutes --
                    }
                    else if(this.hours>0){
                        this.minutes = 59
                        this.hours --
                    }
                    else if(this.days>0){
                        this.hours = 23
                        this.days --
                    } 
                    else if(this.days==0){
                        clearInterval(this.timeID)
                    }
				},1000)
            },

css:

        .active_nam{
            margin-top: 0.625rem;
            font-size: 1.3125rem;
            color: #FFF;
        }
        .countdown{
            display: flex;
            margin-top: 1.875rem;
            .div-con{
                font-size: 1.25rem;
                font-weight: 600;
                width: 2.8125rem;
                height: 2.8125rem;
                color: #20209D;
                background-color: #FFF;
                border-radius: 0.3125rem;
                display: flex;
                justify-content: center;
                align-items: center;
            }
            .div-dw{
                font-size: 1.3125rem;
                color: #FFF;
                width: 2.8125rem;
                height: 2.8125rem;
                border-radius: 0.3125rem;
                display: flex;
                justify-content: center;
                align-items: center; 
            }
        }
        .diachronic{
            margin-top: 1.25rem;
            font-size: 1.3125rem;
            color: #FFF;
        }

Switch组件(主要借助伪元素实现效果):

                    <div class="switch-con" @click="barrageIsShow = !barrageIsShow">
                        <div class="outer">
                            <input type="checkbox" :checked="barrageIsShow">
                            <div class="slider"></div>
                        </div>
                    </div>

css:

              .switch-con{
                    margin-top: 1rem;
                    width: 3.8rem;
                    height: 2rem;
                    border-radius: 1.5625rem;
                    padding: 0.187rem 0 0 0.125rem;
                    border: 0.0125rem solid transparent;
                    background-clip: padding-box, border-box;
                    background-origin: padding-box, border-box;
                    background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(33, 31, 48, 0.58))), -webkit-gradient(linear, left top, right top, from(rgba(193, 99, 152, 0.44)), to(rgba(176, 175, 242, 0.58)));
                    .outer{
                        width:3.375rem;
                        height:1.5rem;
                        position: relative;
                        .slider{
                            position: absolute;
                            left:0;
                            top:0;
                            right:0;
                            bottom:0;
                            background:rgba(33, 31, 48, 0.58);
                            border-radius: 1.125rem;
                            transition:  0.4s;
                            cursor: default;
                            line-height: 1.625rem;
                            &::before{
                                content: '';
                                position: absolute;
                                width:1.62rem;
                                height:1.62rem;
                                left:0;
                                top:-0.09rem;
                                border-radius: 50%;
                                transition:  0.4s;
                                box-sizing: border-box;
                                border: 0.0625rem solid transparent;
                                background-clip: padding-box, border-box;
                                background-origin: padding-box, border-box;
                                background-image: -webkit-gradient(linear, left top, left bottom, from(#CF48A9)), -webkit-gradient(linear, left top, right top, from(rgba(116, 30, 79, 1)), to(rgba(255, 255, 255, 0.44)));
                            }
                        }
                        input{
                            display: none;
                            &:checked + .slider{
                                color: #fff;
                                box-sizing: border-box;
                                border: 0.0625rem solid transparent;
                                background-clip: padding-box, border-box;
                                background-origin: padding-box, border-box;
                                background-image: -webkit-gradient(linear, left top, left bottom, from(#F487CD)), -webkit-gradient(linear, left top, right top, from(rgba(61, 12, 31, 0.85)), to(rgba(255, 255, 255, 0.24)));
                            }
                            &:checked + .slider::before{
                                left: 1.75rem;
                            }
                        }
                    }
                }

2.借助 countTo 和 vueBaberrage 组件实施数字跳动以及弹幕效果

        关于这两个组件的使用就不过多介绍大家自行去搜索也很简单

        重点在于建立长链接生成一个唯一key后在把key储存给后端完成链接后通过vux加上侦听器实时监听数据变化并完成css效果,一下是部分代码:

长链接的js文件中:

import client from "./client"
const push = client();
// 这里的client 需要借助 @stomp/stompjs
export function initKey(shopcode) {
    let str = shopcode + "-" + Math.uuid();
    yid.cache.set("pushkey", str);
    push.connect("wss://xxxxx.com/ws" , shopcode + "|" + str , async function (frame) {
        let {webdata,memberBuyInfo} = JSON.parse(frame);
        if(webdata)
        Store.commit("update/setTimely",webdata);
        if(memberBuyInfo)
        Store.commit("update/setBuyInfo",memberBuyInfo);
    }, function (frame) {
        console.error('disconnect', frame);
    });
}

在要是用的组件中侦听: 

            "$store.state.update.webdata": function () { // 获取vuex中数据并操作
                // console.log(this.$store.state.update.webdata,'setTimely徐飞');
                this.refreshData()
                this.data = this.$store.state.update.webdata
                this.startVal = this.endVal // 保证第二次变化的初始值
                this.endVal = this.data.ordersMoney?this.data.ordersMoney:0
            },
            "$store.state.update.memberBuyInfo": function () { // 获取vuex中数据并操作
                // console.log(this.$store.state.update.memberBuyInfo,'memberBuyInfo徐飞');
                let memberBuyInfo = this.$store.state.update.memberBuyInfo
                this.addToList(memberBuyInfo)
            }

弹幕样式更改:

 /*弹幕区域高度*/
    /deep/ .baberrage-stage {
        padding: 12.5rem 2rem;
        width: 87.5rem;
        height: 62.5rem;
    }

    /*弹幕消息框*/
    /deep/ .baberrage-item .normal {
        padding: 0 1.25rem;
        background: none;
    }

    // /*头像*/
    /deep/ .baberrage-item .normal .baberrage-avatar {
        width: 2.875rem;
        height: 2.875rem;
        border-radius: 50%;
        border: 0.0625rem solid transparent;
        background-clip: padding-box, border-box;
        background-origin: padding-box, border-box;
        background-image: linear-gradient(rgba(193, 99, 152, 0.44)), linear-gradient(to right, rgba(193, 99, 152, 0.44), rgba(176, 175, 242, 0.58));
        img{
            width: 2.875rem;
            height: 2.875rem;
            border-radius: 50%;
        }
    }

    // /*文字*/
    /deep/ .baberrage-item .normal .baberrage-msg {
        height: 2rem;
        margin-top: 0.4375rem;
        margin-left: -0.5rem;
        font-size: 1.125rem;
        line-height: 2rem;
        background: rgba(0,0,0,0.2)!important;
        border-radius:0 1.5625rem 1.5625rem 0;
        border: 0.0563rem solid rgba(193, 99, 152, 0.44);
        border-left: none;
        padding: 0 1.25rem;
    }

3.原生js实现大屏效果(并改变fontsize大小完成rem自适应):

            handleFullScreen (params) {
                // 实现全屏的方法
                function requestFullScreen(element) {
                    var requestMethod = element.requestFullScreen || //W3C
                        element.webkitRequestFullScreen || //Chrome
                        element.mozRequestFullScreen || //FireFox
                        element.msRequestFullScreen; //IE11
                    if (requestMethod) {
                        requestMethod.call(element);
                    } else if (typeof window.ActiveXObject !== "undefined") { //for Internet Explorer
                        var wscript = new ActiveXObject("WScript.Shell");
                        if (wscript !== null) {
                        wscript.SendKeys("{F11}");
                        }
                    }
                }
                requestFullScreen(document.documentElement);
                let doc = document.getElementsByClassName('Content')[0]
                let bgimg = document.getElementsByClassName('bg-img')[0]
                doc.style.position = 'fixed'
                doc.style.left = 0
                doc.style.top = 0
                doc.style.width = '100vw'
                bgimg.style.height = '100vh'
                window.onresize = function () {
                    let doc = document.getElementsByClassName('Content')[0]
                    if (!doc) { return }
                    if (document.fullscreenElement) {
                    console.log('进入全屏')
                    if(doc.offsetWidth > 1430) // 徐飞对单页面进行rem适配
                    document.documentElement.style.fontSize=doc.offsetWidth/105+"px"
                    // console.log(document.documentElement.style.fontSize,'doc.offsetWidth')  // 太强了
                    } else {
                    // 退出全屏的时候恢复原来的样式
                    console.log('退出全屏')
                    doc.style.position = 'relative'
                    doc.style.left = 'inherit'
                    doc.style.top = 'inherit'
                    doc.style.width = 'inherit'
                    bgimg.style.height = 'inherit'
                    document.documentElement.style.fontSize = 'inherit'
                    }
                };
            },
            exitFull() { //退出全屏 esc键和F11可以直接退出,
                // 判断各种浏览器,找到正确的方法
                var exitMethod = document.exitFullscreen || //W3C
                    document.mozCancelFullScreen || //FireFox
                    document.webkitExitFullscreen || //Chrome等
                    document.webkitExitFullscreen; //IE11
                if (exitMethod) {
                    exitMethod.call(document);
                } else if (typeof window.ActiveXObject !== "undefined") { //for Internet Explorer
                    var wscript = new ActiveXObject("WScript.Shell");
                    if (wscript !== null) {
                        wscript.SendKeys("{F11}");
                    }
                }
            },

遇到的问题:

1.子组件使用v-show后里面调用弹幕组件会失效

解决办法:使用路由加载数据实时大屏。顺便解决了大屏之前f11后丑陋的问题

其他还好都解决了。

 

 

 

 

 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值