|
|
@@ -185,7 +185,7 @@
|
|
|
<el-tabs class="live-console-tab-right" v-model="tabLeft.activeName" @tab-click="handleClick" :stretch="true">
|
|
|
<el-tab-pane :label="onlineLabel" name="online">
|
|
|
<el-scrollbar ref="manageLeftRef_online" style="height: 800px; width: 100%;">
|
|
|
- <el-row style="margin-top: 10px" type="flex" align="middle" v-for="u in onlineUserList">
|
|
|
+ <el-row style="margin-top: 10px" type="flex" align="middle" v-for="u in onlineDisplayList" :key="u.userId">
|
|
|
<el-col :span="20">
|
|
|
<el-row type="flex" align="middle">
|
|
|
<el-col :span="4" style="padding-left: 10px;"><el-avatar :src="u.avatar"></el-avatar></el-col>
|
|
|
@@ -207,7 +207,7 @@
|
|
|
</el-tab-pane>
|
|
|
<el-tab-pane :label="offlineLabel" name="offline">
|
|
|
<el-scrollbar ref="manageLeftRef_offline" style="height: 800px; width: 100%;">
|
|
|
- <el-row style="margin-top: 10px" type="flex" align="middle" v-for="u in offlineUserList">
|
|
|
+ <el-row style="margin-top: 10px" type="flex" align="middle" v-for="u in offlineDisplayList" :key="u.userId">
|
|
|
<el-col :span="20">
|
|
|
<el-row type="flex" align="middle">
|
|
|
<el-col :span="4" style="padding-left: 10px;"><el-avatar :src="u.avatar"></el-avatar></el-col>
|
|
|
@@ -229,7 +229,7 @@
|
|
|
</el-tab-pane>
|
|
|
<el-tab-pane :label="silencedUserLabel" name="silenced">
|
|
|
<el-scrollbar ref="manageLeftRef_silenced" style="height: 800px; width: 100%;">
|
|
|
- <el-row style="margin-top: 10px" type="flex" align="middle" v-for="u in silencedUserList">
|
|
|
+ <el-row style="margin-top: 10px" type="flex" align="middle" v-for="u in silencedDisplayList" :key="u.userId">
|
|
|
<el-col :span="20">
|
|
|
<el-row type="flex" align="middle">
|
|
|
<el-col :span="4" style="padding-left: 10px;"><el-avatar :src="u.avatar"></el-avatar></el-col>
|
|
|
@@ -257,7 +257,7 @@
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
-import { blockUser,changeUserStatus, watchUserList } from '@/api/live/liveWatchUser'
|
|
|
+import { blockUser,changeUserStatus,getLiveUserTotals, watchUserList } from '@/api/live/liveWatchUser'
|
|
|
import { getLiveVideoByLiveId } from '@/api/live/liveVideo'
|
|
|
import {getLive, getLivingUrl} from '@/api/live/live'
|
|
|
import { getLiveOrderTimeGranularity } from '@/api/live/liveOrder'
|
|
|
@@ -321,6 +321,50 @@ export default {
|
|
|
// ... 其他数据
|
|
|
chatScrollTop: 0, // 保存聊天滚动位置
|
|
|
socket: null,
|
|
|
+ userTotal: {
|
|
|
+ online: 0, // 在线总人数
|
|
|
+ offline: 0, // 离线总人数
|
|
|
+ silenced: 0 // 禁言总人数
|
|
|
+ },
|
|
|
+ // 各Tab的显示列表(仅存储当前需要展示的数据)
|
|
|
+ onlineDisplayList: [], // 在线用户显示列表
|
|
|
+ offlineDisplayList: [], // 离线用户显示列表
|
|
|
+ silencedDisplayList: [], // 禁言用户显示列表
|
|
|
+ // 各Tab的分页参数
|
|
|
+ pageParams: {
|
|
|
+ online: {
|
|
|
+ currentPage: 1, // 当前页(下一页加载用)
|
|
|
+ pageSize: 20, // 当前页(下一页加载用)
|
|
|
+ prevPage: 0, // 上一页页码(上一页加载用)
|
|
|
+ totalLoaded: 0, // 已加载总条数
|
|
|
+ total: 0, // 总数据量
|
|
|
+ hasMore: true, // 是否有下一页
|
|
|
+ hasPrev: false // 是否有上一页
|
|
|
+ },
|
|
|
+ offline: {
|
|
|
+ currentPage: 1,
|
|
|
+ pageSize: 20,
|
|
|
+ prevPage: 0,
|
|
|
+ totalLoaded: 0,
|
|
|
+ total: 0,
|
|
|
+ hasMore: true,
|
|
|
+ hasPrev: false
|
|
|
+ },
|
|
|
+ silenced: {
|
|
|
+ currentPage: 1,
|
|
|
+ pageSize: 20,
|
|
|
+ prevPage: 0,
|
|
|
+ totalLoaded: 0,
|
|
|
+ total: 0,
|
|
|
+ hasMore: true,
|
|
|
+ hasPrev: false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ scrLoading: {
|
|
|
+ online: { next: false, prev: false },
|
|
|
+ offline: { next: false, prev: false },
|
|
|
+ silenced: { next: false, prev: false }
|
|
|
+ }
|
|
|
}
|
|
|
},
|
|
|
created() {
|
|
|
@@ -340,32 +384,14 @@ export default {
|
|
|
companyId() {
|
|
|
return this.$store.state.user.user.companyId
|
|
|
},
|
|
|
- onlineUserList() {
|
|
|
- return this.userList.filter(u => u.online === 0)
|
|
|
- },
|
|
|
onlineLabel() {
|
|
|
- if (this.onlineUserList.length > 0) {
|
|
|
- return '在线(' + this.onlineUserList.length + ')'
|
|
|
- }
|
|
|
- return '在线'
|
|
|
- },
|
|
|
- offlineUserList() {
|
|
|
- return this.userList.filter(u => u.online === 1)
|
|
|
+ return `在线(${this.userTotal.online})`;
|
|
|
},
|
|
|
offlineLabel() {
|
|
|
- if (this.offlineUserList.length > 0) {
|
|
|
- return '离线(' + this.offlineUserList.length + ')'
|
|
|
- }
|
|
|
- return '离线'
|
|
|
- },
|
|
|
- silencedUserList() {
|
|
|
- return this.userList.filter(u => u.msgStatus === 1)
|
|
|
+ return `离线(${this.userTotal.offline})`;
|
|
|
},
|
|
|
silencedUserLabel() {
|
|
|
- if (this.silencedUserList.length > 0) {
|
|
|
- return '禁言(' + this.silencedUserList.length + ')'
|
|
|
- }
|
|
|
- return '禁言'
|
|
|
+ return `禁言(${this.userTotal.silenced})`;
|
|
|
}
|
|
|
},
|
|
|
mounted() {
|
|
|
@@ -380,6 +406,24 @@ export default {
|
|
|
this.$refs.manageRightRef.wrap.addEventListener('scroll', this.saveChatScrollPosition);
|
|
|
}
|
|
|
});
|
|
|
+ this.initScrollListeners();
|
|
|
+ },
|
|
|
+ beforeDestroy() {
|
|
|
+ this.saveTabScrollPositions()
|
|
|
+ // 移除滚动监听(避免内存泄漏)
|
|
|
+ const scrollRefs = {
|
|
|
+ online: this.$refs.manageLeftRef_online,
|
|
|
+ offline: this.$refs.manageLeftRef_offline,
|
|
|
+ silenced: this.$refs.manageLeftRef_silenced
|
|
|
+ };
|
|
|
+ Object.keys(scrollRefs).forEach(tabName => {
|
|
|
+ const scrollEl = scrollRefs[tabName]?.wrap;
|
|
|
+ if (scrollEl) {
|
|
|
+ scrollEl.removeEventListener('scroll', () =>
|
|
|
+ this.handleTabScroll(tabName, scrollEl)
|
|
|
+ );
|
|
|
+ }
|
|
|
+ })
|
|
|
},
|
|
|
// 使用 deactivated 和 activated 钩子替代 beforeDestroy 和 destroyed
|
|
|
deactivated() {
|
|
|
@@ -539,7 +583,6 @@ export default {
|
|
|
|
|
|
const currentTime = new Date().getTime();
|
|
|
const elapsedTime = currentTime - this.startTime; // 总流逝时间(毫秒)
|
|
|
- console.log(this.startTime)
|
|
|
|
|
|
if (elapsedTime < 0) {
|
|
|
elapsedTimeEl.textContent = '00:00:00';
|
|
|
@@ -661,8 +704,158 @@ export default {
|
|
|
}
|
|
|
},
|
|
|
handleClick(tab) {
|
|
|
- console.log("click",tab.name)
|
|
|
- console.log("liveId", this.liveId)
|
|
|
+ const tabName = tab.name;
|
|
|
+ const params = this.pageParams[tabName];
|
|
|
+ const displayList = this[`${tabName}DisplayList`];
|
|
|
+ // 首次切换到该Tab或列表为空时初始化
|
|
|
+ if (displayList.length < 20) {
|
|
|
+ // 重置分页参数
|
|
|
+ params.currentPage = 1;
|
|
|
+ params.pageSize = 20;
|
|
|
+ params.prevPage = 0;
|
|
|
+ params.totalLoaded = 0;
|
|
|
+ params.hasMore = true;
|
|
|
+ params.hasPrev = false;
|
|
|
+ // 加载第一页
|
|
|
+ this.loadNextPage(tabName);
|
|
|
+ } else {
|
|
|
+ // 非首次切换,恢复滚动位置
|
|
|
+ this.$nextTick(() => {
|
|
|
+ const scrollEl = this.getScrollElement(tabName);
|
|
|
+ if (scrollEl) {
|
|
|
+ scrollEl.scrollTop = this.tabScrollPositions[tabName] || 0;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ saveTabScrollPositions() {
|
|
|
+ this.tabScrollPositions = {
|
|
|
+ online: this.getScrollElement('online')?.scrollTop || 0,
|
|
|
+ offline: this.getScrollElement('offline')?.scrollTop || 0,
|
|
|
+ silenced: this.getScrollElement('silenced')?.scrollTop || 0
|
|
|
+ };
|
|
|
+ },
|
|
|
+ // 加载指定Tab的用户列表(核心加载逻辑)
|
|
|
+ loadNextPage(tabName) {
|
|
|
+ const params = this.pageParams[tabName];
|
|
|
+ const displayList = this[`${tabName}DisplayList`];
|
|
|
+ console.log(`加载 ${tabName} 用户列表`)
|
|
|
+ console.log(!params.hasMore || this.scrLoading[tabName].next)
|
|
|
+ console.log(params.currentPage)
|
|
|
+ // 若没有更多数据或正在加载,直接返回
|
|
|
+ if (!params.hasMore || this.scrLoading[tabName].next) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.scrLoading[tabName].next = true;
|
|
|
+ const queryParams = {
|
|
|
+ liveId: this.liveId,
|
|
|
+ pageNum: params.currentPage,
|
|
|
+ pageSize: 20,
|
|
|
+ online: tabName === 'online' ? 0 : 1,
|
|
|
+ msgStatus: tabName === 'silenced' ? 1 : 0
|
|
|
+ };
|
|
|
+ // 调用接口加载对应状态的分页数据(需后端支持按状态筛选)
|
|
|
+ watchUserList(queryParams).then(response => {
|
|
|
+ this.scrLoading[tabName].next = false;
|
|
|
+ if (response.code !== 200) return;
|
|
|
+
|
|
|
+ const { rows, total } = response;
|
|
|
+ params.total = total; // 记录总数据量
|
|
|
+ // 过滤重复数据(基于userId)
|
|
|
+ const newRows = rows.filter(row =>
|
|
|
+ !displayList.some(u => u.userId === row.userId)
|
|
|
+ );
|
|
|
+ displayList.push(...newRows)
|
|
|
+ // 添加新数据并限制最大长度(避免内存占用过大)
|
|
|
+ if (displayList.length >= 40) { // 最大保留100条
|
|
|
+ this[`${tabName}DisplayList`] = displayList.slice(-40);
|
|
|
+ // 记录滚动位置(用于加载后校准)
|
|
|
+ const scrollEl = this.getScrollElement(tabName);
|
|
|
+ // 校准滚动位置(保持视觉连续性)
|
|
|
+ this.$nextTick(() => {
|
|
|
+ if (scrollEl) {
|
|
|
+ scrollEl.scrollTop = scrollEl.scrollHeight * 0.5;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ // 更新分页状态
|
|
|
+ params.hasMore = params.currentPage * params.pageSize < total;
|
|
|
+ params.currentPage += 1;
|
|
|
+ params.hasPrev = params.currentPage > 2; // 当前页>2时一定有上一页
|
|
|
+ params.prevPage = params.currentPage - 2;
|
|
|
+ }).catch(() => {
|
|
|
+ this.scrLoading[tabName].next = false;
|
|
|
+ });
|
|
|
+ },
|
|
|
+ // 新增:加载上一页(向上滚动时)
|
|
|
+ loadPrevPage(tabName) {
|
|
|
+ const params = this.pageParams[tabName];
|
|
|
+ const displayList = this[`${tabName}DisplayList`];
|
|
|
+ // 边界校验:无上一页/正在加载/当前页<=1
|
|
|
+ console.log(`加载 ${tabName} 上一页`);
|
|
|
+ console.log(!params.hasPrev || this.scrLoading[tabName].prev || params.currentPage <= 1)
|
|
|
+ if (!params.hasPrev || this.scrLoading[tabName].prev || params.currentPage <= 1) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.scrLoading[tabName].prev = true;
|
|
|
+ const targetPage = params.prevPage > 0 ? params.prevPage : params.currentPage - 2;
|
|
|
+ const queryParams = {
|
|
|
+ liveId: this.liveId,
|
|
|
+ pageNum: targetPage,
|
|
|
+ pageSize: 20,
|
|
|
+ online: tabName === 'online' ? 0 : 1,
|
|
|
+ msgStatus: tabName === 'silenced' ? 1 : 0
|
|
|
+ };
|
|
|
+ watchUserList(queryParams).then(response => {
|
|
|
+ this.scrLoading[tabName].prev = false;
|
|
|
+ if (response.code !== 200) return;
|
|
|
+
|
|
|
+ const { rows } = response;
|
|
|
+ if (rows.length === 0) {
|
|
|
+ params.hasPrev = false;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 记录滚动位置(用于加载后校准)
|
|
|
+ const scrollEl = this.getScrollElement(tabName);
|
|
|
+ const scrollTop = scrollEl?.scrollTop || 0;
|
|
|
+ const itemHeight = 80; // 预估行高(根据实际样式调整)
|
|
|
+ const newItemsHeight = rows.length * itemHeight;
|
|
|
+
|
|
|
+ // 过滤重复数据并添加到列表头部
|
|
|
+ const newRows = rows.filter(row => !displayList.some(u => u.userId === row.userId));
|
|
|
+ this[`${tabName}DisplayList`] = [...newRows, ...displayList];
|
|
|
+ params.totalLoaded += newRows.length;
|
|
|
+
|
|
|
+ // 限制最大长度
|
|
|
+ if (this[`${tabName}DisplayList`].length > 40) {
|
|
|
+ this[`${tabName}DisplayList`] = this[`${tabName}DisplayList`].slice(0, 40);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新分页状态
|
|
|
+ params.prevPage = targetPage - 1;
|
|
|
+ params.hasPrev = targetPage > 1; // 上一页页码>1时还有更多上一页
|
|
|
+ params.currentPage = params.currentPage - 1;
|
|
|
+ if(params.currentPage * 20 < params.total) params.hasMore = true;
|
|
|
+ // 校准滚动位置(保持视觉连续性)
|
|
|
+ this.$nextTick(() => {
|
|
|
+ if (scrollEl) {
|
|
|
+ scrollEl.scrollTop = scrollEl.scrollHeight * 0.5;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }).catch(() => {
|
|
|
+ this.scrLoading[tabName].prev = false;
|
|
|
+ });
|
|
|
+ },
|
|
|
+ // 辅助:获取Tab对应的滚动容器
|
|
|
+ getScrollElement(tabName) {
|
|
|
+ const scrollRefs = {
|
|
|
+ online: this.$refs.manageLeftRef_online,
|
|
|
+ offline: this.$refs.manageLeftRef_offline,
|
|
|
+ silenced: this.$refs.manageLeftRef_silenced
|
|
|
+ };
|
|
|
+ return scrollRefs[tabName]?.wrap;
|
|
|
},
|
|
|
getLiveVideo() {
|
|
|
getLiveVideoByLiveId(this.liveId).then(res => {
|
|
|
@@ -671,22 +864,62 @@ export default {
|
|
|
},
|
|
|
getList() {
|
|
|
this.resetParams()
|
|
|
- this.loadUserList()
|
|
|
+ // this.loadUserList()
|
|
|
+ this.loadUserTotals(); // 先加载总人数
|
|
|
+ // this.handleClick('online')
|
|
|
+ this.handleClick({name:'online'})
|
|
|
this.loadMsgList()
|
|
|
},
|
|
|
+ loadUserTotals() {
|
|
|
+ if (!this.liveId) return;
|
|
|
+ // 假设后端提供一个接口返回总人数(如果没有,可通过首次加载全量数据后统计)
|
|
|
+ getLiveUserTotals({ liveId: this.liveId }).then(res => {
|
|
|
+ if (res.code === 200) {
|
|
|
+ this.userTotal = res.data; // { online, offline, silenced }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
resetParams() {
|
|
|
- this.userList= []
|
|
|
- this.userParams = {
|
|
|
- pageNum: 1,
|
|
|
- pageSize: 10,
|
|
|
- liveId: this.liveId
|
|
|
- }
|
|
|
- this.msgList = []
|
|
|
+ // 重置各Tab的显示列表和分页参数
|
|
|
+ this.onlineDisplayList = [];
|
|
|
+ this.offlineDisplayList = [];
|
|
|
+ this.silencedDisplayList = [];
|
|
|
+ this.pageParams = {
|
|
|
+ online: {
|
|
|
+ currentPage: 1, // 当前页(下一页加载用)
|
|
|
+ pageSize: 20, // 当前页(下一页加载用)
|
|
|
+ prevPage: 0, // 上一页页码(上一页加载用)
|
|
|
+ totalLoaded: 0, // 已加载总条数
|
|
|
+ total: 0, // 总数据量
|
|
|
+ hasMore: true, // 是否有下一页
|
|
|
+ hasPrev: false // 是否有上一页
|
|
|
+ },
|
|
|
+ offline: {
|
|
|
+ currentPage: 1,
|
|
|
+ pageSize: 20,
|
|
|
+ prevPage: 0,
|
|
|
+ totalLoaded: 0,
|
|
|
+ total: 0,
|
|
|
+ hasMore: true,
|
|
|
+ hasPrev: false
|
|
|
+ },
|
|
|
+ silenced: {
|
|
|
+ currentPage: 1,
|
|
|
+ pageSize: 20,
|
|
|
+ prevPage: 0,
|
|
|
+ totalLoaded: 0,
|
|
|
+ total: 0,
|
|
|
+ hasMore: true,
|
|
|
+ hasPrev: false
|
|
|
+ }
|
|
|
+ };
|
|
|
+ // 消息参数保留
|
|
|
+ this.msgList = [];
|
|
|
this.msgParams = {
|
|
|
pageNum: 1,
|
|
|
pageSize: 10,
|
|
|
liveId: this.liveId
|
|
|
- }
|
|
|
+ };
|
|
|
},
|
|
|
loadUserList() {
|
|
|
if(this.liveId == null) return
|
|
|
@@ -804,6 +1037,8 @@ export default {
|
|
|
user.msgStatus = u.msgStatus;
|
|
|
}
|
|
|
});
|
|
|
+ // 4. 关键:重新筛选所有Tab的显示列表,确保状态同步
|
|
|
+ this.refreshUserDisplayLists(u);
|
|
|
|
|
|
let msg = u.msgStatus === 0 ? "已解禁" : "已禁言"
|
|
|
this.msgSuccess(msg);
|
|
|
@@ -812,6 +1047,49 @@ export default {
|
|
|
this.msgError("操作失败");
|
|
|
})
|
|
|
},
|
|
|
+ // 新增:重新筛选所有Tab的显示列表
|
|
|
+ refreshUserDisplayLists(user) {
|
|
|
+ const { userId, msgStatus: newStatus, online } = user;
|
|
|
+ const oldStatus = newStatus === 1 ? 0 : 1; // 操作前的状态(反向)
|
|
|
+
|
|
|
+ // 1. 禁言操作(newStatus=1):从原在线/离线列表移除,加入禁言列表
|
|
|
+ if (newStatus === 1) {
|
|
|
+ // 从在线/离线列表中移除该用户(根据当前在线状态)
|
|
|
+ if (online === 0) {
|
|
|
+ this.onlineDisplayList = this.onlineDisplayList.filter(u => u.userId !== userId);
|
|
|
+ this.userTotal.online = Math.max(0, this.userTotal.online - 1);
|
|
|
+ } else {
|
|
|
+ this.offlineDisplayList = this.offlineDisplayList.filter(u => u.userId !== userId);
|
|
|
+ this.userTotal.offline = Math.max(0, this.userTotal.offline - 1);
|
|
|
+ }
|
|
|
+ this.userTotal.silenced = this.userTotal.silenced + 1;
|
|
|
+ // 添加到禁言列表(去重+限制长度)
|
|
|
+ const silencedList = this.silencedDisplayList.filter(u => u.userId !== userId);
|
|
|
+ silencedList.push(user);
|
|
|
+ this.silencedDisplayList = silencedList.slice(-40);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 解禁操作(newStatus=0):从禁言列表移除,回到原在线/离线列表
|
|
|
+ else {
|
|
|
+ // 从禁言列表移除
|
|
|
+ this.silencedDisplayList = this.silencedDisplayList.filter(u => u.userId !== userId);
|
|
|
+ this.userTotal.silenced = Math.max(0, this.userTotal.silenced - 1);
|
|
|
+ // 回到对应的在线/离线列表(根据当前在线状态)
|
|
|
+ if (online === 0) {
|
|
|
+ // 加入在线列表(去重+限制长度)
|
|
|
+ const onlineList = this.onlineDisplayList.filter(u => u.userId !== userId);
|
|
|
+ onlineList.push(user);
|
|
|
+ this.onlineDisplayList = onlineList.slice(-40);
|
|
|
+ this.userTotal.online = this.userTotal.online + 1;
|
|
|
+ } else {
|
|
|
+ // 加入离线列表(去重+限制长度)
|
|
|
+ const offlineList = this.offlineDisplayList.filter(u => u.userId !== userId);
|
|
|
+ offlineList.push(user);
|
|
|
+ this.offlineDisplayList = offlineList.slice(-40);
|
|
|
+ this.userTotal.offline = this.userTotal.offline + 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
connectWebSocket() {
|
|
|
this.$store.dispatch('initLiveWs', {
|
|
|
liveWsUrl: this.liveWsUrl,
|
|
|
@@ -835,6 +1113,9 @@ export default {
|
|
|
message.msgStatus = 0
|
|
|
}
|
|
|
delete message.params
|
|
|
+ if(this.msgList.length > 50){
|
|
|
+ this.msgList.shift()
|
|
|
+ }
|
|
|
this.msgList.push(message)
|
|
|
// 移动到底部
|
|
|
this.$nextTick(() => {
|
|
|
@@ -844,12 +1125,48 @@ export default {
|
|
|
})
|
|
|
}
|
|
|
else if (cmd === 'entry' || cmd === 'out') {
|
|
|
- this.loadUserList()
|
|
|
- let user = data
|
|
|
- if(this.userList.length > 0){
|
|
|
- this.userList = this.userList.filter(u => u.userId !== user.userId)
|
|
|
+ const user = data;
|
|
|
+ const online = cmd === 'entry' ? 0 : 1; // 0=在线,1=离线
|
|
|
+ const info = {
|
|
|
+ online:online,
|
|
|
+ msgStatus: user.msgStatus || 0,
|
|
|
+ nickName: user.nickName || '',
|
|
|
+ userType: user.userType || 0,
|
|
|
+ userId: user.userId || '',
|
|
|
+ };
|
|
|
+
|
|
|
+ // 1. 更新总人数(在线/离线互转)
|
|
|
+ if (cmd === 'entry') {
|
|
|
+ this.userTotal.online += 1;
|
|
|
+ this.userTotal.offline = Math.max(0, this.userTotal.offline - 1); // 确保不小于0
|
|
|
+ } else {
|
|
|
+ this.userTotal.offline += 1;
|
|
|
+ this.userTotal.online = Math.max(0, this.userTotal.online - 1); // 确保不小于0
|
|
|
+ }
|
|
|
+ // 2. 强制更新相关列表(无论当前激活哪个Tab)
|
|
|
+ if (cmd === 'entry') {
|
|
|
+ // 用户进入:从离线列表删除,添加到在线列表
|
|
|
+ this.offlineDisplayList = this.offlineDisplayList.filter(u => u.userId !== user.userId);
|
|
|
+ const newOnlineList = this.onlineDisplayList.filter(u => u.userId !== user.userId);
|
|
|
+ newOnlineList.push(info);
|
|
|
+ this.onlineDisplayList = newOnlineList.slice(-40); // 限制最大50条
|
|
|
+ } else {
|
|
|
+ // 用户离开:从在线列表删除,添加到离线列表
|
|
|
+ this.onlineDisplayList = this.onlineDisplayList.filter(u => u.userId !== user.userId);
|
|
|
+ const newOfflineList = this.offlineDisplayList.filter(u => u.userId !== user.userId);
|
|
|
+ newOfflineList.push(info);
|
|
|
+ this.offlineDisplayList = newOfflineList.slice(-40); // 限制最大50条
|
|
|
+ }
|
|
|
+ // 3. 处理禁言列表(如果用户是禁言状态,无论进出都要同步)
|
|
|
+ if (info.msgStatus === 1) {
|
|
|
+ // 禁言用户:从禁言列表删除旧数据,添加新数据
|
|
|
+ const newSilencedList = this.silencedDisplayList.filter(u => u.userId !== user.userId);
|
|
|
+ newSilencedList.push(info);
|
|
|
+ this.silencedDisplayList = newSilencedList.slice(-40);
|
|
|
+ } else {
|
|
|
+ // 非禁言用户:从禁言列表删除(如果存在)
|
|
|
+ this.silencedDisplayList = this.silencedDisplayList.filter(u => u.userId !== user.userId);
|
|
|
}
|
|
|
- this.userList.push(user)
|
|
|
} else if (cmd === 'live_start') {
|
|
|
|
|
|
} else if (cmd === 'live_end') {
|
|
|
@@ -876,7 +1193,42 @@ export default {
|
|
|
this.socket.send(JSON.stringify(msg))
|
|
|
|
|
|
this.newMsg = '';
|
|
|
- }
|
|
|
+ },
|
|
|
+ // 初始化滚动监听(在mounted中调用)
|
|
|
+ initScrollListeners() {
|
|
|
+ // 为每个Tab的滚动容器添加监听
|
|
|
+ this.$nextTick(() => {
|
|
|
+ const scrollRefs = {
|
|
|
+ online: this.$refs.manageLeftRef_online,
|
|
|
+ offline: this.$refs.manageLeftRef_offline,
|
|
|
+ silenced: this.$refs.manageLeftRef_silenced
|
|
|
+ };
|
|
|
+
|
|
|
+ Object.keys(scrollRefs).forEach(tabName => {
|
|
|
+ const scrollEl = scrollRefs[tabName]?.wrap;
|
|
|
+ if (scrollEl) {
|
|
|
+ scrollEl.addEventListener('scroll', () =>
|
|
|
+ this.handleTabScroll(tabName, scrollEl)
|
|
|
+ );
|
|
|
+ }
|
|
|
+ });
|
|
|
+ });
|
|
|
+ },
|
|
|
+ handleTabScroll(tabName, scrollEl) {
|
|
|
+ const { scrollTop, scrollHeight, clientHeight } = scrollEl;
|
|
|
+ const bottomThreshold = 50; // 距离底部100px触发下一页
|
|
|
+ const topThreshold = 50; // 距离顶部100px触发上一页
|
|
|
+
|
|
|
+ // 加载下一页(滚动到底部附近)
|
|
|
+ if (scrollHeight - scrollTop - clientHeight < bottomThreshold) {
|
|
|
+ this.loadNextPage(tabName);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 加载上一页(滚动到顶部附近)
|
|
|
+ if (scrollTop < topThreshold) {
|
|
|
+ this.loadPrevPage(tabName);
|
|
|
+ }
|
|
|
+ },
|
|
|
},
|
|
|
destroyed() {
|
|
|
this.hls?.destroy();
|