Lenny-Hu / note

blog
5 stars 1 forks source link

[Vue组件封装] 将loading组件封装成loading指令(在指定区域内显示loading的效果) #19

Open Lenny-Hu opened 5 years ago

Lenny-Hu commented 5 years ago

在已有loading组件的基础下,封装一个loading指令,用起来比较方便,效果类似elementUI 的v-loading

参考API

CSS

.m-section-loading {
  position: relative;
  min-height: 50px;

  .mask-box {
    position: absolute;
    z-index: 100;
    background-color: rgba(255, 255, 255, 0.8);
    margin: 0;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
  }

  .mdl-spinner {
    position: absolute;
    top: 50%;
    left: 50%;
    margin-top: -14px;
    margin-left: -14px;
  }

  .mask-box.top .mdl-spinner {
    top: 20%;
  }
}

JS

import Vue from 'vue'

Vue.directive('loading', (el, binding) => {
  let className = 'js-ui-spinner-box'
  let box = el.querySelector(`.${className}`)

  if (binding.value) {
    if (box) {
      box.style.display = 'block'
    } else {
      el.classList.add('m-section-loading')
      box = document.createElement('div')
      box.className = `${className} mask-box`
      //  使用 Vue.component 获取已注册的loading组件构造器
      //  使用 Vue.extend 创建一个子类,并使用 new 创造实例, 调用 $mount 进行渲染后,手动挂载到指定元素
      let Spinner = Vue.extend(Vue.component('ui-spinner'))
      // propsData 仅限于 new 创建的实例时传递 props
      let component = new Spinner({
        propsData: {
          active: true
        }
      }).$mount()

      box.appendChild(component.$el)
      box.classList.add('fade-in')
      el.appendChild(box)
    }
  } else {
    box && (box.style.display = 'none')
  }
  // 列表太高时,将loading放在列表的上半部分(默认居中)
  box && (el.offsetHeight >= 400 ? box.classList.add('top') : box.classList.remove('top'))
})

使用

<template>
      <div v-if="loading || list.length">
        <div v-loading="loading">
             // 列表循环代码
        </div>
      </div>
      <div v-else>
        <no-data></no-data>
      </div>
    </div>
  </section>
</template>
<script>
export default {
  data () {
    return {
     list: [],
      loading: true
    }
  },
  methods: {
    async getList () {
      this.loading = true
      await this.AJAX...  // ajax请求代码
      this.loading = false
    }
  },
  created () {
    this.getList()
  }
}
</script>
Lenny-Hu commented 5 years ago

最近在写fj-service-system的时候,遇到了一些问题。那就是我有些组件,比如Dialog、Message这样的组件,是引入三方组件库,比如element-ui这样的,还是自己实现一个?虽然它们有按需引入的功能,但是整体风格和我的整个系统不搭。于是就可以考虑自己手动实现这些简单的组件了。

通常我们看Vue的一些文章的时候,我们能看到的通常是讲Vue单文件组件化开发页面的。单一组件开发的文章相对就较少了。我在做fj-service-system项目的时候,发现其实单一组件开发也是很有意思的。可以写写记录下来。因为写的不是什么ui框架,所以也只是一个记录,没有github仓库,权且看代码吧。

主要讲三种方式调用组件:

  • v-model或者.sync显式控制组件显示隐藏
  • 通过js代码调用
  • 通过Vue指令调用

在写组件的时候很多写法、灵感来自于element-ui,感谢。

Author: Molunerfinn Link: https://molunerfinn.com/vue-components/ Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.

《Vue组件三种调用方式》文章参考

Lenny-Hu commented 4 years ago

简单的loading(插件形式调用)

