@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.
Files changed (205) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +172 -0
  3. package/README.zh.md +173 -0
  4. package/bin/cli.js +434 -0
  5. package/knowledge-base/index.yaml +454 -0
  6. package/knowledge-base/platform-slice-template.md +233 -0
  7. package/knowledge-base/scenario-spec.md +350 -0
  8. package/knowledge-base/scenarios/conference/base/general-conference.md +365 -0
  9. package/knowledge-base/scenarios/conference/base/webinar-conference.md +130 -0
  10. package/knowledge-base/scenarios/conference/medical/1v1-video-consultation.md +145 -0
  11. package/knowledge-base/scenarios/conference/medical/medical-multidoctor-consultation.md +113 -0
  12. package/knowledge-base/scenarios/live/entertainment-live-room.md +118 -0
  13. package/knowledge-base/slice-spec.md +546 -0
  14. package/knowledge-base/slices/conference/web/ai-tools.md +225 -0
  15. package/knowledge-base/slices/conference/web/beauty-effects.md +188 -0
  16. package/knowledge-base/slices/conference/web/device-control.md +338 -0
  17. package/knowledge-base/slices/conference/web/login-auth.md +261 -0
  18. package/knowledge-base/slices/conference/web/network-quality.md +190 -0
  19. package/knowledge-base/slices/conference/web/official-roomkit-api.md +298 -0
  20. package/knowledge-base/slices/conference/web/official-roomkit-login-ui.md +246 -0
  21. package/knowledge-base/slices/conference/web/participant-list.md +238 -0
  22. package/knowledge-base/slices/conference/web/participant-management.md +718 -0
  23. package/knowledge-base/slices/conference/web/prejoin-check.md +293 -0
  24. package/knowledge-base/slices/conference/web/room-call.md +213 -0
  25. package/knowledge-base/slices/conference/web/room-chat.md +426 -0
  26. package/knowledge-base/slices/conference/web/room-lifecycle.md +534 -0
  27. package/knowledge-base/slices/conference/web/room-schedule.md +281 -0
  28. package/knowledge-base/slices/conference/web/screen-share.md +211 -0
  29. package/knowledge-base/slices/conference/web/video-layout.md +675 -0
  30. package/knowledge-base/slices/conference/web/virtual-background.md +197 -0
  31. package/knowledge-base/slices/conference/web/webinar-interaction.md +206 -0
  32. package/knowledge-base/slices/live/anchor-lifecycle.md +122 -0
  33. package/knowledge-base/slices/live/anchor-preview.md +90 -0
  34. package/knowledge-base/slices/live/anchor-room-config.md +104 -0
  35. package/knowledge-base/slices/live/audience-list.md +86 -0
  36. package/knowledge-base/slices/live/audience-manage.md +92 -0
  37. package/knowledge-base/slices/live/audience-watch.md +85 -0
  38. package/knowledge-base/slices/live/audio.md +116 -0
  39. package/knowledge-base/slices/live/barrage.md +88 -0
  40. package/knowledge-base/slices/live/beauty.md +99 -0
  41. package/knowledge-base/slices/live/coguest-apply.md +105 -0
  42. package/knowledge-base/slices/live/device-control.md +91 -0
  43. package/knowledge-base/slices/live/error-codes.md +167 -0
  44. package/knowledge-base/slices/live/gift.md +84 -0
  45. package/knowledge-base/slices/live/ios/.gitkeep +0 -0
  46. package/knowledge-base/slices/live/ios/anchor-lifecycle.md +313 -0
  47. package/knowledge-base/slices/live/ios/anchor-preview.md +228 -0
  48. package/knowledge-base/slices/live/ios/anchor-room-config.md +257 -0
  49. package/knowledge-base/slices/live/ios/audience-list.md +353 -0
  50. package/knowledge-base/slices/live/ios/audience-manage.md +381 -0
  51. package/knowledge-base/slices/live/ios/audience-watch.md +286 -0
  52. package/knowledge-base/slices/live/ios/audio.md +373 -0
  53. package/knowledge-base/slices/live/ios/barrage.md +285 -0
  54. package/knowledge-base/slices/live/ios/beauty.md +323 -0
  55. package/knowledge-base/slices/live/ios/coguest-apply.md +506 -0
  56. package/knowledge-base/slices/live/ios/device-control.md +286 -0
  57. package/knowledge-base/slices/live/ios/error-codes.md +270 -0
  58. package/knowledge-base/slices/live/ios/gift.md +315 -0
  59. package/knowledge-base/slices/live/ios/live-list.md +269 -0
  60. package/knowledge-base/slices/live/ios/login-auth.md +247 -0
  61. package/knowledge-base/slices/live/live-list.md +82 -0
  62. package/knowledge-base/slices/live/login-auth.md +78 -0
  63. package/package.json +34 -0
  64. package/skills/trtc/SKILL.md +326 -0
  65. package/skills/trtc/room-builder/SKILL.md +138 -0
  66. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/README.md +108 -0
  67. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/docs/backend-contract.zh-CN.md +162 -0
  68. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/docs/integration.zh-CN.md +154 -0
  69. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/docs/theme.zh-CN.md +78 -0
  70. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/index.html +12 -0
  71. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/package.json +28 -0
  72. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/postcss.config.js +5 -0
  73. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/App.vue +25 -0
  74. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/ConsultationManagePanel.vue +838 -0
  75. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/LanguageSwitch.vue +102 -0
  76. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/LoadingSpinner.vue +6 -0
  77. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/MedicalAlert.vue +34 -0
  78. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/MedicalBusinessPanel.vue +148 -0
  79. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/MedicalButton.vue +49 -0
  80. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/MedicalConfirmDialog.vue +68 -0
  81. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/MedicalDataPanel.vue +196 -0
  82. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/MedicalRecordPanel.vue +270 -0
  83. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/PrescriptionPanel.vue +363 -0
  84. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/config/basic-info-config.ts +29 -0
  85. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/config/lib-generate-test-usersig-es.min.d.ts +4 -0
  86. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/config/lib-generate-test-usersig-es.min.js +2 -0
  87. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/config/runtime-config.ts +12 -0
  88. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/env.d.ts +32 -0
  89. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/components/ConsultationChatPanel.vue +123 -0
  90. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/components/ConsultationMembersPanel.vue +230 -0
  91. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/components/ConsultationTranscriptionPanel.vue +135 -0
  92. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/components/ConsultationVideoStage.vue +113 -0
  93. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/components/InviteDoctorDialog.vue +132 -0
  94. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/components/KickMemberConfirmDialog.vue +50 -0
  95. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/types.ts +77 -0
  96. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/useConsultationChat.ts +97 -0
  97. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/useConsultationDevices.ts +48 -0
  98. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/useConsultationParticipants.ts +121 -0
  99. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/useConsultationPermissions.ts +25 -0
  100. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/utils.ts +70 -0
  101. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/i18n/en-US/index.ts +553 -0
  102. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/i18n/index.ts +25 -0
  103. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/i18n/medicalTranslate.ts +85 -0
  104. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/i18n/state.ts +49 -0
  105. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/i18n/zh-CN/index.ts +463 -0
  106. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/main.ts +12 -0
  107. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/mock/appointments.ts +96 -0
  108. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/mock/users.ts +79 -0
  109. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/router/index.ts +63 -0
  110. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/services/adapters/index.ts +25 -0
  111. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/services/adapters/integration/appointmentService.ts +77 -0
  112. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/services/adapters/integration/authService.ts +38 -0
  113. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/services/adapters/integration/launchContext.ts +31 -0
  114. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/services/adapters/integration/userService.ts +35 -0
  115. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/services/adapters/mock/appointmentService.ts +43 -0
  116. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/services/adapters/mock/authService.ts +33 -0
  117. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/services/adapters/mock/userService.ts +43 -0
  118. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/services/adapters/types.ts +135 -0
  119. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/shared/icons.ts +53 -0
  120. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/styles/index.css +106 -0
  121. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/styles/tailwind.css +3 -0
  122. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/styles/theme.css +209 -0
  123. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/utils/auth.ts +50 -0
  124. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/utils/format.ts +24 -0
  125. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/utils/navigation.ts +12 -0
  126. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/utils/session.ts +28 -0
  127. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/views/DoctorConsultationView.vue +777 -0
  128. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/views/DoctorDashboardView.vue +678 -0
  129. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/views/LoginView.vue +441 -0
  130. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/views/PatientConsultationFinishedView.vue +185 -0
  131. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/views/PatientConsultationView.vue +1003 -0
  132. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/views/PatientSelectDoctorView.vue +317 -0
  133. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/views/PatientWaitingView.vue +454 -0
  134. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/tsconfig.json +21 -0
  135. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/tsconfig.node.json +8 -0
  136. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/vite.config.ts +17 -0
  137. package/skills/trtc/room-builder/templates/scenarios/medical-consultation//346/216/245/345/205/245/350/257/264/346/230/216.md +6 -0
  138. package/skills/trtc/room-builder/tools/render_ai_instructions.py +226 -0
  139. package/skills/trtc-apply/SKILL.md +97 -0
  140. package/skills/trtc-apply/guardrails/apply_lib/__init__.py +0 -0
  141. package/skills/trtc-apply/guardrails/apply_lib/__pycache__/__init__.cpython-313.pyc +0 -0
  142. package/skills/trtc-apply/guardrails/apply_lib/__pycache__/rule_parser.cpython-313.pyc +0 -0
  143. package/skills/trtc-apply/guardrails/apply_lib/rule_parser.py +268 -0
  144. package/skills/trtc-docs/SKILL.md +207 -0
  145. package/skills/trtc-onboarding/SKILL.md +839 -0
  146. package/skills/trtc-onboarding/reference/path-a1-demo.md +103 -0
  147. package/skills/trtc-onboarding/reference/path-a2-integrate.md +693 -0
  148. package/skills/trtc-onboarding/reference/path-b-troubleshoot.md +115 -0
  149. package/skills/trtc-onboarding/reference/path-c-expand.md +43 -0
  150. package/skills/trtc-onboarding/reference/reporting-protocol.md +174 -0
  151. package/skills/trtc-onboarding/reference/supported-matrix.md +100 -0
  152. package/skills/trtc-onboarding/reference/usersig-handling.md +140 -0
  153. package/skills/trtc-search/SKILL.md +221 -0
  154. package/skills/trtc-topic/SKILL.md +638 -0
  155. package/skills/trtc-topic/guardrails/__pycache__/gate_slice_read.cpython-313.pyc +0 -0
  156. package/skills/trtc-topic/guardrails/__pycache__/gate_slice_write.cpython-313.pyc +0 -0
  157. package/skills/trtc-topic/guardrails/__pycache__/stop_require_apply_evidence.cpython-313.pyc +0 -0
  158. package/skills/trtc-topic/guardrails/gate_slice_read.py +133 -0
  159. package/skills/trtc-topic/guardrails/gate_slice_write.py +169 -0
  160. package/skills/trtc-topic/guardrails/stop_require_apply_evidence.py +97 -0
  161. package/skills/trtc-topic/references/execution-units.yaml +58 -0
  162. package/skills/trtc-topic/runtime/README.md +50 -0
  163. package/skills/trtc-topic/runtime/RUNTIME.md +128 -0
  164. package/skills/trtc-topic/runtime/lib/__init__.py +0 -0
  165. package/skills/trtc-topic/runtime/lib/platforms.py +194 -0
  166. package/skills/trtc-topic/runtime/package-lock.json +1211 -0
  167. package/skills/trtc-topic/runtime/package.json +13 -0
  168. package/skills/trtc-topic/runtime/telemetry-bridge.mjs +339 -0
  169. package/skills/trtc-topic/runtime/telemetry_collector.py +293 -0
  170. package/skills/trtc-topic/scripts/STATE-MACHINE-GUIDE.md +186 -0
  171. package/skills/trtc-topic/scripts/__pycache__/apply.cpython-313.pyc +0 -0
  172. package/skills/trtc-topic/scripts/apply.py +581 -0
  173. package/skills/trtc-topic/scripts/finalize_session.py +113 -0
  174. package/skills/trtc-topic/scripts/init_slice_queue.py +96 -0
  175. package/skills/trtc-topic/scripts/lib/__pycache__/state_machine.cpython-313.pyc +0 -0
  176. package/skills/trtc-topic/scripts/lib/state_machine.py +328 -0
  177. package/skills/trtc-topic/scripts/next_slice.py +137 -0
  178. package/skills/trtc-topic/tests/README.md +70 -0
  179. package/skills/trtc-topic/tests/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc +0 -0
  180. package/skills/trtc-topic/tests/__pycache__/conftest.cpython-313-pytest-9.0.3.pyc +0 -0
  181. package/skills/trtc-topic/tests/__pycache__/test_apply_cli.cpython-313-pytest-9.0.2.pyc +0 -0
  182. package/skills/trtc-topic/tests/__pycache__/test_apply_cli.cpython-313-pytest-9.0.3.pyc +0 -0
  183. package/skills/trtc-topic/tests/__pycache__/test_end_to_end.cpython-313-pytest-9.0.2.pyc +0 -0
  184. package/skills/trtc-topic/tests/__pycache__/test_end_to_end.cpython-313-pytest-9.0.3.pyc +0 -0
  185. package/skills/trtc-topic/tests/__pycache__/test_finalize_session.cpython-313-pytest-9.0.2.pyc +0 -0
  186. package/skills/trtc-topic/tests/__pycache__/test_finalize_session.cpython-313-pytest-9.0.3.pyc +0 -0
  187. package/skills/trtc-topic/tests/__pycache__/test_gates.cpython-313-pytest-9.0.2.pyc +0 -0
  188. package/skills/trtc-topic/tests/__pycache__/test_gates.cpython-313-pytest-9.0.3.pyc +0 -0
  189. package/skills/trtc-topic/tests/__pycache__/test_session_resolver.cpython-313-pytest-9.0.2.pyc +0 -0
  190. package/skills/trtc-topic/tests/__pycache__/test_session_resolver.cpython-313-pytest-9.0.3.pyc +0 -0
  191. package/skills/trtc-topic/tests/__pycache__/test_state_machine.cpython-313-pytest-9.0.2.pyc +0 -0
  192. package/skills/trtc-topic/tests/__pycache__/test_state_machine.cpython-313-pytest-9.0.3.pyc +0 -0
  193. package/skills/trtc-topic/tests/__pycache__/test_stop_require_apply.cpython-313-pytest-9.0.2.pyc +0 -0
  194. package/skills/trtc-topic/tests/__pycache__/test_stop_require_apply.cpython-313-pytest-9.0.3.pyc +0 -0
  195. package/skills/trtc-topic/tests/__pycache__/test_topic_skill_invariants.cpython-313-pytest-9.0.2.pyc +0 -0
  196. package/skills/trtc-topic/tests/__pycache__/test_topic_skill_invariants.cpython-313-pytest-9.0.3.pyc +0 -0
  197. package/skills/trtc-topic/tests/conftest.py +72 -0
  198. package/skills/trtc-topic/tests/test_apply_cli.py +480 -0
  199. package/skills/trtc-topic/tests/test_end_to_end.py +305 -0
  200. package/skills/trtc-topic/tests/test_finalize_session.py +51 -0
  201. package/skills/trtc-topic/tests/test_gates.py +316 -0
  202. package/skills/trtc-topic/tests/test_session_resolver.py +260 -0
  203. package/skills/trtc-topic/tests/test_state_machine.py +414 -0
  204. package/skills/trtc-topic/tests/test_stop_require_apply.py +99 -0
  205. package/skills/trtc-topic/tests/test_topic_skill_invariants.py +130 -0
