fs-upload.vue 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. <template>
  2. <view class="fs-file-list">
  3. <view v-for="(item, index) in modelValue" :key="index" class="fs-file-box">
  4. <fs-icon type="icon-close" size="20px" colorType="danger" class="fs-file-del" @click="handleDel(index)"></fs-icon>
  5. <fs-avatar
  6. v-if="mediaType === 'image'"
  7. :src="formatPath(item)"
  8. :shape="shape"
  9. :size="size"
  10. :width="width"
  11. :height="height"
  12. radius
  13. @click="handlePreview(formatPath(item))"
  14. ></fs-avatar>
  15. <video v-else :src="formatPath(item)" controls class="fs-file-video"></video>
  16. </view>
  17. <view class="fs-file-box" @click="upload" v-if="modelValue.length < count">
  18. <slot>
  19. <fs-avatar :shape="shape" :size="size" :width="width" :height="height" radius bgColor="#EBEFF5">
  20. <fs-icon type="icon-plus" size="50px"></fs-icon>
  21. </fs-avatar>
  22. </slot>
  23. </view>
  24. </view>
  25. </template>
  26. <script>
  27. /**
  28. * 上传组件
  29. * @description 上传组件
  30. * @property {String} action 上传地址
  31. * @property {String} name 文件对应的key,开发者在服务器端通过这个key可以获取到文件二进制内容
  32. * @property {Object} HTTP 请求 Header, header 中不能设置 Referer
  33. * @property {String} mediaType = [image | video] 媒体类型
  34. * @property {Number} count 最多上传数量
  35. * @property {String} shape 上传框形状
  36. * @property {String} size 上传框大小
  37. * @property {String} width 上传框宽度(优先级高于size)
  38. * @property {String} height 上传框高度(优先级高于size)
  39. * @property {String} height 上传框高度(优先级高于size)
  40. * @property {String} height 上传框高度(优先级高于size)
  41. * @property {Object} chooseDatachooseImage/chooseVideo参数
  42. * @property {Object} formData 上传数据
  43. * @property {String} pathKey 接口返回的图片路径的key(如果没有可设置成空)
  44. * @property {String} cloudUpload 是否云上传(需配置云服务空间)
  45. */
  46. export default {
  47. name: 'fs-upload'
  48. }
  49. </script>
  50. <script setup>
  51. import { getCurrentInstance } from 'vue'
  52. import utils from '@/utils/utils'
  53. import config from '@/utils/config'
  54. const { proxy } = getCurrentInstance()
  55. const props = defineProps({
  56. action: String,
  57. name: {
  58. type: String,
  59. default: 'file'
  60. },
  61. header: {
  62. type: Object,
  63. default: () => {}
  64. },
  65. count: {
  66. type: Number,
  67. default: 1
  68. },
  69. chooseData: {
  70. type: Object,
  71. default: () => {}
  72. },
  73. modelValue: {
  74. type: Array,
  75. default: () => []
  76. },
  77. mediaType: {
  78. type: String,
  79. default: 'image',
  80. validator(value) {
  81. return ['image', 'video'].includes(value)
  82. }
  83. },
  84. shape: {
  85. type: String,
  86. default: 'square',
  87. validator(value) {
  88. return ['square', 'circle'].includes(value)
  89. }
  90. },
  91. size: {
  92. type: String,
  93. default: '150rpx'
  94. },
  95. width: {
  96. type: String,
  97. default: '150rpx'
  98. },
  99. height: {
  100. type: String,
  101. default: '150rpx'
  102. },
  103. formData: {
  104. type: Object,
  105. default() {
  106. return {}
  107. }
  108. },
  109. pathKey: {
  110. type: String,
  111. default: 'filePath'
  112. },
  113. editable: Boolean,
  114. cloudUpload: Boolean
  115. })
  116. const emit = defineEmits(['update:modelValue'])
  117. const handlePreview = current => {
  118. uni.previewImage({
  119. current,
  120. urls: props.modelValue.map(item => formatPath(item))
  121. })
  122. }
  123. const upload = async () => {
  124. let methods = ''
  125. if (props.mediaType === 'image') {
  126. methods = 'chooseAndUploadImage'
  127. } else {
  128. methods = 'chooseAndUploadVideo'
  129. }
  130. utils[methods](
  131. {
  132. count: props.count,
  133. ...props.chooseData
  134. },
  135. {
  136. url: props.action || proxy.$uploadUrl,
  137. name: props.name,
  138. header: props.header,
  139. formData: props.formData,
  140. editable: props.editable
  141. },
  142. props.cloudUpload
  143. ).then(res => {
  144. const fileList = [...props.modelValue, ...res]
  145. fileList.length > props.count && fileList.splice(props.count)
  146. emit('update:modelValue', fileList)
  147. })
  148. }
  149. const handleDel = index => {
  150. props.modelValue.splice(index, 1)
  151. emit('update:modelValue', props.modelValue)
  152. }
  153. const formatPath = item => {
  154. const path = props.pathKey ? item[props.pathKey] : item
  155. return utils.isHttp(path) ? path : config.baseUrl + path
  156. }
  157. </script>
  158. <style lang="scss" scoped>
  159. .fs-file {
  160. &-list {
  161. display: flex;
  162. flex-wrap: wrap;
  163. padding-top: var(--gutter);
  164. }
  165. &-box {
  166. position: relative;
  167. margin-bottom: var(--gutter);
  168. margin-left: var(--gutter);
  169. }
  170. &-video {
  171. width: 300rpx;
  172. height: 200rpx;
  173. }
  174. &-del {
  175. position: absolute;
  176. top: 0;
  177. right: 0;
  178. transform: translate(50%, -50%);
  179. font-size: 22px;
  180. color: var(--sub);
  181. z-index: 10;
  182. }
  183. }
  184. </style>