Przeglądaj źródła

feat: 解决Tracking详情页Container Status部分样式bug

Jack Zhou 1 dzień temu
rodzic
commit
db7209ff7a

+ 7 - 2
src/components/ContainerStatus/src/ContainerStatus.vue

@@ -57,7 +57,12 @@ watch(
             <div class="step-dot-icon"></div>
             <div class="info">
               <div class="left-info">
-                <div class="title">{{ item.title }}</div>
+                <div class="title">
+                  <VEllipsisTooltip :max-width="220" :max-height="20" :line-clamp="1">{{
+                    item.title
+                  }}</VEllipsisTooltip>
+                </div>
+
                 <div class="date">{{ formatTimezone(item.date, item.timezone) }}</div>
               </div>
               <div class="right-country">{{ item.country }}</div>
@@ -157,7 +162,7 @@ watch(
     .info {
       flex: 1;
       display: flex;
-      height: 52px;
+      min-height: 52px;
       margin-top: -22px;
       background-color: var(--color-container-status-node-bg);
       border: 1px solid var(--color-border);

+ 71 - 37
src/components/VEllipsisTooltip/src/VEllipsisTooltip.vue

@@ -5,6 +5,7 @@
     :popper-class="popperClass"
     :placement="placement"
     v-bind="tooltipProps"
+    :offset="20"
     ref="tooltipRef"
   >
     <div
@@ -12,20 +13,20 @@
       class="ellipsis-container"
       :class="{ 'is-clamp-multi': lineClamp > 1 }"
       :style="finalContainerStyle"
-      @mouseenter="handleMouseEnter"
+      @mouseenter.capture="handleMouseEnter"
       @mouseleave="handleMouseLeave"
     >
-      <slot>
-        <span ref="textRef" class="ellipsis-text" :style="textStyle">
-          {{ content }}
-        </span>
-      </slot>
+      <div ref="textRef" class="ellipsis-text" :style="finalTextStyle">
+        <slot>
+          <span>{{ content }}</span>
+        </slot>
+      </div>
     </div>
   </el-tooltip>
 </template>
 
 <script setup>
-import { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue'
+import { ref, computed, onMounted, onUnmounted, onUpdated, watch, nextTick } from 'vue'
 
 const props = defineProps({
   content: String,
@@ -71,13 +72,11 @@ const tooltipRef = ref(null)
 // --- 是否应显示 tooltip ---
 const shouldShowTooltip = ref(false)
 
-// --- 动态计算样式类 ---
-const containerClasses = computed(() => ({
-  'is-clamp-multi': props.lineClamp > 1
-}))
+// --- 插槽文案(无 content prop 时从 DOM 读取,供 tooltip 使用)---
+const slotTooltipText = ref('')
 
 // --- 实际用于 Tooltip 显示的内容 ---
-const tooltipContent = computed(() => props.content || '')
+const tooltipContent = computed(() => props.content || slotTooltipText.value)
 
 // --- 容器样式(max-width / max-height)---
 const finalContainerStyle = computed(() => ({
@@ -114,31 +113,51 @@ const finalTextStyle = computed(() => {
   }
 })
 
-// --- 检查是否溢出 ---
-const checkOverflow = async () => {
-  await nextTick() // 确保 DOM 更新
-  const container = containerRef.value
-  const textEl = textRef.value || container?.querySelector('.ellipsis-text')
+// --- 实际发生省略的测量节点:插槽包一层 div/span 时,溢出体现在子节点 scrollWidth 上 ---
+const getMeasureEl = (textEl) => {
+  if (!textEl) return null
+  const { children } = textEl
+  if (children.length === 1) {
+    return children[0]
+  }
+  return textEl
+}
 
-  if (!container || !textEl) return
+// --- 同步测量(mouseenter 必须用同步,否则 el-tooltip 先看到 disabled=true 不会弹出)---
+const measureOverflow = () => {
+  const textEl = textRef.value
 
-  let isOverflowing = false
+  if (!textEl) return
+
+  slotTooltipText.value = (textEl.textContent || '').trim()
+
+  const measureEl = getMeasureEl(textEl)
+  if (!measureEl) return
 
   if (props.lineClamp <= 1) {
-    isOverflowing = textEl.scrollWidth > container.clientWidth
+    // 与 clientWidth 比较;子像素取整可能导致相等,故留 1px 容差
+    shouldShowTooltip.value = measureEl.scrollWidth > measureEl.clientWidth + 1
   } else {
-    // 多行看高度是否溢出(line-clamp 截断)
-    isOverflowing = textEl.scrollHeight > container.clientHeight
+    shouldShowTooltip.value = measureEl.scrollHeight > measureEl.clientHeight + 1
   }
+}
 
-  shouldShowTooltip.value = isOverflowing
+// --- 检查是否溢出(DOM 更新后)---
+const checkOverflow = async () => {
+  await nextTick()
+  measureOverflow()
 }
 
-// --- 鼠标事件用于手动触发检查(防抖)---
+// --- 捕获阶段先更新 shouldShowTooltip,再交给 el-tooltip 处理悬停 ---
 let resizeTimer
 const handleMouseEnter = () => {
   clearTimeout(resizeTimer)
-  resizeTimer = setTimeout(checkOverflow, 50)
+  measureOverflow()
+  resizeTimer = setTimeout(measureOverflow, 50)
+}
+
+const handleMouseLeave = () => {
+  clearTimeout(resizeTimer)
 }
 
 // 可选:也可监听 resize
@@ -147,6 +166,11 @@ onMounted(() => {
   checkOverflow()
 })
 
+// 插槽内容变化(如 v-for 文案)不会触发 props watch,需在更新后重算溢出
+onUpdated(() => {
+  nextTick(checkOverflow)
+})
+
 onUnmounted(() => {
   window.removeEventListener('resize', checkOverflow)
   clearTimeout(resizeTimer)
@@ -165,30 +189,35 @@ watch(
 <style lang="scss" scoped>
 .ellipsis-container {
   display: inline-block;
+  box-sizing: border-box;
+  width: 100%;
   max-width: v-bind('finalContainerStyle.maxWidth');
   max-height: v-bind('finalContainerStyle.maxHeight');
+  min-width: 0;
   overflow: hidden;
   vertical-align: top;
-  line-height: 32px;
+  /* 不固定行高:否则在继承较小字号时行盒约 13px,会裁掉加粗/降部笔画 */
+  line-height: normal;
+  font: inherit;
 }
 
 .ellipsis-text {
-  width: 100%; // 利用父容器宽度
-}
-.ellipsis-container {
-  display: inline-block;
-  max-width: v-bind('finalContainerStyle.maxWidth');
-  max-height: v-bind('finalContainerStyle.maxHeight');
+  width: 100%;
+  min-width: 0;
+  display: block;
   overflow: hidden;
-  line-height: 1.5;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  line-height: inherit;
 }
 
-.ellipsis-text {
-  width: 100%;
-  display: block;
+/* 插槽根节点常为 div(如 .title),省略与 nowrap 需作用在真正承载文字的节点上 */
+.ellipsis-container:not(.is-clamp-multi) .ellipsis-text > :deep(*) {
   overflow: hidden;
   text-overflow: ellipsis;
   white-space: nowrap;
+  min-width: 0;
+  max-width: 100%;
 }
 
 .ellipsis-container.is-clamp-multi .ellipsis-text {
@@ -199,12 +228,17 @@ watch(
   white-space: normal;
   word-break: break-all;
 }
+
+.ellipsis-container.is-clamp-multi .ellipsis-text > :deep(*) {
+  white-space: normal;
+  word-break: break-all;
+}
 </style>
 
 <!-- 全局样式建议提取到全局 SCSS 文件 -->
 <style>
 .ellipsis-tooltip {
-  max-width: 200px;
+  max-width: 260px;
   word-break: break-word;
   white-space: pre-line;
   margin-bottom: -15px;