drag-weektime一天时间段选择 新增与编辑

效果图:
 

 
一天的时间范围选择,在网上找的插件,不过大多都是新增,没有修改时的数据回显
在原来的组件上加了数据回显的处理,记录一下方法
 
时间选择的插件 dragWeekTime.vue
<template>
  <div class="c-weektime">
    <div class="c-schedue"></div>
    <!-- mode 为1 表示正在选中, 'c-schedue-notransi' 样式就是选择淡蓝色 框的 -->
    <div
      :class="{ 'c-schedue': true, 'c-schedue-notransi': mode }"
      :style="styleValue"
    ></div>

    <table class="c-weektime-table" :class="{ 'c-min-table': colspan < 2 }">
      <thead class="c-weektime-head">
        <tr>
          <th rowspan="8" class="week-td">星期/时间</th>
          <th :colspan="12 * colspan">00:00 - 12:00</th>
          <th :colspan="12 * colspan">12:00 - 24:00</th>
        </tr>
        <tr>
          <td v-for="t in theadArr" :key="t" :colspan="colspan">
            {{ t }}
          </td>
        </tr>
      </thead>
      <tbody class="c-weektime-body">
        <tr v-for="t in data" :key="t.row">
          <td>{{ t.value }}</td>
          <td
            v-for="n in t.child"
            :key="`${n.row}-${n.col}`"
            :data-week="n.row"
            :data-time="n.col"
            @mouseenter="cellEnter(n)"
            @mousedown="cellDown(n)"
            @mouseup="cellUp(n)"
            :class="selectClasses(n)"
            class="weektime-atom-item"
          ></td>
        </tr>
        <tr>
          <td colspan="49" class="c-weektime-preview">
            <div class="g-clearfix c-weektime-con">
              <span class="g-pull-left">{{
                selectState ? "已选择时间段" : "可拖动鼠标选择时间段"
              }}</span>
              <a class="g-pull-right" @click.prevent="$emit('on-clear')"
                >清空选择</a
              >
            </div>
            <div v-if="selectState" class="c-weektime-time">
              <div v-for="t in selectValue" :key="t.id">
                <p v-if="t.value">
                  <span class="g-tip-text">{{ t.week }}:</span>
                  <span>{{ t.value }}</span>
                </p>
              </div>
            </div>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</template>
