vue 自己捣鼓周日程日历组件

需求:想要一个周日程表,记录每天的计划,点击可查看详情。可自定义时间段通过后台获取时间段显示

分析:

通过需求,超级课程表app这款软件其中课表和这个需求很像,只不过这个需求第一列的时间段是自定义的,不是上午下午两个,但是原理都差不多
原本想找一些第三方插件使用,由于时间充足,而且自己也想封装成一个组件方便以后或许会碰到类似的需求,于是自己手动写了一个日程日历。
效果如下

 优化修改:数据量大时,格子显示太长问题,这里进行了优化,如果超过2个就进行展开与收缩操作

例子

<template>
  <div id="app">
    <WSchedule :planList="timePeriodList" :isFirstDayOfMondayOrSunday="7" :hasNumExpend="2" @handleDetail="handleDetail" @handleCardDetail="handleCardDetail" @changeWeek="changeWeek">
      <template v-slot:thing="{row}">
        <span>时段:{{ row.timePeriod }}</span>
        <span>课程:{{ row.course }}</span>
        <span>值班员:{{ row.watchman }}</span>
        <span>地点:{{ row.place }}</span>
      </template>
    </WSchedule>
  </div>
</template>

<script>

import WSchedule from '@/components/WeekSchedule'

export default {
  name: 'App',
  components: {
    WSchedule,
  },
  data() {
    /**
     * 获取当天时间
     * @returns {string}
     */
    function getCurDay(num = 0) {
      var datetime = new Date();
      var year = datetime.getFullYear();
      var month = datetime.getMonth() + 1 < 10 ? "0" + (datetime.getMonth() + 1) : datetime.getMonth() + 1;
      let day = datetime.getDate()
      if ((day + num) > 0) {
        day = (day + num) < 10 ? "0" + (datetime.getDate() + num) : datetime.getDate() + num;
      } else {
        day = (day - num) < 10 ? "0" + (datetime.getDate() - num) : datetime.getDate() - num;
      }
      return `${year}-${month}-${day}`
    }
    return {
      timePeriodList: [
        {
          timePeriod: '8:00~10:00',
          schedule: [
            {
              isExpend: false,
              [getCurDay()]: [
                {
                  timePeriod: '8:00~10:00',
                  date: getCurDay(),
                  course: '大学英语',
                  watchman: '井底的蜗牛',
                  place: '测试地点',
                  status: 1,
                },
                {
                  timePeriod: '8:00~10:00',
                  date: getCurDay(),
                  course: '大学英语',
                  watchman: '井底的蜗牛',
                  place: '测试地点',
                  status: 2,
                },
                {
                  timePeriod: '8:00~10:00',
                  date: getCurDay(),
                  course: '大学英语',
                  watchman: '井底的蜗牛',
                  place: '测试地点',
                  status: 3,
                },
              ],
            },
            {
              isExpend: false,
              [getCurDay(-1)]: [
                {
                  id: 1,
                  timePeriod: '8:00~10:00',
                  date: getCurDay(-1),
                  course: '大学英语',
                  watchman: '井底的蜗牛',
                  place: '测试地点',
                  status: 1,
                },
              ]
            }
          ]
        },
        {
          timePeriod: '12:00~14:00',
          schedule: [
            {
              isExpend: false,
              [getCurDay()]: [
                {
                  timePeriod: '12:00~14:00',
                  date: getCurDay(),
                  course: '大学英语',
                  watchman: '井底的蜗牛',
                  place: '测试地点',
                  status: 2,
                },
                {
                  timePeriod: '12:00~14:00',
                  date: getCurDay(),
                  course: '大学英语',
                  watchman: '井底的蜗牛',
                  place: '测试地点',
                  status: 3,
                },
              ],
            },
            {
              isExpend: false,
              [getCurDay(-1)]: [
                {
                  timePeriod: '12:00~14:00',
                  date: getCurDay(-1),
                  course: '大学英语',
                  watchman: '井底的蜗牛',
                  place: '测试地点',
                  status: 1,
                },
                {
                  timePeriod: '实验室1',
                  date: getCurDay(-1),
                  course: '大学英语',
                  watchman: '井底的蜗牛',
                  place: '测试地点',
                  status: 1,
                },
                {
                  timePeriod: '实验室1',
                  date: getCurDay(-1),
                  course: '大学英语',
                  watchman: '井底的蜗牛',
                  place: '测试地点',
                  status: 1,
                },
              ]
            }
          ]
        },
        {
          timePeriod: '14:00~16:00',
          schedule: []
        },
      ],
    }
  },
  methods: {
    /**
     * 点击详情
     * @param row
     */
    handleDetail(row){
      console.log(row)
    },
    /**
     * 点击卡片查看全部内容
     */
    handleCardDetail(row) {
      console.log(row)
    },
    /**
     * 切换周
     * @param date
     */
    changeWeek(date){
      console.log(date)
    }
  },
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

 封装组件 weekTools WeekSchedule/index.vue

<template>
  <div class="course-week">
    <div class="week-top">
      <div class="week-btn-wrap">
        <span @click="getLastWeek">上周</span>
        <span @click="getCurWeek">本周</span>
        <span @click="getNextWeek">下周</span>
      </div>
      <span class="w-today-date"> {{ todayDate }}</span>
      <div class="w-choose-status">
        <div v-for="sta in cardStatus">
          <span class="square" :style="{background:sta.color}"></span>
          <span class="title">{{ sta.title }}</span>
        </div>
      </div>
    </div>
    <div class="week-table">
      <div class="table-header">
        <div class="table-week">
          <template v-for="(item,index) of weeks">
            <span class="w-first" v-if="index===0" :key="index">{{ item }}</span>
            <span v-else :key="index">{{ item }}</span>
          </template>
        </div>
        <div class="w-table-date">
          <template v-for="(item,index) of months">
            <span class="w-first" v-if="index===0" :key="index">
            </span>
            <template v-else>
              <span :key="index" class="w-day-item" :class="{'w-isCurDate':item&&item.isCurDate}">
                {{ `${item && item.isCurDate ? item && item.showDate + '(今天)' || '' : item && item.showDate || ''}` }}
              </span>
            </template>
          </template>
        </div>
      </div>
      <div class="w-time-period-list">
        <ul class="w-time-period-row">
          <!--循环时段,看时段有多少个-->
          <template v-if="planList.length>0">
            <li class="w-time-period-col" v-for="(period,p_index) in planList"
                :key="`period${p_index}`">
              <!--第一列显示时段-->
              <div class="w-time-period"> {{ period.timePeriod }}</div>
              <!-- 后面显示周一到周日的计划-->
              <div class="w-row-day">
                <!-- 循环显示每周的日期-->
                <template v-for="(month,m_index) of months">
                  <!-- v-if="month" 去除数据处理的时候移除数组第一个为empty的问题-->
                  <div v-if="month" :key="`month${m_index}`" class="w-things" @click="handleCardDetail(month,period)">
                    <!-- 循环每个时间段的计划-->
                    <template v-for="(card,t_index) of period.schedule">
                      <template v-for="(single,sIndex) in card[month.date]">
                        <template v-if="!card.isExpend">
                          <div v-if="single.date===month.date&&sIndex<hasNumExpend"
                               :key="`thing${sIndex}`"
                               class="w-thing-item"
                               @click="handleDetail(single)"
                               :style="{background: cardStatus[single.status].color}">
                            <slot name="thing" :row="single"></slot>
                          </div>
                        </template>
                        <template v-if="card.isExpend">
                          <div v-if="single.date===month.date"
                               :key="`thing${sIndex}`"
                               class="w-thing-item"
                               @click="handleDetail(single)"
                               :style="{background: cardStatus[single.status].color}">
                            <slot name="thing" :row="single"></slot>
                          </div>
                        </template>
                        <div class="w_expand"
                             v-if="card[month.date].length>hasNumExpend&&(card[month.date].length-1)===sIndex&&!card.isExpend&&single.date===month.date"
                             @click="handleExpand(card)">展开
                        </div>
                        <div class="w_shrink"
                             v-if="card[month.date].length>hasNumExpend&&(card[month.date].length-1)===sIndex&&card.isExpend&&single.date===month.date"
                             @click="handleExpand(card)">收缩
                        </div>
                      </template>
                    </template>
                  </div>
                </template>
              </div>
            </li>
          </template>
          <div class="w-noMore" v-else><span>暂无数据</span></div>
        </ul>
      </div>
    </div>
  </div>
</template>

<script>
import { formatDate , getCurDay } from "@/utils/weekTools";

export default {
  name: 'WeekSchedule',
  props: {
    planList: {
      type: Array,
      default: []
    },
    //卡片状态
    cardStatus: {
      type: Object,
      default: () => {
        return {
          1: {
            title: '已过期',
            color: '#9CADADB7'
          },
          2: {
            title: '进行中',
            color: '#FF6200'
          },
          3: {
            title: '未开始',
            color: '#3291F8'
          },
        }
      }
    },
    //第一列是星期几
    isFirstDayOfMondayOrSunday: {
      type: Number,
      default: 1,
    },
    hasNumExpend:{
      type:Number,
      default:2
    }
  },
  data () {
    return {
      weeks: [
        '时段', '周一', '周二', '周三', '周四', '周五', '周六', '周日',
      ],
      todayDate: '',
      months: [],
      curDate: '',
      nowDate: new Date(),
    }
  },
  watch: {
    isFirstDayOfMondayOrSunday: {
      handler (val) {
        if (val > 1) {
          let arr = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
          const arr1 = arr.slice(val - 1)
          const arr2 = arr.slice(0, val - 1)
          this.weeks = ['时段', ...arr1, ...arr2]
        }
      },
      immediate: true
    }
  },
  mounted () {
    this.getCurWeek()
  },
  methods: {
    //展开与缩放操作
    handleExpand (row) {
      row.isExpend = !row.isExpend
    },
    /**
     * 获取 时间
     * @param time
     */
    getWeek (time) {
      this.curDate = new Date(time)
      //当前是周几
      const whichDay = time.getDay()
      let num = 0
      if (this.isFirstDayOfMondayOrSunday <= whichDay) {
        num = this.isFirstDayOfMondayOrSunday
      } else {
        num = this.isFirstDayOfMondayOrSunday - 7
      }
      const weekDay = time.getDay() - num
      time = this.addDate(time, weekDay * -1)
      for (let i = 0; i < 7; i++) {
        const { year, month, day } = formatDate(i === 0 ? time : this.addDate(time, 1))
        this.months.push({
          date: `${year}-${month}-${day}`,
          showDate: `${month}-${day}`,
          timestamp: new Date(`${year}-${month}-${day}`).getTime()
        })
      }
      this.months.sort((a, b) => a.timestamp - b.timestamp)
      delete this.months[0]
      this.todayDate = `${this.months[1].date} ~ ${this.months[this.months.length - 1].date}`
    },
    /**
     * 处理日期
     * @param date
     * @param n
     * @returns {*}
     */
    addDate (date, n) {
      date.setDate(date.getDate() + n)
      return date
    },
    /**
     * 上周
     */
    getLastWeek () {
      const date = this.addDate(this.curDate, -7),
        { year, month, day } = formatDate(date),
        dateObj = {
          date: `${year}-${month}-${day}`,
          timestamp: new Date(`${year}-${month}-${day}`).getTime()
        }
      this.dealDate(date)
      this.$emit('changeWeek', dateObj)
    },
    /**
     * 本周
     */
    getCurWeek () {
      const { year, month, day } = formatDate(new Date()),
        dateObj = {
          date: `${year}-${month}-${day}`,
          timestamp: new Date(`${year}-${month}-${day}`).getTime()
        }
      this.dealDate(new Date())
      this.$emit('changeWeek', dateObj)
    },
    /**
     * 下周
     */
    getNextWeek () {
      const date = this.addDate(this.curDate, 7),
        { year, month, day } = formatDate(date),
        dateObj = {
          date: `${year}-${month}-${day}`,
          timestamp: new Date(`${year}-${month}-${day}`).getTime()
        }
      this.dealDate(date)
      this.$emit('changeWeek', dateObj)
    },
    /**
     * 显示当天日期状态
     * @param date
     */
    dealDate (date) {
      this.months = ['']
      this.getWeek(date)
      const curDate = getCurDay()
      this.months.forEach(item => {
        item.isCurDate = item.date === curDate
      })
    },
    /**
     * 点击卡片子内容查看详情
     * @param row
     */
    handleDetail (row) {
      this.$emit('handleDetail', row)
    },
    /**
     * 点击卡片查看全部内容
     * @param month
     * @param period
     */
    handleCardDetail (month, period) {
      this.$emit('handleCardDetail', { ...month, ...period })
    }
  }
}
</script>

<style>
ul {
  list-style: none;
}

ul, li {
  margin: 0;
  padding: 0;
}

.course-week {
  width: 100%;
  border: 1px solid #ddd;
  padding: 1%;
  box-sizing: border-box;
}

.week-top {
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
  height: 40px;
  padding: 0 1%;
  box-sizing: border-box;

}

.week-top .week-btn-wrap {
  width: 200px;
  display: flex;
  justify-content: space-around;
  color: #409EFF;
}

.week-top .week-btn-wrap span {
  cursor: pointer;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 15px;
}

.w-today-date {
  font-weight: bold;
  font-size: 16px;
}

.w-choose-status {
  display: flex;
  justify-content: flex-end;
  width: 200px;
}

.w-choose-status > div {
  width: 100%;
  flex: 1;
  display: flex;
  padding: 0 2%;
  white-space: nowrap;
  line-height: 20px;
  box-sizing: border-box;
}

.w-choose-status > div .square {
  display: flex;
  width: 16px;
  height: 16px;
  border-radius: 4px;
  box-sizing: border-box;
}

.w-choose-status > div .title {
  display: flex;
  align-items: center;
  line-height: 16px;
  padding-left: 4px;
  font-size: 14px;
  box-sizing: border-box;
}


.week-table {
  display: flex;
  flex-direction: column;
}

.week-table .table-header {
  width: 100%;
  height: 80px;
  background: #EAEDF2;
  display: flex;
  flex-direction: column;
  align-items: center;
  border-bottom: 1px solid #EAEDF2;
  box-sizing: border-box;
}

.table-header .w-table-date, .table-week {
  width: 100%;
  height: 40px;
  text-align: left;
  display: flex;
  justify-content: center;
  align-items: center;

}

.table-header .w-table-date > span, .table-week > span {
  flex: 1;
  color: #000;
  height: 100%;
  font-size: 14px;
  display: flex;
  justify-content: center;
  align-items: center;
  font-weight: bold;
}


.w-table-date .w-day-item, .table-week .w-day-item {
  color: #000;
  font-size: 14px;
  display: flex;
  justify-content: center;
  align-items: center;
}

.week-table .w-time-period-list {
  width: 100%;
}

.w-time-period-list .w-time-period-row {
  width: 100%;
  min-height: 60px;
}

.w-time-period-col {
  width: 100%;
  min-height: 60px;
  display: flex;
}

.w-time-period-col .w-time-period {
  width: 12.5%;
  display: flex;
  justify-content: center;
  align-items: center;
  border-left: 1px solid #EAEDF2;
  border-bottom: 1px solid #EAEDF2;
  box-sizing: border-box;
}

.w-time-period-col .w-row-day {
  width: 87.5%;
  display: flex;
  justify-content: center;
}

.w-row-day .w-things {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  border-left: 1px solid #EAEDF2;
  border-bottom: 1px solid #EAEDF2;
  box-sizing: border-box;
}

.w-row-day .w-things:last-child {
  border-right: 1px solid #EAEDF2;
}

.w-things .w-thing-item {
  display: flex;
  width: 80%;
  font-size: 14px;
  flex-direction: column;
  justify-content: space-around;
  min-height: 90px;
  border-radius: 10px;
  margin: 2% 1%;
  padding: 1% 2%;
  cursor: pointer;
  color: #fff;
  background: #FF6200;
  box-sizing: border-box;
  transition: all 1s linear .5s;
}


.w-isCurDate {
  color: #FF2525 !important;
}

.w-noMore {
  min-height: 200px;
  padding: 2%;
  display: flex;
  justify-content: center;
  align-items: center;
  border: 1px solid rgba(156, 173, 173, 0.3);
  color: #9CADADB7;
  box-sizing: border-box;
}

.w_expand, .w_shrink {
  color: #0A98D5;
  cursor: pointer;
  width: 100%;
  padding: 2% 0;
  display: flex;
  justify-content: center;
  align-items: center;
  box-sizing: border-box;
}
</style>

js代码 weekTools.js

/**
 * 唯一的随机字符串,用来区分每条数据
 * @returns {string}
 */
export function getUid() {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
    const r = (Math.random() * 16) | 0;
    const v = c === 'x' ? r : (r & 0x3) | 0x8;
    return v.toString(16);
  });
}

