面试大盘表 - 时间维护功能

2023-11-11

需求描述

由于本人主要负责干部和招聘板块,招聘板块接到了要做一个面试大盘表的需求,需要展示面试官的面试情况,还需要面试官维护自己的可面试时间,之后人资进行面试安排,然后面试者到手机端进行自主选择面试官和面试场次。

最终展示

功能描述

移出维护区域自动上下滚动,单机是单个格子维护,点击鼠标不放开拖动是多个格子维护,每一列是一天的时间。

面试有面试的状态展示,涉及单元格的合并,判断逻辑较为复杂暂不具体分享,有兴趣或有问题可一起讨论。

设计思路

提供日期和时间的表格范围,监听鼠标的按下、拖动和抬起事件,根据起始位置和结束位置来改变被选中单元格的状态。

上下自动滚动是监听移入移出事件,mouseleave和mouseenter事件,注意:这两个事件只会自身生效,而mouseover和mouseout在子元素中会一直触发。在移出的时候判断鼠标位置来添加上滚或者下滚的延时函数,滚动的逻辑是scrollTop的加减,需要在触底和触顶的时候移出定时器,代码中包含相关注释,在移入的时候需要清除定时器来停止上下滚动行为。

时间维护代码

<template>
  <basic-container>
    <div style="overflow: scroll; white-space: nowrap">
      <div class="left_container">
        <div class="date_picker">
          <vue-datepicker-local v-model="interviewDate" type="inline" @change="dateChange"></vue-datepicker-local>
          <!-- <t-date-picker-panel v-model="interviewDate" @change="dateChange" /> -->
        </div>
        <div>
          <el-tabs v-model="typeName" type="border-card" @tab-click="handleClick">
            <el-tab-pane label="初试" name="first">
              <div class="type_table">
                <div class="type_table_title">
                  <div class="table_title_post">岗位</div>
                  <div>领头人</div>
                  <div>钻尖</div>
                  <div>后备</div>
                  <div>其他</div>
                </div>
                <div v-for="item in postList" :key="item.type">
                  <el-tooltip class="item" effect="dark" :content="item.applyPostName" placement="top">
                    <div class="post_postName">{{ item.applyPostName }}</div>
                  </el-tooltip>
                  <div>{{ item.talentType01First }}</div>
                  <div>{{ item.talentType02First }}</div>
                  <div>{{ item.talentType03First }}</div>
                  <div>{{ item.talentType04First === -1 ? 0 : item.talentType04First }}</div>
                </div>
              </div>
            </el-tab-pane>
            <el-tab-pane label="复试" name="second">
              <div class="type_table">
                <div class="type_table_title">
                  <div class="table_title_post">岗位</div>
                  <div>领头人</div>
                  <div>钻尖</div>
                  <div>后备</div>
                  <div>其他</div>
                </div>
                <div v-for="item in postList" :key="item.type">
                  <el-tooltip class="item" effect="dark" :content="item.applyPostName" placement="top">
                    <div class="post_postName">{{ item.applyPostName }}</div>
                  </el-tooltip>
                  <div>{{ item.talentType01Second }}</div>
                  <div>{{ item.talentType02Second }}</div>
                  <div>{{ item.talentType03Second }}</div>
                  <div>{{ item.talentType04Second === -1 ? 0 : item.talentType04Second }}</div>
                </div>
              </div>
            </el-tab-pane>
            <el-tab-pane label="终试" name="third">
              <div class="type_table">
                <div class="type_table_title">
                  <div class="table_title_post">岗位</div>
                  <div>领头人</div>
                  <div>钻尖</div>
                  <div>后备</div>
                  <div>其他</div>
                </div>
                <div v-for="item in postList" :key="item.type">
                  <el-tooltip class="item" effect="dark" :content="item.applyPostName" placement="top">
                    <div class="post_postName">{{ item.applyPostName }}</div>
                  </el-tooltip>
                  <div>{{ item.talentType01Last }}</div>
                  <div>{{ item.talentType02Last }}</div>
                  <div>{{ item.talentType03Last }}</div>
                  <div>{{ item.talentType04Last === -1 ? 0 : item.talentType04Last }}</div>
                </div>
              </div>
            </el-tab-pane>
          </el-tabs>
        </div>
      </div>
      <div v-loading="loading" class="weektime" :style="{ width: containerWidth }">
        <div class="weektime-main">
          <div class="weektime-hd">
            <div class="weektime-hd-title">日期</div>
            <div class="weektime-hd-con">
              <!-- <div class="weektime-hd-con-top">
              <div class="weektime-date-range">00:00 - 12:00</div>
              <div class="weektime-date-range">12:00 - 24:00</div>
            </div> -->
              <!-- <div> -->
              <div v-for="week in weekDays" :key="week" :style="{ color: $moment(week).format('E') === '1' && '#dcdee2' }" class="weektime-date-cell">{{ week }}</div>
              <!-- </div> -->
            </div>
          </div>
          <div class="weektime-hd">
            <div class="weektime-hd-title">星期</div>
            <div class="weektime-hd-con">
              <!-- <div class="weektime-hd-con-top">
              <div class="weektime-date-range">00:00 - 12:00</div>
              <div class="weektime-date-range">12:00 - 24:00</div>
            </div> -->
              <!-- <div> -->
              <div v-for="week in weekDays" :key="week" :style="{ color: $moment(week).format('E') === '1' && '#dcdee2' }" class="weektime-date-cell">
                {{ elist[Number($moment(week).format('E')) - 1] }}
              </div>
              <!-- </div> -->
            </div>
          </div>
          <div class="weektime-bd" @mouseleave="mouseleave" @mouseenter="mouseenter">
            <div class="week-body">
              <div v-for="(hour, index) in hourList" :key="hour.id" class="week-item">
                {{ index !== 0 ? hour.intervalName : '' }}
                <!-- <div v-if="hour.intervalName === '12:30'" class="grey_space"></div> -->
              </div>
            </div>
            <div class="time-body" @mousedown="handleMousedown" @mouseup="handleMouseup" @mousemove="handleMousemove">
              <el-tooltip v-for="(i, key) in weekTimes" :key="key" :data-index="key" :content="tiptxt(key)" :open-delay="800" placement="top" effect="dark">
                <div
                  class="time-cell"
                  :class="{
                    active: list[key] === '1',
                    active_blue: ['2', '3'].includes(list[key]),
                    active_pick: list[key] === '4',
                    active_grey: isGrey(key),
                    'pre-active': preViewIndex.includes(key),
                    disable: disableTimes.includes(key),
                    firstActive:
                      list[key] !== '0' &&
                      ((key >= weekDays.length &&
                        (list[key - weekDays.length] !== list[key] ||
                          (infoList.findIndex((item) => item.index === key - weekDays.length) !== -1 &&
                            infoList.findIndex((item) => item.index === key) !== -1 &&
                            infoList[infoList.findIndex((item) => item.index === key - weekDays.length)].id !== infoList[infoList.findIndex((item) => item.index === key)].id))) ||
                        key < weekDays.length ||
                        (Math.floor(key / weekDays.length) < hourList.length && hourList[Math.floor(key / weekDays.length)].intervalName === '14:30')),
                    lastActive:
                      list[key] !== '0' &&
                      ((key + weekDays.length <= weekDays.length * hourList.length &&
                        (list[key + weekDays.length] !== list[key] ||
                          (infoList.findIndex((item) => item.index === key + weekDays.length) !== -1 &&
                            infoList.findIndex((item) => item.index === key) !== -1 &&
                            infoList[infoList.findIndex((item) => item.index === key + weekDays.length)].id !== infoList[infoList.findIndex((item) => item.index === key)].id))) ||
                        key + weekDays.length > weekDays.length * hourList.length ||
                        (Math.floor(key / weekDays.length) < hourList.length && hourList[Math.floor(key / weekDays.length)].intervalName === '12:30'))
                  }"
                >
                  {{ cardText(key) }}
                </div>
              </el-tooltip>
            </div>
          </div>
        </div>
        <div class="weektime-help">
          <div class="weektime-help-tx">
            <div class="weektime-help-bd">
              <!-- 当前功能暂时不需要,如之后需要添加可以参考 -->
              <!-- <span class="color-box"></span>
              <span class="text-box">未选</span>
              <span class="color-box color-active"></span>
              <span class="text-box">已选</span> -->
            </div>
            <!-- <div class="weektime-help-ft" @click="initList()">清空选择</div> -->
          </div>
          <!-- <div class="weektime-help-select">
          <p v-for="(week, key) in weekDays" v-show="showTimeText[key]" :key="key">
            <span class="weektime-help-week-tx">{{ week + ':' }}</span>
            <span>{{ showTimeText[key] }}</span>
          </p>
        </div> -->
        </div>
        <div class="submit_btn">
          <!-- element的按钮不够大,所以自己又写了个按钮,但是也存在表格的点击事件的优先级比按钮高 -->
          <!-- <div v-loading="submitLoading" class="my_button" @click="submit">提交</div> -->
          <el-button v-loading="submitLoading" type="primary" size="medium" @click="submit">提交</el-button>
        </div>
      </div>
    </div>
  </basic-container>
