OfficeSystem/components/task/AttachmentImageUploader.vue

176 lines
3.1 KiB
Vue
Raw Normal View History

2025-11-14 17:57:26 +08:00
<template>
<view class="attachment-block">
<view class="form-item clickable-item" @click="handleChooseImages">
<view class="form-icon">{{ icon }}</view>
<text class="form-label">{{ title }}</text>
<text class="arrow"></text>
</view>
<view class="images-preview" v-if="images.length">
<view
class="image-item"
v-for="(image, index) in images"
:key="image + index"
>
<image
:src="image"
mode="aspectFill"
class="preview-image"
@click="previewImage(index)"
/>
<view class="remove-btn" @click.stop="removeImage(index)"></view>
</view>
</view>
</view>
</template>
<script setup>
import { computed } from 'vue';
import { chooseAndUploadImages } from '@/utils/qiniu.js';
const props = defineProps({
modelValue: {
type: Array,
default: () => []
},
maxCount: {
type: Number,
default: 9
},
title: {
type: String,
default: '添加照片'
},
icon: {
type: String,
default: '🏔️'
}
});
const emit = defineEmits(['update:modelValue', 'change']);
const images = computed({
get: () => props.modelValue,
set: (val) => {
emit('update:modelValue', val);
emit('change', val);
}
});
const handleChooseImages = async () => {
const remainingCount = props.maxCount - images.value.length;
if (remainingCount <= 0) {
uni.showToast({
title: `最多只能添加${props.maxCount}张图片`,
icon: 'none'
});
return;
}
try {
const urls = await chooseAndUploadImages({
count: remainingCount,
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera']
});
images.value = [...images.value, ...urls];
} catch (err) {
console.error('选择或上传图片失败:', err);
uni.showToast({
title: err?.message || '选择图片失败',
icon: 'none'
});
}
};
const previewImage = (index) => {
uni.previewImage({
urls: images.value,
current: index
});
};
const removeImage = (index) => {
const next = [...images.value];
next.splice(index, 1);
images.value = next;
};
</script>
<style scoped lang="scss">
.attachment-block {
margin-bottom: 12px;
}
.form-item {
display: flex;
align-items: center;
padding: 16px;
background-color: #fff;
border-radius: 8px;
gap: 12px;
}
.clickable-item {
cursor: pointer;
&:active {
background-color: #f5f5f5;
}
}
.form-icon {
font-size: 20px;
flex-shrink: 0;
}
.form-label {
flex: 1;
font-size: 15px;
color: #333;
}
.arrow {
font-size: 20px;
color: #999;
flex-shrink: 0;
}
.images-preview {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 12px;
}
.image-item {
position: relative;
width: 100px;
height: 100px;
border-radius: 8px;
overflow: hidden;
}
.preview-image {
width: 100%;
height: 100%;
}
.remove-btn {
position: absolute;
top: 6px;
right: 6px;
width: 24px;
height: 24px;
background-color: rgba(0, 0, 0, 0.6);
color: #fff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
cursor: pointer;
}
</style>