2

总有一个功能,你会用的到!
接上篇文章:vue项目干货
源码地址

15.关于echarts图表及随区域大小变化resize

创建lineChart组件,折线图表
image.png
代码如下:

<template>
    <div :id="srcData.id" class="warnCount"></div>
</template>
<script>
    import echarts from 'echarts';
    import resize from '../resize' //用于可视区域改变时resize
    export default {
        name: "chart",
        mixins: [resize],
        data() {
            return {
                chartColumn: null,
                scale: 1,
            }
        },
        props: {
            srcData: Object, //数据
            isGetData: Boolean  //获取好数据 可以开始绘制标记
        },
        watch: {
            isGetData(val) {
                this.drawChart();
            }
        },
        methods: {
            drawChart() {
                let me=this;
                setTimeout(() => {
                    let me = this;
                    this.chartColumn.clear();
                    this.chartColumn.setOption({
                        tooltip: {
                            trigger: 'axis',
                            confine: true
                        },
                        yAxis: {
                            name: '得分',
                            type: 'value',
                            axisLine: {
                                lineStyle: {
                                    color: '#72aafa'
                                },
                            },
                            splitLine: {
                                show:false,
                                lineStyle: {
                                    color: '#154d5a',
                                    width: 0.5
                                }
                            },
                        },
                        xAxis :{
                            type: 'time',
                            splitLine: {
                                show: false,
                                lineStyle: {
                                    color: '#72aafa',
                                    
                                }
                            },
                            axisLine: {
                                lineStyle: {
                                    color: '#72aafa'
                                }
                            },
                            
                        },
                        grid:{
                            top:'15%',
                            right:'10%',
                            left:'10%',
                            bottom:'15%'
                        },
                        series:this.srcData.sData
                    })
                }, 0);
            }
        },
        created() {
            var me = this;
        },
        updated() {
        },
        mounted() {
            let charDom = document.getElementById(this.srcData.id), me = this;
            charDom.style.height = this.srcData.height + "px";
            this.scale = window.innerWidth / 1920;
            this.chartColumn = echarts.init(charDom);
            this.chartColumn.on('click', function (params) {
                me.$emit('click', params);
            })
        },
        beforeDestroy() {
            if (this.chartColumn) {
                this.chartColumn.clear()
            }
        },
        destroy() {
            if (this.chartColumn) {
                this.chartColumn.dispose()
                this.chartColumn = null;
            }
        },
        components: {}
    }
</script>
<style scoped lang="scss">
    .warnCount {
        width: 100%;
        height: 350px;
    }
</style>

接下来开始使用该组件:
image.png
传值:

warnCountTrendData: {
    id: 'warnCountTrend',
    height: 350,
    sData:[
        {
            name:'竞逐对',
            symbol:'circle',
            symbolSize:6,
            type:'line',
            lineStyle:{
                width:1
            },
            data:[
                {value:["2020-01-01 00:00:00",0]},
                {value:["2020-01-02 00:00:00",0]},
                {value:["2020-01-04 01:01:01",200]},
                {value:["2020-01-07 01:01:01",200]}

            ]
        },
        {
            name:'竞逐对1',
            symbol:'circle',
            symbolSize:6,
            type:'line',
            lineStyle:{
                width:1
            },
            data:[
                {value:["2020-01-02 00:00:00",0]},
                {value:["2020-01-06 04:01:00",100]},
                {value:["2020-01-07 01:01:01",200]}
            ]
        }
    ]
}

当窗口改变时window.onresize可以被监听到,但当导航栏收起时监听不到,此项目的解决方案是安装:element-resize-detector
resize.js文件:

import {debounce} from '../common/util';
import elementResizeDetectorMaker from 'element-resize-detector'
export default {
    data() {
        return {
            $_sidebarElm: null
        }
    },
    mounted() {
        let me=this;
        this.__resizeHandler = debounce(() => {
            if (this.chartColumn) {
                this.chartColumn.resize()
            }
        }, 100)
        window.addEventListener('resize', this.__resizeHandler);
        const erd = elementResizeDetectorMaker()
        erd.listenTo(document.getElementsByClassName("contentBox"),(element)=>{
           me.$nextTick(()=>{
                me.resize()
            })
        })
    },
    beforeDestroy() {
        window.removeEventListener('resize', this.__resizeHandler);
    },
    methods: {
        resize() {
            this.__resizeHandler()
        }
    }
}