/**
 * 计算时间差
 * @param beginTime:2022-01-13
 * @param endTime:2022-01-13
 * @returns {{hours: number, seconds: number, minutes: number, day: number}}
 */
export function dealTime(beginTime, endTime) {
  var dateBegin = new Date(beginTime);
  var dateEnd = new Date(endTime);
  var dateDiff = dateEnd.getTime() - dateBegin.getTime(); //时间差的毫秒数
  var day = Math.floor(dateDiff / (24 * 3600 * 1000)); //计算出相差天数
  var leave1 = dateDiff % (24 * 3600 * 1000); //计算天数后剩余的毫秒数
  var hours = Math.floor(leave1 / (3600 * 1000)); //计算出小时数
  //计算相差分钟数
  var leave2 = leave1 % (3600 * 1000); //计算小时数后剩余的毫秒数
  var minutes = Math.floor(leave2 / (60 * 1000)); //计算相差分钟数

  //计算相差秒数
  var leave3 = leave2 % (60 * 1000); //计算分钟数后剩余的毫秒数
  var seconds = Math.round(leave3 / 1000);
  return {
    day,
    hours,
    minutes,
    seconds
  }
}

/**
 * 获取当天时间
 * @returns {string}
 */
export function getCurDay() {
  var datetime = new Date();
  var year = datetime.getFullYear();
  var month = datetime.getMonth() + 1 < 10 ? "0" + (datetime.getMonth() + 1) : datetime.getMonth() + 1;
  var date = datetime.getDate() < 10 ? "0" + datetime.getDate() : datetime.getDate();
  return `${year}-${month}-${date}`
}

