index.vue 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. <template>
  2. <div>
  3. <h1 v-if="NODE_ENV === 'development'">
  4. 我的id:{{ mySocketId }},<n-button @click="copyToClipBoard(mySocketId)">
  5. 复制
  6. </n-button>
  7. </h1>
  8. <div>
  9. <n-input-group>
  10. <n-input-group-label>被控id</n-input-group-label>
  11. <n-input
  12. :style="{ width: '200px' }"
  13. placeholder="请输入被控id"
  14. v-model:value="receiverId"
  15. />
  16. <n-button
  17. type="error"
  18. @click="handleClose"
  19. v-if="appStore.remoteDesk.isRemoteing"
  20. >
  21. 结束控制
  22. </n-button>
  23. <n-button
  24. v-else
  25. type="primary"
  26. @click="handleRemote"
  27. >
  28. 开始远程
  29. </n-button>
  30. </n-input-group>
  31. </div>
  32. <div
  33. class="wrap"
  34. ref="remoteVideoRef"
  35. @mousedown="handleMouseDown"
  36. @mousemove="handleMouseMove"
  37. @mouseup="handleMouseUp"
  38. @dblclick="handleDoublelclick"
  39. @contextmenu="handleContextmenu"
  40. ></div>
  41. </div>
  42. </template>
  43. <script lang="ts" setup>
  44. import { Key } from '@nut-tree/shared';
  45. import { copyToClipBoard, getRandomString } from 'billd-utils';
  46. import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
  47. import { usePull } from '@/hooks/use-pull';
  48. import { useTip } from '@/hooks/use-tip';
  49. import { useAppStore } from '@/store/app';
  50. import { useNetworkStore } from '@/store/network';
  51. import {
  52. RemoteDeskBehaviorEnum,
  53. WsMsgTypeEnum,
  54. WsRemoteDeskBehaviorType,
  55. WsStartRemoteDesk,
  56. } from '@/types/websocket';
  57. import { NODE_ENV } from 'script/constant';
  58. const num = '123456';
  59. const appStore = useAppStore();
  60. const networkStore = useNetworkStore();
  61. const { videoWrapRef, initPull } = usePull(num);
  62. const roomId = ref(num);
  63. const receiverId = ref('');
  64. const remoteVideoRef = ref<HTMLDivElement>();
  65. const isDown = ref(false);
  66. let clickTimer;
  67. let isLongClick = false;
  68. const mySocketId = computed(() => {
  69. return networkStore.wsMap.get(roomId.value)?.socketIo?.id || '-1';
  70. });
  71. onUnmounted(() => {
  72. window.removeEventListener('keydown', handleKeyDown);
  73. handleClose();
  74. });
  75. onMounted(() => {
  76. initPull({
  77. isRemoteDesk: true,
  78. });
  79. videoWrapRef.value = remoteVideoRef.value;
  80. window.addEventListener('keydown', handleKeyDown);
  81. });
  82. watch(
  83. () => appStore.remoteDesk.isClose,
  84. (newval) => {
  85. if (newval) {
  86. networkStore.removeRtc(receiverId.value);
  87. useTip({
  88. content: '远程连接断开',
  89. hiddenCancel: true,
  90. hiddenClose: true,
  91. }).catch();
  92. }
  93. }
  94. );
  95. function handleKeyDown(e: KeyboardEvent) {
  96. // console.log(e.key, e.code);
  97. const keyMap = {
  98. Delete: Key.Delete,
  99. Enter: Key.Enter,
  100. Space: Key.Space,
  101. Backspace: Key.Backspace,
  102. ShiftLeft: Key.LeftShift,
  103. ShiftRight: Key.RightShift,
  104. AltLeft: Key.LeftAlt,
  105. AltRight: Key.RightAlt,
  106. Tab: Key.Tab,
  107. Backquote: Key.Quote,
  108. Backslash: Key.Backslash,
  109. ArrowUp: Key.Up,
  110. ArrowDown: Key.Down,
  111. ArrowLeft: Key.Left,
  112. ArrowRight: Key.Right,
  113. CapsLock: Key.CapsLock,
  114. ControlLeft: Key.LeftControl,
  115. ControlRight: Key.RightControl,
  116. MetaLeft: Key.LeftCmd,
  117. LeftWin: Key.LeftCmd,
  118. MetaRight: Key.RightCmd,
  119. RightWin: Key.RightCmd,
  120. Fn: Key.Fn,
  121. F1: Key.F1,
  122. F2: Key.F2,
  123. F3: Key.F3,
  124. F4: Key.F4,
  125. F5: Key.F5,
  126. F6: Key.F6,
  127. F7: Key.F7,
  128. F8: Key.F8,
  129. F9: Key.F9,
  130. F10: Key.F10,
  131. F11: Key.F11,
  132. F12: Key.F12,
  133. F13: Key.F13,
  134. F14: Key.F14,
  135. F15: Key.F15,
  136. F16: Key.F16,
  137. F17: Key.F17,
  138. F18: Key.F18,
  139. F19: Key.F19,
  140. F20: Key.F20,
  141. F21: Key.F21,
  142. F22: Key.F22,
  143. F23: Key.F23,
  144. F24: Key.F24,
  145. };
  146. networkStore.rtcMap
  147. .get(receiverId.value)
  148. ?.dataChannelSend<WsRemoteDeskBehaviorType['data']>({
  149. requestId: getRandomString(8),
  150. msgType: WsMsgTypeEnum.remoteDeskBehavior,
  151. data: {
  152. roomId: roomId.value,
  153. sender: mySocketId.value,
  154. receiver: receiverId.value,
  155. type: RemoteDeskBehaviorEnum.keyboardType,
  156. keyboardtype: keyMap[e.code] || e.key,
  157. x: 0,
  158. y: 0,
  159. },
  160. });
  161. }
  162. function handleDoublelclick() {
  163. networkStore.rtcMap
  164. .get(receiverId.value)
  165. ?.dataChannelSend<WsRemoteDeskBehaviorType['data']>({
  166. requestId: getRandomString(8),
  167. msgType: WsMsgTypeEnum.remoteDeskBehavior,
  168. data: {
  169. roomId: roomId.value,
  170. sender: mySocketId.value,
  171. receiver: receiverId.value,
  172. type: RemoteDeskBehaviorEnum.doubleClick,
  173. keyboardtype: 0,
  174. x: 0,
  175. y: 0,
  176. },
  177. });
  178. }
  179. function handleContextmenu() {
  180. networkStore.rtcMap
  181. .get(receiverId.value)
  182. ?.dataChannelSend<WsRemoteDeskBehaviorType['data']>({
  183. requestId: getRandomString(8),
  184. msgType: WsMsgTypeEnum.remoteDeskBehavior,
  185. data: {
  186. roomId: roomId.value,
  187. sender: mySocketId.value,
  188. receiver: receiverId.value,
  189. type: RemoteDeskBehaviorEnum.rightClick,
  190. keyboardtype: 0,
  191. x: 0,
  192. y: 0,
  193. },
  194. });
  195. }
  196. function handleMouseDown(event: MouseEvent) {
  197. isDown.value = true;
  198. clickTimer = setTimeout(function () {
  199. console.log('长按');
  200. isLongClick = true;
  201. clearTimeout(clickTimer);
  202. }, 300);
  203. // 获取点击相对于视窗的位置
  204. const clickX = event.clientX;
  205. const clickY = event.clientY;
  206. // 获取目标元素的位置和尺寸信息
  207. // @ts-ignore
  208. const rect: DOMRect = event.target.getBoundingClientRect();
  209. // 计算点击位置相对于元素的坐标
  210. const xInsideElement = clickX - rect.left;
  211. const yInsideElement = clickY - rect.top;
  212. const x = (xInsideElement / rect.width) * 1000;
  213. const y = (yInsideElement / rect.height) * 1000;
  214. console.log('handleMouseDown', x, y, xInsideElement, yInsideElement);
  215. networkStore.rtcMap
  216. .get(receiverId.value)
  217. ?.dataChannelSend<WsRemoteDeskBehaviorType['data']>({
  218. requestId: getRandomString(8),
  219. msgType: WsMsgTypeEnum.remoteDeskBehavior,
  220. data: {
  221. roomId: roomId.value,
  222. sender: mySocketId.value,
  223. receiver: receiverId.value,
  224. keyboardtype: 0,
  225. type: isLongClick
  226. ? RemoteDeskBehaviorEnum.pressButtonLeft
  227. : RemoteDeskBehaviorEnum.pressButtonLeft,
  228. x,
  229. y,
  230. },
  231. });
  232. }
  233. function handleMouseMove(event: MouseEvent) {
  234. // 获取点击相对于视窗的位置
  235. const clickX = event.clientX;
  236. const clickY = event.clientY;
  237. // 获取目标元素的位置和尺寸信息
  238. // @ts-ignore
  239. const rect: DOMRect = event.target.getBoundingClientRect();
  240. // 计算点击位置相对于元素的坐标
  241. const xInsideElement = clickX - rect.left;
  242. const yInsideElement = clickY - rect.top;
  243. const x = (xInsideElement / rect.width) * 1000;
  244. const y = (yInsideElement / rect.height) * 1000;
  245. console.log('handleMouseMove', x, y, xInsideElement, yInsideElement);
  246. networkStore.rtcMap
  247. .get(receiverId.value)
  248. ?.dataChannelSend<WsRemoteDeskBehaviorType['data']>({
  249. requestId: getRandomString(8),
  250. msgType: WsMsgTypeEnum.remoteDeskBehavior,
  251. data: {
  252. roomId: roomId.value,
  253. sender: mySocketId.value,
  254. receiver: receiverId.value,
  255. type: RemoteDeskBehaviorEnum.move,
  256. keyboardtype: 0,
  257. x,
  258. y,
  259. },
  260. });
  261. }
  262. function handleMouseUp(event: MouseEvent) {
  263. if (clickTimer) {
  264. clearTimeout(clickTimer);
  265. }
  266. isDown.value = false;
  267. // 获取点击相对于视窗的位置
  268. const clickX = event.clientX;
  269. const clickY = event.clientY;
  270. // 获取目标元素的位置和尺寸信息
  271. // @ts-ignore
  272. const rect: DOMRect = event.target.getBoundingClientRect();
  273. // 计算点击位置相对于元素的坐标
  274. const xInsideElement = clickX - rect.left;
  275. const yInsideElement = clickY - rect.top;
  276. const x = (xInsideElement / rect.width) * 1000;
  277. const y = (yInsideElement / rect.height) * 1000;
  278. console.log('handleMouseUp', x, y, xInsideElement, yInsideElement);
  279. networkStore.rtcMap
  280. .get(receiverId.value)
  281. ?.dataChannelSend<WsRemoteDeskBehaviorType['data']>({
  282. requestId: getRandomString(8),
  283. msgType: WsMsgTypeEnum.remoteDeskBehavior,
  284. data: {
  285. roomId: roomId.value,
  286. sender: mySocketId.value,
  287. receiver: receiverId.value,
  288. keyboardtype: 0,
  289. type: isLongClick
  290. ? RemoteDeskBehaviorEnum.releaseButtonLeft
  291. : RemoteDeskBehaviorEnum.releaseButtonLeft,
  292. x,
  293. y,
  294. },
  295. });
  296. isLongClick = false;
  297. }
  298. function handleClose() {
  299. networkStore.removeRtc(receiverId.value);
  300. }
  301. function handleRemote() {
  302. networkStore.wsMap.get(roomId.value)?.send<WsStartRemoteDesk['data']>({
  303. requestId: getRandomString(8),
  304. msgType: WsMsgTypeEnum.startRemoteDesk,
  305. data: {
  306. roomId: roomId.value,
  307. sender: mySocketId.value,
  308. receiver: receiverId.value,
  309. },
  310. });
  311. }
  312. </script>
  313. <style lang="scss" scoped>
  314. .wrap {
  315. line-height: 0;
  316. cursor: none;
  317. }
  318. </style>