【项目实战】Vue项目开发之二次封装组件(一)

1,224 阅读3分钟

这是我参与更文挑战的第 16 天,活动详情查看: 更文挑战

前文【专栏-效率工具】系列文章,从开发工具、版本控制工具、接口调试工具 到 开发规范,再到博客搭建工具等等, 当然还有许多工具需要我们慢慢探索的,工作学习中用到的工具,分别一一作了介绍,养成习惯,提高开发效率,减少 bug 数量等等。这里码上几篇链接: 【接口调试工具-Insomnia】【效率工具】优化美丽舒适的开发环境【效率工具】下一代接口模拟工具-mock service worker...-,-、 记录提高日常码字幸福感的【实测能用】自用效率工具,持续更新、记录 仅当参考,按需食用,不足之处,欢迎各路大佬不吝赐教,补充完善,欢迎分享

  • 工欲善其事,必先利其器。实践(巧偷懒)促进科技发展-_-!

  • 本文及后续更文将为大家分享日常开发用到的一些工具函数,总结的样式,代码规范(CodeStyle),代码段等。为刚融入团队的新同学可以迅速 follow 团队的开发规范,快速融入到项目开发中,提高我们的工作效率,减少累赘代码,提高代码质量,减少bug.

  • 这里继续为大家分享一些代码段-Vue组件封装:Vue-Components

一、Vue 自定义组件封装 Vue Components

在使用 Vue 系列开项目时,基本就是在写页面,写业务,根据业务需求进行自定义满足各种需求的封装组件, 简单封装也不是那么难, 几个基于 iView UI 的封装组件, 同时学习一些封装组件方法,供学习参考,

1. Rate 组件二次封装

这里需求很简单,星星换成我们自己的好看的图标,,

是不是没有那么难,由简入深,就需要对相关 api 进行深入探究了:比方说组件传值的 n种方法,

// XRate.vue
<template>
  <Rate v-bind="$attrs"
        v-on="$listeners"
        custom-icon="iconfont icon-star" />
</template>

<script>
import { Rate } from "iview";
export default {
  name: "XRate",
  components: {
    Rate,
  },
};
</script>

2. Drawer 抽屉组件二次封装

这里列举的超简单的封装,具体逻辑还需具体分析,再次开发进行功能迭代升级

效果图: level-1 x-drawer-day16

level-2 x-drawer-day16-2

<template>
  <div class='x-drawer'>
    <Drawer v-bind="$attrs"
            v-on="$listeners">
      <!-- ↓↓ 插槽内容 ↓↓ -->
      <template>
        <slot></slot>
      </template>
      <!-- ↑↑ 插槽内容 ↑↑ -->

      <!-- trigger -->
      <div class="x-drawer-trigger-wrapper">
        <slot name="trigger"></slot>
      </div>

    </Drawer>
  </div>
</template>

<script>
import { Drawer } from "iview";
export default {
  name: "XDrawer",
  props: {},
  components: {
    Drawer,
  },
  data() {
    return {};
  },
  mounted() {},
  methods: {},
};
</script>
<style lang="scss" scoped>
</style>

使用 引入组件 && 注册

<button @click="showDrawer">显示抽屉</button>

<Drawer v-model="isShow"
        placement="left"
        :closable="true"
        width="512"
        title="first-level-drawer">
  <p>first drawer > Some contents...</p>
  <button @click="showInnerDrawer">显示二层抽屉</button>
  <template v-slot:trigger>
    <xn-button icon='edit'>{{ taobao }}</xn-button>
  </template>
</Drawer>
<Drawer v-model="innerShow"
        placement="left"
        :closable="true"
        title="second-level drawer">
  This is second-level-drawer.
</Drawer>

3. XDate 日期选择组件封装

utils: utils/date-range.js

// utils/date-range.js
export { getYearMonthDay, getDate, getFullYear }

const getYearMonthDay = (date) => {
  let year = date.getFullYear()
  let month = date.getMonth()
  let day = date.getDate()
  return { year, month, day }
}
const getDate = (year, month, day) => {
  return new Date(year, month, day)
}

function getFullYear(date) {
  return date.getFullYear()
}
<template>
<!-- TODO 选择当前日期样式 color 优化 -->
  <div class="x-date"
       v-click-outside>
    <input type="text"
           :value="formatDate">
    <div class="pannel"
         v-if="isVisible">
      <div class="pannel-nav">
        <span @click="prevYear">&lt;</span>
        <span @click="prevMonth">&lt;&lt;</span>
        <span>{{time.year}}年</span>
        <span>{{time.month + 1}}月</span>
        <span @click="nextMonth">&gt;&gt;</span>
        <span @click="nextYear">&gt;</span>
      </div>
      <div class="pannel-content">
        <div class="week">
          <span class="cell"
                v-for="week in weekDays"
                :key="week">
            {{ week }}
          </span>
        </div>
        <div class="days">
          <!-- 直接列出一个 6 * 7 列表 -->
          <div v-for="i in 6"
               :key="i">
            <!-- 是否当月 不是 变灰 -->
            <span class="cell cell-days"
                  @click="chooseDate(visibleDays[(i-1)*7+(j-1)])"
                  :class="[
                    {notCurrentMonth: !isCurrentMonth(visibleDays[(i-1)*7+(j-1)])},
                    {today: isToday(visibleDays[(i-1)*7+(j-1)])},
                    {select: isSelect(visibleDays[(i-1)*7+(j-1)])}
                  ]"
                  v-for="j in 7">
              {{ visibleDays[(i-1) * 7 + (j - 1)].getDate() }}
            </span>
          </div>
        </div>
      </div>
      <div class="pannel-footer">
        昨天 今天 明天
      </div>
    </div>
  </div>
