上传文件其中包含图片
This commit is contained in:
parent
8dcca60350
commit
37b81d188a
|
|
@ -6,19 +6,38 @@
|
||||||
<text class="arrow">›</text>
|
<text class="arrow">›</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="files-list" v-if="files.length">
|
<view class="images-preview" v-if="imageFiles.length">
|
||||||
|
<view
|
||||||
|
class="image-item"
|
||||||
|
v-for="(file, index) in imageFiles"
|
||||||
|
:key="(file.uid || file.path) + index"
|
||||||
|
>
|
||||||
|
<image
|
||||||
|
:src="file.path"
|
||||||
|
mode="aspectFill"
|
||||||
|
class="preview-image"
|
||||||
|
@click="previewImage(index)"
|
||||||
|
/>
|
||||||
|
<view class="remove-btn" @click.stop="removeFile(file)">✕</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="files-list" v-if="otherFiles.length">
|
||||||
<view
|
<view
|
||||||
class="file-item"
|
class="file-item"
|
||||||
v-for="(file, index) in files"
|
v-for="(file, index) in otherFiles"
|
||||||
:key="file.path + index"
|
:key="(file.uid || file.path) + index"
|
||||||
@click="previewFile(file)"
|
@click="previewFile(file)"
|
||||||
>
|
>
|
||||||
<text class="file-icon">{{ getFileIcon(file.name) }}</text>
|
<text class="file-type-badge" :class="getFileTypeClass(file.name)">
|
||||||
|
{{ getFileTypeLabel(file.name) }}
|
||||||
|
</text>
|
||||||
<view class="file-info">
|
<view class="file-info">
|
||||||
<text class="file-name">{{ file.name }}</text>
|
<text class="file-name">{{ file.name }}</text>
|
||||||
<text class="file-size" v-if="file.size > 0">{{ formatFileSize(file.size) }}</text>
|
<text class="file-size" v-if="file.size > 0">{{ formatFileSize(file.size) }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="remove-btn" @click.stop="removeFile(index)">✕</view>
|
<view class="file-icon">{{ getFileIcon(file.name) }}</view>
|
||||||
|
<view class="remove-btn" @click.stop="removeFile(file)">✕</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
@ -61,6 +80,17 @@ const files = computed({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'heic', 'heif', 'svg'];
|
||||||
|
|
||||||
|
const isImageFile = (file) => {
|
||||||
|
if (!file) return false;
|
||||||
|
const ext = getFileExtension(file.name);
|
||||||
|
return imageExtensions.includes(ext);
|
||||||
|
};
|
||||||
|
|
||||||
|
const imageFiles = computed(() => files.value.filter((file) => isImageFile(file)));
|
||||||
|
const otherFiles = computed(() => files.value.filter((file) => !isImageFile(file)));
|
||||||
|
|
||||||
const handleChooseFiles = async () => {
|
const handleChooseFiles = async () => {
|
||||||
const remainingCount = props.maxCount - files.value.length;
|
const remainingCount = props.maxCount - files.value.length;
|
||||||
if (remainingCount <= 0) {
|
if (remainingCount <= 0) {
|
||||||
|
|
@ -95,9 +125,10 @@ const chooseFilesWithUni = (remainingCount) => {
|
||||||
extension: props.extensions,
|
extension: props.extensions,
|
||||||
success: async (res) => {
|
success: async (res) => {
|
||||||
try {
|
try {
|
||||||
await uploadFiles(res.tempFiles.map(file => ({
|
await uploadFiles(res.tempFiles.map((file) => ({
|
||||||
path: file.path,
|
path: file.path,
|
||||||
name: file.name
|
name: file.name,
|
||||||
|
size: file.size || 0
|
||||||
})));
|
})));
|
||||||
resolve();
|
resolve();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -251,15 +282,18 @@ const uploadFiles = async (fileList) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeFile = (index) => {
|
const removeFile = (file) => {
|
||||||
const next = [...files.value];
|
const next = files.value.filter((item) => item !== file);
|
||||||
next.splice(index, 1);
|
|
||||||
files.value = next;
|
files.value = next;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getFileExtension = (fileName = '') => {
|
||||||
|
if (!fileName) return '';
|
||||||
|
return fileName.split('.').pop().toLowerCase();
|
||||||
|
};
|
||||||
|
|
||||||
const getFileIcon = (fileName) => {
|
const getFileIcon = (fileName) => {
|
||||||
if (!fileName) return '📄';
|
const ext = getFileExtension(fileName);
|
||||||
const ext = fileName.split('.').pop().toLowerCase();
|
|
||||||
const iconMap = {
|
const iconMap = {
|
||||||
pdf: '📕',
|
pdf: '📕',
|
||||||
doc: '📘',
|
doc: '📘',
|
||||||
|
|
@ -279,6 +313,36 @@ const getFileIcon = (fileName) => {
|
||||||
return iconMap[ext] || '📄';
|
return iconMap[ext] || '📄';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getFileTypeKey = (fileName) => {
|
||||||
|
const ext = getFileExtension(fileName);
|
||||||
|
if (!ext) return 'other';
|
||||||
|
if (imageExtensions.includes(ext)) return 'image';
|
||||||
|
if (['pdf'].includes(ext)) return 'pdf';
|
||||||
|
if (['doc', 'docx', 'wps'].includes(ext)) return 'doc';
|
||||||
|
if (['xls', 'xlsx', 'csv'].includes(ext)) return 'xls';
|
||||||
|
if (['ppt', 'pptx'].includes(ext)) return 'ppt';
|
||||||
|
if (['zip', 'rar', '7z'].includes(ext)) return 'zip';
|
||||||
|
return 'other';
|
||||||
|
};
|
||||||
|
|
||||||
|
const getFileTypeLabel = (fileName) => {
|
||||||
|
const type = getFileTypeKey(fileName);
|
||||||
|
const labelMap = {
|
||||||
|
image: 'IMG',
|
||||||
|
pdf: 'PDF',
|
||||||
|
doc: 'DOC',
|
||||||
|
xls: 'XLS',
|
||||||
|
ppt: 'PPT',
|
||||||
|
zip: 'ZIP',
|
||||||
|
other: 'FILE'
|
||||||
|
};
|
||||||
|
return labelMap[type] || 'FILE';
|
||||||
|
};
|
||||||
|
|
||||||
|
const getFileTypeClass = (fileName) => {
|
||||||
|
return `badge-${getFileTypeKey(fileName)}`;
|
||||||
|
};
|
||||||
|
|
||||||
const formatFileSize = (bytes) => {
|
const formatFileSize = (bytes) => {
|
||||||
if (!bytes || bytes === 0) return '';
|
if (!bytes || bytes === 0) return '';
|
||||||
const k = 1024;
|
const k = 1024;
|
||||||
|
|
@ -287,6 +351,15 @@ const formatFileSize = (bytes) => {
|
||||||
return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
|
return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const previewImage = (index) => {
|
||||||
|
const urls = imageFiles.value.map((file) => file.path);
|
||||||
|
if (!urls.length) return;
|
||||||
|
uni.previewImage({
|
||||||
|
urls,
|
||||||
|
current: urls[index] || urls[0]
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const previewFile = (file) => {
|
const previewFile = (file) => {
|
||||||
if (!file?.path) {
|
if (!file?.path) {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
|
|
@ -296,14 +369,9 @@ const previewFile = (file) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const imageExts = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
|
const ext = getFileExtension(file.name);
|
||||||
const ext = (file.name || '').split('.').pop().toLowerCase();
|
if (imageExtensions.includes(ext)) {
|
||||||
|
previewImage(imageFiles.value.findIndex((item) => item === file));
|
||||||
if (imageExts.includes(ext)) {
|
|
||||||
uni.previewImage({
|
|
||||||
urls: [file.path],
|
|
||||||
current: file.path
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -392,6 +460,9 @@ const previewFile = (file) => {
|
||||||
|
|
||||||
.files-list {
|
.files-list {
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-item {
|
.file-item {
|
||||||
|
|
@ -408,11 +479,6 @@ const previewFile = (file) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-icon {
|
|
||||||
font-size: 24px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-info {
|
.file-info {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -447,5 +513,85 @@ const previewFile = (file) => {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.images-preview {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-item {
|
||||||
|
position: relative;
|
||||||
|
width: calc((100% - 16px) / 3);
|
||||||
|
aspect-ratio: 1;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: #f4f4f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-item .remove-btn {
|
||||||
|
position: absolute;
|
||||||
|
top: 6px;
|
||||||
|
right: 6px;
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-type-badge {
|
||||||
|
padding: 4px 10px;
|
||||||
|
border-radius: 999px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-pdf {
|
||||||
|
background-color: #fff1f0;
|
||||||
|
color: #f5222d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-doc {
|
||||||
|
background-color: #f0f5ff;
|
||||||
|
color: #2f54eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-xls {
|
||||||
|
background-color: #f6ffed;
|
||||||
|
color: #52c41a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-ppt {
|
||||||
|
background-color: #fff7e6;
|
||||||
|
color: #fa8c16;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-zip {
|
||||||
|
background-color: #f9f0ff;
|
||||||
|
color: #722ed1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-image {
|
||||||
|
background-color: #fff7e6;
|
||||||
|
color: #d48806;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-other {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-icon {
|
||||||
|
font-size: 20px;
|
||||||
|
color: #999;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user