vue3鼠标移动矩形选择组件

vue3鼠标移动矩形选择组件

vue3实现鼠标按下后开始移动鼠标选择,移动到的数据就选中,如果已经是被选中则反选。


实例

demo组件

<template>
  <div style="width: 100%;">
    <div class="time">
      <div class="timeleft">/小时</div>
      <div class="timeright">
        <div class="timeright_cell">
          <el-row :gutter="0">
            <el-col :span="12">
              <div class="topitem">00:00~12:00</div>
            </el-col>
            <el-col :span="12">
              <div class="topitem">12:00~24:00</div>
            </el-col>
          </el-row>
        </div>
        <div class="timeright_cell">
          <el-row :gutter="0">
            <el-col :span="1" v-for="(item, index) in 24" :key="item" @click="clicktime(index)">
              <div class="topitem" :style="{ '--bgColor': bgColor }" :class="{ 'is_selected': isTimeSelected(index) }">
                {{ index }}</div>
            </el-col>
          </el-row>
        </div>
      </div>
    </div>
    <div class="time">
      <div style="width: 8%;">
        <div class="timelefts" :style="{ '--bgColor': bgColor }" :class="{ 'is_selected': isDaySelected(index) }"
          v-for="(dayName, index) in ['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日']" :key="dayName"
          @click="clickDay(index)">{{
            dayName }}</div>
      </div>

      <div class="objects" ref="objectsRef" @mousedown="handleMouseDown">
        <!-- 矩形选择框 -->
        <div class="mask" ref="maskRef" v-show="maskPosition.show" :style="'width:' +
          maskWidth +
          'left:' +
          maskLeft +
          'height:' +
          maskHeight +
          'top:' +
          maskTop
          " />

        <!-- 选择对象内容的目标插槽 -->
        <!-- <slot name="selcetObject" /> -->
        <div class="objects_content">
          <div v-for="item in weekData" :key="item.id" class="select_object" :day_index="item.daynum"
            :time_index="item.timenum" :object_id="item.id" :class="{ 'is_selected': item.status }"
            :style="{ '--bgColor': bgColor }">
            <!-- {{ item.id }} -->
          </div>
        </div>
      </div>
    </div>
    <!-- 鼠标画矩形选择对象 -->

  </div>
</template>

<script lang="ts" setup>
import { reactive, toRefs, ref, computed } from "vue";

const clientX = ref(0);
const clientY = ref(0);

const hasHandleObjectIds = ref<number[]>([]);//已处理id数组

const isDaySelected = computed(() => {
  return (index) => {
    if (props.weekData && props.weekData.length > 0) {
      return props.weekData
        .filter(t => t.daynum === index + 1)
        .every(item => item.status === true);
    }
    return false;
  };
})

const isTimeSelected = computed(() => {
  return (index) => {
    if (props.weekData && props.weekData.length > 0) {
      return props.weekData
        .filter(t => t.timenum === index + 1)
        .every(item => item.status === true);
    }
    return false;
  };
})

const props = withDefaults(
  defineProps<{
    objectClassName?: string; // 选择对象的class name,用于定义如何获取对象
    objectIdName?: string; // 选择对象的id name,用于定义如何获取对象的id
    weekData?: Array<any>; // 选中的对象
    useCtrlSelect?: boolean; // 是否支持按住Ctrl多选
    bgColor?: string; //选中的背景色
  }>(),
  {
    objectClassName: 'select_object',
    objectIdName: 'object_id',
    bgColor: '#409eff',
    useCtrlSelect: false // 默认支持按住Ctrl多选
  }
);

const objectsRef = ref();
const maskRef = ref();
const emits = defineEmits(["update:weekData"]);
const state = reactive({
  maskPosition: {
    show: false,
    startX: 0,
    startY: 0,
    endX: 0,
    endY: 0
  }, // 矩形框位置
  isPressCtrlKey: false // 是否按下了Ctrl键
});
const { maskPosition, isPressCtrlKey } = toRefs(state);