// 日期格式处理
export function formatDate(date) {
  var year = date.getFullYear();
  var months = date.getMonth() + 1;
  var month = (months < 10 ? '0' + months : months).toString();
  var day = (date.getDate() < 10 ? '0' + date.getDate() : date.getDate()).toString();
  return {
    year: year.toString(),
    month,
    day
  }
}

/**
 * 数组中,某个属性相同的数据放在一块,如把某个日期相同的相连一起
 * @param list 传入的数组
 * @param prop 那个属性相同的数据
 * @returns {*[]}
 */
export function margePropData(list = [], prop) {
  let arr = [], tempArr = {};
  list.forEach(item => {
    if (!tempArr[item[prop]]) {
      tempArr[item[prop]] = [item]
    } else {
      tempArr[item[prop]].push(item)
    }
  })
  for (const tempArrKey in tempArr) {
    arr = [...arr, ...tempArr[tempArrKey]]
  }
  return arr
}

/**
 * 合并行
 * @param list
 * @param prop
 */
export function mergeRows(list = [], prop) {
  list.forEach(ele => {
    ele.rowspan = 1
  })
  const len = list.length
  for (let i = 0; i < len; i++) {
    for (let j = i + 1; j < len; j++) {
      if (list[i][prop] === list[j][prop]) {
        list[i].rowspan++
        list[j].rowspan--
      }
    }
    // 这里跳过已经重复的数据
    i = i + list[i].rowspan - 1
  }
  return list
}

