使用vue把一周的时间划分为可以选择多个阶段

本文详细介绍了如何使用Vue.js创建一个可选择一周多个时间段的组件,通过`mousedown`、`mousemove`和`mouseup`事件实现鼠标操作,适用于时间管理或任务分配场景。

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

提示:使用vue把一周的时间划分为可以选择多个阶段段(亲测有效)

一、直接复制以下代码

在这里插入图片描述

代码如下(示例):


<template>
  <div class="byted-weektime" @mousedown="dian" @mousemove="yi" @mouseup="li">
    <div class="calendar">
      <table class="calendar-table" style="width:610px">
        <thead class="calendar-head">
          <tr>
            <th rowspan="6" class="week-td">星期/时间</th>
            <th colspan="24">00:00 - 12:00</th>
            <th colspan="24">12:00 - 24:00</th>
          </tr>
          <tr>
            <td colspan="2" v-for="(item,index) in tableHeader" :key="index">{{item}}</td>
          </tr>
        </thead>
        <tbody id="tableBody">
          <div
            id="kuang"
            :style="{width:kuangObj.width+'px',height:kuangObj.height+'px',top:kuangObj.top+'px',left:kuangObj.left+'px',bottom:kuangObj.bottom+'px',right:kuangObj.right+'px'}"
          ></div>
          <tr>
            <td>星期一</td>
            <td
              @mousedown.prevent="handleMouseDown(i,0)"
              @mouseup.prevent="handleMouseUp(i,0)"
              class="calendar-atom-time"
              :class="item.class"
              v-for="(item,i) in rowUnit[0]"
              :key="i"
            ></td>
          </tr>
          <tr>
            <td>星期二</td>
            <td
              @mousedown.prevent="handleMouseDown(i,1)"
              @mouseup.prevent="handleMouseUp(i,1)"
              class="calendar-atom-time"
              :class="item.class"
              v-for="(item,i) in rowUnit[1]"
              :key="i"
            ></td>
          </tr>
          <tr>
            <td>星期三</td>
            <td
              @mousedown.prevent="handleMouseDown(i,2)"
              @mouseup.prevent="handleMouseUp(i,2)"
              class="calendar-atom-time"
              :class="item.class"
              v-for="(item,i) in rowUnit[2]"
              :key="i"
            ></td>
          </tr>
          <tr>
            <td>星期四</td>
            <td
              @mousedown.prevent="handleMouseDown(i,3)"
              @mouseup.prevent="handleMouseUp(i,3)"
              class="calendar-atom-time"
              :class="item.class"
              v-for="(item,i) in rowUnit[3]"
              :key="i"
            ></td>
          </tr>
          <tr>
            <td>星期五</td>
            <td
              @mousedown.prevent="handleMouseDown(i,4)"
              @mouseup.prevent="handleMouseUp(i,4)"
              class="calendar-atom-time"
              :class="item.class"
              v-for="(item,i) in rowUnit[4]"
              :key="i"
            ></td>
          </tr>
          <tr>
            <td>星期六</td>
            <td
              @mousedown.prevent="handleMouseDown(i,5)"
              @mouseup.prevent="handleMouseUp(i,5)"
              class="calendar-atom-time"
              :class="item.class"
              v-for="(item,i) in rowUnit[5]"
              :key="i"
            ></td>
          </tr>
          <tr>
            <td>星期日</td>
            <td
              @mousedown.prevent="handleMouseDown(i,6)"
              @mouseup.prevent="handleMouseUp(i,6)"
              class="calendar-atom-time"
              :class="item.class"
              v-for="(item,i) in rowUnit[6]"
              :key="i"
            ></td>
          </tr>

          <tr>
            <td colspan="49" class="td-table-tip">
              <div class="clearfix">
                <span class="pull-left tip-text">请用鼠标点选时间段</span>
                <a @click="clear" class="pull-right">清空</a>
              </div>
            </td>
          </tr>
          <tr>
            <td colspan="49" class="timeContent">
              <div v-for="(item,index) in timeStr" :key="index" v-show="item.length">
                <span>{{weekDate[index+1]}}:</span>
                <strong>
                  <span>{{item}}</span>
                </strong>
              </div>
            </td>
          </tr>
        </tbody>
      </table>
    </div>
    <!-- 鼠标的画框的效果 -->
    <div
      id="container"
      v-bind:style="{backgroundColor:back,
            height:h+'px',
            width:w+'px',
            position:'absolute',
            left:left+'px',
            top:top+'px',
            opacity:0.2,
            border:len+'px dashed #000',
            zIndex:-1}"
    ></div>
  </div>