// 若支持按住Ctrl多选,监听Ctrl事件
if (props.useCtrlSelect) {
  // 释放
  document.addEventListener("keyup", event => {
    if (event.keyCode === 17) {
      isPressCtrlKey.value = false;
    }
  });
  // 按下
  document.addEventListener("keydown", event => {
    if (event.keyCode === 17) {
      isPressCtrlKey.value = true;
    }
  });
}
//点击星期天数的事件
const clickDay = (index) => {
  var weekData: Array<any> = [];
  weekData = props.weekData === undefined ? [] : props.weekData;
  // const hours = weekData[index].hours;
  // const allIn = hasHandleObjectIds.value.every(item => weekData.includes(item));
  const arr = weekData.filter(t => t.daynum == index + 1);
  const allIn = arr.every(item => item.status === true);
  if (allIn) {
    arr.forEach(e => {
      e.status = false;
      hasHandleObjectIds.value.splice(hasHandleObjectIds.value.findIndex(t => t == e.id), 1);
    });
  } else {
    arr.forEach(e => {
      e.status = true;
      hasHandleObjectIds.value.push(e.id);
    });
  }
}
// 点击时间点数
const clicktime = (index) => {
  var weekData: Array<any> = [];
  weekData = props.weekData === undefined ? [] : props.weekData;
  const arr = weekData.filter(t => t.timenum == index + 1);
  const allIn = arr.every(item => item.status === true);
  if (allIn) {
    arr.forEach(e => {
      e.status = false;
      hasHandleObjectIds.value.splice(hasHandleObjectIds.value.findIndex(t => t == e.id), 1);
    });
  } else {
    arr.forEach(e => {
      e.status = true;
      hasHandleObjectIds.value.push(e.id);
    });
  }
}
/** 鼠标按下 */
const handleMouseDown = event => {
  //点下时清空已处理数组
  hasHandleObjectIds.value.length = 0;
  var id = Number(event.target.getAttribute(props.objectIdName))
  var weekData: Array<any> = [];
  weekData = props.weekData === undefined ? [] : props.weekData;
  // const hourItem = weekData.flatMap(day => day.hours).find(item => item.id === id);
  const index = weekData.findIndex(t => t.id == id);
  if (index > -1) {
    weekData[index].status = !weekData[index].status;
  }
  if (!hasHandleObjectIds.value.includes(id)) {
    hasHandleObjectIds.value.push(id);
  } else {
    hasHandleObjectIds.value.splice(hasHandleObjectIds.value.findIndex(t => t == id), 1);
  }
  // 展示矩形框,通过坐标位置来画出矩形
  maskPosition.value.show = true;
  maskPosition.value.startX = event.clientX;
  maskPosition.value.startY = event.clientY;
  maskPosition.value.endX = event.clientX;
  maskPosition.value.endY = event.clientY;
  // 监听鼠标移动事件和抬起离开事件
  objectsRef.value.addEventListener("mousemove", handleMouseMove);
  objectsRef.value.addEventListener("mouseup", handleMouseUp);
};

