@tplc/wot 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (328) hide show
  1. package/components/common/AbortablePromise.ts +36 -0
  2. package/components/common/abstracts/_config.scss +7 -0
  3. package/components/common/abstracts/_function.scss +76 -0
  4. package/components/common/abstracts/_mixin.scss +339 -0
  5. package/components/common/abstracts/variable.scss +1346 -0
  6. package/components/common/base64.ts +30 -0
  7. package/components/common/canvasHelper.ts +49 -0
  8. package/components/common/clickoutside.ts +34 -0
  9. package/components/common/dayjs.ts +157 -0
  10. package/components/common/event.ts +8 -0
  11. package/components/common/props.ts +51 -0
  12. package/components/common/util.ts +751 -0
  13. package/components/composables/useCell.ts +13 -0
  14. package/components/composables/useChildren.ts +122 -0
  15. package/components/composables/useCountDown.ts +138 -0
  16. package/components/composables/useLockScroll.ts +39 -0
  17. package/components/composables/useParent.ts +41 -0
  18. package/components/composables/usePopover.ts +193 -0
  19. package/components/composables/useQueue.ts +52 -0
  20. package/components/composables/useRaf.ts +37 -0
  21. package/components/composables/useTouch.ts +44 -0
  22. package/components/composables/useTranslate.ts +22 -0
  23. package/components/wd-action-sheet/index.scss +204 -0
  24. package/components/wd-action-sheet/types.ts +128 -0
  25. package/components/wd-action-sheet/wd-action-sheet.vue +174 -0
  26. package/components/wd-backtop/index.scss +25 -0
  27. package/components/wd-backtop/types.ts +37 -0
  28. package/components/wd-backtop/wd-backtop.vue +48 -0
  29. package/components/wd-badge/index.scss +59 -0
  30. package/components/wd-badge/types.ts +53 -0
  31. package/components/wd-badge/wd-badge.vue +69 -0
  32. package/components/wd-button/index.scss +340 -0
  33. package/components/wd-button/types.ts +112 -0
  34. package/components/wd-button/wd-button.vue +176 -0
  35. package/components/wd-calendar/index.scss +244 -0
  36. package/components/wd-calendar/types.ts +235 -0
  37. package/components/wd-calendar/wd-calendar.vue +456 -0
  38. package/components/wd-calendar-view/index.scss +9 -0
  39. package/components/wd-calendar-view/month/index.scss +151 -0
  40. package/components/wd-calendar-view/month/month.vue +391 -0
  41. package/components/wd-calendar-view/month/types.ts +19 -0
  42. package/components/wd-calendar-view/monthPanel/index.scss +89 -0
  43. package/components/wd-calendar-view/monthPanel/month-panel.vue +388 -0
  44. package/components/wd-calendar-view/monthPanel/types.ts +48 -0
  45. package/components/wd-calendar-view/types.ts +134 -0
  46. package/components/wd-calendar-view/utils.ts +451 -0
  47. package/components/wd-calendar-view/wd-calendar-view.vue +111 -0
  48. package/components/wd-calendar-view/year/index.scss +148 -0
  49. package/components/wd-calendar-view/year/types.ts +19 -0
  50. package/components/wd-calendar-view/year/year.vue +220 -0
  51. package/components/wd-calendar-view/yearPanel/index.scss +24 -0
  52. package/components/wd-calendar-view/yearPanel/types.ts +38 -0
  53. package/components/wd-calendar-view/yearPanel/year-panel.vue +140 -0
  54. package/components/wd-card/index.scss +70 -0
  55. package/components/wd-card/types.ts +30 -0
  56. package/components/wd-card/wd-card.vue +40 -0
  57. package/components/wd-cell/index.scss +189 -0
  58. package/components/wd-cell/types.ts +96 -0
  59. package/components/wd-cell/wd-cell.vue +135 -0
  60. package/components/wd-cell-group/index.scss +55 -0
  61. package/components/wd-cell-group/types.ts +41 -0
  62. package/components/wd-cell-group/wd-cell-group.vue +45 -0
  63. package/components/wd-checkbox/index.scss +285 -0
  64. package/components/wd-checkbox/types.ts +68 -0
  65. package/components/wd-checkbox/wd-checkbox.vue +185 -0
  66. package/components/wd-checkbox-group/index.scss +20 -0
  67. package/components/wd-checkbox-group/types.ts +59 -0
  68. package/components/wd-checkbox-group/wd-checkbox-group.vue +103 -0
  69. package/components/wd-circle/index.scss +18 -0
  70. package/components/wd-circle/types.ts +54 -0
  71. package/components/wd-circle/wd-circle.vue +318 -0
  72. package/components/wd-col/index.scss +19 -0
  73. package/components/wd-col/types.ts +15 -0
  74. package/components/wd-col/wd-col.vue +91 -0
  75. package/components/wd-col-picker/index.scss +241 -0
  76. package/components/wd-col-picker/types.ts +170 -0
  77. package/components/wd-col-picker/wd-col-picker.vue +550 -0
  78. package/components/wd-collapse/index.scss +55 -0
  79. package/components/wd-collapse/types.ts +63 -0
  80. package/components/wd-collapse/wd-collapse.vue +160 -0
  81. package/components/wd-collapse-item/index.scss +79 -0
  82. package/components/wd-collapse-item/types.ts +36 -0
  83. package/components/wd-collapse-item/wd-collapse-item.vue +182 -0
  84. package/components/wd-config-provider/types.ts +1023 -0
  85. package/components/wd-config-provider/wd-config-provider.vue +82 -0
  86. package/components/wd-count-down/index.scss +14 -0
  87. package/components/wd-count-down/types.ts +41 -0
  88. package/components/wd-count-down/utils.ts +52 -0
  89. package/components/wd-count-down/wd-count-down.vue +60 -0
  90. package/components/wd-count-to/index.scss +6 -0
  91. package/components/wd-count-to/types.ts +110 -0
  92. package/components/wd-count-to/wd-count-to.vue +134 -0
  93. package/components/wd-curtain/index.scss +80 -0
  94. package/components/wd-curtain/types.ts +45 -0
  95. package/components/wd-curtain/wd-curtain.vue +174 -0
  96. package/components/wd-datetime-picker/index.scss +243 -0
  97. package/components/wd-datetime-picker/types.ts +225 -0
  98. package/components/wd-datetime-picker/wd-datetime-picker.vue +827 -0
  99. package/components/wd-datetime-picker-view/index.scss +0 -0
  100. package/components/wd-datetime-picker-view/types.ts +137 -0
  101. package/components/wd-datetime-picker-view/wd-datetime-picker-view.vue +514 -0
  102. package/components/wd-divider/index.scss +32 -0
  103. package/components/wd-divider/types.ts +12 -0
  104. package/components/wd-divider/wd-divider.vue +29 -0
  105. package/components/wd-drop-menu/index.scss +89 -0
  106. package/components/wd-drop-menu/types.ts +38 -0
  107. package/components/wd-drop-menu/wd-drop-menu.vue +128 -0
  108. package/components/wd-drop-menu-item/index.scss +66 -0
  109. package/components/wd-drop-menu-item/types.ts +78 -0
  110. package/components/wd-drop-menu-item/wd-drop-menu-item.vue +230 -0
  111. package/components/wd-fab/index.scss +115 -0
  112. package/components/wd-fab/types.ts +61 -0
  113. package/components/wd-fab/wd-fab.vue +257 -0
  114. package/components/wd-form/index.scss +10 -0
  115. package/components/wd-form/types.ts +93 -0
  116. package/components/wd-form/wd-form.vue +185 -0
  117. package/components/wd-form-item/index.scss +17 -0
  118. package/components/wd-form-item/types.ts +22 -0
  119. package/components/wd-form-item/wd-form-item.vue +65 -0
  120. package/components/wd-gap/index.scss +9 -0
  121. package/components/wd-gap/types.ts +23 -0
  122. package/components/wd-gap/wd-gap.vue +39 -0
  123. package/components/wd-grid/index.scss +9 -0
  124. package/components/wd-grid/types.ts +54 -0
  125. package/components/wd-grid/wd-grid.vue +107 -0
  126. package/components/wd-grid-item/index.scss +137 -0
  127. package/components/wd-grid-item/types.ts +74 -0
  128. package/components/wd-grid-item/wd-grid-item.vue +181 -0
  129. package/components/wd-icon/index.scss +1222 -0
  130. package/components/wd-icon/types.ts +21 -0
  131. package/components/wd-icon/wd-icon.vue +53 -0
  132. package/components/wd-icon/wd-icons.ttf +0 -0
  133. package/components/wd-img/index.scss +19 -0
  134. package/components/wd-img/types.ts +53 -0
  135. package/components/wd-img/wd-img.vue +76 -0
  136. package/components/wd-img-cropper/index.scss +227 -0
  137. package/components/wd-img-cropper/types.ts +87 -0
  138. package/components/wd-img-cropper/wd-img-cropper.vue +659 -0
  139. package/components/wd-index-anchor/index.scss +34 -0
  140. package/components/wd-index-anchor/type.ts +9 -0
  141. package/components/wd-index-anchor/wd-index-anchor.vue +57 -0
  142. package/components/wd-index-bar/index.scss +39 -0
  143. package/components/wd-index-bar/type.ts +28 -0
  144. package/components/wd-index-bar/wd-index-bar.vue +158 -0
  145. package/components/wd-input/index.scss +326 -0
  146. package/components/wd-input/types.ts +182 -0
  147. package/components/wd-input/wd-input.vue +327 -0
  148. package/components/wd-input-number/index.scss +132 -0
  149. package/components/wd-input-number/types.ts +78 -0
  150. package/components/wd-input-number/wd-input-number.vue +221 -0
  151. package/components/wd-loading/index.scss +34 -0
  152. package/components/wd-loading/types.ts +31 -0
  153. package/components/wd-loading/wd-loading.vue +90 -0
  154. package/components/wd-loadmore/index.scss +39 -0
  155. package/components/wd-loadmore/types.ts +24 -0
  156. package/components/wd-loadmore/wd-loadmore.vue +53 -0
  157. package/components/wd-message-box/index.scss +121 -0
  158. package/components/wd-message-box/index.ts +95 -0
  159. package/components/wd-message-box/types.ts +116 -0
  160. package/components/wd-message-box/wd-message-box.vue +326 -0
  161. package/components/wd-navbar/index.scss +103 -0
  162. package/components/wd-navbar/types.ts +52 -0
  163. package/components/wd-navbar/wd-navbar.vue +142 -0
  164. package/components/wd-navbar-capsule/index.scss +65 -0
  165. package/components/wd-navbar-capsule/types.ts +0 -0
  166. package/components/wd-navbar-capsule/wd-navbar-capsule.vue +31 -0
  167. package/components/wd-notice-bar/index.scss +86 -0
  168. package/components/wd-notice-bar/types.ts +56 -0
  169. package/components/wd-notice-bar/wd-notice-bar.vue +223 -0
  170. package/components/wd-notify/index.scss +34 -0
  171. package/components/wd-notify/index.ts +59 -0
  172. package/components/wd-notify/types.ts +62 -0
  173. package/components/wd-notify/wd-notify.vue +83 -0
  174. package/components/wd-number-keyboard/index.scss +78 -0
  175. package/components/wd-number-keyboard/key/index.scss +79 -0
  176. package/components/wd-number-keyboard/key/index.vue +76 -0
  177. package/components/wd-number-keyboard/key/types.ts +11 -0
  178. package/components/wd-number-keyboard/types.ts +79 -0
  179. package/components/wd-number-keyboard/wd-number-keyboard.vue +173 -0
  180. package/components/wd-overlay/index.scss +17 -0
  181. package/components/wd-overlay/types.ts +25 -0
  182. package/components/wd-overlay/wd-overlay.vue +46 -0
  183. package/components/wd-pagination/index.scss +57 -0
  184. package/components/wd-pagination/types.ts +41 -0
  185. package/components/wd-pagination/wd-pagination.vue +121 -0
  186. package/components/wd-password-input/index.scss +123 -0
  187. package/components/wd-password-input/types.ts +48 -0
  188. package/components/wd-password-input/wd-password-input.vue +58 -0
  189. package/components/wd-picker/index.scss +216 -0
  190. package/components/wd-picker/types.ts +186 -0
  191. package/components/wd-picker/wd-picker.vue +409 -0
  192. package/components/wd-picker-view/index.scss +91 -0
  193. package/components/wd-picker-view/types.ts +162 -0
  194. package/components/wd-picker-view/wd-picker-view.vue +361 -0
  195. package/components/wd-popover/index.scss +123 -0
  196. package/components/wd-popover/types.ts +69 -0
  197. package/components/wd-popover/wd-popover.vue +216 -0
  198. package/components/wd-popup/index.scss +112 -0
  199. package/components/wd-popup/types.ts +68 -0
  200. package/components/wd-popup/wd-popup.vue +227 -0
  201. package/components/wd-progress/index.scss +62 -0
  202. package/components/wd-progress/types.ts +40 -0
  203. package/components/wd-progress/wd-progress.vue +201 -0
  204. package/components/wd-radio/index.scss +300 -0
  205. package/components/wd-radio/types.ts +42 -0
  206. package/components/wd-radio/wd-radio.vue +136 -0
  207. package/components/wd-radio-group/index.scss +23 -0
  208. package/components/wd-radio-group/types.ts +36 -0
  209. package/components/wd-radio-group/wd-radio-group.vue +54 -0
  210. package/components/wd-rate/index.scss +24 -0
  211. package/components/wd-rate/types.ts +91 -0
  212. package/components/wd-rate/wd-rate.vue +131 -0
  213. package/components/wd-resize/index.scss +26 -0
  214. package/components/wd-resize/types.ts +6 -0
  215. package/components/wd-resize/wd-resize.vue +155 -0
  216. package/components/wd-row/index.scss +10 -0
  217. package/components/wd-row/types.ts +16 -0
  218. package/components/wd-row/wd-row.vue +63 -0
  219. package/components/wd-search/index.scss +148 -0
  220. package/components/wd-search/types.ts +83 -0
  221. package/components/wd-search/wd-search.vue +237 -0
  222. package/components/wd-segmented/index.scss +97 -0
  223. package/components/wd-segmented/types.ts +68 -0
  224. package/components/wd-segmented/wd-segmented.vue +143 -0
  225. package/components/wd-select-picker/index.scss +177 -0
  226. package/components/wd-select-picker/types.ts +116 -0
  227. package/components/wd-select-picker/wd-select-picker.vue +486 -0
  228. package/components/wd-sidebar/index.scss +25 -0
  229. package/components/wd-sidebar/types.ts +28 -0
  230. package/components/wd-sidebar/wd-sidebar.vue +41 -0
  231. package/components/wd-sidebar-item/index.scss +93 -0
  232. package/components/wd-sidebar-item/types.ts +31 -0
  233. package/components/wd-sidebar-item/wd-sidebar-item.vue +114 -0
  234. package/components/wd-skeleton/index.scss +101 -0
  235. package/components/wd-skeleton/index.ts +1 -0
  236. package/components/wd-skeleton/types.ts +69 -0
  237. package/components/wd-skeleton/wd-skeleton.vue +135 -0
  238. package/components/wd-slider/index.scss +91 -0
  239. package/components/wd-slider/types.ts +104 -0
  240. package/components/wd-slider/wd-slider.vue +377 -0
  241. package/components/wd-sort-button/index.scss +86 -0
  242. package/components/wd-sort-button/types.ts +43 -0
  243. package/components/wd-sort-button/wd-sort-button.vue +76 -0
  244. package/components/wd-status-tip/index.scss +37 -0
  245. package/components/wd-status-tip/types.ts +59 -0
  246. package/components/wd-status-tip/wd-status-tip.vue +94 -0
  247. package/components/wd-step/index.scss +236 -0
  248. package/components/wd-step/types.ts +33 -0
  249. package/components/wd-step/wd-step.vue +151 -0
  250. package/components/wd-steps/index.scss +10 -0
  251. package/components/wd-steps/types.ts +59 -0
  252. package/components/wd-steps/wd-steps.vue +37 -0
  253. package/components/wd-sticky/index.scss +9 -0
  254. package/components/wd-sticky/types.ts +13 -0
  255. package/components/wd-sticky/wd-sticky.vue +190 -0
  256. package/components/wd-sticky-box/index.scss +6 -0
  257. package/components/wd-sticky-box/types.ts +20 -0
  258. package/components/wd-sticky-box/wd-sticky-box.vue +154 -0
  259. package/components/wd-swipe-action/index.scss +22 -0
  260. package/components/wd-swipe-action/types.ts +43 -0
  261. package/components/wd-swipe-action/wd-swipe-action.vue +307 -0
  262. package/components/wd-swiper/index.scss +23 -0
  263. package/components/wd-swiper/types.ts +189 -0
  264. package/components/wd-swiper/wd-swiper.vue +202 -0
  265. package/components/wd-swiper-nav/index.scss +153 -0
  266. package/components/wd-swiper-nav/types.ts +42 -0
  267. package/components/wd-swiper-nav/wd-swiper-nav.vue +37 -0
  268. package/components/wd-switch/index.scss +58 -0
  269. package/components/wd-switch/types.ts +56 -0
  270. package/components/wd-switch/wd-switch.vue +83 -0
  271. package/components/wd-tab/index.scss +8 -0
  272. package/components/wd-tab/types.ts +20 -0
  273. package/components/wd-tab/wd-tab.vue +100 -0
  274. package/components/wd-tabbar/index.scss +57 -0
  275. package/components/wd-tabbar/types.ts +88 -0
  276. package/components/wd-tabbar/wd-tabbar.vue +104 -0
  277. package/components/wd-tabbar-item/index.scss +52 -0
  278. package/components/wd-tabbar-item/types.ts +51 -0
  279. package/components/wd-tabbar-item/wd-tabbar-item.vue +101 -0
  280. package/components/wd-table/index.scss +132 -0
  281. package/components/wd-table/types.ts +69 -0
  282. package/components/wd-table/wd-table.vue +255 -0
  283. package/components/wd-table-col/index.scss +44 -0
  284. package/components/wd-table-col/types.ts +54 -0
  285. package/components/wd-table-col/wd-table-col.vue +149 -0
  286. package/components/wd-tabs/index.scss +280 -0
  287. package/components/wd-tabs/types.ts +71 -0
  288. package/components/wd-tabs/wd-tabs.vue +528 -0
  289. package/components/wd-tag/index.scss +115 -0
  290. package/components/wd-tag/types.ts +81 -0
  291. package/components/wd-tag/wd-tag.vue +154 -0
  292. package/components/wd-text/index.scss +34 -0
  293. package/components/wd-text/types.ts +98 -0
  294. package/components/wd-text/wd-text.vue +138 -0
  295. package/components/wd-textarea/index.scss +343 -0
  296. package/components/wd-textarea/types.ts +298 -0
  297. package/components/wd-textarea/wd-textarea.vue +303 -0
  298. package/components/wd-toast/index.scss +66 -0
  299. package/components/wd-toast/index.ts +109 -0
  300. package/components/wd-toast/types.ts +76 -0
  301. package/components/wd-toast/wd-toast.vue +212 -0
  302. package/components/wd-tooltip/index.scss +61 -0
  303. package/components/wd-tooltip/types.ts +102 -0
  304. package/components/wd-tooltip/wd-tooltip.vue +167 -0
  305. package/components/wd-transition/index.scss +91 -0
  306. package/components/wd-transition/types.ts +89 -0
  307. package/components/wd-transition/wd-transition.vue +221 -0
  308. package/components/wd-upload/index.scss +173 -0
  309. package/components/wd-upload/types.ts +378 -0
  310. package/components/wd-upload/utils.ts +152 -0
  311. package/components/wd-upload/wd-upload.vue +737 -0
  312. package/components/wd-video-preview/index.scss +34 -0
  313. package/components/wd-video-preview/types.ts +32 -0
  314. package/components/wd-video-preview/wd-video-preview.vue +76 -0
  315. package/components/wd-watermark/index.scss +18 -0
  316. package/components/wd-watermark/types.ts +82 -0
  317. package/components/wd-watermark/wd-watermark.vue +592 -0
  318. package/components/wot-design-uni/wot-design-uni.vue +14 -0
  319. package/global.d.ts +108 -0
  320. package/index.ts +30 -0
  321. package/locale/index.ts +41 -0
  322. package/locale/lang/en-US.ts +128 -0
  323. package/locale/lang/th-TH.ts +127 -0
  324. package/locale/lang/vi-VN.ts +89 -0
  325. package/locale/lang/zh-CN.ts +127 -0
  326. package/locale/lang/zh-HK.ts +84 -0
  327. package/locale/lang/zh-TW.ts +84 -0
  328. package/package.json +20 -0
