fs-upload.vue 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  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 {Boolean} editable 图片编辑
  45. * @property {Boolean} cloudUpload 是否云上传(需配置云服务空间)
  46. */
  47. export default {
  48. name: 'fs-upload'
  49. }
  50. </script>
  51. <script setup>
  52. import { getCurrentInstance } from 'vue'
  53. import utils from '@/utils/utils'
  54. import config from '@/utils/config'
  55. const { proxy } = getCurrentInstance()
  56. const props = defineProps({
  57. action: String,
  58. name: {
  59. type: String,
  60. default: 'file'
  61. },
  62. header: {
  63. type: Object,
  64. default: () => {}
  65. },
  66. count: {
  67. type: Number,
  68. default: 1
  69. },
  70. chooseData: {
  71. type: Object,
  72. default: () => {}
  73. },
  74. modelValue: {
  75. type: Array,
  76. default: () => []
  77. },
  78. mediaType: {
  79. type: String,
  80. default: 'image',
  81. validator(value) {
  82. return ['image', 'video'].includes(value)
  83. }
  84. },
  85. shape: {
  86. type: String,
  87. default: 'square',
  88. validator(value) {
  89. return ['square', 'circle'].includes(value)
  90. }
  91. },
  92. size: {
  93. type: String,
  94. default: '150rpx'
  95. },
  96. width: {
  97. type: String,
  98. default: '150rpx'
  99. },
  100. height: {
  101. type: String,
  102. default: '150rpx'
  103. },
  104. formData: {
  105. type: Object,
  106. default() {
  107. return {}
  108. }
  109. },
  110. pathKey: {
  111. type: String,
  112. default: 'filePath'
  113. },
  114. editable: Boolean,
  115. cloudUpload: Boolean
  116. })
  117. const emit = defineEmits(['update:modelValue'])
  118. const handlePreview = current => {
  119. uni.previewImage({
  120. current,
  121. urls: props.modelValue.map(item => formatPath(item))
  122. })
  123. }
  124. const upload = async () => {
  125. let methods = ''
  126. if (props.mediaType === 'image') {
  127. methods = 'chooseAndUploadImage'
  128. } else {
  129. methods = 'chooseAndUploadVideo'
  130. }
  131. utils[methods](
  132. {
  133. count: props.count,
  134. ...props.chooseData
  135. },
  136. {
  137. url: props.action || proxy.$uploadUrl,
  138. name: props.name,
  139. header: props.header,
  140. formData: props.formData,
  141. editable: props.editable
  142. },
  143. props.cloudUpload
  144. ).then(res => {
  145. const fileList = [...props.modelValue, ...res]
  146. fileList.length > props.count && fileList.splice(props.count)
  147. emit('update:modelValue', fileList)
  148. })
  149. }
  150. const handleDel = index => {
  151. props.modelValue.splice(index, 1)
  152. emit('update:modelValue', props.modelValue)
  153. }
  154. const formatPath = item => {
  155. const path = props.pathKey ? item[props.pathKey] : item
  156. return utils.isHttp(path) ? path : config.baseUrl + path
  157. }
  158. </script>
  159. <style lang="scss" scoped>
  160. .fs-file {
  161. &-list {
  162. display: flex;
  163. flex-wrap: wrap;
  164. padding-top: var(--gutter);
  165. }
  166. &-box {
  167. position: relative;
  168. margin-bottom: var(--gutter);
  169. margin-left: var(--gutter);
  170. }
  171. &-video {
  172. width: 300rpx;
  173. height: 200rpx;
  174. }
  175. &-del {
  176. position: absolute;
  177. top: 0;
  178. right: 0;
  179. transform: translate(50%, -50%);
  180. font-size: 22px;
  181. color: var(--sub);
  182. z-index: 10;
  183. }
  184. }
  185. </style>