<script>
const createArr = (len) => {
  return Array.from(Array(len)).map((ret, id) => id);
};
export default {
  name: "DragWeektime",
  props: {
    value: {
      type: Array,
    },
    data: {
      type: Array,
    },
    colspan: {
      type: Number,
      default() {
        return 2;
      },
    },
  },
  computed: {
    // 用于计算 用户选择时间段的范围
    styleValue() {
      return {
        width: `${this.width}px`,
        height: `${this.height}px`,
        left: `${this.left}px`,
        top: `${this.top}px`,
      };
    },
    selectValue() {
      return this.value;
    },
    selectState() {
      return this.value.some((ret) => ret.value);
    },
    selectClasses() {
      return (n) => (n.check ? "ui-selected" : "");
    },
  },
  methods: {
    // 鼠标悬浮的最后位置
    cellEnter(item) {
      console.log("cellEnter");
      // 找到鼠标最终悬停位置的 表格
      const ele = document.querySelector(
        `td[data-week='${item.row}'][data-time='${item.col}']`
      );
      if (ele && !this.mode) {
        this.left = ele.offsetLeft;
        this.top = ele.offsetTop;
      } else {
        // 根据起始点 和 最终点, 计算出 选中框的 left、top 属性 和 width 和 height 高度
        if (item.col <= this.col && item.row <= this.row) {
          // 最终的节点,在起始点的 —— 左上角
          this.width = (this.col - item.col + 1) * ele.offsetWidth;
          this.height = (this.row - item.row + 1) * ele.offsetHeight;
          this.left = ele.offsetLeft;
          this.top = ele.offsetTop;
        } else if (item.col >= this.col && item.row >= this.row) {
          // 最终的节点,在起始点的 —— 右下角
          this.width = (item.col - this.col + 1) * ele.offsetWidth;
          this.height = (item.row - this.row + 1) * ele.offsetHeight;
          if (item.col > this.col && item.row === this.row)
            this.top = ele.offsetTop;
          if (item.col === this.col && item.row > this.row)
            this.left = ele.offsetLeft;
        } else if (item.col > this.col && item.row < this.row) {
          // 最终的节点,在起始点的 —— 右上角
          this.width = (item.col - this.col + 1) * ele.offsetWidth;
          this.height = (this.row - item.row + 1) * ele.offsetHeight;
          this.top = ele.offsetTop;
        } else if (item.col < this.col && item.row > this.row) {
          // 最终的节点,在起始点的 —— 左下角
          this.width = (this.col - item.col + 1) * ele.offsetWidth;
          this.height = (item.row - this.row + 1) * ele.offsetHeight;
          this.left = ele.offsetLeft;
        }
      }
    },
    cellDown(item) {
      console.log("cellDown");
      // item = {
      //   'week': '星期一',
      //   'value': '01:00~01:30',
      //   'begin': '01:00',
      //   'end': '01:30',
      //   'row': 0, // 代表星期几, 0 代表星期一 6 代表星期 天
      //   'col': 2 // 代表哪个时段 (半个小时为一个时段,一天有48个时段)
      // }
      // 获取用户选中的点
      const ele = document.querySelector(
        `td[data-week='${item.row}'][data-time='${item.col}']`
      );
      // 判断用户选中的格子是否被选中
      this.check = Boolean(item.check); // 用于样式的渲染
      this.mode = 1; //  mode 为1 表示正在选中 的样式
      if (ele) {
        // with 和 height 是用户手动选择的范围
        this.width = ele.offsetWidth;
        this.height = ele.offsetHeight;
      }
      // 起始行
      this.row = item.row;
      // 起始列
      this.col = item.col;
    },
    cellUp(item) {
      console.log("cellUp", this.data);
      // 最终的节点,在起始点的 —— 左上角
      if (item.col <= this.col && item.row <= this.row) {
        this.selectWeek(
          [item.row, this.row],
          [item.col, this.col],
          !this.check
        );
      } else if (item.col >= this.col && item.row >= this.row) {
        // 最终的节点,在起始点的 —— 右下角
        this.selectWeek(
          [this.row, item.row],
          [this.col, item.col],
          !this.check
        );
      } else if (item.col > this.col && item.row < this.row) {
        // 最终的节点,在起始点的 —— 右上角
        this.selectWeek(
          [item.row, this.row],
          [this.col, item.col],
          !this.check
        );
      } else if (item.col < this.col && item.row > this.row) {
        // 最终的节点,在起始点的 —— 左下角
        this.selectWeek(
          [this.row, item.row],
          [item.col, this.col],
          !this.check
        );
      }
      this.width = 0; // 选中的宽度为 0
      this.height = 0; // 选中的高度为 0
      this.mode = 0; // 标记不是选中的状态
    },
    // 计算出 起始点  和  最重点  范围的节点,然后将它们的状态值改为 “选中状态", 会计算出样式
    selectWeek(row, col, check) {
      const [minRow, maxRow] = row;
      const [minCol, maxCol] = col;
      this.data.forEach((item) => {
        item.child.forEach((t) => {
          if (
            t.row >= minRow &&
            t.row <= maxRow &&
            t.col >= minCol &&
            t.col <= maxCol
          ) {
            this.$set(t, "check", check);
          }
        });
      });
    },
  },
  data() {
    return {
      width: 0,
      height: 0,
      left: 0,
      top: 0,
      mode: 0,
      row: 0,
      col: 0,
      theadArr: [],
    };
  },
  created() {
    this.theadArr = createArr(24);
  },
};
</script>
<style lang="less" scoped>
.c-weektime {
  min-width: 640px;
  position: relative;
  display: inline-block;
}
.c-schedue {
  background: #598fe6;
  position: absolute;
  width: 0;
  height: 0;
  opacity: 0.6;
  pointer-events: none;
}
.c-schedue-notransi {
  transition: width 0.12s ease, height 0.12s ease, top 0.12s ease,
    left 0.12s ease;
}
.c-weektime-table {
  border-collapse: collapse;
  th {
    vertical-align: inherit;
    font-weight: bold;
  }
  tr {
    height: 30px;
  }
  tr,
  td,
  th {
    user-select: none;
    border: 1px solid #dee4f5;
    text-align: center;
    min-width: 12px;
    line-height: 1.8em;
    transition: background 0.2s ease;
  }
  .c-weektime-head {
    font-size: 12px;
    .week-td {
      width: 70px;
    }
  }
  .c-weektime-body {
    font-size: 12px;
    td {
      &.weektime-atom-item {
        user-select: unset;
        background-color: #f5f5f5;
      }
      &.ui-selected {
        background-color: #598fe6;
      }
    }
  }
  .c-weektime-preview {
    line-height: 2.4em;
    padding: 0 10px;
    font-size: 14px;
    .c-weektime-con {
      line-height: 46px;
      user-select: none;
    }
    .c-weektime-time {
      text-align: left;
      line-height: 2.4em;
      p {
        max-width: 625px;
        line-height: 1.4em;
        word-break: break-all;
        margin-bottom: 8px;
      }
    }
  }
}
.c-min-table {
  tr,
  td,
  th {
    min-width: 24px;
  }
}
.g-clearfix {
  &:after,
  &:before {
    clear: both;
    content: " ";
    display: table;
  }
}
.g-pull-left {
  float: left;
}
.g-pull-right {
  float: right;
}
.g-tip-text {
  color: #999;
}
</style>

 

