初始提交:企业级日报系统完整代码
功能特性: - JWT用户认证系统 - 日报CRUD管理 - 三级权限控制 - 多维度搜索过滤 - 统计分析功能 - 评论互动系统 - 响应式Cool Admin界面 - 暗色主题支持 技术栈: - 后端:Django 4.2.7 + DRF + SimpleJWT - 前端:Vue 3 + Element Plus + Pinia - 数据库:SQLite/PostgreSQL - 部署:Docker + Nginx 包含内容: - 完整的后端API代码 - 现代化前端界面 - 数据库迁移文件 - 部署脚本和文档 - 演示页面和测试工具
This commit is contained in:
108
frontend/src/layout/components/ScrollPane.vue
Normal file
108
frontend/src/layout/components/ScrollPane.vue
Normal file
@@ -0,0 +1,108 @@
|
||||
<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>
|
Reference in New Issue
Block a user