index.vue 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. <template>
  2. <uni-shadow-root class="vant-uploader-index"><view class="van-uploader">
  3. <view class="van-uploader__wrapper">
  4. <view v-for="(item,index) in (lists)" :key="item.index" v-if="previewImage" class="van-uploader__preview">
  5. <image v-if="item.isImage" :mode="imageFit" :src="item.url || item.path" :alt="item.name || ('图片' + index)" class="van-uploader__preview-image" :style="'width: '+(computedPreviewSize)+'; height: '+(computedPreviewSize)+';'" :data-url="item.url || item.path" @click="doPreviewImage"></image>
  6. <view v-else class="van-uploader__file" :style="'width: '+(computedPreviewSize)+'; height: '+(computedPreviewSize)+';'">
  7. <van-icon name="description" class="van-uploader__file-icon"></van-icon>
  8. <view class="van-uploader__file-name van-ellipsis">{{ item.name || item.url || item.path }}</view>
  9. </view>
  10. <van-icon v-if="deletable" name="clear" class="van-uploader__preview-delete" :data-index="index" @click.native="deleteItem"></van-icon>
  11. </view>
  12. <block v-if="isInCount">
  13. <view v-if="useSlot" class="van-uploader__slot" @click="startUpload">
  14. <slot></slot>
  15. </view>
  16. <view v-else class="van-uploader__upload" :style="'width: '+(computedPreviewSize)+'; height: '+(computedPreviewSize)+';'" @click="startUpload">
  17. <van-icon name="plus" class="van-uploader__upload-icon"></van-icon>
  18. <text v-if="uploadText" class="van-uploader__upload-text">{{ uploadText }}</text>
  19. </view>
  20. </block>
  21. </view>
  22. </view></uni-shadow-root>
  23. </template>
  24. <wxs src="../wxs/utils.wxs" module="utils"></wxs>
  25. <script>
  26. import VanIcon from '../icon/index.vue'
  27. global['__wxVueOptions'] = {components:{'van-icon': VanIcon}}
  28. global['__wxRoute'] = 'vant/uploader/index'
  29. import { VantComponent } from '../common/component';
  30. import { isImageFile } from './utils';
  31. import { addUnit } from '../common/utils';
  32. VantComponent({
  33. props: {
  34. disabled: Boolean,
  35. multiple: Boolean,
  36. uploadText: String,
  37. useSlot: Boolean,
  38. useBeforeRead: Boolean,
  39. previewSize: {
  40. type: null,
  41. value: 90,
  42. observer: 'setComputedPreviewSize'
  43. },
  44. name: {
  45. type: [Number, String],
  46. value: ''
  47. },
  48. accept: {
  49. type: String,
  50. value: 'image'
  51. },
  52. fileList: {
  53. type: Array,
  54. value: [],
  55. observer: 'formatFileList'
  56. },
  57. maxSize: {
  58. type: Number,
  59. value: Number.MAX_VALUE
  60. },
  61. maxCount: {
  62. type: Number,
  63. value: 100
  64. },
  65. deletable: {
  66. type: Boolean,
  67. value: true
  68. },
  69. previewImage: {
  70. type: Boolean,
  71. value: true
  72. },
  73. previewFullImage: {
  74. type: Boolean,
  75. value: true
  76. },
  77. imageFit: {
  78. type: String,
  79. value: 'scaleToFill'
  80. }
  81. },
  82. data: {
  83. lists: [],
  84. computedPreviewSize: '',
  85. isInCount: true
  86. },
  87. methods: {
  88. formatFileList() {
  89. const { fileList = [], maxCount } = this.data;
  90. const lists = fileList.map(item => (Object.assign(Object.assign({}, item), { isImage: typeof item.isImage === 'undefined' ? isImageFile(item) : item.isImage })));
  91. this.setData({ lists, isInCount: lists.length < maxCount });
  92. },
  93. setComputedPreviewSize(val) {
  94. this.setData({
  95. computedPreviewSize: addUnit(val)
  96. });
  97. },
  98. startUpload() {
  99. if (this.data.disabled)
  100. return;
  101. const { name = '', capture = ['album', 'camera'], maxCount = 100, multiple = false, maxSize, accept, lists, useBeforeRead = false // 是否定义了 beforeRead
  102. } = this.data;
  103. let chooseFile = null;
  104. const newMaxCount = maxCount - lists.length;
  105. // 设置为只选择图片的时候使用 chooseImage 来实现
  106. if (accept === 'image') {
  107. chooseFile = new Promise((resolve, reject) => {
  108. wx.chooseImage({
  109. count: multiple ? (newMaxCount > 9 ? 9 : newMaxCount) : 1,
  110. sourceType: capture,
  111. success: resolve,
  112. fail: reject
  113. });
  114. });
  115. }
  116. else {
  117. chooseFile = new Promise((resolve, reject) => {
  118. wx.chooseMessageFile({
  119. count: multiple ? newMaxCount : 1,
  120. type: 'file',
  121. success: resolve,
  122. fail: reject
  123. });
  124. });
  125. }
  126. chooseFile.then((res) => {
  127. const file = multiple ? res.tempFiles : res.tempFiles[0];
  128. // 检查文件大小
  129. if (file instanceof Array) {
  130. const sizeEnable = file.every(item => item.size <= maxSize);
  131. if (!sizeEnable) {
  132. this.$emit('oversize', { name });
  133. return;
  134. }
  135. }
  136. else if (file.size > maxSize) {
  137. this.$emit('oversize', { name });
  138. return;
  139. }
  140. // 触发上传之前的钩子函数
  141. if (useBeforeRead) {
  142. this.$emit('before-read', {
  143. file,
  144. name,
  145. callback: (result) => {
  146. if (result) {
  147. // 开始上传
  148. this.$emit('after-read', { file, name });
  149. }
  150. }
  151. });
  152. }
  153. else {
  154. this.$emit('after-read', { file, name });
  155. }
  156. });
  157. },
  158. deleteItem(event) {
  159. const { index } = event.currentTarget.dataset;
  160. this.$emit('delete', { index, name: this.data.name });
  161. },
  162. doPreviewImage(event) {
  163. if (!this.data.previewFullImage)
  164. return;
  165. const curUrl = event.currentTarget.dataset.url;
  166. const images = this.data.lists
  167. .filter(item => item.isImage)
  168. .map(item => item.url || item.path);
  169. this.$emit('click-preview', { url: curUrl, name: this.data.name });
  170. wx.previewImage({
  171. urls: images,
  172. current: curUrl,
  173. fail() {
  174. wx.showToast({ title: '预览图片失败', icon: 'none' });
  175. }
  176. });
  177. }
  178. }
  179. });
  180. export default global['__wxComponents']['vant/uploader/index']
  181. </script>
  182. <style platform="mp-weixin">
  183. @import '../common/index.css';.van-uploader{position:relative;display:inline-block}.van-uploader__wrapper{display:-webkit-flex;display:flex;-webkit-flex-wrap:wrap;flex-wrap:wrap}.van-uploader__upload{position:relative;display:-webkit-flex;display:flex;-webkit-flex-direction:column;flex-direction:column;-webkit-align-items:center;align-items:center;-webkit-justify-content:center;justify-content:center;box-sizing:border-box;width:80px;height:80px;margin:0 8px 8px 0;background-color:#fff;border:1px dashed #ebedf0;border-radius:4px}.van-uploader__upload-icon{display:inline-block;width:24px;height:24px;color:#969799;font-size:24px}.van-uploader__upload-text{margin-top:8px;color:#969799;font-size:12px}.van-uploader__preview{position:relative;margin:0 8px 8px 0}.van-uploader__preview-image{display:block;width:80px;height:80px;border-radius:4px}.van-uploader__preview-delete{position:absolute;top:-8px;right:-8px;color:#969799;font-size:18px;background-color:#fff;border-radius:100%}.van-uploader__file{display:-webkit-flex;display:flex;-webkit-flex-direction:column;flex-direction:column;-webkit-align-items:center;align-items:center;-webkit-justify-content:center;justify-content:center;width:80px;height:80px;background-color:#f7f8fa;border-radius:4px}.van-uploader__file-icon{display:inline-block;width:20px;height:20px;color:#646566;font-size:20px}.van-uploader__file-name{box-sizing:border-box;width:100%;margin-top:8px;padding:0 5px;color:#646566;font-size:12px;text-align:center}
  184. </style>