/** 鼠标移动 */
const handleMouseMove = event => {
  if (clientX.value !== event.clientX || clientY.value !== event.clientY) {
    clientX.value = event.clientX
    clientY.value = event.clientY
    maskPosition.value.endX = event.clientX;
    maskPosition.value.endY = event.clientY;
    var weekData: Array<any> = [];
    weekData = props.weekData === undefined ? [] : props.weekData;
    const selectedObjects = objectsRef.value.querySelectorAll(`.${props.objectClassName}`);
    // 获取鼠标画出的矩形框位置
    const rectanglePosition = maskRef.value.getClientRects()[0];
    var rectangleSelObjects: Array<number> = [];//矩形框内的id数组
    selectedObjects.forEach(item => {
      const objectPosition = item.getClientRects()[0];
      // 这里获取的id的方式定义于父组件的objectIdName
      if (compareObjectPosition(objectPosition, rectanglePosition)) {
        let id = item.getAttribute(props.objectIdName);
        rectangleSelObjects.push(Number(id));
      }
    });
    let handle = (id: number) => {
      const index = weekData.findIndex(t => t.id == id);
      if (index > -1) weekData[index].status = !weekData[index].status;
      // let index = tempSelectObjectIds.findIndex(t => t == id);
    }
    // rectangleSelObjects.forEach(id => {
    //   if (!hasHandleObjectIds.value.includes(id)) {
    //     handle(id);
    //     hasHandleObjectIds.value.push(id);
    //   }
    // })
    // hasHandleObjectIds.value.forEach(id => {
    //   console.log(rectangleSelObjects);
    //   if (!rectangleSelObjects.includes(id)) {
    //     console.log(id);
    //     handle(id);
    //     hasHandleObjectIds.value.splice(hasHandleObjectIds.value.findIndex(t => t == id), 1);
    //   }
    // })
    // 处理存在于 hasHandleObjectIds 中但不在 rectangleSelObjects 中的元素
    for (let i = hasHandleObjectIds.value.length - 1; i >= 0; i--) {
      const id = hasHandleObjectIds.value[i];
      if (!rectangleSelObjects.includes(id)) {
        handle(id);
        hasHandleObjectIds.value.splice(i, 1);
      }
    }

    // 处理存在于 rectangleSelObjects 中但不在 hasHandleObjectIds 中的元素
    for (const id of rectangleSelObjects) {
      if (!hasHandleObjectIds.value.includes(id)) {
        handle(id);
        hasHandleObjectIds.value.push(id);
      }
    }
    // emits("update:weekData", weekData);
    // emits("update:selectObjectIds", tempSelectObjectIds);
  }
};


/** 鼠标抬起离开 */
const handleMouseUp = () => {
  // 移除鼠标监听事件
  objectsRef.value.removeEventListener("mousemove", handleMouseMove);
  objectsRef.value.removeEventListener("mouseup", handleMouseUp);
  maskPosition.value.show = false;
  handleResetMaskPosition();
};


/**
 * 判断对象坐标是否在鼠标画出的矩形框坐标位置内
 * @param objectPosition 对象坐标位置
 * @param rectanglePosition 鼠标画出的矩形框坐标位置
 */
const compareObjectPosition = (objectPosition, rectanglePosition) => {
  const maxX = Math.max(
    objectPosition.x + objectPosition.width,
    rectanglePosition.x + rectanglePosition.width
  );
  const maxY = Math.max(
    objectPosition.y + objectPosition.height,
    rectanglePosition.y + rectanglePosition.height
  );
  const minX = Math.min(objectPosition.x, rectanglePosition.x);
  const minY = Math.min(objectPosition.y, rectanglePosition.y);
  return (
    maxX - minX <= objectPosition.width + rectanglePosition.width &&
    maxY - minY <= objectPosition.height + rectanglePosition.height
  );
};

/** 重置鼠标位置 */
const handleResetMaskPosition = () => {
  maskPosition.value.startX = 0;
  maskPosition.value.startY = 0;
  maskPosition.value.endX = 0;
  maskPosition.value.endY = 0;
};

/** 通过鼠标位置实时计算矩形框大小 */
const maskWidth = computed(() => {
  return `${Math.abs(maskPosition.value.endX - maskPosition.value.startX)}px;`;
});
const maskHeight = computed(() => {
  return `${Math.abs(maskPosition.value.endY - maskPosition.value.startY)}px;`;
});
const maskLeft = computed(() => {
  return `${Math.min(maskPosition.value.startX, maskPosition.value.endX)}px;`;
});
const maskTop = computed(() => {
  return `${Math.min(maskPosition.value.startY, maskPosition.value.endY)}px;`;
});
</script>

