UploadFile.vue 7.9 KB

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