Browse Source

v-ellipse-tooltip.top

lifanagju_citu 4 months ago
parent
commit
0d0820bb6f

+ 2 - 0
components.d.ts

@@ -28,6 +28,7 @@ declare module 'vue' {
     CtSearch: typeof import('./src/components/CtSearch/index.vue')['default']
     CtTable: typeof import('./src/components/CtTable/index.vue')['default']
     CtTextField: typeof import('./src/components/CtVuetify/CtTextField/index.vue')['default']
+    CtTooltip: typeof import('./src/components/CtTooltip/index.vue')['default']
     DatePicker: typeof import('./src/components/DatePicker/index.vue')['default']
     Echarts: typeof import('./src/components/Echarts/index.vue')['default']
     ElCascader: typeof import('element-plus/es')['ElCascader']
@@ -67,6 +68,7 @@ declare module 'vue' {
     TextInput: typeof import('./src/components/FormUI/TextInput/index.vue')['default']
     TipDialog: typeof import('./src/components/CtDialog/tipDialog.vue')['default']
     ToolBar: typeof import('./src/components/PreviewImg/toolBar.vue')['default']
+    ToolTip: typeof import('./src/components/CtTooltip/ToolTip.vue')['default']
     VerificationCode: typeof import('./src/components/VerificationCode/index.vue')['default']
     Verifition: typeof import('./src/components/Verifition/index.vue')['default']
     VerifyPoints: typeof import('./src/components/Verifition/Verify/VerifyPoints.vue')['default']

+ 148 - 0
src/components/CtTooltip/ToolTip.vue

@@ -0,0 +1,148 @@
+<template>
+  <!-- 指示 -->
+  <transition name="el-fade-in-linear">
+    <div v-show="tooltipShow" :style="tooltipStyle" class="wq-tooltip"
+    >
+      <span class="wq-tooltip-text" v-text="text"></span>
+      <!-- 箭头 -->
+      <!-- <div :class="[
+        {'left':placements === 'left'},
+        {'bottom':placements==='bottom'},
+        {'right':placements==='right'},
+        {'top':placements==='top'}]" 
+        class="wq-tooltip-arrow"
+      >
+      </div> -->
+    </div>
+  </transition>
+</template>
+
+<script>
+import {ref, computed } from 'vue'
+
+export default {
+  setup() {
+    // 显示弹框
+    const tooltipShow = ref(false);
+
+    // 提示内容
+    const text = ref()
+
+    // 方向
+    const placements = ref('left')
+
+    // 显示
+    function showTip() {
+      tooltipShow.value = true
+    }
+    //设置提示内容
+    function setContent(content) {
+      text.value = content
+    }
+    //隐藏
+    function hiddenTip() {
+      tooltipShow.value = false
+    }
+
+    // 位置
+    const tooltipPosition = ref({
+      x: 0,
+      y: 0
+    })
+    const tooltipStyle = computed(() => {
+      return {
+        transform: `translate3d(${tooltipPosition.value.x}px,${tooltipPosition.value.y}px,0)`
+      }
+    })
+    return {
+      tooltipShow,
+      showTip,
+      hiddenTip,
+      setContent,
+      tooltipPosition,
+      tooltipStyle,
+      text,
+      placements,
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+// tooltip
+.wq-tooltip {
+  padding: 5px 16px;
+  font-size: .875rem;
+  line-height: 1.6;
+  min-width: 10px;
+  word-wrap: break-word;
+  position: fixed;
+  left: 0;
+  top: 0;
+  pointer-events: none;
+  background: rgb(66, 66, 66);
+  color: rgb(238, 238, 238);
+  border-radius: 4px;
+  font-size: .875rem;
+  line-height: 1.6;
+  display: block;
+  z-index: 2000;
+  opacity: 1;
+  width: auto;
+  transition-property: opacity,width;
+}
+
+// 小箭头
+.wq-tooltip-arrow {
+  position: absolute;
+  width: 0;
+  height: 0;
+  border-width: 8px;
+  border-style: solid;
+}
+
+// 小箭头如果在左侧
+.wq-tooltip-arrow.left {
+  border-color: transparent transparent transparent #303133;
+  right: -15px;
+  top: 50%;
+  transform: translate3d(0, -50%, 0);
+}
+
+// 小箭头如果在下侧
+.wq-tooltip-arrow.bottom {
+  top: -15px;
+  border-color: transparent transparent #303133 transparent;
+  left: 50%;
+  transform: translate3d(-50%, 0, 0);
+}
+
+// 小箭头如果在右侧
+.wq-tooltip-arrow.right {
+  left: -15px;
+  top: 50%;
+  transform: translate3d(0, -50%, 0);
+  border-color: transparent #303133 transparent transparent;
+}
+
+// 小箭头如果在上侧
+.wq-tooltip-arrow.top {
+  bottom: -15px;
+  border-color: #303133 transparent transparent transparent;
+  left: 50%;
+  transform: translate3d(-50%, 0, 0);
+}
+
+/* 动画 */
+.tooltip-enter-from,
+.tooltip-leave-to {
+  opacity: 0;
+  transition: opacity .3s ease;
+}
+
+.tooltip-leave-from,
+.tooltip-enter-to {
+  transition: opacity .1s ease;
+}
+</style>
+

+ 124 - 0
src/components/CtTooltip/index.js

@@ -0,0 +1,124 @@
+// 引入组件
+import {createApp, nextTick} from "vue";
+import MyToolTip from './ToolTip.vue'
+
+/**
+ * v-ellipse-tooltip.top
+*/
+
+// 位置定位
+function calculationLocation(el, target, placements) {
+  if (!el || !target) return;
+  el.tooltipPosition.y = 0;
+  el.tooltipPosition.x = 0;
+  let el_dom = el.$el.nextElementSibling.getBoundingClientRect()
+  let target_dom = target.getBoundingClientRect()
+
+  if (placements === "left") {
+    el.tooltipPosition.x = target_dom.x - el_dom.width - 10
+    el.tooltipPosition.y = target_dom.y - el_dom.height / 2 + target_dom.height / 2
+  } else if (placements === "bottom") {
+    el.tooltipPosition.x = target_dom.x + target_dom.width / 2 - el_dom.width / 2
+    el.tooltipPosition.y = target_dom.y + el_dom.height + 10
+  } else if (placements === "right") {
+    el.tooltipPosition.x = target_dom.x + target_dom.width + 10
+    el.tooltipPosition.y = target_dom.y - el_dom.height / 2 + target_dom.height / 2
+  } else if (placements === "top") {
+    el.tooltipPosition.x = target_dom.x + target_dom.width / 2 - el_dom.width / 2
+    el.tooltipPosition.y = target_dom.y - el_dom.height - 10
+  }
+}
+
+// 方向
+const allPlacements = ['left', 'bottom', 'right', 'top']
+
+
+function getElStyleAttr(element, attr) {
+  const styles = window.getComputedStyle(element)
+  return styles[attr]
+}
+
+// const getPosition = (target) => {
+//   const x = target.offsetLeft + target.offsetWidth/2
+//   const y = target.offsetTop + target.offsetHeight/2
+//   return { x, y}
+// }
+
+const isOverflow = (target) => {
+  const scrollWidth = target.scrollWidth
+  const offsetWidth = target.offsetWidth
+  // const range = document.createRange()
+  // range.setStart(target, 0)
+  // range.setEnd(target, target.childNodes.length)
+  // const rangeWidth = range.getBoundingClientRect().width
+  // const padding = (parseInt(getElStyleAttr(target, 'paddingLeft'), 10) || 0) + (parseInt(getElStyleAttr(target, 'paddingRight'), 10) || 0)
+  // return (rangeWidth + padding > target.offsetWidth) || scrollWidth > offsetWidth
+  return scrollWidth > offsetWidth
+}
+
+export const ellipsisTooltip = {
+  mounted(el, binding) {
+    //获取指令的参数
+    const {
+        value: {
+            placement, content, destroyOnLeave
+        } = {}
+    } = binding;
+    // 加上超出...样式
+    el.style.overflow = "hidden";
+    el.style.textOverflow = "ellipsis";
+    el.style.whiteSpace = "nowrap";
+    //鼠标移开时 清除元素
+    const onMouseLeave = () => {
+      if (el.w_tipInstance) {
+        el.w_tipInstance.hiddenTip()
+        el.w_tooltip.remove()
+        el.w_tipInstance = null
+        el.w_tooltip = null
+      }
+    };
+    const onMouseEnter = () => {
+      // 判断内容长度 需要展示
+      if (isOverflow(el)) {
+        // const positionXY = getPosition(el)
+        const directiveList = allPlacements.filter(placement => binding.modifiers[placement])
+        const placements = directiveList.length ? directiveList : allPlacements
+        if (!el.w_tooltip) {
+          // 创建tooltip实例
+          const vm = createApp(MyToolTip)
+          // 创建根元素
+          el.w_tooltip = document.createElement('div')
+          // 挂载到页面
+          document.body.appendChild(el.w_tooltip)
+          el.w_tooltip.id = `tooltip_${Math.floor(Math.random() * 10000)}`
+          el.w_tipInstance = vm.mount(el.w_tooltip)
+        }
+        // 设置 tooltip 显示方向
+        el.w_tipInstance.placements = placement || placements[0] || 'top'
+        // 设置显示内容
+        el.w_tipInstance.setContent(content || el.innerText)
+        // 使 tooltip 显示
+        el.w_tipInstance.showTip()
+        nextTick(() => {
+          // 计算 tooltip 在页面中的位置
+          calculationLocation(el.w_tipInstance, el, placements[0])
+        })
+        el._scrollHandler = () => {
+          // 重新定位位置
+          if (el.w_tipInstance && el.w_tipInstance.tooltipShow) calculationLocation(el.w_tipInstance, el, placements[0])
+        }
+        window.addEventListener('scroll', el._scrollHandler)
+        const _destroyOnLeave = destroyOnLeave || true
+        if (_destroyOnLeave) el.addEventListener("mouseleave", onMouseLeave);
+      }
+    };
+    el.addEventListener("mouseenter", onMouseEnter);
+  },
+  unmounted(el) {
+    if (el.w_tooltip) {
+      document.body.removeChild(el.w_tooltip)
+    }
+    window.removeEventListener('scroll', el._scrollHandler)
+  }
+}
+

+ 18 - 19
src/components/Enterprise/hotPromoted.vue

@@ -8,10 +8,11 @@
             <v-img :src="item?.enterprise.logoUrl || 'https://minio.citupro.com/dev/menduner/company-avatar.png'" alt="" width="77" height="77" style="border-radius: 4px;"/>
           </div>
           <div class="company-info cursor-pointer">
-            <h3 :ref="el => { if(el) companyNameRefs[index] = el }">
+            <h3 v-ellipse-tooltip.top>{{ formatName(item.enterprise.anotherName || item.enterprise.name) }}</h3>
+            <!-- <h3 :ref="el => { if(el) companyNameRefs[index] = el }">
               {{ formatName(item.enterprise.anotherName || item.enterprise.name) }}
               <v-tooltip v-if="isTextOverflow[index]" activator="parent" location="top">{{ formatName(item.enterprise.anotherName || item.enterprise.name) }}</v-tooltip>
-            </h3>
+            </h3> -->
             <p>
               {{ item?.enterprise.scaleName }}
               <span class="septal-line" v-if="item.enterprise.industryName"></span>
@@ -19,8 +20,7 @@
             </p>
           </div>
         </div>
-        <div class="px-5 py-1 ellipsis-tag" :style="{'height': '33px', 'border-bottom': item.enterprise.welfareList && item.enterprise.welfareList.length ? '1px solid #EBEBEB' : 'none'}">
-          <v-tooltip v-if="item.enterprise?.welfareList?.length" activator="parent" location="top">{{ item.enterprise.welfareList.toString() }}</v-tooltip>
+        <div v-ellipse-tooltip.top class="px-5 py-1 ellipsis-tag" :style="{'height': '33px', 'border-bottom': item.enterprise.welfareList && item.enterprise.welfareList.length ? '1px solid #EBEBEB' : 'none'}">
           <span class="welfareTag mr-5" v-for="(k, i) in item.enterprise.welfareList" :key="i">{{ k }}</span>
         </div>
         <!-- 职位列表 -->
@@ -28,8 +28,7 @@
           <li class="company-job-item" v-for="(k, i) in item.jobList" :key="i" :class="{'company-job-item-hover': k.active}" @mouseenter="k.active = true" @mouseleave="k.active = false" @click="handleClickPosition(k)">
             <div class="job-info" @mouseenter="k.active = true" @mouseleave="k.active = false" @click.stop="handleClickPosition(k)">
               <div class="mb-2 d-flex">
-                <v-tooltip v-if="isTextOverflow[index]" activator="parent" location="top">{{ formatName(k.name) }}</v-tooltip>
-                <p :class="['name', 'cursor-pointer', {'default-active': k.active }]" :style="{'max-width': !k.payFrom && !k.payTo ? '290px' : '200px'}">{{ formatName(k.name) }}</p>
+                <span v-ellipse-tooltip.top :class="['name', 'cursor-pointer', {'default-active': k.active }]" :style="{'max-width': !k.payFrom && !k.payTo ? '290px' : '200px'}">{{ formatName(k.name) }}</span>
                 <span v-if="!k.payFrom && !k.payTo" class="salary">面议</span>
                 <span v-else class="salary">{{ k.payFrom ? k.payFrom + '-' : '' }}{{ k.payTo }}{{ k.payName ? '/' + k.payName : '' }}</span>
               </div>
@@ -57,7 +56,7 @@
 </template>
 
 <script setup name="hotPromoted">
-import { ref, watch, nextTick } from 'vue'
+import { ref, watch } from 'vue'
 import { timesTampChange } from '@/utils/date'
 import { formatName } from '@/utils/getText'
 import { jumpToEnterpriseDetail } from '@/utils/position'
@@ -71,26 +70,26 @@ const props = defineProps({
 })
 
 const router = useRouter()
-const companyNameRefs = ref({})
-const isTextOverflow = ref({})
 
+// const isTextOverflow = ref({})
+// const companyNameRefs = ref({})
 // 检查文本是否溢出
-const checkTextOverflow = () => {
-  Object.entries(companyNameRefs.value).forEach(([index, element]) => {
-    if (element) {
-      isTextOverflow.value[index] = element.scrollWidth > element.clientWidth
-    }
-  })
-}
+// const checkTextOverflow = () => {
+//   Object.entries(companyNameRefs.value).forEach(([index, element]) => {
+//     if (element) {
+//       isTextOverflow.value[index] = element.scrollWidth > element.clientWidth
+//     }
+//   })
+// }
 
 const list = ref([])
 watch(
   () => props.items, 
   (newVal) => {
     list.value = newVal
-    nextTick(() => {
-      checkTextOverflow()
-    })
+    // nextTick(() => {
+    //   checkTextOverflow()
+    // })
   },
   { immediate: true },
   { deep: true }

+ 5 - 0
src/main.js

@@ -31,6 +31,8 @@ import VueLuckyCanvas from '@lucky-canvas/vue'
 
 import router from './router'
 
+import { ellipsisTooltip } from '@/components/CtTooltip/index.js'
+
 import './permission'
 
 const pinia = createPinia()
@@ -46,8 +48,11 @@ app.use(router)
 app.use(Clipboard)
 app.use(VueLuckyCanvas)
 
+
 // app.config.globalProperties.$echarts = echarts
 registerPlugins(app)
 app.component('VueDatePicker', VueDatePicker)
 
+app.directive('ellipse-tooltip', ellipsisTooltip)
+
 app.mount('#app')

+ 7 - 0
src/views/recruit/personal/recommend/components/positionList.vue

@@ -12,6 +12,7 @@
           <div class="sub-li-top">
             <div class="sub-li-info">
               <p :class="['name', {'default-active': item.active }]">{{ formatName(item.name) }}</p>
+              <v-tooltip activator="parent" location="top">{{ formatName(item.name) }}</v-tooltip>
               <svg-icon v-if="item.hire" name="pin" size="30"></svg-icon>
             </div>
           </div>
@@ -40,6 +41,11 @@
               <v-avatar size="35">
                 <v-img :src="item.logoUrl || 'https://minio.citupro.com/dev/menduner/company-avatar.png'" />
               </v-avatar>
+              <v-tooltip activator="parent" location="top">
+                {{ formatName(item.anotherName || item.name) }}
+                {{ item?.industryName ? spaces(2) + item.industryName : '' }}
+                {{ item?.scaleName ? spaces(2) + item.scaleName : '' }}
+              </v-tooltip>
               <span class="names ml-2 font-size-14 ellipsis" style="max-width: 88%;">
                 {{ formatName(item.anotherName || item.name) }}
                 <span class="color-999 font-size-13 ml-3">
@@ -60,6 +66,7 @@
 defineOptions({ name: 'position-card-item' })
 import { ref, watch } from 'vue'
 import { formatName } from '@/utils/getText'
+import { spaces } from '@/utils/index.js'
 
 const emit = defineEmits([''])
 const props = defineProps({