当前位置:   article > 正文

Vue3上传(Upload)_vue3上传文件

vue3上传文件

可自定义设置以下属性:

  • 接受上传的文件类型(accept),类型:string,默认 '*',,与<input type="file" />的 accept 属性一致,详见 input accept Attribute

  • 是否支持多选文件(multiple),类型:boolean,默认 false

  • 限制上传数量(maxCount),当为 1 时,始终用最新上传的文件代替当前文件,类型:number,默认 1

  • 上传描述文字(tip),类型:string | slot,默认 'Upload'

  • 预览图片缩放规则,仅当上传文件为图片时生效(fit),类型:'fill' | 'contain' | 'cover' | 'none' | 'scale-down',默认 'contain'

  • Space 组件属性配置(spaceProps),用于配置多个文件时的排列方式,类型:object,默认 {}

  • Spin 组件属性配置(spinProps),用于配置上传中样式,类型:object,默认 {}

  • Image 组件属性配置(imageProps),用于配置图片预览,类型:object,默认 {}

  • Message 组件属性配置(messageProps),用于配置操作消息提示,类型:object,默认 {}

  • 操作成功的消息提示(actionMessage),传 {} 即可不显示任何消息提示,类型:{upload?: string, remove?: string },默认 { upload: '上传成功', remove: '删除成功' }

  • 上传文件之前的钩子(beforeUpload),类型:Function,默认: () => true,参数为上传的文件,返回 false 则停止上传,返回 true 继续上传,通常用来现在用户上传的文件格式和大小

  • 上传文件的方式(uploadMode),类型:'base64' | 'custom',默认 'base64'

  • 自定义上传行为(customRequest),类型:Function,默认 () => {},只有 uploadMode: custom 时,才会使用 customRequest 自定义上传行为

  • 是否禁用(disabled),类型:boolean,默认 false,禁用时只能预览,不能删除和上传

  • 已上传的文件列表(v-model:file-list),类型:Array<{name?: string, url: any, [propName: string]: any}>,默认: []

效果如下图:在线预览

2b6c163e35394174a3b575e6655b24ab.png

84e10fe20c484c62b2c72082bcc2b8b1.png

其中引入使用了以下组件:

