feat(tenant): 推送配置说明与IM多端互踢配置

推送服务配置页新增字段说明提示(通道业务键含义、Channel ID 规则、
厂商凭据来源);IM 服务配置页新增"多端登录与互踢"卡片,支持三种
模式选择;app.ts 类型增加 multiDeviceLoginMode 字段。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
这个提交包含在:
XuqmGroup 2026-05-06 07:16:45 +08:00
父节点 8521e35660
当前提交 4df6bd0e79
共有 3 个文件被更改,包括 62 次插入0 次删除

查看文件

@ -39,6 +39,7 @@ export interface ImServiceConfig {
historyRetentionDays: number
conversationPullLimit: number
multiClientConversationDeleteSync: boolean
multiDeviceLoginMode?: 'MULTI_DEVICE_FREE' | 'SAME_PLATFORM_ONE' | 'SINGLE_DEVICE'
}
export interface UpdateServiceConfig {

查看文件

@ -108,6 +108,36 @@
</div>
</el-card>
<el-card style="margin-bottom:16px">
<template #header>多端登录与互踢</template>
<div class="service-config-list">
<div class="service-config-row service-config-row--column">
<div>
<div class="service-config-title">多端登录方式</div>
<div class="service-config-desc">控制同一账号在多个设备上的登录行为</div>
</div>
<el-radio-group
:model-value="imConfig.multiDeviceLoginMode"
class="login-mode-group"
@change="(val: ImServiceConfig['multiDeviceLoginMode']) => onToggleImConfig({ multiDeviceLoginMode: val })"
>
<el-radio value="MULTI_DEVICE_FREE">
全平台随意登录不互踢
<div class="radio-desc">多台设备可同时在线不限数量</div>
</el-radio>
<el-radio value="SAME_PLATFORM_ONE">
相同平台踢旧设备
<div class="radio-desc">同一平台Android/iOS/鸿蒙只保留最新登录设备不同平台可同时在线</div>
</el-radio>
<el-radio value="SINGLE_DEVICE">
全平台只保留一台设备
<div class="radio-desc">任意设备登录后其他所有设备均被踢下线</div>
</el-radio>
</el-radio-group>
</div>
</div>
</el-card>
<el-card>
<template #header>好友与关系链</template>
<div class="service-config-list">
@ -169,6 +199,7 @@ const imConfig = computed<ImServiceConfig>(() => {
historyRetentionDays: parsed.historyRetentionDays ?? 7,
conversationPullLimit: parsed.conversationPullLimit ?? 100,
multiClientConversationDeleteSync: parsed.multiClientConversationDeleteSync ?? false,
multiDeviceLoginMode: normalizeLoginMode(parsed.multiDeviceLoginMode),
}
} catch {
return defaultImConfig()
@ -186,9 +217,20 @@ function defaultImConfig(): ImServiceConfig {
historyRetentionDays: 7,
conversationPullLimit: 100,
multiClientConversationDeleteSync: false,
multiDeviceLoginMode: 'MULTI_DEVICE_FREE',
}
}
function normalizeLoginMode(
mode: ImServiceConfig['multiDeviceLoginMode'] | string | undefined,
): ImServiceConfig['multiDeviceLoginMode'] {
const normalized = (mode ?? '').toString().trim().toUpperCase()
if (normalized === 'SAME_PLATFORM_ONE' || normalized === 'SINGLE_DEVICE' || normalized === 'MULTI_DEVICE_FREE') {
return normalized as ImServiceConfig['multiDeviceLoginMode']
}
return 'MULTI_DEVICE_FREE'
}
function normalizeFriendRequestMode(
mode: ImServiceConfig['friendRequestMode'] | string | undefined,
allowFriendRequest: boolean | undefined,
@ -252,6 +294,11 @@ onMounted(loadData)
.service-name { font-size: 15px; }
.service-config-list { display: flex; flex-direction: column; gap: 12px; }
.service-config-row { display: flex; justify-content: space-between; align-items: center; gap: 12px; font-size: 13px; color: #666; }
.service-config-row--column { flex-direction: column; align-items: flex-start; }
.service-config-title { font-size: 13px; font-weight: 500; color: #303133; line-height: 1.4; }
.service-config-desc { margin-top: 4px; font-size: 12px; color: #909399; line-height: 1.4; }
.login-mode-group { display: flex; flex-direction: column; gap: 12px; margin-top: 12px; }
.login-mode-group :deep(.el-radio) { height: auto; align-items: flex-start; margin-right: 0; }
.login-mode-group :deep(.el-radio__label) { white-space: normal; font-size: 13px; font-weight: 500; color: #303133; }
.radio-desc { font-size: 12px; color: #909399; font-weight: 400; line-height: 1.5; margin-top: 2px; }
</style>

查看文件

@ -34,6 +34,13 @@
<el-card>
<template #header>厂商配置</template>
<el-divider content-position="left">通知通道</el-divider>
<el-alert
description="通知通道定义 Android 通知渠道,以及 iOS/鸿蒙 对应的分类配置。业务键Key为业务层自定义的唯一标识,代码内部用于引用此通道,例如 im_message;Channel ID 需与 App manifest 中声明的 NotificationChannel ID 完全一致;版本号在渠道参数变更时自增,客户端将以新版本号重新注册渠道。"
type="info"
:closable="false"
:show-icon="false"
style="margin-bottom:12px"
/>
<el-table :data="pushConfig.channels" border style="margin-bottom:16px">
<el-table-column label="业务键" min-width="130">
<template #default="{ row }">
@ -80,6 +87,13 @@
<el-button style="margin-bottom:20px" @click="addChannel">新增通道</el-button>
<el-divider content-position="left">业务分类路由</el-divider>
<el-alert
description="业务分类路由将系统内置的推送类型(如 IM 消息、好友请求、系统通知)映射到对应通道和优先级,决定推送到达时的展示行为。"
type="info"
:closable="false"
:show-icon="false"
style="margin-bottom:12px"
/>
<el-table :data="routeRows" border style="margin-bottom:20px">
<el-table-column label="业务分类" width="160">
<template #default="{ row }">{{ row.type }}</template>