</template>

<script>
export default {
  name: "timeSelect",
  data() {
    return {
      tableHeader: [
        "00",
        "01",
        "02",
        "03",
        "04",
        "05",
        "06",
        "07",
        "08",
        "09",
        "10",
        "11",
        "12",
        "13",
        "14",
        "15",
        "16",
        "17",
        "18",
        "19",
        "20",
        "21",
        "22",
        "23"
      ],
      weekDate: {
        "1": "星期一",
        "2": "星期二",
        "3": "星期三",
        "4": "星期四",
        "5": "星期五",
        "6": "星期六",
        "7": "星期日"
      },
      rowUnit: [], //每一个单元格
      timeContent: [], //选中的时间段原始数据
      timeSection: [], //时间段,可以返回给后台的数据
      timeStr: [], //时间段,前端显示的数据
      beginDay: 0,
      beginTime: 0,
      downEvent: false,
      kuangObj: {
        width: 0,
        height: 0,
        top: 0,
        left: 0,
        bottom: 0,
        right: 0,
        oldLeft: 0,
        oldTop: 0,
        flag: false
      },
      py: "",
      px: "",
      back: "#31676f",
      h: "",
      w: "",
      top: "",
      left: "",
      len: 0
    };
  },
  created() {
    this.init();
  },
  mounted() {},
  methods: {
    init() {
      for (let i = 0; i < 7; i++) {
        let arr = [];
        for (let j = 0; j < 48; j++) {
          arr.push({ class: null, timeData: j });
        }
        this.rowUnit.push(arr);
        this.timeContent.push({ arr: [] });
        this.timeSection.push([]);
        this.timeStr.push("");
      }
    },
    handleMouseDown(i, day) {
      this.downEvent = true; //按下时鼠标不在范围内则不算
      this.beginDay = day;
      this.beginTime = i;
    },
    dian(event) {
      // console.log(event);
      this.px = event.pageX; //获取x坐标
      this.py = event.pageY;
    },
    yi(event) {
      if (this.px == "" || this.py == "") {
        return;
      }
      var px1 = this.px;
      var px2 = this.py;
      this.left = event.pageX;
      this.top = event.pageY;
      this.h = this.top - this.py;
      this.w = this.left - this.px;
      var hc = -this.h;
      var wc = -this.w;
      this.len = 1;
      this.back = "#31676f";
      if (this.h < 0 && this.w >= 0) {
        // console.log(1);
        this.h = hc;
        this.left = px1;
      } else if (this.h >= 0 && this.w < 0) {
        // console.log(2);
        this.w = wc;
        this.top = px2;
      } else if (this.h < 0 && this.w < 0) {
        // console.log(3);
        this.h = hc;
        this.w = wc;
      } else {
        // console.log(4);
        this.left = this.px;
        this.top = this.py;
      }
      if (this.w < 0) {
        this.w = 0 - this.w;
      }
      if (this.h < 0) {
        this.h = 0 - this.h;
      }
    },
    li() {
      this.px = "";
      this.py = "";
      this.h = "";
      this.w = "";
      this.top = "";
      this.left = "";
      this.len = 0;
      this.back = "";
    },
    handleMouseUp(i, day) {
      let _this = this;
      let begin = this.beginTime;
      let start = begin <= i ? begin : i; //x轴 起点
      let length = Math.abs(begin - i);
      let end = start + length; //x轴 终点

      let dayStart = this.beginDay <= day ? this.beginDay : day; //y轴 起点
      let dayLength = Math.abs(this.beginDay - day);
      let dayEnd = dayStart + dayLength; //y轴 终点

      //当框选范围内所有块都是选中状态时,执行反选
      function isAdd() {
        for (let x = dayStart; x < dayEnd + 1; x++) {
          for (let y = start; y < end + 1; y++) {
            if (_this.rowUnit[x][y].class == null) return true;
          }
        }
        return false;
      }
      //当点击事件是在table内才触发选取数据操作
      if (this.downEvent) {
        //选时间段
        if (isAdd()) {
          //没选中的全都选上
          for (let x = dayStart; x < dayEnd + 1; x++) {
            for (let y = start; y < end + 1; y++) {
              if (this.rowUnit[x][y].class == null) {
                this.rowUnit[x][y].class = "ui-selected";
                this.timeContent[x].arr.push(this.rowUnit[x][y].timeData);
              }
            }
          }
        } else {
          //反选
          for (let x = dayStart; x < dayEnd + 1; x++) {
            for (let y = start; y < end + 1; y++) {
              if (this.rowUnit[x][y].class == "ui-selected") {
                this.rowUnit[x][y].class = null;
                var c = this.rowUnit[x][y].timeData;
                var kong = "";
                for (let i = 0; i < this.timeContent[x].arr.length; i++) {
                  if (c == this.timeContent[x].arr[i]) {
                    kong = i;
                  }
                }
                console.log(kong);
                this.timeContent[x].arr.splice(kong, 1);
              }
            }
          }
        }
        //过滤时间段,将临近的时间段合并
        this.filterTime(dayStart, dayEnd);
      }
      this.downEvent = false;
    },
    filterTime(start, end) {
      //选中的x,y坐标信息 x:0-47  y:0-6
      function sortCut(arr) {
        //提取连续的数字
        var result = [];
        arr.forEach(function(v, i) {
          var temp = result[result.length - 1];
          if (!i) {
            result.push([v]);
          } else if (v % 1 === 0 && v - temp[temp.length - 1] == 1) {
            temp.push(v);
          } else {
            result.push([v]);
          }
        });
        return result;
      }
      function toStr(num) {
        if (Number.isInteger(num)) {
          let str = num < 10 ? "0" + num : num.toString();
          return str + ":00";
        } else {
          let str =
            Math.floor(num) < 10
              ? "0" + Math.floor(num)
              : Math.floor(num).toString();
          return str + ":30";
        }
      }
      function timeToStr(arr) {
        //把数组转成方便人看到字符串
        let str = "";
        arr.forEach((arr, index) => {
          let str1 = "";
          if (index == 0) {
            str1 = toStr(arr[0]) + "~" + toStr(arr[1]);
          } else {
            str1 = " , " + toStr(arr[0]) + "~" + toStr(arr[1]);
          }
          str += str1;
        });

        return str;
      }
      //排序,分割成
      for (let i = start; i < end + 1; i++) {
        let arr1 = sortCut(this.timeContent[i].arr.sort((a, b) => a - b));
        let arr2 = [];
        arr1.forEach(arr => {
          //转成带小数点的时间段,以及供前端显示的字符串
          let arr3 = [];
          arr3.push(arr[0] / 2);
          arr3.push(arr[arr.length - 1] / 2 + 0.5);
          arr2.push(arr3);
        });
        this.timeStr[i] = timeToStr(arr2);
        this.timeSection[i] = arr2;
      }
      // console.log(this.timeSection)
    },
    clear() {
      this.rowUnit.forEach(item => {
        item.forEach(item1 => {
          item1.class = null;
        });
      });
      this.timeContent.forEach(item => {
        item.arr = [];
      });
      this.timeSection.forEach(item => {
        //赋值成空数组[]出问题
        item.length = 0;
      });
      //遍历赋值成'',不管用
      this.timeStr.length = 0;
      for (let i = 0; i < 7; i++) {
        this.timeStr.push("");
      }
      //this.initState = true
    },
  }
};
</script>

