vue3 手写dropdown

<template>
  <div class="drp_component" :class="classname">
    <p @click="openDrp" class="ws-n" ref="drpDef" title="">
      <slot></slot>
    </p>
  </div>
  <transition :name="openType">
    <div
      :style="{ top: top + 'px', left: left + 'px' }"
      class="drp_item_wrap"
      ref="drpItemWrap"
      v-if="show"
      @click="show = false"
      title=""
    >
      <slot name="dropdown"></slot>
      <span
        class="arrow"
        :class="{
          arror_top: arrowPosition === 'top',
          arror_bottom: arrowPosition === 'bottom',
        }"
        :style="{ left: arrowLeft + 'px' }"
      ></span>
    </div>
  </transition>
</template>

<script>
export default {
  name: "HelloWorld",
};
</script>
<script setup>
import { nextTick, ref, watch } from "vue";
import $ from "jquery";

const openType = ref("");
const classname = ref(Math.random().toString(36).substr(2));
const show = ref(false);
const top = ref(9999999);
const left = ref(9999999);
const arrowLeft = ref(0);
const arrowPosition = ref("");
const openDrp = () => {
  show.value = !show.value;
  if (!show.value) return;
  nextTick(() => {
    const body = document.querySelector("body");
    if (body.append) {
      body.append(drpItemWrap.value);
    } else {
      body.appendChild(drpItemWrap.value);
    }
    let dcWidth = document.body.clientWidth;
    let dcHeight = document.body.clientHeight;
    let pLeft =
      $(`.${classname.value}`).offset().left - document.documentElement.scrollLeft;
    let pTop = $(`.${classname.value}`).offset().top - document.documentElement.scrollTop;
    let pWidth = $(`.${classname.value}`).outerWidth();
    let pHeight = $(`.${classname.value}`).outerHeight();
    let itemWidth = $(".drp_item_wrap").outerWidth();
    let itemHeight = $(".drp_item_wrap").outerHeight();
    if (pTop + pHeight + itemHeight - dcHeight > 0) {
      top.value = pTop - itemHeight - 10;
      arrowPosition.value = "bottom";
      openType.value = "el-zoom-in-bottom";
    } else {
      top.value = pTop + pHeight + 10;
      arrowPosition.value = "top";
      openType.value = "el-zoom-in-top";
    }
    if (pLeft + pWidth / 2 - itemWidth / 2 < 0) {
      left.value = 10;
      arrowLeft.value = pLeft - 10 + pWidth / 2 - 7;
    } else if (pLeft + pWidth / 2 + itemWidth / 2 > dcWidth) {
      left.value = dcWidth - itemWidth - 10;
      arrowLeft.value = pLeft - left.value + pWidth / 2 - 7;
    } else {
      left.value = pLeft + pWidth / 2 - itemWidth / 2;
      arrowLeft.value = itemWidth / 2 - 7;
    }
    show.value = !show.value;
    setTimeout(() => {
      show.value = !show.value;
    });
  });
};
const colse = () => {
  if (show.value) show.value = false;
};
const drpDef = ref(null);
const drpItemWrap = ref(null);
const cm = (e) => {
  if (
    drpItemWrap.value &&
    !drpItemWrap.value.contains(e.target) &&
    drpDef.value &&
    !drpDef.value.contains(e.target)
  ) {
    colse();
  }
};
watch(show, (v) => {
  if (v) {
    window.addEventListener("scroll", colse);
    window.addEventListener("wheel", colse);
    window.addEventListener("mousedown", cm);
  } else {
    window.removeEventListener("scroll", colse);
    window.removeEventListener("wheel", colse);
    window.removeEventListener("mousedown", cm);
  }
});
</script>

<style lang="less">
.drp_component {
  line-height: 1;
}
.drp_item_wrap {
  line-height: 1;
  background-color: #fff;
  padding: 5px 0 5px;
  border: 1px solid var(--el-border-color-light);
  box-shadow: var(--el-box-shadow-light);
  position: fixed;
  z-index: 9999999;
  border-radius: 4px;
  > div {
    padding: 10px 16px;
    cursor: pointer;
    user-select: none;
    font-size: 14px;
    text-align: left;
    white-space: nowrap;
  }
  > div:hover {
    background-color: var(--el-color-primary-light-9);
    color: var(--el-color-primary);
  }
  > button {
    border: none;
    outline: none;
    padding: 6px 16px;
    cursor: pointer;
    user-select: none;
    font-size: 14px;
    text-align: left;
    white-space: nowrap;
    background-color: #fff;
    display: block;
    width: 100%;
  }
  > button:hover {
    background-color: var(--el-color-primary-light-9);
    color: var(--el-color-primary);
  }
  > button[disabled] {
    background-color: #fff;
    cursor: not-allowed;
    color: #8F9BB3;
  }
  .arrow {
    position: absolute;
    width: 10px;
    height: 10px;
    border: 1px solid var(--el-border-color-light);
    z-index: -1;
    transform: rotate(45deg);
    background-color: #fff;
  }
  .arror_top {
    top: -5px;
    border-bottom-color: transparent;
    border-right-color: transparent;
  }
  .arror_bottom {
    bottom: -5px;
    border-top-color: transparent;
    border-left-color: transparent;
  }
}
</style>

  

posted @ 2023-02-07 09:37  愚公不移山  阅读(132)  评论(0)    收藏  举报