<style scoped lang="scss">
.time {
  width: 100%;
  display: flex;
  align-items: center;
  // border: #999 1px solid;

  .timeleft {
    width: 7.95%;
    height: 80px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #dad8d8;
    border-bottom: #999 1px solid;
    border-top: #999 1px solid;
    border-left: #999 1px solid;
    box-sizing: border-box;
  }

  .timeright {
    height: 80px;
    background: #dad8d8;
    width: 92%;
    border-right: #999 1px solid;
    box-sizing: border-box;
    border-top: #999 1px solid;

    .timeright_cell {
      border-bottom: #999 1px solid;
      box-sizing: border-box;

      .topitem {
        height: 38.6px;
        display: flex;
        align-items: center;
        justify-content: center;
        border-left: #999 1px solid;
        text-align: center;
        box-sizing: border-box;
      }

      .is_selected {
        background: var(--bgColor);
        color: #fff;
      }
    }
  }

  .timelefts {
    height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #dad8d8;
    border-bottom: #999 1px solid;
    border-left: #999 1px solid;
    border-right: #999 1px solid;
    box-sizing: border-box;
  }

  .is_selected {
    background: var(--bgColor);
    color: #fff;
  }

  .objects {
    height: 100%;
    width: 92%;
    // overflow-y: auto;

    .mask {
      position: fixed;
      background: #409eff;
      opacity: 0;
      z-index: 100;
    }

    .objects_content {
      user-select: none;
      display: flex;
      flex-wrap: wrap;

      >div {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 2.083%;
        height: 40px;
        box-sizing: border-box;
        border-bottom: #999 1px solid;
        border-right: #999 1px solid;
      }

      .is_selected {
        color: #fff;
        background-color: var(--bgColor);
      }
    }

  }
}
</style>

数组不传的话就是这个

<template>
  <div style="width: 100%;">
    <div class="time">
      <div class="timeleft">/小时</div>
      <div class="timeright">
        <div class="timeright_cell">
          <el-row :gutter="0">
            <el-col :span="12">
              <div class="topitem">00:00~12:00</div>
            </el-col>
            <el-col :span="12">
              <div class="topitem">12:00~24:00</div>
            </el-col>
          </el-row>
        </div>
        <div class="timeright_cell">
          <el-row :gutter="0">
            <el-col :span="1" v-for="(item, index) in 24" :key="item" @click="clicktime(index)">
              <div class="topitem" :style="{ '--bgColor': bgColor }" :class="{ 'is_selected': isTimeSelected(index) }">
                {{ index }}</div>
            </el-col>
          </el-row>
        </div>
      </div>
    </div>
    <div class="time">
      <div style="width: 8%;">
        <div class="timelefts" :style="{ '--bgColor': bgColor }" :class="{ 'is_selected': isDaySelected(index) }"
          v-for="(dayName, index) in ['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日']" :key="dayName"
          @click="clickDay(index)">{{
            dayName }}</div>
      </div>

      <div class="objects" ref="objectsRef" @mousedown="handleMouseDown">
        <!-- 矩形选择框 -->
        <div class="mask" ref="maskRef" v-show="maskPosition.show" :style="'width:' +
          maskWidth +
          'left:' +
          maskLeft +
          'height:' +
          maskHeight +
          'top:' +
          maskTop
          " />

        <!-- 选择对象内容的目标插槽 -->
        <!-- <slot name="selcetObject" /> -->
        <div class="objects_content">
          <div v-for="item in weekData" :key="item.id" class="select_object" :day_index="item.daynum"
            :time_index="item.timenum" :object_id="item.id" :class="{ 'is_selected': item.status }"
            :style="{ '--bgColor': bgColor }">
            <!-- {{ item.id }} -->
          </div>
        </div>
      </div>
    </div>
    <!-- 鼠标画矩形选择对象 -->

  </div>
</template>

<script lang="ts" setup>
import { reactive, toRefs, ref, computed, onMounted } from "vue";

const clientX = ref(0);
const clientY = ref(0);

