Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(uploader): support resolve-type to get different file result #1194

Merged
merged 6 commits into from
Sep 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file modified packages/varlet-cli/lib/node/bin.js
100755 → 100644
Empty file.
11 changes: 11 additions & 0 deletions packages/varlet-shared/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,14 @@ export const inViewport = (element: HTMLElement) => {

return xInViewport && yInViewport
}

export const toDataURL = (file: File): Promise<string> =>
new Promise((resolve) => {
const fileReader = new FileReader()

fileReader.onload = () => {
resolve(fileReader.result as string)
}

fileReader.readAsDataURL(file)
})
36 changes: 13 additions & 23 deletions packages/varlet-ui/src/uploader/Uploader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ import ImagePreview from '../image-preview'
import Ripple from '../ripple'
import Hover from '../hover'
import { defineComponent, nextTick, reactive, computed, watch, ref } from 'vue'
import { props, type VarFile, type ValidateTrigger } from './props'
import { isNumber, toNumber, isString, normalizeToArray } from '@varlet/shared'
import { props, type VarFile, type UploaderValidateTrigger } from './props'
import { isNumber, toNumber, isString, normalizeToArray, toDataURL } from '@varlet/shared'
import { isHTMLSupportImage, isHTMLSupportVideo } from '../utils/shared'
import { call, useValidation, createNamespace, formatElevation } from '../utils/components'
import { useForm } from '../form/provide'
Expand Down Expand Up @@ -217,27 +217,17 @@ export default defineComponent({
return Array.from<File>(fileList as ArrayLike<File>)
}

function resolver(varFile: VarFile): Promise<VarFile> {
return new Promise((resolve) => {
// For performance, only file reader processing is performed on images
if (!varFile.file!.type.startsWith('image')) {
resolve(varFile)
return
}

const fileReader = new FileReader()

fileReader.onload = () => {
const base64 = fileReader.result as string

varFile.cover = base64
varFile.url = base64

resolve(varFile)
}
async function resolver(varFile: VarFile): Promise<VarFile> {
if (
props.resolveType === 'data-url' ||
(varFile.file!.type.startsWith('image') && props.resolveType === 'default')
) {
const dataURL = await toDataURL(varFile.file!)
varFile.cover = dataURL
varFile.url = dataURL
}

fileReader.readAsDataURL(varFile.file as File)
})
return varFile
}

function getResolvers(varFiles: VarFile[]) {
Expand Down Expand Up @@ -370,7 +360,7 @@ export default defineComponent({
ImagePreview.close()
}

function validateWithTrigger(trigger: ValidateTrigger) {
function validateWithTrigger(trigger: UploaderValidateTrigger) {
nextTick(() => {
const { validateTrigger, rules, modelValue } = props
vt(validateTrigger, trigger, rules, modelValue, varFileUtils)
Expand Down
134 changes: 133 additions & 1 deletion packages/varlet-ui/src/uploader/__tests__/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ test('test uploader preview', async () => {
},
})

await wrapper.vm.handleChange(createEvent('cat.jpg', 'image/jpg'))
await wrapper.vm.handleChange(createEvent('cat.png', 'image/png'))
await delay(16)
await wrapper.find('.var-uploader__file').trigger('click')
await delay(100)
Expand Down Expand Up @@ -365,3 +365,135 @@ test('test uploader extra slot', async () => {

wrapper.unmount()
})

test('test uploader resolve-type as default when file type is image', async () => {
const { mockRestore } = mockFileReader('data:image/png;base64,')
const { mockRestore: mockRestoreStubs } = mockStubs()
const onUpdateModelValue = vi.fn((value) => wrapper.setProps({ modelValue: value }))

const wrapper = mount(VarUploader, {
props: {
modelValue: [],
resolveType: 'default',
'onUpdate:modelValue': onUpdateModelValue,
},
})

await wrapper.vm.handleChange(createEvent('cat.png', 'image/png'))
await delay(100)
expect(wrapper.vm.files[0].cover.includes('data:image/png;base64,')).toBe(true)

mockRestoreStubs()
wrapper.unmount()
mockRestore()
})

test('test uploader resolve-type as default when file type is not image', async () => {
const { mockRestore } = mockFileReader('data:')
const { mockRestore: mockRestoreStubs } = mockStubs()
const onUpdateModelValue = vi.fn((value) => wrapper.setProps({ modelValue: value }))

const wrapper = mount(VarUploader, {
props: {
modelValue: [],
resolveType: 'default',
'onUpdate:modelValue': onUpdateModelValue,
},
})

await wrapper.vm.handleChange(createEvent('data.json', 'application/json'))
await delay(100)
expect(wrapper.vm.files[0].cover.includes('data:')).toBe(false)

mockRestoreStubs()
wrapper.unmount()
mockRestore()
})

test('test uploader resolve-type as file when file type is image', async () => {
const { mockRestore } = mockFileReader('data:image/png;base64,')
const { mockRestore: mockRestoreStubs } = mockStubs()
const onUpdateModelValue = vi.fn((value) => wrapper.setProps({ modelValue: value }))

const wrapper = mount(VarUploader, {
props: {
modelValue: [],
resolveType: 'file',
'onUpdate:modelValue': onUpdateModelValue,
},
})

await wrapper.vm.handleChange(createEvent('cat.png', 'image/png'))
await delay(100)
expect(wrapper.vm.files[0].cover).toBe('')

mockRestoreStubs()
wrapper.unmount()
mockRestore()
})

test('test uploader resolve-type as file when file type is not image', async () => {
const { mockRestore } = mockFileReader('data:')
const { mockRestore: mockRestoreStubs } = mockStubs()
const onUpdateModelValue = vi.fn((value) => wrapper.setProps({ modelValue: value }))

const wrapper = mount(VarUploader, {
props: {
modelValue: [],
resolveType: 'file',
'onUpdate:modelValue': onUpdateModelValue,
},
})

await wrapper.vm.handleChange(createEvent('data.json', 'application/json'))
await delay(100)
expect(wrapper.vm.files[0].cover).toBe('')

mockRestoreStubs()
wrapper.unmount()
mockRestore()
})

test('test uploader resolve-type as data-url when file type is image', async () => {
const { mockRestore } = mockFileReader('data:image/png;base64,')
const { mockRestore: mockRestoreStubs } = mockStubs()
const onUpdateModelValue = vi.fn((value) => wrapper.setProps({ modelValue: value }))

const wrapper = mount(VarUploader, {
props: {
modelValue: [],
resolveType: 'data-url',
'onUpdate:modelValue': onUpdateModelValue,
},
})

await wrapper.vm.handleChange(createEvent('cat.png', 'image/png'))
await delay(100)
expect(wrapper.vm.files[0].cover.includes('data:image/png;base64,')).toBe(true)

mockRestoreStubs()
wrapper.unmount()
mockRestore()
})

test('test uploader resolve-type as data-url when file type is not image', async () => {
const { mockRestore } = mockFileReader('data:')
const { mockRestore: mockRestoreStubs } = mockStubs()
const onUpdateModelValue = vi.fn((value) => wrapper.setProps({ modelValue: value }))

const wrapper = mount(VarUploader, {
props: {
modelValue: [],
resolveType: 'data-url',
'onUpdate:modelValue': onUpdateModelValue,
},
})

await wrapper.vm.handleChange(createEvent('data.json', 'application/json'))
await delay(100)
expect(wrapper.vm.files[0].cover.includes('data:')).toBe(true)

mockRestoreStubs()
wrapper.unmount()
mockRestore()
})
1 change: 1 addition & 0 deletions packages/varlet-ui/src/uploader/docs/en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ const files = ref([
| `previewed` | Whether to allow preview | _boolean_ | `true` |
| `ripple` | Whether to open ripple | _boolean_ | `true` |
| `hide-list` | Whether to hide the file list | _boolean_ | `false` |
| `resolve-type` | The file read result type, Can be set to `default` `file` `data-url` (`default`, the image type contains base64 and File object, other types contain only File object. `file`, which contains only File object. `data-url`, which contains base64 and File object) | _string_ | `default` |
| `validate-trigger` | Timing to trigger validation, The optional value is `onChange` `onRemove` | _ValidateTriggers[]_ | `['onChange', 'onRemove']` |
| `rules` | The validation rules,Returns `true` to indicate that the validation passed,The remaining values are converted to text as user prompts | _Array<(v: VarFile, u: VarFileUtils) => any>_ | `-` |

Expand Down
1 change: 1 addition & 0 deletions packages/varlet-ui/src/uploader/docs/zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,7 @@ const files = ref([
| `previewed` | 是否允许预览 | _boolean_ | `true` |
| `ripple` | 是否开启水波纹 | _boolean_ | `true` |
| `hide-list` | 是否隐藏文件列表 | _boolean_ | `false` |
| `resolve-type` | 文件读取结果类型,可选值为 `default` `file` `data-url`(`default`,图像包含 base64 编码和 File 对象,其他类型仅包含 File 对象。`file`,仅包含 File 对象。`data-url`,包含 base64 编码和 File 对象) | _string_ | `default` |
| `validate-trigger` | 触发验证的时机, 可选值为 `onChange` `onRemove` | _ValidateTriggers[]_ | `['onChange', 'onRemove']` |
| `rules` | 验证规则,返回 `true` 表示验证通过,其余的值则转换为文本作为用户提示 | _Array<(v: VarFile, u: VarFileUtils) => any>_ | `-` |

Expand Down
10 changes: 8 additions & 2 deletions packages/varlet-ui/src/uploader/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ export interface VarFile {
state?: 'loading' | 'success' | 'error'
}

export type ValidateTrigger = 'onChange' | 'onRemove'
export type UploaderResolveType = 'default' | 'file' | 'data-url'

export type UploaderValidateTrigger = 'onChange' | 'onRemove'

export const props = {
modelValue: {
Expand All @@ -34,6 +36,10 @@ export const props = {
type: [Boolean, Number, String],
default: true,
},
resolveType: {
type: String as PropType<UploaderResolveType>,
default: 'default',
},
removable: {
type: Boolean,
default: true,
Expand All @@ -49,7 +55,7 @@ export const props = {
default: true,
},
validateTrigger: {
type: Array as PropType<Array<ValidateTrigger>>,
type: Array as PropType<Array<UploaderValidateTrigger>>,
default: () => ['onChange', 'onRemove'],
},
rules: Array as PropType<Array<(v: VarFile) => any>>,
Expand Down
3 changes: 3 additions & 0 deletions packages/varlet-ui/types/uploader.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export type VarFileFit = 'fill' | 'contain' | 'cover' | 'none' | 'scale-down'

export type VarFileState = 'loading' | 'success' | 'error'

export type VarFileResolveType = 'default' | 'file' | 'data-url'

export interface VarFile {
file?: File
name?: string
Expand Down Expand Up @@ -41,6 +43,7 @@ export interface UploaderProps extends BasicAttributes {
previewed?: boolean
hideList?: boolean
ripple?: boolean
resolveType?: VarFileResolveType
validateTrigger?: Array<UploaderValidateTrigger>
rules?: Array<(v: VarFile[], u: UploaderVarFileUtils) => any>
onBeforeFilter?: ListenerProp<(files: VarFile[]) => Promise<VarFile[]> | VarFile[]>
Expand Down