数据处理方 handleData.js
const handleData = weekData => {
    let allWeeks = [
        '星期一',
        '星期二',
        '星期三',
        '星期四',
        '星期五',
        '星期六',
        '星期日',
    ];
    // 将weekdata中空数据补齐
    allWeeks = allWeeks.map((item, i) => {
        let newItem = {
            id: i,
            week: item,
            value: '',
        };
        weekData.some(subItem => {
            if (subItem.week === item) {
                newItem = {
                    ...subItem,
                };
            }
        });
        return newItem;
    });

    const formatDate = (date, fmt) => {
        let o = {
            'M+': date.getMonth() + 1,
            'd+': date.getDate(),
            'h+': date.getHours(),
            'm+': date.getMinutes(),
            's+': date.getSeconds(),
            'q+': Math.floor((date.getMonth() + 3) / 3),
            S: date.getMilliseconds(),
        };
        if (/(y+)/.test(fmt)) {
            fmt = fmt.replace(
                RegExp.$1,
                (date.getFullYear() + '').substr(4 - RegExp.$1.length),
            );
        }
        for (var k in o) {
            if (new RegExp('(' + k + ')').test(fmt)) {
                fmt = fmt.replace(
                    RegExp.$1,
                    RegExp.$1.length === 1
                        ? o[k]
                        : ('00' + o[k]).substr(('' + o[k]).length),
                );
            }
        }
        return fmt;
    };

    const createArr = len => {
        return Array.from(Array(len)).map((ret, id) => id);
    };

    const formatWeektime = col => {
        const timestamp = 1542384000000; // '2018-11-17 00:00:00'
        const beginstamp = timestamp + col * 1800000; // col * 30 * 60 * 1000
        const endstamp = beginstamp + 1800000;

        const begin = formatDate(new Date(beginstamp), 'hh:mm');
        const end = formatDate(new Date(endstamp), 'hh:mm');
        return `${begin}~${end}`;
    };

    const data = allWeeks.map((ret, index) => {
        const children = (ret, row, max) => {
            const valueRange = [];
            if (ret.value) {
                ret.value.split('、').forEach(item => {
                    if (item) {
                        const minValue = new Date(
                            `2018-01-01 ${item.split('~')[0]}`,
                        ).getTime();
                        const maxValue = new Date(
                            `2018-01-01 ${item.split('~')[1]}`,
                        ).getTime();
                        valueRange.push({
                            minValue,
                            maxValue,
                        });
                    }
                });
                console.log(valueRange);
            }
            return createArr(max).map((t, col) => {
                const newItem = {
                    week: ret.week,
                    value: formatWeektime(col),
                    begin: formatWeektime(col).split('~')[0],
                    end: formatWeektime(col).split('~')[1],
                    row: row,
                    col: col,
                };
                if (valueRange.length) {
                    const endTime = new Date(
                        `2018-01-01 ${newItem.end}`,
                    ).getTime();
                    valueRange.some(item => {
                        if (
                            endTime > item.minValue &&
                            endTime <= item.maxValue
                        ) {
                            newItem.check = true;
                            return true;
                        }
                    });
                }
                return newItem;
            });
        };
        return {
            value: ret.week,
            row: index,
            child: children(ret, index, 48),
        };
    });
    return data;
};