</template>

<script>
import * as utils from "../../utils/date-range";
export default {
  name: "XDate",
  data() {
    let { year, month } = utils.getYearMonthDay(this.value);
    return {
      isVisible: false, // 日期面板显示控制
      weekDays: ["日", "一", "二", "三", "四", "五", "六"],
      time: { year, month },
    };
  },
  props: {
    value: {
      type: Date,
      default: new Date(),
    },
  },
  directives: {
    clickOutside: {
      // 指令的生命周期
      bind(el, bindings, vnode) {
        // context
        // console.log(bindings,vnode)

        // 把事件绑定给 document 上, 看一下点击的是否是当前元素的内部
        let handler = (e) => {
          if (el.contains(e.target)) {
            // 判断一下是否当前面板已经显示出来了
            if (!vnode.context.isVisible) {
              vnode.context.focus();
              // console.log("focus");
            }
          } else {
            if (vnode.context.isVisible) {
              vnode.context.blur();
              // console.log("blur");
            }
          }
          // console.log('e.target', e.target)
        };
        el.handler = handler;
        document.addEventListener("click", handler);
      },
      unbind(el) {
        document.removeEventListener("click", el.handler);
      },
    },
  },
  methods: {
    focus() {
      this.isVisible = true;
    },
    blur() {
      this.isVisible = false;
    },
    isCurrentMonth(date) {
      // date 她是不是当月 this.value 年和月 是否相等
      let { year, month } = utils.getYearMonthDay(utils.getDate(this.time.year, this.time.month, 1));
      let { year: y, month: m } = utils.getYearMonthDay(date);
      return year === y && month === m;
    },
    isToday(date) {
      let { year, month, day } = utils.getYearMonthDay(new Date());
      let { year: y, month: m, day: d } = utils.getYearMonthDay(date);
      return year === y && month === m && day === d;
    },
    chooseDate(date) {
      console.log("chooseDate", date);

      this.time = utils.getYearMonthDay(date);
      this.$emit("input", date);
      this.blur(); // 关闭 面板
    },
    isSelect(date) {
      let { year, month, day } = utils.getYearMonthDay(this.value);
      let { year: y, month: m, day: d } = utils.getYearMonthDay(date);
      return year === y && month === m && day === d;
    },
    prevYear() {
      let y = utils.getDate(this.time.year, this.time.month,1)
      y.setFullYear(y.getFullYear() - 1)
      this.time = utils.getYearMonthDay(y)
    },
    prevMonth() {
      let d = utils.getDate(this.time.year, this.time.month,1)
      d.setMonth(d.getMonth() - 1)
      this.time = utils.getYearMonthDay(d)
    },
    nextMonth(){
      let d = utils.getDate(this.time.year, this.time.month,1)
      d.setMonth(d.getMonth() + 1)
      this.time = utils.getYearMonthDay(d)
    },
    nextYear(){
      let y = utils.getDate(this.time.year, this.time.month,1)
      y.setFullYear(y.getFullYear() + 1)
      this.time = utils.getYearMonthDay(y)
    }
  },
  mounted() {
    // console.log(this.visibleDays);
  },
  computed: {
    visibleDays() {
      // 可见日期
      // 先获取当前周几
      let { year, month } = utils.getYearMonthDay(
        utils.getDate(this.time.year, this.time.month, 1)
      );
      // 获取当前月份的第一天
      let currentFirstDay = utils.getDate(year, month, 1);
      // 前面就移动几天
      let week = currentFirstDay.getDay();
      // 当前开始的天数
      let startDay = currentFirstDay - week * 60 * 60 * 1000 * 24;

      // 循环 42 天
      let arr = [];
      for (let i = 0; i < 42; i++) {
        arr.push(new Date(startDay + i * 60 * 60 * 1000 * 24));
      }
      return arr;
    },
    formatDate() {
      let { year, month, day } = utils.getYearMonthDay(this.value); // getFullYear getMonth getDate
      return `${year}-${month + 1}-${day}`;
    },
  },
};
</script>

<style lang="scss">
.x-date {
  margin-bottom: 21px;
  .pannel {
    box-sizing: border-box;
    padding: 5px;
    width: 32 * 7 + 12px;
    position: absolute;
    margin-top: 10px;
    background: #fff;
    border: 1px solid pink;
    &-nav {
      height: 30px;
      display: flex;
      justify-content: space-around;
      span {
        cursor: pointer;
        user-select: none;
        line-height: 30px;
      }
    }
    &-content {
      color: #000;
      .week {
        .cell:first-child,
        .cell:last-child {
          color: red;
        }
      }
      .cell {
        box-sizing: border-box;
        display: inline-flex;
        justify-content: center;
        align-items: center;
        width: 32px;
        height: 32px;
        font-weight: bold;
      }
      .cell-days:hover {
        border: 1px solid pink;
      }
      .notCurrentMonth {
        color: gray;
      }
      .today {
        background: red;
        color: #fff !important;
      }
      .select {
        color: red;
        border: 1px solid red;
      }
    }
  }
}
</style>

当然好用的工具还有很多,这里列举部分仅作参考,抛砖引玉,

有没有 get 到呢?

欢迎各路大神评论出你的私藏工具, 把你的好用的【效率工具】分享给大家=,=

今日份预告:

继续更新 分享一些积累的 codes/utils/components/styles等等吧

下一篇敬请期待! hahah~