sign.vue 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. <template>
  2. <ui-group-option class="sign-guide">
  3. <div class="info">
  4. <div class="guide-cover">
  5. <img :src="getResource(getFileUrl(guide.cover))" />
  6. <ui-icon
  7. type="preview"
  8. class="icon"
  9. ctrl
  10. @click="playSceneGuide(paths, undefined, true)"
  11. v-if="paths.length"
  12. />
  13. </div>
  14. <div>
  15. <p>{{ guide.title }}</p>
  16. </div>
  17. </div>
  18. <div class="actions" v-if="edit">
  19. <ui-more
  20. :options="menus"
  21. style="margin-left: 20px"
  22. @click="(action: keyof typeof actions) => actions[action]()"
  23. />
  24. </div>
  25. </ui-group-option>
  26. </template>
  27. <script setup lang="ts">
  28. import { Guide, getGuidePaths } from '@/store'
  29. import { getFileUrl, saveAs } from '@/utils'
  30. import { getResource } from '@/env'
  31. import { computed, watchEffect, nextTick } from 'vue';
  32. import { playSceneGuide, isScenePlayIng, pauseSceneGuide } from '@/sdk'
  33. import { VideoRecorder } from '@simaq/core';
  34. const props = withDefaults(
  35. defineProps<{ guide: Guide, edit?: boolean }>(),
  36. { edit: true }
  37. )
  38. const emit = defineEmits<{
  39. (e: 'delete'): void
  40. (e: 'play'): void
  41. (e: 'edit'): void
  42. }>()
  43. const menus = [
  44. { label: '编辑', value: 'edit' },
  45. { label: '下载', value: 'download' },
  46. { label: '删除', value: 'delete' },
  47. ]
  48. const actions = {
  49. edit: () => emit('edit'),
  50. delete: () => emit('delete'),
  51. download: () => {
  52. const config: any = {
  53. // uploadUrl: '',
  54. // resolution: '4k',
  55. // autoDownload: false,
  56. // systemAudio: true,
  57. // debug: true,
  58. resolution: '1080p',
  59. autoDownload: false,
  60. platform: 'canvas',
  61. config: {
  62. frameRate: 60,
  63. canvasId: ".scene-canvas > canvas",
  64. },
  65. disbaledAudio: false,
  66. systemAudio: false,
  67. debug: false,
  68. }
  69. const videoRecorder = new VideoRecorder(config);
  70. videoRecorder.startRecord()
  71. let stopWatch: () => void
  72. const stopRecord = () => {
  73. stopWatch && stopWatch()
  74. pauseSceneGuide()
  75. }
  76. videoRecorder.on('record', blob => {
  77. saveAs(new File([blob], '录屏.mp4', { type: 'video/mp4; codecs=h264' }), props.guide.title + ".mp4")
  78. })
  79. videoRecorder.off('*')
  80. videoRecorder.on('startRecord', () => {
  81. playSceneGuide(paths.value, undefined, true)
  82. stopWatch = watchEffect(() => {
  83. if (!isScenePlayIng.value) {
  84. videoRecorder.endRecord()
  85. nextTick(stopWatch)
  86. }
  87. })
  88. })
  89. videoRecorder.on('cancelRecord', stopRecord)
  90. videoRecorder.on('endRecord', stopRecord)
  91. }
  92. }
  93. const paths = computed(() => getGuidePaths(props.guide))
  94. </script>
  95. <style lang="scss" scoped>
  96. .sign-guide {
  97. display: flex;
  98. justify-content: space-between;
  99. align-items: center;
  100. padding: 20px 0;
  101. border-bottom: 1px solid var(--colors-border-color);
  102. &:first-child {
  103. border-top: 1px solid var(--colors-border-color);
  104. }
  105. .info {
  106. flex: 1;
  107. display: flex;
  108. align-items: center;
  109. .guide-cover {
  110. position: relative;
  111. &::after {
  112. content: '';
  113. position: absolute;
  114. inset: 0;
  115. background: rgba(0,0,0,.2)
  116. }
  117. .icon {
  118. position: absolute;
  119. z-index: 1;
  120. left: 50%;
  121. top: 50%;
  122. transform: translate(-50%, -50%);
  123. font-size: 16px;
  124. }
  125. img {
  126. width: 48px;
  127. height: 48px;
  128. object-fit: cover;
  129. border-radius: 4px;
  130. overflow: hidden;
  131. background-color: rgba(255,255,255,.6);
  132. display: block;
  133. }
  134. }
  135. div {
  136. margin-left: 10px;
  137. p {
  138. color: #fff;
  139. font-size: 14px;
  140. margin-bottom: 6px;
  141. }
  142. }
  143. }
  144. .actions {
  145. flex: none;
  146. }
  147. }
  148. </style>