upload.vue 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. <template>
  2. <div class="common-update">
  3. <div v-if="type === 'preview'">
  4. <div class="no-tips" v-if="fileList.length === 0">暂无附件</div>
  5. <div class="common-perview-image" v-else>
  6. <div class="common-update-image-box" v-for="(item,index) in fileList" :key="item.id">
  7. <div class="el-image video-image" v-if="item.type === 'mp4'">
  8. <div class="video-icon">
  9. <i class="el-icon-video-play" @click="openVideo(item)"></i>
  10. </div>
  11. <img :src="videoPreviewList[item.id]" class="el-image__inner">
  12. </div>
  13. <el-image :src="item.url" :preview-src-list="[item.url]" v-else>
  14. <div slot="error" class="image-slot">
  15. <el-tooltip class="item" effect="dark" :content="item.name" placement="bottom">
  16. <div class="image-text">
  17. <span>{{item.type.toUpperCase()}}</span>
  18. <div class="file-download">
  19. <i class="el-icon-download" @click="download(item)"></i>
  20. </div>
  21. </div>
  22. </el-tooltip>
  23. </div>
  24. </el-image>
  25. </div>
  26. </div>
  27. </div>
  28. <div v-else>
  29. <div class="common-update-image">
  30. <div class="common-update-image-box" v-for="(item,index) in fileList" :key="item.id">
  31. <div class="el-image video-image" v-if="item.type === 'mp4'">
  32. <div class="video-icon">
  33. <i class="el-icon-video-play" @click="openVideo(item)"></i>
  34. </div>
  35. <img :src="videoPreviewList[item.id]" class="el-image__inner">
  36. </div>
  37. <el-image :src="item.url" :preview-src-list="[item.url]" v-else>
  38. <div slot="error" class="image-slot">
  39. <el-tooltip class="item" effect="dark" :content="item.name" placement="bottom">
  40. <div class="image-text">{{item.type.toUpperCase()}}</div>
  41. </el-tooltip>
  42. </div>
  43. </el-image>
  44. <div class="common-update-close" @click="removeFile(index)">
  45. <i class="el-icon-close"></i>
  46. </div>
  47. </div>
  48. <el-upload :action="action" :headers="headers" :show-file-list="false" name="uploadFile"
  49. :before-upload="beforeUpload" :on-success="successFile" :on-error="errorFile" :accept="accept"
  50. v-if="fileList.length < maxLen" :on-progress="progress" :limit="maxLen" :on-exceed="handleExceed"
  51. :multiple="maxLen === 1 ? false : true">
  52. <div class="common-update-button">
  53. <i class="iconfont huifont-xinzeng"></i>
  54. <div class="common-update-button-label">{{text}}</div>
  55. </div>
  56. </el-upload>
  57. </div>
  58. <div class="update-image-tips"><span class="color-red">*</span> 请上传小于10M的文件</div>
  59. </div>
  60. <el-dialog :close-on-click-modal="false" :title="video.name" custom-class="monitor-dialog"
  61. :visible.sync="visible" width="900px" height="500px" :append-to-body="true">
  62. <div class="video-box">
  63. <video v-if="visible" width="900" height="445" controls>
  64. <source :src="video.url" type="video/mp4" />
  65. </video>
  66. </div>
  67. </el-dialog>
  68. </div>
  69. </template>
  70. <script>
  71. import config from '@/config';
  72. import {
  73. getToken
  74. } from '@/uitls/auth';
  75. export default {
  76. props: {
  77. list: {
  78. type: Array,
  79. default: () => {
  80. return []
  81. }
  82. },
  83. type: {
  84. type: String,
  85. default: 'preview'
  86. },
  87. maxLen: {
  88. type: Number,
  89. default: 5
  90. },
  91. text: {
  92. type: String,
  93. default: '上传图片'
  94. },
  95. accept: {
  96. type: String,
  97. default: '.png, .jpg, .jpeg'
  98. },
  99. },
  100. data() {
  101. return {
  102. action: config.baseURL + '/file/filenode/-1', //上传地址
  103. headers: {
  104. token: ''
  105. },
  106. url: '',
  107. srcList: [],
  108. fileList: [],
  109. videoPreviewList: {},
  110. visible: false,
  111. video: {}
  112. };
  113. },
  114. mounted() {
  115. this.headers.token = getToken();
  116. this.fileList = JSON.parse(JSON.stringify(this.list));
  117. },
  118. methods: {
  119. beforeUpload(file) {
  120. //上传前
  121. if ((parseInt(file.size) / 1024 / 1024) > 10) {
  122. //判断上传的文件不大于30M
  123. this.$message.warning('请上传小于10M的文件');
  124. return false;
  125. }
  126. },
  127. progress(e) {
  128. let percent = e.percent >= 100 ? 99 : parseInt(e.percent)
  129. this.$loading({
  130. percent: (percent + '%')
  131. });
  132. },
  133. successFile(response, file, fileList) {
  134. //上传成功
  135. if (!response.data) return this.errorFile();
  136. this.$message.success('上传成功');
  137. let data = response.data;
  138. let typeList = data.name.split('.');
  139. this.fileList.push({
  140. id: data.id,
  141. name: data.name,
  142. url: data.node.url,
  143. type: typeList[typeList.length - 1]
  144. });
  145. this.$loading.close();
  146. },
  147. errorFile() {
  148. //上传失败
  149. this.$message.error('上传失败');
  150. this.$loading.close();
  151. },
  152. removeFile(index) {
  153. this.$confirm('确定要删除该文件?', () => {
  154. this.fileList.splice(index, 1);
  155. });
  156. },
  157. download(item) {
  158. window.location.href = config.baseURL + '/file/filenode/' + item.id;
  159. },
  160. openVideo(item) {
  161. this.video = item;
  162. this.visible = true;
  163. },
  164. handleExceed(files, fileList) {
  165. this.$message.warning('最多上传' + this.maxLen + '个文件,当前已上传' + fileList.length + '个文件,请重新选择');
  166. },
  167. mp4Preview(item) {
  168. const video = document.createElement('video') // 也可以自己创建video
  169. video.src = item.url // url地址 url跟 视频流是一样的
  170. video.crossOrigin = '*' // 解决跨域问题,也就是提示污染资源无法转换视频
  171. video.currentTime = 1 // 第一帧
  172. video.oncanplay = () => {
  173. let canvas = document.createElement('canvas') // 获取 canvas 对象
  174. const ctx = canvas.getContext('2d') // 绘制2d
  175. canvas.width = video.clientWidth ? video.clientWidth : 320 // 获取视频宽度
  176. canvas.height = video.clientHeight ? video.clientHeight : 320 //获取视频高度
  177. // 利用canvas对象方法绘图
  178. ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
  179. // 转换成base64形式
  180. const videoFirstImgsrc = canvas.toDataURL('image/png') // 截取后的视频封面
  181. this.videoPreviewList[item.id] = videoFirstImgsrc;
  182. this.$forceUpdate();
  183. video.remove()
  184. canvas.remove();
  185. }
  186. }
  187. },
  188. watch: {
  189. list(val) {
  190. this.fileList = JSON.parse(JSON.stringify(val));
  191. },
  192. fileList() {
  193. for (let i = 0; i < this.fileList.length; i++) {
  194. if (this.fileList[i].type === 'mp4') {
  195. this.mp4Preview(this.fileList[i])
  196. }
  197. }
  198. }
  199. },
  200. }
  201. </script>
  202. <style lang="scss">
  203. .el-dialog.monitor-dialog {
  204. height: 500px;
  205. margin-top: 20vh !important;
  206. }
  207. .common-update {
  208. display: flex;
  209. flex-wrap: wrap;
  210. .update-image-tips {
  211. font-size: 12px;
  212. opacity: 0.7;
  213. }
  214. .video-image {
  215. cursor: pointer;
  216. position: relative;
  217. &:hover {
  218. .video-icon {
  219. opacity: 1;
  220. }
  221. }
  222. .video-icon {
  223. position: absolute;
  224. top: 0;
  225. left: 0;
  226. right: 0;
  227. bottom: 0;
  228. display: flex;
  229. align-items: center;
  230. justify-content: center;
  231. background: rgba(0, 0, 0, 0.6);
  232. opacity: 0;
  233. transition: opacity 200ms;
  234. i {
  235. font-size: 24px;
  236. }
  237. }
  238. }
  239. .common-update-button {
  240. width: 100px;
  241. height: 100px;
  242. border: 1px dashed $--border-color-base;
  243. display: flex;
  244. flex-direction: column;
  245. justify-content: center;
  246. margin: 4px 0;
  247. .huifont-xinzeng {
  248. line-height: 20px;
  249. font-size: 20px;
  250. }
  251. .common-update-button-label {
  252. font-size: 14px;
  253. line-height: 20px;
  254. padding-top: 10px;
  255. }
  256. }
  257. .common-perview-image {
  258. display: flex;
  259. flex-wrap: wrap;
  260. .el-image {
  261. width: 64px;
  262. height: 64px;
  263. border-radius: 2px;
  264. background: transparent;
  265. }
  266. }
  267. .image-slot {
  268. width: 100%;
  269. height: 100%;
  270. border-radius: 4px;
  271. background: #253642;
  272. position: relative;
  273. .file-download {
  274. position: absolute;
  275. display: none;
  276. align-items: center;
  277. justify-content: center;
  278. font-size: 20px;
  279. background: rgba(0, 0, 0, 0.8);
  280. top: 0;
  281. left: 0;
  282. right: 0;
  283. bottom: 0;
  284. color: #fff;
  285. cursor: pointer;
  286. }
  287. .image-text {
  288. font-size: 20px;
  289. width: 100%;
  290. height: 100%;
  291. display: flex;
  292. align-items: center;
  293. justify-content: center;
  294. color: #fff;
  295. font-weight: bold;
  296. &:hover {
  297. .file-download {
  298. display: flex;
  299. }
  300. }
  301. }
  302. }
  303. .common-update-image-box {
  304. display: flex;
  305. position: relative;
  306. margin-right: 4px;
  307. }
  308. .common-update-image {
  309. display: flex;
  310. flex-wrap: wrap;
  311. .common-update-image-box {
  312. margin: 4px 8px 4px 0;
  313. }
  314. .el-image {
  315. width: 100px;
  316. height: 100px;
  317. border-radius: 2px;
  318. background: transparent;
  319. }
  320. .common-update-close {
  321. position: absolute;
  322. right: -7px;
  323. top: -7px;
  324. background: $--color-danger;
  325. width: 14px;
  326. height: 14px;
  327. line-height: 14px;
  328. text-align: center;
  329. border-radius: 50%;
  330. font-size: 10px;
  331. cursor: pointer;
  332. color: #fff;
  333. }
  334. }
  335. }
  336. </style>