vue布放时间 时间段选择

js yekong

wanjunshijiecom 2021-09-15 at 19.39.03@2x

演示地址:
点击右下角布防时间查看效果

组件

<template>
  <div class="c-weektime">
    <div class="c-schedue"></div>
    <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">

      </thead>
      <tbody class="c-weektime-body">
      <div style="width: 100%;margin-bottom: 10px" v-for="t in data" :key="t.row">
        <tr class="tableinfo1">
          <td></td>
          <td v-for="t in theadArr" :key="t">{{ t }}</td>
        </tr>
        <tr class="tableinfo1">
          <td></td>
          <td v-for="t in theadArr" :key="t">
            <div class="linex"></div>
          </td>
        </tr>
        <tr class="tableinfo2">
          <td class="xingqi">{{ 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>
      </div>
      <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) {
      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 {
        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) {
      const ele = document.querySelector(
        `td[data-week='${item.row}'][data-time='${item.col}']`
      )
      this.check = Boolean(item.check)
      this.mode = 1
      if (ele) {
        this.width = ele.offsetWidth
        this.height = ele.offsetHeight
      }

      this.row = item.row
      this.col = item.col
    },
    cellUp (item) {
      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
      this.height = 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="scss" scoped>
.c-weektime {
  width: 100%;
  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;
  width: 100%;

  th {
    vertical-align: inherit;
    font-weight: bold;
  }

  //tr {
  //  height: 30px;
  //}

  tr,
  td,
  th {
    user-select: none;
    border: 0;
    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;
}

.tableinfo1 {
  width: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-wrap: nowrap;
  flex-direction: row;
  height: 15px;

  td {
    flex: 1;
    font-size: 12px;
    font-family: PingFang SC Medium;
    font-weight: 400;
    color: #FFFFFF;
    display: flex;
    height: 10px;
    line-height: 10px;
    justify-content: center;
    align-items: center;
    flex-wrap: nowrap;
    flex-direction: row;
  }

  .linex {
    width: 1px;
    height: 4px;
    background: #fff;
  }

  td:nth-child(1) {
    flex: 2;
  }

  .weektime-atom-item {
    height: 20px;
  }

}

.tableinfo2 {
  width: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-wrap: nowrap;
  flex-direction: row;

  td {
    flex: 1;
  }

  td:nth-child(2) {
    border-radius: 5px 0 0 5px;
  }

  td:last-child {
    border-radius: 0px 5px 5px 0px;
  }

  .weektime-atom-item {
    height: 20px;
  }

  .xingqi {
    font-size: 16px;
    font-family: PingFang SC Bold;
    font-weight: 400;
    color: #FFFFFF;
    flex: 4;
  }
}
</style>

引用组件

<template>
  <div>
    <el-dialog class="sp-dialog" title="布防时间" :visible.sync="dialogVisible" width="50%">
      <div class="gongzuomoshi">
        <div class="operation">
          <div class="delete cur">
            <img src="../../../assets/icon_timedelete.png" alt="">
            删除
          </div>
          <div @click="clearWeektime" class="deleteAll cur"><img src="../../../assets/icon_timedelete.png" alt="">
            删除全部
          </div>
        </div>
        <drag-weektime
          v-model="mult_timeRange"
          :data="weektimeData"
          @on-clear="clearWeektime"
        >
        </drag-weektime>
      </div>
      <div slot="footer" class="dialog-footer">
        <el-button class="chonghui">保存</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import DragWeektime from '@/components/drag-weektime'
import weektimeData from '@/data/weektime_data'

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: '',
  props: [''],
  data () {
    return {
      dialogVisible: true,
      checkAll: false,
      checkedCities: ['上海', '北京'],
      cities: ['上海', '北京'],
      weektimeData: weektimeData,
      isIndeterminate: true,
    }
  },

  components: { DragWeektime },
  computed: {
    mult_timeRange () {
      return this.weektimeData.map((item) => {
        return {
          id: item.row,
          week: item.value,
          value: splicing(item.child),
        }
      })
    },
  },

  beforeMount () {
  },

  mounted () {
  },

  methods: {
    // 清空时间段
    clearWeektime () {
      this.weektimeData.forEach((item) => {
        item.child.forEach((t) => {
          this.$set(t, 'check', false)
        })
      })
    },
    getshow () {
      this.dialogVisible = true
    },
    handleClose (done) {
      this.$confirm('确认关闭?')
        .then((_) => {
          done()
        })
        .catch((_) => {
        })
    },
  },

  watch: {},
}
</script>
<style lang='scss' scoped>
.gongzuomoshi {
  display: flex;
  justify-content: flex-start;
  align-items: flex-start;
  flex-wrap: nowrap;
  flex-direction: column;
}

.dialog-footer {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-wrap: nowrap;
  flex-direction: row;

  .chonghui {
    width: 100px;
    height: 40px;
    background: linear-gradient(180deg, #0A7E57 0%, #135C68 100%);
    color: #fff;
    border: none;
  }

  .save {
    width: 100px;
    height: 40px;
    background: linear-gradient(180deg, #0A447E 0%, #133A68 100%);
    margin-left: 15px;
    margin-right: 15px;
    color: #fff;
    border: none;
  }

  .quxiao {
    width: 100px;
    height: 40px;
    background: #1E2B44;
    color: #fff;
    border: none;
  }

  .hzgj {
    background: linear-gradient(180deg, #0A447E 0%, #133A68 100%);
    border: none;
    color: #fff;
  }

  .hzgj:hover, .chonghui:hover, .save:hover, .quxiao:hover {
    color: #fff !important;
  }
}

.operation {
  display: flex;
  justify-content: flex-start;
  align-items: center;
  flex-wrap: nowrap;
  flex-direction: row;
  margin-bottom: 50px;
  width: 100%;

  .delete {
    width: 90px;
    height: 40px;
    background: linear-gradient(180deg, #0A447E 0%, #133A68 100%);
    display: flex;
    justify-content: center;
    align-items: center;
    flex-wrap: nowrap;
    flex-direction: row;
    font-size: 14px;
    font-family: PingFang SC Medium;
    font-weight: 400;
    color: #FFFFFF;
    margin-right: 10px;

    img {
      width: 16px;
      height: 16px;
      margin-right: 10px;
    }
  }

  .deleteAll {
    width: 110px;
    height: 40px;
    background: linear-gradient(180deg, #760A7E 0%, #681346 100%);
    display: flex;
    justify-content: center;
    align-items: center;
    flex-wrap: nowrap;
    flex-direction: row;
    font-size: 14px;
    font-family: PingFang SC Medium;
    font-weight: 400;
    color: #FFFFFF;

    img {
      width: 16px;
      height: 16px;
      margin-right: 10px;
    }
  }
}
</style>

下载链接

下载链接 提取码: nc2l

喜欢