Skip to content

Commit

Permalink
refactor(components): refactor upload (element-plus#6014)
Browse files Browse the repository at this point in the history
* refactor(components): refactor ElUpload

* refactor(components): refactor upload

* test: use jsx

* refactor: resolve review comments

* fix: ts error

* refactor: re-order imports

* refactor: rename

* fix: infinity watch

* refactor: rename

* refactor: address PR comments

Co-authored-by: Kevin <[email protected]>
  • Loading branch information
emojiiii and sxzz committed Mar 5, 2022
1 parent ce10bab commit 13ffea1
Show file tree
Hide file tree
Showing 24 changed files with 980 additions and 1,025 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@
"i18n-ally.localesPaths": "packages/locale/lang",
"i18n-ally.enabledParsers": ["ts"],
"i18n-ally.enabledFrameworks": ["vue", "vue-sfc"],
"i18n-ally.keystyle": "nested"
"i18n-ally.keystyle": "nested",
"iconify.includes": ["ep"]
}
68 changes: 34 additions & 34 deletions docs/en-US/component/upload.md

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions docs/examples/upload/custom-thumbnail.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<el-upload action="#" list-type="picture-card" :auto-upload="false">
<template #default>
<el-icon><plus /></el-icon>
<el-icon><Plus /></el-icon>
</template>
<template #file="{ file }">
<div>
Expand All @@ -18,14 +18,14 @@
class="el-upload-list__item-delete"
@click="handleDownload(file)"
>
<el-icon><download /></el-icon>
<el-icon><Download /></el-icon>
</span>
<span
v-if="!disabled"
class="el-upload-list__item-delete"
@click="handleRemove(file)"
>
<el-icon><delete /></el-icon>
<el-icon><Delete /></el-icon>
</span>
</span>
</div>
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/upload/photo-album.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
:on-preview="handlePictureCardPreview"
:on-remove="handleRemove"
>
<el-icon><plus /></el-icon>
<el-icon><Plus /></el-icon>
</el-upload>
<el-dialog v-model="dialogVisible">
<img width="100%" :src="dialogImageUrl" alt="" />
Expand Down
5 changes: 3 additions & 2 deletions packages/components/upload/__tests__/upload-dragger.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { provide, h, defineComponent } from 'vue'
import { provide, h, defineComponent, computed } from 'vue'
import makeMount from '@element-plus/test-utils/make-mount'
import { uploadContextKey } from '@element-plus/tokens'
import UploadDragger from '../src/upload-dragger.vue'

