Upload
Upload基于 VantUploader做了一层 Formily 适配。上传行为遵循 Vant 语义:在afterRead中自行处理请求和文件状态。
设计重点
- 编辑态里展示给用户看的文件列表,统一走
fileList,并映射到字段dataSource - 表单真正提交的值,统一走
formatValue(fileList)的返回结果 Upload不内置action/httpRequest请求层;上传、失败文案、响应映射都建议在 Vant 的afterRead回调里完成
基础使用
组件负责选文件和维护文件列表,适合跟随整个表单一起提交二进制数据的场景。
vue
<script setup lang="ts">
import { createForm } from '@silver-formily/core'
import { Form, FormButtonGroup, FormItem, Submit, Upload } from '@silver-formily/vant'
import { Field } from '@silver-formily/vue'
import { showDemoResult } from '../shared'
const form = createForm()
async function showUploadResult(values: Record<string, File[]>) {
await showDemoResult(values)
}
</script>
<template>
<Form :form="form">
<Field
name="attachments"
title="巡检附件"
description="适合先选中文件,最后跟随整张表单一起提交"
:decorator="[FormItem, {
labelAlign: 'top',
}]"
:component="[Upload, {
accept: 'image/*,.pdf',
formatValue: (fileList: any[] = []) => fileList.map(item => item.file).filter(Boolean),
maxCount: 3,
uploadText: '上传附件',
}]"
/>
<FormButtonGroup>
<Submit @submit="showUploadResult">
查看结果
</Submit>
</FormButtonGroup>
</Form>
</template>自动上传
在 afterRead 中自行发起请求,并按 Vant 的文件项格式写回 status / message / url。
当前文档站示例使用 Cloudflare Pages Functions 提供空上传接口:POST /mock/upload(仅解析请求并返回 mock URL,不落盘文件)。
vue
<script setup lang="ts">
import { createForm } from '@silver-formily/core'
import { Form, FormButtonGroup, FormItem, Submit, Upload } from '@silver-formily/vant'
import { Field } from '@silver-formily/vue'
import { showFailToast } from 'vant'
import { showDemoResult } from '../shared'
const form = createForm()
const uploadAction = 'https://vant.silver-formily.org/mock/upload'
async function afterRead(item: any) {
item.status = 'uploading'
item.message = '上传中...'
try {
const formData = new FormData()
formData.append('file', item.file)
const response = await fetch(uploadAction, {
body: formData,
method: 'POST',
})
const result = await response.json()
item.url = result.data?.url
item.status = 'done'
item.message = ''
}
catch (error) {
showFailToast(error instanceof Error ? error.message : '上传失败')
throw error
}
}
async function showUploadResult(values: Record<string, string[]>) {
await showDemoResult(values, '上传完成后的字段值')
}
</script>
<template>
<Form :form="form">
<Field
name="attachments"
title="自动上传"
description="上传成功后只提交远端 URL,不再提交原始文件对象"
:decorator="[FormItem, {
labelAlign: 'top',
}]"
:component="[Upload, {
accept: 'image/*',
afterRead,
formatValue: (fileList: any[] = []) => fileList.map(item => item.url),
maxCount: 2,
uploadText: '上传图片',
}]"
/>
<FormButtonGroup>
<Submit @submit="showUploadResult">
查看结果
</Submit>
</FormButtonGroup>
</Form>
</template>阅读态
readPretty 模式下会切到 PreviewText.Upload,常见的文件名、URL 数组和对象数组都能直接展示。
vue
<script setup lang="ts">
import type { UploadPreviewFile } from '@silver-formily/vant'
import { createForm } from '@silver-formily/core'
import { Form, FormItem, Upload } from '@silver-formily/vant'
import { Field } from '@silver-formily/vue'
function openExternalFile(url: string) {
window.open(url, '_blank', 'noopener')
}
function isImageFile(file: { file?: File, isImage?: boolean, url?: string }) {
if (file.isImage) {
return true
}
if (file.file?.type?.startsWith('image/')) {
return true
}
return /\.(?:bmp|gif|jpe?g|png|svg|webp)$/i.test(file.url ?? '')
}
const previewFile: UploadPreviewFile = (file) => {
if (isImageFile(file)) {
return
}
if (file.url) {
openExternalFile(file.url)
}
}
const attachmentFiles = [
{
name: '门店照片.jpg',
url: 'https://fastly.jsdelivr.net/npm/@vant/assets/cat.jpeg',
},
{
name: '报修记录.pdf',
url: 'https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf',
},
]
const form = createForm({
values: {
attachments: attachmentFiles,
},
})
</script>
<template>
<Form :form="form">
<Field
name="attachments"
title="历史附件"
description="点击文件可按自定义逻辑打开"
read-pretty
:decorator="[FormItem, {
labelAlign: 'top',
}]"
:component="[Upload, { previewFile }]"
:data-source="attachmentFiles"
/>
</Form>
</template>API
使用约定
fileList会映射为字段dataSource- 字段
value由formatValue(fileList)单向计算得出,不会反向推导出fileList - 当
maxCount = 1且未显式传reupload时,组件会默认打开reupload,更贴近单文件替换场景
扩展属性
| 属性名 | 类型 | 描述 | 默认值 |
|---|---|---|---|
modelValue | any | 字段最终值,由 formatValue(fileList) 单向生成 | - |
formatValue | Function | 把文件列表转换成字段最终值 | fileList => fileList |
fileList | UploadFileListItem[] | 当前文件列表,对应字段 dataSource,用于预览展示 | [] |
previewFile | Function | 阅读态下点击预览项时的自定义打开逻辑 | - |
其余属性可以参考Vant Uploader 官方文档
字段注入
可以通过字段实例拿到内部 Uploader 引用:
ts
const uploaderRef = fieldRef.value?.invoke('getUploaderRef')
uploaderRef?.value?.chooseFile()
uploaderRef?.value?.reuploadFile(0)
uploaderRef?.value?.closeImagePreview()官方透传属性与事件
除上面的扩展属性外,其余属性、插槽和事件都继续遵循 Vant Uploader 语义,例如 accept、maxCount、uploadText、afterRead、beforeRead、previewImage、oversize、delete、clickPreview 等。