sign.vue 4.8 KB

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