fix: 调整名字的宽度
parent
ba2504de4a
commit
955c92cff3
|
|
@ -19,7 +19,11 @@
|
|||
<span class="text-[2.75rem] font-600" v-if="index > 2">{{ index + 1 }}</span>
|
||||
</template>
|
||||
<template #name="{ data }">
|
||||
<span class="text-[#C0EEFF] text-[2.75rem]">{{ data.name }}</span>
|
||||
<Tooltip :text="data.name" position="bottom">
|
||||
<span class="block max-w-[9rem] overflow-hidden text-ellipsis whitespace-nowrap text-[#C0EEFF] text-[2.75rem]">
|
||||
{{ data.name }}
|
||||
</span>
|
||||
</Tooltip>
|
||||
</template>
|
||||
<template #total="{data}">
|
||||
<span class="text-[#C0EEFF] text-[2.75rem]">{{ data.total }}</span>
|
||||
|
|
@ -47,7 +51,11 @@
|
|||
<span class="text-[2.75rem] font-600" v-if="index > 2">{{ index + 1 }}</span>
|
||||
</template>
|
||||
<template #name="{ data }">
|
||||
<span class="text-[#C0EEFF] text-[2.75rem]">{{ data.name }}</span>
|
||||
<Tooltip :text="data.name" position="bottom">
|
||||
<span class="block max-w-full overflow-hidden text-ellipsis whitespace-nowrap text-[#C0EEFF] text-[2.75rem]">
|
||||
{{ data.name }}
|
||||
</span>
|
||||
</Tooltip>
|
||||
</template>
|
||||
<template #total="{data}">
|
||||
<span class="text-[#C0EEFF] text-[2.75rem]">{{ data.total }}</span>
|
||||
|
|
@ -60,6 +68,7 @@
|
|||
import SvgComponent from "@/components/SvgComponent.vue";
|
||||
import RankingTable from "@/components/table/RankingTable.vue";
|
||||
import MobileDrawer from "./MobileDrawer.vue";
|
||||
import Tooltip from "./Tooltip.vue";
|
||||
|
||||
const columns = [
|
||||
{ field: "rank", header: "名次", align: "justify-center", width: "6rem" },
|
||||
|
|
|
|||
|
|
@ -0,0 +1,159 @@
|
|||
<template>
|
||||
<span
|
||||
ref="triggerRef"
|
||||
class="inline-flex max-w-full min-w-0 align-middle outline-none"
|
||||
tabindex="0"
|
||||
@mouseenter="showTip"
|
||||
@mouseleave="hideTip"
|
||||
@focus="showTip"
|
||||
@blur="hideTip"
|
||||
>
|
||||
<slot></slot>
|
||||
</span>
|
||||
|
||||
<Teleport to="body">
|
||||
<Transition
|
||||
enter-active-class="transition-all duration-150 ease-out"
|
||||
leave-active-class="transition-all duration-150 ease-in"
|
||||
enter-from-class="opacity-0 translate-y-[0.25rem]"
|
||||
leave-to-class="opacity-0 translate-y-[0.25rem]"
|
||||
>
|
||||
<div
|
||||
v-if="visible"
|
||||
ref="tipRef"
|
||||
class="fixed z-[9999] max-w-[min(38rem,calc(100vw-2rem))] px-[1.5rem] py-[1rem] text-[#D9F7FF] text-[2rem] leading-[1.25] break-all pointer-events-none border border-[rgba(132,232,255,0.55)] rounded-[0.75rem] bg-[linear-gradient(180deg,rgba(10,58,132,0.96)_0%,rgba(7,35,98,0.98)_100%)] shadow-[0_0_1.5rem_rgba(68,193,239,0.28),inset_0_0_1rem_rgba(68,193,239,0.14)]"
|
||||
:style="tipStyle"
|
||||
role="tooltip"
|
||||
>
|
||||
<span class="block">{{ text }}</span>
|
||||
<span
|
||||
class="absolute h-[0.85rem] w-[0.85rem] rotate-45 border-[rgba(132,232,255,0.55)] border-solid bg-[rgba(7,35,98,0.98)]"
|
||||
:class="arrowPlacementClass"
|
||||
></span>
|
||||
</div>
|
||||
</Transition>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, nextTick, onBeforeUnmount, reactive, ref } from "vue";
|
||||
|
||||
type TooltipPosition = "top" | "bottom" | "left" | "right";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
text: string;
|
||||
position?: TooltipPosition;
|
||||
delay?: number;
|
||||
}>(),
|
||||
{
|
||||
position: "top",
|
||||
delay: 160,
|
||||
},
|
||||
);
|
||||
|
||||
const triggerRef = ref<HTMLElement | null>(null);
|
||||
const tipRef = ref<HTMLElement | null>(null);
|
||||
const visible = ref(false);
|
||||
const isListening = ref(false);
|
||||
const timer = ref<number | null>(null);
|
||||
|
||||
const gap = 10;
|
||||
const viewportPadding = 8;
|
||||
const tipStyle = reactive({
|
||||
top: "0px",
|
||||
left: "0px",
|
||||
});
|
||||
|
||||
const arrowPlacementClass = computed(() => {
|
||||
const positions: Record<TooltipPosition, string> = {
|
||||
top: "bottom-[-0.48rem] left-1/2 translate-x-[-50%] border-r border-b",
|
||||
bottom: "top-[-0.48rem] left-1/2 translate-x-[-50%] border-t border-l",
|
||||
left: "right-[-0.48rem] top-1/2 translate-y-[-50%] border-t border-r",
|
||||
right: "left-[-0.48rem] top-1/2 translate-y-[-50%] border-b border-l",
|
||||
};
|
||||
|
||||
return positions[props.position];
|
||||
});
|
||||
|
||||
const clearTimer = () => {
|
||||
if (timer.value) {
|
||||
window.clearTimeout(timer.value);
|
||||
timer.value = null;
|
||||
}
|
||||
};
|
||||
|
||||
const clamp = (value: number, min: number, max: number) => Math.min(Math.max(value, min), max);
|
||||
|
||||
const addPositionListeners = () => {
|
||||
if (isListening.value) return;
|
||||
window.addEventListener("scroll", calculatePosition, true);
|
||||
window.addEventListener("resize", calculatePosition);
|
||||
isListening.value = true;
|
||||
};
|
||||
|
||||
const removePositionListeners = () => {
|
||||
if (!isListening.value) return;
|
||||
window.removeEventListener("scroll", calculatePosition, true);
|
||||
window.removeEventListener("resize", calculatePosition);
|
||||
isListening.value = false;
|
||||
};
|
||||
|
||||
const showTip = () => {
|
||||
if (!props.text) return;
|
||||
|
||||
if (visible.value) {
|
||||
calculatePosition();
|
||||
return;
|
||||
}
|
||||
|
||||
clearTimer();
|
||||
timer.value = window.setTimeout(async () => {
|
||||
timer.value = null;
|
||||
visible.value = true;
|
||||
await nextTick();
|
||||
calculatePosition();
|
||||
addPositionListeners();
|
||||
}, props.delay);
|
||||
};
|
||||
|
||||
const hideTip = () => {
|
||||
clearTimer();
|
||||
visible.value = false;
|
||||
removePositionListeners();
|
||||
};
|
||||
|
||||
const calculatePosition = () => {
|
||||
if (!triggerRef.value || !tipRef.value) return;
|
||||
|
||||
const triggerRect = triggerRef.value.getBoundingClientRect();
|
||||
const tipRect = tipRef.value.getBoundingClientRect();
|
||||
let top = triggerRect.top - tipRect.height - gap;
|
||||
let left = triggerRect.left + (triggerRect.width - tipRect.width) / 2;
|
||||
|
||||
switch (props.position) {
|
||||
case "bottom":
|
||||
top = triggerRect.bottom + gap;
|
||||
left = triggerRect.left + (triggerRect.width - tipRect.width) / 2;
|
||||
break;
|
||||
case "left":
|
||||
top = triggerRect.top + (triggerRect.height - tipRect.height) / 2;
|
||||
left = triggerRect.left - tipRect.width - gap;
|
||||
break;
|
||||
case "right":
|
||||
top = triggerRect.top + (triggerRect.height - tipRect.height) / 2;
|
||||
left = triggerRect.right + gap;
|
||||
break;
|
||||
case "top":
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
tipStyle.top = `${clamp(top, viewportPadding, window.innerHeight - tipRect.height - viewportPadding)}px`;
|
||||
tipStyle.left = `${clamp(left, viewportPadding, window.innerWidth - tipRect.width - viewportPadding)}px`;
|
||||
};
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
hideTip();
|
||||
});
|
||||
</script>
|
||||
Loading…
Reference in New Issue