上传文件其中包含图片
This commit is contained in:
parent
8dcca60350
commit
37b81d188a
|
|
@ -6,19 +6,38 @@
|
|||
<text class="arrow">›</text>
|
||||
</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
|
||||
class="file-item"
|
||||
v-for="(file, index) in files"
|
||||
:key="file.path + index"
|
||||
v-for="(file, index) in otherFiles"
|
||||
:key="(file.uid || file.path) + index"
|
||||
@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">
|
||||
<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 class="file-icon">{{ getFileIcon(file.name) }}</view>
|
||||
<view class="remove-btn" @click.stop="removeFile(file)">✕</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 remainingCount = props.maxCount - files.value.length;
|
||||
if (remainingCount <= 0) {
|
||||
|
|
@ -95,9 +125,10 @@ const chooseFilesWithUni = (remainingCount) => {
|
|||
extension: props.extensions,
|
||||
success: async (res) => {
|
||||
try {
|
||||
await uploadFiles(res.tempFiles.map(file => ({
|
||||
await uploadFiles(res.tempFiles.map((file) => ({
|
||||
path: file.path,
|
||||
name: file.name
|
||||
name: file.name,
|
||||
size: file.size || 0
|
||||
})));
|
||||
resolve();
|
||||
} catch (error) {
|
||||
|
|
@ -251,15 +282,18 @@ const uploadFiles = async (fileList) => {
|
|||
}
|
||||
};
|
||||
|
||||
const removeFile = (index) => {
|
||||
const next = [...files.value];
|
||||
next.splice(index, 1);
|
||||
const removeFile = (file) => {
|
||||
const next = files.value.filter((item) => item !== file);
|
||||
files.value = next;
|
||||
};
|
||||
|
||||
const getFileExtension = (fileName = '') => {
|
||||
if (!fileName) return '';
|
||||
return fileName.split('.').pop().toLowerCase();
|
||||
};
|
||||
|
||||
const getFileIcon = (fileName) => {
|
||||
if (!fileName) return '📄';
|
||||
const ext = fileName.split('.').pop().toLowerCase();
|
||||
const ext = getFileExtension(fileName);
|
||||
const iconMap = {
|
||||
pdf: '📕',
|
||||
doc: '📘',
|
||||
|
|
@ -279,6 +313,36 @@ const getFileIcon = (fileName) => {
|
|||
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) => {
|
||||
if (!bytes || bytes === 0) return '';
|
||||
const k = 1024;
|
||||
|
|
@ -287,6 +351,15 @@ const formatFileSize = (bytes) => {
|
|||
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) => {
|
||||
if (!file?.path) {
|
||||
uni.showToast({
|
||||
|
|
@ -296,14 +369,9 @@ const previewFile = (file) => {
|
|||
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
|
||||
});
|
||||
const ext = getFileExtension(file.name);
|
||||
if (imageExtensions.includes(ext)) {
|
||||
previewImage(imageFiles.value.findIndex((item) => item === file));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -392,6 +460,9 @@ const previewFile = (file) => {
|
|||
|
||||
.files-list {
|
||||
margin-top: 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.file-item {
|
||||
|
|
@ -408,11 +479,6 @@ const previewFile = (file) => {
|
|||
}
|
||||
}
|
||||
|
||||
.file-icon {
|
||||
font-size: 24px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.file-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
|
|
@ -447,5 +513,85 @@ const previewFile = (file) => {
|
|||
font-size: 14px;
|
||||
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>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user