index.vue 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  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. function handleKeyDown(e: KeyboardEvent) {
  83. // console.log(e.key, e.code);
  84. const keyMap = {
  85. Enter: Key.Enter,
  86. Space: Key.Space,
  87. Backspace: Key.Backspace,
  88. ShiftLeft: Key.LeftShift,
  89. ShiftRight: Key.RightShift,
  90. AltLeft: Key.LeftAlt,
  91. AltRight: Key.RightAlt,
  92. Tab: Key.Tab,
  93. Backquote: Key.Quote,
  94. Backslash: Key.Backslash,
  95. ArrowUp: Key.Up,
  96. ArrowDown: Key.Down,
  97. ArrowLeft: Key.Left,
  98. ArrowRight: Key.Right,
  99. CapsLock: Key.CapsLock,
  100. ControlLeft: Key.LeftControl,
  101. ControlRight: Key.RightControl,
  102. MetaLeft: Key.LeftCmd,
  103. LeftWin: Key.LeftCmd,
  104. MetaRight: Key.RightCmd,
  105. RightWin: Key.RightCmd,
  106. Fn: Key.Fn,
  107. F1: Key.F1,
  108. F2: Key.F2,
  109. F3: Key.F3,
  110. F4: Key.F4,
  111. F5: Key.F5,
  112. F6: Key.F6,
  113. F7: Key.F7,
  114. F8: Key.F8,
  115. F9: Key.F9,
  116. F10: Key.F10,
  117. F11: Key.F11,
  118. F12: Key.F12,
  119. F13: Key.F13,
  120. F14: Key.F14,
  121. F15: Key.F15,
  122. F16: Key.F16,
  123. F17: Key.F17,
  124. F18: Key.F18,
  125. F19: Key.F19,
  126. F20: Key.F20,
  127. F21: Key.F21,
  128. F22: Key.F22,
  129. F23: Key.F23,
  130. F24: Key.F24,
  131. };
  132. networkStore.rtcMap
  133. .get(receiverId.value)
  134. ?.dataChannelSend<WsRemoteDeskBehaviorType['data']>({
  135. requestId: getRandomString(8),
  136. msgType: WsMsgTypeEnum.remoteDeskBehavior,
  137. data: {
  138. roomId: roomId.value,
  139. sender: mySocketId.value,
  140. receiver: receiverId.value,
  141. type: RemoteDeskBehaviorEnum.keyboardType,
  142. keyboardtype: keyMap[e.code] || e.key,
  143. x: 0,
  144. y: 0,
  145. },
  146. });
  147. }
  148. watch(
  149. () => appStore.remoteDesk.isClose,
  150. (newval) => {
  151. if (newval) {
  152. networkStore.removeRtc(receiverId.value);
  153. useTip({
  154. content: '远程连接断开',
  155. hiddenCancel: true,
  156. hiddenClose: true,
  157. }).catch();
  158. }
  159. }
  160. );
  161. function handleDoublelclick() {
  162. networkStore.rtcMap
  163. .get(receiverId.value)
  164. ?.dataChannelSend<WsRemoteDeskBehaviorType['data']>({
  165. requestId: getRandomString(8),
  166. msgType: WsMsgTypeEnum.remoteDeskBehavior,
  167. data: {
  168. roomId: roomId.value,
  169. sender: mySocketId.value,
  170. receiver: receiverId.value,
  171. type: RemoteDeskBehaviorEnum.doubleClick,
  172. keyboardtype: 0,
  173. x: 0,
  174. y: 0,
  175. },
  176. });
  177. }
  178. function handleContextmenu() {
  179. networkStore.rtcMap
  180. .get(receiverId.value)
  181. ?.dataChannelSend<WsRemoteDeskBehaviorType['data']>({
  182. requestId: getRandomString(8),
  183. msgType: WsMsgTypeEnum.remoteDeskBehavior,
  184. data: {
  185. roomId: roomId.value,
  186. sender: mySocketId.value,
  187. receiver: receiverId.value,
  188. type: RemoteDeskBehaviorEnum.rightClick,
  189. keyboardtype: 0,
  190. x: 0,
  191. y: 0,
  192. },
  193. });
  194. }
  195. function handleMouseDown(event: MouseEvent) {
  196. isDown.value = true;
  197. clickTimer = setTimeout(function () {
  198. console.log('长按');
  199. isLongClick = true;
  200. clearTimeout(clickTimer);
  201. }, 300);
  202. // 获取点击相对于视窗的位置
  203. const clickX = event.clientX;
  204. const clickY = event.clientY;
  205. // 获取目标元素的位置和尺寸信息
  206. // @ts-ignore
  207. const rect: DOMRect = event.target.getBoundingClientRect();
  208. // 计算点击位置相对于元素的坐标
  209. const xInsideElement = clickX - rect.left;
  210. const yInsideElement = clickY - rect.top;
  211. const x = (xInsideElement / rect.width) * 1000;
  212. const y = (yInsideElement / rect.height) * 1000;
  213. console.log('handleMouseDown', x, y, xInsideElement, yInsideElement);
  214. networkStore.rtcMap
  215. .get(receiverId.value)
  216. ?.dataChannelSend<WsRemoteDeskBehaviorType['data']>({
  217. requestId: getRandomString(8),
  218. msgType: WsMsgTypeEnum.remoteDeskBehavior,
  219. data: {
  220. roomId: roomId.value,
  221. sender: mySocketId.value,
  222. receiver: receiverId.value,
  223. keyboardtype: 0,
  224. type: isLongClick
  225. ? RemoteDeskBehaviorEnum.pressButtonLeft
  226. : RemoteDeskBehaviorEnum.pressButtonLeft,
  227. x,
  228. y,
  229. },
  230. });
  231. }
  232. function handleMouseMove(event: MouseEvent) {
  233. // 获取点击相对于视窗的位置
  234. const clickX = event.clientX;
  235. const clickY = event.clientY;
  236. // 获取目标元素的位置和尺寸信息
  237. // @ts-ignore
  238. const rect: DOMRect = event.target.getBoundingClientRect();
  239. // 计算点击位置相对于元素的坐标
  240. const xInsideElement = clickX - rect.left;
  241. const yInsideElement = clickY - rect.top;
  242. const x = (xInsideElement / rect.width) * 1000;
  243. const y = (yInsideElement / rect.height) * 1000;
  244. console.log('handleMouseMove', x, y, xInsideElement, yInsideElement);
  245. networkStore.rtcMap
  246. .get(receiverId.value)
  247. ?.dataChannelSend<WsRemoteDeskBehaviorType['data']>({
  248. requestId: getRandomString(8),
  249. msgType: WsMsgTypeEnum.remoteDeskBehavior,
  250. data: {
  251. roomId: roomId.value,
  252. sender: mySocketId.value,
  253. receiver: receiverId.value,
  254. type: RemoteDeskBehaviorEnum.move,
  255. keyboardtype: 0,
  256. x,
  257. y,
  258. },
  259. });
  260. }
  261. function handleMouseUp(event: MouseEvent) {
  262. if (clickTimer) {
  263. clearTimeout(clickTimer);
  264. }
  265. isDown.value = false;
  266. // 获取点击相对于视窗的位置
  267. const clickX = event.clientX;
  268. const clickY = event.clientY;
  269. // 获取目标元素的位置和尺寸信息
  270. // @ts-ignore
  271. const rect: DOMRect = event.target.getBoundingClientRect();
  272. // 计算点击位置相对于元素的坐标
  273. const xInsideElement = clickX - rect.left;
  274. const yInsideElement = clickY - rect.top;
  275. const x = (xInsideElement / rect.width) * 1000;
  276. const y = (yInsideElement / rect.height) * 1000;
  277. console.log('handleMouseUp', x, y, xInsideElement, yInsideElement);
  278. networkStore.rtcMap
  279. .get(receiverId.value)
  280. ?.dataChannelSend<WsRemoteDeskBehaviorType['data']>({
  281. requestId: getRandomString(8),
  282. msgType: WsMsgTypeEnum.remoteDeskBehavior,
  283. data: {
  284. roomId: roomId.value,
  285. sender: mySocketId.value,
  286. receiver: receiverId.value,
  287. keyboardtype: 0,
  288. type: isLongClick
  289. ? RemoteDeskBehaviorEnum.releaseButtonLeft
  290. : RemoteDeskBehaviorEnum.releaseButtonLeft,
  291. x,
  292. y,
  293. },
  294. });
  295. isLongClick = false;
  296. }
  297. function handleClose() {
  298. networkStore.removeRtc(receiverId.value);
  299. }
  300. function handleRemote() {
  301. networkStore.wsMap.get(roomId.value)?.send<WsStartRemoteDesk['data']>({
  302. requestId: getRandomString(8),
  303. msgType: WsMsgTypeEnum.startRemoteDesk,
  304. data: {
  305. roomId: roomId.value,
  306. sender: mySocketId.value,
  307. receiver: receiverId.value,
  308. },
  309. });
  310. }
  311. </script>
  312. <style lang="scss" scoped>
  313. .wrap {
  314. line-height: 0;
  315. cursor: none;
  316. }
  317. </style>