Files
APP/uni_modules/xe-upload/components/xe-upload/xe-upload.vue
2025-11-07 12:16:10 +08:00

286 lines
7.5 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!-- eslint-disable -->
<template>
<view>
<!-- #ifdef APP-PLUS -->
<view class="xe-upload" v-html="renderInput" :props="mergeProps" :change:props="XeUpload.renderProps"></view>
<!-- #endif -->
</view>
</template>
<script>
import {
chooseMedia,
chooseFile,
chooseMessageFile,
uploadFile,
} from '../../tools/apis';
import {
deepMerge,
awaitWrap,
base64ToPath,
isArray,
} from '../../tools/tools';
export default {
name: 'XeUpload',
props: {
options: {
default: () => ({}),
type: Object,
},
},
data() {
return {
id: 0, // APP上传框ID
renderInput: '', // APP上传框
};
},
computed: {
mergeOptions({ options = {} }) {
const tmpOptions = {
name: 'file',
};
return deepMerge(tmpOptions, options);
},
mergeProps({ id, renderInput, mergeOptions }) {
return {
id,
renderInput,
upload: mergeOptions,
};
},
},
methods: {
// 上传事件
async upload(type, config = {}) {
let tmpResult = [];
if (['image', 'video'].includes(type)) {
const [err, res] = await chooseMedia(type, config);
if (err) return this.handleError(err);
tmpResult = res?.tempFiles || [];
}
// H5 || APP-PLUS || MP-WEIXIN
if (['file'].includes(type)) {
let tmpFiles = {};
let tmpErr = null;
// #ifdef H5
[tmpErr, tmpFiles] = await chooseFile(config);
// #endif
// #ifdef MP-WEIXIN
[tmpErr, tmpFiles] = await chooseMessageFile(config);
// #endif
// #ifdef APP-PLUS
this.id = Math.floor(Math.random() * 100000000 + 1);
this.initInput(config.extension);
// #endif
if (tmpErr) return this.handleError(tmpErr);
tmpResult = tmpFiles?.tempFiles || [];
}
this.handleUpload(tmpResult);
},
// 初始化上传框
initInput(extension) {
const { id } = this;
let accept = extension;
if (isArray(extension)) {
accept = extension.join(',');
}
this.renderInput = `<input type="file" id="xe-upload-${id}" name="xe-upload" ${accept ? 'accept="' + accept + '"' : ''} />`;
},
// 文件上传没有传入上传url时返回本地链接
async handleUpload(files = []) {
if (files.filter((e) => Boolean(e)).length === 0) return;
const { mergeOptions } = this;
if (!mergeOptions.url) {
return this.handleEmits({
type: 'choose',
data: files,
});
}
const tmpUploads = files.map((e) =>
uploadFile(
{
...mergeOptions,
filePath: e.tempFilePath,
},
e,
)
);
const [err, res] = await awaitWrap(Promise.all(tmpUploads));
if (err) return this.handleError(err);
this.handleEmits({
type: 'success',
data: res,
});
},
// 处理失败事件
handleError(error) {
this.handleEmits({
type: 'warning',
data: error,
});
},
// 处理响应事件
async handleEmits(e) {
// #ifdef APP-PLUS
if (e.type === 'choose') {
// 将base64转为本地链接
for (let i = 0; i < e.data.length; i += 1) {
const item = e.data[i];
if (!item.base64Url) {
continue;
}
const [parseError, parseUrl] = await awaitWrap(base64ToPath(item.base64Url, item.name));
if (!parseError) {
e.data[i].tempFilePath = parseUrl;
} else {
e.data[i].tempFilePath = item.base64Url;
}
delete e.data[i].base64Url;
}
}
// #endif
this.$emit('callback', e);
},
},
};
</script>
<!-- #ifdef APP-PLUS -->
<script module="XeUpload" lang="renderjs">
import {
appUploadFile,
} from '../../tools/apis';
import {
awaitWrap,
fileToBase64,
} from '../../tools/tools';
export default {
data() {
return {
id: 0, // 上传框ID
uploadOptions: {}, // 上传配置
};
},
methods: {
// 处理 XeUpload 传入 renderjs 数据,以及调起上传框
renderProps(info) {
const { id, renderInput, upload } = info;
if (!renderInput) return;
this.id = id;
this.uploadOptions = upload;
this.$nextTick(() => {
const dom = document.getElementById(`xe-upload-${id}`);
dom.addEventListener('change', () => {
this.handleUpload();
});
dom?.click?.();
});
},
// 处理文件上传(没有传入url时返回本地链接)
async handleUpload() {
const {
url,
name,
header = {},
formData = {},
} = this.uploadOptions || {};
const dom = document.getElementById(`xe-upload-${this.id}`);
if (!dom.files[0]) return;
const tmpFileList = Array.from(dom.files);
const tmpUploads = [];
for (let i = 0; i < tmpFileList.length; i += 1) {
const e = tmpFileList[i];
let tmpType = 'file';
if (e.type.includes('image')) {
tmpType = 'image';
}
if (e.type.includes('video')) {
tmpType = 'video';
}
const tmpExts = {
size: e.size,
name: e.name,
type: e.type,
fileType: tmpType,
tempFilePath: '',
base64Url: '',
};
// 没有传入上传url时直接返回本地链接
if (!url) {
const [parseError, parseUrl] = await awaitWrap(fileToBase64(dom.files[i]));
if (!parseError) {
tmpExts.base64Url = parseUrl;
}
tmpUploads.push(tmpExts);
continue;
};
const tmpData = new FormData();
tmpData.append(name, dom.files[i], e.name);
for (let key in formData) {
tmpData.append(key, formData[key]);
}
// 上传进度
const onprogress = (ev) => {
if(ev.lengthComputable) {
var result = (ev.loaded / ev.total) * 100;
this.handleRenderEmits({
type: 'onprogress',
data: {
progress: Math.floor(result),
current: i + 1,
total: tmpFileList.length,
},
});
};
}
tmpUploads.push(appUploadFile({
url,
header,
formData: tmpData
}, tmpExts, onprogress ));
}
// 没有传入上传url时直接返回本地链接列表
if (!url) {
return this.handleRenderEmits({
type: 'choose',
data: tmpUploads,
});
}
this.handleRenderEmits({
type: 'onprogress',
data: {
progress: 0,
current: 1,
total: tmpFileList.length,
},
});
// 处理上传文件
const [err, res] = await awaitWrap(Promise.all(tmpUploads));
if (err) {
return this.handleRenderEmits({
type: 'warning',
data: err,
});
}
this.handleRenderEmits({
type: 'success',
data: res,
});
},
// 数据传输到XeUpload组件
handleRenderEmits(data) {
this.$ownerInstance.callMethod('handleEmits', data);
},
},
};
</script>
<!-- #endif -->
<style scoped>
.xe-upload {
display: none;
}
</style>