@mixin m-loading {
  .m-loading-box {
    display: block;
    width: 100%;
    height: 100%;
    position: fixed;
    z-index: 9999;
    left: 0;
    top: 0;
    bottom: 0;
    right: 0;
    background: rgba(0, 0, 0, .5);
    overflow-y: hidden;
  }

  .m-loading-inner {
    position: absolute;
    top: 50%;
    margin-top: -57px;
    margin-left: -57px;
    left: 50%;
    width: 64px;
    height: 64px;
    padding: 15px;
    background-color: #fff;
    border-radius: 10px;
    overflow: hidden;
  }

  .m-loading-body {
    img {
      width: 32px;
      height: 32px;
    }

    .text {
      margin-top: 10px;
      color: #999;
    }
  }
}
(function () {
  var ModuleLoading = Vue.extend({
    template: '<div class="m-loading-box" v-if="visible">' +
    '<div class="m-loading-inner">' +
      '<div class="m-loading-body f-tac">' +
        '<img src="load.gif" />' +
        '<div class="text f-toe" v-if="loadingText">{{loadingText}}</div>' +
      '</div>' +
    '</div>' +
  '</div>',
    data: function () {
      return {
        visible: false,
        loadingText: '加载中...'
      }
    },
    methods: {
      show: function (text) {
        if (text) {
          this.loadingText = text;
        }
        this.visible = true;
      },
      close: function () {
        this.visible = false;
        this.loadingText = '';
      }
    }
  });

  var loadingPlugin = {
    install: function (Vue) {
      Vue.prototype.$loading = (function () {
        let instance = new ModuleLoading();
        instance.vm = instance.$mount();

        document.body.appendChild(instance.vm.$el);
        return instance.vm;
      })();
    }
  };

  $(function () {
    Vue.use(loadingPlugin);
  });
})();

使用

this.$loading.show('提示文本');
this.$loading.close();
Lenny-Hu commented 4 years ago

简单的模态框组件

@mixin m-dialog {
  body.modal-open {
    overflow: hidden;
    padding-right: 16px;
  }

  .m-dialog-box {
    display: block;
    width: 100%;
    height: 100%;
    position: fixed;
    z-index: 1000;
    left: 0;
    top: 0;
    bottom: 0;
    right: 0;
    background: rgba(0, 0, 0, .5);
    overflow-y: auto;
  }

  .m-dialog-inner {
    margin: 20px auto 20px;
    width: 600px;
    min-height: 150px;
    background-color: #fff;
    border-radius: 10px;
    overflow: hidden;
  }

  .m-dialog-header {
    position: relative;
    height: 55px;
    line-height: 55px;
    text-align: center;
    color: #333;
    font-size: 24px;
    border-bottom-right-radius: 0;
    border-bottom-left-radius: 0;
    letter-spacing: 2px;
    border-bottom: 1px solid #ddd;

    .close {
      position: absolute;
      width: 26px;
      height: 22px;
      top: 0;
      right: 14px;
      cursor: pointer;
      margin: 0;
      color: #999;
    }
  }

  .m-dialog-body {
    padding: 10px;
    min-height: 200px;
  }

  .m-dialog-footer {
    padding: 25px 10px;

    .btn {
      & + .btn {
        margin-left: 50px;
      }

      &.disabled {
        cursor: auto;
        opacity: 0.5;
      }
    }
  }
}
(function () {
  var ModuleLoading = Vue.extend({
    template: '<div class="m-loading-box" v-if="visible">' +
    '<div class="m-loading-inner">' +
      '<div class="m-loading-body f-tac">' +
        '<img src="/dianping/www/images/common/load.gif" />' +
        '<div class="text f-toe" v-if="loadingText">{{loadingText}}</div>' +
      '</div>' +
    '</div>' +
  '</div>',
    data: function () {
      return {
        visible: false,
        loadingText: ''
      }
    },
    methods: {
      show: function (text) {
        this.loadingText = text || '加载中...';
        this.visible = true;
      },
      close: function () {
        this.visible = false;
        this.loadingText = '';
      }
    }
  });

  var loadingPlugin = {
    install: function (Vue) {
      Vue.prototype.$loading = (function () {
        var instance = new ModuleLoading();
        instance.vm = instance.$mount();

        document.body.appendChild(instance.vm.$el);
        return instance.vm;
      })();
    }
  };

  $(function () {
    Vue.use(loadingPlugin);
  });
})();

使用

<m-dialog title="标题啊" v-model="showDlg" @close="onDlgClose" @open="onDlgOpen"></m-dialog>