vue基于vant封装日期组件

本文介绍了在Vue项目中基于 vant 组件库封装日期选择组件的过程,包括在main.js和components/index.js中引入与导出,以及在 vantCalendar 文件夹下创建组件。在封装过程中遇到的问题是van-calendar的poppable属性导致的v-model监听事件触发两次,解决方案是通过van-popup控制弹出层,并自定义样式以实现预期效果。组件目前不支持平铺显示,但提供了具体的使用示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 在src/main.js中引入组件
import components from './components'
/******----- 自定义组件 -----******/
for (let k in components) {
    Vue.use(components[k]);
}
  • 在src/components/index.js 中导出组件
import VantCalendar from './vantCalendar' //日期组件
let components = {
  VantCalendar
}

export default components;
  • 在src/components目录下新建文件夹 vantCalendar,里面新建两个文件index.js和VantCalendar.vue
  • 在src/components/vantCalendar/index.js中注册组件
import vantCalendar from "./VantCalendar.vue";

const VantCalendar = {
  install(Vue) {
    Vue.component(vantCalendar.name, vantCalendar);
  }
};

export default VantCalendar;
  •  在src/components/vantCalendar/VantCalendar.vue中写组件封装的逻辑,使用v-bind="$attrs"和v-on="$listeners"实现在父组件调用时子组件可以使用原生组件带的属性和方法

注意: 遇到的坑 van-calendar的poppable的属性为true时,v-model的值只能是true或false,用来控制弹出层的显隐。这样,会影响父组件的v-model通过props传过来的值,在选择日期点击确定后会触发2次value的watch监听事件,第一次value是选中的日期,第2次是false了,因为第二次监听的value 就变成了当前子组件的v-model,此时弹出层要关闭了,所以是false。

 刚开始没用van-popup在外层包裹,是直接在van-calendar上写,用了v-on="$listeners"就会有上面问题,但是不用的话在父组件中的子组件调用上调用原生组件带的方法就无效!解决方法就是poppable属性写死为false,这样它就没有v-model属性了,然后通过van-popup来控制弹出层的显隐,poppable为true才支持的那些属性van-popup也支持,所以抽出来做成配置项,但是poppable为false是平铺效果,不太友好,就自己通过控制css来将它手动调整成正常显示,所以加了style配置项。

这样解决的弊端就是该组件不支持平铺显示,要支持就还得再优化一下,写个判断条件渲染,因为感觉平铺效果一般不会用到,所以在这里没实现。上代码:

<template>
  <div class="vantCal">
    <!-- 只读 -->
    <div v-if="readonly"></div>
    <!-- 日期组件 -->
    <van-popup
      v-if="!readonly"
      v-model="show"
      :position="position"
      :round="round"
      :closeable="closeable"
      :style="styleObjC"
    >
      <van-calendar
        :type="type"
        v-bind="$attrs"
        v-on="$listeners"
        :poppable="false"
        @confirm="onConfirm"
      />
    </van-popup>
  </div>
</template>

<script>
export default {
  name: "VantCalendar",
  props: {
    readonly: {
      type: Boolean,
      default: false
    },
    value: {
      type: [String, Array, Number]
    },
    position: {
      // 弹出方向  默认底部
      type: String,
      default: "bottom"
    },
    round: {
      // 是否显示圆角
      type: Boolean,
      default: true
    },
    closeable: {
      // 是否显示关闭图标
      type: Boolean,
      default: true
    },
    type: {
      /* 
        single表示选择单个日期,
        multiple表示选择多个日期,
        range表示选择日期区间
      */
      type: String,
      default: "single"
    },
    styleObj: {
      // 自定义样式
      type: Object,
      default() {
        return {};
      }
    }
  },
  data() {
    return {
      // 当前值
      date: this.value,
      show: false,
      styleObjC: this.styleObj
    };
  },
  watch: {
    position: {
      immediate: true,
      deep: true,
      handler(val) {
        if (val === "bottom") {
          this.styleObjC = { height: "60%" };
        } else if (val === "left" || val === "right") {
          this.styleObjC = { height: "60%", top: "70%" };
        }
      }
    },
    styleObj: {
      immediate: true,
      deep: true,
      handler(val) {
        this.styleObjC = val;
      }
    },
    value: {
      immediate: true,
      deep: true,
      handler(val) {
        this.date = val;
      }
    },
    date: {
      immediate: true,
      deep: true,
      handler(val) {
        this.$emit("input", val);
      }
    }
  },
  methods: {
    // 处理:类型不同,时间格式不同
    handleTime(date) {
      // console.log('date', date)
      let time = "";
      let arr = [];
      switch (this.type) {
        // 单个日期
        case "single":
          time = this.formatDate(date);
          break;
        // 多个日期
        case "multiple":
          date.map(item => {
            arr.push(this.formatDate(item));
          });
          time = arr.join(",");
          break;
        // 日期区间  开始日期 - 结束日期
        case "range":
          time = `${this.formatDate(date[0])} - ${this.formatDate(date[1])}`;
          break;
        default:
          break;
      }
      return time;
    },
    // 格式化中国标准时间
    formatDate(date) {
      return `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`;
    },
    onConfirm(value) {
      this.show = false;
      this.date = this.handleTime(value);
    }
  }
};
</script>

<style lang="less" scoped></style>
  • 组件使用:举例说明 
<template>
  <div class="home">
    <van-field
        v-model="date"
        name="履行日期"
        label="履行日期"
        clickable
        placeholder="请选择日期"
        @click="dateClick"
      />
      <VantCalendar ref="myVantCalendar" v-model="date" type="single" color="#1989fa" :styleObj="{height: '70%'}" @select="changeTime">
      </VantCalendar>
  </div>
</template>
export default {
  data() {
    return {
      date: '',
    }
  },
  methods: {
    dateClick() { 
      this.$refs.myVantCalendar.show = true
    },
    changeTime(value) {
      console.log('父组件value111', value)
    },
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值