@tencent-rtc/trtc-agent-skills 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +172 -0
- package/README.zh.md +173 -0
- package/bin/cli.js +434 -0
- package/knowledge-base/index.yaml +454 -0
- package/knowledge-base/platform-slice-template.md +233 -0
- package/knowledge-base/scenario-spec.md +350 -0
- package/knowledge-base/scenarios/conference/base/general-conference.md +365 -0
- package/knowledge-base/scenarios/conference/base/webinar-conference.md +130 -0
- package/knowledge-base/scenarios/conference/medical/1v1-video-consultation.md +145 -0
- package/knowledge-base/scenarios/conference/medical/medical-multidoctor-consultation.md +113 -0
- package/knowledge-base/scenarios/live/entertainment-live-room.md +118 -0
- package/knowledge-base/slice-spec.md +546 -0
- package/knowledge-base/slices/conference/web/ai-tools.md +225 -0
- package/knowledge-base/slices/conference/web/beauty-effects.md +188 -0
- package/knowledge-base/slices/conference/web/device-control.md +338 -0
- package/knowledge-base/slices/conference/web/login-auth.md +261 -0
- package/knowledge-base/slices/conference/web/network-quality.md +190 -0
- package/knowledge-base/slices/conference/web/official-roomkit-api.md +298 -0
- package/knowledge-base/slices/conference/web/official-roomkit-login-ui.md +246 -0
- package/knowledge-base/slices/conference/web/participant-list.md +238 -0
- package/knowledge-base/slices/conference/web/participant-management.md +718 -0
- package/knowledge-base/slices/conference/web/prejoin-check.md +293 -0
- package/knowledge-base/slices/conference/web/room-call.md +213 -0
- package/knowledge-base/slices/conference/web/room-chat.md +426 -0
- package/knowledge-base/slices/conference/web/room-lifecycle.md +534 -0
- package/knowledge-base/slices/conference/web/room-schedule.md +281 -0
- package/knowledge-base/slices/conference/web/screen-share.md +211 -0
- package/knowledge-base/slices/conference/web/video-layout.md +675 -0
- package/knowledge-base/slices/conference/web/virtual-background.md +197 -0
- package/knowledge-base/slices/conference/web/webinar-interaction.md +206 -0
- package/knowledge-base/slices/live/anchor-lifecycle.md +122 -0
- package/knowledge-base/slices/live/anchor-preview.md +90 -0
- package/knowledge-base/slices/live/anchor-room-config.md +104 -0
- package/knowledge-base/slices/live/audience-list.md +86 -0
- package/knowledge-base/slices/live/audience-manage.md +92 -0
- package/knowledge-base/slices/live/audience-watch.md +85 -0
- package/knowledge-base/slices/live/audio.md +116 -0
- package/knowledge-base/slices/live/barrage.md +88 -0
- package/knowledge-base/slices/live/beauty.md +99 -0
- package/knowledge-base/slices/live/coguest-apply.md +105 -0
- package/knowledge-base/slices/live/device-control.md +91 -0
- package/knowledge-base/slices/live/error-codes.md +167 -0
- package/knowledge-base/slices/live/gift.md +84 -0
- package/knowledge-base/slices/live/ios/.gitkeep +0 -0
- package/knowledge-base/slices/live/ios/anchor-lifecycle.md +313 -0
- package/knowledge-base/slices/live/ios/anchor-preview.md +228 -0
- package/knowledge-base/slices/live/ios/anchor-room-config.md +257 -0
- package/knowledge-base/slices/live/ios/audience-list.md +353 -0
- package/knowledge-base/slices/live/ios/audience-manage.md +381 -0
- package/knowledge-base/slices/live/ios/audience-watch.md +286 -0
- package/knowledge-base/slices/live/ios/audio.md +373 -0
- package/knowledge-base/slices/live/ios/barrage.md +285 -0
- package/knowledge-base/slices/live/ios/beauty.md +323 -0
- package/knowledge-base/slices/live/ios/coguest-apply.md +506 -0
- package/knowledge-base/slices/live/ios/device-control.md +286 -0
- package/knowledge-base/slices/live/ios/error-codes.md +270 -0
- package/knowledge-base/slices/live/ios/gift.md +315 -0
- package/knowledge-base/slices/live/ios/live-list.md +269 -0
- package/knowledge-base/slices/live/ios/login-auth.md +247 -0
- package/knowledge-base/slices/live/live-list.md +82 -0
- package/knowledge-base/slices/live/login-auth.md +78 -0
- package/package.json +34 -0
- package/skills/trtc/SKILL.md +326 -0
- package/skills/trtc/room-builder/SKILL.md +138 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/README.md +108 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/docs/backend-contract.zh-CN.md +162 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/docs/integration.zh-CN.md +154 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/docs/theme.zh-CN.md +78 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/index.html +12 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/package.json +28 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/postcss.config.js +5 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/App.vue +25 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/ConsultationManagePanel.vue +838 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/LanguageSwitch.vue +102 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/LoadingSpinner.vue +6 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/MedicalAlert.vue +34 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/MedicalBusinessPanel.vue +148 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/MedicalButton.vue +49 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/MedicalConfirmDialog.vue +68 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/MedicalDataPanel.vue +196 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/MedicalRecordPanel.vue +270 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/PrescriptionPanel.vue +363 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/config/basic-info-config.ts +29 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/config/lib-generate-test-usersig-es.min.d.ts +4 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/config/lib-generate-test-usersig-es.min.js +2 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/config/runtime-config.ts +12 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/env.d.ts +32 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/components/ConsultationChatPanel.vue +123 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/components/ConsultationMembersPanel.vue +230 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/components/ConsultationTranscriptionPanel.vue +135 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/components/ConsultationVideoStage.vue +113 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/components/InviteDoctorDialog.vue +132 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/components/KickMemberConfirmDialog.vue +50 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/types.ts +77 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/useConsultationChat.ts +97 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/useConsultationDevices.ts +48 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/useConsultationParticipants.ts +121 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/useConsultationPermissions.ts +25 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/utils.ts +70 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/i18n/en-US/index.ts +553 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/i18n/index.ts +25 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/i18n/medicalTranslate.ts +85 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/i18n/state.ts +49 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/i18n/zh-CN/index.ts +463 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/main.ts +12 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/mock/appointments.ts +96 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/mock/users.ts +79 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/router/index.ts +63 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/services/adapters/index.ts +25 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/services/adapters/integration/appointmentService.ts +77 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/services/adapters/integration/authService.ts +38 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/services/adapters/integration/launchContext.ts +31 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/services/adapters/integration/userService.ts +35 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/services/adapters/mock/appointmentService.ts +43 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/services/adapters/mock/authService.ts +33 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/services/adapters/mock/userService.ts +43 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/services/adapters/types.ts +135 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/shared/icons.ts +53 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/styles/index.css +106 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/styles/tailwind.css +3 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/styles/theme.css +209 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/utils/auth.ts +50 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/utils/format.ts +24 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/utils/navigation.ts +12 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/utils/session.ts +28 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/views/DoctorConsultationView.vue +777 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/views/DoctorDashboardView.vue +678 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/views/LoginView.vue +441 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/views/PatientConsultationFinishedView.vue +185 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/views/PatientConsultationView.vue +1003 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/views/PatientSelectDoctorView.vue +317 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/views/PatientWaitingView.vue +454 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/tsconfig.json +21 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/tsconfig.node.json +8 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation/vite.config.ts +17 -0
- package/skills/trtc/room-builder/templates/scenarios/medical-consultation//346/216/245/345/205/245/350/257/264/346/230/216.md +6 -0
- package/skills/trtc/room-builder/tools/render_ai_instructions.py +226 -0
- package/skills/trtc-apply/SKILL.md +97 -0
- package/skills/trtc-apply/guardrails/apply_lib/__init__.py +0 -0
- package/skills/trtc-apply/guardrails/apply_lib/__pycache__/__init__.cpython-313.pyc +0 -0
- package/skills/trtc-apply/guardrails/apply_lib/__pycache__/rule_parser.cpython-313.pyc +0 -0
- package/skills/trtc-apply/guardrails/apply_lib/rule_parser.py +268 -0
- package/skills/trtc-docs/SKILL.md +207 -0
- package/skills/trtc-onboarding/SKILL.md +839 -0
- package/skills/trtc-onboarding/reference/path-a1-demo.md +103 -0
- package/skills/trtc-onboarding/reference/path-a2-integrate.md +693 -0
- package/skills/trtc-onboarding/reference/path-b-troubleshoot.md +115 -0
- package/skills/trtc-onboarding/reference/path-c-expand.md +43 -0
- package/skills/trtc-onboarding/reference/reporting-protocol.md +174 -0
- package/skills/trtc-onboarding/reference/supported-matrix.md +100 -0
- package/skills/trtc-onboarding/reference/usersig-handling.md +140 -0
- package/skills/trtc-search/SKILL.md +221 -0
- package/skills/trtc-topic/SKILL.md +638 -0
- package/skills/trtc-topic/guardrails/__pycache__/gate_slice_read.cpython-313.pyc +0 -0
- package/skills/trtc-topic/guardrails/__pycache__/gate_slice_write.cpython-313.pyc +0 -0
- package/skills/trtc-topic/guardrails/__pycache__/stop_require_apply_evidence.cpython-313.pyc +0 -0
- package/skills/trtc-topic/guardrails/gate_slice_read.py +133 -0
- package/skills/trtc-topic/guardrails/gate_slice_write.py +169 -0
- package/skills/trtc-topic/guardrails/stop_require_apply_evidence.py +97 -0
- package/skills/trtc-topic/references/execution-units.yaml +58 -0
- package/skills/trtc-topic/runtime/README.md +50 -0
- package/skills/trtc-topic/runtime/RUNTIME.md +128 -0
- package/skills/trtc-topic/runtime/lib/__init__.py +0 -0
- package/skills/trtc-topic/runtime/lib/platforms.py +194 -0
- package/skills/trtc-topic/runtime/package-lock.json +1211 -0
- package/skills/trtc-topic/runtime/package.json +13 -0
- package/skills/trtc-topic/runtime/telemetry-bridge.mjs +339 -0
- package/skills/trtc-topic/runtime/telemetry_collector.py +293 -0
- package/skills/trtc-topic/scripts/STATE-MACHINE-GUIDE.md +186 -0
- package/skills/trtc-topic/scripts/__pycache__/apply.cpython-313.pyc +0 -0
- package/skills/trtc-topic/scripts/apply.py +581 -0
- package/skills/trtc-topic/scripts/finalize_session.py +113 -0
- package/skills/trtc-topic/scripts/init_slice_queue.py +96 -0
- package/skills/trtc-topic/scripts/lib/__pycache__/state_machine.cpython-313.pyc +0 -0
- package/skills/trtc-topic/scripts/lib/state_machine.py +328 -0
- package/skills/trtc-topic/scripts/next_slice.py +137 -0
- package/skills/trtc-topic/tests/README.md +70 -0
- package/skills/trtc-topic/tests/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc +0 -0
- package/skills/trtc-topic/tests/__pycache__/conftest.cpython-313-pytest-9.0.3.pyc +0 -0
- package/skills/trtc-topic/tests/__pycache__/test_apply_cli.cpython-313-pytest-9.0.2.pyc +0 -0
- package/skills/trtc-topic/tests/__pycache__/test_apply_cli.cpython-313-pytest-9.0.3.pyc +0 -0
- package/skills/trtc-topic/tests/__pycache__/test_end_to_end.cpython-313-pytest-9.0.2.pyc +0 -0
- package/skills/trtc-topic/tests/__pycache__/test_end_to_end.cpython-313-pytest-9.0.3.pyc +0 -0
- package/skills/trtc-topic/tests/__pycache__/test_finalize_session.cpython-313-pytest-9.0.2.pyc +0 -0
- package/skills/trtc-topic/tests/__pycache__/test_finalize_session.cpython-313-pytest-9.0.3.pyc +0 -0
- package/skills/trtc-topic/tests/__pycache__/test_gates.cpython-313-pytest-9.0.2.pyc +0 -0
- package/skills/trtc-topic/tests/__pycache__/test_gates.cpython-313-pytest-9.0.3.pyc +0 -0
- package/skills/trtc-topic/tests/__pycache__/test_session_resolver.cpython-313-pytest-9.0.2.pyc +0 -0
- package/skills/trtc-topic/tests/__pycache__/test_session_resolver.cpython-313-pytest-9.0.3.pyc +0 -0
- package/skills/trtc-topic/tests/__pycache__/test_state_machine.cpython-313-pytest-9.0.2.pyc +0 -0
- package/skills/trtc-topic/tests/__pycache__/test_state_machine.cpython-313-pytest-9.0.3.pyc +0 -0
- package/skills/trtc-topic/tests/__pycache__/test_stop_require_apply.cpython-313-pytest-9.0.2.pyc +0 -0
- package/skills/trtc-topic/tests/__pycache__/test_stop_require_apply.cpython-313-pytest-9.0.3.pyc +0 -0
- package/skills/trtc-topic/tests/__pycache__/test_topic_skill_invariants.cpython-313-pytest-9.0.2.pyc +0 -0
- package/skills/trtc-topic/tests/__pycache__/test_topic_skill_invariants.cpython-313-pytest-9.0.3.pyc +0 -0
- package/skills/trtc-topic/tests/conftest.py +72 -0
- package/skills/trtc-topic/tests/test_apply_cli.py +480 -0
- package/skills/trtc-topic/tests/test_end_to_end.py +305 -0
- package/skills/trtc-topic/tests/test_finalize_session.py +51 -0
- package/skills/trtc-topic/tests/test_gates.py +316 -0
- package/skills/trtc-topic/tests/test_session_resolver.py +260 -0
- package/skills/trtc-topic/tests/test_state_machine.py +414 -0
- package/skills/trtc-topic/tests/test_stop_require_apply.py +99 -0
- package/skills/trtc-topic/tests/test_topic_skill_invariants.py +130 -0
|
@@ -0,0 +1,718 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: conference/participant-management
|
|
3
|
+
name: 参会人管理与会控
|
|
4
|
+
product: conference
|
|
5
|
+
platform: web
|
|
6
|
+
tags: [participant, role, admin, owner, kick, transfer, moderation, mute, device-control, message-control, device-request]
|
|
7
|
+
platforms: [web]
|
|
8
|
+
related: [conference/participant-list, conference/room-call, conference/room-lifecycle, conference/room-chat, conference/device-control, conference/screen-share]
|
|
9
|
+
api_docs:
|
|
10
|
+
- title: 成员管理
|
|
11
|
+
url: https://cloud.tencent.com/document/product/647/126927
|
|
12
|
+
business_decisions:
|
|
13
|
+
- key: management_features
|
|
14
|
+
tier: blocking
|
|
15
|
+
multi_select: true
|
|
16
|
+
baseline: ["list"] # 始终生成的基础能力,不作为多选项展示(参会人列表默认提供)
|
|
17
|
+
question: "主持人需要哪些会控权限?(参会人列表默认展示,以下为额外管控能力,可多选)"
|
|
18
|
+
options:
|
|
19
|
+
- { label: "管控单个成员 —— 静音 / 关闭摄像头 / 禁止发言 / 邀请开设备", value: "single-control" }
|
|
20
|
+
- { label: "全场管控 —— 全员静音、全员禁画、全员禁言、设备申请审批", value: "all-room" }
|
|
21
|
+
- { label: "成员身份管理 —— 设置 / 撤销管理员、转让主持人、移出成员(含破坏性操作)", value: "role-and-kick" }
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
# 参会人管理与会控
|
|
25
|
+
|
|
26
|
+
## 功能说明
|
|
27
|
+
|
|
28
|
+
参会人管理与会控负责会议中所有"针对成员"的治理动作——既包括成员级治理(角色调整、移出、名片维护、单成员关闭设备),也包括房间级会控(全员禁麦、全员禁画、全员禁言、设备申请审批与邀请)。它解决的是"谁可以管理谁、可以做什么、规则如何同步、被治理的人如何感知和响应"这一整套问题。它不负责成员列表的纯展示(归属 `conference/participant-list`),也不等同于成员列表组件本身。
|
|
29
|
+
|
|
30
|
+
## 核心概念
|
|
31
|
+
|
|
32
|
+
### 角色与操作
|
|
33
|
+
|
|
34
|
+
| 角色 | 关键操作 | 说明 |
|
|
35
|
+
|------|----------|------|
|
|
36
|
+
| 房主 | 设置 / 撤销管理员、转让房主、移出成员、关闭成员设备、全员禁麦 / 禁画 / 禁言、处理设备申请、邀请成员开启设备 | 通用会议中同一时间通常只有一个房主,是成员治理与会控的最高权限角色 |
|
|
37
|
+
| 管理员 | 移出成员、关闭成员设备、单独 / 全体禁言、全体禁用设备、处理设备申请、邀请成员开启设备 | 由房主授予;可执行大部分治理动作,但不能设置管理员、撤销管理员或转让房主 |
|
|
38
|
+
| 普通成员 | 查看自身角色、维护自身名片、申请开启受限设备、响应管理侧设备邀请 | 不能治理他人,但可维护自己的名片,并在设备受限时发起开启申请或响应邀请 |
|
|
39
|
+
| 设备 / 聊天 / 共享模块 | 消费会控结果 | 禁言、禁设备、禁共享等规则最终会作用到输入区、设备按钮和共享入口 |
|
|
40
|
+
|
|
41
|
+
### 关键治理动作
|
|
42
|
+
|
|
43
|
+
| 动作 | 谁可以发起 | 结果 |
|
|
44
|
+
|------|------------|------|
|
|
45
|
+
| `setAdmin` | 仅房主 | 把普通成员设为管理员 |
|
|
46
|
+
| `revokeAdmin` | 仅房主 | 撤销管理员身份 |
|
|
47
|
+
| `transferOwner` | 仅房主 | 把房主身份转交给其他成员 |
|
|
48
|
+
| `kickParticipant` | 房主 / 管理员 | 移出指定成员 |
|
|
49
|
+
| `closeParticipantDevice` | 房主 / 管理员 | 关闭指定成员的麦克风、摄像头或屏幕共享 |
|
|
50
|
+
| `disableUserMessage` | 房主 / 管理员 | 单独禁言 / 解禁某成员 |
|
|
51
|
+
| `disableAllDevices` | 房主 / 管理员 | 全员禁用 / 解除禁用 麦克风、摄像头或屏幕共享 |
|
|
52
|
+
| `disableAllMessages` | 房主 / 管理员 | 全体禁言 / 解除禁言 |
|
|
53
|
+
| `requestToOpenDevice` | 普通成员 | 申请房主 / 管理员批准开启被禁用的设备 |
|
|
54
|
+
| `approveOpenDeviceRequest` / `rejectOpenDeviceRequest` | 房主 / 管理员 | 处理普通成员的设备开启申请 |
|
|
55
|
+
| `inviteToOpenDevice` | 房主 / 管理员 | 邀请成员开启麦克风、摄像头或屏幕共享 |
|
|
56
|
+
| `acceptOpenDeviceInvitation` / `declineOpenDeviceInvitation` | 普通成员 | 接受 / 拒绝管理侧的设备开启邀请 |
|
|
57
|
+
| `updateParticipantNameCard` | 成员本人 / 房主 / 管理员 | 更新成员名片 |
|
|
58
|
+
| `updateParticipantMetaData` | 业务层按权限控制 | 更新业务扩展字段 |
|
|
59
|
+
|
|
60
|
+
### 事件流
|
|
61
|
+
|
|
62
|
+
| 阶段 | 参与方 | 关键动作 |
|
|
63
|
+
|------|--------|----------|
|
|
64
|
+
| 权限确认 | 当前用户 | 根据本地角色决定治理 / 会控入口的展示与可执行动作 |
|
|
65
|
+
| 发起操作 | 房主 / 管理员 / 普通成员 | 角色调整、移出、关闭设备、全员规则、设备协作或资料维护 |
|
|
66
|
+
| 规则同步 | 房间状态 | 角色变化、成员去留、设备 / 消息禁用结果、待处理申请与邀请同步到所有端 |
|
|
67
|
+
| 成员响应 | 普通成员 | 根据当前限制调整输入和设备操作;必要时发起设备申请或响应邀请 |
|
|
68
|
+
| 结果反馈 | 客户端 / 业务层 | 申请被批准 / 拒绝 / 超时 / 已被他人处理等结果回流到界面 |
|
|
69
|
+
| 规则解除 | 房主 / 管理员 | 取消或调整当前会控状态,并收口相关提示和入口 |
|
|
70
|
+
|
|
71
|
+
### 状态与数据
|
|
72
|
+
|
|
73
|
+
| 数据 / 状态 | 说明 |
|
|
74
|
+
|-------------|------|
|
|
75
|
+
| `localParticipant.role` | 当前用户角色,是治理和会控入口显隐的唯一可信来源 |
|
|
76
|
+
| `participant.role` | 成员角色,用于判断房主、管理员、普通成员 |
|
|
77
|
+
| `participant.microphoneStatus` / `cameraStatus` / `screenShareStatus` | 成员当前设备状态,会控生效后会自动同步 |
|
|
78
|
+
| `participant.isMessageDisabled` | 成员单独禁言状态 |
|
|
79
|
+
| 全员设备禁用状态 | 控制普通成员是否还能自行开启麦克风、摄像头或屏幕共享;房主和管理员通常不受该限制 |
|
|
80
|
+
| 全员消息禁用状态 | 控制普通成员聊天输入区是否可用 |
|
|
81
|
+
| 管理端房间级会控读模型 | 供房主 / 管理员按钮、菜单文案和二次确认提示回显当前全员静音 / 禁画 / 禁言状态;不应只存在于一次点击的临时变量中 |
|
|
82
|
+
| `pendingDeviceApplications` | 当前仍待管理员处理的设备开启申请 |
|
|
83
|
+
| `pendingDeviceInvitations` | 当前仍待成员响应的设备开启邀请 |
|
|
84
|
+
| `nameCard` / `metaData` | 成员名片 / 业务扩展信息 |
|
|
85
|
+
| 当前治理动作结果 | 表示成功、失败、无权限、目标已离会等结果 |
|
|
86
|
+
| 管理员数量限制 | 应以产品配置或服务端规则为准,业务层不要写死 |
|
|
87
|
+
|
|
88
|
+
### 角色能力矩阵
|
|
89
|
+
|
|
90
|
+
| 能力 | 房主 | 管理员 | 普通成员 |
|
|
91
|
+
|------|------|--------|----------|
|
|
92
|
+
| 查看成员列表 | 支持 | 支持 | 支持 |
|
|
93
|
+
| 修改自己的名片 | 支持 | 支持 | 支持 |
|
|
94
|
+
| 修改任意成员名片 | 支持 | 支持 | 不支持 |
|
|
95
|
+
| 设置 / 撤销管理员 | 支持 | 不支持 | 不支持 |
|
|
96
|
+
| 转让房主 | 支持 | 不支持 | 不支持 |
|
|
97
|
+
| 解散房间 | 支持 | 不支持 | 不支持 |
|
|
98
|
+
| 移出成员 | 支持 | 支持 | 不支持 |
|
|
99
|
+
| 关闭指定成员设备 | 支持 | 支持 | 不支持 |
|
|
100
|
+
| 全员禁用设备 | 支持 | 支持 | 不支持 |
|
|
101
|
+
| 单独禁言 | 支持 | 支持 | 不支持 |
|
|
102
|
+
| 全体禁言 | 支持 | 支持 | 不支持 |
|
|
103
|
+
| 处理设备开启申请 | 支持 | 支持 | 不支持 |
|
|
104
|
+
| 邀请成员开启设备 | 支持 | 支持 | 不支持 |
|
|
105
|
+
| 申请开启受限设备 | 不需要 | 不需要 | 支持 |
|
|
106
|
+
|
|
107
|
+
### 状态机
|
|
108
|
+
|
|
109
|
+
```text
|
|
110
|
+
idle
|
|
111
|
+
→ role-checked
|
|
112
|
+
→ applying-action
|
|
113
|
+
→ waiting-sync
|
|
114
|
+
→ synced
|
|
115
|
+
→ idle
|
|
116
|
+
|
|
117
|
+
applying-action
|
|
118
|
+
→ denied
|
|
119
|
+
→ failed
|
|
120
|
+
→ idle
|
|
121
|
+
|
|
122
|
+
room-default
|
|
123
|
+
→ moderation-applied
|
|
124
|
+
→ participant-restricted
|
|
125
|
+
→ request-or-invitation-pending
|
|
126
|
+
→ result-synced
|
|
127
|
+
→ moderation-released
|
|
128
|
+
→ room-default
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## 前置条件
|
|
132
|
+
**通用依赖**:见 [login-auth 平台 slice](login-auth.md)。
|
|
133
|
+
|
|
134
|
+
**额外依赖**:
|
|
135
|
+
- 已安装 `tuikit-atomicx-vue3@latest`
|
|
136
|
+
|
|
137
|
+
**前置状态**:
|
|
138
|
+
- 已阅读 `conference/participant-management`,明确当前能力的产品边界。
|
|
139
|
+
- 已完成 `conference/login-auth`,确保当前页面具备稳定登录态。
|
|
140
|
+
- 已根据业务流程接入会议上下文;需要房间状态时,优先通过 `conference/room-lifecycle` 统一承接。
|
|
141
|
+
|
|
142
|
+
## 最佳实践
|
|
143
|
+
|
|
144
|
+
### ✅ ALWAYS
|
|
145
|
+
|
|
146
|
+
1. **同时在界面层和调用层做权限校验** —— 普通成员不可见的治理 / 会控入口应在界面层隐藏;接口调用仍要处理无权限、目标已离会、申请或邀请超时等失败情况。
|
|
147
|
+
2. **统一通过 `participant.role` 或 `localParticipant.role` 判断角色** —— 不要依赖 `adminList`、缓存字段或展示文案。
|
|
148
|
+
3. **把成员级治理与房间级会控分层处理** —— 设置管理员、转让房主、移出成员、关闭单成员设备属于成员级;全员禁麦 / 禁画 / 禁言属于房间级;两者表达方式和恢复路径不同,但同属于本 slice。
|
|
149
|
+
4. **把创建期默认规则与会中动态会控分开** —— 创建期默认禁麦 / 禁画 / 禁聊归属 `conference/room-lifecycle`;会中动态调整归属本 slice。
|
|
150
|
+
5. **让房主 / 管理员的房间级会控按钮体现当前状态并支持 toggle** —— "全员静音 / 解除全员静音"、"全员禁言 / 解除全员禁言"应围绕当前状态成对出现,不要只提供单向"开启限制"。
|
|
151
|
+
6. **让管理端按钮状态从可恢复的房间级状态源初始化** —— 页面刷新、重进房或管理面板重新挂载后,应从房间状态、`roomInfo` 映射或业务侧会控快照中恢复,而不是默认回到未禁用态。
|
|
152
|
+
7. **让会控结果直接作用到真实交互路径** —— 禁言要禁用输入区、禁设备要限制真实开启动作、禁共享要限制共享入口,而不是只改文案。
|
|
153
|
+
8. **让治理与会控结果都回流到界面** —— 角色变化、成员离会、设备被关闭、申请被处理、邀请被接受 / 拒绝后,成员列表、入口显隐、待处理列表和提示信息都应同步更新。
|
|
154
|
+
9. **明确 `disableAllDevices` 的作用范围** —— 房间级规则,可作用于麦克风、摄像头和屏幕分享;通常只约束普通成员,房主和管理员不受该限制。
|
|
155
|
+
10. **明确 `disableAllMessages` 的作用范围** —— 通常只约束普通成员;房主和管理员仍需保留必要的会中管理与沟通能力。
|
|
156
|
+
11. **区分房间级禁用与单成员关闭的恢复路径** —— 房间级禁用期间,普通成员入口表现为 disabled,按产品需要走申请开启链路;单成员关闭只是一次"纠正当前状态"的动作,没有叠加房间级禁用时,成员获得提示后仍可再次主动开启。
|
|
157
|
+
12. **对设备申请和邀请做好超时、取消和重复处理收口** —— 多管理员协作时,申请或邀请可能已被他人处理,界面应及时关闭无效入口。
|
|
158
|
+
13. **管理员数量限制和业务字段规则来自产品配置或服务端约束** —— 不要在客户端写死上限;`metaData` 字段含义和展示规则应提前约定。
|
|
159
|
+
|
|
160
|
+
### ❌ NEVER
|
|
161
|
+
|
|
162
|
+
1. **不要把成员级治理和房间级会控看成两个完全不同的系统** —— 它们应统一在一个治理面板里被设计和使用,但要清楚标注每个动作影响的是单成员还是全房间。
|
|
163
|
+
2. **不要依赖 `adminList`、本地缓存或展示文案判断角色** —— 通用会议不单独维护管理员列表,角色判断应回到成员状态本身。
|
|
164
|
+
3. **不要把全员会控按钮做成只有"开启"没有"解除"的单向动作** —— 真实产品中的全员静音 / 禁画 / 禁言都应支持回退。
|
|
165
|
+
4. **不要只在管理端成功提示,不处理成员端反馈** —— 被治理 / 被会控的用户必须看到清晰反馈,否则会误以为是本地故障。
|
|
166
|
+
5. **不要把全员规则状态只存在当前页面内存中** —— 页面刷新后会导致管理端 UI 与真实房间规则脱节。
|
|
167
|
+
6. **不要在房间级禁用仍生效时让普通成员直接调用开启能力** —— 这会绕开会控语义,并产生"点了没反应"的混乱体验。
|
|
168
|
+
7. **不要让普通成员暴露房间级会控入口** —— 会控能力应严格受房主 / 管理员权限边界约束。
|
|
169
|
+
8. **不要把一次点击或一次接口返回当成最终结果** —— 仍需等待角色变化、成员去留、设备协作结果等真实状态回流。
|
|
170
|
+
9. **不要把 `metaData` 当成无结构字符串自由拼接** —— 业务侧应提前约定字段含义和展示方式。
|
|
171
|
+
|
|
172
|
+
## 业务决策与代码生成的对应关系
|
|
173
|
+
|
|
174
|
+
`business_decisions.management_features` 的多选结果直接决定生成代码的范围。`list` 是 **baseline(基础项)**:始终生成,不作为多选项让用户勾选;`single-control` / `all-room` / `role-and-kick` 是用户多选的额外管控能力。
|
|
175
|
+
|
|
176
|
+
| 档位 | 生成的 API 与 UI |
|
|
177
|
+
|---|---|
|
|
178
|
+
| `list`(baseline,始终生成) | `participantList` / `getParticipantList` (含 cursor 分页) / `localParticipant` / `speakingUsers` / `participantWithScreen`;只读列表 + 角色 / 设备状态展示 |
|
|
179
|
+
| `single-control` | `closeParticipantDevice` (Mic/Camera/Screen) / `inviteToOpenDevice` / `disableUserMessage`;成员行右侧"⋯"菜单出现"关 TA 设备 / 邀请打开 / 单独禁言"等条目 |
|
|
180
|
+
| `all-room` | `disableAllDevices` (Mic/Camera) / `disableAllMessages` / `pendingDeviceApplications` / `approveOpenDeviceRequest` / `rejectOpenDeviceRequest`;面板顶部出现全员控制 toggle 与待审批列表 |
|
|
181
|
+
| `role-and-kick` | `setAdmin` / `revokeAdmin` / `transferOwner` / `kickParticipant`;成员菜单出现高风险条目,移出 / 转让需二次确认 |
|
|
182
|
+
|
|
183
|
+
`list` 因为是 baseline,始终生成;其余三档中未勾选的,对应 API 不导出、不导入、对应 UI 入口不渲染(按 topic G8 规则)。
|
|
184
|
+
|
|
185
|
+
## 角色权限速览
|
|
186
|
+
|
|
187
|
+
| 角色 | 枚举值 | 可执行 |
|
|
188
|
+
|------|--------|--------|
|
|
189
|
+
| 房主 | `RoomParticipantRole.Owner` | 所有治理与会控动作 |
|
|
190
|
+
| 管理员 | `RoomParticipantRole.Admin` | 移出成员、关闭成员设备、单独 / 全体禁言、全体禁用设备、处理设备申请、邀请成员开启设备;不可设管理员、撤销管理员、转让房主 |
|
|
191
|
+
| 普通成员 | `RoomParticipantRole.GeneralUser` | 查看成员列表、修改自己的名片、申请开启被禁用的设备 |
|
|
192
|
+
|
|
193
|
+
> 详细能力矩阵见产品级 slice。前端权限校验只用于 UI 显隐;接口调用仍要 `try/catch` 处理无权限错误。
|
|
194
|
+
|
|
195
|
+
## 代码示例
|
|
196
|
+
|
|
197
|
+
### 成员列表加载与渲染(`list` 档基础)
|
|
198
|
+
|
|
199
|
+
```ts
|
|
200
|
+
import { onMounted, watch } from 'vue';
|
|
201
|
+
import { useRoomParticipantState, useRoomState } from 'tuikit-atomicx-vue3/room';
|
|
202
|
+
|
|
203
|
+
const { currentRoom } = useRoomState();
|
|
204
|
+
const {
|
|
205
|
+
participantList,
|
|
206
|
+
participantListCursor,
|
|
207
|
+
localParticipant,
|
|
208
|
+
getParticipantList,
|
|
209
|
+
} = useRoomParticipantState();
|
|
210
|
+
|
|
211
|
+
onMounted(() => {
|
|
212
|
+
watch(
|
|
213
|
+
() => currentRoom.value?.roomId,
|
|
214
|
+
async (roomId) => {
|
|
215
|
+
if (!roomId) return;
|
|
216
|
+
try {
|
|
217
|
+
await getParticipantList({ cursor: '' });
|
|
218
|
+
} catch (error) {
|
|
219
|
+
console.error('获取成员列表失败:', error);
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
{ immediate: true },
|
|
223
|
+
);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
async function loadMoreParticipants() {
|
|
227
|
+
if (!participantListCursor.value) return;
|
|
228
|
+
try {
|
|
229
|
+
await getParticipantList({ cursor: participantListCursor.value });
|
|
230
|
+
} catch (error) {
|
|
231
|
+
console.error('加载更多成员失败:', error);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### 角色与设备状态展示
|
|
237
|
+
|
|
238
|
+
```ts
|
|
239
|
+
import {
|
|
240
|
+
DeviceStatus,
|
|
241
|
+
RoomParticipant,
|
|
242
|
+
RoomParticipantRole,
|
|
243
|
+
useRoomParticipantState,
|
|
244
|
+
} from 'tuikit-atomicx-vue3/room';
|
|
245
|
+
|
|
246
|
+
const { localParticipant } = useRoomParticipantState();
|
|
247
|
+
|
|
248
|
+
function getRoleText(role: RoomParticipantRole) {
|
|
249
|
+
if (role === RoomParticipantRole.Owner) return '房主';
|
|
250
|
+
if (role === RoomParticipantRole.Admin) return '管理员';
|
|
251
|
+
return '成员';
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function isCameraOn(participant: RoomParticipant) {
|
|
255
|
+
return participant.cameraStatus === DeviceStatus.On;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function isMicrophoneOn(participant: RoomParticipant) {
|
|
259
|
+
return participant.microphoneStatus === DeviceStatus.On;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function isLocalUser(userId: string) {
|
|
263
|
+
return userId === localParticipant.value?.userId;
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### 发言态、屏幕共享态
|
|
268
|
+
|
|
269
|
+
```ts
|
|
270
|
+
import { useRoomParticipantState } from 'tuikit-atomicx-vue3/room';
|
|
271
|
+
|
|
272
|
+
const {
|
|
273
|
+
speakingUsers,
|
|
274
|
+
participantListWithVideo,
|
|
275
|
+
participantWithScreen,
|
|
276
|
+
} = useRoomParticipantState();
|
|
277
|
+
|
|
278
|
+
function isSpeaking(userId: string) {
|
|
279
|
+
return speakingUsers.value.has(userId);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function getVolume(userId: string) {
|
|
283
|
+
return speakingUsers.value.get(userId) || 0;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function isSharingScreen(userId: string) {
|
|
287
|
+
return participantWithScreen.value?.userId === userId;
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### 名片与扩展字段
|
|
292
|
+
|
|
293
|
+
```ts
|
|
294
|
+
import { useRoomParticipantState } from 'tuikit-atomicx-vue3/room';
|
|
295
|
+
|
|
296
|
+
const {
|
|
297
|
+
updateParticipantNameCard,
|
|
298
|
+
updateParticipantMetaData,
|
|
299
|
+
} = useRoomParticipantState();
|
|
300
|
+
|
|
301
|
+
async function updateUserNameCard(userId: string, nameCard: string) {
|
|
302
|
+
try {
|
|
303
|
+
await updateParticipantNameCard({ userId, nameCard });
|
|
304
|
+
} catch (error) {
|
|
305
|
+
console.error('修改成员名片失败:', error);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
async function updateUserMetaData(userId: string, metaData: Record<string, string>) {
|
|
310
|
+
try {
|
|
311
|
+
await updateParticipantMetaData({ userId, metaData });
|
|
312
|
+
} catch (error) {
|
|
313
|
+
console.error('更新成员扩展信息失败:', error);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### 角色管理(`role-and-kick` 档,仅房主可调用)
|
|
319
|
+
|
|
320
|
+
```ts
|
|
321
|
+
import { computed } from 'vue';
|
|
322
|
+
import { RoomParticipantRole, useRoomParticipantState } from 'tuikit-atomicx-vue3/room';
|
|
323
|
+
|
|
324
|
+
const {
|
|
325
|
+
localParticipant,
|
|
326
|
+
setAdmin,
|
|
327
|
+
revokeAdmin,
|
|
328
|
+
transferOwner,
|
|
329
|
+
kickParticipant,
|
|
330
|
+
} = useRoomParticipantState();
|
|
331
|
+
|
|
332
|
+
const hasOwnerPermission = computed(
|
|
333
|
+
() => localParticipant.value?.role === RoomParticipantRole.Owner,
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
const hasAdminPermission = computed(() => {
|
|
337
|
+
const role = localParticipant.value?.role;
|
|
338
|
+
return role === RoomParticipantRole.Owner || role === RoomParticipantRole.Admin;
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
async function setUserAsAdmin(userId: string) {
|
|
342
|
+
if (!hasOwnerPermission.value) return;
|
|
343
|
+
await setAdmin({ userId });
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
async function revokeUserAdmin(userId: string) {
|
|
347
|
+
if (!hasOwnerPermission.value) return;
|
|
348
|
+
await revokeAdmin({ userId });
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
async function transferRoomOwner(userId: string) {
|
|
352
|
+
if (!hasOwnerPermission.value) return;
|
|
353
|
+
await transferOwner({ userId });
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
async function kickUser(userId: string) {
|
|
357
|
+
if (!hasAdminPermission.value) return;
|
|
358
|
+
await kickParticipant({ userId });
|
|
359
|
+
}
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### 单成员设备 / 消息控制(`single-control` 档)
|
|
363
|
+
|
|
364
|
+
```ts
|
|
365
|
+
import { DeviceType, useRoomParticipantState } from 'tuikit-atomicx-vue3/room';
|
|
366
|
+
|
|
367
|
+
const {
|
|
368
|
+
closeParticipantDevice,
|
|
369
|
+
inviteToOpenDevice,
|
|
370
|
+
disableUserMessage,
|
|
371
|
+
} = useRoomParticipantState();
|
|
372
|
+
|
|
373
|
+
// 关闭指定成员的麦克风 / 摄像头 / 屏幕共享
|
|
374
|
+
async function closeUserMicrophone(userId: string) {
|
|
375
|
+
await closeParticipantDevice({ userId, deviceType: DeviceType.Microphone });
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
async function closeUserCamera(userId: string) {
|
|
379
|
+
await closeParticipantDevice({ userId, deviceType: DeviceType.Camera });
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
async function closeUserScreenShare(userId: string) {
|
|
383
|
+
await closeParticipantDevice({ userId, deviceType: DeviceType.ScreenShare });
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// 邀请成员开启麦克风 / 摄像头 / 屏幕共享
|
|
387
|
+
async function inviteUserToOpenCamera(userId: string) {
|
|
388
|
+
await inviteToOpenDevice({
|
|
389
|
+
userId,
|
|
390
|
+
device: DeviceType.Camera,
|
|
391
|
+
timeout: 60,
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// 单独禁言 / 解禁
|
|
396
|
+
async function disableUserChat(userId: string, disable: boolean) {
|
|
397
|
+
await disableUserMessage({ userId, disable });
|
|
398
|
+
}
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
### 全员会控(`all-room` 档,房主 / 管理员 toggle)
|
|
402
|
+
|
|
403
|
+
```ts
|
|
404
|
+
import { reactive, watch } from 'vue';
|
|
405
|
+
import { DeviceType, useRoomParticipantState, useRoomState } from 'tuikit-atomicx-vue3/room';
|
|
406
|
+
|
|
407
|
+
const { currentRoom } = useRoomState();
|
|
408
|
+
const { disableAllDevices, disableAllMessages } = useRoomParticipantState();
|
|
409
|
+
|
|
410
|
+
// 管理端按钮状态:必须支持回显,否则刷新后丢失
|
|
411
|
+
const moderation = reactive({
|
|
412
|
+
allMicrophoneDisabled: false,
|
|
413
|
+
allCameraDisabled: false,
|
|
414
|
+
allMessagesDisabled: false,
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
watch(
|
|
418
|
+
() => currentRoom.value?.roomId,
|
|
419
|
+
async (roomId) => {
|
|
420
|
+
if (!roomId) {
|
|
421
|
+
moderation.allMicrophoneDisabled = false;
|
|
422
|
+
moderation.allCameraDisabled = false;
|
|
423
|
+
moderation.allMessagesDisabled = false;
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
await hydrateModerationFromBackend(roomId);
|
|
427
|
+
},
|
|
428
|
+
{ immediate: true },
|
|
429
|
+
);
|
|
430
|
+
|
|
431
|
+
async function hydrateModerationFromBackend(_roomId: string) {
|
|
432
|
+
// TODO: 从当前房间状态、roomInfo 映射或业务后端 moderation snapshot 恢复三个布尔值。
|
|
433
|
+
// 没有这一步,刷新后管理端按钮会错误显示为"未禁用",与真实房间规则脱节。
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
async function toggleAllMicrophone() {
|
|
437
|
+
const next = !moderation.allMicrophoneDisabled;
|
|
438
|
+
try {
|
|
439
|
+
await disableAllDevices({ deviceType: DeviceType.Microphone, disable: next });
|
|
440
|
+
moderation.allMicrophoneDisabled = next;
|
|
441
|
+
} catch (error) {
|
|
442
|
+
console.error('切换全员静音失败', error);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
async function toggleAllCamera() {
|
|
447
|
+
const next = !moderation.allCameraDisabled;
|
|
448
|
+
try {
|
|
449
|
+
await disableAllDevices({ deviceType: DeviceType.Camera, disable: next });
|
|
450
|
+
moderation.allCameraDisabled = next;
|
|
451
|
+
} catch (error) {
|
|
452
|
+
console.error('切换全员禁画失败', error);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
async function toggleAllMessages() {
|
|
457
|
+
const next = !moderation.allMessagesDisabled;
|
|
458
|
+
try {
|
|
459
|
+
await disableAllMessages({ disable: next });
|
|
460
|
+
moderation.allMessagesDisabled = next;
|
|
461
|
+
} catch (error) {
|
|
462
|
+
console.error('切换全员禁言失败', error);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
### 设备申请审批(`all-room` 档,管理侧)
|
|
468
|
+
|
|
469
|
+
```ts
|
|
470
|
+
import {
|
|
471
|
+
type DeviceRequestInfo,
|
|
472
|
+
DeviceType,
|
|
473
|
+
RoomParticipantEvent,
|
|
474
|
+
useRoomParticipantState,
|
|
475
|
+
} from 'tuikit-atomicx-vue3/room';
|
|
476
|
+
import { onMounted, onUnmounted } from 'vue';
|
|
477
|
+
|
|
478
|
+
const {
|
|
479
|
+
pendingDeviceApplications,
|
|
480
|
+
approveOpenDeviceRequest,
|
|
481
|
+
rejectOpenDeviceRequest,
|
|
482
|
+
subscribeEvent,
|
|
483
|
+
unsubscribeEvent,
|
|
484
|
+
} = useRoomParticipantState();
|
|
485
|
+
|
|
486
|
+
function getDeviceName(deviceType: DeviceType) {
|
|
487
|
+
if (deviceType === DeviceType.Microphone) return '麦克风';
|
|
488
|
+
if (deviceType === DeviceType.Camera) return '摄像头';
|
|
489
|
+
return '屏幕分享';
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
async function approveDeviceApplication(application: DeviceRequestInfo) {
|
|
493
|
+
await approveOpenDeviceRequest({
|
|
494
|
+
userId: application.senderUserId,
|
|
495
|
+
device: application.deviceType,
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
async function rejectDeviceApplication(application: DeviceRequestInfo) {
|
|
500
|
+
await rejectOpenDeviceRequest({
|
|
501
|
+
userId: application.senderUserId,
|
|
502
|
+
device: application.deviceType,
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
function onDeviceRequestReceived(info: { request: DeviceRequestInfo }) {
|
|
507
|
+
console.log('收到新的设备申请:', getDeviceName(info.request.deviceType));
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
onMounted(() => {
|
|
511
|
+
subscribeEvent(RoomParticipantEvent.onDeviceRequestReceived, onDeviceRequestReceived);
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
onUnmounted(() => {
|
|
515
|
+
unsubscribeEvent(RoomParticipantEvent.onDeviceRequestReceived, onDeviceRequestReceived);
|
|
516
|
+
});
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
### 普通成员申请开启被禁用的设备
|
|
520
|
+
|
|
521
|
+
```ts
|
|
522
|
+
import { DeviceType, useRoomParticipantState } from 'tuikit-atomicx-vue3/room';
|
|
523
|
+
|
|
524
|
+
const { requestToOpenDevice, cancelOpenDeviceRequest } = useRoomParticipantState();
|
|
525
|
+
|
|
526
|
+
async function requestToOpenMicrophone() {
|
|
527
|
+
await requestToOpenDevice({ device: DeviceType.Microphone, timeout: 60 });
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
async function cancelMicrophoneRequest() {
|
|
531
|
+
await cancelOpenDeviceRequest({ device: DeviceType.Microphone });
|
|
532
|
+
}
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
### 事件监听
|
|
536
|
+
|
|
537
|
+
```ts
|
|
538
|
+
import { onMounted, onUnmounted } from 'vue';
|
|
539
|
+
import { RoomParticipantEvent, useRoomParticipantState } from 'tuikit-atomicx-vue3/room';
|
|
540
|
+
|
|
541
|
+
const { subscribeEvent, unsubscribeEvent } = useRoomParticipantState();
|
|
542
|
+
|
|
543
|
+
function onKickedFromRoom({ reason, message }) {
|
|
544
|
+
console.warn('当前用户已被移出房间:', reason, message);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
function onParticipantDeviceClosed({ device, operator }) {
|
|
548
|
+
console.warn('设备已被管理员关闭:', device, operator);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
onMounted(() => {
|
|
552
|
+
subscribeEvent(RoomParticipantEvent.onKickedFromRoom, onKickedFromRoom);
|
|
553
|
+
subscribeEvent(RoomParticipantEvent.onParticipantDeviceClosed, onParticipantDeviceClosed);
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
onUnmounted(() => {
|
|
557
|
+
unsubscribeEvent(RoomParticipantEvent.onKickedFromRoom, onKickedFromRoom);
|
|
558
|
+
unsubscribeEvent(RoomParticipantEvent.onParticipantDeviceClosed, onParticipantDeviceClosed);
|
|
559
|
+
});
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
| **状态 / 事件** | **触发时机** | **处理建议** |
|
|
563
|
+
|---|---|---|
|
|
564
|
+
| `RoomParticipantEvent.onKickedFromRoom` | 当前用户被移出房间 | 跳转回大厅 / 登录页,并清理会中状态 |
|
|
565
|
+
| `RoomParticipantEvent.onParticipantDeviceClosed` | 成员被管理员关闭设备 | toast 告知用户"麦克风 / 摄像头已被管理员关闭" |
|
|
566
|
+
| `RoomParticipantEvent.onDeviceRequestReceived` | 管理员收到成员申请 | 展示审批弹窗或申请入口提醒 |
|
|
567
|
+
| `RoomParticipantEvent.onDeviceRequestCancelled` | 申请者取消申请 | 移除待处理申请 |
|
|
568
|
+
| `RoomParticipantEvent.onDeviceRequestTimeout` | 申请超时 | 提示申请已失效,刷新申请列表 |
|
|
569
|
+
| `RoomParticipantEvent.onDeviceRequestApproved` | 申请被批准 | 申请者可继续打开对应设备 |
|
|
570
|
+
| `RoomParticipantEvent.onDeviceRequestRejected` | 申请被拒绝 | 申请者展示拒绝提示 |
|
|
571
|
+
| `RoomParticipantEvent.onDeviceRequestProcessed` | 申请已被其他管理员处理 | 关闭本地审批入口避免重复处理 |
|
|
572
|
+
| `RoomParticipantEvent.onDeviceInvitationReceived` | 成员收到管理员邀请 | 展示接受 / 拒绝入口 |
|
|
573
|
+
|
|
574
|
+
## 调用时序
|
|
575
|
+
|
|
576
|
+
```
|
|
577
|
+
完成 login-auth 并进入会议
|
|
578
|
+
│
|
|
579
|
+
▼
|
|
580
|
+
读取 localParticipant.role 判断当前用户权限
|
|
581
|
+
│
|
|
582
|
+
├─ 房主 / 管理员 → 展示治理 + 会控入口(按 management_features 决定哪些档)
|
|
583
|
+
│ │
|
|
584
|
+
│ ├─ list 档(baseline,始终生成)→ 拉成员列表 + 分页 + 排序
|
|
585
|
+
│ ├─ single-control 档 → 关 TA 设备 / 邀请打开 / 单独禁言
|
|
586
|
+
│ ├─ all-room 档 → 全员 toggle + 设备申请审批
|
|
587
|
+
│ └─ role-and-kick 档 → 设管理员 / 转让房主 / 移出成员(带二次确认)
|
|
588
|
+
│
|
|
589
|
+
└─ 普通成员 → 只展示成员列表 + 自己的名片维护 + 设备申请入口
|
|
590
|
+
│
|
|
591
|
+
▼
|
|
592
|
+
所有动作的失败结果(无权限 / 目标已离会 / 申请超时)需要 try/catch 兜底
|
|
593
|
+
│
|
|
594
|
+
▼
|
|
595
|
+
房间级会控状态需要在刷新 / 重进房后从后端 / 房间状态恢复
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
## 平台特有注意事项
|
|
599
|
+
|
|
600
|
+
### 1. 权限判断要前后端双重收口
|
|
601
|
+
前端应先隐藏无权限操作提升体验,但真正的权限校验仍要依赖 SDK / 服务端结果,不能只信任页面按钮状态。
|
|
602
|
+
|
|
603
|
+
### 2. 房主转移会影响后续治理边界
|
|
604
|
+
一旦执行 `transferOwner()`,结束会议、修改规则、全员会控等权限边界都会变化,页面状态必须同步切换。
|
|
605
|
+
|
|
606
|
+
### 3. 创建期规则与会中会控要分层
|
|
607
|
+
默认禁麦、禁聊等会议初始规则属于 `conference/room-lifecycle`;会中动态控制才属于本 slice。
|
|
608
|
+
|
|
609
|
+
### 4. 房主 / 管理员端的会控按钮必须是 toggle,而不是单向动作
|
|
610
|
+
`disableAllDevices()` 与 `disableAllMessages()` 都支持通过 `disable: true / false` 在"开启限制"和"解除限制"之间切换。管理端按钮文案、icon 和二次确认文案应围绕当前状态成对出现。
|
|
611
|
+
|
|
612
|
+
### 5. 页面刷新或重进房后,要从房间级状态恢复管理端按钮态
|
|
613
|
+
本地 `ref` 只适合作为当前页面的 UI cache,不适合作为房间级会控状态的唯一来源。进入会议、管理面板重新挂载或页面刷新后,应从当前房间状态、`roomInfo` 映射或业务后端保存的会控快照中恢复管理端按钮态。
|
|
614
|
+
|
|
615
|
+
### 6. 被控制方必须得到清晰反馈
|
|
616
|
+
远程关闭麦克风、摄像头或禁言后,被控制成员需要在本端看到明确提示,否则很容易误以为是本地故障。
|
|
617
|
+
|
|
618
|
+
### 7. 设备与共享入口要同时表达"不可用原因"和"恢复方式"
|
|
619
|
+
普通成员遇到 `disableAllDevices()` 触发的房间级禁用时,设备或共享按钮不应只做成不可点击的灰态而没有解释;至少应给出 toast 或等价提示,并根据产品需求决定是否在点击时调用 `requestToOpenDevice()`。
|
|
620
|
+
|
|
621
|
+
### 8. 单成员关闭与房间级禁用的恢复路径不同
|
|
622
|
+
`closeParticipantDevice()` 是一次"纠正当前状态"的动作,没有叠加房间级禁用时,成员获得提示后可再次主动开启;房间级禁用期间成员入口应表现为 disabled,按需走申请链路。
|
|
623
|
+
|
|
624
|
+
### 9. 成员管理适合与业务角色标签联动
|
|
625
|
+
若业务有"主持人 / 嘉宾 / 观察员"等角色,建议通过 `metaData` 与会议治理能力协同,而不是在 UI 上维护一套脱节状态。
|
|
626
|
+
|
|
627
|
+
## 代码生成约束
|
|
628
|
+
|
|
629
|
+
### 编译必要条件
|
|
630
|
+
- **通用条件**:见 [login-auth 平台 slice](login-auth.md)。
|
|
631
|
+
- **额外导入**:至少需要导入 `useRoomParticipantState`,按需导入 `DeviceType`、`RoomParticipantRole`、`DeviceStatus`、`RoomParticipantEvent`。
|
|
632
|
+
- **运行前提**:当前用户已在会议内,且具备相应角色权限。
|
|
633
|
+
|
|
634
|
+
### 生成规则
|
|
635
|
+
|
|
636
|
+
#### MUST(生成时必须包含)
|
|
637
|
+
|
|
638
|
+
1. **通过 `useRoomParticipantState` 承接所有治理与会控动作** — 成员状态与角色变化才能统一收口。
|
|
639
|
+
**Verify**: 检查是否存在 `useRoomParticipantState()`。
|
|
640
|
+
2. **在展示治理 / 会控入口前先读取本地角色状态** — UI 才能与权限边界一致。
|
|
641
|
+
**Verify**: 检查是否存在基于 `localParticipant.role` / `RoomParticipantRole` 的显示判断。
|
|
642
|
+
3. **角色管理动作(setAdmin / revokeAdmin / transferOwner)只对房主可见可调** — 严格限定。
|
|
643
|
+
**Verify**: 检查是否存在 `localParticipant.role === RoomParticipantRole.Owner` 的前置判断。
|
|
644
|
+
4. **房主 / 管理员端的全员会控按钮必须 toggle,并支持解除** — 不能只展示"全员静音"这类单向动作。
|
|
645
|
+
**Verify**: 检查管理端是否根据当前状态生成"开启限制 / 解除限制"两种文案,且 `disable` 参数会在 `true / false` 之间切换。
|
|
646
|
+
5. **管理端会控按钮状态必须从可恢复的房间级状态源初始化或回填** — 页面刷新、重进房后,管理端仍应能回显当前全员规则。
|
|
647
|
+
**Verify**: 检查是否存在初始化 / 恢复房间级会控状态的逻辑(例如基于 `currentRoom` 的 watch + hydrate 函数)。
|
|
648
|
+
6. **会控结果要联动到聊天 / 设备 / 共享真实入口** — 否则页面会出现"按钮可点但实际被禁用"的错觉。
|
|
649
|
+
**Verify**: 检查聊天输入区、设备开关按钮、共享入口是否消费了对应禁用状态。
|
|
650
|
+
7. **失败兜底处理要覆盖无权限、目标已离会、申请超时等情况** — 接口调用必须 try/catch,不能只信任 UI 显隐。
|
|
651
|
+
**Verify**: 检查是否存在 try/catch + 失败提示。
|
|
652
|
+
|
|
653
|
+
#### MUST NOT(生成时绝不能出现)
|
|
654
|
+
|
|
655
|
+
1. **不要把角色治理动作(setAdmin 等)混进纯展示型成员列表组件** — 会破坏列表层与治理层边界。
|
|
656
|
+
**Verify**: 检查治理逻辑是否独立于只读展示层。
|
|
657
|
+
2. **不要把全员会控按钮做成只下发 `disable: true` 的单向动作** — 真实产品中的全员静音 / 禁画 / 禁言都应支持解除。
|
|
658
|
+
**Verify**: 检查是否缺少 `disable: false` 的解除路径或等价 toggle 分支。
|
|
659
|
+
3. **不要把全员规则状态只存在当前页面内存中** — 页面刷新后会导致管理端 UI 与真实房间规则脱节。
|
|
660
|
+
**Verify**: 检查是否没有任何房间级状态恢复逻辑。
|
|
661
|
+
4. **不要在房间级禁用仍生效时让普通成员直接调用开启能力** — 这会绕开会控语义。
|
|
662
|
+
**Verify**: 检查普通成员在全员禁设备 / 禁共享时是否优先走提示或申请链路,而不是直接 `open*` / `unmuteMicrophone()` / `startScreenShare()`。
|
|
663
|
+
5. **不要依赖 adminList、本地缓存或展示文案判断角色** — 角色判断应回到 `participant.role` / `localParticipant.role`。
|
|
664
|
+
**Verify**: 检查角色判断是否使用 `RoomParticipantRole` 枚举。
|
|
665
|
+
6. **不要把成员级治理和房间级会控写成互不感知的两个模块** — 它们同源、应共享一个治理面板的入口体系。
|
|
666
|
+
**Verify**: 检查 UI 组织是否将"对单成员"与"对全员"清晰分组在同一面板。
|
|
667
|
+
|
|
668
|
+
### 集成检查点
|
|
669
|
+
- 当前 slice 与 `conference/participant-list`、`conference/room-lifecycle`、`conference/room-chat`、`conference/device-control`、`conference/screen-share`、`conference/room-call`、`conference/room-lifecycle` 联动。
|
|
670
|
+
- 集成方式通常为新增治理面板(含角色管理、单成员控制、全员会控、设备申请审批四类入口),不需要修改底层会控实现。
|
|
671
|
+
- 如果业务存在企业组织权限体系或合规审计要求,建议把重要治理动作同步记录到业务日志系统。
|
|
672
|
+
|
|
673
|
+
## 验证矩阵
|
|
674
|
+
|
|
675
|
+
| 层级 | 检查项 | 验证手段 | 预期结果 |
|
|
676
|
+
|------|--------|----------|---------|
|
|
677
|
+
| 1. 编译级 | 已导入 `useRoomParticipantState` / `RoomParticipantRole` / `DeviceType` 等核心依赖 | 检查 `import` 语句 | 治理 + 会控相关 API 与枚举可解析 |
|
|
678
|
+
| 2. 静态规则级 | 角色判断、toggle 文案、状态回显与失败兜底齐全 | 搜索角色枚举判断、`disable` toggle 分支、try/catch、`hydrate*` 类恢复逻辑 | 形成"控制 + 感知 + 管理端回显 + 失败处理"闭环 |
|
|
679
|
+
| 3. 运行时级 | 治理与会控动作可执行并反馈结果 | 在房主 / 管理员账号下执行操作 | 成功后成员状态刷新;失败时有提示;toggle 可解除 |
|
|
680
|
+
| 4. 业务行为级 | 刷新后管理端与被控成员都能看到一致状态 | 刷新管理端页面并用被控制账号观察页面 | 房间级规则不因页面刷新丢失,设备 / 聊天 / 共享入口状态持续正确 |
|
|
681
|
+
|
|
682
|
+
## 排障指南
|
|
683
|
+
|
|
684
|
+
### 常见问题
|
|
685
|
+
|
|
686
|
+
| 问题 | 表现 | 处理建议 |
|
|
687
|
+
|------|------|----------|
|
|
688
|
+
| 管理入口展示错误 | 普通成员看到了管理员或房主操作入口 | 检查本地角色判断是否基于 `participant.role` / `localParticipant.role`,而不是缓存或展示文案 |
|
|
689
|
+
| 设置 / 撤销管理员后标签未更新 | 操作成功后,成员列表仍显示旧角色 | 检查角色变化是否已回流到成员列表与入口显隐逻辑 |
|
|
690
|
+
| 房主转移后权限混乱 | 新旧房主的可执行操作没有及时切换 | 检查房主变更后的本地角色刷新和按钮显隐是否同步更新 |
|
|
691
|
+
| 移出成员后仍显示在列表中 | 成员已被移出会议,但列表里还存在 | 检查成员离会结果是否已同步到 `participant-list` 和 `room-lifecycle` |
|
|
692
|
+
| 成员重新入会后角色异常 | 用户重新进入会议后仍沿用上次角色 | 检查是否错误缓存了临时角色,重新入会应以最新同步角色为准 |
|
|
693
|
+
| 会控生效但成员侧无变化 | 已开启全员禁言 / 禁设备 / 禁共享,但成员侧入口仍可用 | 检查聊天、设备、共享模块是否直接消费了会控状态 |
|
|
694
|
+
| 单独禁言判定错误 | 房间未全体禁言但某成员不能发消息 | 检查是否存在单独禁言(`disableUserMessage`),不要误判为聊天模块异常 |
|
|
695
|
+
| 设备申请长期不收口 | 申请已取消、超时或被他人处理,但界面仍显示待处理 | 检查申请状态是否和超时、取消、已处理结果同步更新 |
|
|
696
|
+
| 刷新后管理端按钮状态丢失 | 房间里其实仍处于全员静音 / 禁画 / 禁聊,但房主端按钮又显示成初始态 | 检查管理端按钮是否从房间状态同步结果或业务侧会控快照恢复 |
|
|
697
|
+
| 规则与默认配置混淆 | 创建前配置的默认禁麦被误当成会中临时会控 | 检查 `room-lifecycle` 与本 slice 是否做了清晰分层 |
|
|
698
|
+
|
|
699
|
+
### 排障流程
|
|
700
|
+
|
|
701
|
+
```text
|
|
702
|
+
发现 参会人管理与会控 相关问题
|
|
703
|
+
├── 第 1 步:确认问题属于成员级治理、房间级会控、还是创建前默认配置
|
|
704
|
+
├── 第 2 步:检查当前用户角色和目标成员角色是否已正确同步
|
|
705
|
+
├── 第 3 步:确认设置管理员、移出成员、关闭设备、全员规则、申请 / 邀请等动作的结果是否已回流到成员列表、设备 / 聊天 / 共享入口和提示信息
|
|
706
|
+
├── 第 4 步:检查是否错误依赖 adminList、缓存角色或本地写死的管理员数量限制
|
|
707
|
+
└── 第 5 步:若仍异常,再回查 participant-list / room-lifecycle / room-chat / device-control / screen-share / room-call / room-lifecycle 的衔接是否正确
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
## 关联知识
|
|
711
|
+
|
|
712
|
+
- **[conference/participant-list](participant-list.md)** —— 成员治理结果最终会体现在成员列表、角色标签和状态展示上。
|
|
713
|
+
- **[conference/room-lifecycle](room-lifecycle.md)** —— 创建前默认规则与会中动态会控应分层。
|
|
714
|
+
- **[conference/room-chat](room-chat.md)** —— 消息权限会直接影响聊天输入区和发送链路。
|
|
715
|
+
- **[conference/device-control](device-control.md)** —— 设备按钮可用态、管理员关闭设备和申请开启链路与本 slice 联动。
|
|
716
|
+
- **[conference/screen-share](screen-share.md)** —— 禁共享、共享关闭和申请 / 邀请链路会直接约束共享能力。
|
|
717
|
+
- **[conference/room-call](room-call.md)** —— 谁可以发起邀请、邀请哪些人,往往与角色边界有关。
|
|
718
|
+
- **[conference/room-lifecycle](room-lifecycle.md)** —— 移出成员、主动离会和房间结束等结果都会影响成员去留。
|