176 lines
3.1 KiB
Vue
176 lines
3.1 KiB
Vue
|
|
<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>
|
|||
|
|
|