①创建上传组件Upload.vue:

  1. <script setup lang="ts">
  2. import { ref, watchEffect } from 'vue'
  3. import Spin from '../spin'
  4. import Message from '../message'
  5. import Image from '../image'
  6. import Space from '../space'
  7. interface FileType {
  8. name?: string // 文件名
  9. url: any // 文件地址
  10. [propName: string]: any // 添加一个字符串索引签名,用于包含带有任意数量的其他属性
  11. }
  12. interface MessageType {
  13. upload?: string // 上传成功的消息提示,没有设置该属性时即不显示上传消息提示
  14. remove?: string // 删除成功的消息提示,没有设置该属性时即不显示删除消息提示
  15. }
  16. interface Props {
  17. accept?: string // 接受上传的文件类型,与 <input type="file" /> 的 accept 属性一致,参考 https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/input/file
  18. multiple?: boolean // 是否支持多选文件
  19. maxCount?: number // 限制上传数量。当为 1 时,始终用最新上传的文件代替当前文件
  20. tip?: string // 上传描述文字 string | slot
  21. fit?: 'contain' | 'fill' | 'cover' | 'none' | 'scale-down' // 预览图片缩放规则,仅当上传文件为图片时生效
  22. spaceProps?: object // Space 组件属性配置,用于配置多个文件时的排列方式
  23. spinProps?: object // Spin 组件属性配置,用于配置上传中样式
  24. imageProps?: object // Image 组件属性配置,用于配置图片预览
  25. messageProps?: object // Message 组件属性配置,用于配置操作消息提示
  26. actionMessage?: MessageType // 操作成功的消息提示,传 {} 即可不显示任何消息提示
  27. beforeUpload?: Function // 上传文件之前的钩子,参数为上传的文件,返回 false 则停止上传,返回 true 继续上传,通常用来现在用户上传的文件格式和大小
  28. uploadMode?: 'base64' | 'custom' // 上传文件的方式,默认是 base64,可选 'base64' | 'custom'
  29. customRequest?: Function // 自定义上传行为,只有 uploadMode: custom 时,才会使用 customRequest 自定义上传行为
  30. disabled?: boolean // 是否禁用,只能预览,不能删除和上传
  31. fileList?: FileType[] // (v-model) 已上传的文件列表
  32. }
  33. const props = withDefaults(defineProps<Props>(), {
  34. accept: '*', // 默认支持所有类型
  35. multiple: false,
  36. maxCount: 1,
  37. tip: 'Upload',
  38. fit: 'contain',
  39. spaceProps: () => ({}),
  40. spinProps: () => ({}),
  41. imageProps: () => ({}),
  42. messageProps: () => ({}),
  43. actionMessage: () => ({ upload: '上传成功', remove: '删除成功' }),
  44. beforeUpload: () => true,
  45. uploadMode: 'base64',
  46. customRequest: () => {},
  47. disabled: false,
  48. fileList: () => []
  49. })
  50. const uploadedFiles = ref<FileType[]>([]) // 上传文件列表
  51. const showUpload = ref(1)
  52. const uploading = ref<boolean[]>(Array(props.maxCount).fill(false))
  53. const uploadInput = ref()
  54. watchEffect(() => {
  55. // 回调立即执行一次,同时会自动跟踪回调中所依赖的所有响应式依赖
  56. initUpload()
  57. })
  58. function initUpload() {
  59. uploadedFiles.value = [...props.fileList]
  60. if (uploadedFiles.value.length > props.maxCount) {
  61. uploadedFiles.value.splice(props.maxCount)
  62. }
  63. if (props.disabled) {
  64. // 禁用
  65. showUpload.value = uploadedFiles.value.length
  66. } else {
  67. if (uploadedFiles.value.length < props.maxCount) {
  68. showUpload.value = props.fileList.length + 1
  69. } else {
  70. showUpload.value = props.maxCount
  71. }
  72. }
  73. }
  74. function isImage(url: string) {
  75. // 检查url是否为图片
  76. const imageUrlReg = /\.(jpg|jpeg|png|gif)$/i
  77. const base64Reg = /^data:image/
  78. return imageUrlReg.test(url) || base64Reg.test(url)
  79. }
  80. function isPDF(url: string) {
  81. // 检查url是否为pdf
  82. const pdfUrlReg = /\.pdf$/i
  83. const base64Reg = /^data:application\/pdf/
  84. return pdfUrlReg.test(url) || base64Reg.test(url)
  85. }
  86. function onDrop(e: DragEvent, index: number) {
  87. // 拖拽上传
  88. const files = e.dataTransfer?.files
  89. if (files?.length) {
  90. const len = files.length
  91. for (let n = 0; n < len; n++) {
  92. if (index + n <= props.maxCount) {
  93. uploadFile(files[n], index + n)
  94. } else {
  95. break
  96. }
  97. }
  98. // input的change事件默认保存上一次input的value值,同一value值(根据文件路径判断)在上传时不重新加载
  99. uploadInput.value[index].value = ''
  100. }
  101. }
  102. function onClick(index: number) {
  103. uploadInput.value[index].click()
  104. }
  105. function onUpload(e: any, index: number) {
  106. // 点击上传
  107. const files = e.target.files
  108. if (files?.length) {
  109. const len = files.length
  110. for (let n = 0; n < len; n++) {
  111. if (index + n < props.maxCount) {
  112. uploadFile(files[n], index + n)
  113. } else {
  114. break
  115. }
  116. }
  117. // input的change事件默认保存上一次input的value值,同一value值(根据文件路径判断)在上传时不重新加载
  118. uploadInput.value[index].value = ''
  119. }
  120. }
  121. const emits = defineEmits(['update:fileList', 'change', 'remove'])
  122. const uploadFile = (file: File, index: number) => {
  123. // 统一上传文件方法
  124. // console.log('开始上传 upload-event files:', file)
  125. if (props.beforeUpload(file)) {
  126. // 使用用户钩子进行上传前文件判断,例如大小、类型限制
  127. if (props.maxCount > showUpload.value) {
  128. showUpload.value++
  129. }
  130. if (props.uploadMode === 'base64') {
  131. // 以base64方式读取文件
  132. uploading.value[index] = true
  133. base64Upload(file, index)
  134. }
  135. if (props.uploadMode === 'custom') {
  136. // 自定义上传行为,需配合 customRequest 属性
  137. uploading.value[index] = true
  138. customUpload(file, index)
  139. }
  140. }
  141. }
  142. function base64Upload(file: File, index: number) {
  143. var reader = new FileReader()
  144. reader.readAsDataURL(file) // 以base64方式读取文件
  145. reader.onloadstart = function (e) {
  146. // 当读取操作开始时触发
  147. // reader.abort() // 取消上传
  148. // console.log('开始读取 onloadstart:', e)
  149. }
  150. reader.onabort = function (e) {
  151. // 当读取操作被中断时触发
  152. // console.log('读取中止 onabort:', e)
  153. }
  154. reader.onerror = function (e) {
  155. // 当读取操作发生错误时触发
  156. // console.log('读取错误 onerror:', e)
  157. }
  158. reader.onprogress = function (e) {
  159. // 在读取Blob时触发,读取上传进度,50ms左右调用一次
  160. // console.log('读取中 onprogress:', e)
  161. // console.log('已读取:', Math.ceil(e.loaded / e.total * 100))
  162. if (e.loaded === e.total) {
  163. // 上传完成
  164. uploading.value[index] = false // 隐藏loading状态
  165. }
  166. }
  167. reader.onload = function (e) {
  168. // 当读取操作成功完成时调用
  169. // console.log('读取成功 onload:', e)
  170. // 该文件的base64数据,如果是图片,则前端可直接用来展示图片
  171. uploadedFiles.value.push({
  172. name: file.name,
  173. url: e.target?.result
  174. })
  175. props.actionMessage.upload && messageRef.value.success(props.actionMessage.upload)
  176. emits('update:fileList', uploadedFiles.value)
  177. emits('change', uploadedFiles.value)
  178. }
  179. reader.onloadend = function (e) {
  180. // 当读取操作结束时触发(要么成功,要么失败)触发
  181. // console.log('读取结束 onloadend:', e)
  182. }
  183. }
  184. function customUpload(file: File, index: number) {
  185. props
  186. .customRequest(file)
  187. .then((res: any) => {
  188. uploadedFiles.value.push(res)
  189. props.actionMessage.upload && messageRef.value.success(props.actionMessage.upload)
  190. emits('update:fileList', uploadedFiles.value)
  191. emits('change', uploadedFiles.value)
  192. })
  193. .catch((err: any) => {
  194. if (props.maxCount > 1) {
  195. showUpload.value = uploadedFiles.value.length + 1
  196. }
  197. messageRef.value.error(err)
  198. })
  199. .finally(() => {
  200. uploading.value[index] = false
  201. })
  202. }
  203. const imageRef = ref()
  204. function onPreview(n: number, url: string) {
  205. if (isImage(url)) {
  206. const files = uploadedFiles.value.slice(0, n).filter((file) => !isImage(file.url))
  207. imageRef.value[n - files.length].preview(0)
  208. } else {
  209. window.open(url)
  210. }
  211. }
  212. function onRemove(index: number) {
  213. if (uploadedFiles.value.length < props.maxCount) {
  214. showUpload.value--
  215. }
  216. const removeFile = uploadedFiles.value.splice(index, 1)
  217. props.actionMessage.remove && messageRef.value.success(props.actionMessage.remove)
  218. emits('remove', removeFile)
  219. emits('update:fileList', uploadedFiles.value)
  220. emits('change', uploadedFiles.value)
  221. }
  222. const messageRef = ref()
  223. function onInfo(content: string) {
  224. messageRef.value.info(content)
  225. }
  226. function onSuccess(content: string) {
  227. messageRef.value.success(content)
  228. }
  229. function onError(content: string) {
  230. messageRef.value.error(content)
  231. }
  232. function onWarning(content: string) {
  233. messageRef.value.warning(content)
  234. }
  235. function onLoading(content: string) {
  236. messageRef.value.loading(content)
  237. }
  238. defineExpose({
  239. info: onInfo,
  240. success: onSuccess,
  241. error: onError,
  242. warning: onWarning,
  243. loading: onLoading
  244. })
  245. </script>
  246. <template>
  247. <div class="m-upload-list">
  248. <Space gap="small" v-bind="spaceProps">
  249. <div class="m-upload-item" v-for="n of showUpload" :key="n">
  250. <div class="m-upload">
  251. <div
  252. v-show="!uploading[n - 1] && !uploadedFiles[n - 1]"
  253. class="m-upload-wrap"
  254. :class="{ 'upload-disabled': disabled }"
  255. @dragenter.stop.prevent
  256. @dragover.stop.prevent
  257. @drop.stop.prevent="disabled ? () => false : onDrop($event, n - 1)"
  258. @click="disabled ? () => false : onClick(n - 1)"
  259. >
  260. <input
  261. ref="uploadInput"
  262. type="file"
  263. @click.stop
  264. :accept="accept"
  265. :multiple="multiple"
  266. @change="onUpload($event, n - 1)"
  267. style="display: none"
  268. />
  269. <div>
  270. <svg
  271. focusable="false"
  272. class="u-plus"
  273. data-icon="plus"
  274. width="1em"
  275. height="1em"
  276. fill="currentColor"
  277. aria-hidden="true"
  278. viewBox="64 64 896 896"
  279. >
  280. <defs></defs>
  281. <path d="M482 152h60q8 0 8 8v704q0 8-8 8h-60q-8 0-8-8V160q0-8 8-8z"></path>
  282. <path d="M176 474h672q8 0 8 8v60q0 8-8 8H176q-8 0-8-8v-60q0-8 8-8z"></path>
  283. </svg>
  284. <p class="u-tip">
  285. <slot>{{ tip }}</slot>
  286. </p>
  287. </div>
  288. </div>
  289. <div class="m-file-uploading" v-show="uploading[n - 1]">
  290. <Spin class="u-spin" tip="uploading" size="small" indicator="spin-line" v-bind="spinProps" />
  291. </div>
  292. <div class="m-file-preview" v-if="uploadedFiles[n - 1]">
  293. <Image
  294. v-if="isImage(uploadedFiles[n - 1].url)"
  295. ref="imageRef"
  296. :bordered="false"
  297. :width="82"
  298. :height="82"
  299. :src="uploadedFiles[n - 1].url"
  300. :name="uploadedFiles[n - 1].name"
  301. v-bind="imageProps"
  302. />
  303. <svg
  304. v-else-if="isPDF(uploadedFiles[n - 1].url)"
  305. class="u-file"
  306. focusable="false"
  307. data-icon="file-pdf"
  308. aria-hidden="true"
  309. viewBox="64 64 896 896"
  310. >
  311. <path
  312. d="M531.3 574.4l.3-1.4c5.8-23.9 13.1-53.7 7.4-80.7-3.8-21.3-19.5-29.6-32.9-30.2-15.8-.7-29.9 8.3-33.4 21.4-6.6 24-.7 56.8 10.1 98.6-13.6 32.4-35.3 79.5-51.2 107.5-29.6 15.3-69.3 38.9-75.2 68.7-1.2 5.5.2 12.5 3.5 18.8 3.7 7 9.6 12.4 16.5 15 3 1.1 6.6 2 10.8 2 17.6 0 46.1-14.2 84.1-79.4 5.8-1.9 11.8-3.9 17.6-5.9 27.2-9.2 55.4-18.8 80.9-23.1 28.2 15.1 60.3 24.8 82.1 24.8 21.6 0 30.1-12.8 33.3-20.5 5.6-13.5 2.9-30.5-6.2-39.6-13.2-13-45.3-16.4-95.3-10.2-24.6-15-40.7-35.4-52.4-65.8zM421.6 726.3c-13.9 20.2-24.4 30.3-30.1 34.7 6.7-12.3 19.8-25.3 30.1-34.7zm87.6-235.5c5.2 8.9 4.5 35.8.5 49.4-4.9-19.9-5.6-48.1-2.7-51.4.8.1 1.5.7 2.2 2zm-1.6 120.5c10.7 18.5 24.2 34.4 39.1 46.2-21.6 4.9-41.3 13-58.9 20.2-4.2 1.7-8.3 3.4-12.3 5 13.3-24.1 24.4-51.4 32.1-71.4zm155.6 65.5c.1.2.2.5-.4.9h-.2l-.2.3c-.8.5-9 5.3-44.3-8.6 40.6-1.9 45 7.3 45.1 7.4zm191.4-388.2L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326zm1.8 562H232V136h302v216a42 42 0 0042 42h216v494z"
  313. ></path>
  314. </svg>
  315. <svg class="u-file" v-else focusable="false" data-icon="file" aria-hidden="true" viewBox="64 64 896 896">
  316. <path d="M534 352V136H232v752h560V394H576a42 42 0 01-42-42z" fill="#e6f7ff"></path>
  317. <path
  318. d="M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM602 137.8L790.2 326H602V137.8zM792 888H232V136h302v216a42 42 0 0042 42h216v494z"
  319. ></path>
  320. </svg>
  321. <div class="m-file-mask">
  322. <a class="m-icon" title="预览" @click="onPreview(n - 1, uploadedFiles[n - 1].url)">
  323. <svg class="u-icon" focusable="false" data-icon="eye" aria-hidden="true" viewBox="64 64 896 896">
  324. <path
  325. d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
  326. ></path>
  327. </svg>
  328. </a>
  329. <a class="m-icon" title="删除" @click.prevent.stop="onRemove(n - 1)" v-show="!disabled">
  330. <svg class="u-icon" focusable="false" data-icon="delete" aria-hidden="true" viewBox="64 64 896 896">
  331. <path
  332. d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"
  333. ></path>
  334. </svg>
  335. </a>
  336. </div>
  337. </div>
  338. </div>
  339. </div>
  340. </Space>
  341. <Message ref="messageRef" v-bind="messageProps" />
  342. </div>
  343. </template>
  344. <style lang="less" scoped>
  345. .m-upload-list {
  346. display: inline-block;
  347. .m-upload-item {
  348. display: inline-block;
  349. }
  350. .mr8 {
  351. margin-right: 8px;
  352. }
  353. }
  354. .m-upload {
  355. position: relative;
  356. display: inline-block;
  357. width: 100px;
  358. height: 100px;
  359. .m-upload-wrap {
  360. display: flex;
  361. align-items: center;
  362. justify-content: center;
  363. text-align: center;
  364. width: 100px;
  365. height: 100px;
  366. border-radius: 8px;
  367. border: 1px dashed #d9d9d9;
  368. background-color: rgba(0, 0, 0, 0.02);
  369. cursor: pointer;
  370. transition: border-color 0.3s;
  371. &:hover {
  372. border-color: @themeColor;
  373. }
  374. .u-plus {
  375. display: inline-block;
  376. width: 14px;
  377. height: 14px;
  378. fill: rgba(0, 0, 0, 0.88);
  379. }
  380. .u-tip {
  381. margin-top: 8px;
  382. font-size: 14px;
  383. color: rgba(0, 0, 0, 0.88);
  384. line-height: 1.5714285714285714;
  385. }
  386. }
  387. .upload-disabled {
  388. cursor: not-allowed;
  389. &:hover {
  390. border-color: #d9d9d9;
  391. }
  392. }
  393. .m-file-uploading {
  394. width: 100px;
  395. height: 100px;
  396. padding: 8px;
  397. border-radius: 8px;
  398. border: 1px dashed #d9d9d9;
  399. background-color: rgba(0, 0, 0, 0.02);
  400. display: flex;
  401. align-items: center;
  402. text-align: center;
  403. .u-spin {
  404. display: inline-block;
  405. :deep(.u-tip) {
  406. max-width: 82px;
  407. overflow: hidden;
  408. white-space: nowrap;
  409. text-overflow: ellipsis;
  410. }
  411. }
  412. }
  413. .m-file-preview {
  414. position: relative;
  415. padding: 8px;
  416. width: 100px;
  417. height: 100px;
  418. border-radius: 8px;
  419. padding: 8px;
  420. border: 1px solid #d9d9d9;
  421. display: flex;
  422. align-items: center;
  423. text-align: center;
  424. .u-file {
  425. display: inline-block;
  426. width: 100%;
  427. height: 60px;
  428. fill: @themeColor;
  429. }
  430. .m-file-mask {
  431. // top right bottom left 简写为 inset: 0
  432. // insert 无论元素的书写模式、行内方向和文本朝向如何,其所定义的都不是逻辑偏移而是实体偏移
  433. position: absolute;
  434. inset: 0;
  435. margin: 8px;
  436. display: flex;
  437. align-items: center;
  438. justify-content: center;
  439. background: rgba(0, 0, 0, 0.5);
  440. opacity: 0;
  441. pointer-events: none;
  442. transition: opacity 0.3s;
  443. .m-icon {
  444. display: inline-block;
  445. height: 16px;
  446. margin: 0 4px;
  447. cursor: pointer;
  448. .u-icon {
  449. display: inline-block;
  450. width: 16px;
  451. height: 16px;
  452. fill: rgba(255, 255, 255, 0.65);
  453. cursor: pointer;
  454. transition: all 0.3s;
  455. &:hover {
  456. fill: rgba(255, 255, 255, 1);
  457. }
  458. }
  459. }
  460. }
  461. &:hover {
  462. .m-file-mask {
  463. opacity: 1;
  464. pointer-events: auto;
  465. }
  466. }
  467. }
  468. }
  469. </style>