@@ -0,0 +1,230 @@
1
+ <script setup lang="ts">
2
+ import {
3
+ Camera,
4
+ CameraOff,
5
+ Mic,
6
+ MicOff,
7
+ UserPlus,
8
+ UserX,
9
+ Video,
10
+ } from '@/shared/icons';
11
+ import { useUIKit } from '@tencentcloud/uikit-base-component-vue3';
12
+ import { DeviceType } from 'tuikit-atomicx-vue3';
13
+ import LoadingSpinner from '@/components/LoadingSpinner.vue';
14
+ import type {
15
+ ConsultationMemberCard,
16
+ ConsultationPermissions,
17
+ } from '@/features/consultation/types';
18
+
19
+ defineProps<{
20
+ memberCards: ConsultationMemberCard[];
21
+ permissions: ConsultationPermissions;
22
+ inviteFeedback?: string;
23
+ kickingUserId?: string | null;
24
+ cancellingDoctorId?: string | null;
25
+ patientDeviceInvitePending: { camera: boolean; microphone: boolean };
26
+ patientDeviceInviteLoading: { camera: boolean; microphone: boolean };
27
+ }>();
28
+ const { t } = useUIKit();
29
+
30
+ const emit = defineEmits<{
31
+ openInvite: [];
32
+ requestKick: [member: { userId: string; userName: string }];
33
+ cancelInvite: [userId: string];
34
+ invitePatientOpenDevice: [deviceType: DeviceType];
35
+ cancelPatientOpenDeviceInvitation: [deviceType: DeviceType];
36
+ closePatientDevice: [deviceType: DeviceType];
37
+ }>();
38
+ </script>
39
+
40
+ <template>
41
+ <div class="h-full min-h-0 flex flex-col">
42
+ <div class="p-4 border-b border-gray-100 flex items-center justify-between">
43
+ <h3 class="font-semibold text-gray-900">
44
+ {{ t('Medical.Manage.MemberManagement') }}
45
+ </h3>
46
+ <span class="px-3 py-1 rounded-full border border-gray-200 text-sm font-medium">
47
+ {{ t('Medical.Manage.MemberCount', { count: memberCards.length }) }}
48
+ </span>
49
+ </div>
50
+
51
+ <div class="p-4 space-y-3 min-h-0 overflow-y-auto">
52
+ <button
53
+ v-if="permissions.canInviteDoctor"
54
+ @click="emit('openInvite')"
55
+ class="w-full h-11 rounded-xl border border-dashed border-[#0D9488] text-[#0D9488] font-semibold hover:bg-[#F0FDFA] transition-colors inline-flex items-center justify-center gap-2"
56
+ >
57
+ <UserPlus :size="18" />
58
+ {{ t('Medical.Manage.InviteDoctor') }}
59
+ </button>
60
+
61
+ <div class="space-y-3">
62
+ <div
63
+ v-for="member in memberCards"
64
+ :key="member.userId"
65
+ class="rounded-[28px] border border-[#E9EEF5] bg-white px-4 py-4 flex items-center gap-3"
66
+ >
67
+ <div class="relative shrink-0">
68
+ <div
69
+ :class="[
70
+ 'w-16 h-16 rounded-full bg-gradient-to-br text-white text-[30px] flex items-center justify-center font-semibold',
71
+ member.avatarClass,
72
+ ]"
73
+ >
74
+ {{ member.userName.charAt(0) }}
75
+ </div>
76
+ </div>
77
+
78
+ <div class="flex-1 min-w-0">
79
+ <div class="flex items-center justify-between gap-2">
80
+ <div class="flex items-center gap-2 min-w-0">
81
+ <p class="font-semibold text-[16px] text-[#0F172A] truncate">
82
+ {{ member.userName }}
83
+ </p>
84
+ <span
85
+ :class="[
86
+ 'px-3 py-1 rounded-full text-[12px] leading-none border',
87
+ member.roleClass,
88
+ member.isPatient ? 'border-[#BFDBFE]' : 'border-transparent',
89
+ ]"
90
+ >
91
+ {{ member.roleLabel }}
92
+ </span>
93
+ </div>
94
+
95
+ <button
96
+ v-if="member.isPatient && permissions.canKickMember"
97
+ @click="
98
+ emit('requestKick', {
99
+ userId: member.userId,
100
+ userName: member.userName,
101
+ })
102
+ "
103
+ :disabled="kickingUserId === member.userId"
104
+ class="w-8 h-8 rounded-full text-[#94A3B8] hover:bg-red-50 hover:text-red-500 disabled:opacity-60 disabled:cursor-not-allowed flex items-center justify-center"
105
+ :title="t('Medical.Manage.KickMember')"
106
+ >
107
+ <LoadingSpinner v-if="kickingUserId === member.userId" />
108
+ <UserX v-else :size="18" />
109
+ </button>
110
+ </div>
111
+
112
+ <div class="mt-2 flex items-center gap-2">
113
+ <button
114
+ v-if="member.isPatient && permissions.canManagePatientDevice"
115
+ @click="
116
+ member.cameraOn
117
+ ? emit('closePatientDevice', DeviceType.Camera)
118
+ : patientDeviceInvitePending.camera
119
+ ? emit('cancelPatientOpenDeviceInvitation', DeviceType.Camera)
120
+ : emit('invitePatientOpenDevice', DeviceType.Camera)
121
+ "
122
+ :disabled="member.pending || patientDeviceInviteLoading.camera"
123
+ :class="[
124
+ 'w-11 h-11 rounded-full border flex items-center justify-center transition-colors disabled:opacity-60 disabled:cursor-not-allowed',
125
+ member.cameraOn
126
+ ? 'bg-white border-[#CBD5E1] text-[#64748B]'
127
+ : patientDeviceInvitePending.camera
128
+ ? 'bg-[#FFF7ED] border-[#FDBA74] text-[#EA580C]'
129
+ : 'bg-[#FEE2E2] border-[#FCA5A5] text-[#EF4444]',
130
+ ]"
131
+ :title="
132
+ member.cameraOn
133
+ ? t('Medical.Manage.ClosePatientCamera')
134
+ : t('Medical.Manage.InvitePatientCamera')
135
+ "
136
+ >
137
+ <LoadingSpinner v-if="patientDeviceInviteLoading.camera" />
138
+ <component
139
+ v-else
140
+ :is="member.cameraOn ? Camera : CameraOff"
141
+ :size="18"
142
+ />
143
+ </button>
144
+ <Video
145
+ v-else
146
+ :size="20"
147
+ :class="member.cameraOn ? 'text-[#475569]' : 'text-[#CBD5E1]'"
148
+ />
149
+
150
+ <button
151
+ v-if="member.isPatient && permissions.canManagePatientDevice"
152
+ @click="
153
+ member.microphoneOn
154
+ ? emit('closePatientDevice', DeviceType.Microphone)
155
+ : patientDeviceInvitePending.microphone
156
+ ? emit(
157
+ 'cancelPatientOpenDeviceInvitation',
158
+ DeviceType.Microphone
159
+ )
160
+ : emit('invitePatientOpenDevice', DeviceType.Microphone)
161
+ "
162
+ :disabled="
163
+ member.pending || patientDeviceInviteLoading.microphone
164
+ "
165
+ :class="[
166
+ 'w-11 h-11 rounded-full border flex items-center justify-center transition-colors disabled:opacity-60 disabled:cursor-not-allowed',
167
+ member.microphoneOn
168
+ ? 'bg-white border-[#CBD5E1] text-[#334155]'
169
+ : patientDeviceInvitePending.microphone
170
+ ? 'bg-[#FFF7ED] border-[#FDBA74] text-[#EA580C]'
171
+ : 'bg-[#FEE2E2] border-[#FCA5A5] text-[#EF4444]',
172
+ ]"
173
+ :title="
174
+ member.microphoneOn
175
+ ? t('Medical.Manage.ClosePatientMicrophone')
176
+ : t('Medical.Manage.InvitePatientMicrophone')
177
+ "
178
+ >
179
+ <LoadingSpinner v-if="patientDeviceInviteLoading.microphone" />
180
+ <component
181
+ v-else
182
+ :is="member.microphoneOn ? Mic : MicOff"
183
+ :size="18"
184
+ />
185
+ </button>
186
+ <Mic
187
+ v-else
188
+ :size="20"
189
+ :class="
190
+ member.microphoneOn ? 'text-[#475569]' : 'text-[#CBD5E1]'
191
+ "
192
+ />
193
+ </div>
194
+ </div>
195
+
196
+ <button
197
+ v-if="
198
+ member.pending &&
199
+ member.cancelable &&
200
+ permissions.canCancelInvite
201
+ "
202
+ @click="emit('cancelInvite', member.userId)"
203
+ :disabled="cancellingDoctorId === member.userId"
204
+ class="h-8 px-3 rounded-full border border-red-200 text-red-500 text-xs font-medium hover:bg-red-50 disabled:opacity-60 disabled:cursor-not-allowed"
205
+ >
206
+ <LoadingSpinner
207
+ v-if="cancellingDoctorId === member.userId"
208
+ class="mr-1"
209
+ />
210
+ {{
211
+ cancellingDoctorId === member.userId
212
+ ? t('Medical.Common.Canceling')
213
+ : t('Medical.Manage.CancelInvite')
214
+ }}
215
+ </button>
216
+ </div>
217
+ </div>
218
+
219
+ <p v-if="inviteFeedback" class="text-xs text-[#64748B] px-1">
220
+ {{ inviteFeedback }}
221
+ </p>
222
+
223
+ <div class="pt-2 text-xs text-gray-500 leading-6">
224
+ <p>• {{ t('Medical.Manage.PermissionTip1') }}</p>
225
+ <p>• {{ t('Medical.Manage.PermissionTip2') }}</p>
226
+ <p>• {{ t('Medical.Manage.PermissionTip3') }}</p>
227
+ </div>
228
+ </div>
229
+ </div>
230
+ </template>
@@ -0,0 +1,135 @@
1
+ <script setup lang="ts">
2
+ import { useUIKit } from '@tencentcloud/uikit-base-component-vue3';
3
+ import { Copy, Download, Mic, MicOff } from '@/shared/icons';
4
+ import LoadingSpinner from '@/components/LoadingSpinner.vue';
5
+ import { formatConsultationClock } from '@/features/consultation/utils';
6
+ import type { ConsultationTranscriptItem } from '@/features/consultation/types';
7
+
8
+ defineProps<{
9
+ transcriptList: ConsultationTranscriptItem[];
10
+ transcriptText: string;
11
+ transcriberRunning: boolean;
12
+ transcriberBusy: boolean;
13
+ transcriberHint?: string;
14
+ isCopyingDraft: boolean;
15
+ isExportingDraft: boolean;
16
+ getSpeakerName: (userId: string) => string;
17
+ }>();
18
+ const { t } = useUIKit();
19
+
20
+ const emit = defineEmits<{
21
+ toggle: [];
22
+ copy: [];
23
+ export: [];
24
+ }>();
25
+ </script>
26
+
27
+ <template>
28
+ <div class="h-full min-h-0 flex flex-col">
29
+ <div class="p-4 border-b border-gray-100 flex items-center justify-between">
30
+ <h3 class="font-semibold text-gray-900">
31
+ {{ t('Medical.Manage.TranscriptionTitle') }}
32
+ </h3>
33
+ <button
34
+ @click="emit('toggle')"
35
+ :disabled="transcriberBusy"
36
+ :class="[
37
+ 'inline-flex items-center gap-2 px-3 py-1 rounded-full text-sm border transition-colors',
38
+ transcriberRunning
39
+ ? 'bg-[#0D9488] text-white border-[#0D9488]'
40
+ : 'bg-white text-gray-700 border-gray-200 hover:border-[#0D9488] hover:text-[#0D9488]',
41
+ ]"
42
+ >
43
+ <LoadingSpinner v-if="transcriberBusy" />
44
+ <component v-else :is="transcriberRunning ? Mic : MicOff" :size="14" />
45
+ {{
46
+ transcriberBusy
47
+ ? t('Medical.Common.Processing')
48
+ : transcriberRunning
49
+ ? t('Medical.Manage.Transcribing')
50
+ : t('Medical.Manage.StartTranscription')
51
+ }}
52
+ </button>
53
+ </div>
54
+ <div class="px-4 py-2 text-xs text-[#0D9488] min-h-6">
55
+ {{ transcriberHint }}
56
+ </div>
57
+ <div class="px-4 pb-3">
58
+ <div
59
+ class="h-[330px] rounded-3xl border border-[#E6EEF5] overflow-y-auto bg-white"
60
+ >
61
+ <div
62
+ class="flex items-center justify-between px-4 py-3 border-b border-[#EEF2F6] text-sm text-gray-500"
63
+ >
64
+ <span class="inline-flex items-center gap-2">
65
+ <span class="w-2 h-2 rounded-full bg-[#86EFAC]"></span>
66
+ {{ t('Medical.Manage.RealtimeTranscribing') }}
67
+ </span>
68
+ <span>{{ t('Medical.Manage.TranscriptCount', { count: transcriptList.length }) }}</span>
69
+ </div>
70
+ <div class="p-4 space-y-3">
71
+ <div
72
+ v-for="item in transcriptList"
73
+ :key="item.segmentId"
74
+ class="flex items-start gap-2"
75
+ >
76
+ <div
77
+ class="w-6 h-6 rounded-full bg-[#3B82F6] text-white text-xs font-semibold flex items-center justify-center shrink-0"
78
+ >
79
+ {{ getSpeakerName(item.speakerUserId).charAt(0) }}
80
+ </div>
81
+ <div class="max-w-[85%]">
82
+ <div class="text-xs text-[#64748B] mb-1">
83
+ {{ getSpeakerName(item.speakerUserId) }}
84
+ {{ formatConsultationClock(item.timestamp) }}
85
+ </div>
86
+ <div
87
+ class="rounded-2xl bg-[#0D9488] text-white px-3 py-2 text-sm leading-6"
88
+ >
89
+ {{ item.sourceText }}
90
+ </div>
91
+ </div>
92
+ </div>
93
+ <div
94
+ v-if="transcriptList.length === 0"
95
+ class="h-[250px] flex items-center justify-center text-center text-gray-400"
96
+ >
97
+ <div>
98
+ <MicOff :size="44" class="mx-auto mb-2 text-gray-300" />
99
+ <p class="text-sm">
100
+ {{
101
+ transcriberRunning
102
+ ? t('Medical.Manage.WaitingSpeech')
103
+ : t('Medical.Manage.ClickStartTranscription')
104
+ }}
105
+ </p>
106
+ </div>
107
+ </div>
108
+ </div>
109
+ </div>
110
+ </div>
111
+ <div class="px-4 pb-3 grid grid-cols-2 gap-3">
112
+ <button
113
+ @click="emit('copy')"
114
+ :disabled="isCopyingDraft || !transcriptText"
115
+ class="h-10 rounded-2xl border border-[#E2E8F0] text-[#0F172A] font-medium inline-flex items-center justify-center gap-2 hover:border-[#0D9488] hover:text-[#0D9488] disabled:opacity-60 disabled:cursor-not-allowed"
116
+ >
117
+ <LoadingSpinner v-if="isCopyingDraft" />
118
+ <Copy v-else :size="16" />
119
+ {{ isCopyingDraft ? t('Medical.Common.Copying') : t('Medical.Common.Copy') }}
120
+ </button>
121
+ <button
122
+ @click="emit('export')"
123
+ :disabled="isExportingDraft || !transcriptText"
124
+ class="h-10 rounded-2xl border border-[#E2E8F0] text-[#0F172A] font-medium inline-flex items-center justify-center gap-2 hover:border-[#0D9488] hover:text-[#0D9488] disabled:opacity-60 disabled:cursor-not-allowed"
125
+ >
126
+ <LoadingSpinner v-if="isExportingDraft" />
127
+ <Download v-else :size="16" />
128
+ {{ isExportingDraft ? t('Medical.Common.Exporting') : t('Medical.Common.Export') }}
129
+ </button>
130
+ </div>
131
+ <div class="px-4 pb-4 text-xs text-gray-500 leading-6">
132
+ <p>• {{ t('Medical.Manage.TranscriptionFooterTip') }}</p>
133
+ </div>
134
+ </div>
135
+ </template>
@@ -0,0 +1,113 @@
1
+ <script setup lang="ts">
2
+ import { useUIKit } from '@tencentcloud/uikit-base-component-vue3';
3
+ import { RoomParticipantView, FillMode, VideoStreamType } from 'tuikit-atomicx-vue3';
4
+ import type { ConsultationVideoMember } from '@/features/consultation/types';
5
+
6
+ withDefaults(
7
+ defineProps<{
8
+ mainMember: ConsultationVideoMember | null;
9
+ thumbnailMembers: ConsultationVideoMember[];
10
+ thumbnailListClass?: string;
11
+ thumbnailCardClass?: string;
12
+ }>(),
13
+ {
14
+ thumbnailListClass: 'top-6 right-6 max-h-[calc(100%-150px)] w-56',
15
+ thumbnailCardClass: 'h-36',
16
+ }
17
+ );
18
+
19
+ const emit = defineEmits<{
20
+ focus: [userId: string];
21
+ }>();
22
+ const { t } = useUIKit();
23
+ </script>
24
+
25
+ <template>
26
+ <div
27
+ class="absolute inset-0 cursor-pointer bg-gradient-to-br from-gray-800 to-gray-900"
28
+ @click="mainMember && emit('focus', mainMember.userId)"
29
+ >
30
+ <RoomParticipantView
31
+ v-if="mainMember?.participant"
32
+ class="absolute inset-0"
33
+ :participant="mainMember.participant"
34
+ :stream-type="VideoStreamType.Camera"
35
+ :fill-mode="FillMode.Fill"
36
+ />
37
+ </div>
38
+
39
+ <div
40
+ v-if="mainMember && !mainMember.videoReady"
41
+ class="absolute inset-0 z-10 flex cursor-pointer items-center justify-center px-6"
42
+ @click="emit('focus', mainMember.userId)"
43
+ >
44
+ <div class="text-center">
45
+ <div
46
+ class="mx-auto mb-6 flex h-48 w-48 items-center justify-center rounded-full bg-gradient-to-br from-primary to-primary-hover shadow-2xl"
47
+ >
48
+ <span class="text-7xl font-semibold text-white">
49
+ {{ mainMember.avatarText }}
50
+ </span>
51
+ </div>
52
+ <h3 class="mb-2 text-3xl font-semibold text-white">
53
+ {{ mainMember.displayName }}
54
+ </h3>
55
+ <span
56
+ class="inline-flex items-center rounded-full bg-medical-success px-3 py-1 text-sm font-semibold text-white"
57
+ >
58
+ {{ t('Medical.Consultation.CameraOff', { role: mainMember.roleLabel }) }}
59
+ </span>
60
+ </div>
61
+ </div>
62
+
63
+ <div
64
+ v-if="thumbnailMembers.length"
65
+ :class="[
66
+ 'absolute z-20 flex flex-col gap-3 overflow-y-auto',
67
+ thumbnailListClass,
68
+ ]"
69
+ >
70
+ <button
71
+ v-for="member in thumbnailMembers"
72
+ :key="member.userId"
73
+ :class="[
74
+ 'relative w-full cursor-pointer overflow-hidden rounded-2xl border border-white/25 bg-gradient-to-br from-gray-700 to-gray-800 shadow-xl',
75
+ thumbnailCardClass,
76
+ ]"
77
+ @click="emit('focus', member.userId)"
78
+ >
79
+ <RoomParticipantView
80
+ v-if="member.participant"
81
+ class="absolute inset-0"
82
+ :participant="member.participant"
83
+ :stream-type="VideoStreamType.Camera"
84
+ :fill-mode="FillMode.Fill"
85
+ />
86
+ <div
87
+ v-if="!member.videoReady"
88
+ class="absolute inset-0 flex items-center justify-center bg-gradient-to-br from-gray-700 to-gray-800 px-3"
89
+ >
90
+ <div class="text-center w-full">
91
+ <div
92
+ class="mx-auto mb-2 flex h-14 w-14 items-center justify-center rounded-full bg-gradient-to-br from-primary to-primary-hover text-xl font-semibold text-white"
93
+ >
94
+ {{ member.avatarText }}
95
+ </div>
96
+ <p class="truncate text-xs font-semibold leading-none text-white">
97
+ {{ member.displayName }}
98
+ </p>
99
+ </div>
100
+ </div>
101
+ <div
102
+ class="absolute bottom-2 left-2 right-2 rounded-xl bg-black/45 px-3 py-2 text-left backdrop-blur-md"
103
+ >
104
+ <p class="truncate text-sm font-semibold text-white">
105
+ {{ member.displayName }}
106
+ </p>
107
+ <p class="mt-1 text-xs leading-none text-white/65">
108
+ {{ member.roleLabel }}
109
+ </p>
110
+ </div>
111
+ </button>
112
+ </div>
113
+ </template>
@@ -0,0 +1,132 @@
1
+ <script setup lang="ts">
2
+ import { useUIKit } from '@tencentcloud/uikit-base-component-vue3';
3
+ import { Search, X } from '@/shared/icons';
4
+ import LoadingSpinner from '@/components/LoadingSpinner.vue';
5
+ import type { DoctorInviteListItem } from '@/features/consultation/types';
6
+
7
+ defineProps<{
8
+ visible: boolean;
9
+ doctorInviteList: DoctorInviteListItem[];
10
+ inviteFeedback?: string;
11
+ invitingDoctorId?: string | null;
12
+ cancellingDoctorId?: string | null;
13
+ }>();
14
+ const { t } = useUIKit();
15
+
16
+ const inviteKeyword = defineModel<string>('inviteKeyword', { default: '' });
17
+ const emit = defineEmits<{
18
+ close: [];
19
+ invite: [userId: string];
20
+ cancelInvite: [userId: string];
21
+ }>();
22
+ </script>
23
+
24
+ <template>
25
+ <div
26
+ v-if="visible"
27
+ class="fixed inset-0 z-[100] bg-black/55 backdrop-blur-sm flex items-center justify-center p-4"
28
+ @click.self="emit('close')"
29
+ >
30
+ <div
31
+ class="w-full max-w-[760px] bg-white rounded-[24px] shadow-[0_20px_60px_rgba(15,23,42,0.25)] overflow-hidden"
32
+ >
33
+ <div
34
+ class="h-[72px] px-6 flex items-center justify-between border-b border-[#EEF2F6]"
35
+ >
36
+ <h3 class="text-[18px] leading-none font-semibold text-[#0F172A]">
37
+ {{ t('Medical.Manage.InviteDoctor') }}
38
+ </h3>
39
+ <button
40
+ @click="emit('close')"
41
+ class="w-8 h-8 rounded-full text-[#94A3B8] hover:bg-[#F1F5F9] hover:text-[#334155] transition-colors flex items-center justify-center"
42
+ >
43
+ <X :size="20" />
44
+ </button>
45
+ </div>
46
+
47
+ <div class="p-6">
48
+ <div class="relative">
49
+ <Search
50
+ class="absolute left-4 top-1/2 -translate-y-1/2 text-[#94A3B8]"
51
+ :size="20"
52
+ />
53
+ <input
54
+ v-model="inviteKeyword"
55
+ class="w-full h-12 rounded-2xl bg-[#F8FAFC] border border-[#EEF2F6] pl-11 pr-4 text-[15px] text-[#0F172A] outline-none focus:border-[#0D9488]"
56
+ :placeholder="t('Medical.Manage.SearchDoctorPlaceholder')"
57
+ />
58
+ </div>
59
+
60
+ <div class="mt-4 space-y-3 max-h-[300px] overflow-y-auto">
61
+ <div
62
+ v-for="item in doctorInviteList"
63
+ :key="item.userId"
64
+ class="h-[92px] rounded-2xl border border-[#EEF2F6] px-5 flex items-center justify-between"
65
+ >
66
+ <div class="flex items-center gap-4 min-w-0">
67
+ <div
68
+ class="w-12 h-12 rounded-full bg-[#0D9488] text-white flex items-center justify-center text-[24px] font-semibold shrink-0"
69
+ >
70
+ {{ item.userName.charAt(0) }}
71
+ </div>
72
+ <div class="min-w-0">
73
+ <p
74
+ class="text-[20px] leading-none font-semibold text-[#0F172A] truncate"
75
+ >
76
+ {{ item.userName }}
77
+ </p>
78
+ <p
79
+ class="mt-1.5 text-[17px] leading-none text-[#6B7280] truncate"
80
+ >
81
+ {{ item.department }} · {{ item.title }}
82
+ </p>
83
+ </div>
84
+ </div>
85
+ <button
86
+ @click="
87
+ item.inviteStatus === 'pending'
88
+ ? emit('cancelInvite', item.userId)
89
+ : emit('invite', item.userId)
90
+ "
91
+ :disabled="
92
+ invitingDoctorId === item.userId ||
93
+ cancellingDoctorId === item.userId
94
+ "
95
+ :class="[
96
+ 'w-[104px] h-[44px] rounded-full text-[16px] font-semibold disabled:opacity-60 disabled:cursor-not-allowed transition-colors',
97
+ item.inviteStatus === 'pending'
98
+ ? 'bg-red-50 text-red-500 border border-red-200 hover:bg-red-100'
99
+ : 'bg-[#0D9488] text-white hover:bg-[#0F766E]',
100
+ ]"
101
+ >
102
+ <LoadingSpinner
103
+ v-if="
104
+ invitingDoctorId === item.userId ||
105
+ cancellingDoctorId === item.userId
106
+ "
107
+ class="mr-1"
108
+ />
109
+ {{
110
+ invitingDoctorId === item.userId
111
+ ? t('Medical.Common.Inviting')
112
+ : cancellingDoctorId === item.userId
113
+ ? t('Medical.Common.Canceling')
114
+ : item.inviteStatus === 'pending'
115
+ ? t('Medical.Common.Cancel')
116
+ : t('Medical.Common.Invite')
117
+ }}
118
+ </button>
119
+ </div>
120
+
121
+ <div
122
+ v-if="doctorInviteList.length === 0"
123
+ class="h-[140px] flex items-center justify-center text-[14px] text-[#94A3B8]"
124
+ >
125
+ {{ t('Medical.Manage.NoInviteDoctor') }}
126
+ </div>
127
+ </div>
128
+ <p class="mt-3 text-xs text-[#0D9488] min-h-5">{{ inviteFeedback }}</p>
129
+ </div>
130
+ </div>
131
+ </div>
132
+ </template>
@@ -0,0 +1,50 @@
1
+ <script setup lang="ts">
2
+ import { useUIKit } from '@tencentcloud/uikit-base-component-vue3';
3
+ import LoadingSpinner from '@/components/LoadingSpinner.vue';
4
+
5
+ defineProps<{
6
+ target: { userId: string; userName: string } | null;
7
+ kickingUserId?: string | null;
8
+ }>();
9
+
10
+ const emit = defineEmits<{
11
+ cancel: [];
12
+ confirm: [];
13
+ }>();
14
+
15
+ const { t } = useUIKit();
16
+ </script>
17
+
18
+ <template>
19
+ <div
20
+ v-if="target"
21
+ class="fixed inset-0 z-[110] bg-black/45 flex items-center justify-center p-4"
22
+ @click.self="emit('cancel')"
23
+ >
24
+ <div
25
+ class="w-full max-w-[360px] rounded-2xl bg-white shadow-[0_16px_40px_rgba(15,23,42,0.24)] p-5"
26
+ >
27
+ <h3 class="text-base font-semibold text-[#0F172A]">{{ t('Medical.Member.ConfirmKickTitle') }}</h3>
28
+ <p class="mt-2 text-sm text-[#64748B] leading-6">
29
+ {{ t('Medical.Member.ConfirmKickMessage', { userName: target.userName }) }}
30
+ </p>
31
+ <div class="mt-5 grid grid-cols-2 gap-3">
32
+ <button
33
+ @click="emit('cancel')"
34
+ :disabled="!!kickingUserId"
35
+ class="h-10 rounded-xl border border-[#E2E8F0] text-[#475569] font-medium disabled:opacity-60"
36
+ >
37
+ {{ t('Medical.Member.CancelButton') }}
38
+ </button>
39
+ <button
40
+ @click="emit('confirm')"
41
+ :disabled="!!kickingUserId"
42
+ class="h-10 rounded-xl bg-red-500 text-white font-medium hover:bg-red-600 disabled:opacity-60"
43
+ >
44
+ <LoadingSpinner v-if="kickingUserId" class="mr-1" />
45
+ {{ kickingUserId ? t('Medical.Member.ProcessingButton') : t('Medical.Member.ConfirmButton') }}
46
+ </button>
47
+ </div>
48
+ </div>
49
+ </div>
50
+ </template>