const AXIOM = 'Rem is the best girl'
Expand All @@ -9,7 +10,7 @@ const Wrapper = defineComponent({
onDrop: Function,
},
setup(props, { slots }) {
provide('uploader', { accept: 'video/*' })
provide(uploadContextKey, { accept: computed(() => 'video/*') })
return () => h(UploadDragger, props, slots)
},
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { h } from 'vue'
import { EVENT_CODE } from '@element-plus/constants'

import makeMount from '@element-plus/test-utils/make-mount'
Expand All @@ -17,7 +16,7 @@ describe('<upload-list />', () => {
test('should render correct', () => {
const wrapper = mount({
slots: {
default: ({ file }: { file: File }) => h('div', null, file.name),
default: ({ file }: { file: File }) => <div>{file.name}</div>,
},
})
expect(wrapper.text()).toBe(testName)
Expand Down
20 changes: 17 additions & 3 deletions packages/components/upload/__tests__/upload.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { nextTick } from 'vue'
import { computed, defineComponent, h, nextTick, provide } from 'vue'
import makeMount from '@element-plus/test-utils/make-mount'
import { on } from '@element-plus/utils'
import { EVENT_CODE } from '@element-plus/constants'

import Upload from '../src/upload.vue'
import { uploadContextKey } from '@element-plus/tokens'
import UploadContent from '../src/upload-content.vue'

const AXIOM = 'Rem is the best girl'
const action = 'test-action'
Expand All @@ -16,7 +17,20 @@ const mockGetFile = (element: HTMLInputElement, files: File[]) => {
})
}

const mount = makeMount(Upload, {
const Wrapper = defineComponent({
props: {
action: {
type: String,
required: true,
},
},
setup(props, { slots }) {
provide(uploadContextKey, { accept: computed(() => 'video/*') })
return () => h(UploadContent, props, slots)
},
})

const mount = makeMount(Wrapper, {
props: {
action,
},
Expand Down
19 changes: 8 additions & 11 deletions packages/components/upload/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import Upload from './src/index.vue'
import { withInstall } from '@element-plus/utils'
import Upload from './src/upload.vue'

import type { App } from 'vue'
import type { SFCWithInstall } from '@element-plus/utils'
export const ElUpload = withInstall(Upload)
export default ElUpload

Upload.install = (app: App): void => {
app.component(Upload.name, Upload)
}

const _Upload = Upload as SFCWithInstall<typeof Upload>

export default _Upload
export const ElUpload = _Upload
export * from './src/upload'
export * from './src/upload-content'
export * from './src/upload-list'
export * from './src/upload-dragger'
86 changes: 45 additions & 41 deletions packages/components/upload/src/ajax.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,29 @@
import { hasOwn } from '@element-plus/utils'
import { isNil } from 'lodash-unified'
import { throwError } from '@element-plus/utils'
import type {
ElUploadProgressEvent,
ElUploadRequestOptions,
ElUploadAjaxError,
} from './upload.type'
UploadRequestHandler,
UploadProgressEvent,
UploadRequestOptions,
} from './upload'

const SCOPE = 'ElUpload'

export class UploadAjaxError extends Error {
status: number
method: string
url: string

constructor(message: string, status: number, method: string, url: string) {
super(message)
this.status = status
this.method = method
this.url = url
}
}

function getError(
action: string,
option: ElUploadRequestOptions,
option: UploadRequestOptions,
xhr: XMLHttpRequest
) {
let msg: string
Expand All @@ -19,11 +35,7 @@ function getError(
msg = `fail to ${option.method} ${action} ${xhr.status}`
}

const err = new Error(msg) as ElUploadAjaxError
err.status = xhr.status
err.method = option.method
err.url = action
return err
return new UploadAjaxError(msg, xhr.status, option.method, action)
}

function getBody(xhr: XMLHttpRequest): XMLHttpRequestResponseType {
Expand All @@ -39,44 +51,40 @@ function getBody(xhr: XMLHttpRequest): XMLHttpRequestResponseType {
}
}

export default function upload(option: ElUploadRequestOptions) {
if (typeof XMLHttpRequest === 'undefined') {
return
}
export const ajaxUpload: UploadRequestHandler = (option) => {
if (typeof XMLHttpRequest === 'undefined')
throwError(SCOPE, 'XMLHttpRequest is undefined')

const xhr = new XMLHttpRequest()
const action = option.action

if (xhr.upload) {
xhr.upload.onprogress = function progress(e) {
if (e.total > 0) {
;(e as ElUploadProgressEvent).percent = (e.loaded / e.total) * 100
}
option.onProgress(e)
}
xhr.upload.addEventListener('progress', (evt) => {
const progressEvt = evt as UploadProgressEvent
progressEvt.percent = evt.total > 0 ? (evt.loaded / evt.total) * 100 : 0
option.onProgress(progressEvt)
})
}

const formData = new FormData()

if (option.data) {
Object.keys(option.data).forEach((key) => {
formData.append(key, option.data[key])
})
for (const [key, value] of Object.entries(option.data)) {
if (Array.isArray(value)) formData.append(key, ...value)
else formData.append(key, value)
}
}

formData.append(option.filename, option.file, option.file.name)

xhr.onerror = function error() {
xhr.addEventListener('error', () => {
option.onError(getError(action, option, xhr))
}
})

xhr.onload = function onload() {
xhr.addEventListener('load', () => {
if (xhr.status < 200 || xhr.status >= 300) {
return option.onError(getError(action, option, xhr))
}

option.onSuccess(getBody(xhr))
}
})

xhr.open(option.method, action, true)

Expand All @@ -85,17 +93,13 @@ export default function upload(option: ElUploadRequestOptions) {
}

const headers = option.headers || {}

for (const item in headers) {
if (hasOwn(headers, item) && headers[item] !== null) {
xhr.setRequestHeader(item, headers[item])
}
}

if (headers instanceof Headers) {
headers.forEach((value, key) => {
xhr.setRequestHeader(key, value)
})
headers.forEach((value, key) => xhr.setRequestHeader(key, value))
} else {
for (const [key, value] of Object.entries(headers)) {
if (isNil(value)) continue
xhr.setRequestHeader(key, String(value))
}
}

xhr.send(formData)
Expand Down
Loading

0 comments on commit 13ffea1

Please sign in to comment.