
功能特性: - JWT用户认证系统 - 日报CRUD管理 - 三级权限控制 - 多维度搜索过滤 - 统计分析功能 - 评论互动系统 - 响应式Cool Admin界面 - 暗色主题支持 技术栈: - 后端:Django 4.2.7 + DRF + SimpleJWT - 前端:Vue 3 + Element Plus + Pinia - 数据库:SQLite/PostgreSQL - 部署:Docker + Nginx 包含内容: - 完整的后端API代码 - 现代化前端界面 - 数据库迁移文件 - 部署脚本和文档 - 演示页面和测试工具
109 lines
2.5 KiB
Vue
109 lines
2.5 KiB
Vue
<template>
|
|
<div
|
|
ref="scrollContainer"
|
|
class="scroll-container"
|
|
@wheel.prevent="handleScroll"
|
|
>
|
|
<div ref="scrollWrapper" class="scroll-wrapper">
|
|
<slot />
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
|
|
|
const emit = defineEmits(['scroll'])
|
|
|
|
const scrollContainer = ref()
|
|
const scrollWrapper = ref()
|
|
|
|
let scrollLeft = 0
|
|
let scrollTop = 0
|
|
|
|
const handleScroll = (e) => {
|
|
const eventDelta = e.wheelDelta || -e.deltaY * 40
|
|
const $container = scrollContainer.value
|
|
const $containerWidth = $container.offsetWidth
|
|
const $wrapper = scrollWrapper.value
|
|
const $wrapperWidth = $wrapper.offsetWidth
|
|
|
|
if (eventDelta > 0) {
|
|
scrollLeft = Math.max(0, scrollLeft - 50)
|
|
} else {
|
|
if ($containerWidth - 50 < $wrapperWidth) {
|
|
if (scrollLeft < $wrapperWidth - $containerWidth + 50) {
|
|
scrollLeft += 50
|
|
}
|
|
}
|
|
}
|
|
|
|
$wrapper.style.left = scrollLeft * -1 + 'px'
|
|
emit('scroll')
|
|
}
|
|
|
|
const moveToTarget = (currentTag) => {
|
|
const $container = scrollContainer.value
|
|
const $containerWidth = $container.offsetWidth
|
|
const $wrapper = scrollWrapper.value
|
|
|
|
if (!currentTag) {
|
|
return
|
|
}
|
|
|
|
const tagList = $wrapper.querySelectorAll('.tags-view-item')
|
|
|
|
let firstTag = null
|
|
let lastTag = null
|
|
|
|
if (tagList.length > 0) {
|
|
firstTag = tagList[0]
|
|
lastTag = tagList[tagList.length - 1]
|
|
}
|
|
|
|
if (firstTag === currentTag) {
|
|
scrollLeft = 0
|
|
} else if (lastTag === currentTag) {
|
|
scrollLeft = $wrapper.offsetWidth - $containerWidth
|
|
} else {
|
|
const tagListArray = [...tagList]
|
|
const currentIndex = tagListArray.findIndex(item => item === currentTag)
|
|
const prevTag = tagListArray[currentIndex - 1]
|
|
const nextTag = tagListArray[currentIndex + 1]
|
|
|
|
const afterNextTagOffsetLeft = nextTag ? nextTag.offsetLeft + nextTag.offsetWidth + 4 : 0
|
|
|
|
const beforePrevTagOffsetLeft = prevTag ? prevTag.offsetLeft - 4 : 0
|
|
|
|
if (afterNextTagOffsetLeft > scrollLeft + $containerWidth) {
|
|
scrollLeft = afterNextTagOffsetLeft - $containerWidth
|
|
} else if (beforePrevTagOffsetLeft < scrollLeft) {
|
|
scrollLeft = beforePrevTagOffsetLeft
|
|
}
|
|
}
|
|
|
|
$wrapper.style.left = scrollLeft * -1 + 'px'
|
|
}
|
|
|
|
// 暴露方法
|
|
defineExpose({
|
|
moveToTarget
|
|
})
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.scroll-container {
|
|
white-space: nowrap;
|
|
position: relative;
|
|
overflow: hidden;
|
|
width: 100%;
|
|
|
|
.scroll-wrapper {
|
|
position: absolute;
|
|
top: 0px;
|
|
height: 100%;
|
|
transition: left 0.3s ease-in-out;
|
|
}
|
|
}
|
|
</style>
|