<style scoped>
.byted-weektime .calendar {
  -webkit-user-select: none;
  position: relative;
  display: inline-block;
}
#tableBody {
  /* position: relative; */
}
/*.byted-weektime .calendar .schedule{background:#2F88FF;width:0;height:0;position:fixed;display:none;top:0;left:0;pointer-events:none;-webkit-transition:all 400ms ease;-moz-transition:all 400ms ease;-ms-transition:all 400ms ease;transition:all 400ms ease}*/
.byted-weektime .calendar .calendar-table {
  border-collapse: collapse;
  border-radius: 4px;
}
.byted-weektime .calendar .calendar-table tr .calendar-atom-time:hover {
  background: #ccc;
}
.byted-weektime .calendar .calendar-table tr .ui-selected {
  background: #2f88ff;
}
.byted-weektime .calendar .calendar-table tr .ui-selected:hover {
  background: #2f88ff;
}
.byted-weektime .calendar .calendar-table tr,
.byted-weektime .calendar .calendar-table td,
.byted-weektime .calendar .calendar-table th {
  border: 1px solid #ccc;
  font-size: 12px;
  text-align: center;

  line-height: 1.8em;
  -webkit-transition: background 200ms ease;
  -moz-transition: background 200ms ease;
  -ms-transition: background 200ms ease;
  transition: background 200ms ease;
}
.byted-weektime .calendar .calendar-table tbody tr {
  height: 30px;
}
.byted-weektime .calendar .calendar-table tbody tr td:first-child {
  background: #f8f9fa;
}
.byted-weektime .calendar .calendar-table thead th,
.byted-weektime .calendar .calendar-table thead td {
  background: #f8f9fa;
}
.byted-weektime .calendar .calendar-table .td-table-tip {
  line-height: 2.4em;
  padding: 0 12px 0 19px;
  background: #fff !important;
}
.byted-weektime .calendar .calendar-table .td-table-tip .clearfix {
  height: 46px;
  line-height: 46px;
}
.byted-weektime .calendar .calendar-table .td-table-tip .pull-left {
  font-size: 14px;
  color: #333333;
}
.byted-weektime .week-td {
  width: 75px;
  padding: 20px 0;
}
.byted-weektime a {
  cursor: pointer;
  color: #2f88ff;
  font-size: 14px;
}
#kuang {
  position: absolute;
  background-color: blue;
  opacity: 0.3;
}
</style>




