JinShan_uniapp/components/gx-upload.vue

237 lines
6.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>