实现文件上传
This commit is contained in:
parent
3c2365660b
commit
fc65bfedcb
|
|
@ -62,10 +62,14 @@
|
||||||
class="file-item"
|
class="file-item"
|
||||||
v-for="(file, index) in formData.files"
|
v-for="(file, index) in formData.files"
|
||||||
:key="index"
|
:key="index"
|
||||||
|
@click="previewFile(file)"
|
||||||
>
|
>
|
||||||
<text class="file-icon">📄</text>
|
<text class="file-icon">{{ getFileIcon(file.name) }}</text>
|
||||||
<text class="file-name">{{ file.name }}</text>
|
<view class="file-info">
|
||||||
<view class="remove-btn" @click="removeFile(index)">✕</view>
|
<text class="file-name">{{ file.name }}</text>
|
||||||
|
<text class="file-size" v-if="file.size > 0">{{ formatFileSize(file.size) }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="remove-btn" @click.stop="removeFile(index)">✕</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
@ -121,7 +125,7 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted } from 'vue';
|
import { ref, computed, onMounted } from 'vue';
|
||||||
import { onLoad } from '@dcloudio/uni-app';
|
import { onLoad } from '@dcloudio/uni-app';
|
||||||
import { chooseAndUploadImages } from '@/utils/qiniu.js';
|
import { chooseAndUploadImages, uploadFileToQiniu, batchUploadFilesToQiniu } from '@/utils/qiniu.js';
|
||||||
import { submitTask } from '@/common/api.js';
|
import { submitTask } from '@/common/api.js';
|
||||||
|
|
||||||
// 表单数据
|
// 表单数据
|
||||||
|
|
@ -273,8 +277,8 @@ const removeImage = (index) => {
|
||||||
formData.value.images.splice(index, 1);
|
formData.value.images.splice(index, 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 选择文件(安卓平台)
|
// 选择文件(支持多平台)
|
||||||
const chooseFiles = () => {
|
const chooseFiles = async () => {
|
||||||
const remainingCount = 5 - formData.value.files.length;
|
const remainingCount = 5 - formData.value.files.length;
|
||||||
if (remainingCount <= 0) {
|
if (remainingCount <= 0) {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
|
|
@ -284,6 +288,72 @@ const chooseFiles = () => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 优先使用 uni.chooseFile(H5和部分平台支持)
|
||||||
|
// #ifdef H5 || MP-WEIXIN || APP-PLUS
|
||||||
|
try {
|
||||||
|
uni.chooseFile({
|
||||||
|
count: remainingCount,
|
||||||
|
extension: ['.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', '.txt', '.zip', '.rar'],
|
||||||
|
success: async (res) => {
|
||||||
|
try {
|
||||||
|
uni.showLoading({
|
||||||
|
title: '上传中...',
|
||||||
|
mask: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// 批量上传文件到七牛云
|
||||||
|
const uploadResults = await batchUploadFilesToQiniu(
|
||||||
|
res.tempFiles.map(file => ({
|
||||||
|
path: file.path,
|
||||||
|
name: file.name
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
// 将上传结果添加到文件列表
|
||||||
|
const newFiles = uploadResults.map(result => ({
|
||||||
|
name: result.name,
|
||||||
|
path: result.url, // 保存七牛云URL
|
||||||
|
size: result.size
|
||||||
|
}));
|
||||||
|
|
||||||
|
formData.value.files = [...formData.value.files, ...newFiles];
|
||||||
|
|
||||||
|
uni.hideLoading();
|
||||||
|
uni.showToast({
|
||||||
|
title: `成功添加${newFiles.length}个文件`,
|
||||||
|
icon: 'success'
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
uni.hideLoading();
|
||||||
|
console.error('上传文件失败:', error);
|
||||||
|
uni.showToast({
|
||||||
|
title: error.message || '上传文件失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.error('选择文件失败:', err);
|
||||||
|
// 如果uni.chooseFile不支持,尝试使用原生方法
|
||||||
|
chooseFilesNative();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
// 如果不支持uni.chooseFile,使用原生方法
|
||||||
|
chooseFilesNative();
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifndef H5 || MP-WEIXIN || APP-PLUS
|
||||||
|
// 其他平台使用原生方法
|
||||||
|
chooseFilesNative();
|
||||||
|
// #endif
|
||||||
|
};
|
||||||
|
|
||||||
|
// 原生文件选择方法(安卓平台)
|
||||||
|
const chooseFilesNative = async () => {
|
||||||
|
const remainingCount = 5 - formData.value.files.length;
|
||||||
|
|
||||||
// 安卓平台使用 plus API 调用原生文件选择器
|
// 安卓平台使用 plus API 调用原生文件选择器
|
||||||
if (typeof plus !== 'undefined') {
|
if (typeof plus !== 'undefined') {
|
||||||
try {
|
try {
|
||||||
|
|
@ -301,7 +371,7 @@ const chooseFiles = () => {
|
||||||
|
|
||||||
// 监听文件选择结果
|
// 监听文件选择结果
|
||||||
const originalOnActivityResult = main.onActivityResult;
|
const originalOnActivityResult = main.onActivityResult;
|
||||||
main.onActivityResult = (requestCode, resultCode, data) => {
|
main.onActivityResult = async (requestCode, resultCode, data) => {
|
||||||
if (requestCode === 1001) {
|
if (requestCode === 1001) {
|
||||||
if (resultCode === -1 && data) { // RESULT_OK = -1
|
if (resultCode === -1 && data) { // RESULT_OK = -1
|
||||||
try {
|
try {
|
||||||
|
|
@ -360,7 +430,38 @@ const chooseFiles = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (files.length > 0) {
|
if (files.length > 0) {
|
||||||
formData.value.files = [...formData.value.files, ...files];
|
// 显示上传中提示
|
||||||
|
uni.showLoading({
|
||||||
|
title: '上传中...',
|
||||||
|
mask: true
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 批量上传文件到七牛云
|
||||||
|
const uploadResults = await batchUploadFilesToQiniu(files);
|
||||||
|
|
||||||
|
// 将上传结果添加到文件列表
|
||||||
|
const newFiles = uploadResults.map(result => ({
|
||||||
|
name: result.name,
|
||||||
|
path: result.url, // 保存七牛云URL
|
||||||
|
size: result.size
|
||||||
|
}));
|
||||||
|
|
||||||
|
formData.value.files = [...formData.value.files, ...newFiles];
|
||||||
|
|
||||||
|
uni.hideLoading();
|
||||||
|
uni.showToast({
|
||||||
|
title: `成功添加${newFiles.length}个文件`,
|
||||||
|
icon: 'success'
|
||||||
|
});
|
||||||
|
} catch (uploadError) {
|
||||||
|
uni.hideLoading();
|
||||||
|
console.error('上传文件失败:', uploadError);
|
||||||
|
uni.showToast({
|
||||||
|
title: uploadError.message || '上传文件失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 恢复原始的 onActivityResult
|
// 恢复原始的 onActivityResult
|
||||||
|
|
@ -368,6 +469,7 @@ const chooseFiles = () => {
|
||||||
main.onActivityResult = originalOnActivityResult;
|
main.onActivityResult = originalOnActivityResult;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
uni.hideLoading();
|
||||||
console.error('处理文件选择结果失败:', error);
|
console.error('处理文件选择结果失败:', error);
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '处理文件失败',
|
title: '处理文件失败',
|
||||||
|
|
@ -402,6 +504,104 @@ const removeFile = (index) => {
|
||||||
formData.value.files.splice(index, 1);
|
formData.value.files.splice(index, 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 获取文件图标
|
||||||
|
const getFileIcon = (fileName) => {
|
||||||
|
if (!fileName) return '📄';
|
||||||
|
const ext = fileName.split('.').pop().toLowerCase();
|
||||||
|
const iconMap = {
|
||||||
|
'pdf': '📕',
|
||||||
|
'doc': '📘',
|
||||||
|
'docx': '📘',
|
||||||
|
'xls': '📗',
|
||||||
|
'xlsx': '📗',
|
||||||
|
'ppt': '📙',
|
||||||
|
'pptx': '📙',
|
||||||
|
'txt': '📄',
|
||||||
|
'zip': '📦',
|
||||||
|
'rar': '📦',
|
||||||
|
'jpg': '🖼️',
|
||||||
|
'jpeg': '🖼️',
|
||||||
|
'png': '🖼️',
|
||||||
|
'gif': '🖼️'
|
||||||
|
};
|
||||||
|
return iconMap[ext] || '📄';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 格式化文件大小
|
||||||
|
const formatFileSize = (bytes) => {
|
||||||
|
if (!bytes || bytes === 0) return '';
|
||||||
|
const k = 1024;
|
||||||
|
const sizes = ['B', 'KB', 'MB', 'GB'];
|
||||||
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||||
|
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
|
||||||
|
};
|
||||||
|
|
||||||
|
// 预览/下载文件
|
||||||
|
const previewFile = (file) => {
|
||||||
|
if (!file.path) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '文件路径不存在',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是图片,使用预览图片功能
|
||||||
|
const imageExts = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
|
||||||
|
const ext = file.name.split('.').pop().toLowerCase();
|
||||||
|
|
||||||
|
if (imageExts.includes(ext)) {
|
||||||
|
uni.previewImage({
|
||||||
|
urls: [file.path],
|
||||||
|
current: file.path
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 其他文件类型,尝试打开或下载
|
||||||
|
// #ifdef H5
|
||||||
|
window.open(file.path, '_blank');
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
plus.runtime.openURL(file.path);
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifndef H5 || APP-PLUS
|
||||||
|
uni.showToast({
|
||||||
|
title: '点击下载文件',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
// 可以调用下载API
|
||||||
|
uni.downloadFile({
|
||||||
|
url: file.path,
|
||||||
|
success: (res) => {
|
||||||
|
if (res.statusCode === 200) {
|
||||||
|
uni.openDocument({
|
||||||
|
filePath: res.tempFilePath,
|
||||||
|
success: () => {
|
||||||
|
console.log('打开文档成功');
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.error('打开文档失败:', err);
|
||||||
|
uni.showToast({
|
||||||
|
title: '无法打开此文件',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.error('下载文件失败:', err);
|
||||||
|
uni.showToast({
|
||||||
|
title: '下载文件失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 格式化时间为中文格式:年月日星期几时分秒
|
// 格式化时间为中文格式:年月日星期几时分秒
|
||||||
const formatTimeToChinese = (date) => {
|
const formatTimeToChinese = (date) => {
|
||||||
const weekdays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
|
const weekdays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
|
||||||
|
|
@ -440,9 +640,10 @@ const handleSubmit = async () => {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 合并所有附件URL(图片和文件)
|
// 合并所有附件URL(图片和文件)
|
||||||
|
// 图片和文件都已经上传到七牛云,直接使用URL
|
||||||
const allAttaches = [
|
const allAttaches = [
|
||||||
...formData.value.images, // 图片已经是七牛云URL
|
...formData.value.images, // 图片已经是七牛云URL
|
||||||
...formData.value.files.map(file => file.path) // 文件路径(如果是URL则直接使用,如果是本地路径可能需要先上传)
|
...formData.value.files.map(file => file.path) // 文件已经是七牛云URL
|
||||||
].filter(url => url && url.trim() !== ''); // 过滤空值
|
].filter(url => url && url.trim() !== ''); // 过滤空值
|
||||||
|
|
||||||
// 将附件数组转换为逗号分隔的字符串
|
// 将附件数组转换为逗号分隔的字符串
|
||||||
|
|
@ -656,9 +857,8 @@ const handleSubmit = async () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
.remove-btn {
|
.remove-btn {
|
||||||
position: absolute;
|
position: relative;
|
||||||
top: 4px;
|
|
||||||
right: 4px;
|
|
||||||
width: 24px;
|
width: 24px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
background-color: rgba(0, 0, 0, 0.6);
|
background-color: rgba(0, 0, 0, 0.6);
|
||||||
|
|
@ -685,20 +885,38 @@ const handleSubmit = async () => {
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-icon {
|
.file-icon {
|
||||||
font-size: 20px;
|
font-size: 24px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-name {
|
.file-info {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-name {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #333;
|
color: #333;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-size {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 进度选择弹窗 */
|
/* 进度选择弹窗 */
|
||||||
|
|
|
||||||
|
|
@ -154,9 +154,13 @@
|
||||||
class="file-attachment-item"
|
class="file-attachment-item"
|
||||||
v-for="(file, fileIndex) in record.fileAttachments"
|
v-for="(file, fileIndex) in record.fileAttachments"
|
||||||
:key="fileIndex"
|
:key="fileIndex"
|
||||||
|
@click="previewRecordFile(file)"
|
||||||
>
|
>
|
||||||
<text class="file-icon">📄</text>
|
<text class="file-icon">{{ getFileIcon(file.name) }}</text>
|
||||||
<text class="file-name">{{ file.name }}</text>
|
<view class="file-info">
|
||||||
|
<text class="file-name">{{ file.name }}</text>
|
||||||
|
<text class="file-size" v-if="file.size > 0">{{ formatFileSize(file.size) }}</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="delay-btn-wrapper" v-if="record.showDelayBtn">
|
<view class="delay-btn-wrapper" v-if="record.showDelayBtn">
|
||||||
|
|
@ -396,7 +400,7 @@ const transformSubmitRecords = (submitList) => {
|
||||||
userName: item.userName || '',
|
userName: item.userName || '',
|
||||||
userAvatar: item.userAvatar || '',
|
userAvatar: item.userAvatar || '',
|
||||||
time: formatTimeToChinese(item.createTime) || '',
|
time: formatTimeToChinese(item.createTime) || '',
|
||||||
content: item.remark || item.description || item.taskDescription || '', // 如果没有提交内容,可能显示任务描述
|
content: item.remark || '', // 如果没有提交内容,可能显示任务描述
|
||||||
progress: null, // API 返回的数据中没有进度字段
|
progress: null, // API 返回的数据中没有进度字段
|
||||||
imageAttachments: imageAttachments,
|
imageAttachments: imageAttachments,
|
||||||
fileAttachments: fileAttachments,
|
fileAttachments: fileAttachments,
|
||||||
|
|
@ -492,6 +496,105 @@ const previewRecordImages = (imageUrls, index) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 获取文件图标
|
||||||
|
const getFileIcon = (fileName) => {
|
||||||
|
if (!fileName) return '📄';
|
||||||
|
const ext = fileName.split('.').pop().toLowerCase();
|
||||||
|
const iconMap = {
|
||||||
|
'pdf': '📕',
|
||||||
|
'doc': '📘',
|
||||||
|
'docx': '📘',
|
||||||
|
'xls': '📗',
|
||||||
|
'xlsx': '📗',
|
||||||
|
'ppt': '📙',
|
||||||
|
'pptx': '📙',
|
||||||
|
'txt': '📄',
|
||||||
|
'zip': '📦',
|
||||||
|
'rar': '📦',
|
||||||
|
'jpg': '🖼️',
|
||||||
|
'jpeg': '🖼️',
|
||||||
|
'png': '🖼️',
|
||||||
|
'gif': '🖼️'
|
||||||
|
};
|
||||||
|
return iconMap[ext] || '📄';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 格式化文件大小
|
||||||
|
const formatFileSize = (bytes) => {
|
||||||
|
if (!bytes || bytes === 0) return '';
|
||||||
|
const k = 1024;
|
||||||
|
const sizes = ['B', 'KB', 'MB', 'GB'];
|
||||||
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||||
|
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
|
||||||
|
};
|
||||||
|
|
||||||
|
// 预览/下载提交记录中的文件
|
||||||
|
const previewRecordFile = (file) => {
|
||||||
|
if (!file.path) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '文件路径不存在',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是图片,使用预览图片功能
|
||||||
|
const imageExts = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
|
||||||
|
const ext = file.name ? file.name.split('.').pop().toLowerCase() : '';
|
||||||
|
|
||||||
|
if (imageExts.includes(ext)) {
|
||||||
|
uni.previewImage({
|
||||||
|
urls: [file.path],
|
||||||
|
current: file.path
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 其他文件类型,尝试打开或下载
|
||||||
|
// #ifdef H5
|
||||||
|
window.open(file.path, '_blank');
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
plus.runtime.openURL(file.path);
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifndef H5 || APP-PLUS
|
||||||
|
uni.showToast({
|
||||||
|
title: '正在下载文件...',
|
||||||
|
icon: 'loading',
|
||||||
|
duration: 2000
|
||||||
|
});
|
||||||
|
// 下载并打开文档
|
||||||
|
uni.downloadFile({
|
||||||
|
url: file.path,
|
||||||
|
success: (res) => {
|
||||||
|
if (res.statusCode === 200) {
|
||||||
|
uni.openDocument({
|
||||||
|
filePath: res.tempFilePath,
|
||||||
|
success: () => {
|
||||||
|
console.log('打开文档成功');
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.error('打开文档失败:', err);
|
||||||
|
uni.showToast({
|
||||||
|
title: '无法打开此文件',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.error('下载文件失败:', err);
|
||||||
|
uni.showToast({
|
||||||
|
title: '下载文件失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 获取标签类型(用于uv-tags组件)
|
// 获取标签类型(用于uv-tags组件)
|
||||||
const getTagType = (tagText) => {
|
const getTagType = (tagText) => {
|
||||||
const status = getStatusFromTagText(tagText);
|
const status = getStatusFromTagText(tagText);
|
||||||
|
|
@ -1113,23 +1216,42 @@ onShow(() => {
|
||||||
.file-attachment-item {
|
.file-attachment-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 6px;
|
gap: 12px;
|
||||||
padding: 8px 12px;
|
padding: 12px;
|
||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
border-radius: 4px;
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background-color: #e0e0e0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-icon {
|
.file-icon {
|
||||||
font-size: 16px;
|
font-size: 24px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-info {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-name {
|
.file-name {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #333;
|
color: #333;
|
||||||
flex: 1;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-size {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-record {
|
.no-record {
|
||||||
|
|
|
||||||
115
utils/qiniu.js
115
utils/qiniu.js
|
|
@ -184,6 +184,121 @@ export const chooseAndUploadImages = (options = {}) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传单个文件到七牛云(支持图片和文档)
|
||||||
|
* @param {string} filePath - 文件临时路径或URI
|
||||||
|
* @param {string} fileName - 文件名(可选,用于文档文件)
|
||||||
|
* @param {object} [options] - 配置选项
|
||||||
|
* @param {number} [options.maxSize=50] - 最大文件大小(MB),文档文件默认50MB
|
||||||
|
* @param {string} [options.prefix='uploads/'] - 文件前缀路径
|
||||||
|
* @param {boolean} [options.showToast=true] - 是否显示提示
|
||||||
|
* @param {string} [options.domain='https://api.ccttiot.com'] - 七牛云域名
|
||||||
|
* @returns {Promise<object>} 返回 { url: string, name: string, size: number }
|
||||||
|
*/
|
||||||
|
export const uploadFileToQiniu = async (filePath, fileName = '', options = {}) => {
|
||||||
|
const {
|
||||||
|
maxSize = 50,
|
||||||
|
prefix = 'uploads/',
|
||||||
|
showToast = true,
|
||||||
|
domain = 'https://api.ccttiot.com',
|
||||||
|
} = options
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. 如果是URI格式(content://),需要先转换为临时路径
|
||||||
|
let tempFilePath = filePath
|
||||||
|
if (filePath.startsWith('content://')) {
|
||||||
|
// 在uni-app中,content:// URI需要特殊处理
|
||||||
|
// 对于文档文件,uni.uploadFile可以直接使用URI
|
||||||
|
tempFilePath = filePath
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 获取文件信息(如果不是URI格式)
|
||||||
|
let fileInfo = { size: 0 }
|
||||||
|
if (!filePath.startsWith('content://')) {
|
||||||
|
try {
|
||||||
|
fileInfo = await getFileInfo(tempFilePath)
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('获取文件信息失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const sizeInMB = fileInfo.size / (1024 * 1024)
|
||||||
|
if (sizeInMB > maxSize) {
|
||||||
|
throw new Error(`文件大小不能超过 ${maxSize}MB`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 获取上传凭证
|
||||||
|
const token = await getQiniuToken()
|
||||||
|
if (!token) {
|
||||||
|
throw new Error('获取上传凭证失败')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 生成唯一文件名
|
||||||
|
let fileExt = 'file'
|
||||||
|
if (fileName) {
|
||||||
|
const parts = fileName.split('.')
|
||||||
|
if (parts.length > 1) {
|
||||||
|
fileExt = parts.pop().toLowerCase()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fileExt = getFileExtension(tempFilePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
const key = generateUniqueKey(prefix, fileExt)
|
||||||
|
|
||||||
|
// 5. 上传到七牛云
|
||||||
|
const uploadResult = await uploadToQiniu(tempFilePath, token, key)
|
||||||
|
|
||||||
|
// 验证上传结果
|
||||||
|
if (!uploadResult || typeof uploadResult !== 'object') {
|
||||||
|
throw new Error('上传失败:返回数据格式错误')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!uploadResult.key) {
|
||||||
|
console.error('上传响应缺少key字段:', uploadResult)
|
||||||
|
throw new Error('上传失败:未返回文件key')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. 构建完整URL
|
||||||
|
const qiniuUrl = `${domain}/${uploadResult.key}`
|
||||||
|
console.log('文件上传成功:', { key: uploadResult.key, url: qiniuUrl })
|
||||||
|
|
||||||
|
// 7. 生成文件名(如果没有提供)
|
||||||
|
const finalFileName = fileName || `文件.${fileExt}`
|
||||||
|
|
||||||
|
if (showToast) {
|
||||||
|
// 上传成功不显示提示,避免打扰用户
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
url: qiniuUrl,
|
||||||
|
name: finalFileName,
|
||||||
|
size: fileInfo.size || 0
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('文件上传失败:', error)
|
||||||
|
if (showToast) {
|
||||||
|
uni.showToast({
|
||||||
|
title: error.message || '上传失败',
|
||||||
|
icon: 'none',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量上传文件到七牛云(支持图片和文档)
|
||||||
|
* @param {Array<{path: string, name?: string}>} files - 文件数组,每个文件包含path和可选的name
|
||||||
|
* @param {object} [options] - 配置选项
|
||||||
|
* @returns {Promise<Array<{url: string, name: string, size: number}>>} 上传结果数组
|
||||||
|
*/
|
||||||
|
export const batchUploadFilesToQiniu = async (files, options = {}) => {
|
||||||
|
return Promise.all(
|
||||||
|
files.map(file => uploadFileToQiniu(file.path, file.name || '', { ...options, showToast: false }))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取文件信息
|
* 获取文件信息
|
||||||
* @private
|
* @private
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user