<think>好的,我现在需要在UniApp中实现一个日期时间选择器组件。用户可能希望选择日、周、月、季或年等不同的时间范围。首先,我需要了解UniApp现有的组件是否支持这些功能,或者是否需要自定义开发。 根据提供的引用内容,特别是引用[1]和引用[2],用户已经提到了一个自定义组件`<zdp-date-picker>`,它支持通过`type`属性来指定选择类型,比如“月”,并且能够通过`@submit-date`事件处理选择的日期。这说明在UniApp中,可以通过封装自定义组件来实现不同类型的日期选择。另外,引用[2]中提到了使用Vue的props和emit来实现父子组件的通信,这对我设计组件的属性传递和事件处理很有帮助。 接下来,我需要考虑如何构建这个组件。首先,组件的类型(日、周、月等)需要能够通过props传入,如引用[2]中的`type`属性。然后,组件内部需要根据不同的类型展示不同的选择界面。例如,选择月份时可能需要展示月份面板,而选择季度则需要将月份分组为季度。 对于日期选择器的UI部分,可能需要使用picker视图或者自定义的滚动选择器。UniApp的picker组件支持选择,可以用于选择年、月、日。但对于周和季度的选择,可能需要更复杂的逻辑,比如计算某年的周数或季度划分。 另外,引用[3]提到了使用swiper组件来实现左右切换的效果,这可能对设计日期选择器的导航结构有帮助。例如,左右滑动切换月份或年份,但需要结合日期选择的具体需求进行调整。 数据绑定方面,需要使用`modelValue`和`update:modelValue`来实现双向绑定,如引用[2]所示。当用户选择日期后,通过emit事件将值传递给父组件。同时,如果需要处理其他信息显示,比如禁用某些日期,可以通过`fileMonthList`这样的props传递数据,并在组件内部处理这些逻辑。 测试阶段需要确保组件在不同类型下的表现正确,比如选择“月”时返回的格式是否为“2023-02”,选择“季”时返回的格式是否为“2023-Q1”等。同时,跨平台兼容性也需要考虑,确保在iOS、Android以及各小程序平台上都能正常运行。 最后,参考现有的示例代码,可以编写组件的基本结构,处理props、事件和UI渲染逻辑。可能需要引入第三方库或工具函数来处理日期计算,比如moment.js或day.js,但需要注意减小包体积,或者自己实现简单的日期处理函数。</think>在 UniApp 中实现日期时间选择器功能,可通过以下两种方式实现: ### 一、使用现有组件(快速实现) 1. **官方picker组件** 使用`<picker>`组件并设置`mode="date"`,支持基础日期选择: ```vue <picker mode="date" :value="date" @change="handleDateChange"> <view>选择日期:{{date}}</view> </picker> ``` 2. **扩展类型选择** 通过修改`mode`属性和格式转换实现更类型: ```vue <picker mode="date" fields="month" @change="val => formattedDate = val.detail.value.replace('-','年') + '月'" > <view>选择月份:{{formattedDate}}</view> </picker> ``` ### 二、自定义高级组件(推荐完整方案) #### 步骤1:创建组件文件 新建`zdp-date-picker.vue`,定义核心props: ```vue <script setup> const props = defineProps({ type: { type: String, default: "day", // 支持 day/week/month/quarter/year validator: val => ["day","week","month","quarter","year"].includes(val) }, modelValue: { type: String, default: "" }, disabledDates: { type: Array, default: () => [] } // 禁用日期列表 }) const emit = defineEmits(['update:modelValue', 'confirm']) </script> ``` #### 步骤2:实现核心逻辑 ```vue <template> <view class="date-picker-wrapper"> <!-- 头部切换栏 --> <view class="type-switch"> <text v-for="t in ['日','周','月','季','年']" :class="{active: currentType === t}" @click="switchType(t)" >{{t}}</text> </view> <!-- 日期面板 --> <view class="date-panel"> <template v-if="type === 'month'"> <picker-view @change="handleMonthChange"> <picker-view-column> <view v-for="year in yearRange">{{year}}年</view> </picker-view-column> <picker-view-column> <view v-for="month in 12">{{month}}月</view> </picker-view-column> </picker-view> </template> <!-- 季度选择特殊处理 --> <template v-if="type === 'quarter'"> <view class="quarter-grid"> <view v-for="q in quarters" :class="{disabled: isQuarterDisabled(q)}" @click="selectQuarter(q)" >{{q}}季度</view> </view> </template> </view> </view> </template> ``` #### 步骤3:日期处理逻辑 ```javascript // 季度处理函数 const getQuarterRange = (year, quarter) => { const startMonth = (quarter - 1) * 3 + 1 return { start: `${year}-${startMonth.toString().padStart(2,'0')}-01`, end: dayjs(`${year}-${startMonth+2}-28`).endOf('month').format('YYYY-MM-DD') } } // 周数计算 const getWeekNumber = date => { const d = new Date(date) d.setHours(0,0,0,0) d.setDate(d.getDate() + 4 - (d.getDay()||7)) return Math.ceil((((d - new Date(d.getFullYear(),0,1))/8.64e7)+1)/7) } ``` #### 步骤4:双向数据绑定 ```javascript // 日期格式化统一处理 const formatOutput = (value, type) => { const map = { day: val => dayjs(val).format('YYYY-MM-DD'), week: val => `${dayjs(val).year()}-W${getWeekNumber(val)}`, quarter: val => `${val.split('-')[0]}-Q${Math.ceil(val.split('-')[1]/3)}`, month: val => dayjs(val).format('YYYY-MM'), year: val => dayjs(val).format('YYYY') } return map[type](value) } // 值变化时触发 const handleDateUpdate = (rawValue) => { const formatted = formatOutput(rawValue, props.type) emit('update:modelValue', formatted) emit('confirm', formatted) // 同时触发确认事件 } ``` #### 步骤5:禁用日期处理 ```javascript // 判断季度是否禁用 const isQuarterDisabled = (q) => { return props.disabledDates.some(date => { const [year, quarter] = date.split('-Q') return year === selectedYear && quarter == q }) } ``` ### 三、组件使用示例 ```vue <zdp-date-picker type="quarter" v-model="selectedDate" :disabled-dates="['2023-Q3','2024-Q1']" @confirm="handleDateConfirm" /> ``` ### 四、关键优化点 1. **性能优化** - 使用`picker-view-column`虚拟滚动 - 对大规模数据采用分页加载 - 添加防抖处理频繁操作 2. **交互增强** - 增加动画过渡效果 - 支持手势滑动切换年月 - 添加范围选择模式(需修改props) 3. **跨平台适配** ```javascript // 处理小程序与H5的样式差异 const platformClass = process.env.VUE_APP_PLATFORM === 'h5' ? 'h5-picker' : 'mp-picker' ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

herry-弟弟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值