<template>
|
<div class="mt--29 w--600">
|
<div>
|
<TitleBox> 社区组织架构 </TitleBox>
|
<div class="mt--20 h--26 flex a-center gap--10 oa">
|
<div
|
v-for="dept in departments"
|
:key="dept.id"
|
class="noActive shrink0"
|
:class="currentDeptId === dept.id && 'active'"
|
@click="switchDepartment(dept.id)"
|
>
|
{{ dept.deptName }}
|
</div>
|
</div>
|
<div
|
class="mt--10 flex a-center gap--10 oa"
|
style="min-height: 160px"
|
@wheel="handleWheel"
|
@mouseenter="handleMouseEnter"
|
@mouseleave="handleMouseLeave"
|
ref="userContainer"
|
>
|
<div
|
v-for="user in displayUsers"
|
:key="`${user.id}-${user._uniqueId || 0}`"
|
class="shrink0 flex flex-column justify-center a-center gap--15 w--126 pt--12 pb--14 personBg"
|
>
|
<img
|
class="w--64 h--64 br50"
|
:src="
|
user.avatar
|
? VUE_APP_IMAGE_BASE_URL + user.avatar
|
: '../../assets/img/logo.png'
|
"
|
alt=""
|
/>
|
<div class="color1">{{ user.userName }}</div>
|
<div class="color2">{{ user.userPosition }}</div>
|
</div>
|
<!-- 如果没有人员,显示加载提示 -->
|
<div
|
v-if="showEmptyState"
|
class="shrink0 flex flex-column justify-center a-center gap--15 w--126 pt--12 pb--14"
|
>
|
<div class="color1">暂无人员</div>
|
</div>
|
</div>
|
</div>
|
<div class="mt--30">
|
<TitleBox> 社区地图 </TitleBox>
|
<div class="relative">
|
<img
|
v-if="communityMapUrl"
|
:src="communityMapUrl"
|
class="w--600 absolute mt--24"
|
style="z-index: 2"
|
/>
|
<img
|
src="../../assets/img/mapBg.png"
|
class="w--600 absolute"
|
style="height: 357px; top: 122px; z-index: 1"
|
/>
|
</div>
|
</div>
|
</div>
|
</template>
|
|
<script>
|
import { getDeptList, getLargeUserList, getCommunityProfile } from "./service";
|
|
export default {
|
components: {},
|
props: {},
|
data() {
|
return {
|
userContainerRef: null,
|
departments: [],
|
currentDeptId: "all",
|
currentUsers: [],
|
displayUsers: [], // 用于显示的人员列表(包含复制数据)
|
communityMapUrl: "",
|
mapRefreshTimer: null,
|
scrollTimer: null,
|
scrollSpeed: 1,
|
isMouseOver: false,
|
VUE_APP_IMAGE_BASE_URL: process.env.VUE_APP_IMAGE_BASE_URL,
|
};
|
},
|
computed: {
|
hasUsers() {
|
return this.currentUsers && this.currentUsers.length > 0;
|
},
|
showEmptyState() {
|
return !this.hasUsers;
|
},
|
},
|
watch: {
|
// 监听部门列表变化
|
// departments: {
|
// handler(newDepts) {
|
// if (newDepts.length > 0 && !this.currentDeptId) {
|
// this.currentDeptId = 'all'; // 默认选择全部
|
// this.loadUsersForDept('all');
|
// } else if (newDepts.length > 0 && this.currentDeptId) {
|
// // 检查当前部门是否在列表中
|
// const currentDeptExists = newDepts.some(dept => dept.id === this.currentDeptId);
|
// if (!currentDeptExists && this.currentDeptId !== 'all') {
|
// // 如果当前部门不在新列表中,切换到全部
|
// this.currentDeptId = 'all';
|
// this.loadUsersForDept('all');
|
// }
|
// }
|
// },
|
// immediate: true
|
// },
|
// 监听当前部门ID变化
|
currentDeptId: {
|
handler(newDeptId) {
|
if (newDeptId) {
|
this.loadUsersForDept(newDeptId);
|
}
|
},
|
},
|
},
|
created() {
|
this.loadDepartments();
|
this.loadCommunityMap();
|
this.startMapRefresh();
|
this.loadUsersForDept("all");
|
},
|
mounted() {
|
// 组件挂载后直接显示所有人员
|
this.startAutoScroll();
|
},
|
beforeDestroy() {
|
this.cleanupScrollTimers();
|
},
|
methods: {
|
// 加载部门列表
|
async loadDepartments() {
|
try {
|
const response = await getDeptList();
|
if (response) {
|
// 添加"全部"分类
|
this.departments = [
|
{ id: "all", deptName: "全部" },
|
...(response || []),
|
];
|
}
|
} catch (error) {
|
console.error("加载部门列表失败:", error);
|
}
|
},
|
|
// 加载指定部门的人员列表
|
async loadUsersForDept(deptId) {
|
try {
|
let response;
|
if (deptId === "all") {
|
// 全部分类时传递空字符串
|
response = await getLargeUserList();
|
} else {
|
response = await getLargeUserList({ deptId });
|
}
|
|
if (response) {
|
// 处理不同的响应格式
|
this.currentUsers = response.data || response || [];
|
// 生成显示列表(包含复制数据以实现无缝滚动)
|
this.updateDisplayUsers();
|
// 重置滚动位置并开始自动滚动
|
this.resetScrollPosition();
|
}
|
} catch (error) {
|
console.error("加载部门人员列表失败:", error);
|
this.currentUsers = [];
|
}
|
},
|
|
// 处理滚轮事件
|
handleWheel(event) {
|
const container = event.currentTarget;
|
// 阻止默认的页面滚动
|
event.preventDefault();
|
// 水平滚动容器
|
container.scrollLeft += event.deltaY;
|
},
|
|
// 切换部门
|
switchDepartment(deptId) {
|
if (this.currentDeptId === deptId) {
|
return; // 如果已经是当前部门,不重复切换
|
}
|
this.currentDeptId = deptId;
|
|
// 先停止当前滚动
|
this.stopAutoScroll();
|
|
// 加载新部门的人员
|
this.loadUsersForDept(deptId);
|
},
|
|
// 重置滚动位置并重新开始自动滚动
|
resetScrollPosition() {
|
this.$nextTick(() => {
|
if (this.$refs.userContainer) {
|
this.$refs.userContainer.scrollLeft = 0;
|
// 延迟一下确保数据更新完成后再开始滚动
|
setTimeout(() => {
|
if (this.displayUsers.length > 0) {
|
this.startAutoScroll();
|
}
|
}, 100);
|
}
|
});
|
},
|
|
|
// 加载社区地图
|
async loadCommunityMap() {
|
try {
|
const response = await getCommunityProfile();
|
if (response && response.content) {
|
this.communityMapUrl = this.VUE_APP_IMAGE_BASE_URL + response.content;
|
}
|
} catch (error) {
|
console.error("加载社区地图失败:", error);
|
}
|
},
|
|
// 开始地图刷新
|
startMapRefresh() {
|
const refreshMap = () => {
|
this.loadCommunityMap();
|
// 设置1分钟(60000毫秒)后刷新
|
this.mapRefreshTimer = setTimeout(refreshMap, 60000);
|
};
|
|
// 立即执行一次,然后设置定时器
|
refreshMap();
|
},
|
|
// 更新显示的用户列表(复制数据实现无缝滚动)
|
updateDisplayUsers() {
|
if (!this.currentUsers || this.currentUsers.length === 0) {
|
this.displayUsers = [];
|
return;
|
}
|
|
// 为了实现无缝滚动,复制3份数据
|
const duplicatedUsers = [];
|
for (let i = 0; i < 3; i++) {
|
this.currentUsers.forEach((user, index) => {
|
duplicatedUsers.push({
|
...user,
|
_uniqueId: i, // 添加唯一标识避免key重复
|
_copyIndex: i // 标记是第几份复制
|
});
|
});
|
}
|
this.displayUsers = duplicatedUsers;
|
},
|
|
// 自动滚动相关方法
|
startAutoScroll() {
|
this.stopAutoScroll();
|
|
const scrollContainer = this.$refs.userContainer;
|
if (!scrollContainer || !this.displayUsers.length) return;
|
|
const autoScroll = () => {
|
// 如果鼠标在容器上,暂停滚动
|
if (this.isMouseOver) {
|
this.scrollTimer = requestAnimationFrame(autoScroll);
|
return;
|
}
|
|
// 获取总宽度
|
const totalWidth = scrollContainer.scrollWidth;
|
const containerWidth = scrollContainer.clientWidth;
|
|
// 检查是否滚动到第三份数据的开始位置(即原始数据的末尾)
|
const originalDataEnd = (totalWidth / 3);
|
|
// 当滚动到第三份数据的开始位置时,瞬间回到第一份数据的相同位置
|
if (scrollContainer.scrollLeft >= originalDataEnd) {
|
scrollContainer.scrollLeft -= originalDataEnd;
|
} else {
|
// 正常向右滚动
|
scrollContainer.scrollLeft += this.scrollSpeed;
|
}
|
|
this.scrollTimer = requestAnimationFrame(autoScroll);
|
};
|
|
// 开始滚动
|
this.scrollTimer = requestAnimationFrame(autoScroll);
|
},
|
|
stopAutoScroll() {
|
if (this.scrollTimer) {
|
cancelAnimationFrame(this.scrollTimer);
|
this.scrollTimer = null;
|
}
|
},
|
|
cleanupScrollTimers() {
|
this.stopAutoScroll();
|
if (this.mapRefreshTimer) {
|
clearTimeout(this.mapRefreshTimer);
|
this.mapRefreshTimer = null;
|
}
|
},
|
|
|
// 重置滚动位置并重新开始自动滚动
|
resetScrollPosition() {
|
this.$nextTick(() => {
|
if (this.$refs.userContainer) {
|
this.$refs.userContainer.scrollLeft = 0;
|
// 重新开始自动滚动
|
this.stopAutoScroll();
|
this.startAutoScroll();
|
}
|
});
|
},
|
|
// 鼠标进入容器时暂停滚动
|
handleMouseEnter() {
|
this.isMouseOver = true;
|
},
|
|
// 鼠标离开容器时恢复滚动
|
handleMouseLeave() {
|
this.isMouseOver = false;
|
},
|
},
|
};
|
</script>
|
<style scoped lang="less">
|
.noActive {
|
font-size: 14px;
|
line-height: 24px;
|
color: rgba(255, 255, 255, 0.7);
|
background: linear-gradient(180deg, #2e5e80 0%, rgba(46, 94, 128, 0) 100%);
|
padding: 0 15px;
|
border-radius: 12px;
|
position: relative;
|
cursor: pointer;
|
}
|
|
.noActive:hover {
|
color: #fff;
|
background: rgba(91, 187, 254, 0.2);
|
box-shadow: inset 0px 2px 4px 0px #5bbbfe;
|
}
|
|
.noActive::before {
|
content: "";
|
position: absolute;
|
top: -1px;
|
left: -1px;
|
right: -1px;
|
bottom: -1px;
|
background: linear-gradient(
|
180deg,
|
rgba(168, 219, 255, 0.8),
|
rgba(168, 219, 255, 0)
|
);
|
border-radius: 12px;
|
z-index: -1;
|
}
|
|
.active {
|
color: #fff;
|
background: rgba(91, 187, 254, 0.2);
|
box-shadow: inset 0px 2px 4px 0px #5bbbfe;
|
}
|
|
.active::before {
|
background: linear-gradient(
|
180deg,
|
rgba(92, 187, 255, 1),
|
rgba(92, 187, 255, 0)
|
);
|
}
|
|
.personBg {
|
background-image: url("../../assets/img/personBg.png");
|
background-size: cover;
|
background-position: center;
|
}
|
|
.color1 {
|
font-family: "Source Han Sans CN Bold";
|
font-weight: bold;
|
font-size: 24px;
|
color: #cce0f8;
|
line-height: 24px;
|
text-shadow: 0px 0px 12px #1068f7;
|
}
|
|
.color2 {
|
font-family: "Source Han Sans CN Regular";
|
font-weight: 400;
|
font-size: 16px;
|
color: #ffffff;
|
line-height: 16px;
|
}
|
|
/* 人员容器滚动样式 */
|
.oa {
|
overflow-x: auto;
|
overflow-y: hidden;
|
scrollbar-width: thin;
|
scrollbar-color: rgba(255, 255, 255, 0.3) transparent;
|
-ms-overflow-style: none;
|
scrollbar-width: none;
|
&::-webkit-scrollbar {
|
display: none;
|
}
|
}
|
|
/* 确保内部元素不被压缩 */
|
.oa > * {
|
flex-shrink: 0;
|
}
|
|
</style>
|