84 lines
2.4 KiB
Vue
84 lines
2.4 KiB
Vue
<template>
|
||
<view class="proof-uploader">
|
||
<view class="label">结算凭证(至少1张)</view>
|
||
<view class="image-list">
|
||
<view v-for="(url, idx) in images" :key="url" class="image-item">
|
||
<image class="thumb" :src="url" mode="aspectFill" @click="preview(idx)" />
|
||
<view class="remove" @click="remove(idx)">×</view>
|
||
</view>
|
||
<view v-if="images.length < maxCount" class="add-btn" @click="chooseImage">
|
||
<u-icon name="plus" size="40" color="#999" />
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import { uploadSalespersonManageFileApi, extractUploadUrl } from '@/api/upload.js';
|
||
|
||
export default {
|
||
name: 'ProofImageUploader',
|
||
props: {
|
||
value: { type: Array, default: () => [] },
|
||
maxCount: { type: Number, default: 5 },
|
||
},
|
||
computed: {
|
||
images: {
|
||
get() { return this.value || []; },
|
||
set(v) { this.$emit('input', v); },
|
||
},
|
||
},
|
||
methods: {
|
||
chooseImage() {
|
||
const remain = this.maxCount - this.images.length;
|
||
uni.chooseImage({
|
||
count: remain,
|
||
sizeType: ['compressed'],
|
||
success: async (res) => {
|
||
const paths = res.tempFilePaths || [];
|
||
for (const filePath of paths) {
|
||
try {
|
||
uni.showLoading({ title: '上传中' });
|
||
const uploadRes = await uploadSalespersonManageFileApi({ filePath });
|
||
const url = extractUploadUrl(uploadRes);
|
||
if (url) {
|
||
this.$emit('input', [...this.images, url]);
|
||
}
|
||
} catch (e) {
|
||
uni.showToast({ title: '上传失败', icon: 'none' });
|
||
} finally {
|
||
uni.hideLoading();
|
||
}
|
||
}
|
||
},
|
||
});
|
||
},
|
||
remove(idx) {
|
||
const next = [...this.images];
|
||
next.splice(idx, 1);
|
||
this.$emit('input', next);
|
||
},
|
||
preview(idx) {
|
||
uni.previewImage({ urls: this.images, current: this.images[idx] });
|
||
},
|
||
},
|
||
};
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.proof-uploader { margin-top: 24rpx; }
|
||
.label { font-size: 26rpx; color: #666; margin-bottom: 16rpx; }
|
||
.image-list { display: flex; flex-wrap: wrap; gap: 16rpx; }
|
||
.image-item { position: relative; width: 160rpx; height: 160rpx; }
|
||
.thumb { width: 100%; height: 100%; border-radius: 12rpx; }
|
||
.remove {
|
||
position: absolute; top: -8rpx; right: -8rpx;
|
||
width: 36rpx; height: 36rpx; background: #ef4444; color: #fff;
|
||
border-radius: 50%; text-align: center; line-height: 36rpx; font-size: 28rpx;
|
||
}
|
||
.add-btn {
|
||
width: 160rpx; height: 160rpx; border: 2rpx dashed #ddd; border-radius: 12rpx;
|
||
display: flex; align-items: center; justify-content: center; background: #fafafa;
|
||
}
|
||
</style>
|