②在要使用的页面引入:

  1. <script setup lang="ts">
  2. import { ref, watchEffect } from 'vue'
  3. const uploadRef = ref()
  4. const files = ref([])
  5. const fileList = ref([
  6. {
  7. name: '1.jpg',
  8. url: 'https://cdn.jsdelivr.net/gh/themusecatcher/resources@0.0.5/1.jpg'
  9. },
  10. {
  11. name: 'Markdown.pdf',
  12. url: 'https://cdn.jsdelivr.net/gh/themusecatcher/resources@0.0.5/Markdown.pdf'
  13. }
  14. ])
  15. const imageList = ref([
  16. {
  17. name: '1.jpg',
  18. url: 'https://cdn.jsdelivr.net/gh/themusecatcher/resources@0.0.5/1.jpg'
  19. }
  20. ])
  21. watchEffect(() => {
  22. console.log('files:', files.value)
  23. })
  24. watchEffect(() => {
  25. console.log('fileList:', fileList.value)
  26. })
  27. watchEffect(() => {
  28. console.log('imageList:', imageList.value)
  29. })
  30. function onBeforeUpload(file: File) {
  31. const acceptTypes = ['image/jpg', 'image/jpeg', 'image/png', 'application/pdf']
  32. if (file.size > 500 * 1024) {
  33. // 文件大于 500KB 时取消上传
  34. uploadRef.value.warning('文件必须小于500KB')
  35. return false // 停止上传
  36. }
  37. if (!acceptTypes.includes(file.type)) {
  38. // 继续上传
  39. uploadRef.value.error('只能上传jpg、jpeg、png、pdf格式的文件')
  40. return false // 停止上传
  41. }
  42. return true // 继续上传
  43. }
  44. function onCustomRequest(file: File) {
  45. return new Promise((resolve, reject) => {
  46. setTimeout(() => {
  47. // 模拟接口调用返回name和url
  48. if (file.type === 'application/pdf') {
  49. var res = {
  50. name: 'Markdown.pdf',
  51. url: 'https://cdn.jsdelivr.net/gh/themusecatcher/resources@0.0.5/Markdown.pdf'
  52. }
  53. } else {
  54. var res = {
  55. name: '1.jpg',
  56. url: 'https://cdn.jsdelivr.net/gh/themusecatcher/resources@0.0.5/1.jpg'
  57. }
  58. }
  59. if (res) {
  60. resolve(res)
  61. } else {
  62. reject('upload request fail ...')
  63. }
  64. }, 1000)
  65. })
  66. }
  67. interface FileType {
  68. name?: string // 文件名
  69. url: any // 文件地址
  70. [propName: string]: any // 添加一个字符串索引签名,用于包含带有任意数量的其他属性
  71. }
  72. function onChange(files: FileType[]) {
  73. console.log('change:', files)
  74. }
  75. function onRemove(file: FileType) {
  76. console.log('remove:', file)
  77. }
  78. </script>
  79. <template>
  80. <div>
  81. <h1>{{ $route.name }} {{ $route.meta.title }}</h1>
  82. <h2 class="mt30 mb10">基本使用</h2>
  83. <Upload v-model:fileList="files" />
  84. <h2 class="mt30 mb10">禁用</h2>
  85. <h3 class="mb10">只能预览,不能删除和上传</h3>
  86. <Upload disabled v-model:fileList="fileList" />
  87. <h2 class="mt30 mb10">多文件上传</h2>
  88. <h3 class="mb10">限制上传数量为3</h3>
  89. <Upload multiple :max-count="3" v-model:fileList="fileList" />
  90. <h2 class="mt30 mb10">自定义样式</h2>
  91. <h3 class="mb10">缩略图等比覆盖,上传描述文字使用:上传</h3>
  92. <Upload :max-count="3" tip="上传" fit="cover" v-model:fileList="fileList" />
  93. <h2 class="mt30 mb10">限制文件大小和类型</h2>
  94. <h3 class="mb10">上传文件最大500KB,同时文件类型只能是图片</h3>
  95. <Upload
  96. ref="uploadRef"
  97. accept="image/*,application/pdf"
  98. :max-count="3"
  99. :before-upload="onBeforeUpload"
  100. v-model:fileList="imageList"
  101. @change="onChange"
  102. @remove="onRemove"
  103. />
  104. <h2 class="mt30 mb10">自定义上传行为</h2>
  105. <Upload
  106. multiple
  107. :max-count="5"
  108. upload-mode="custom"
  109. :custom-request="onCustomRequest"
  110. v-model:fileList="fileList"
  111. @change="onChange"
  112. @remove="onRemove"
  113. />
  114. </div>
  115. </template>

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/995763
推荐阅读
相关标签
  

闽ICP备14008679号