</template>

<script>
import VueDatepickerLocal from '@/components/datePickLocal'
import * as API from '../interviewOverviewTableApi'
import { GetDateStr } from '@/util/date.js'
let DayTimes = 24 * 2
export default {
  name: 'InterviewOverviewTable',
  props: {
    value: String,
    startTime: Number,
    endTime: Number,
    customDisableTimes: Array
  },
  components: {
    VueDatepickerLocal
  },
  watch: {
    value(n) {
      if (n.split('') === this.list.join('')) return
      this.initList(n)
    }
  },
  data() {
    return {
      timer: '',
      jobNo: '',
      deptTypeCode: '',
      param: {},
      loading: false,
      submitLoading: false,
      interviewDate: '',
      typeName: 'first',
      containerWidth: 'calc(100% - 330px)',
      isMove: false,
      hourList: [],
      statusList: ['', '待安排', '已安排', '无学生预约', '无学生预约', '未安排面试'],
      postList: [
        {
          type: '合计',
          zy: '2',
          zj: '3',
          hb: '95'
        },
        {
          type: '合计',
          zy: '2',
          zj: '3',
          hb: '95'
        },
        {
          type: '合计',
          zy: '2',
          zj: '3',
          hb: '95'
        },
        {
          type: '合计',
          zy: '2',
          zj: '3',
          hb: '95'
        }
      ],
      elist: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
      textList: [],
      list: [],
      infoList: [],
      weekTimes: 24,
      // 实际是以选择的日期向后七天,这里只是当时本来是周几的形式,之后只是为了方便遍历
      weekDays: ['7-3', '7-4', '7-5', '7-6', '7-7', '7-8', '7-9', '7-10'],
      sumitWeekDays: ['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日'],
      timeTextList: [], //显示的时间数组 ["00:00","00:30","01:00",...]
      startIndex: 0,
      axis: {},
      preViewIndex: [],
      showTimeText: []
    }
  },
  computed: {
    disableTimes() {
      if (Array.isArray(this.customDisableTimes) && this.customDisableTimes.every((num) => typeof num === 'number')) return this.customDisableTimes
      if (this.startTime > -1 && this.endTime > -1) {
        const disabled = []
        for (let index = 0; index < this.weekTimes; index++) {
          const firstIdx = index % DayTimes
          if (this.startTime > firstIdx || this.endTime < firstIdx) disabled.push(index)
        }
        return disabled
      }
      return []
    }
  },
  mounted() {
    this.getInfo()
  },
  methods: {
    // 1. 我的空闲时间 2. 已安排面试 3. 安排面试且有人预约 4. 安排面试过了时间没人预约   灰色为前端处理,当前时间超过维护的时间且未安排面试

    getInfo() {
      this.loading = true
      if (window.dd.env.platform === 'notInDingTalk') {
        this.param = this.getAllParams()
        this.deptTypeCode = (JSON.stringify(this.$store.state.user.loginUserInfo) !== '{}' && this.$store.state.user.loginUserInfo.depttypeCode) || this.param.deptTypeCode
        this.jobNo = (this.$store.state.user && this.$store.state.user.userInfo.user_name) || this.param.jobNo
        this.getHourList()
        this.timeTextList = this.initTimeText()
        document.addEventListener('mouseup', this.resetMousemove)
        this.initList(this.value)
        this.interviewDate = this.$moment()
        this.getPostList()
        return
      }
      window.dd.ready(() => {
        const corpId = 'ding6cb12bb491f5fbbc'
        window.dd.runtime.permission.requestAuthCode({
          corpId,
          onSuccess: (res) => {
            API.getDingUserInfoExternal({ code: res.code }).then((res) => {
              if (res.data.code === 200) {
                this.jobNo = res.data.data.jobNumber
                API.getDeptTypeByJobNo({ jobNo: this.jobNo }).then((res) => {
                  if (res.data.code === 200) {
                    this.deptTypeCode = res.data.data[0].value
                    this.getHourList()
                    this.timeTextList = this.initTimeText()
                    document.addEventListener('mouseup', this.resetMousemove)
                    this.initList(this.value)
                    this.interviewDate = this.$moment()
                    this.getPostList()
                  }
                })
              }
            })
          }
        })
      })
    },
    getAllParams() {
      const href = window.location.href
      const query = href.substring(href.indexOf('?') + 1)
      const vars = query.split('&')
      const obj = {}
      for (let i = 0; i < vars.length; i++) {
        const pair = vars[i].split('=')
        // 将参数名和参数值分别作为对象的属性名和属性值
        obj[pair[0]] = pair[1]
      }
      return obj
    },
    isGrey(i) {
      const date = this.sumitWeekDays[i % this.sumitWeekDays.length]
      if (Math.floor(i / this.weekDays.length) + 1 >= this.hourList.length) return
      const time = this.hourList[Math.floor(i / this.weekDays.length) + 1].intervalName
      if (this.list[i] === '1' && new Date(date.slice(0, 10) + ' ' + time + ':00').getTime() < new Date().getTime()) {
        return true
      }
    },
    cardText(i) {
      if (
        this.list[i] !== '0' &&
        ((i >= this.weekDays.length &&
          (this.list[i - this.weekDays.length] !== this.list[i] ||
            (this.infoList.findIndex((item) => item.index === i - this.weekDays.length) !== -1 &&
              this.infoList.findIndex((item) => item.index === i) !== -1 &&
              this.infoList[this.infoList.findIndex((item) => item.index === i - this.weekDays.length)].id !==
                this.infoList[this.infoList.findIndex((item) => item.index === i)].id))) ||
          i < this.weekDays.length ||
          (Math.floor(i / this.weekDays.length) < this.hourList.length && this.hourList[Math.floor(i / this.weekDays.length)].intervalName === '14:30'))
      ) {
        return this.recursionSelect(i, i)
      }
      const text = this.cardText(i - this.weekDays.length, i - this.weekDays.length)
      if (i - this.weekDays.length >= 0) {
        if (text && text.split('-').length > 1) {
          if (['2', '4'].includes(this.list[i])) {
            return '预计人数:' + this.infoList.find((item) => item.index === i).expectedNum
          } else if (this.list[i] === '3') {
            return '已预约人数:' + this.infoList.find((item) => item.index === i).expectedNum
          } else if (this.isGrey(i)) {
            return '未安排面试'
          } else if (!this.isGrey(i) && this.list[i] === '1') {
            return '待安排面试'
          } else if (this.list[i] === '4') {
            return '无学生预约'
          }
        }
        if (text && text.includes('人数') && this.list[i] === '4') {
          return '无学生预约'
        }
      }
    },
    // 递归遍历,拿到时间段
    recursionSelect(i, startIndex) {
      // 这里和firstActive的逻辑一样,但是最开始的时候就没封一块,太长了懒得改了
      // 需要处理边界问题,还要处理12.30-14.30这个中午特殊时间的问题,12.30下一个时间是14.30,所以这两个不是连续的
      // 下午到晚上的分割线,7点之后是晚上,之前是下午
      // 判断是不是首个的依据是list[i](当前格子状态),前一个不等于list[i]或者
      // this.infoList[this.infoList.findIndex((item) => item.index === i + this.weekDays.length)].id !==
      // this.infoList[this.infoList.findIndex((item) => item.index === i)].id
      // 这个id的判断是因为安排多场面试之后不能连在一起,要判断是不是一场面试
      if (
        this.list[i] !== '0' &&
        ((i + this.weekDays.length <= this.weekDays.length * this.hourList.length && this.list[i + this.weekDays.length] !== this.list[i]) ||
          (this.infoList.findIndex((item) => item.index === i + this.weekDays.length) !== -1 &&
            this.infoList.findIndex((item) => item.index === i) !== -1 &&
            this.infoList[this.infoList.findIndex((item) => item.index === i + this.weekDays.length)].id !==
              this.infoList[this.infoList.findIndex((item) => item.index === i)].id) ||
          i + this.weekDays.length > this.weekDays.length * this.hourList.length ||
          (Math.floor(i / this.weekDays.length) < this.hourList.length && this.hourList[Math.floor(i / this.weekDays.length)].intervalName === '12:30'))
      ) {
        if (this.hourList[Math.floor(startIndex / this.weekDays.length)] && this.hourList[Math.floor(i / this.weekDays.length) + 1]) {
          return this.hourList[Math.floor(startIndex / this.weekDays.length)].intervalName + '-' + this.hourList[Math.floor(i / this.weekDays.length) + 1].intervalName
        } else if (this.hourList[Math.floor(startIndex / this.weekDays.length)] && this.hourList[Math.floor(i / this.weekDays.length)]) {
          return this.hourList[Math.floor(startIndex / this.weekDays.length)].intervalName + '-' + this.hourList[Math.floor(i / this.weekDays.length)].intervalName
        }
      } else {
        return this.recursionSelect(i + this.weekDays.length, startIndex)
      }
    },
    getPostList() {
      API.getTodoData({ deptTypeCode: this.deptTypeCode }).then((res) => {
        if (res.data.code === 200) {
          this.postList = res.data.data
        }
      })
    },
    getDetail() {
      this.loading = true

      API.timeDetail({
        interviewerJobNo: this.jobNo,
        interviewDate: this.$moment(this.interviewDate).format('YYYY-MM-DD') + ' 00:00:00'
      })
        .then((res) => {
          if (res.data.code === 200) {
            this.list = new Array(this.weekTimes).fill('0')
            for (let i = 0; i < this.list.length; i++) {
              this.$set(this.list, i, '0')
            }
            res.data.data.forEach((item) => {
              if (this.weekDays.findIndex((w) => item.interviewDate.includes(w)) !== -1) {
                const resultIndex =
                  this.hourList.findIndex((h) => h.id === item.timeId) * this.sumitWeekDays.length + this.sumitWeekDays.findIndex((i) => item.interviewDate.includes(i))
                if (Number(item.status) > Number(this.list[resultIndex])) {
                  this.$set(this.list, resultIndex, item.status + '')
                  if ([2, 3, 4].includes(item.status)) {
                    this.infoList.push({
                      index: resultIndex,
                      id: item.id,
                      expectedNum: item.expectedNum
                    })
                  }
                }
              }
            })
            this.loading = false
          }
        })
        .catch(() => (this.loading = false))
    },
    submit() {
      this.submitLoading = true
      const params = []
      this.list.forEach((item, index) => {
        if (item === '1') {
          const obj = {
            interviewerJobNo: this.jobNo,
            interviewDate: this.sumitWeekDays[index % this.sumitWeekDays.length],
            timeId: this.hourList[Math.floor(index / this.sumitWeekDays.length)].id
          }
          params.push(obj)
        }
      })
      API.updateInterviewerTime({
        jobNo: this.jobNo,
        delDate: this.$moment(this.interviewDate).format('YYYY-MM-DD HH:mm:ss'),
        params
      })
        .then((res) => {
          if (res.data.code === 200) {
            this.submitLoading = false
            this.$message.success('提交成功')
          }
        })
        .catch(() => (this.submitLoading = false))
    },
    dateChange() {
      this.weekDays.forEach((item, index) => {
        this.$set(this.weekDays, index, GetDateStr(index, this.interviewDate, true))
        this.sumitWeekDays[index] = this.$moment(GetDateStr(index, this.interviewDate)).format('YYYY-MM-DD HH:mm:ss')
      })
      this.getDetail()
    },
    getHourList() {
      API.hourList({ current: 1, size: 1000, jobNo: this.jobNo }).then((res) => {
        if (res.data.code === 200) {
          this.hourList = res.data.data.records
          DayTimes = this.weekDays.length
          this.weekTimes = this.hourList.length * DayTimes
          this.dateChange()
        }
      })
    },
    /**
     * 鼠标停留时提示当前时间段
     */
    tiptxt(index) {
      const timeIndex = index % DayTimes
      const weekIndex = ~~(index / DayTimes)
      // 为了解决没有intervalName的问题
      if (
        this.hourList[weekIndex] &&
        this.hourList[weekIndex].intervalName &&
        this.hourList[weekIndex + 1] &&
        this.hourList[weekIndex + 1].intervalName &&
        this.weekDays[timeIndex]
      ) {
        return `${this.weekDays[timeIndex]} ${this.hourList[weekIndex].intervalName} - ${this.hourList[weekIndex + 1].intervalName}`
      } else {
        return ''
      }
    },
    /**
     * 初始化显示的时间数组
     * @return {Array} ["00:00","00:30","01:00",...]
     */
    initTimeText() {
      const timeTextList = []
      const hours = []
      const minutes = ['00', '30']
      for (let i = 0; i <= 24; i++) {
        i < 10 ? hours.push('0' + i) : hours.push(i.toString())
      }
      for (const hour of hours) {
        for (const minute of minutes) {
          timeTextList.push(`${hour}:${minute}`)
        }
      }
      return timeTextList
    },
    handleMousedown(event) {
      this.startIndex = event.target.getAttribute('data-index')
      if (this.disableTimes.includes(~~this.startIndex)) return
      this.isMove = true
      this.axis.startx = this.startIndex % DayTimes
      this.axis.starty = ~~(this.startIndex / DayTimes)
    },
    mouseleave(event) {
      if (this.isMove) {
        var body = document.getElementsByClassName('weektime-bd')
        const scrollDistance = 60
        this.timer = setInterval(() => {
          if (event.offsetY > 400) {
            body[0].scrollTop = body[0].scrollTop + scrollDistance
            //判断是否滚动到底部, 每次加scrollDistance,判断相等需要计算如何才能正好加完时相等,所以判断大于等于
            // 可视高度加距离顶部的滚动高度
            if (body[0].clientHeight + body[0].scrollTop >= body[0].scrollHeight - 1) {
              clearInterval(this.timer)
            }
          } else {
            body[0].scrollTop = body[0].scrollTop - scrollDistance
            //判断是否滚动到顶部, 每次加scrollDistance,判断相等需要计算如何才能减好加完时相等,所以判断小于等于
            // 距离顶部的滚动高度为0即触顶,由于每次减一定数据所以可能无法达到0而是一个负数
            // 例如从40开始减,一次减60是-20,但dom会取0,值是-20,所以小于等于
            if (body[0].scrollTop <= 0) {
              clearInterval(this.timer)
            }
          }
        }, 300)
      }
    },
    mouseenter(event) {
      clearInterval(this.timer)
    },
    handleMouseup(event) {
      clearInterval(this.timer)
      this.handleMousemove(event)
      this.resetMousemove()
    },
    handleMousemove(event) {
      if (!this.isMove) return
      const index = event.target.getAttribute('data-index')
      this.axis.endx = index % DayTimes
      this.axis.endy = ~~(index / DayTimes)
      this.preViewIndex = this.getSelectIndex()
    },
    resetMousemove() {
      if (!this.isMove) return
      this.setSelectIndex(this.preViewIndex)
      this.isMove = false
      this.axis = {}
      this.preViewIndex = []
    },
    /**
     * 获取拖动鼠标选择的index数组
     */
    getSelectIndex() {
      const indexList = []
      const newAxis = {
        startx: Math.min(this.axis.startx, this.axis.endx),
        starty: Math.min(this.axis.starty, this.axis.endy),
        endx: Math.max(this.axis.startx, this.axis.endx),
        endy: Math.max(this.axis.starty, this.axis.endy)
      }
      if (newAxis.endx - newAxis.startx === DayTimes || newAxis.startx - newAxis.endx === DayTimes) {
        indexList.push(newAxis.startx + newAxis.starty * DayTimes)
        indexList.push(newAxis.endy + newAxis.endx * DayTimes)
      }
      for (let y = newAxis.starty; y <= newAxis.endy; y++) {
        for (let x = newAxis.startx; x <= newAxis.endx; x++) {
          indexList.push(x + y * DayTimes)
        }
      }
      return indexList.filter((v) => !this.disableTimes.includes(v))
    },
    /**
     * 设置选择的时间段并赋给绑定的值,选完之前标色是这个方法
     * @param {Array} indexList 选择的index数组
     */
    setSelectIndex(indexList) {
      if (!Array.isArray(indexList)) return
      const listLength = indexList.length
      for (let i = 0; i < listLength; i++) {
        let newData = '1'
        if (this.list[indexList[i]] === '1') {
          newData = '0'
        }
        if (!['0', '1'].includes(this.list[indexList[i]])) {
          newData = this.list[indexList[i]]
        }
        this.list.splice(indexList[i], 1, newData)
      }
      this.$emit('input', this.list.join(''))

      this.showSelectTime(this.list)
    },
    /**
     * 展示选择的时间段
     * @param {Array} list 已选择的list数组
     */
    showSelectTime(list) {
      if (!Array.isArray(list)) return
      const weeksSelect = []
      const listlength = list.length
      this.showTimeText = []
      if (listlength === 0) return
      // 把 336长度的 list 分成 8 组,每组 48 个
      for (var i = 0; i < listlength; i += DayTimes) {
        weeksSelect.push(list.slice(i, i + DayTimes))
      }
      weeksSelect.forEach((item) => {
        this.showTimeText.push(this.getTimeText(item))
      })
    },
    getTimeText(arrIndex) {
      if (!Array.isArray(arrIndex)) return ''
      /*方法一 matchAll 正则匹配 (速度较慢) */
      // let strIndex = arrIndex.join('');
      // let arrMatches = Array.from(strIndex.matchAll(/1+/g));
      // let timeText = "";
      // arrMatches.forEach(value => {
      //   timeText += this.timeTextList[value.index];
      //   timeText += '~' + this.timeTextList[value.index + value[0].length] + '、';
      // })
      /*方法一 end */
      /**方法二 循环 (速度是方法一的十倍+)*/
      const timeLength = arrIndex.length
      let isSelect = false
      let timeText = ''
      arrIndex.forEach((value, index) => {
        if (value === '1') {
          if (!isSelect) {
            timeText += this.timeTextList[index]
            isSelect = true
          }
          if (index === timeLength - 1) timeText += '~' + this.timeTextList[index + 1] + '、'
        } else {
          if (isSelect) {
            timeText += '~' + this.timeTextList[index] + '、'
            isSelect = false
          }
        }
      })
      /*方法二 end */
      return timeText.slice(0, -1)
    },
    initList(value) {
      const reg = new RegExp('^[01]{' + this.weekTimes + '}$')
      if (value && reg.test(value)) {
        this.list = value.split('')
        return this.showSelectTime(this.list)
      }
      this.list = new Array(this.weekTimes).fill('0')
      this.$emit('input', this.list.join(''))
      this.showSelectTime(this.list)
    }
  },
  destroyed() {
    document.removeEventListener('mouseup', this.resetMousemove)
  }
}
</script>

