237 lines
6.4 KiB
Vue
237 lines
6.4 KiB
Vue
<template>
|
||
<view>
|
||
<view class="upload-area">
|
||
<view class="upload-list">
|
||
<view class="upload-item" v-for="(item, index) in fileList" :key="index">
|
||
<image :src="item.url" mode="aspectFill" @click="previewImage(index)"></image>
|
||
<view class="delete-btn" @click="handleDelete(index)">
|
||
<u-icon name="close" color="#fff" size="24"></u-icon>
|
||
</view>
|
||
</view>
|
||
<view class="upload-btn" @click="chooseMedia('album');" v-if="fileList.length < 1">
|
||
<u-icon name="plus" size="40" color="#c0c4cc"></u-icon>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<!-- <text class="note">注:电子印章必须为透明底的PNG格式,其他格式将不被接受</text> -->
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import { BASE_URL,IMAGE_BASE_URL } from '@/utils/config';
|
||
|
||
export default {
|
||
data() {
|
||
return {
|
||
fileList: [],
|
||
}
|
||
},
|
||
methods: {
|
||
showUploadAction() {
|
||
uni.showActionSheet({
|
||
itemList: ['拍照', '从相册选择'],
|
||
success: (res) => {
|
||
if (res.tapIndex === 0) {
|
||
this.chooseMedia('camera');
|
||
} else {
|
||
this.chooseMedia('album');
|
||
}
|
||
}
|
||
});
|
||
},
|
||
|
||
async chooseMedia(sourceType) {
|
||
try {
|
||
// 1. 选择图片
|
||
const res = await new Promise((resolve, reject) => {
|
||
uni.chooseImage({
|
||
count: 1,
|
||
sourceType: [sourceType === 'camera' ? 'camera' : 'album'],
|
||
extension: ['png'],
|
||
success: resolve,
|
||
fail: reject
|
||
});
|
||
});
|
||
|
||
// 2. 验证文件格式
|
||
const tempFilePath = res.tempFilePaths[0];
|
||
// const isValid = await this.checkPngFormat(tempFilePath);
|
||
|
||
// if (!isValid) {
|
||
// uni.showToast({ title: '请上传PNG格式图片', icon: 'none' });
|
||
// return;
|
||
// }
|
||
|
||
// 3. 添加到预览列表
|
||
this.fileList = [{
|
||
url: tempFilePath,
|
||
status: 'uploading'
|
||
}];
|
||
|
||
// 4. 上传文件
|
||
await this.uploadFile(this.fileList[0]);
|
||
|
||
} catch (err) {
|
||
console.error('选择图片失败:', err);
|
||
uni.showToast({
|
||
title: err.errMsg.includes('cancel') ? '已取消选择' : '请选择PNG格式图片',
|
||
icon: 'none'
|
||
});
|
||
}
|
||
},
|
||
|
||
async checkPngFormat(filePath) {
|
||
return new Promise((resolve) => {
|
||
// 获取文件后缀名
|
||
const ext = filePath.substring(filePath.lastIndexOf('.') + 1).toLowerCase();
|
||
if (ext !== 'png') {
|
||
resolve(false);
|
||
return;
|
||
}
|
||
|
||
// 读取文件头验证
|
||
const fs = uni.getFileSystemManager();
|
||
fs.readFile({
|
||
filePath,
|
||
encoding: 'binary',
|
||
success: (readRes) => {
|
||
// PNG文件头应该是 '\x89PNG\r\n\x1a\n'
|
||
const isRealPng = readRes.data.startsWith('\x89PNG\r\n\x1a\n');
|
||
resolve(isRealPng);
|
||
},
|
||
fail: () => resolve(false)
|
||
});
|
||
});
|
||
},
|
||
|
||
async uploadFile(file) {
|
||
// 上传前再次验证
|
||
// const isValid = await this.checkPngFormat(file.url);
|
||
// if (!isValid) {
|
||
// uni.showToast({ title: '文件格式不符合要求', icon: 'none' });
|
||
// return Promise.reject('Invalid file format');
|
||
// }
|
||
|
||
try {
|
||
const res = await new Promise((resolve, reject) => {
|
||
uni.uploadFile({
|
||
url: `${IMAGE_BASE_URL}/api/v1/upload`,
|
||
filePath: file.url,
|
||
name: 'file',
|
||
header: {
|
||
'Authorization': `Bearer ${uni.getStorageSync('token')}`,
|
||
'Content-Type': 'multipart/form-data'
|
||
},
|
||
success: (uploadRes) => {
|
||
try {
|
||
const data = JSON.parse(uploadRes.data);
|
||
if (data.success) {
|
||
resolve(data.data);
|
||
} else {
|
||
reject(data.message || '上传失败');
|
||
}
|
||
} catch (e) {
|
||
reject('解析响应失败');
|
||
}
|
||
},
|
||
fail: (err) => reject(err)
|
||
});
|
||
});
|
||
// 更新文件状态
|
||
const index = this.fileList.findIndex(f => f.url === file.url);
|
||
if (index !== -1) {
|
||
this.$set(this.fileList, index, {
|
||
...file,
|
||
status: 'success',
|
||
serverUrl: res
|
||
});
|
||
}
|
||
this.$emit('upload-success',res)
|
||
} catch (err) {
|
||
console.error('上传失败:', err);
|
||
const index = this.fileList.findIndex(f => f.url === file.url);
|
||
if (index !== -1) {
|
||
this.$set(this.fileList, index, {
|
||
...file,
|
||
status: 'failed',
|
||
error: err.message || err
|
||
});
|
||
}
|
||
uni.showToast({
|
||
title: `上传失败: ${err.message || err}`,
|
||
icon: 'none'
|
||
});
|
||
throw err;
|
||
}
|
||
},
|
||
|
||
previewImage(index) {
|
||
uni.previewImage({
|
||
current: index,
|
||
urls: [this.fileList[index].url]
|
||
});
|
||
},
|
||
|
||
handleDelete(index) {
|
||
this.fileList.splice(index, 1);
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.upload-area {
|
||
margin-top: 16rpx;
|
||
|
||
.upload-list {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
margin: -5rpx;
|
||
|
||
.upload-item, .upload-btn {
|
||
width: 160rpx;
|
||
height: 160rpx;
|
||
margin: 5rpx;
|
||
position: relative;
|
||
background: #f8f8f8;
|
||
border-radius: 8rpx;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
overflow: hidden;
|
||
|
||
image, video {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.delete-btn {
|
||
position: absolute;
|
||
right: 0;
|
||
top: 0;
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
background: rgba(0, 0, 0, 0.5);
|
||
border-radius: 0 0 0 8rpx;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
}
|
||
}
|
||
|
||
.upload-btn {
|
||
border: 1rpx dashed #c0c4cc;
|
||
}
|
||
}
|
||
}
|
||
|
||
.note {
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
margin-top: 24rpx;
|
||
display: block;
|
||
}
|
||
|
||
|
||
</style>
|