123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209 |
- import { ElUpload, ElIcon, ElProgress } from 'element-plus'
- import { SuccessFilled, CircleCloseFilled, Paperclip } from '@element-plus/icons-vue'
- import './index.less'
- import { toRefs, defineComponent, computed } from 'vue'
- import CipButton from '@cip/components/cip-button'
- export default defineComponent({
- name: 'CipUpload',
- props: {
- uploadFile: { // 文件上传
- type: Function,
- default: (info) => {}
- },
- accept: { // 可选择文件类型控制
- type: String,
- default: '' // 所有
- },
- beforeRemove: Function, // 删除文件之前的钩子
- onRemove: Function, // 文件列表移除文件时的钩子
- beforeUpload: Function, // 上传文件之前的钩子
- onError: Function, // 文件上传失败时的钩子
- onSuccess: Function, // 文件上传成功时的钩子
- onPreview: Function, // 点击文件列表中已上传的文件时的钩子
- fileList: { type: Array, default: () => [] }, // 默认上传文件
- multiple: Boolean, // 是否允许多个
- disabled: Boolean, // 禁用状态
- drag: Boolean, // 是否拖拽上传
- action: String, // 上传地址
- limit: Number, // 上传数量限制
- showFileList: { // 是否展示默认,后期考虑不用默认的
- type: Boolean,
- default: true
- },
- itemStyle: { // 文件列表的行内样式
- type: Object,
- default: () => ({})
- },
- listType: { // 展示形式
- type: String,
- default: 'text'
- }
- },
- emits: ['update:fileList'],
- inheritAttrs: false,
- setup (props, { slots, emit, attrs }) {
- const { fileList } = toRefs(props)
- const uploadDisabled = computed(() => props.disabled || props.limit <= fileList.value.length)
- function genDefault () {
- if (props.drag) { // 拖拽
- return <div class="cip-upload-drag-trigger">
- <i class="el-icon-upload" style="font-size: 50px"></i>
- <div>
- 点击或将文件拖拽到这里上传
- </div>
- <div class={'el-upload__tip'}>{slots.tip?.()}</div>
- </div>
- } else if (props.listType === 'picture-card') { // 缩略图
- return uploadDisabled.value || <div class="cip-upload-picture-trigger" style={ props.itemStyle }>
- <i class="el-icon-plus"></i>
- <div>上传</div>
- </div>
- } else { // 默认
- return <CipButton buttonType="upload" disabled={uploadDisabled.value}></CipButton>
- }
- }
- async function deleteItem (index) {
- const uploadFiles = fileList.value.concat()
- const file = uploadFiles.splice(index, 1)[0]
- let flag
- try {
- flag = await props.beforeRemove?.(file, uploadFiles)
- } catch (err) {
- flag = props.beforeRemove?.(file, uploadFiles)
- }
- // 如果beforeRemove函数不存在 或者 返回值为true
- if (flag || !props.beforeRemove) {
- emit('update:fileList', uploadFiles)
- props.onRemove?.(file, uploadFiles)
- }
- }
- function genUpload (slots) {
- return <ElUpload
- {...attrs}
- fileList={props.fileList}
- onUpdate:fileList={list => emit('update:fileList', list)}
- class={'cip-upload-content'}
- action={props.action}
- httpRequest={httpRequest}
- beforeUpload={props.beforeUpload}
- multiple={props.multiple}
- disabled={props.disabled}
- accept={props.accept}
- drag={props.drag}
- show-file-list={false}
- >
- {{
- // 1. default或者trigger存在,不渲染默认;渲染对应的default或者trigger;trigger在下面一行渲染
- default: () => slots.default?.() || (slots.trigger ? undefined : genDefault()),
- // 这个trigger要这么写,如果给它赋值函数,会出bug
- trigger: slots.trigger ? () => slots.trigger() : undefined,
- tip: () => props.drag || <div class={'el-upload__tip'} style={{ marginLeft: props.listType === 'picture-card' ? '0' : '12px' }}>{slots.tip?.()}</div>
- }}
- </ElUpload>
- }
- function httpRequest (opts) {
- function setStatus (status, uploadFile) {
- const uid = opts.file.uid
- const index = fileList.value.findIndex(file => file.uid === uid)
- if (status === 'error') {
- Object.assign(fileList.value[index], { status, opts })
- } else if (status === 'success') {
- Object.assign(fileList.value[index], { status, ...uploadFile, opts: undefined })
- }
- emit('update:fileList', fileList.value.concat())
- }
- props.uploadFile(opts)?.then(file => {
- setStatus('success', file)
- props.onSuccess?.(file, opts.file, fileList.value.concat())
- }).catch((err) => {
- setStatus('error')
- props.onError?.(err, opts.file, fileList.value.concat())
- })
- }
- function reUpload (item) {
- item.status = 'ready'
- httpRequest(item.opts)
- }
- function genItems () {
- return <>
- {genUpload(slots)}
- { fileList.value?.length === 0
- ? undefined
- : <ul class="el-upload-list el-upload-list--text">
- {
- fileList.value.map((item, index) => <li style={ props.itemStyle } class={['el-upload-list__item', `is-${item.status}`]} key={item.name + index} onClick={() => props.onPreview?.(item)}>
- <div class="el-upload-list__item-name">
- <ElIcon><Paperclip /></ElIcon>
- <span class="el-upload-list__item-file-name">{item.name}</span>
- </div>
- <div class="el-upload-list__item-label">
- {item.status === 'error' && <i class="el-icon-refresh-right" onClick={() => reUpload(item)}></i>}
- <ElIcon class="upload--success"><SuccessFilled /></ElIcon>
- <i class="el-icon-close upload--close" onClick={() => deleteItem(index)}></i>
- </div>
- {item.status === 'ready' && <ElProgress stroke-width={2} indeterminate percentage={72} />}
- </li>)
- }
- </ul>}
- </>
- }
- function genPictureCard () {
- return <>
- <ul class="el-upload-list el-upload-list--picture-card">
- {
- fileList.value.map((item, index) => <li class="el-upload-list__item" style={ props.itemStyle } key={item.name + index} onClick={() => props.onPreview?.(item)}>
- {item.status === 'success' && <img src={item.url} />}
- <div class="upload--close">
- <ElIcon onClick={() => deleteItem(index)}><CircleCloseFilled /></ElIcon>
- </div>
- {item.status === 'ready' && <ElProgress stroke-width={4} style="top:50%;width:80%" indeterminate percentage={72} />}
- </li>)
- }
- {uploadDisabled.value || <li class="el-upload el-upload--picture-card" style={ props.itemStyle }>{genUpload({ trigger: slots.trigger, default: slots.default })}</li>}
- </ul>
- <div class={'el-upload__tip'}>{slots.tip?.()}</div>
- </>
- }
- function genPicture () {
- return <>
- {genUpload(slots)}
- { fileList.value?.length === 0
- ? undefined
- : <ul class="el-upload-list el-upload-list--picture">
- {
- fileList.value.map((item, index) => <li key={item.name + index} onClick={() => props.onPreview?.(item)}>
- {['jpeg', 'jpg', 'png', 'gif', 'svg'].includes(item.name?.split('.').pop())
- ? <img src={item.url} />
- : <i class="picture-icon"></i>}
- <div class="picture-info">
- <div class="item-name">{item.name}</div>
- <span class="item-size">{item.size}</span>
- </div>
- <div class="upload--close">
- <ElIcon onClick={() => deleteItem(index)}><CircleCloseFilled /></ElIcon>
- </div>
- </li>)
- }
- </ul>}
- </>
- }
- const renderTypeMap = {
- text: genItems,
- 'picture-card': genPictureCard,
- picture: genPicture
- }
- return () => <div class="cip-upload">
- { props.showFileList ? (renderTypeMap[props.listType]?.()) : genUpload(slots) }
- </div>
- }
- })
|