sign.vue 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. <template>
  2. <ui-group-option class="sign record-sign">
  3. <div class="content">
  4. <span class="cover">
  5. <img :src="getResource(getFileUrl(record.cover))" alt="" v-if="record.cover">
  6. <ui-icon
  7. type="preview"
  8. ctrl
  9. class="preview"
  10. @click="actions.play()"
  11. v-if="record.status === RecordStatus.SUCCESS"
  12. />
  13. </span>
  14. <ui-input
  15. type="text"
  16. :modelValue="record.title"
  17. @update:modelValue="(title: string) => $emit('updateTitle', title.trim())"
  18. v-show="isEditTitle"
  19. ref="inputRef"
  20. height="28px"
  21. :maxlength="15"
  22. />
  23. <div class="title" v-show="!isEditTitle">
  24. <p>{{ record.title }}</p>
  25. <span v-if="record.status === RecordStatus.RUN">后台正在处理</span>
  26. </div>
  27. </div>
  28. <div class="action" v-if="edit">
  29. <ui-icon type="order" ctrl />
  30. <ui-more
  31. :options="menus"
  32. style="margin-left: 20px"
  33. @click="(action: keyof typeof actions) => actions[action]()"
  34. />
  35. </div>
  36. <Shot
  37. v-if="isShot"
  38. @close="closeHandler"
  39. @append="appendFragment"
  40. @updateCover="(cover: string) => $emit('updateCover', cover)"
  41. @deleteRecord="$emit('delete')"
  42. :record="record" />
  43. <Preview
  44. v-if="isPlayVideo"
  45. :type="MediaType.video"
  46. :url="record.url"
  47. @close="isPlayVideo = false"
  48. />
  49. </ui-group-option>
  50. </template>
  51. <script lang="ts">
  52. import { defineComponent, ref, computed, watchEffect } from 'vue'
  53. import { getFileUrl, getExtname } from '@/utils'
  54. import { useFocus } from 'bill/hook/useFocus'
  55. import { RecordStatus, createRecordFragment, getRecordFragmentBlobs, recordFragments } from '@/store'
  56. import { saveAs, loadPack } from '@/utils'
  57. import { MediaType, Preview } from '@/components/static-preview/index.vue'
  58. import { getResource } from '@/env'
  59. import Shot from './shot.vue'
  60. import type { PropType } from 'vue'
  61. import type { RecordProcess } from './help'
  62. import { isTemploraryID } from '@/store'
  63. import { Message } from 'bill/index'
  64. export default defineComponent({
  65. props: {
  66. record: {
  67. type: Object as PropType<RecordProcess>,
  68. required: true
  69. },
  70. edit: {
  71. type: Boolean,
  72. required: false,
  73. default: true
  74. }
  75. },
  76. emits: {
  77. 'updateCover': (cover: string) => true,
  78. 'updateTitle': (title: string) => true,
  79. 'delete': () => true
  80. },
  81. setup(props, { emit }) {
  82. const menus = computed(() => {
  83. const base = []
  84. if ([RecordStatus.SUCCESS, RecordStatus.UN].includes(props.record.status)) {
  85. base.push(
  86. { label: '重命名', value: 'rename' },
  87. { label: '继续录制', value: 'continue' },
  88. { label: '下载', value: 'download' },
  89. )
  90. }
  91. base.push({ label: '删除', value: 'delete' })
  92. return base
  93. })
  94. const isShot = ref<boolean>(false)
  95. const inputRef = ref()
  96. const isEditTitle = useFocus(computed(() => inputRef.value?.vmRef.root))
  97. watchEffect(() => {
  98. if (!isEditTitle.value && !props.record.title.length) {
  99. isEditTitle.value = true
  100. Message.warning('视频名称不可为空')
  101. }
  102. })
  103. const isPlayVideo = ref(false)
  104. const actions = {
  105. continue: () => isShot.value = true,
  106. delete: () => emit('delete'),
  107. rename: () => isEditTitle.value = true,
  108. play: () => isPlayVideo.value = true,
  109. download() {
  110. const url = getResource(props.record.url)
  111. const ext = getExtname(url) || 'mp4'
  112. loadPack(saveAs(url, `${props.record.title}.${ext}`))
  113. },
  114. }
  115. props.record.immediately && actions.continue()
  116. const closeHandler = () => {
  117. if (getRecordFragmentBlobs(props.record).length === 0 && isTemploraryID(props.record.id)) {
  118. emit('delete')
  119. }
  120. isShot.value = false
  121. }
  122. const appendFragment = (blobs: Blob[]) => {
  123. recordFragments.value.push(
  124. ...blobs.map(blob => createRecordFragment({ url: blob, recordId: props.record.id }))
  125. )
  126. props.record.status = RecordStatus.UN
  127. }
  128. return {
  129. menus,
  130. actions,
  131. isShot,
  132. isEditTitle,
  133. closeHandler,
  134. inputRef,
  135. RecordStatus,
  136. MediaType,
  137. isPlayVideo,
  138. getResource,
  139. getFileUrl,
  140. appendFragment
  141. }
  142. },
  143. components: {
  144. Shot,
  145. Preview
  146. }
  147. })
  148. </script>
  149. <style lang="scss" src="./style.scss" scoped>
  150. </style>
  151. <style>
  152. .record-sign .ui-input .text.suffix input {
  153. padding-right: 60px;
  154. }
  155. </style>