/**
 * 根据当前数据的位置,在数组中插入数据
 * 如数组【1,2,4,5】想要在2后面插入3,
 *1:首先获取到2的下标,
 *2:然后获取要插入之前的数据,获取要插入之后的数据,中间就是插入的位置
 *3:最后把这三个按顺序合并就得到在想要的位置插入数据
 * @param list
 * @param index
 * @param target
 */
export function insertArrPositionOfIndex(list = [], index = 0, target = {}) {
  //根据index 找出小于index的数据放在左边
  const leftList = list.filter((t, i) => i < index);
  //根据index 找出大于index的数据放在右边
  const rightList = list.filter((t, i) => i >= index);
  // 最终合并数据
  return [...leftList, target, ...rightList]
}

/**
 * 校验规则
 */
export function verifyRules(list = [], require = []) {
  let message = null
  for (const key of require) {
    const isEmpty = list.every(it => !it[key.prop])
    if (isEmpty) {
      message = key.message
      break;
    }
  }
  return message
}

/**
 * 获取元素下标
 * @param dir 为 1:得到正序遍历方法;为 -1: 得到逆序遍历方法。
 * @returns {(function(*, *, *=): (number|number|number))|*}
 */
export function findArrIndex(dir = 1) {
  return function (array, cb, context) {
    let length = array.length;
    // 控制初始 index,0 或者 length-1
    let index = dir >= 0 ? 0 : length - 1;

    // 条件: 在数组范围内;
    // 递增或递减:递加 1 或者 -1; 妙啊~
    for (; index >= 0 && index <= length - 1; index += dir) {
      if (cb.call(context, array[index], index)) return index
    }
    return -1
  }
}

