uploadList1.1.0.vue 7.7 KB

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