UploadFile.vue 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. <template>
  2. <div>
  3. <div class="file-area">
  4. <span class="label-text">{{ labelText }}</span>
  5. <div class="upload-box">
  6. <el-upload
  7. ref="upload"
  8. class="file-uploader"
  9. action="no"
  10. :accept="acceptFileType"
  11. :multiple="true"
  12. :show-file-list="false"
  13. :auto-upload="false"
  14. :on-change="onFileChange"
  15. >
  16. <el-button>选取{{ labelText }}文件</el-button>
  17. </el-upload>
  18. <el-button size="small" type="primary" @click="uploadFiles">上传</el-button>
  19. </div>
  20. </div>
  21. <el-divider />
  22. <div class="upload-tip">{{ uploadTip }}</div>
  23. <ul slot="file-list" class="file-list">
  24. <li v-for="(file, i) in file_list" :key="i">
  25. <div class="file-name">
  26. <span>
  27. <SvgIcon :icon-class="iconClass" size="12" />
  28. <span>{{ file.file_name ?? file.name }}</span>
  29. <!-- <span>({{ file.size }})</span> -->
  30. </span>
  31. <span v-show="file.progress > 0 && file.progress < 100"> {{ file.progress }}% </span>
  32. <span v-show="file.file_id"> 完成 </span>
  33. </div>
  34. <SvgIcon icon-class="delete-black" size="12" @click="removeFile(file, i)" />
  35. <SvgIcon v-show="moduleData.type == 'picture'" icon-class="mark" size="12" @click="viewDialog(file)" />
  36. </li>
  37. </ul>
  38. <FillDescribe :file-data="curFile" :visible.sync="visible" @fillDescribeToFile="fillDescribeToFile" />
  39. </div>
  40. </template>
  41. <script>
  42. import { fileUpload } from '@/api/app';
  43. import { conversionSize } from '@/utils/common';
  44. import FillDescribe from '../../common/FillDescribe';
  45. export default {
  46. name: 'UploadFile',
  47. components: {
  48. FillDescribe,
  49. },
  50. props: {
  51. // 组件标签
  52. labelText: {
  53. type: String,
  54. default: '',
  55. },
  56. // 上传支持的文件格式
  57. acceptFileType: {
  58. type: String,
  59. default: '',
  60. },
  61. // 提示语
  62. uploadTip: {
  63. type: String,
  64. default: '',
  65. },
  66. // 图标
  67. iconClass: {
  68. type: String,
  69. default: '',
  70. },
  71. moduleData: {
  72. type: Object,
  73. default: () => ({}),
  74. },
  75. },
  76. data() {
  77. return {
  78. curFile: null,
  79. conversionSize,
  80. file_id_list: [],
  81. file_list: [],
  82. visible: false,
  83. };
  84. },
  85. computed: {},
  86. watch: {},
  87. methods: {
  88. // 显示自定义样式文件列表
  89. onFileChange(file, fileList) {
  90. this.afterSelectFile(file);
  91. console.log(123);
  92. fileList.forEach((file) => {
  93. if (!file.progress || file.progress <= 0) file.progress = 0;
  94. if (!file.title) file.title = '';
  95. if (!file.describe) file.describe = '';
  96. });
  97. this.file_list = fileList;
  98. },
  99. // 删除文件
  100. removeFile(file, i) {
  101. this.$confirm('是否删除当前文件?', '提示', {
  102. confirmButtonText: '确定',
  103. cancelButtonText: '取消',
  104. type: 'warning',
  105. })
  106. .then(() => {
  107. this.$refs.upload.handleRemove(file);
  108. if (this.file_list[i].file_id) {
  109. this.file_list.splice(i, 1);
  110. this.file_id_list.splice(i, 1);
  111. }
  112. })
  113. .catch(() => {});
  114. },
  115. // 文件校验
  116. afterSelectFile(file) {
  117. const fileName = file.name;
  118. console.log(this.moduleData.single_size);
  119. let singleSizeTip = `文件[${fileName}]大小超过 ${conversionSize(this.moduleData.single_size)},被移除!`;
  120. if (file.size > this.moduleData.single_size * 1024 * 1024) {
  121. this.$message.error(singleSizeTip);
  122. this.$refs.upload.handleRemove(file);
  123. return false;
  124. }
  125. const suffix = fileName.slice(fileName.lastIndexOf('.') + 1, fileName.length).toLowerCase();
  126. let fileType = [];
  127. let typeTip = '';
  128. if (this.moduleData.type === 'audio') {
  129. fileType = ['mp3', 'acc', 'wma'];
  130. typeTip = '音频文件只能是 mp3、acc、wma 格式!';
  131. } else if (this.moduleData.type === 'picture') {
  132. fileType = ['jpg', 'png', 'jpeg'];
  133. typeTip = '图片文件只能是 jpg、png、jpeg 格式!';
  134. }
  135. const isNeedType = fileType.includes(suffix);
  136. if (!isNeedType) {
  137. typeTip += `,[${fileName}]被移除!`;
  138. this.$message.error(typeTip);
  139. this.$refs.upload.handleRemove(file);
  140. return false;
  141. }
  142. },
  143. // 上传文件
  144. uploadFiles() {
  145. const files = (this.file_list || []).filter((file) => file.uid);
  146. if (files.length <= 0) {
  147. this.$message.error('没有需要上传的文件!');
  148. return false;
  149. }
  150. const totalSize = files.reduce((sum, cur) => sum + Number(cur.size || 0), 0);
  151. if (totalSize > this.moduleData.total_size * 1024 * 1024) {
  152. this.$message.error(`文件总大小不能超过${conversionSize(this.moduleData.total_size)}`);
  153. return false;
  154. }
  155. files.forEach((file) => {
  156. let form = new FormData();
  157. form.append(file.name, file.raw, file.name);
  158. fileUpload('Mid', form, {
  159. handleUploadProgress: (progressEvent) => {
  160. let per = Number((progressEvent.progress * 100).toFixed(2) || 0);
  161. let en = this.file_list.find((p) => p.uid === file.uid);
  162. if (en) {
  163. en.progress = per;
  164. this.$forceUpdate();
  165. }
  166. },
  167. }).then(({ file_info_list }) => {
  168. let file_index = this.file_list.findIndex((p) => p.uid === file.uid);
  169. if (file_index > -1) {
  170. this.file_list[file_index] = file_info_list[0];
  171. this.file_id_list.push(file_info_list[0].file_id);
  172. }
  173. });
  174. });
  175. },
  176. // 显示弹窗
  177. viewDialog(file) {
  178. this.visible = true;
  179. this.curFile = file;
  180. },
  181. // 给文件加介绍
  182. fillDescribeToFile(file) {
  183. let en = this.file_list.find((p) => p.uid === file.uid);
  184. if (en) {
  185. Object.assign(en, file);
  186. }
  187. },
  188. },
  189. };
  190. </script>
  191. <style lang="scss" scoped>
  192. .module-content {
  193. .file-area {
  194. display: flex;
  195. column-gap: 16px;
  196. align-items: center;
  197. .label-text {
  198. font-size: 14px;
  199. color: $font-light-color;
  200. }
  201. div {
  202. flex: 1;
  203. }
  204. }
  205. .el-divider {
  206. margin: 16px 0;
  207. }
  208. .upload-tip {
  209. margin-bottom: 16px;
  210. font-size: 12px;
  211. color: #86909c;
  212. }
  213. .upload-box {
  214. display: flex;
  215. justify-content: space-between;
  216. .file-uploader {
  217. flex: 1;
  218. :deep .el-upload {
  219. &--text {
  220. width: 100%;
  221. background-color: $fill-color;
  222. border-radius: 2px 0 0 2px;
  223. .el-button {
  224. width: 100%;
  225. color: #86909c;
  226. text-align: left;
  227. }
  228. }
  229. }
  230. }
  231. .el-button {
  232. border-radius: 0 2px 2px 0;
  233. }
  234. }
  235. .file-list {
  236. display: flex;
  237. flex-direction: column;
  238. row-gap: 16px;
  239. li {
  240. display: flex;
  241. column-gap: 12px;
  242. align-items: center;
  243. .file-name {
  244. display: flex;
  245. column-gap: 14px;
  246. align-items: center;
  247. justify-content: space-between;
  248. max-width: 360px;
  249. padding: 8px 12px;
  250. font-size: 14px;
  251. color: #1d2129;
  252. background-color: #f7f8fa;
  253. span {
  254. display: flex;
  255. column-gap: 14px;
  256. align-items: center;
  257. }
  258. }
  259. .svg-icon {
  260. cursor: pointer;
  261. }
  262. }
  263. }
  264. }
  265. </style>