debounce为函数防抖方法:

export const debounce = (func, wait, immediate) => {
    let timeout, args, context, timestamp, result

    const later = function () {
        // 据上一次触发时间间隔
        const last = +new Date() - timestamp

        // 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
        if (last < wait && last > 0) {
            timeout = setTimeout(later, wait - last)
        } else {
            timeout = null
            // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
            if (!immediate) {
                result = func.apply(context, args)
                if (!timeout) context = args = null
            }
        }
    }

    return function (...args) {
        context = this
        timestamp = +new Date()
        const callNow = immediate && !timeout
        // 如果延时不存在,重新设定延时
        if (!timeout) timeout = setTimeout(later, wait)
        if (callNow) {
            result = func.apply(context, args)
            context = args = null
        }

        return result
    }
}

实现效果:

16.抽屉

通过drawer控制抽屉展示隐藏
image.png

<el-button @click="showDrawer" type="primary" class='showBtn' :style='sty'>
        <i class='iconfont iconshezhi' v-if='!drawer'></i>
        <i class='iconfont iconguanbi' v-else></i>
    </el-button>
    <el-drawer  :visible.sync="drawer" direction="rtl" style='position:absolute' @close='onClose'>
    <span class='drawTitle'>设置</span>
    <div style='margin:0 auto;width:100%;'>
        <el-form ref="form" label-width="80px">
            <el-form-item label="选择主题">
                <el-select v-model="value" placeholder="请选择" @change='changeColor'>
                    <el-option
                    v-for="item in options"
                    :key="item.value"
                    :label="item.label"
                    :value="item.value">
                    </el-option>
                </el-select>
            </el-form-item>
        </el-form>
    </div>
</el-drawer>

再来控制控制按钮的样式:

.showBtn {
    position:absolute;
    top:50%;
    transform :translateY(-50%);
    z-index:100000;
    transition: right .3s;
    padding:10px!important;
    i {
        font-size:20px;
    }
}
.drawTitle {
    position: absolute;
    top: 4%;
    left: 5%;
    font-weight: 700;
    color: #666;
    font-size: 17px;
}

方法逻辑:

showDrawer:function(){
    let me=this;
    me.drawer = !me.drawer;
    me.value=sessionStorage.getItem('theme');
    me.sty.right=me.drawer?'30%':'0' //按钮位置
},
onClose:function(){
    let me=this;
    me.sty.right='0'
}

实现效果:
image.png

17.换肤功能

增加如下两个文件(需安装sass)
image.png
mixin.scss

@mixin get-theme-value($type, $value, $css-key, $css-value) {
    @each $themename , $theme in $themes {
        [data-theme = '#{$themename}'] & {
            $_type: map-get($theme, $type);
            #{$css-key}: #{$css-value} map-get($_type , $value );
        }
    }
}

@mixin get-color-value($type, $value) {
    @each $themename , $theme in $themes {
        [data-theme = '#{$themename}'] & {
            $_type: map-get($theme, $type);
            #{$type}-color: map-get($_type , $value );
        }
    }
}

@mixin background($color){
    @include get-theme-value('background', $color, 'background', '');
}
@mixin background-color($color){
    @include get-color-value('background', $color);
}

@mixin font-color($color) {
    @include get-theme-value('font', $color, 'color', '');
}

@mixin border-color($color) {
    @include get-color-value('border', $color);
}
@mixin border($color, $css-key:'border', $css-value:'1px solid') {
    @include get-theme-value('border', $color,  $css-key , $css-value)
}