@@ -0,0 +1,737 @@
1
+ <template>
2
+ <view :class="['wd-upload', customClass]" :style="customStyle">
3
+ <!-- 预览列表 -->
4
+ <view
5
+ :class="['wd-upload__preview', customPreviewClass]"
6
+ v-for="(file, index) in uploadFiles"
7
+ :key="index"
8
+ >
9
+ <!-- 成功时展示图片 -->
10
+ <view class="wd-upload__status-content">
11
+ <image
12
+ v-if="isImage(file)"
13
+ :src="file.url"
14
+ :mode="imageMode"
15
+ class="wd-upload__picture"
16
+ @click="onPreviewImage(file)"
17
+ />
18
+ <template v-else-if="isVideo(file)">
19
+ <view class="wd-upload__video" v-if="file.thumb" @click="onPreviewVideo(file)">
20
+ <image :src="file.thumb" :mode="imageMode" class="wd-upload__picture" />
21
+ <wd-icon name="play-circle-filled" custom-class="wd-upload__video-paly"></wd-icon>
22
+ </view>
23
+ <view v-else class="wd-upload__video" @click="onPreviewVideo(file)">
24
+ <!-- #ifdef APP-PLUS || MP-DINGTALK -->
25
+ <wd-icon custom-class="wd-upload__video-icon" name="video"></wd-icon>
26
+ <!-- #endif -->
27
+ <!-- #ifndef APP-PLUS -->
28
+ <!-- #ifndef MP-DINGTALK -->
29
+ <video
30
+ :src="file.url"
31
+ :title="file.name || '视频' + index"
32
+ object-fit="contain"
33
+ :controls="false"
34
+ :poster="file.thumb"
35
+ :autoplay="false"
36
+ :show-center-play-btn="false"
37
+ :show-fullscreen-btn="false"
38
+ :show-play-btn="false"
39
+ :show-loading="false"
40
+ :show-progress="false"
41
+ :show-mute-btn="false"
42
+ :enable-progress-gesture="false"
43
+ :enableNative="true"
44
+ class="wd-upload__video"
45
+ ></video>
46
+ <wd-icon name="play-circle-filled" custom-class="wd-upload__video-paly"></wd-icon>
47
+ <!-- #endif -->
48
+ <!-- #endif -->
49
+ </view>
50
+ </template>
51
+
52
+ <view v-else class="wd-upload__file" @click="onPreviewFile(file)">
53
+ <wd-icon name="file" custom-class="wd-upload__file-icon"></wd-icon>
54
+ <view class="wd-upload__file-name">{{ file.name || file.url }}</view>
55
+ </view>
56
+ </view>
57
+
58
+ <view
59
+ v-if="file[props.statusKey] !== 'success'"
60
+ class="wd-upload__mask wd-upload__status-content"
61
+ >
62
+ <!-- loading时展示loading图标和进度 -->
63
+ <view v-if="file[props.statusKey] === 'loading'" class="wd-upload__status-content">
64
+ <wd-loading :type="loadingType" :size="loadingSize" :color="loadingColor" />
65
+ <text class="wd-upload__progress-txt">{{ file.percent }}%</text>
66
+ </view>
67
+ <!-- 失败时展示失败图标以及失败信息 -->
68
+ <view v-if="file[props.statusKey] === 'fail'" class="wd-upload__status-content">
69
+ <wd-icon name="close-outline" custom-class="wd-upload__icon"></wd-icon>
70
+ <text class="wd-upload__progress-txt">{{ file.error || translate('error') }}</text>
71
+ </view>
72
+ </view>
73
+ <!-- 上传状态为上传中时不展示移除按钮 -->
74
+ <wd-icon
75
+ v-if="file[props.statusKey] !== 'loading' && !disabled"
76
+ name="error-fill"
77
+ custom-class="wd-upload__close"
78
+ @click="removeFile(index)"
79
+ ></wd-icon>
80
+ </view>
81
+
82
+ <block v-if="showUpload">
83
+ <view
84
+ :class="['wd-upload__evoke-slot', customEvokeClass]"
85
+ v-if="$slots.default"
86
+ @click="handleChoose"
87
+ >
88
+ <slot></slot>
89
+ </view>
90
+ <!-- 唤起项 -->
91
+ <view
92
+ v-else
93
+ @click="handleChoose"
94
+ :class="['wd-upload__evoke', disabled ? 'is-disabled' : '', customEvokeClass]"
95
+ >
96
+ <!-- 唤起项图标 -->
97
+ <wd-icon class="wd-upload__evoke-icon" name="fill-camera"></wd-icon>
98
+ <!-- 有限制个数时确认是否展示限制个数 -->
99
+ <view v-if="limit && showLimitNum" class="wd-upload__evoke-num">
100
+ ({{ uploadFiles.length }}/{{ limit }})
101
+ </view>
102
+ </view>
103
+ </block>
104
+ </view>
105
+ <wd-video-preview ref="videoPreview"></wd-video-preview>
106
+ </template>
107
+
108
+ <script lang="ts">
109
+ export default {
110
+ name: 'wd-upload',
111
+ options: {
112
+ addGlobalClass: true,
113
+ virtualHost: true,
114
+ styleIsolation: 'shared',
115
+ },
116
+ }
117
+ </script>
118
+
119
+ <script lang="ts" setup>
120
+ import { computed, ref, watch } from 'vue'
121
+ import {
122
+ context,
123
+ getType,
124
+ isEqual,
125
+ isImageUrl,
126
+ isVideoUrl,
127
+ isFunction,
128
+ isDef,
129
+ } from '../common/util'
130
+ import { chooseFile } from './utils'
131
+ import { useTranslate } from '../composables/useTranslate'
132
+ import {
133
+ uploadProps,
134
+ type UploadFileItem,
135
+ type ChooseFile,
136
+ type UploadExpose,
137
+ type UploadErrorEvent,
138
+ type UploadChangeEvent,
139
+ type UploadSuccessEvent,
140
+ type UploadProgressEvent,
141
+ type UploadOversizeEvent,
142
+ type UploadRemoveEvent,
143
+ type UploadMethod,
144
+ } from './types'
145
+ import type { VideoPreviewInstance } from '../wd-video-preview/types'
146
+
147
+ const props = defineProps(uploadProps)
148
+
149
+ const emit = defineEmits<{
150
+ (e: 'fail', value: UploadErrorEvent): void
151
+ (e: 'change', value: UploadChangeEvent): void
152
+ (e: 'success', value: UploadSuccessEvent): void
153
+ (e: 'progress', value: UploadProgressEvent): void
154
+ (e: 'oversize', value: UploadOversizeEvent): void
155
+ (e: 'chooseerror', value: any): void
156
+ (e: 'remove', value: UploadRemoveEvent): void
157
+ (e: 'update:fileList', value: UploadFileItem[]): void
158
+ }>()
159
+
160
+ defineExpose<UploadExpose>({
161
+ submit: () => startUploadFiles(),
162
+ })
163
+
164
+ const { translate } = useTranslate('upload')
165
+
166
+ const uploadFiles = ref<UploadFileItem[]>([])
167
+
168
+ const showUpload = computed(() => !props.limit || uploadFiles.value.length < props.limit)
169
+
170
+ const videoPreview = ref<VideoPreviewInstance>()
171
+
172
+ watch(
173
+ () => props.fileList,
174
+ (val) => {
175
+ const { statusKey } = props
176
+ if (isEqual(val, uploadFiles.value)) return
177
+ const uploadFileList: UploadFileItem[] = val.map((item) => {
178
+ item[statusKey] = item[statusKey] || 'success'
179
+ item.response = item.response || ''
180
+ return { ...item, uid: context.id++ }
181
+ })
182
+ uploadFiles.value = uploadFileList
183
+ },
184
+ {
185
+ deep: true,
186
+ immediate: true,
187
+ },
188
+ )
189
+
190
+ watch(
191
+ () => props.limit,
192
+ (val) => {
193
+ if (val && val < uploadFiles.value.length) {
194
+ console.error('[wot-design]Error: props limit must less than fileList.length')
195
+ }
196
+ },
197
+ {
198
+ deep: true,
199
+ immediate: true,
200
+ },
201
+ )
202
+
203
+ watch(
204
+ () => props.beforePreview,
205
+ (fn) => {
206
+ if (fn && !isFunction(fn) && getType(fn) !== 'asyncfunction') {
207
+ console.error('The type of beforePreview must be Function')
208
+ }
209
+ },
210
+ {
211
+ deep: true,
212
+ immediate: true,
213
+ },
214
+ )
215
+
216
+ watch(
217
+ () => props.onPreviewFail,
218
+ (fn) => {
219
+ if (fn && !isFunction(fn) && getType(fn) !== 'asyncfunction') {
220
+ console.error('The type of onPreviewFail must be Function')
221
+ }
222
+ },
223
+ {
224
+ deep: true,
225
+ immediate: true,
226
+ },
227
+ )
228
+
229
+ watch(
230
+ () => props.beforeRemove,
231
+ (fn) => {
232
+ if (fn && !isFunction(fn) && getType(fn) !== 'asyncfunction') {
233
+ console.error('The type of beforeRemove must be Function')
234
+ }
235
+ },
236
+ {
237
+ deep: true,
238
+ immediate: true,
239
+ },
240
+ )
241
+
242
+ watch(
243
+ () => props.beforeUpload,
244
+ (fn) => {
245
+ if (fn && !isFunction(fn) && getType(fn) !== 'asyncfunction') {
246
+ console.error('The type of beforeUpload must be Function')
247
+ }
248
+ },
249
+ {
250
+ deep: true,
251
+ immediate: true,
252
+ },
253
+ )
254
+
255
+ watch(
256
+ () => props.beforeChoose,
257
+ (fn) => {
258
+ if (fn && !isFunction(fn) && getType(fn) !== 'asyncfunction') {
259
+ console.error('The type of beforeChoose must be Function')
260
+ }
261
+ },
262
+ {
263
+ deep: true,
264
+ immediate: true,
265
+ },
266
+ )
267
+
268
+ watch(
269
+ () => props.buildFormData,
270
+ (fn) => {
271
+ if (fn && !isFunction(fn) && getType(fn) !== 'asyncfunction') {
272
+ console.error('The type of buildFormData must be Function')
273
+ }
274
+ },
275
+ {
276
+ deep: true,
277
+ immediate: true,
278
+ },
279
+ )
280
+
281
+ function emitFileList() {
282
+ emit('update:fileList', uploadFiles.value)
283
+ }
284
+
285
+ /**
286
+ * 组件内部上传方法
287
+ * @param file 文件
288
+ * @param formData
289
+ * @param options
290
+ */
291
+ const upload: UploadMethod = (file, formData, options) => {
292
+ const uploadTask = uni.uploadFile({
293
+ url: options.action,
294
+ header: options.header,
295
+ name: options.name,
296
+ fileName: options.name,
297
+ fileType: options.fileType as 'image' | 'video' | 'audio',
298
+ formData,
299
+ filePath: file.url,
300
+ success(res) {
301
+ if (res.statusCode === options.statusCode) {
302
+ // 上传成功进行文件列表拼接
303
+ options.onSuccess(res, file, formData)
304
+ } else {
305
+ // 上传失败处理
306
+ options.onError({ ...res, errMsg: res.errMsg || '' }, file, formData)
307
+ }
308
+ },
309
+ fail(err) {
310
+ // 上传失败处理
311
+ options.onError(err, file, formData)
312
+ },
313
+ })
314
+ // 获取当前文件加载的百分比
315
+ uploadTask.onProgressUpdate((res) => {
316
+ options.onProgress(res, file)
317
+ })
318
+ }
319
+
320
+ const startUpload: UploadMethod = (file, formData, options) => {
321
+ const { statusKey, uploadMethod } = props
322
+ // 设置上传中,防止重复发起上传
323
+ file[statusKey] = 'loading'
324
+ if (isFunction(uploadMethod)) {
325
+ uploadMethod(file, formData, options)
326
+ } else {
327
+ upload(file, formData, options)
328
+ }
329
+ }
330
+
331
+ /**
332
+ * 获取图片信息
333
+ * @param img
334
+ */
335
+ function getImageInfo(img: string) {
336
+ return new Promise<UniApp.GetImageInfoSuccessData>((resolve, reject) => {
337
+ uni.getImageInfo({
338
+ src: img,
339
+ success: (res) => {
340
+ resolve(res)
341
+ },
342
+ fail: (error) => {
343
+ reject(error)
344
+ },
345
+ })
346
+ })
347
+ }
348
+
349
+ /**
350
+ * @description 初始化文件数据
351
+ * @param {Object} file 上传的文件
352
+ */
353
+ function initFile(file: ChooseFile) {
354
+ const { statusKey } = props
355
+ // 状态初始化
356
+ const initState: UploadFileItem = {
357
+ uid: context.id++,
358
+ // 仅h5支持 name
359
+ name: file.name || '',
360
+ thumb: file.thumb || '',
361
+ [statusKey]: 'pending',
362
+ size: file.size || 0,
363
+ url: file.path,
364
+ percent: 0,
365
+ }
366
+
367
+ uploadFiles.value.push(initState)
368
+ if (props.autoUpload) {
369
+ startUploadFiles()
370
+ }
371
+ }
372
+
373
+ /**
374
+ * 开始上传文件
375
+ */
376
+ function startUploadFiles() {
377
+ const { buildFormData, formData = {}, statusKey } = props
378
+ const { action, name, header = {}, accept, successStatus } = props
379
+ const statusCode = isDef(successStatus) ? successStatus : 200
380
+
381
+ for (const uploadFile of uploadFiles.value) {
382
+ // 仅开始未上传的文件
383
+ if (uploadFile[statusKey] === 'pending') {
384
+ if (buildFormData) {
385
+ buildFormData({
386
+ file: uploadFile,
387
+ formData,
388
+ resolve: (formData: Record<string, any>) => {
389
+ formData &&
390
+ startUpload(uploadFile, formData, {
391
+ onSuccess: handleSuccess,
392
+ onError: handleError,
393
+ onProgress: handleProgress,
394
+ action,
395
+ header,
396
+ name,
397
+ fileName: name,
398
+ fileType: accept as 'image' | 'video' | 'audio',
399
+ statusCode,
400
+ })
401
+ },
402
+ })
403
+ } else {
404
+ startUpload(uploadFile, formData, {
405
+ onSuccess: handleSuccess,
406
+ onError: handleError,
407
+ onProgress: handleProgress,
408
+ action,
409
+ header,
410
+ name,
411
+ fileName: name,
412
+ fileType: accept as 'image' | 'video' | 'audio',
413
+ statusCode,
414
+ })
415
+ }
416
+ }
417
+ }
418
+ }
419
+
420
+ /**
421
+ * @description 上传失败捕获
422
+ * @param {Object} err 错误返回信息
423
+ * @param {Object} file 上传的文件
424
+ */
425
+ function handleError(
426
+ err: Record<string, any>,
427
+ file: UploadFileItem,
428
+ formData: Record<string, any>,
429
+ ) {
430
+ const { statusKey } = props
431
+ const index = uploadFiles.value.findIndex((item) => item.uid === file.uid)
432
+ if (index > -1) {
433
+ uploadFiles.value[index][statusKey] = 'fail'
434
+ uploadFiles.value[index].error = err.message
435
+ uploadFiles.value[index].response = err
436
+ emit('fail', { error: err, file, formData })
437
+ emitFileList()
438
+ }
439
+ }
440
+
441
+ /**
442
+ * @description 上传成功捕获
443
+ * @param {Object} res 接口返回信息
444
+ * @param {Object} file 上传的文件
445
+ */
446
+ function handleSuccess(
447
+ res: Record<string, any>,
448
+ file: UploadFileItem,
449
+ formData: Record<string, any>,
450
+ ) {
451
+ const { statusKey } = props
452
+ const index = uploadFiles.value.findIndex((item) => item.uid === file.uid)
453
+ if (index > -1) {
454
+ uploadFiles.value[index][statusKey] = 'success'
455
+ uploadFiles.value[index].response = res.data
456
+ emit('change', { fileList: uploadFiles.value })
457
+ emit('success', { file, fileList: uploadFiles.value, formData })
458
+ emitFileList()
459
+ }
460
+ }
461
+
462
+ /**
463
+ * @description 上传中捕获
464
+ * @param {Object} res 接口返回信息
465
+ * @param {Object} file 上传的文件
466
+ */
467
+ function handleProgress(res: UniApp.OnProgressUpdateResult, file: UploadFileItem) {
468
+ const index = uploadFiles.value.findIndex((item) => item.uid === file.uid)
469
+ if (index > -1) {
470
+ uploadFiles.value[index].percent = res.progress
471
+ emit('progress', { response: res, file })
472
+ }
473
+ }
474
+
475
+ /**
476
+ * @description 选择文件的实际操作,将chooseFile自己用promise包了一层
477
+ */
478
+ function onChooseFile() {
479
+ const {
480
+ multiple,
481
+ maxSize,
482
+ accept,
483
+ sizeType,
484
+ limit,
485
+ sourceType,
486
+ compressed,
487
+ maxDuration,
488
+ camera,
489
+ beforeUpload,
490
+ } = props
491
+ // 文件选择
492
+ chooseFile({
493
+ multiple,
494
+ sizeType,
495
+ sourceType,
496
+ maxCount: limit ? limit - uploadFiles.value.length : 9,
497
+ accept,
498
+ compressed,
499
+ maxDuration,
500
+ camera,
501
+ })
502
+ .then((res) => {
503
+ // 成功选择初始化file
504
+ let files = res
505
+ // 单选只有一个
506
+ if (!multiple) {
507
+ files = files.slice(0, 1)
508
+ }
509
+ // 遍历列表逐个初始化上传参数
510
+ const mapFiles = async (files: ChooseFile[]) => {
511
+ for (let index = 0; index < files.length; index++) {
512
+ const file = files[index]
513
+ if (file.type === 'image' && !file.size) {
514
+ const imageInfo = await getImageInfo(file.path)
515
+ file.size = imageInfo.width * imageInfo.height
516
+ }
517
+ Number(file.size) <= maxSize ? initFile(file) : emit('oversize', { file })
518
+ }
519
+ }
520
+
521
+ // 上传前的钩子
522
+ if (beforeUpload) {
523
+ beforeUpload({
524
+ files,
525
+ fileList: uploadFiles.value,
526
+ resolve: (isPass: boolean) => {
527
+ isPass && mapFiles(files)
528
+ },
529
+ })
530
+ } else {
531
+ mapFiles(files)
532
+ }
533
+ })
534
+ .catch((error) => {
535
+ emit('chooseerror', { error })
536
+ })
537
+ }
538
+
539
+ /**
540
+ * @description 选择文件,内置拦截选择操作
541
+ */
542
+ function handleChoose() {
543
+ if (props.disabled) return
544
+ const { beforeChoose } = props
545
+
546
+ // 选择图片前的钩子
547
+ if (beforeChoose) {
548
+ beforeChoose({
549
+ fileList: uploadFiles.value,
550
+ resolve: (isPass: boolean) => {
551
+ isPass && onChooseFile()
552
+ },
553
+ })
554
+ } else {
555
+ onChooseFile()
556
+ }
557
+ }
558
+
559
+ /**
560
+ * @description 移除文件
561
+ * @param {Object} file 上传的文件
562
+ * @param {Number} index 删除
563
+ */
564
+ function handleRemove(file: UploadFileItem) {
565
+ uploadFiles.value.splice(
566
+ uploadFiles.value.findIndex((item) => item.uid === file.uid),
567
+ 1,
568
+ )
569
+ emit('change', {
570
+ fileList: uploadFiles.value,
571
+ })
572
+ emit('remove', { file })
573
+ emitFileList()
574
+ }
575
+
576
+ function removeFile(index: number) {
577
+ const { beforeRemove } = props
578
+ const intIndex: number = index
579
+ const file = uploadFiles.value[intIndex]
580
+ if (beforeRemove) {
581
+ beforeRemove({
582
+ file,
583
+ index: intIndex,
584
+ fileList: uploadFiles.value,
585
+ resolve: (isPass: boolean) => {
586
+ isPass && handleRemove(file)
587
+ },
588
+ })
589
+ } else {
590
+ handleRemove(file)
591
+ }
592
+ }
593
+
594
+ /**
595
+ * 预览文件
596
+ * @param file
597
+ */
598
+ function handlePreviewFile(file: UploadFileItem) {
599
+ uni.openDocument({
600
+ filePath: file.url,
601
+ showMenu: true,
602
+ })
603
+ }
604
+
605
+ /**
606
+ * 预览图片
607
+ * @param index
608
+ * @param lists
609
+ */
610
+ function handlePreviewImage(index: number, lists: string[]) {
611
+ const { onPreviewFail } = props
612
+ uni.previewImage({
613
+ urls: lists,
614
+ current: lists[index],
615
+ fail() {
616
+ if (onPreviewFail) {
617
+ onPreviewFail({
618
+ index,
619
+ imgList: lists,
620
+ })
621
+ } else {
622
+ uni.showToast({ title: '预览图片失败', icon: 'none' })
623
+ }
624
+ },
625
+ })
626
+ }
627
+
628
+ /**
629
+ * 预览视频
630
+ * @param index
631
+ * @param lists
632
+ */
633
+ function handlePreviewVieo(index: number, lists: UploadFileItem[]) {
634
+ const { onPreviewFail } = props
635
+ // #ifdef MP-WEIXIN
636
+ uni.previewMedia({
637
+ current: index,
638
+ sources: lists.map((file) => {
639
+ return {
640
+ url: file.url,
641
+ type: 'video',
642
+ poster: file.thumb,
643
+ }
644
+ }),
645
+ fail() {
646
+ if (onPreviewFail) {
647
+ onPreviewFail({
648
+ index,
649
+ imgList: [],
650
+ })
651
+ } else {
652
+ uni.showToast({ title: '预览视频失败', icon: 'none' })
653
+ }
654
+ },
655
+ })
656
+ // #endif
657
+
658
+ // #ifndef MP-WEIXIN
659
+ videoPreview.value?.open({
660
+ url: lists[index].url,
661
+ poster: lists[index].thumb,
662
+ title: lists[index].name,
663
+ })
664
+ // #endif
665
+ }
666
+
667
+ function onPreviewImage(file: UploadFileItem) {
668
+ const { beforePreview } = props
669
+ const lists = uploadFiles.value.filter((file) => isImage(file))
670
+ const index: number = lists.findIndex((item) => item.url === file.url)
671
+ if (beforePreview) {
672
+ beforePreview({
673
+ index,
674
+ imgList: lists.map((file) => file.url),
675
+ resolve: (isPass: boolean) => {
676
+ isPass &&
677
+ handlePreviewImage(
678
+ index,
679
+ lists.map((file) => file.url),
680
+ )
681
+ },
682
+ })
683
+ } else {
684
+ handlePreviewImage(
685
+ index,
686
+ lists.map((file) => file.url),
687
+ )
688
+ }
689
+ }
690
+
691
+ function onPreviewVideo(file: UploadFileItem) {
692
+ const { beforePreview } = props
693
+ const lists = uploadFiles.value.filter((file) => isVideo(file))
694
+ const index: number = lists.findIndex((item) => item.url === file.url)
695
+ if (beforePreview) {
696
+ beforePreview({
697
+ index,
698
+ imgList: [],
699
+ resolve: (isPass: boolean) => {
700
+ isPass && handlePreviewVieo(index, lists)
701
+ },
702
+ })
703
+ } else {
704
+ handlePreviewVieo(index, lists)
705
+ }
706
+ }
707
+
708
+ function onPreviewFile(file: UploadFileItem) {
709
+ const { beforePreview } = props
710
+ const lists = uploadFiles.value.filter((file) => {
711
+ return !isVideo(file) && !isImage(file)
712
+ })
713
+ const index: number = lists.findIndex((item) => item.url === file.url)
714
+ if (beforePreview) {
715
+ beforePreview({
716
+ index,
717
+ imgList: [],
718
+ resolve: (isPass: boolean) => {
719
+ isPass && handlePreviewFile(file)
720
+ },
721
+ })
722
+ } else {
723
+ handlePreviewFile(file)
724
+ }
725
+ }
726
+
727
+ function isVideo(file: UploadFileItem) {
728
+ return (file.name && isVideoUrl(file.name)) || isVideoUrl(file.url)
729
+ }
730
+
731
+ function isImage(file: UploadFileItem) {
732
+ return (file.name && isImageUrl(file.name)) || isImageUrl(file.url)
733
+ }
734
+ </script>
735
+ <style lang="scss" scoped>
736
+ @import './index';
737
+ </style>