yumi-admin/apps/src/views/operate/online-room.vue
2026-04-16 18:44:08 +08:00

334 lines
8.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script lang="ts" setup>
import { computed, reactive, ref } from 'vue';
import { Page } from '@vben/common-ui';
import { useAccessStore } from '@vben/stores';
import { onlineRoom, updateProfile } from '#/api/legacy/room';
import { getAllowedSysOrigins } from '#/views/system/shared';
import {
Button,
Card,
Empty,
Modal,
Space,
Tag,
message,
} from 'antdv-next';
import AccountHandleModal from '#/views/approval/components/account-handle-modal.vue';
import RoomDetailsDrawer from '#/views/app-system/components/room-details-drawer.vue';
import OnlineRoomMembersDrawer from './components/online-room-members-drawer.vue';
import RoomSearchInput from './components/room-search-input.vue';
import SystemRegionSelect from './components/system-region-select.vue';
defineOptions({ name: 'OperateOnlineRoom' });
const accessStore = useAccessStore();
const sysOriginOptions = computed(() => {
const options = getAllowedSysOrigins(accessStore.accessCodes || []);
return options.length > 0 ? options : getAllowedSysOrigins([]);
});
const regionSelectRef = ref<InstanceType<typeof SystemRegionSelect> | null>(null);
const loading = ref(false);
const searchLoading = ref(false);
const list = ref<Array<Record<string, any>>>([]);
const roomStatusSavingId = ref<number | string>('');
const membersOpen = ref(false);
const accountHandleOpen = ref(false);
const roomDetailsOpen = ref(false);
const activeSessionId = ref<number | string>('');
const activeUserId = ref<number | string>('');
const activeRoomId = ref<number | string>('');
const query = reactive<Record<string, any>>({
limit: 200,
region: '',
roomAccount: '',
roomId: '',
sysOrigin: sysOriginOptions.value[0]?.value || '',
});
async function loadData() {
loading.value = true;
try {
list.value = (await onlineRoom({ ...query })) || [];
} finally {
loading.value = false;
}
}
function handleSearch() {
void loadData();
}
function changeSysOrigin() {
regionSelectRef.value?.clearValue();
query.region = '';
query.roomId = '';
query.roomAccount = '';
void loadData();
}
function loadSearchRoom() {
searchLoading.value = true;
}
function searchRoomSuccess(result: Record<string, any>) {
searchLoading.value = false;
query.roomId = String(result?.id || '');
query.roomAccount = '';
}
function searchRoomFail() {
query.roomId = '';
query.roomAccount = '';
searchLoading.value = false;
}
function openMembers(record: Record<string, any>) {
activeSessionId.value = record.profile?.id || '';
membersOpen.value = true;
}
function openAccountHandle(record: Record<string, any>) {
activeUserId.value = record.profile?.userId || '';
accountHandleOpen.value = true;
}
function openRoomDetails(record: Record<string, any>) {
activeRoomId.value = record.profile?.id || '';
roomDetailsOpen.value = true;
}
function isRoomClosed(record: Record<string, any>) {
return record.profile?.event === 'CLOSE';
}
function handleToggleRoomStatus(record: Record<string, any>) {
const roomId = record.profile?.id;
const userId = record.profile?.userId;
if (!roomId || !userId) {
message.warning('房间信息不完整,无法操作');
return;
}
const nextEvent = isRoomClosed(record) ? 'AVAILABLE' : 'CLOSE';
Modal.confirm({
title: nextEvent === 'CLOSE' ? '是否确认关闭该房间?' : '是否确认恢复该房间?',
async onOk() {
roomStatusSavingId.value = roomId;
try {
await updateProfile({
event: nextEvent,
id: roomId,
sysOrigin: record.profile?.sysOrigin || query.sysOrigin,
userId,
});
message.success(nextEvent === 'CLOSE' ? '关闭成功' : '恢复成功');
await loadData();
} finally {
roomStatusSavingId.value = '';
}
},
});
}
loadData();
</script>
<template>
<Page title="在线房间">
<Card>
<div class="toolbar">
<select
v-model="query.sysOrigin"
class="plain-select"
@change="changeSysOrigin"
>
<option
v-for="item in sysOriginOptions"
:key="item.value"
:value="item.value"
>
{{ item.label }}
</option>
</select>
<div v-if="query.sysOrigin === 'ASWAT'" class="region-select">
<SystemRegionSelect
ref="regionSelectRef"
v-model:value="query.region"
placeholder="请选择区域"
:sys-origin="query.sysOrigin"
/>
</div>
<div class="room-search">
<RoomSearchInput
:key="query.sysOrigin"
:sys-origin="query.sysOrigin"
placeholder="房间ID"
@fail="searchRoomFail"
@load="loadSearchRoom"
@success="searchRoomSuccess"
/>
</div>
<Button :loading="searchLoading || loading" type="primary" @click="handleSearch">搜索</Button>
</div>
<div class="room-grid">
<Card
v-for="item in list"
:key="item.profile?.id || item.id"
:loading="loading"
class="room-card"
>
<div class="room-head">
<div class="cover-row">
<img
:src="item.profile?.roomCover || 'https://dummyimage.com/80x80/e2e8f0/64748b&text=Room'"
alt=""
class="room-cover"
>
<Space wrap>
<Tag>{{ item.activeRoom?.region || '-' }}</Tag>
<Tag v-if="item.activeRoom?.hot" color="red">热门</Tag>
</Space>
</div>
<div class="card-actions">
<Button size="small" type="link" @click="openMembers(item)">在线成员</Button>
<Button
size="small"
type="link"
:loading="roomStatusSavingId === item.profile?.id"
@click="handleToggleRoomStatus(item)"
>
{{ isRoomClosed(item) ? '恢复' : '关闭' }}
</Button>
<Button size="small" type="link" @click="openAccountHandle(item)">账号处理</Button>
<Button size="small" type="link" @click="openRoomDetails(item)">房间详情</Button>
</div>
</div>
<div class="room-body">
<div class="ellipsis">名称{{ item.profile?.roomName || '-' }}</div>
<div class="ellipsis">ID{{ item.profile?.id || '-' }}</div>
<div class="ellipsis">账号{{ item.profile?.roomAccount || '-' }}</div>
<div class="ellipsis">
密码{{ item.profile?.setting?.password || '无' }}
</div>
<div class="ellipsis">公告{{ item.profile?.roomDesc || '-' }}</div>
<div class="ellipsis">
国家{{ item.activeRoom?.countryName || item.activeRoom?.countryCode || '-' }}
</div>
<div class="ellipsis">
家族
{{ item.activeRoom?.familyProfile
? `[${item.activeRoom.familyProfile.familyAccount}] ${item.activeRoom.familyProfile.familyName || '-'}`
: '无' }}
</div>
</div>
</Card>
</div>
<Empty v-if="!loading && list.length === 0" description="暂无在线房间" />
</Card>
<OnlineRoomMembersDrawer
:open="membersOpen"
:session-id="activeSessionId"
@close="membersOpen = false"
/>
<AccountHandleModal
:open="accountHandleOpen"
:user-id="activeUserId"
@close="accountHandleOpen = false"
@success="accountHandleOpen = false"
/>
<RoomDetailsDrawer
:open="roomDetailsOpen"
:room-id="activeRoomId"
@close="roomDetailsOpen = false"
/>
</Page>
</template>
<style scoped>
.toolbar {
display: flex;
flex-wrap: wrap;
gap: 12px;
margin-bottom: 16px;
}
.plain-input,
.plain-select {
border: 1px solid var(--ant-color-border);
border-radius: 6px;
min-width: 180px;
padding: 4px 11px;
}
.room-search {
width: 360px;
}
.region-select {
min-width: 180px;
width: 180px;
}
.room-grid {
display: grid;
gap: 12px;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
}
.room-card :deep(.ant-card-body) {
display: grid;
gap: 12px;
}
.room-head {
display: grid;
gap: 10px;
}
.cover-row {
align-items: center;
display: flex;
gap: 12px;
}
.room-cover {
border-radius: 12px;
height: 56px;
object-fit: cover;
width: 56px;
}
.card-actions {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.room-body {
color: rgb(71 85 105);
display: grid;
gap: 8px;
}
.ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>