/**
 * map转换成数组
 * @param target
 * @returns {*[]}
 * @constructor
 */
export function MapConvertArr(target = {}) {
  let list = [];
  for (let key in target) {
    list.push(target[key]);
  }
  return list;
}

/**
 * 对象数组去重
 * @param arr 数组
 * @param prop 根据什么字段去重
 * @returns {any[]}
 */
export function arrayDeduplication(arr, prop) {
  let map = new Map();
  return arr.filter(item => !map.has(item[prop]) && map.set(item[prop], 1));
}

/**
 * 获取当前天时间
 * @param param 【Y:年;M:月;D:日;h:小时;m:分钟;s:秒;】 默认精确到秒
 * @returns {*}
 */
export function getCurrentDate(param = 's') {
  var now = new Date();
  var year = now.getFullYear(); //得到年份
  var month = now.getMonth();//得到月份
  var date = now.getDate();//得到日期
  var day = now.getDay();//得到周几
  var hour = now.getHours();//得到小时
  var minu = now.getMinutes();//得到分钟
  var sec = now.getSeconds();//得到秒
  month = month + 1;
  if (month < 10) month = "0" + month;
  if (date < 10) date = "0" + date;
  if (hour < 10) hour = "0" + hour;
  if (minu < 10) minu = "0" + minu;
  if (sec < 10) sec = "0" + sec;

  const arr = {
    'Y': year,
    'M': year + "-" + month,
    'D': year + "-" + month + "-" + date,
    'h': year + "-" + month + "-" + date + " " + hour,
    'm': year + "-" + month + "-" + date + " " + hour + ":" + minu,
    's': year + "-" + month + "-" + date + " " + hour + ":" + minu + ":" + sec
  }
  return arr[param];
}

/**
 * 获取当天时间前后七天时间
 * @param day day>0 当天时间的后几天 day<0 当天时间前几天
 * @returns {string}
 */
export function getRecentDate(day) {
  var date1 = new Date(),
    time1 = date1.getFullYear() + "-" + (date1.getMonth() + 1) + "-" + date1.getDate();//time1表示当前时间
  var date2 = new Date(date1);
  date2.setDate(date1.getDate() + day);
  const y = date2.getFullYear();
  const m = (date2.getMonth() + 1) > 9 ? (date2.getMonth() + 1) : '0' + (date2.getMonth() + 1)
  const d = date2.getDate() > 9 ? date2.getDate() : '0' + date2.getDate()
  return y + "-" + m + "-" + d;
}

export function MyDebounce(fn, duration = 100) {
  let timer = null
  return (...args) => {
    clearTimeout(timer)
    timer = setTimeout(() => {
      fn(...args);
    }, duration)

  }
}

export function MyThrottle(fn, duration = 100) {
  let target = true;
  return (...arg) => {
    if (!target) {
      return false;
    }
    target = false;
    setTimeout(() => {
      fn(...arg);
      target = true
    }, duration)
  }
}

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值