明道云视图插件开发-批量上传附件
iamdu2025-07-01 11:34:57明道云 浏览: 934
<template>
<!--
* 名称: [明道云视图开发工具]
* 作者: [DU]
* 联系方式: [iiamdu]
* 时间: [2024-05-28]
* 当前版本: [0.0.1]
* 版本提交时间: 2025-05-28 20:32:58
* 描述: 上传附件
*
-->
<div class="upload-container">
<h2>批量上传附件到单条记录</h2>
<input type="file" @change="handleFileChange" multiple />
<div class="selected-files" v-if="selectedFiles.length > 0">
<h3>已选择 {{ selectedFiles.length }} 个附件:</h3>
<ul>
<li v-for="(file, index) in selectedFiles" :key="index">
{{ file.name }} ({{ formatFileSize(file.size) }})
</li>
</ul>
</div>
<button @click="handleUploadAndAddRecord" :disabled="selectedFiles.length === 0">上传附件并创建记录</button>
<div v-if="uploading" class="progress">
正在上传附件... {{ uploadedCount }}/{{ totalCount }}
<div class="progress-bar">
<div class="progress-inner" :style="{width: uploadProgress + '%'}"></div>
</div>
</div>
<div v-if="uploadSuccess" class="success">记录创建成功!已上传 {{ uploadedCount }} 个附件</div>
<div v-if="uploadError" class="error">上传失败:{{ uploadError }}</div>
<div class="upload-results" v-if="uploadResults.length > 0">
<h3>上传结果:</h3>
<ul>
<li v-for="(result, index) in uploadResults" :key="index" :class="result.success ? 'success' : 'error'">
{{ result.fileName }}: {{ result.success ? '成功' : '失败 - ' + result.error }}
</li>
</ul>
</div>
</div>
</template>
<script setup>
import { ref, reactive, onMounted, onUnmounted, computed, nextTick } from "vue";
import { config, env, api, utils, md_emitter } from "mdye";
import axios from 'axios';
const { appId, worksheetId, viewId } = config;
// 批量上传相关状态
const selectedFiles = ref([]); // 选择的文件列表
const uploadResults = ref([]); // 上传结果记录
const uploadedCount = ref(0); // 已上传文件数
const totalCount = ref(0); // 总文件数
const uploadProgress = ref(0); // 上传进度百分比
const currentFileUrl = ref(''); // 当前文件的URL,全局变量以便在不同函数间共享
// 文件大小格式化工具函数
const formatFileSize = (bytes) => {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
};
const file = ref(null);
const files = ref(null);
const keys = ref(null);
const filePath = ref(null);
const Url = ref(null);
const fileName = ref(null);
const fileSize = ref(null);
const uploading = ref(false);
const lastfilePath = ref(null);
const uploadSuccess = ref(false);
const uploadError = ref('');
const originalFileName = ref('');//原始文件名
const fileType = ref('');
const bucketnum = ref(4); //4为图片,3为文档视频等
const preUrl = ref('https://p1.mingdaoyun.cn/');
// 获取上传凭证
const getUploadToken = async () => {
// 确保扩展名格式正确,避免重复的点
let fileExtension = '';
if (fileType.value) {
fileExtension = fileType.value.startsWith('.') ? fileType.value : `.${fileType.value}`;//扩展名
}
// 生成一个唯一的文件名,避免只有扩展名的情况
const uniqueFileName = originalFileName.value || ('file_' + Date.now() + '_' + Math.floor(Math.random() * 1000));
console.log('使用的文件名:', uniqueFileName);
const response = await axios.post('https://www.mingdao.com/api/Qiniu/GetUploadToken', {
files: [
{
bucket: bucketnum.value, // 3 表示文档、视频等,4 图片
ext: fileExtension, // 确保格式正确的扩展名
}
],
type: 0,//固定
projectId:config.projectId, // 替换为实际的项目 ID
appId, // 替换为实际的应用 ID
worksheetId // 替换为实际的工作表 ID
},
{
headers: {
'authorization': 'md_pss_id 0ca03402f07204706606501004400e0450bc0200af084023', // 替换为实际的授权 Token 用官方页面上传附件 F12下请求头复制
'content-type': 'application/json'
}
});
const responseData = response.data.data[0];
console.log('上传凭证响应:', responseData);
// 检查key是否包含完整的文件名
let modifiedKey = responseData.key;
files.value = responseData.serverName + modifiedKey;
keys.value = modifiedKey;
fileName.value = responseData.key.split('/')[responseData.key.split('/').length - 1];
Url.value = responseData.url;
return response.data.data[0].uptoken;
};
// 上传文件
const uploadFile = async (file, uptoken) => {
const formData = new FormData();
console.log('上传文件对象:', file,uptoken,keys.value);
formData.append('token', uptoken);
formData.append('file', file); // 使用实际的文件对象
// formData.append('name', 'link_record.jpeg');
formData.append('key', keys.value);
// formData.append('chunk', 0);
// formData.append('chunks', 1);
//formData.append('x:serverName', preUrl.value);
// 根据文档示例添加filePath参数,使用本地时间而非UTC时间
const today = new Date();
const year = today.getFullYear();
const month = String(today.getMonth() + 1).padStart(2, '0');
const day = String(today.getDate()).padStart(2, '0');
const localDateStr = `${year}${month}${day}`;
formData.append('x:filePath', filePath.value || `${config.projectId}/${appId}/${worksheetId}/${localDateStr}/`);
formData.append('x:fileName', fileName.value);
//formData.append('x:originalFileName', 'link_record');
// formData.append('x:fileExt', fileType.value);
//formData.append('x:fileExt', '.xlsx');
const response = await axios.post('https://upload.qiniup.com/', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
});
console.log('上传成功,响应数据:', response.data);
// 保存上传结果数据
//lastKey.value = response.data.key;
//lastfileName.value = response.data.fileName;
lastfilePath.value = response.data.filePath;
//lastfsize.value = parseInt(response.data.fsize); // 确保是数字类型
// 简化URL构建逻辑,直接使用preUrl + key
console.log('构建URL的原始数据:', response.data);
console.log('当前preUrl值:', preUrl.value);
console.log('当前key值:', response.data.key);
fileName.value = response.data.fileName;
// 直接使用preUrl + key构建URL,存储在全局变量中
currentFileUrl.value = preUrl.value + response.data.key;
console.log('简化构建的URL:', currentFileUrl.value);
// 返回全局变量中的URL
return currentFileUrl.value; // 返回完整的文件 URL
};
// 准备单个附件的数据对象
const prepareAttachmentData = (fileId, fileSize, serverName, filePath, fileName, fileExt, originalFileName, key, url) => {
// 调试输出,帮助检查参数
console.log('准备附件数据:', {
fileId, fileSize, serverName, filePath, fileName, fileExt, originalFileName, key, url
});
// 如果fileName为空,从key中提取
let finalFileName = fileName;
return {
"fileID": fileId,
"fileSize": parseInt(fileSize) || 0,
"serverName": serverName,
"filePath": filePath,
"fileName": finalFileName,
//"fileExt": fileExt, // 应该是 .png 格式 不要这行 避免地址多个后缀 因为文件名已包含
"originalFileName": originalFileName || finalFileName,
"key": key,
"url": url,
"oldOriginalFileName": originalFileName || finalFileName,
"index": 0,
"isEdit": false
};
};
// 收集所有上传的附件数据
const allAttachments = ref([]);
// 添加附件到集合
const addAttachmentToCollection = (fileId, fileSize, serverName, filePath, fileName, fileExt, originalFileName, key, url) => {
const attachment = prepareAttachmentData(fileId, fileSize, serverName, filePath, fileName, fileExt, originalFileName, key, url);
allAttachments.value.push(attachment);
console.log(`附件已添加到集合,当前共有 ${allAttachments.value.length} 个附件`);
};
// 新增记录并赋值附件字段
const addRecordWithAttachment = async () => {
console.log('准备添加记录,附件数量:', allAttachments.value.length);
if (allAttachments.value.length === 0) {
console.error('没有可用的附件数据');
return;
}
// 构建附件字段的值
const attachmentValue = {
"attachmentData": [],
"attachments": allAttachments.value,
"knowledgeAtts": []
};
// 将对象转换为JSON字符串
const attachmentValueStr = JSON.stringify(attachmentValue);
const response = await api.addWorksheetRow({
appId, // 替换为实际的应用 ID
worksheetId, // 替换为实际的工作表 ID
receiveControls: [
{
"controlId": "6843b20c82453d0d030cf68f",
"type": 14,
"value": attachmentValueStr,
"controlName": "附件",
"dot": 0
}
]
});
return response;
};
// 文件选择事件 - 支持多文件
const handleFileChange = (event) => {
const files = event.target.files;
if (!files || files.length === 0) return;
// 清空之前的选择
selectedFiles.value = [];
uploadResults.value = [];
uploadError.value = '';
// 将FileList转换为数组并保存
for (let i = 0; i < files.length; i++) {
selectedFiles.value.push(files[i]);
}
console.log(`已选择 ${selectedFiles.value.length} 个文件`);
// 如果有文件,设置第一个文件的信息(用于兼容旧代码)
if (selectedFiles.value.length > 0) {
const firstFile = selectedFiles.value[0];
file.value = firstFile;
// 提取文件信息 - 正确处理文件名和扩展名
const fileName = firstFile.name;
const lastDotIndex = fileName.lastIndexOf('.');
if (lastDotIndex > 0) {
// 有扩展名的情况
originalFileName.value = fileName.substring(0, lastDotIndex); // 不包含扩展名的文件名
fileType.value = fileName.substring(lastDotIndex + 1); // 不带点的扩展名
} else {
// 没有扩展名的情况
originalFileName.value = fileName;
fileType.value = '';
}
fileSize.value = firstFile.size; // 文件大小
// 根据文件类型设置存储桶和URL前缀
if (!firstFile.type.startsWith('image/')) {
bucketnum.value = 3; // 文档视频等
preUrl.value = 'https://doc.mingdao.com/';
} else {
bucketnum.value = 4; // 图片
preUrl.value = 'https://p1.mingdaoyun.cn/';
}
}
};
// 上传单个文件并收集附件数据
const uploadSingleFile = async (fileObj) => {
try {
// 设置当前处理的文件信息
file.value = fileObj;
// 正确提取文件名和扩展名
const fileNameStr = fileObj.name; // 使用不同的变量名避免与全局变量冲突
const lastDotIndex = fileNameStr.lastIndexOf('.');
if (lastDotIndex > 0) {
// 有扩展名的情况
originalFileName.value = fileNameStr.substring(0, lastDotIndex); // 不包含扩展名的文件名
fileType.value = fileNameStr.substring(lastDotIndex + 1); // 不带点的扩展名
} else {
// 没有扩展名的情况
originalFileName.value = fileNameStr;
fileType.value = '';
}
fileSize.value = fileObj.size;
// 根据文件类型设置存储桶和URL前缀
if (!fileObj.type.startsWith('image/')) {
bucketnum.value = 3; // 文档视频等
preUrl.value = 'https://doc.mingdao.com/';
} else {
bucketnum.value = 4; // 图片
preUrl.value = 'https://p1.mingdaoyun.cn/';
}
// 获取上传凭证
console.log(`正在获取上传凭证: ${fileObj.name}...`);
const uptoken = await getUploadToken();
// 上传文件
console.log(`正在上传文件: ${fileObj.name}...`);
const fileUrl = await uploadFile(fileObj, uptoken);
// 生成唯一的附件ID
const fileId = "o_1ir3mg6m1l741oh7" + Date.now() + "_" + Math.floor(Math.random() * 1000);
// 使用简化的URL构建逻辑
console.log('上传后获取的key:', keys.value);
// 检查文件名是否正确设置
console.log('当前fileName值:', fileName.value);
// 如果fileName为空,从key中提取
if (!fileName.value && keys.value) {
const keyParts = keys.value.split('/');
if (keyParts.length > 0) {
const extractedName = keyParts[keyParts.length - 1];
// 如果提取的是纯扩展名(如.jpeg),则生成一个随机文件名
if (extractedName.startsWith('.')) {
fileName.value = 'file_' + Date.now() + '_' + Math.floor(Math.random() * 1000) + extractedName;
console.log('生成随机文件名:', fileName.value);
} else {
fileName.value = extractedName;
}
console.log('从key中提取并设置fileName:', fileName.value);
}
}
// 如果还是没有文件名,使用原始文件名
if (!fileName.value && originalFileName.value) {
fileName.value = originalFileName.value;
if (fileType.value && !fileName.value.endsWith('.' + fileType.value)) {
fileName.value += '.' + fileType.value;
}
console.log('使用原始文件名作为fileName:', fileName.value);
}
// 直接使用preUrl + key构建URL,使用全局变量
currentFileUrl.value = preUrl.value + keys.value;
console.log('最终使用的URL:', currentFileUrl.value);
// 检查并修复文件路径问题
if (!lastfilePath.value && keys.value) {
// 如果没有filePath但有key,尝试从key中提取路径
const keyParts = keys.value.split('/');
if (keyParts.length > 1) {
// 移除最后一个部分(文件名),剩余部分就是路径
const pathParts = keyParts.slice(0, keyParts.length - 1);
lastfilePath.value = pathParts.join('/') + '/';
console.log('从key中提取的文件路径:', lastfilePath.value);
}
}
// 确保正确添加扩展名
const fileExt = fileType.value ? `.${fileType.value}` : '';
// 添加附件数据到集合
addAttachmentToCollection(
fileId,
fileSize.value,
preUrl.value,
lastfilePath.value,
fileName.value,
fileExt, // 正确添加扩展名,格式为 .png 或 .jpeg 等
originalFileName.value,
keys.value,
currentFileUrl.value // 使用全局变量中的URL
);
// 添加到成功结果
uploadResults.value.push({
fileName: fileObj.name,
success: true
});
return true;
} catch (error) {
console.error(`上传文件失败: ${fileObj.name}`, error);
// 添加到失败结果
uploadResults.value.push({
fileName: fileObj.name,
success: false,
error: error.message || '未知错误'
});
return false;
}
};
// 批量上传附件并创建一条记录
const handleUploadAndAddRecord = async () => {
if (selectedFiles.value.length === 0) {
uploadError.value = '请先选择文件';
return;
}
uploading.value = true;
uploadSuccess.value = false;
uploadError.value = '';
uploadedCount.value = 0;
totalCount.value = selectedFiles.value.length;
uploadProgress.value = 0;
uploadResults.value = [];
// 清空之前的附件集合
allAttachments.value = [];
try {
// 逐个上传文件,收集附件数据
for (let i = 0; i < selectedFiles.value.length; i++) {
const currentFile = selectedFiles.value[i];
const success = await uploadSingleFile(currentFile);
// 更新进度
uploadedCount.value++;
uploadProgress.value = Math.floor((uploadedCount.value / totalCount.value) * 100);
}
// 检查是否有成功上传的文件
const successCount = uploadResults.value.filter(r => r.success).length;
if (successCount > 0) {
// 创建一条包含所有附件的记录
console.log('创建包含所有附件的记录...');
const recordResult = await addRecordWithAttachment();
console.log('记录创建结果:', recordResult);
uploadSuccess.value = true;
if (successCount < totalCount.value) {
uploadError.value = `部分文件上传失败 (${successCount}/${totalCount.value} 成功),但记录已成功创建`;
}
} else {
uploadError.value = '所有文件上传失败,未创建记录';
}
} catch (error) {
console.error('批量上传或创建记录过程中发生错误:', error);
uploadError.value = error.message || '批量上传或创建记录过程中发生未知错误';
} finally {
uploading.value = false;
}
};
</script>
<style>
.app-container {
padding: 20px;
max-width: 800px;
margin: 0 auto;
}
.upload-container {
background-color: #f9f9f9;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: bold;
}
.selected-record {
margin-top: 10px;
padding: 8px;
background-color: #ecf5ff;
border-radius: 4px;
border-left: 3px solid #409eff;
}
.upload-results {
margin-top: 20px;
padding: 15px;
background-color: #f9f9f9;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.upload-results ul {
padding-left: 20px;
}
.upload-results li {
margin-bottom: 5px;
}
.upload-results .success {
color: #67c23a;
}
.upload-results .error {
color: #f56c6c;
}
.progress {
margin: 15px 0;
}
.progress-bar {
height: 10px;
background-color: #e9e9e9;
border-radius: 5px;
margin-top: 5px;
overflow: hidden;
}
.progress-inner {
height: 100%;
background-color: #409eff;
transition: width 0.3s;
}
.selected-files {
margin: 15px 0;
padding: 10px;
background-color: #f0f9eb;
border-radius: 4px;
border-left: 3px solid #67c23a;
}
.selected-files ul {
padding-left: 20px;
margin: 10px 0 0;
}
.selected-files li {
margin-bottom: 5px;
}
.success {
color: #67c23a;
font-weight: bold;
}
</style>
联系站长
友情链接
其他入口
QQ与微信加好友
粤ICP备17018681号 站点地图 www.iamdu.com 版权所有 服务商提供:阿里云 Designed by :DU
本站部分资源内容来源于网络,若侵犯您的权益,请联系删除!