file.vue 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. <template>
  2. <div class="sc-upload-file">
  3. <el-upload ref="uploader"
  4. :class="hideAdd && 'el-upload-hide-add'"
  5. v-model:file-list="defaultFileList"
  6. action=""
  7. :accept="accept"
  8. :limit="limit"
  9. :multiple="multiple"
  10. :disabled="disabled"
  11. show-file-list
  12. :http-request="request"
  13. :before-upload="before"
  14. :before-remove="beforeRemove"
  15. :on-success="success"
  16. :on-error="error"
  17. :on-preview="handlePreview"
  18. :on-exceed="handleExceed">
  19. <slot>
  20. <el-button type="primary" :disabled="disabled">Click to upload</el-button>
  21. </slot>
  22. <template #tip>
  23. <div v-if="tip" class="el-upload__tip">{{ tip }}</div>
  24. </template>
  25. </el-upload>
  26. <span style="display: none!important;"><el-input v-model="value"></el-input></span>
  27. </div>
  28. <sc-image-viewer :showViewer="showPictureViewer" :imageList="previewImageList" teleported @close="showPictureViewer = false"></sc-image-viewer>
  29. <sc-video-viewer v-if="showVideoViewer" :videoUrl="previewVideoUrl" hideOnModal @close="showVideoViewer = false"></sc-video-viewer>
  30. </template>
  31. <script>
  32. import config from "@/config/upload";
  33. export default {
  34. props: {
  35. modelValue: { type: Array, default: () => [] },
  36. tip: { type: String, default: "" },
  37. accept: { type: String, default: "" },
  38. maxSize: { type: Number, default: 50 },
  39. limit: { type: Number, default: 0 },
  40. multiple: { type: Boolean, default: true },
  41. disabled: { type: Boolean, default: false },
  42. hideAdd: { type: Boolean, default: false },
  43. onSuccess: { type: Function, default: () => { return true } }
  44. },
  45. data() {
  46. return {
  47. value: "",
  48. defaultFileList: [],
  49. showPictureViewer: false,
  50. showVideoViewer: false,
  51. previewImageList: [],
  52. previewVideoUrl: ""
  53. }
  54. },
  55. watch: {
  56. modelValue(val) {
  57. if (JSON.stringify(val) != JSON.stringify(this.formatArr(this.defaultFileList))) {
  58. this.defaultFileList = val;
  59. this.value = val;
  60. }
  61. },
  62. defaultFileList: {
  63. deep: true,
  64. handler(val) {
  65. this.$emit("update:modelValue", this.formatArr(val));
  66. this.value = val.map(v => v.path).join(",");
  67. }
  68. }
  69. },
  70. mounted() {
  71. this.defaultFileList = this.modelValue;
  72. this.value = this.modelValue;
  73. },
  74. methods: {
  75. // 格式化数组值
  76. formatArr(arr) {
  77. return arr.map(item => ({ id: item.id, name: item.name, mineType: item.mineType, path: item.path }));
  78. },
  79. before(file) {
  80. const maxSize = file.size / 1024 / 1024 < this.maxSize;
  81. if (!maxSize) {
  82. this.$message.warning(`上传文件大小不能超过 ${this.maxSize}MB!`);
  83. return false;
  84. }
  85. },
  86. success(res, file) {
  87. let os = this.onSuccess(res, file);
  88. if (os != undefined && os == false) return false;
  89. file.name = res.fileName;
  90. file.path = res.path;
  91. file.mineType = res.mineType;
  92. },
  93. error(message) {
  94. this.$notify.error({ title: "上传文件未成功", message });
  95. },
  96. beforeRemove({ id, name, path }) { // id, name, path, size
  97. return this.$confirm(`是否移除 ${name}? 此操作不可逆!`, "提示", {
  98. type: "warning",
  99. confirmButtonText: "移除"
  100. }).then(() => {
  101. const entityID = id || path;
  102. this.$API.common.folder.rm(entityID).then(res => {
  103. if (res.code == 200) return true;
  104. else return false;
  105. }).catch(() => {
  106. return false;
  107. });
  108. }).catch(() => {
  109. return false;
  110. });
  111. },
  112. handleExceed() {
  113. this.$message.warning(`当前设置最多上传 ${this.limit} 个文件,请移除后上传!`);
  114. },
  115. async handlePreview(uploadFile) {
  116. if (config.imageIncludes(uploadFile.mineType)) {
  117. this.showPictureViewer = true;
  118. this.previewImageList = ["/api/folder/" + uploadFile.path];
  119. } else if (config.videoIncludes(uploadFile.mineType)) {
  120. this.showVideoViewer = true;
  121. this.previewVideoUrl = "/api/folder/" + uploadFile.path;
  122. } else {
  123. const res = await this.$API.common.folder.get(uploadFile.path);
  124. const a = document.createElement("a");
  125. a.href = URL.createObjectURL(res);
  126. a.download = uploadFile.name;
  127. a.click();
  128. }
  129. },
  130. request(param) {
  131. const data = new FormData();
  132. data.append(param.filename, param.file);
  133. this.$API.common.folder.up(data, {
  134. onUploadProgress: e => {
  135. const percent = parseInt(((e.loaded / e.total) * 100) | 0, 10);
  136. param.onProgress({ percent });
  137. }
  138. }).then(res => {
  139. if (res.code == 200) param.onSuccess({ path: res.expands.file, fileName: param.file.name, mineType: param.file.type })
  140. else param.onError(res.message || "未知错误");
  141. }).catch(err => param.onError(err));
  142. }
  143. }
  144. }
  145. </script>
  146. <style lang="scss" scoped>
  147. .el-form-item.is-error .sc-upload-file:deep(.el-upload-dragger) {
  148. border-color: var(--el-color-danger);
  149. }
  150. .sc-upload-file {
  151. width: 100%;
  152. :deep(.el-upload-list__item) {
  153. transition: none !important;
  154. }
  155. .el-upload-hide-add {
  156. :deep(.el-upload) {
  157. display: none;
  158. }
  159. :deep(.el-upload-list) {
  160. margin-top: 0;
  161. .el-upload-list__item {
  162. &:hover {
  163. background-color: transparent;
  164. }
  165. .el-upload-list__item-info {
  166. width: 100%;
  167. }
  168. .el-upload-list__item-status-label {
  169. display: none;
  170. }
  171. }
  172. }
  173. }
  174. }
  175. </style>