vxe-pc-ui
Version:
A vue based PC component library
1,173 lines • 70.8 kB
JavaScript
import { defineComponent, ref, h, reactive, watch, computed, TransitionGroup, inject, createCommentVNode, onUnmounted, onMounted } from 'vue';
import XEUtils from 'xe-utils';
import { VxeUI, getConfig, getI18n, getIcon, useSize, createEvent, globalEvents, renderEmptyElement } from '../../ui';
import { getSlotVNs } from '../..//ui/src/vn';
import { errLog, warnLog } from '../../ui/src/log';
import { initTpImg, getTpImg, getEventTargetNode, toCssUnit } from '../../ui/src/dom';
import { readLocalFile } from './util';
import VxeButtonComponent from '../../button/src/button';
export default defineComponent({
name: 'VxeUpload',
props: {
modelValue: [Array, String, Object],
showList: {
type: Boolean,
default: () => getConfig().upload.showList
},
moreConfig: Object,
readonly: {
type: Boolean,
default: null
},
disabled: {
type: Boolean,
default: null
},
mode: {
type: String,
default: () => getConfig().upload.mode
},
imageTypes: {
type: Array,
default: () => XEUtils.clone(getConfig().upload.imageTypes, true)
},
imageConfig: {
type: Object,
default: () => XEUtils.clone(getConfig().upload.imageConfig, true)
},
/**
* 已废弃,被 image-config 替换
* @deprecated
*/
imageStyle: {
type: Object,
default: () => XEUtils.clone(getConfig().upload.imageStyle, true)
},
fileTypes: {
type: Array,
default: () => XEUtils.clone(getConfig().upload.fileTypes, true)
},
dragSort: Boolean,
dragToUpload: {
type: Boolean,
default: () => XEUtils.clone(getConfig().upload.dragToUpload, true)
},
pasteToUpload: {
type: Boolean,
default: () => XEUtils.clone(getConfig().upload.pasteToUpload, true)
},
keyField: String,
singleMode: Boolean,
urlMode: Boolean,
multiple: Boolean,
limitSize: {
type: [String, Number],
default: () => getConfig().upload.limitSize
},
showLimitSize: {
type: Boolean,
default: () => getConfig().upload.showLimitSize
},
limitSizeText: {
type: [String, Number, Function],
default: () => getConfig().upload.limitSizeText
},
limitCount: {
type: [String, Number],
default: () => getConfig().upload.limitCount
},
showLimitCount: {
type: Boolean,
default: () => getConfig().upload.showLimitCount
},
limitCountText: {
type: [String, Number, Function],
default: () => getConfig().upload.limitCountText
},
nameField: {
type: String,
default: () => getConfig().upload.nameField
},
typeField: {
type: String,
default: () => getConfig().upload.typeField
},
urlField: {
type: String,
default: () => getConfig().upload.urlField
},
sizeField: {
type: String,
default: () => getConfig().upload.sizeField
},
showErrorStatus: {
type: Boolean,
default: () => getConfig().upload.showErrorStatus
},
showProgress: {
type: Boolean,
default: () => getConfig().upload.showProgress
},
progressText: {
type: [String, Number, Function],
default: () => getConfig().upload.progressText
},
autoHiddenButton: {
type: Boolean,
default: () => getConfig().upload.autoHiddenButton
},
showUploadButton: {
type: Boolean,
default: () => getConfig().upload.showUploadButton
},
buttonText: {
type: [String, Number, Function],
default: () => getConfig().upload.buttonText
},
buttonIcon: {
type: String,
default: () => getConfig().upload.buttonIcon
},
showButtonText: {
type: Boolean,
default: () => getConfig().upload.showButtonText
},
showButtonIcon: {
type: Boolean,
default: () => getConfig().upload.showButtonIcon
},
showRemoveButton: {
type: Boolean,
default: () => getConfig().upload.showRemoveButton
},
showDownloadButton: {
type: Boolean,
default: () => getConfig().upload.showDownloadButton
},
showPreview: {
type: Boolean,
default: () => getConfig().upload.showPreview
},
showTip: {
type: Boolean,
default: () => null
},
tipText: [String, Number, Function],
hintText: String,
previewMethod: Function,
uploadMethod: Function,
beforeRemoveMethod: Function,
removeMethod: Function,
beforeDownloadMethod: Function,
downloadMethod: Function,
getUrlMethod: Function,
getThumbnailUrlMethod: Function,
size: {
type: String,
default: () => getConfig().upload.size || getConfig().size
}
},
emits: [
'update:modelValue',
'add',
'remove',
'remove-fail',
'download',
'download-fail',
'upload-success',
'upload-error',
'sort-dragend'
],
setup(props, context) {
const { emit, slots } = context;
const $xeForm = inject('$xeForm', null);
const formItemInfo = inject('xeFormItemInfo', null);
const $xeTable = inject('$xeTable', null);
const xID = XEUtils.uniqueId();
const { computeSize } = useSize(props);
const refElem = ref();
const refPopupElem = ref();
const refDragLineElem = ref();
const refModalDragLineElem = ref();
const reactData = reactive({
isDragUploadStatus: false,
showMorePopup: false,
isActivated: false,
fileList: [],
fileCacheMaps: {},
isDragMove: false,
dragIndex: -1,
dragTipText: ''
});
const internalData = {
imagePreviewTypes: ['jpg', 'jpeg', 'png', 'gif'],
prevDragIndex: -1
// prevDragPos: ''
};
const refMaps = {
refElem
};
const computeFormReadonly = computed(() => {
const { readonly } = props;
if (readonly === null) {
if ($xeForm) {
return $xeForm.props.readonly;
}
return false;
}
return readonly;
});
const computeIsDisabled = computed(() => {
const { disabled } = props;
if (disabled === null) {
if ($xeForm) {
return $xeForm.props.disabled;
}
return false;
}
return disabled;
});
const computeKeyField = computed(() => {
return props.keyField || '_X_KEY';
});
const computeIsImage = computed(() => {
return props.mode === 'image';
});
const computeNameProp = computed(() => {
return props.nameField || 'name';
});
const computeTypeProp = computed(() => {
return props.typeField || 'type';
});
const computeUrlProp = computed(() => {
return props.urlField || 'url';
});
const computeSizeProp = computed(() => {
return props.sizeField || 'size';
});
const computeLimitMaxSize = computed(() => {
return XEUtils.toNumber(props.limitSize) * 1024 * 1024;
});
const computeLimitMaxCount = computed(() => {
return props.multiple ? XEUtils.toNumber(props.limitCount) : 1;
});
const computeOverCount = computed(() => {
const { multiple } = props;
const { fileList } = reactData;
const limitMaxCount = computeLimitMaxCount.value;
if (multiple) {
if (limitMaxCount) {
return fileList.length >= limitMaxCount;
}
return true;
}
return fileList.length >= 1;
});
const computeLimitSizeUnit = computed(() => {
const limitSize = XEUtils.toNumber(props.limitSize);
if (limitSize) {
if (limitSize > 1048576) {
return `${limitSize / 1048576}T`;
}
if (limitSize > 1024) {
return `${limitSize / 1024}G`;
}
return `${limitSize}M`;
}
return '';
});
const computedShowTipText = computed(() => {
const { showTip, tipText } = props;
if (XEUtils.isBoolean(showTip)) {
return showTip;
}
const defShowTip = getConfig().upload.showTip;
if (XEUtils.isBoolean(defShowTip)) {
return defShowTip;
}
if (tipText) {
return true;
}
return false;
});
const computedDefTipText = computed(() => {
const { limitSize, fileTypes, multiple, limitCount } = props;
const tipText = props.tipText || props.hintText;
const isImage = computeIsImage.value;
const limitSizeUnit = computeLimitSizeUnit.value;
if (XEUtils.isString(tipText)) {
return tipText;
}
if (XEUtils.isFunction(tipText)) {
return `${tipText({})}`;
}
const defTips = [];
if (isImage) {
if (multiple && limitCount) {
defTips.push(getI18n('vxe.upload.imgCountHint', [limitCount]));
}
if (limitSize && limitSizeUnit) {
defTips.push(getI18n('vxe.upload.imgSizeHint', [limitSizeUnit]));
}
}
else {
if (fileTypes && fileTypes.length) {
defTips.push(getI18n('vxe.upload.fileTypeHint', [fileTypes.join('/')]));
}
if (limitSize && limitSizeUnit) {
defTips.push(getI18n('vxe.upload.fileSizeHint', [limitSizeUnit]));
}
if (multiple && limitCount) {
defTips.push(getI18n('vxe.upload.fileCountHint', [limitCount]));
}
}
return defTips.join(getI18n('vxe.base.comma'));
});
const computeImageOpts = computed(() => {
return Object.assign({}, props.imageConfig || props.imageStyle);
});
const computeImgStyle = computed(() => {
const imageOpts = computeImageOpts.value;
const { width, height } = imageOpts;
const stys = {};
if (width) {
stys.width = toCssUnit(width);
}
if (height) {
stys.height = toCssUnit(height);
}
return stys;
});
const computeMoreOpts = computed(() => {
return Object.assign({ showMoreButton: true }, props.moreConfig);
});
const computeMaps = {};
const $xeUpload = {
xID,
props,
context,
reactData,
internalData,
getRefMaps: () => refMaps,
getComputeMaps: () => computeMaps
};
const getUniqueKey = () => {
return XEUtils.uniqueId();
};
const getFieldKey = (item) => {
const keyField = computeKeyField.value;
return item[keyField];
};
const updateFileList = () => {
const { modelValue, multiple } = props;
const formReadonly = computeFormReadonly.value;
const keyField = computeKeyField.value;
const nameProp = computeNameProp.value;
const typeProp = computeTypeProp.value;
const urlProp = computeUrlProp.value;
const sizeProp = computeSizeProp.value;
const fileList = modelValue
? (modelValue ? (XEUtils.isArray(modelValue) ? modelValue : [modelValue]) : []).map(item => {
if (!item || XEUtils.isString(item)) {
const url = `${item || ''}`;
const urlObj = XEUtils.parseUrl(item);
const name = (urlObj ? urlObj.searchQuery[nameProp] : '') || parseFileName(url);
return {
[nameProp]: name,
[typeProp]: (urlObj ? urlObj.searchQuery[typeProp] : '') || parseFileType(name),
[urlProp]: url,
[sizeProp]: XEUtils.toNumber(urlObj ? urlObj.searchQuery[sizeProp] : 0) || 0,
[keyField]: getUniqueKey()
};
}
const name = item[nameProp] || '';
item[nameProp] = name;
item[typeProp] = item[typeProp] || parseFileType(name);
item[urlProp] = item[urlProp] || '';
item[sizeProp] = item[sizeProp] || 0;
item[keyField] = item[keyField] || getUniqueKey();
return item;
})
: [];
reactData.fileList = (formReadonly || multiple) ? fileList : (fileList.slice(0, 1));
};
const parseFileName = (url) => {
return decodeURIComponent(`${url || ''}`).split('/').pop() || '';
};
const parseFileType = (name) => {
// 这里不用split('.').pop()因为没有后缀时会返回自身
const index = name.lastIndexOf('.');
if (index > 0) {
return name.substring(index + 1).toLowerCase();
}
return '';
};
const dispatchEvent = (type, params, evnt) => {
emit(type, createEvent(evnt, { $upload: $xeUpload }, params));
};
const handleChange = (value) => {
const { singleMode, urlMode } = props;
const urlProp = computeUrlProp.value;
const nameProp = computeNameProp.value;
let restList = value ? value.slice(0) : [];
if (urlMode) {
restList = restList.map(item => {
const url = item[urlProp];
if (url) {
const urlObj = XEUtils.parseUrl(url);
if (!urlObj.searchQuery[nameProp]) {
return `${url}${url.indexOf('?') === -1 ? '?' : '&'}${nameProp}=${encodeURIComponent(item[nameProp] || '')}`;
}
}
return url;
});
}
emit('update:modelValue', singleMode ? (restList[0] || null) : restList);
};
const getThumbnailFileUrl = (item) => {
const getThumbnailUrlFn = props.getThumbnailUrlMethod || getConfig().upload.getThumbnailUrlMethod;
if (getThumbnailUrlFn) {
return getThumbnailUrlFn({
$upload: $xeUpload,
option: item
});
}
return getFileUrl(item);
};
const getFileUrl = (item) => {
const getUrlFn = props.getUrlMethod || getConfig().upload.getUrlMethod;
const urlProp = computeUrlProp.value;
return getUrlFn
? getUrlFn({
$upload: $xeUpload,
option: item
})
: item[urlProp];
};
const handleDefaultFilePreview = (item) => {
const { imageTypes, showDownloadButton } = props;
const typeProp = computeTypeProp.value;
const beforeDownloadFn = props.beforeDownloadMethod || getConfig().upload.beforeDownloadMethod;
const { imagePreviewTypes } = internalData;
// 如果是预览图片
if (imagePreviewTypes.concat(imageTypes || []).some(type => `${type}`.toLowerCase() === `${item[typeProp]}`.toLowerCase())) {
if (VxeUI.previewImage) {
VxeUI.previewImage({
urlList: [getFileUrl(item)],
showDownloadButton,
beforeDownloadMethod: beforeDownloadFn
? () => {
return beforeDownloadFn({
$upload: $xeUpload,
option: item
});
}
: undefined
});
}
}
};
const handlePreviewFileEvent = (evnt, item) => {
const previewFn = props.previewMethod || getConfig().upload.previewMethod;
if (props.showPreview) {
if (previewFn) {
previewFn({
$upload: $xeUpload,
option: item
});
}
else {
handleDefaultFilePreview(item);
}
}
};
const handlePreviewImageEvent = (evnt, item, index) => {
const { showDownloadButton } = props;
const { fileList } = reactData;
const beforeDownloadFn = props.beforeDownloadMethod || getConfig().upload.beforeDownloadMethod;
if (props.showPreview) {
if (VxeUI.previewImage) {
VxeUI.previewImage({
urlList: fileList.map(item => getFileUrl(item)),
activeIndex: index,
showDownloadButton,
beforeDownloadMethod: beforeDownloadFn
? ({ index }) => {
return beforeDownloadFn({
$upload: $xeUpload,
option: fileList[index]
});
}
: undefined
});
}
}
};
const handleUploadResult = (item, file) => {
const { showErrorStatus } = props;
const fileKey = getFieldKey(item);
const uploadFn = props.uploadMethod || getConfig().upload.uploadMethod;
if (uploadFn) {
return Promise.resolve(uploadFn({
$upload: $xeUpload,
file,
option: item,
updateProgress(percentNum) {
const { fileCacheMaps } = reactData;
const cacheItem = fileCacheMaps[getFieldKey(item)];
if (cacheItem) {
cacheItem.percent = Math.max(0, Math.min(99, XEUtils.toNumber(percentNum)));
}
}
})).then(res => {
const { fileCacheMaps } = reactData;
const cacheItem = fileCacheMaps[fileKey];
if (cacheItem) {
cacheItem.percent = 100;
}
Object.assign(item, res);
dispatchEvent('upload-success', { option: item, data: res }, null);
}).catch((res) => {
const { fileCacheMaps } = reactData;
const cacheItem = fileCacheMaps[fileKey];
if (cacheItem) {
cacheItem.status = 'error';
}
if (showErrorStatus) {
Object.assign(item, res);
}
else {
reactData.fileList = reactData.fileList.filter(obj => getFieldKey(obj) !== fileKey);
}
dispatchEvent('upload-error', { option: item, data: res }, null);
}).finally(() => {
const { fileCacheMaps } = reactData;
const cacheItem = fileCacheMaps[fileKey];
if (cacheItem) {
cacheItem.loading = false;
}
});
}
else {
const { fileCacheMaps } = reactData;
const cacheItem = fileCacheMaps[fileKey];
if (cacheItem) {
cacheItem.loading = false;
}
}
return Promise.resolve();
};
const handleReUpload = (item) => {
const { uploadMethod, urlMode } = props;
const { fileCacheMaps } = reactData;
const fileKey = getFieldKey(item);
const cacheItem = fileCacheMaps[fileKey];
const uploadFn = uploadMethod || getConfig().upload.uploadMethod;
if (uploadFn && cacheItem) {
const file = cacheItem.file;
cacheItem.loading = true;
cacheItem.status = '';
cacheItem.percent = 0;
handleUploadResult(item, file).then(() => {
if (urlMode) {
handleChange(reactData.fileList);
}
});
}
};
const uploadFile = (files, evnt) => {
const { multiple, urlMode, showLimitSize, limitSizeText, showLimitCount, limitCountText } = props;
const { fileList } = reactData;
const uploadFn = props.uploadMethod || getConfig().upload.uploadMethod;
const keyField = computeKeyField.value;
const nameProp = computeNameProp.value;
const typeProp = computeTypeProp.value;
const urlProp = computeUrlProp.value;
const sizeProp = computeSizeProp.value;
const limitMaxSize = computeLimitMaxSize.value;
const limitMaxCount = computeLimitMaxCount.value;
const limitSizeUnit = computeLimitSizeUnit.value;
let selectFiles = files;
if (multiple && limitMaxCount) {
// 校验文件数量
if (showLimitCount && fileList.length >= limitMaxCount) {
if (VxeUI.modal) {
VxeUI.modal.notification({
title: getI18n('vxe.modal.errTitle'),
status: 'error',
content: limitCountText ? `${XEUtils.isFunction(limitCountText) ? limitCountText({ maxCount: limitMaxCount }) : limitCountText}` : getI18n('vxe.upload.overCountErr', [limitMaxCount])
});
}
return;
}
const overNum = selectFiles.length - (limitMaxCount - fileList.length);
if (showLimitCount && overNum > 0) {
const overExtraList = selectFiles.slice(limitMaxCount - fileList.length);
if (limitCountText) {
VxeUI.modal.notification({
title: getI18n('vxe.modal.errTitle'),
status: 'error',
content: `${XEUtils.isFunction(limitCountText) ? limitCountText({ maxCount: limitMaxCount }) : limitCountText}`
});
}
else if (VxeUI.modal) {
VxeUI.modal.notification({
title: getI18n('vxe.modal.errTitle'),
status: 'error',
width: null,
slots: {
default() {
return h('div', {
class: 'vxe-upload--file-message-over-error'
}, [
h('div', {}, getI18n('vxe.upload.overCountExtraErr', [limitMaxCount, overNum])),
h('div', {
class: 'vxe-upload--file-message-over-extra'
}, overExtraList.map((file, index) => {
return h('div', {
key: index,
class: 'vxe-upload--file-message-over-extra-item'
}, file.name);
}))
]);
}
}
});
}
}
selectFiles = selectFiles.slice(0, limitMaxCount - fileList.length);
}
// 校验文件大小
if (showLimitSize && limitMaxSize) {
for (let i = 0; i < files.length; i++) {
const file = files[0];
if (file.size > limitMaxSize) {
if (VxeUI.modal) {
VxeUI.modal.notification({
title: getI18n('vxe.modal.errTitle'),
status: 'error',
content: limitSizeText ? `${XEUtils.isFunction(limitSizeText) ? limitSizeText({ maxSize: limitMaxSize }) : limitSizeText}` : getI18n('vxe.upload.overSizeErr', [limitSizeUnit])
});
}
return;
}
}
}
const cacheMaps = Object.assign({}, reactData.fileCacheMaps);
const newFileList = multiple ? fileList : [];
const uploadPromiseRests = [];
selectFiles.forEach(file => {
const { name } = file;
const fileKey = getUniqueKey();
const fileObj = {
[nameProp]: name,
[typeProp]: parseFileType(name),
[sizeProp]: file.size,
[urlProp]: URL.createObjectURL(file),
[keyField]: fileKey
};
if (uploadFn) {
cacheMaps[fileKey] = {
file: file,
loading: true,
status: '',
percent: 0
};
}
const item = reactive(fileObj);
if (uploadFn) {
uploadPromiseRests.push(handleUploadResult(item, file));
}
newFileList.push(item);
dispatchEvent('add', { option: item }, evnt);
});
reactData.fileList = newFileList;
reactData.fileCacheMaps = cacheMaps;
Promise.all(urlMode ? uploadPromiseRests : []).then(() => {
handleChange(newFileList);
// 自动更新校验状态
if ($xeForm && formItemInfo) {
$xeForm.triggerItemEvent(evnt, formItemInfo.itemConfig.field, newFileList);
}
});
};
const handleChoose = (evnt) => {
const { multiple, imageTypes, fileTypes } = props;
const isDisabled = computeIsDisabled.value;
const isImage = computeIsImage.value;
if (isDisabled) {
return Promise.resolve({
status: false,
files: [],
file: null
});
}
return readLocalFile({
multiple,
types: isImage ? imageTypes : fileTypes
}).then((params) => {
uploadFile(params.files, evnt);
return params;
});
};
const clickEvent = (evnt) => {
handleChoose(evnt).catch(() => {
// 错误文件类型
});
};
const handleRemoveEvent = (evnt, item, index) => {
const { fileList } = reactData;
fileList.splice(index, 1);
handleChange(fileList);
// 自动更新校验状态
if ($xeForm && formItemInfo) {
$xeForm.triggerItemEvent(evnt, formItemInfo.itemConfig.field, fileList);
}
dispatchEvent('remove', { option: item }, evnt);
};
const removeFileEvent = (evnt, item, index) => {
const beforeRemoveFn = props.beforeRemoveMethod || getConfig().upload.beforeRemoveMethod;
const removeFn = props.removeMethod || getConfig().upload.removeMethod;
Promise.resolve(beforeRemoveFn
? beforeRemoveFn({
$upload: $xeUpload,
option: item
})
: true).then(status => {
if (status) {
if (removeFn) {
Promise.resolve(removeFn({
$upload: $xeUpload,
option: item
})).then(() => {
handleRemoveEvent(evnt, item, index);
}).catch(e => e);
}
else {
handleRemoveEvent(evnt, item, index);
}
}
else {
dispatchEvent('remove-fail', { option: item }, evnt);
}
});
};
const handleDownloadEvent = (evnt, item) => {
dispatchEvent('download', { option: item }, evnt);
};
const downloadFileEvent = (evnt, item) => {
const beforeDownloadFn = props.beforeDownloadMethod || getConfig().upload.beforeDownloadMethod;
const downloadFn = props.downloadMethod || getConfig().upload.downloadMethod;
Promise.resolve(beforeDownloadFn
? beforeDownloadFn({
$upload: $xeUpload,
option: item
})
: true).then(status => {
if (status) {
if (downloadFn) {
Promise.resolve(downloadFn({
$upload: $xeUpload,
option: item
})).then(() => {
handleDownloadEvent(evnt, item);
}).catch(e => e);
}
else {
handleDownloadEvent(evnt, item);
}
}
else {
dispatchEvent('download-fail', { option: item }, evnt);
}
});
};
const handleUploadDragleaveEvent = (evnt) => {
const targetElem = evnt.currentTarget;
const { clientX, clientY } = evnt;
if (targetElem) {
const { x: targetX, y: targetY, height: targetHeight, width: targetWidth } = targetElem.getBoundingClientRect();
if (clientX < targetX || clientX > targetX + targetWidth || clientY < targetY || clientY > targetY + targetHeight) {
reactData.isDragUploadStatus = false;
}
}
};
const handleUploadDragoverEvent = (evnt) => {
const dataTransfer = evnt.dataTransfer;
if (dataTransfer) {
const { items } = dataTransfer;
if (items && items.length) {
evnt.preventDefault();
reactData.isDragUploadStatus = true;
}
}
};
const uploadTransferFileEvent = (evnt, files) => {
const { imageTypes } = props;
const { imagePreviewTypes } = internalData;
const isImage = computeIsImage.value;
if (isImage) {
const pasteImgTypes = imagePreviewTypes.concat(imageTypes && imageTypes.length ? imageTypes : []);
files = files.filter(file => {
const fileType = `${file.type.split('/')[1] || ''}`.toLowerCase();
if (pasteImgTypes.some(type => `${type}`.toLowerCase() === fileType)) {
return true;
}
return false;
});
}
// 如果全部不满足条件
if (!files.length) {
if (VxeUI.modal) {
VxeUI.modal.notification({
title: getI18n('vxe.modal.errTitle'),
status: 'error',
content: getI18n('vxe.upload.uploadTypeErr')
});
}
return;
}
uploadFile(files, evnt);
};
const handleUploadDropEvent = (evnt) => {
const dataTransfer = evnt.dataTransfer;
if (dataTransfer) {
const { items } = dataTransfer;
if (items && items.length) {
evnt.preventDefault();
const files = handleTransferFiles(items);
if (files.length) {
uploadTransferFileEvent(evnt, files);
}
}
}
reactData.isDragUploadStatus = false;
};
const handleTransferFiles = (items) => {
const files = [];
XEUtils.arrayEach(items, item => {
const file = item.getAsFile();
if (file) {
files.push(file);
}
});
return files;
};
const handleMoreEvent = () => {
const formReadonly = computeFormReadonly.value;
const isImage = computeIsImage.value;
if (VxeUI.modal) {
VxeUI.modal.open({
title: formReadonly ? getI18n('vxe.upload.morePopup.readTitle') : getI18n(`vxe.upload.morePopup.${isImage ? 'imageTitle' : 'fileTitle'}`),
width: 660,
height: 500,
escClosable: true,
showMaximize: true,
resize: true,
maskClosable: true,
slots: {
default() {
const { showErrorStatus, dragToUpload, dragSort } = props;
const { isActivated, isDragMove, isDragUploadStatus, dragIndex } = reactData;
const { fileList } = reactData;
const isDisabled = computeIsDisabled.value;
const ons = {};
if (dragToUpload && dragIndex === -1) {
ons.onDragover = handleUploadDragoverEvent;
ons.onDragleave = handleUploadDragleaveEvent;
ons.onDrop = handleUploadDropEvent;
}
return h('div', Object.assign({ ref: refPopupElem, class: ['vxe-upload--more-popup', {
'is--readonly': formReadonly,
'is--disabled': isDisabled,
'is--active': isActivated,
'show--error': showErrorStatus,
'is--drag': isDragUploadStatus
}] }, ons), [
isImage
? (dragSort
? h(TransitionGroup, {
name: `vxe-upload--drag-list${isDragMove ? '' : '-disabled'}`,
tag: 'div',
class: 'vxe-upload--image-more-list'
}, {
default: () => renderImageItemList(fileList, true).concat(renderImageAction(true))
})
: h('div', {
class: 'vxe-upload--image-more-list'
}, renderImageItemList(fileList, true).concat(renderImageAction(true))))
: h('div', {
class: 'vxe-upload--file-more-list'
}, [
renderFileAction(true),
dragSort
? h(TransitionGroup, {
name: `vxe-upload--drag-list${isDragMove ? '' : '-disabled'}`,
tag: 'div',
class: 'vxe-upload--file-list'
}, {
default: () => renderFileItemList(fileList, false)
})
: h('div', {
class: 'vxe-upload--file-list'
}, renderFileItemList(fileList, true))
]),
dragSort
? h('div', {
ref: refModalDragLineElem,
class: 'vxe-upload--drag-line'
})
: renderEmptyElement($xeUpload),
isDragUploadStatus
? h('div', {
class: 'vxe-upload--drag-placeholder'
}, getI18n('vxe.upload.dragPlaceholder'))
: renderEmptyElement($xeUpload)
]);
}
},
onShow() {
reactData.showMorePopup = true;
},
onHide() {
reactData.showMorePopup = false;
}
});
}
};
const showDropTip = (evnt, dragEl, dragPos) => {
const { showMorePopup } = reactData;
const el = refElem.value;
const popupEl = refPopupElem.value;
const wrapperEl = showMorePopup ? popupEl : el;
if (!wrapperEl) {
return;
}
const wrapperRect = wrapperEl.getBoundingClientRect();
const ddLineEl = refDragLineElem.value;
const mdLineEl = refModalDragLineElem.value;
const currDLineEl = showMorePopup ? mdLineEl : ddLineEl;
if (currDLineEl) {
const dragRect = dragEl.getBoundingClientRect();
currDLineEl.style.display = 'block';
currDLineEl.style.top = `${Math.max(1, dragRect.y - wrapperRect.y)}px`;
currDLineEl.style.left = `${Math.max(1, dragRect.x - wrapperRect.x)}px`;
currDLineEl.style.height = `${dragRect.height}px`;
currDLineEl.style.width = `${dragRect.width - 1}px`;
currDLineEl.setAttribute('drag-pos', dragPos);
}
};
const hideDropTip = () => {
const ddLineEl = refDragLineElem.value;
const mdLineEl = refModalDragLineElem.value;
if (ddLineEl) {
ddLineEl.style.display = '';
}
if (mdLineEl) {
mdLineEl.style.display = '';
}
};
// 拖拽
const handleDragSortDragstartEvent = (evnt) => {
evnt.stopPropagation();
if (evnt.dataTransfer) {
evnt.dataTransfer.setDragImage(getTpImg(), 0, 0);
}
const dragEl = evnt.currentTarget;
const parentEl = dragEl.parentElement;
const dragIndex = XEUtils.findIndexOf(Array.from(parentEl.children), item => dragEl === item);
reactData.isDragMove = true;
reactData.dragIndex = dragIndex;
setTimeout(() => {
reactData.isDragMove = false;
}, 500);
};
const handleDragSortDragoverEvent = (evnt) => {
evnt.stopPropagation();
evnt.preventDefault();
const { dragIndex } = reactData;
if (dragIndex === -1) {
return;
}
const isImage = computeIsImage.value;
const dragEl = evnt.currentTarget;
const parentEl = dragEl.parentElement;
const currIndex = XEUtils.findIndexOf(Array.from(parentEl.children), item => dragEl === item);
let dragPos = '';
if (isImage) {
const offsetX = evnt.clientX - dragEl.getBoundingClientRect().x;
dragPos = offsetX < dragEl.clientWidth / 2 ? 'left' : 'right';
}
else {
const offsetY = evnt.clientY - dragEl.getBoundingClientRect().y;
dragPos = offsetY < dragEl.clientHeight / 2 ? 'top' : 'bottom';
}
if (dragIndex === currIndex) {
showDropTip(evnt, dragEl, dragPos);
return;
}
showDropTip(evnt, dragEl, dragPos);
internalData.prevDragIndex = currIndex;
internalData.prevDragPos = dragPos;
};
const handleDragSortDragendEvent = (evnt) => {
const { fileList, dragIndex } = reactData;
const { prevDragIndex, prevDragPos } = internalData;
const oldIndex = dragIndex;
const targetIndex = prevDragIndex;
const dragOffsetIndex = prevDragPos === 'bottom' || prevDragPos === 'right' ? 1 : 0;
const oldItem = fileList[oldIndex];
const newItem = fileList[targetIndex];
if (oldItem && newItem) {
fileList.splice(oldIndex, 1);
const ptfIndex = XEUtils.findIndexOf(fileList, item => newItem === item);
const nIndex = ptfIndex + dragOffsetIndex;
fileList.splice(nIndex, 0, oldItem);
dispatchEvent('sort-dragend', {
oldItem: oldItem,
newItem: newItem,
dragPos: prevDragPos,
offsetIndex: dragOffsetIndex,
_index: {
newIndex: nIndex,
oldIndex: oldIndex
}
}, evnt);
}
hideDropTip();
reactData.dragIndex = -1;
};
const handleItemMousedownEvent = (evnt) => {
if ($xeTable) {
evnt.stopPropagation();
}
reactData.isActivated = true;
};
const handleGlobalPasteEvent = (evnt) => {
const { pasteToUpload } = props;
const { isActivated } = reactData;
if (!isActivated || !pasteToUpload) {
return;
}
const clipboardData = evnt.clipboardData || evnt.originalEvent.clipboardData;
if (!clipboardData) {
return;
}
const { items } = clipboardData;
if (!items) {
return;
}
const files = handleTransferFiles(items);
if (files.length) {
evnt.preventDefault();
uploadTransferFileEvent(evnt, files);
}
};
const handleGlobalMousedownEvent = (evnt) => {
const el = refElem.value;
const popupEl = refPopupElem.value;
let isActivated = getEventTargetNode(evnt, el).flag;
if (!isActivated && popupEl) {
const parentEl = popupEl.parentElement || popupEl;
const modalEl = parentEl ? parentEl.parentElement : parentEl;
isActivated = getEventTargetNode(evnt, modalEl).flag;
}
reactData.isActivated = isActivated;
};
const handleGlobalBlurEvent = () => {
reactData.isActivated = false;
};
const uploadMethods = {
dispatchEvent,
choose() {
return handleChoose(null);
}
};
const uploadPrivateMethods = {};
Object.assign($xeUpload, uploadMethods, uploadPrivateMethods);
const renderFileItemList = (currList, isMoreView) => {
const { showRemoveButton, showDownloadButton, showProgress, progressText, showPreview, showErrorStatus, dragSort } = props;
const { fileCacheMaps } = reactData;
const isDisabled = computeIsDisabled.value;
const formReadonly = computeFormReadonly.value;
const nameProp = computeNameProp.value;
const typeProp = computeTypeProp.value;
const cornerSlot = slots.corner;
const ons = {};
if (dragSort && currList.length > 1) {
ons.onDragstart = handleDragSortDragstartEvent;
ons.onDragover = handleDragSortDragoverEvent;
ons.onDragend = handleDragSortDragendEvent;
}
return currList.map((item, index) => {
const fileKey = getFieldKey(item);
const cacheItem = fileCacheMaps[fileKey];
const isLoading = cacheItem && cacheItem.loading;
const isError = cacheItem && cacheItem.status === 'error';
return h('div', Object.assign({ key: dragSort ? fileKey : index, class: ['vxe-upload--file-item', {
'is--preview': showPreview,
'is--loading': isLoading,
'is--error': isError
}], fileid: fileKey, draggable: dragSort ? true : null }, ons), [
h('div', {
class: 'vxe-upload--file-item-icon'
}, [
h('i', {
class: getIcon()[`UPLOAD_FILE_TYPE_${`${item[typeProp]}`.toLocaleUpperCase()}`] || getIcon().UPLOAD_FILE_TYPE_DEFAULT
})
]),
h('div', {
class: 'vxe-upload--file-item-name',
onClick(evnt) {
if (!isLoading && !isError) {
handlePreviewFileEvent(evnt, item);
}
}
}, `${item[nameProp] || ''}`),
isLoading
? h('div', {
class: 'vxe-upload--file-item-loading-icon'
}, [
h('i', {
class: getIcon().UPLOAD_LOADING
})
])
: createCommentVNode(),
showProgress && isLoading && cacheItem
? h('div', {
class: 'vxe-upload--file-item-loading-text'
}, progressText ? XEUtils.toFormatString(`${XEUtils.isFunction(progressText) ? progressText({}) : progressText}`, { percent: cacheItem.percent }) : getI18n('vxe.upload.uploadProgress', [cacheItem.percent]))
: createCommentVNode(),
showErrorStatus && isError
? h('div', {
class: 'vxe-upload--image-item-error'
}, [
h(VxeButtonComponent, {
icon: getIcon().UPLOAD_IMAGE_RE_UPLOAD,
mode: 'text',
status: 'primary',
content: getI18n('vxe.upload.reUpload'),
onClick() {
handleReUpload(item);
}
})
])
: createCommentVNode(),
h('div', {
class: 'vxe-upload--file-item-btn-wrapper'
}, [
cornerSlot
? h('div', {
class: 'vxe-upload--file-item-corner'
}, getSlotVNs(cornerSlot({ option: item, isMoreView, readonly: formReadonly })))
: createCommentVNode(),
showDownloadButton && !isLoading
? h('div', {
class: 'vxe-upload--file-item-download-btn',
onClick(evnt) {
downloadFileEvent(evnt, item);
}
}, [
h('i', {
class: getIcon().UPLOAD_FILE_DOWNLOAD
}