const hasHandleObjectIds = ref<number[]>([]);//已处理id数组
// 全局计数器
let uniqueId = 1;
// 初始化 weekData
const weekData = ref([] as any);
// 初始化 weekData 中的 hours
onMounted(() => {
  for (let i = 0; i < 7; i++) {
    for (let hour = 0, timenum = 1; hour < 48; hour += 2) {
      weekData.value.push({ id: uniqueId++, status: false, daynum: i + 1, timenum: timenum });
      weekData.value.push({ id: uniqueId++, status: false, daynum: i + 1, timenum: timenum });
      timenum = (timenum % 24) + 1; // 使 timenum 在 1 到 24 之间循环
    }
  }
});
const isDaySelected = computed(() => {
  return (index) => {
    if (weekData.value && weekData.value.length > 0) {
      return weekData.value
        .filter(t => t.daynum === index + 1)
        .every(item => item.status === true);
    }
    return false;
  };
})

const isTimeSelected = computed(() => {
  return (index) => {
    if (weekData.value && weekData.value.length > 0) {
      return weekData.value
        .filter(t => t.timenum === index + 1)
        .every(item => item.status === true);
    }
    return false;
  };
})

const props = withDefaults(
  defineProps<{
    objectClassName?: string; // 选择对象的class name,用于定义如何获取对象
    objectIdName?: string; // 选择对象的id name,用于定义如何获取对象的id
    useCtrlSelect?: boolean; // 是否支持按住Ctrl多选
    bgColor?: string; //选中的背景色
  }>(),
  {
    objectClassName: 'select_object',
    objectIdName: 'object_id',
    bgColor: '#409eff',
    useCtrlSelect: false // 默认支持按住Ctrl多选
  }
);

const objectsRef = ref();
const maskRef = ref();
const emits = defineEmits(["update:weekData"]);
const state = reactive({
  maskPosition: {
    show: false,
    startX: 0,
    startY: 0,
    endX: 0,
    endY: 0
  }, // 矩形框位置
  isPressCtrlKey: false // 是否按下了Ctrl键
});
const { maskPosition, isPressCtrlKey } = toRefs(state);

// 若支持按住Ctrl多选,监听Ctrl事件
if (props.useCtrlSelect) {
  // 释放
  document.addEventListener("keyup", event => {
    if (event.keyCode === 17) {
      isPressCtrlKey.value = false;
    }
  });
  // 按下
  document.addEventListener("keydown", event => {
    if (event.keyCode === 17) {
      isPressCtrlKey.value = true;
    }
  });
}
//点击星期天数的事件
const clickDay = (index) => {
  // var weekData: Array<any> = [];
  // weekData = weekData === undefined ? [] : weekData;
  // const hours = weekData[index].hours;
  // const allIn = hasHandleObjectIds.value.every(item => weekData.includes(item));
  const arr = weekData.value.filter(t => t.daynum == index + 1);
  const allIn = arr.every(item => item.status === true);
  if (allIn) {
    arr.forEach(e => {
      e.status = false;
      hasHandleObjectIds.value.splice(hasHandleObjectIds.value.findIndex(t => t == e.id), 1);
    });
  } else {
    arr.forEach(e => {
      e.status = true;
      hasHandleObjectIds.value.push(e.id);
    });
  }
}
// 点击时间点数
const clicktime = (index) => {
  const arr = weekData.value.filter(t => t.timenum == index + 1);
  const allIn = arr.every(item => item.status === true);
  if (allIn) {
    arr.forEach(e => {
      e.status = false;
      hasHandleObjectIds.value.splice(hasHandleObjectIds.value.findIndex(t => t == e.id), 1);
    });
  } else {
    arr.forEach(e => {
      e.status = true;
      hasHandleObjectIds.value.push(e.id);
    });
  }
}
/** 鼠标按下 */
const handleMouseDown = event => {
  //点下时清空已处理数组
  hasHandleObjectIds.value.length = 0;
  var id = Number(event.target.getAttribute(props.objectIdName))
  // var weekData: Array<any> = [];
  // weekData = weekData === undefined ? [] : weekData;
  // const hourItem = weekData.flatMap(day => day.hours).find(item => item.id === id);
  const index = weekData.value.findIndex(t => t.id == id);
  if (index > -1) {
    weekData.value[index].status = !weekData.value[index].status;
  }
  if (!hasHandleObjectIds.value.includes(id)) {
    hasHandleObjectIds.value.push(id);
  } else {
    hasHandleObjectIds.value.splice(hasHandleObjectIds.value.findIndex(t => t == id), 1);
  }
  // 展示矩形框,通过坐标位置来画出矩形
  maskPosition.value.show = true;
  maskPosition.value.startX = event.clientX;
  maskPosition.value.startY = event.clientY;
  maskPosition.value.endX = event.clientX;
  maskPosition.value.endY = event.clientY;
  // 监听鼠标移动事件和抬起离开事件
  objectsRef.value.addEventListener("mousemove", handleMouseMove);
  objectsRef.value.addEventListener("mouseup", handleMouseUp);
};

