uploadList1.1.0.vue 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. <template>
  2. <!-- 这是一个简单组件,只关注视图层 -->
  3. <div v-show="taskList.length > 0" class="upload-list-component">
  4. <div class="head">
  5. <div class="left">
  6. <i class="iconfont iconmaterial_preview_upload"></i>
  7. <span class="text">正在上传 {{taskList.length}}</span>
  8. </div>
  9. <div class="right">
  10. <i
  11. class="iconfont icon-material_preview_upload_collect"
  12. :class="this.expandSwitch ? '' : 'upsideDown'"
  13. @click="onClickExpand"></i>
  14. </div>
  15. </div>
  16. <div class="content" v-show="expandSwitch">
  17. <div
  18. class="list-item"
  19. v-for="(taskItem, index) in taskList"
  20. :key="index"
  21. >
  22. <div class="left">
  23. <img class="type-icon" :src="uploadFieIconUrl" alt=""/>
  24. <span class="text">{{taskItem.title}}</span>
  25. </div>
  26. <div class="right">
  27. <span :class="{
  28. 'text-fail': taskItem.status === 'FAIL',
  29. 'text-loading': taskItem.status === 'LOADING',
  30. }">
  31. {{taskItem.statusText}}
  32. </span>
  33. <span v-show="taskItem.ifKnowProgress && taskItem.status === 'LOADING'" class="progress-text">{{Math.round(taskItem.progress * 100) + '%'}}</span>
  34. <img
  35. v-show="(taskItem.status === 'LOADING' && taskItem.ifKnowProgress) || taskItem.status === 'FAIL'"
  36. class="cancel-btn"
  37. :src="require('@/assets/images/icons/material_preview_upload_cancel@2x.png')"
  38. @click="onClickCancel(taskItem.uid)"
  39. />
  40. </div>
  41. <div class="progress-bar" v-if="taskItem.ifKnowProgress && taskItem.status === 'LOADING'" :style="{width: Math.round(taskItem.progress * 100) + '%'}"></div>
  42. <div class="progress-bar-unknown" v-if="!taskItem.ifKnowProgress && taskItem.status ==='LOADING'"></div>
  43. </div>
  44. </div>
  45. </div>
  46. </template>
  47. <script>
  48. export default {
  49. props: {
  50. fileType: {
  51. type: String,
  52. default: 'IMAGE', // 'IMAGE' | 'AUDIO' | 'VIDEO'
  53. },
  54. taskList: {
  55. /**
  56. * 每个元素的内部结构:{
  57. * title: String
  58. * ifKnowProgress: Boolean // false则进度条效果是loading而不是具体进度
  59. * progress:Number // 进度,0到1之间。
  60. * status: 'FAIL' | 'LOADING' // 将来可能还需要:'SUCCESS', 'CANCELLED'
  61. * statusText,
  62. * uid: String,
  63. * }
  64. */
  65. type: Array,
  66. default: () => []
  67. // default: () => [
  68. // {
  69. // title: '标题',
  70. // ifKnowProgress: true,
  71. // progress: '0.3',
  72. // status: 'LOADING',
  73. // statusText: '上传中',
  74. // uid: 'a'
  75. // },
  76. // {
  77. // title: '标题',
  78. // ifKnowProgress: false,
  79. // progress: '0.3',
  80. // status: 'LOADING',
  81. // statusText: '后台处理中',
  82. // uid: 'b'
  83. // },
  84. // {
  85. // title: '标题',
  86. // ifKnowProgress: true,
  87. // progress: '0.55',
  88. // status: 'FAIL',
  89. // statusText: '失败',
  90. // },
  91. // ]
  92. },
  93. },
  94. computed: {
  95. uploadFieIconUrl() {
  96. switch (this.fileType) {
  97. case 'IMAGE':
  98. return require('@/assets/images/icons/upload-file-type-icon-image@2x.png')
  99. case 'AUDIO':
  100. return require('@/assets/images/icons/upload-file-type-icon-audio@2x.png')
  101. case 'VIDEO':
  102. return require('@/assets/images/icons/upload-file-type-icon-video@2x.png')
  103. default:
  104. return require('@/assets/images/icons/upload-file-type-icon-image@2x.png')
  105. }
  106. },
  107. },
  108. data() {
  109. return {
  110. expandSwitch: true,
  111. }
  112. },
  113. methods: {
  114. onClickExpand() {
  115. this.expandSwitch = !this.expandSwitch
  116. },
  117. onClickCancel(uid) {
  118. this.$emit('cancel-task', uid)
  119. },
  120. }
  121. }
  122. </script>
  123. <style lang="less" scoped>
  124. .upload-list-component {
  125. width: 550px;
  126. background: #FFFFFF;
  127. box-shadow: 0px 4px 12px 0px rgba(0, 0, 0, 0.1);
  128. border-radius: 4px;
  129. border: 1px solid #E4E7ED;
  130. .head {
  131. display: flex;
  132. justify-content: space-between;
  133. align-items: center;
  134. height: 60px;
  135. padding: 0 26px;
  136. border-bottom: 1px solid #EBEDF0;
  137. .left {
  138. display: flex;
  139. align-items: center;
  140. .iconmaterial_preview_upload {
  141. color: @color;
  142. font-size: 30px;
  143. margin-right: 10px;
  144. animation: spin 2s linear infinite;
  145. }
  146. .text {
  147. font-size: 14px;
  148. font-family: MicrosoftYaHei;
  149. color: #323233;
  150. line-height: 19px;
  151. }
  152. }
  153. .right {
  154. display: flex;
  155. align-items: center;
  156. i {
  157. color: #979797;
  158. cursor: pointer;
  159. font-size: 12px;
  160. transition: transform 0.2s;
  161. &.upsideDown {
  162. transform: rotate(180deg);
  163. }
  164. }
  165. }
  166. }
  167. .content {
  168. padding-left: 26px;
  169. padding-right: 26px;
  170. .list-item {
  171. display: flex;
  172. justify-content: space-between;
  173. align-items: center;
  174. height: 60px;
  175. border-bottom: 1px solid #EBEDF0;
  176. position: relative;
  177. overflow: hidden;
  178. .left {
  179. display: flex;
  180. align-items: center;
  181. width: 60%;
  182. .type-icon {
  183. width: 28px;
  184. height: 28px;
  185. margin-right: 10px;
  186. object-fit: contain;
  187. }
  188. .text {
  189. font-size: 14px;
  190. font-family: MicrosoftYaHei;
  191. color: #323233;
  192. line-height: 19px;
  193. overflow: hidden;
  194. text-overflow: ellipsis;
  195. word-break: keep-all;
  196. white-space: nowrap;
  197. max-width: 90%;
  198. }
  199. }
  200. .right {
  201. display: flex;
  202. justify-content: flex-end;
  203. align-items: center;
  204. width: 40%;
  205. .text-fail {
  206. margin-right: 10px;
  207. font-size: 12px;
  208. font-family: MicrosoftYaHei;
  209. color: #FA5555;
  210. }
  211. .text-loading {
  212. margin-right: 10px;
  213. font-size: 12px;
  214. font-family: MicrosoftYaHei;
  215. color: #969799;
  216. }
  217. .progress-text {
  218. margin-right: 10px;
  219. font-size: 12px;
  220. font-family: MicrosoftYaHei;
  221. color: #969799;
  222. }
  223. img {
  224. width: 18px;
  225. height: 18px;
  226. cursor: pointer;
  227. }
  228. }
  229. .progress-bar {
  230. position: absolute;
  231. left: 0;
  232. bottom: 0;
  233. height: 2px;
  234. background: linear-gradient(144deg, #00AEFB 0%, @color 100%);
  235. }
  236. .progress-bar-unknown {
  237. position: absolute;
  238. left: 0;
  239. bottom: 0;
  240. height: 2px;
  241. background: linear-gradient(144deg, #00AEFB 0%, @color 100%);
  242. width: 330px;
  243. animation: marquee 10s linear infinite
  244. }
  245. }
  246. }
  247. }
  248. @keyframes spin {
  249. 100% {
  250. transform: rotate(360deg)
  251. }
  252. }
  253. @keyframes marquee {
  254. 0% {
  255. transform: translateX(-100%)
  256. }
  257. 100% {
  258. transform: translateX(496px)
  259. }
  260. }
  261. </style>