@mixin box-shadow($color, $css-value) {
    @include get-theme-value('boxShadow', $color, 'box-shadow', $css-value)
}
@mixin common-link-font($size: 1.2) {
    font-size: #{$size}rem; font-weight: 600;
    &:hover,
    &.selected{
        @include font-color('color4');
    }
    @include font-color('color1');
}
@mixin common-font($size: 1.2, $font-weight: 400) {
    font-size: #{$size}rem; font-weight: $font-weight;
    @include font-color('color1');
}

@mixin flex($justify: flex-start, $align-items: center, $dir: row, $wrap: nowrap, $align-content: stretch) {
  display: flex;
  flex-flow: $dir $wrap;
  justify-content: $justify;
  align-items: $align-items;
  align-content: $align-content;
}

theme.css 定义两套主题

@import './mixin.scss';
// default theme

$default-theme : (
  font: (
    color1: #16284c,
  ),
  border: (
    color1: #16284c,
  ),
  background: (
    color1: #16284c,
  ),
  boxShadow: (
    color1: rgba(0,0,0,.3)
  )
);

//红色主题
$red-theme : (
  font: (
    color1: red
  ),
  border: (
    color1: red
  ),
  background: (
    color1: red
  ),
  boxShadow: (
    color1: rgba(0,0,0,.3)
  )
);

$themes: (
  default-theme: $default-theme,
  red-theme: $red-theme
);

App.vue

<template>
    <div id="app">
        <transition name="fade" mode="out-in">
            <router-view></router-view>
        </transition>
    </div>
</template>
<script>
    export default {
        name: 'app',
        data(){
          return {
              getNowThemeInfo:'' //初始主题
          }
        },
        mounted() {
          let me=this;  
          me.getNowThemeInfo=sessionStorage.getItem('theme');
          me.setBodyTheme();
          
        },
        methods: {
            setBodyTheme() {
                document.body.dataset.theme = this.getNowThemeInfo;
            }
        },
        components: {}
    }

</script>

<style lang="scss">
    body {
        margin: 0px;
        padding: 0px;
        /*background: url(assets/bg1.jpg) center !important;
            background-size: cover;*/
        // background: #1F2D3D;
        font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, SimSun, sans-serif;
        font-size: 14px;
        -webkit-font-smoothing: antialiased;
    }

    #app {
        position: absolute;
        top: 0px;
        bottom: 0px;
        width: 100%;
    }

    .el-submenu [class^=fa] {
        vertical-align: baseline;
        margin-right: 10px;
    }

    .el-menu-item [class^=fa] {
        vertical-align: baseline;
        margin-right: 10px;
    }

    .toolbar {
        background: #f2f2f2;
        padding: 10px;
        //border:1px solid #dfe6ec;
        margin: 10px 0px;
        .el-form-item {
            margin-bottom: 10px;
        }
    }

    .fade-enter-active,
    .fade-leave-active {
        transition: all .2s ease;
    }

    .fade-enter,
    .fade-leave-active {
        opacity: 0;
    }
</style>

当某个元素的颜色信息需要随主题改变时,需要这样定义颜色信息
image.png
你会发现浏览器中样式使这样写的
image.png
基本换肤功能就实现了,下面介绍下如何切换(此项目将主题信息存储在session中)
main.js中增加如下方法,监听session

Vue.prototype.resetSetItem = function (key, newVal) {
  if (key =='theme') {
      // 创建一个StorageEvent事件
      var newStorageEvent = document.createEvent('StorageEvent');
      const storage = {
          setItem: function (k, val) {
              sessionStorage.setItem(k, val);
              // 初始化创建的事件
              newStorageEvent.initStorageEvent('setItem', false, false, k, null, val, null, null);
              // 派发对象
              window.dispatchEvent(newStorageEvent)
          }
      }
      return storage.setItem(key, newVal);
  }
}

在app.vue中监听变化

window.addEventListener('setItem', ()=> {
    var val = sessionStorage.getItem('theme');
    console.log(val);
    this.getNowThemeInfo=sessionStorage.getItem('theme');
    this.setBodyTheme();
})

在改变主题信息时记得存session里theme
录屏工具打不开了...效果先看项目吧

将不断更新完善,期待您的批评指正!


薇薇
298 声望24 粉丝

路漫漫其修远兮,吾将上下而求索