feat(avatar): add BOXIM chat takeover

This commit is contained in:
stefanfeng
2026-08-19 17:56:57 +08:00
parent cfcfe7146e
commit 25fb8fbee5
15 changed files with 1340 additions and 880 deletions
@@ -27,6 +27,7 @@
v-for="item in permissionItems"
:key="item.key"
class="permission-row"
:class="{ takeover: item.key === 'takeover' }"
type="button"
role="switch"
:aria-checked="permissionState[item.key]"
@@ -51,14 +52,18 @@
<rect x="4" y="14" width="6" height="6" rx="1" />
<rect x="14" y="14" width="6" height="6" rx="1" />
</svg>
<svg v-else viewBox="0 0 24 24">
<svg v-else-if="item.key === 'interact'" viewBox="0 0 24 24">
<path d="M7 10v10H4a2 2 0 0 1-2-2v-6a2 2 0 0 1 2-2h3Zm0 10h9.5a2 2 0 0 0 1.9-1.4l2.3-7A2 2 0 0 0 18.8 9H14l.8-3.2A2.3 2.3 0 0 0 12.6 3L7 10v10Z" />
</svg>
<svg v-else viewBox="0 0 24 24">
<path d="M20 13.5a4.5 4.5 0 0 1-4.5 4.5H9l-5 3 1.4-4.5A6.5 6.5 0 0 1 3 11.4V8.5A4.5 4.5 0 0 1 7.5 4H14" />
<path d="M18 3v5m-2.5-2.5h5" />
</svg>
</span>
<span class="permission-copy">
<strong>{{ item.title }}</strong>
<small>{{ item.description }}</small>
<small>{{ item.key === 'takeover' ? takeoverDescription : item.description }}</small>
</span>
<span class="permission-switch" :class="{ active: permissionState[item.key] }" aria-hidden="true">
@@ -96,12 +101,14 @@
</template>
<script setup lang="ts">
import { onMounted, reactive, ref } from 'vue'
import { computed, onMounted, reactive, ref } from 'vue'
import { useRouter } from 'vue-router'
import {
getAvatarPermissionSettings,
getTakeoverStatus,
updateAvatarPermissionSettings,
type AvatarPermission,
type TakeoverStatus,
} from '@/api'
import { useAvatarStore } from '@/store/avatar'
import { pickAvatarId } from '@/utils/avatar-page-data.js'
@@ -147,6 +154,12 @@ const permissionItems: Array<{
description: '点赞、收藏、评论、回复等操作',
tone: 'pink',
},
{
key: 'takeover',
title: '分身主动接管聊天回复',
description: '收到私聊消息 3 秒后回复,主人发言时暂停',
tone: 'cyan',
},
]
const permissionState = reactive<PermissionState>({
@@ -155,14 +168,21 @@ const permissionState = reactive<PermissionState>({
publish: false,
browse: false,
interact: false,
takeover: false,
})
const activeAvatarId = ref('')
const loading = ref(true)
const saving = ref(false)
const errorMessage = ref('')
const toastMessage = ref('')
const takeoverStatus = ref<TakeoverStatus | null>(null)
let toastTimer: number | undefined
const takeoverDescription = computed(() => {
if (!permissionState.takeover) return '收到私聊消息 3 秒后回复,主人发言时暂停'
return takeoverStatus.value?.message || '正在连接 BOXIM'
})
const setPermissions = (permissions: AvatarPermission[]) => {
const enabled = new Set(permissions)
for (const item of permissionItems) permissionState[item.key] = enabled.has(item.key)
@@ -196,6 +216,7 @@ const loadSettings = async () => {
try {
const settings = await getAvatarPermissionSettings(activeAvatarId.value)
setPermissions(settings.permissions || [])
await loadTakeoverStatus()
} catch (error: any) {
errorMessage.value = readableError(error, '授权设置加载失败,请稍后重试')
} finally {
@@ -203,6 +224,15 @@ const loadSettings = async () => {
}
}
const loadTakeoverStatus = async () => {
if (!activeAvatarId.value) return
try {
takeoverStatus.value = await getTakeoverStatus(activeAvatarId.value)
} catch {
takeoverStatus.value = null
}
}
const saveSettings = async () => {
if (!activeAvatarId.value || loading.value || saving.value) return
saving.value = true
@@ -210,6 +240,8 @@ const saveSettings = async () => {
try {
const settings = await updateAvatarPermissionSettings(activeAvatarId.value, selectedPermissions())
setPermissions(settings.permissions || [])
await loadTakeoverStatus()
if (permissionState.takeover) window.setTimeout(loadTakeoverStatus, 1400)
showToast('授权设置已保存')
} catch (error: any) {
errorMessage.value = readableError(error, '授权设置保存失败,请重试')
@@ -363,6 +395,10 @@ svg {
background: #fcfcfd;
}
.permission-row.takeover {
min-height: 76px;
}
.permission-icon {
width: 34px;
height: 34px;
@@ -400,6 +436,11 @@ svg {
background: #fff3f7;
}
.permission-icon.cyan {
color: #35a6a2;
background: #effaf9;
}
.permission-copy {
min-width: 0;
}
@@ -427,6 +468,15 @@ svg {
white-space: nowrap;
}
.permission-row.takeover .permission-copy small {
display: -webkit-box;
overflow: hidden;
text-overflow: ellipsis;
white-space: normal;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
.permission-switch {
position: relative;
width: 43px;