diff --git a/digital-avatar-app/src/api/index.ts b/digital-avatar-app/src/api/index.ts
index 47acede..cd92827 100644
--- a/digital-avatar-app/src/api/index.ts
+++ b/digital-avatar-app/src/api/index.ts
@@ -299,4 +299,13 @@ export const getCurrentUser = () =>
export const logoutUser = () =>
request.post('/huihui/logout')
+// 更新接管配置
+export const updateTakeoverConfig = (avatarId: string, data: {
+ authorizationId: string
+ takeoverEnabled: boolean
+ takeoverMode?: 'immediate' | 'delayed'
+ takeoverDelaySeconds?: number
+}) =>
+ request.put(`/avatar/${avatarId}/authorizations/takeover`, data)
+
export default request
diff --git a/digital-avatar-app/src/views/AuthorizationManage.vue b/digital-avatar-app/src/views/AuthorizationManage.vue
index d805da6..6f33ecb 100644
--- a/digital-avatar-app/src/views/AuthorizationManage.vue
+++ b/digital-avatar-app/src/views/AuthorizationManage.vue
@@ -30,6 +30,46 @@
{{ auth.status === 'active' ? '撤销' : '授权' }}
+
+
+
+
+
+ 已启用
+ {{ auth.takeoverMode === 'immediate' ? '立即接管' : '延迟 ' + (auth.takeoverDelaySeconds || 30) + 's' }}
+
+
@@ -47,7 +87,7 @@
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { useAvatarStore } from '@/store/avatar'
-import { getAuthorizationList, updateAuthorization } from '@/api'
+import { getAuthorizationList, updateAuthorization, updateTakeoverConfig } from '@/api'
import { pickAvatarId, unwrapListData } from '@/utils/avatar-page-data.js'
const router = useRouter()
@@ -60,8 +100,14 @@ const authList = ref>([])
+// 接管配置编辑状态(每行一个)
+const editingTakeover = ref>({})
+
// 从后端加载授权列表
const loadAuth = async () => {
try {
@@ -137,6 +183,38 @@ const goBack = () => {
router.back()
}
+// 切换接管配置编辑状态
+const toggleTakeoverEdit = (auth: any) => {
+ editingTakeover.value[auth.id] = !editingTakeover.value[auth.id]
+}
+
+// 保存接管配置
+const saveTakeoverConfig = async (auth: any) => {
+ const avatarId = pickAvatarId(avatarStore.currentAvatarId, avatarStore.avatars)
+ if (!avatarId) return
+
+ const delay = auth.takeoverMode === 'delayed' ? (auth.takeoverDelaySeconds ?? 30) : undefined
+
+ try {
+ await updateTakeoverConfig(avatarId, {
+ authorizationId: auth.id,
+ takeoverEnabled: auth.takeoverEnabled ?? false,
+ takeoverMode: auth.takeoverMode || 'immediate',
+ takeoverDelaySeconds: delay,
+ })
+ editingTakeover.value[auth.id] = false
+ } catch (e: any) {
+ alert('保存接管配置失败:' + (e?.message || '请重试'))
+ }
+}
+
+// 取消编辑接管配置
+const cancelTakeoverEdit = (auth: any) => {
+ // 重新加载以恢复原始值
+ loadAuth()
+ editingTakeover.value[auth.id] = false
+}
+
onMounted(() => {
loadAuth()
})
@@ -321,4 +399,172 @@ onMounted(() => {
font-weight: 600;
cursor: pointer;
}
+
+/* 接管配置 */
+.takeover-section {
+ margin-top: 8px;
+ padding-top: 12px;
+ border-top: 1px solid #F3F4F6;
+ width: 100%;
+}
+
+.takeover-toggle-btn {
+ background: none;
+ border: none;
+ color: #6366F1;
+ font-size: 12px;
+ font-weight: 500;
+ cursor: pointer;
+ padding: 4px 0;
+}
+
+.takeover-form {
+ margin-top: 10px;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ background: #FAFBFC;
+ border-radius: 8px;
+ padding: 12px;
+}
+
+.takeover-row {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+
+.takeover-label {
+ font-size: 13px;
+ color: #374151;
+ font-weight: 500;
+ min-width: 72px;
+ flex-shrink: 0;
+}
+
+/* 切换开关 */
+.toggle-switch {
+ position: relative;
+ display: inline-block;
+ width: 40px;
+ height: 22px;
+ flex-shrink: 0;
+}
+
+.toggle-switch input {
+ opacity: 0;
+ width: 0;
+ height: 0;
+}
+
+.toggle-slider {
+ position: absolute;
+ cursor: pointer;
+ inset: 0;
+ background: #D1D5DB;
+ border-radius: 22px;
+ transition: background 0.2s;
+}
+
+.toggle-slider::before {
+ content: '';
+ position: absolute;
+ height: 16px;
+ width: 16px;
+ left: 3px;
+ bottom: 3px;
+ background: white;
+ border-radius: 50%;
+ transition: transform 0.2s;
+}
+
+.toggle-switch input:checked + .toggle-slider {
+ background: #6366F1;
+}
+
+.toggle-switch input:checked + .toggle-slider::before {
+ transform: translateX(18px);
+}
+
+.takeover-select {
+ flex: 1;
+ padding: 6px 10px;
+ border: 1px solid #E5E7EB;
+ border-radius: 6px;
+ font-size: 13px;
+ color: #374151;
+ background: white;
+ outline: none;
+}
+
+.takeover-select:disabled {
+ background: #F3F4F6;
+ color: #9CA3AF;
+}
+
+.takeover-input {
+ flex: 1;
+ padding: 6px 10px;
+ border: 1px solid #E5E7EB;
+ border-radius: 6px;
+ font-size: 13px;
+ color: #374151;
+ background: white;
+ outline: none;
+ width: 80px;
+}
+
+.takeover-actions {
+ display: flex;
+ gap: 8px;
+ justify-content: flex-end;
+ margin-top: 4px;
+}
+
+.takeover-cancel-btn {
+ padding: 6px 16px;
+ border-radius: 6px;
+ font-size: 12px;
+ font-weight: 500;
+ cursor: pointer;
+ border: 1px solid #E5E7EB;
+ background: white;
+ color: #6B7280;
+}
+
+.takeover-save-btn {
+ padding: 6px 16px;
+ border-radius: 6px;
+ font-size: 12px;
+ font-weight: 500;
+ cursor: pointer;
+ border: none;
+ background: #6366F1;
+ color: white;
+}
+
+/* 接管配置摘要 */
+.takeover-summary {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ margin-top: 6px;
+}
+
+.takeover-badge {
+ padding: 2px 8px;
+ border-radius: 4px;
+ font-size: 11px;
+ font-weight: 600;
+}
+
+.takeover-badge.enabled {
+ background: #EEF2FF;
+ color: #6366F1;
+}
+
+.takeover-mode-text {
+ font-size: 12px;
+ color: #6B7280;
+}