videoUpload.vue 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. <template>
  2. <view class="video-uploader">
  3. <up-upload
  4. :file-list="fileList"
  5. :max-count="maxCount"
  6. accept="video"
  7. :preview-full-image="false"
  8. :max-size="maxSize * 1024 * 1024"
  9. @after-read="handleAfterRead"
  10. @delete="handleDelete"
  11. >
  12. <view class="upload-btn">
  13. <u-icon name="plus" size="32" color="#999"></u-icon>
  14. <text class="upload-text">{{
  15. fileList.length ? t("重新选择") : t("选择视频")
  16. }}</text>
  17. </view>
  18. </up-upload>
  19. <!-- 预览 -->
  20. <view v-if="fileList.length" class="preview">
  21. <video class="preview-video" :src="fileList[0].url" controls></video>
  22. </view>
  23. <!-- 上传进度 -->
  24. <u-line-progress
  25. v-if="uploading"
  26. :percentage="progress"
  27. stroke-width="6"
  28. active-color="#3c9cff"
  29. :show-text="true"
  30. class="preview"
  31. />
  32. </view>
  33. </template>
  34. <script setup>
  35. import { ref, watch } from "vue";
  36. import { $uploadFile, Toast } from "@/utils";
  37. import { t } from "@/locale";
  38. const props = defineProps({
  39. // 初始图片列表
  40. modelValue: {
  41. type: Array,
  42. default: () => [],
  43. },
  44. // 最大上传数量
  45. maxCount: {
  46. type: Number,
  47. default: 1,
  48. },
  49. maxSize: {
  50. type: Number,
  51. default: 50,
  52. },
  53. // 是否多选
  54. multiple: {
  55. type: Boolean,
  56. default: false,
  57. },
  58. disabled: {
  59. type: Boolean,
  60. default: false,
  61. },
  62. // 上传按钮文字
  63. uploadText: {
  64. type: String,
  65. default: "上传图片",
  66. },
  67. });
  68. const fileList = ref([]);
  69. const uploading = ref(false);
  70. const progress = ref(0);
  71. const emit = defineEmits(["update:modelValue", "success", "delete", "error"]);
  72. watch(
  73. () => props.modelValue,
  74. (newVal) => {
  75. fileList.value = newVal.map((item) => {
  76. return typeof item === "string" ? { url: item } : item;
  77. });
  78. },
  79. { immediate: true }
  80. );
  81. /**
  82. * 文件选择后处理
  83. */
  84. const handleAfterRead = async (event) => {
  85. const file = Array.isArray(event.file) ? event.file[0] : event.file;
  86. uploading.value = true;
  87. progress.value = 0;
  88. try {
  89. const uploadFiles = Array.isArray(file) ? file : [file];
  90. const results = await Promise.all(
  91. uploadFiles.map((file) => {
  92. return $uploadFile({
  93. filePath: file.url,
  94. name: "file",
  95. onProgress(e) {
  96. progress.value = e.progress;
  97. },
  98. });
  99. })
  100. );
  101. const newFiles = results.map((res) => ({
  102. url: res.data.url,
  103. name: res.data.name,
  104. status: "success",
  105. }));
  106. fileList.value = [...fileList.value, ...newFiles];
  107. emit("update:modelValue", fileList.value);
  108. emit("success", newFiles);
  109. Toast(t("成功"));
  110. } catch (error) {
  111. emit("error", error);
  112. Toast(t("失败"));
  113. } finally {
  114. uploading.value = false;
  115. }
  116. };
  117. const handleDelete = (event) => {
  118. const { index } = event;
  119. const deletedFile = fileList.value[index];
  120. fileList.value.splice(index, 1);
  121. progress.value = 0;
  122. emit("update:modelValue", fileList.value);
  123. emit("delete", deletedFile);
  124. };
  125. defineExpose({
  126. clearFiles: () => {
  127. fileList.value = [];
  128. progress.value = 0;
  129. emit("update:modelValue", []);
  130. },
  131. });
  132. </script>
  133. <style lang="less" scoped>
  134. .video-uploader {
  135. padding: 10rpx;
  136. .upload-btn {
  137. display: flex;
  138. flex-direction: column;
  139. align-items: center;
  140. justify-content: center;
  141. width: 150rpx;
  142. height: 150rpx;
  143. border: 1px dashed #c0c4cc;
  144. border-radius: 8rpx;
  145. background-color: #f5f7fa;
  146. .upload-text {
  147. margin-top: 10rpx;
  148. font-size: 24rpx;
  149. color: #909399;
  150. }
  151. }
  152. .preview {
  153. margin-top: 20rpx;
  154. .preview-video {
  155. width: 100%;
  156. max-height: 200rpx;
  157. border-radius: 12rpx;
  158. background-color: #000;
  159. }
  160. }
  161. }
  162. </style>