/** 鼠标移动 */
const handleMouseMove = event => {
  if (clientX.value !== event.clientX || clientY.value !== event.clientY) {
    clientX.value = event.clientX
    clientY.value = event.clientY
    maskPosition.value.endX = event.clientX;
    maskPosition.value.endY = event.clientY;
    // var weekData: Array<any> = [];
    // weekData = props.weekData === undefined ? [] : props.weekData;
    const selectedObjects = objectsRef.value.querySelectorAll(`.${props.objectClassName}`);
    // 获取鼠标画出的矩形框位置
    const rectanglePosition = maskRef.value.getClientRects()[0];
    var rectangleSelObjects: Array<number> = [];//矩形框内的id数组
    selectedObjects.forEach(item => {
      const objectPosition = item.getClientRects()[0];
      // 这里获取的id的方式定义于父组件的objectIdName
      if (compareObjectPosition(objectPosition, rectanglePosition)) {
        let id = item.getAttribute(props.objectIdName);
        rectangleSelObjects.push(Number(id));
      }
    });
    let handle = (id: number) => {
      const index = weekData.value.findIndex(t => t.id == id);
      if (index > -1) weekData.value[index].status = !weekData.value[index].status;
      // let index = tempSelectObjectIds.findIndex(t => t == id);
    }
    // rectangleSelObjects.forEach(id => {
    //   if (!hasHandleObjectIds.value.includes(id)) {
    //     handle(id);
    //     hasHandleObjectIds.value.push(id);
    //   }
    // })
    // hasHandleObjectIds.value.forEach(id => {
    //   console.log(rectangleSelObjects);
    //   if (!rectangleSelObjects.includes(id)) {
    //     console.log(id);
    //     handle(id);
    //     hasHandleObjectIds.value.splice(hasHandleObjectIds.value.findIndex(t => t == id), 1);
    //   }
    // })
    // 处理存在于 hasHandleObjectIds 中但不在 rectangleSelObjects 中的元素
    for (let i = hasHandleObjectIds.value.length - 1; i >= 0; i--) {
      const id = hasHandleObjectIds.value[i];
      if (!rectangleSelObjects.includes(id)) {
        handle(id);
        hasHandleObjectIds.value.splice(i, 1);
      }
    }

    // 处理存在于 rectangleSelObjects 中但不在 hasHandleObjectIds 中的元素
    for (const id of rectangleSelObjects) {
      if (!hasHandleObjectIds.value.includes(id)) {
        handle(id);
        hasHandleObjectIds.value.push(id);
      }
    }
    // emits("update:weekData", weekData);
    // emits("update:selectObjectIds", tempSelectObjectIds);
  }
};


/** 鼠标抬起离开 */
const handleMouseUp = () => {
  // 移除鼠标监听事件
  objectsRef.value.removeEventListener("mousemove", handleMouseMove);
  objectsRef.value.removeEventListener("mouseup", handleMouseUp);
  maskPosition.value.show = false;
  handleResetMaskPosition();
};


/**
 * 判断对象坐标是否在鼠标画出的矩形框坐标位置内
 * @param objectPosition 对象坐标位置
 * @param rectanglePosition 鼠标画出的矩形框坐标位置
 */