export default handleData;

 

组件引用 index.vue
<template>
  <div class="about">
    <div>
      <h3>新增</h3>
      <drag-weektime
        v-model:value="multTimeRange"
        :data="weektimeData"
        @on-clear="clearWeektime"
      >
      </drag-weektime>
      <a-button type="parimary" @click="submit">提交</a-button>
    </div>
    <div>
      <h3>数据回显</h3>
      <drag-weektime
        v-model:value="multTimeRange2"
        :data="weektimeData2"
        @on-clear="clearWeektime2"
      >
      </drag-weektime>
      <a-button type="parimary" @click="submit2">提交</a-button>
    </div>
  </div>
</template>
<script>
import DragWeektime from "./components/dragWeekTime.vue";
import handleData from "./components/handleData";

function splicing(list) {
  let same;
  let i = -1;
  let len = list.length;
  let arr = [];

  if (!len) return;
  while (++i < len) {
    const item = list[i];
    if (item.check) {
      if (item.check !== Boolean(same)) {
        arr.push(...["、", item.begin, "~", item.end]);
      } else if (arr.length) {
        arr.pop();
        arr.push(item.end);
      }
    }
    same = Boolean(item.check);
  }
  arr.shift();
  return arr.join("");
}

export default {
  name: "DragTime",
  components: { DragWeektime }, //, DatePicker
  computed: {
    multTimeRange() {
      return this.weektimeData.map((item) => {
        return {
          id: item.row,
          week: item.value,
          value: splicing(item.child),
        };
      });
    },
    multTimeRange2() {
      return this.weektimeData2.map((item) => {
        return {
          id: item.row,
          week: item.value,
          value: splicing(item.child),
        };
      });
    },
  },
  data() {
    return {
      weektimeData: handleData([]),
      weektimeData2: handleData([]),
    };
  },
  created() {
    setTimeout(() => {
      const testData = [
        {
          id: 0,
          week: "星期一",
          value: "05:00~11:00、15:00~17:30",
        },
        { id: 2, week: "星期三", value: "06:30~18:00" },
      ];
      this.weektimeData2 = handleData(testData);
      console.log("multTimeRange后台数据", this.multTimeRange2);
      console.log("weektimeData转换后数据", this.weektimeData2);
    }, 1000);
  },
  methods: {
    // 清空时间段
    clearWeektime() {
      this.weektimeData.forEach((item) => {
        item.child.forEach((t) => {
          t.check = false;
        });
      });
    },
    // 提交数据格式
    submit() {
      console.log("submit", this.multTimeRange);
    },
    // 清空时间段
    clearWeektime2() {
      this.weektimeData2.forEach((item) => {
        item.child.forEach((t) => {
          t.check = false;
        });
      });
    },
    // 提交数据格式
    submit2() {
      console.log("submit", this.multTimeRange);
    },
  },
};
</script>

 

组件需要的数据是这种有 48 个时间段的数据,如果选中,就将 check 属性设置为 true
 
一般提交时传给后端的数据,是转化过的,如下:

 

这个 handleData.js 就是用来将后端返回的数据,再处理成组件需要的数据

拿到后台数据格式可以是这种星期一到星期日完整的数据:

const testData = [
                { id: 0, week: '星期一', value: '05:00~11:00、15:00~17:30' },
                { id: 1, week: '星期二', value: '' },
                { id: 2, week: '星期三', value: '06:30~18:00' },
                { id: 3, week: '星期四', value: '' },
                { id: 4, week: '星期五', value: '' },
                { id: 5, week: '星期六', value: '' },
                { id: 6, week: '星期日', value: '' },
            ];

 也可以是只有value有值的数据:

const testData = [
        {
          id: 0,
          week: "星期一",
          value: "05:00~11:00、15:00~17:30",
        },
        { id: 2, week: "星期三", value: "06:30~18:00" },
      ];

handledata方法已经做了处理

要注意这里的数据时间段是用 、分隔,如果是其他分隔,可以修改成上面的数据格式 ,或者修改处理方法

posted @ 2023-02-16 14:43  潇湘羽西  阅读(455)  评论(0编辑  收藏  举报