fix: 用户商品模块
Build and Deploy Vue3 / build (push) Successful in 1m34s
Build and Deploy Vue3 / deploy (push) Successful in 1m10s

This commit is contained in:
2026-04-16 15:43:08 +08:00
parent 985412c3bc
commit c7245cec67
3 changed files with 196 additions and 61 deletions
+104 -4
View File
@@ -1,6 +1,9 @@
<template>
<div class="tags-view-container">
<div class="tags-view-wrapper">
<div class="tags-view-wrapper" ref="scrollWrapperRef"
@wheel.prevent="handleWheel"
@mouseenter="hovered = true" @mouseleave="hovered = false"
@scroll="updateScrollbar">
<div class="tags-view-scroll">
<router-link
v-for="tag in visitedViews"
@@ -22,6 +25,9 @@
</el-icon>
</router-link>
</div>
<div class="scroll-track" :class="{ visible: hovered && hasOverflow }">
<div class="scroll-thumb" :style="thumbStyle" @mousedown="onThumbDown"></div>
</div>
</div>
<!-- 右键菜单 -->
@@ -181,6 +187,65 @@ const openMenu = (e, tag) => {
selectedTag.value = tag
}
// 横向滚轮 + 自定义滚动条
const scrollWrapperRef = ref(null)
const hovered = ref(false)
const hasOverflow = ref(false)
const thumbStyle = ref({ width: '0px', left: '0px' })
const handleWheel = (e) => {
if (scrollWrapperRef.value) {
scrollWrapperRef.value.scrollLeft += e.deltaY || e.deltaX
}
}
const getTrackWidth = () => {
const el = scrollWrapperRef.value
if (!el) return 0
return el.clientWidth - 24
}
const updateScrollbar = () => {
const el = scrollWrapperRef.value
if (!el) return
hasOverflow.value = el.scrollWidth > el.clientWidth
if (!hasOverflow.value) return
const tw = getTrackWidth()
const ratio = el.clientWidth / el.scrollWidth
const thumbW = Math.max(ratio * tw, 30)
const maxScroll = el.scrollWidth - el.clientWidth
const scrollRatio = maxScroll > 0 ? el.scrollLeft / maxScroll : 0
const thumbLeft = scrollRatio * (tw - thumbW)
thumbStyle.value = { width: thumbW + 'px', left: thumbLeft + 'px' }
}
const onThumbDown = (e) => {
e.preventDefault()
const el = scrollWrapperRef.value
if (!el) return
const startX = e.clientX
const startScroll = el.scrollLeft
const maxScroll = el.scrollWidth - el.clientWidth
const tw = getTrackWidth()
const ratio = el.clientWidth / el.scrollWidth
const thumbW = Math.max(ratio * tw, 30)
const movable = tw - thumbW
const onMove = (ev) => {
const dx = ev.clientX - startX
const scrollDelta = movable > 0 ? (dx / movable) * maxScroll : 0
el.scrollLeft = Math.min(Math.max(startScroll + scrollDelta, 0), maxScroll)
}
const onUp = () => {
document.removeEventListener('mousemove', onMove)
document.removeEventListener('mouseup', onUp)
}
document.addEventListener('mousemove', onMove)
document.addEventListener('mouseup', onUp)
}
watch(visitedViews, () => nextTick(updateScrollbar), { deep: true })
// 关闭右键菜单
const closeMenu = () => {
visible.value = false
@@ -201,10 +266,13 @@ const handleClickOutside = () => {
onMounted(() => {
initTags()
document.addEventListener('click', handleClickOutside)
nextTick(updateScrollbar)
window.addEventListener('resize', updateScrollbar)
})
onBeforeUnmount(() => {
document.removeEventListener('click', handleClickOutside)
window.removeEventListener('resize', updateScrollbar)
})
</script>
@@ -220,12 +288,13 @@ onBeforeUnmount(() => {
.tags-view-wrapper {
height: 100%;
width: 100%;
display: flex;
align-items: center;
padding: 0 12px;
overflow-x: auto;
overflow-x: scroll;
overflow-y: hidden;
white-space: nowrap;
position: relative;
scrollbar-width: none;
-ms-overflow-style: none;
}
.tags-view-wrapper::-webkit-scrollbar {
@@ -239,6 +308,37 @@ onBeforeUnmount(() => {
gap: 4px;
}
.scroll-track {
position: absolute;
left: 12px;
right: 12px;
bottom: 1px;
height: 3px;
opacity: 0;
transition: opacity 0.25s;
pointer-events: none;
z-index: 5;
}
.scroll-track.visible {
opacity: 1;
pointer-events: auto;
}
.scroll-thumb {
position: absolute;
top: 0;
height: 100%;
border-radius: 3px;
background: rgba(180,188,199,0.45);
transition: background 0.15s;
cursor: pointer;
}
.scroll-thumb:hover {
background: rgba(180,188,199,0.65);
}
.tag, .active-tag {
height: 32px;
display: inline-flex;