const compareObjectPosition = (objectPosition, rectanglePosition) => {
  const maxX = Math.max(
    objectPosition.x + objectPosition.width,
    rectanglePosition.x + rectanglePosition.width
  );
  const maxY = Math.max(
    objectPosition.y + objectPosition.height,
    rectanglePosition.y + rectanglePosition.height
  );
  const minX = Math.min(objectPosition.x, rectanglePosition.x);
  const minY = Math.min(objectPosition.y, rectanglePosition.y);
  return (
    maxX - minX <= objectPosition.width + rectanglePosition.width &&
    maxY - minY <= objectPosition.height + rectanglePosition.height
  );
};

/** 重置鼠标位置 */
const handleResetMaskPosition = () => {
  maskPosition.value.startX = 0;
  maskPosition.value.startY = 0;
  maskPosition.value.endX = 0;
  maskPosition.value.endY = 0;
};

/** 通过鼠标位置实时计算矩形框大小 */
const maskWidth = computed(() => {
  return `${Math.abs(maskPosition.value.endX - maskPosition.value.startX)}px;`;
});
const maskHeight = computed(() => {
  return `${Math.abs(maskPosition.value.endY - maskPosition.value.startY)}px;`;
});
const maskLeft = computed(() => {
  return `${Math.min(maskPosition.value.startX, maskPosition.value.endX)}px;`;
});
const maskTop = computed(() => {
  return `${Math.min(maskPosition.value.startY, maskPosition.value.endY)}px;`;
});
</script>

<style scoped lang="scss">
.time {
  width: 100%;
  display: flex;
  align-items: center;
  // border: #999 1px solid;

  .timeleft {
    width: 7.95%;
    height: 80px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #dad8d8;
    border-bottom: #999 1px solid;
    border-top: #999 1px solid;
    border-left: #999 1px solid;
    box-sizing: border-box;
  }

  .timeright {
    height: 80px;
    background: #dad8d8;
    width: 92%;
    border-right: #999 1px solid;
    box-sizing: border-box;
    border-top: #999 1px solid;

    .timeright_cell {
      border-bottom: #999 1px solid;
      box-sizing: border-box;

      .topitem {
        height: 38.6px;
        display: flex;
        align-items: center;
        justify-content: center;
        border-left: #999 1px solid;
        text-align: center;
        box-sizing: border-box;
      }

      .is_selected {
        background: var(--bgColor);
        color: #fff;
      }
    }
  }

  .timelefts {
    height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #dad8d8;
    border-bottom: #999 1px solid;
    border-left: #999 1px solid;
    border-right: #999 1px solid;
    box-sizing: border-box;
  }

  .is_selected {
    background: var(--bgColor);
    color: #fff;
  }

  .objects {
    height: 100%;
    width: 92%;
    // overflow-y: auto;

    .mask {
      position: fixed;
      background: #409eff;
      opacity: 0;
      z-index: 100;
    }

    .objects_content {
      user-select: none;
      display: flex;
      flex-wrap: wrap;

      >div {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 2.083%;
        height: 40px;
        box-sizing: border-box;
        border-bottom: #999 1px solid;
        border-right: #999 1px solid;
      }

      .is_selected {
        color: #fff;
        background-color: var(--bgColor);
      }
    }

  }
}
</style>

组件使用

<demo v-model:weekData="weekData"></demo>
import Demo from "./components/test/tests.vue";
// 全局计数器
let uniqueId = 1;
// 初始化 weekData
const weekData = ref([] as any);
// 初始化 weekData 中的 hours
onMounted(() => {
	for (let i = 0; i < 7; i++) {
		for (let hour = 0, timenum = 1; hour < 48; hour += 2) {
			weekData.value.push({ id: uniqueId++, status: false, daynum: i + 1, timenum: timenum });
			weekData.value.push({ id: uniqueId++, status: false, daynum: i + 1, timenum: timenum });
			timenum = (timenum % 24) + 1; // 使 timenum 在 1 到 24 之间循环
		}
	}
});

结束

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值