file.vue 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  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. :drag="drag"
  10. :multiple="multiple"
  11. :disabled="disabled"
  12. show-file-list
  13. :http-request="request"
  14. :before-upload="before"
  15. :before-remove="beforeRemove"
  16. :on-success="success"
  17. :on-error="error"
  18. :on-preview="handlePreview"
  19. :on-exceed="handleExceed">
  20. <slot>
  21. <div v-if="drag" class="file-empty">
  22. <el-icon><el-icon-upload-filled /></el-icon>
  23. <h4>将文件拖到此处,或<el-text type="primary">点击上传</el-text></h4>
  24. </div>
  25. <vxe-button v-else status="primary" size="mini" content="点击上传" :disabled="disabled"></vxe-button>
  26. </slot>
  27. <template #tip>
  28. <div v-if="tip" :class="['el-upload__tip', tipRequired && 'el-upload__tip-required']">{{ tip }}</div>
  29. </template>
  30. </el-upload>
  31. <span style="display: none!important;"><el-input v-model="value"></el-input></span>
  32. </div>
  33. <file-viewer v-if="showViewer" ref="fileViewer" @closed="showViewer = false"></file-viewer>
  34. </template>
  35. <script>
  36. import XEUtils from "xe-utils";
  37. export default {
  38. props: {
  39. modelValue: { type: Array, default: () => [] },
  40. tip: { type: String, default: "" },
  41. tipRequired: { type: Boolean, default: false },
  42. accept: { type: String, default: "" },
  43. maxSize: { type: Number, default: 50 },
  44. limit: { type: Number, default: 0 },
  45. drag: { type: Boolean, default: false },
  46. multiple: { type: Boolean, default: true },
  47. disabled: { type: Boolean, default: false },
  48. hideAdd: { type: Boolean, default: false },
  49. onSuccess: { type: Function, default: () => true },
  50. params: { type: Object, default: () => ({}) }
  51. },
  52. data() {
  53. return {
  54. value: "",
  55. defaultFileList: [],
  56. showViewer: false
  57. }
  58. },
  59. watch: {
  60. modelValue(val) {
  61. if (JSON.stringify(val) != JSON.stringify(this.formatArr(this.defaultFileList))) {
  62. this.defaultFileList = val;
  63. this.value = val;
  64. }
  65. },
  66. defaultFileList: {
  67. deep: true,
  68. handler(val) {
  69. this.$emit("update:modelValue", this.formatArr(val));
  70. this.value = val.map(v => v.path).join();
  71. }
  72. }
  73. },
  74. mounted() {
  75. this.defaultFileList = this.modelValue;
  76. this.value = this.modelValue;
  77. },
  78. methods: {
  79. // 格式化数组值
  80. formatArr(arr) {
  81. return arr.map(item => ({ id: item.id, name: item.name, contentType: item.contentType, path: item.path }));
  82. },
  83. before(file) {
  84. const maxSize = file.size / 1024 / 1024 < this.maxSize;
  85. if (!maxSize) {
  86. this.$message.warning(`上传文件大小不能超过 ${this.maxSize}MB!`);
  87. return false;
  88. }
  89. },
  90. success(res, file) {
  91. const os = this.onSuccess(res, file);
  92. if (os === false) return false;
  93. file.path = res.path;
  94. file.contentType = file.raw.type;
  95. },
  96. error(message) {
  97. message && this.$notify.error({ title: "上传文件未成功", message });
  98. },
  99. beforeRemove(file) {
  100. if (file.status == "success") {
  101. return new Promise((resolve, reject) => {
  102. this.$confirm(`是否移除 ${file.name}? 此操作不可逆!`, "提示", {
  103. type: "warning",
  104. confirmButtonText: "移除"
  105. }).then(() => {
  106. this.$API.common.minio.rm(XEUtils.pick(file, "id", "path")).then(res => {
  107. this.$emit("removeSuccess");
  108. resolve();
  109. }).catch(() => reject());
  110. }).catch(() => reject());
  111. });
  112. }
  113. },
  114. handleExceed() {
  115. this.$message.warning(`当前设置最多上传 ${this.limit} 个文件,请移除后上传!`);
  116. },
  117. handlePreview(uploadFile) {
  118. this.showViewer = true;
  119. nextTick(() => this.$refs.fileViewer.init(uploadFile));
  120. },
  121. request(param) {
  122. const data = new FormData();
  123. data.append(param.filename, param.file);
  124. XEUtils.objectEach(this.params, (value, key) => data.append(key, value));
  125. this.$API.common.minio.up(data, {
  126. onUploadProgress: e => {
  127. const percent = parseInt(((e.loaded / e.total) * 100) | 0, 10);
  128. param.onProgress({ percent });
  129. },
  130. transformRequest: [function (data, headers) {
  131. // 移除 Axios 可能自动设置的 Content-Type,让浏览器自动设置
  132. delete headers["Content-Type"];
  133. return data;
  134. }]
  135. }).then(res => {
  136. if (res.code == 200) param.onSuccess({ path: res.expands.file });
  137. else param.onError();
  138. }).catch(() => param.onError());
  139. }
  140. }
  141. }
  142. </script>
  143. <style lang="scss" scoped>
  144. .sc-upload-file:deep(.el-upload-dragger) {border-radius: 0;}
  145. .el-form-item.is-error .sc-upload-file:deep(.el-upload-dragger) {border-color: var(--el-color-danger);border-radius: 0;}
  146. .sc-upload-file {
  147. width: 100%;
  148. :deep(.el-upload-list__item) {transition: none !important;}
  149. .el-upload-hide-add {
  150. :deep(.el-upload) {display: none;}
  151. :deep(.el-upload-list) {
  152. margin-top: 0;
  153. .el-upload-list__item {
  154. &:last-child {margin-bottom: 0;}
  155. &:hover {background-color: transparent;}
  156. .el-upload-list__item-info {width: 100%;}
  157. .el-upload-list__item-status-label {display: none;}
  158. }
  159. }
  160. }
  161. .file-empty i {font-size: 28px;}
  162. .file-empty h4 {
  163. font-size: 13px;
  164. font-weight: normal;
  165. color: #8c939d;
  166. .el-text {font-size: inherit;}
  167. }
  168. .el-upload__tip {color: #999;}
  169. .el-upload__tip-required {
  170. position: relative;
  171. &::before {
  172. content: "*";
  173. margin-right: 4px;
  174. color: var(--el-color-danger);
  175. }
  176. }
  177. }
  178. </style>