<style lang="scss" scoped>
$width: 66px;
$borderColor: #dbdbdb;
.grey_space {
  background: #dcdcdc;
  height: 16px;
}
/deep/.t-date-picker__panel-date {
  width: 320px;
}
.submit_btn {
  position: fixed;
  bottom: 10px;
  // left: 50%;
  background: white;
  width: 70%;
  height: 50px;
  line-height: 50px;
  text-align: center;
  z-index: 1000;
  .my_button {
    margin: 0 auto;
    width: 100px;
    height: 50px;
    background-color: #409eff;
    color: #fff;
    border-radius: 6px;
    cursor: pointer;
  }
}
/deep/.el-tabs__content {
  max-height: calc(100vh - 420px);
  overflow-y: auto;
  padding-top: 0;
}
.left_container {
  display: inline-block;
  white-space: nowrap;
  vertical-align: top;
  width: 320px;
  margin-right: 10px;
  white-space: nowrap;
  /deep/.datepicker-popup {
    width: 300px;
  }
  /deep/.calendar {
    width: 100%;
  }
  /deep/.calendar-body {
    width: 100%;
  }
  /deep/ .el-tabs__nav-scroll {
    display: flex;
    padding: 0 10px;
    justify-content: space-around;
  }
  /deep/ .el-tabs {
    margin-top: 10px;
  }
  /deep/ .date_picker {
    text-align: center;
  }
  .type_table .post_postName {
    flex: 2;
    text-align: left;
    overflow: hidden; /*内容会被修剪,并且其余内容是不可见的*/
    text-overflow: ellipsis; /*显示省略符号来代表被修剪的文本。*/
    white-space: nowrap;
  }
  .type_table {
    font-size: 12px;
    text-align: center;
    line-height: 40px;
    .type_table_title {
      font-size: 14px;
      font-weight: 600;
      position: sticky;
      top: 0;
      background: #fff;
    }
    & > div {
      display: flex;
      justify-content: space-around;
      flex-wrap: nowrap;
      border-bottom: 1px solid #dbdbdb;
      height: 40px;
      padding: 5px;
      & > div {
        flex: 1;
      }
      & > .table_title_post {
        flex: 2;
      }
    }
  }
}
div,
span,
p {
  margin: 0;
  padding: 0;
  border: 0;
  // font-weight: normal;
  vertical-align: baseline;
  -webkit-tap-highlight-color: transparent;
  -ms-tap-highlight-color: transparent;
  -moz-box-sizing: border-box;
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
}
.weektime {
  display: inline-block;
  min-width: 580px;
  font-size: 14px;
  line-height: 32px;
  color: #515a6e;
  user-select: none;
}
.weektime .weektime-main {
  border: 1px solid #dcdee2;
  border-bottom: 0;
  position: relative;
}
.weektime .weektime-hd {
  display: flex;
  border-bottom: 1px solid #dcdee2;
  position: sticky;
  top: 0;
  // background: #f8f8f9;
}
.weektime .weektime-hd-title {
  display: flex;
  justify-content: center;
  align-items: center;
  /* padding: 0 6px; */
  width: 80px;
  /* height: 65px; */
}
.weektime .weektime-hd-con {
  flex: 1;
  display: flex;
  justify-content: space-around;
  -webkit-box-orient: vertical;
  // flex-direction: column;
}
.weektime .weektime-hd-con-top {
  display: flex;
  border-bottom: 1px solid #dcdee2;
}
.weektime .weektime-date-range {
  width: 288px;
  height: 32px;
  line-height: 32px;
  text-align: center;
  border-left: 1px solid #dcdee2;
}
.weektime .weektime-hd-con-bottom {
  display: flex;
}
.weektime .weektime-date-cell {
  // display: inline-block;
  width: calc(100% / 8);
  padding: 5px;
  margin: 0 2.5px;
  height: 37px;
  /* line-height: 32px; */
  text-align: center;
  border-left: 1px solid #dcdee2;
}
.weektime .weektime-bd {
  display: flex;
  max-height: calc(100vh - 330px);
  overflow: auto;
}
.weektime .week-body {
  width: 80px;
  flex-shrink: 0;
}
.weektime .week-item {
  // border-top: 1px solid #dcdee2;
  text-align: center;
  height: 37px;
  line-height: 30px;
  &:first-child {
    margin-top: -19px;
  }
  &:last-child {
    height: 18px;
  }
}
.weektime .time-body {
  width: 100%;
  height: 210px;
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-around;
  position: relative;
}
.weektime .time-cell {
  position: relative;
  width: calc((100% - 35px) / 8);
  height: 37px;
  border-radius: 8px;
  font-size: 12px;
  margin: 0 2.5px;
  border-left: 1px solid $borderColor;
  border-top: 1px solid $borderColor;
  overflow: hidden;
  transition: all 0.3s ease;
  outline-width: 0;
  text-align: center;
  // 处理滚动条带来的列偏移
  &:nth-of-type(8n) {
    width: calc((100% - 35px) / 8 - 9px);
  }
  .time-cell-card {
    position: absolute;
    width: $width;
    height: 74px;
    background: #35cb44;
  }
}
$borderActiveColor: #35cb44;
.weektime .active {
  background: #ddf8ed;
  color: #69bf7e;
  border-top: 0;
  // border-left: 2px solid $borderActiveColor;
  // border-right: 2px solid $borderActiveColor;
  border-radius: 0;
}
.weektime {
  .active_blue {
    background: #d3eaff;
    color: #6198fe;
    border-radius: 0;
    border-top: 0;
  }
  .active_pick {
    background: #ffdddc;
    color: #e07d28;
    border-radius: 0;
    border-top: 0;
  }
  .active_grey {
    background: #d8d8d8;
    color: #4d6778;
    border-radius: 0;
    border-top: 0;
  }
}
.weektime .time-cell.firstActive {
  // border-top: 2px solid $borderActiveColor;
  border-top-left-radius: 8px;
  border-top-right-radius: 8px;
}
.weektime .time-cell.lastActive {
  // border-bottom: 2px solid $borderActiveColor;
  border-bottom-left-radius: 8px;
  border-bottom-right-radius: 8px;
}
.weektime .time-cell.disable {
  cursor: no-drop;
}
.weektime .time-cell::after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: transparent;
  opacity: 0.5;
  transition: all 866ms ease;
  z-index: 99999;
}
.weektime .pre-active::after {
  background: #113860;
}
.weektime .disable::after {
  background: #cccccc;
}
/* 没用到的样式 */
.time-area {
  width: 545px;
  height: 210px;
  position: absolute;
  top: 0;
  left: 0;
  z-index: 100;
  background: transparent;
}
.weektime .weektime-help {
  // width: 545px;
  border: 1px solid #dcdee2;
  border-top: 0;
  border-top: none;
  padding: 5px 15px;
  // min-height: 40px;
}
.weektime .weektime-help-tx {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.weektime .weektime-help-week-tx {
  color: #999;
}
.weektime .weektime-help-bd {
  display: flex;
  align-items: center;
  -webkit-box-pack: start;
  -ms-flex-pack: start;
  justify-content: flex-start;
  padding: 4px 0;
}
.weektime .weektime-help .color-box {
  width: 14px;
  height: 20px;
  background: #fff;
  border: 1px solid #dddddd;
  display: block;
  margin-right: 6px;
}
.weektime .weektime-help-bd .color-box.color-active {
  background: #2d8cf0;
}
.weektime .weektime-help .text-box {
  margin-right: 15px;
}
.weektime .weektime-help .weektime-help-ft {
  color: #2d8cf0;
  cursor: pointer;
}
</style>

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

面试大盘表 - 时间维护功能 的相关文章

随机推荐

  • git-常见问题解决方法(全)

    git使用过程中遇到的问题解决方法记录 问题 1 更新代码后显示 unable to unlink old xxx xxx xx invalid argument 问题原因 要提交或更新的文件被系统线程占用 解决方法 把相关服务暂停 重新p
  • PDF文件如何转成markdown格式

    百度上根据pdf转makrdown为关键字进行搜索 结果大多数是反过来的转换 即markdown文本转PDF格式 但是PDF转markdown的解决方案很少 正好我工作上有这个需求 所以自己实现了一个解决方案 下图是一个用PDF XChan
  • 微前端的出现的背景和意义

    目录 微前端是什么 大规模 Web 应用的困局 传统 Web 应用的利与弊 背景和意义总结 微前端是什么 微前端是一种类似于微服务的架构 是一种由独立交付的多个前端应用组成整体的架构风格 将前端应用分解成一些更小 更简单的能够独立开发 测试
  • mysql删除一行_MySql删除表中一行的实操方法

    MySql删除表中一行的实操方法 首先你要确定能够唯一确定你那一行数据的字段或字段组合是哪些 DELETE FROM 表名 WHERE 字段1 and 字段2 and 字段1 为能够唯一确定某一行数据的字段组合 中填写你要 删除的字段具体值
  • Unity-ScrollRect-循环播放图片(确实没有转载是真的)

    都在代码和代码注释里了 发表在这里我还是有私心想要问问题的 是因为自己在使用时发现 Unity会调用两次通过继承来的PictureScrollView的Start函数两次 我并不能想明白是怎么回事 还请有了解的指点一下 先谢谢能给我指点的各
  • 【好文分享】亲试可行!简单快捷!如何在Ubuntu上编译Linux0.11

    2023年9月10日 周日上午 昨天晚上按照博客园的这篇文章试了一下 很快就成功在Ubuntu上编译运行了Linux0 11 https www cnblogs com chaoguo1234 p 16883932 html
  • linux redhat6.5 64位 login登陆无限循环

    场景 公司VC上虚机要迁移 通过拷贝vmfs文件方式迁移 开机后 发现无法登陆 一开始怀疑密码有问题 后来排除 然后网上搜索要修改 etc pam d login里边的参数 64位系统将 lib security pam limits so
  • Eclipse 报错: “Workspace in use or cannot be created, chose a different one.”

    打开eclipse报错 Workspace in use or cannot be created chose a different one 意识是 正在使用或无法创建工作区 选择另一个 解决办法 找到你eclipse得工作区 打开 me
  • C++ typeid运算符:获取类型信息

    typeid 运算符用来获取一个表达式的类型信息 类型信息对于编程语言非常重要 它描述了数据的各种属性 对于基本类型 int float 等C 内置类型 的数据 类型信息所包含的内容比较简单 主要是指数据的类型 对于类类型的数据 也就是对象
  • vue2和vue3 父子组件传参及区别

    vue3 1 父组件传子组件 在父组件的子组件标签中定义一个属性 在子组件中用defineProps接收父组件传来的值 父组件
  • python读取excle表中的数据

    没什么可介绍的 直接看代码 import pandas as pd from pandas import DataFrame if name main 读取excle表中的数据 file path r D ex Concrete Data
  • 从零搭建一个vue项目

    为了几个姐妹的需求 本文详细图解怎么样从零搭建一个vue项目 供参考 第一步 了解工具 首先我们需要一些工具 比如npm nodejs vue cli 和一个编译器vscode 也可以用别的 这里用vscode作为开发工具演示 第二步 检查
  • VMware虚拟机首次安装centos7.15后配置网络和关闭防火墙

    听了朋友的意见打算学下linux 学习当然是从安装开始了 网上找了个最新版的边看教程边装 网上看了好几个不同的教程 安装的时候还不难 都比较详细 但配置网络时都说的不太清楚 毕竟我没什么基础 如果不说清楚的话 很多地方我也不太懂 看了很多个
  • 编译内核的相关知识

    1 在PC端搭建环境 ubantu 2 树莓派等芯片带操作系统的启动过程 C51 STM32 裸机 用C直接操控底层寄存器实现相关业务 业务流程型的裸机代码 3 带有操作系统的 X86 intel windows 启动过程 电源 gt bi
  • openGL之API学习(三十)深度缓冲区深度值为负值

    通过 glReadPixels 0 0 WINDOW WIDTH WINDOW HEIGHT GL DEPTH COMPONENT GL UNSIGNED BYTE tmpPixelsBuffer 从帧缓冲区中读取深度信息 深度值竟然是负值
  • 插入排序 Insertion Sort

    插入排序 Insertion Sort 基本概念 插入排序的实现 时间复杂度和空间复杂度 稳定性 基本概念 从 index 1开始 不断将元素插入右边已经排好序的数组 适用于少量元素 Example 9 2 1 4 3 Step 1 9 2
  • 理解分组卷积与深度可分离卷积

    这两种卷积分别是在ResNext论文与MobileNet系列中体现的 貌似Xception中也有深度可分离卷积的体现 作用都很简单 为了降参 目录 1 分组卷积 group convolution 2 深度可分离卷积 depthwise s
  • 测试用例设计--等价类的几个例子

    等价类的设计思路 根据输入条件 确定等价类 包括有效等价类和无效等价类 建立等价类列表 为每个等价类规定一个唯一的编号 设计一个测试用例 使其尽可能多地覆盖尚未被覆盖的有效等价类 重复这一步 直到所有的有效等价类被覆盖完为止 设计一个测试用
  • 如何查看chrome浏览器插件位置 查看chrome浏览器插件位置的方法

    Chrome浏览器插件种类目繁多 插件支持功能也比较强大 Chrome浏览器内的下载的插件往往也可以被其他浏览器支持 那么如何查看chrome浏览器插件位置 想知道谷歌浏览器插件的存放位置小伙伴快来阅读下文教程 谷歌浏览器最新版下载2020
  • 面试大盘表 - 时间维护功能

    需求描述 由于本人主要负责干部和招聘板块 招聘板块接到了要做一个面试大盘表的需求 需要展示面试官的面试情况 还需要面试官维护自己的可面试时间 之后人资进行面试安排 然后面试者到手机端进行自主选择面试官和面试场次 最终展示 功能描述 移出维护