fs-upload.vue 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  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. cloudUpload: Boolean
  114. })
  115. const emit = defineEmits(['update:modelValue'])
  116. const handlePreview = current => {
  117. uni.previewImage({
  118. current,
  119. urls: props.modelValue.map(item => formatPath(item))
  120. })
  121. }
  122. const upload = async () => {
  123. let methods = ''
  124. if (props.mediaType === 'image') {
  125. methods = 'chooseAndUploadImage'
  126. } else {
  127. methods = 'chooseAndUploadVideo'
  128. }
  129. utils[methods](
  130. {
  131. count: props.count,
  132. ...props.chooseData
  133. },
  134. {
  135. url: props.action || proxy.$uploadUrl,
  136. name: props.name,
  137. header: props.header,
  138. formData: props.formData
  139. },
  140. props.cloudUpload
  141. ).then(res => {
  142. const fileList = [...props.modelValue, ...res]
  143. fileList.length > props.count && fileList.splice(props.count)
  144. emit('update:modelValue', fileList)
  145. })
  146. }
  147. const handleDel = index => {
  148. props.modelValue.splice(index, 1)
  149. emit('update:modelValue', props.modelValue)
  150. }
  151. const formatPath = item => {
  152. const path = props.pathKey ? item[props.pathKey] : item
  153. return utils.isHttp(path) ? path : config.baseUrl + path
  154. }
  155. </script>
  156. <style lang="scss" scoped>
  157. .fs-file {
  158. &-list {
  159. display: flex;
  160. flex-wrap: wrap;
  161. padding-top: var(--gutter);
  162. }
  163. &-box {
  164. position: relative;
  165. margin-bottom: var(--gutter);
  166. margin-left: var(--gutter);
  167. }
  168. &-video {
  169. width: 300rpx;
  170. height: 200rpx;
  171. }
  172. &-del {
  173. position: absolute;
  174. top: 0;
  175. right: 0;
  176. transform: translate(50%, -50%);
  177. font-size: 22px;
  178. color: var(--sub);
  179. z-index: 10;
  180. }
  181. }
  182. </style>