uploadImage.vue内容:
<view class="flex-tab upload-img-box">
<view v-for="(img,cur) in data" :key="cur" class="upload-tab">
<image :src="isObjectArr?img.imgUrl:img" @tap="lookImgFuc(data,cur)"></image>
<image v-if="isUpload" @click="deleteImgFuc(cur)" class="delete-btn" src="https://25s1xcx2-1331094596.cos.ap-guangzhou.myqcloud.com/canteen/2025/02/19/f6a029d052784eff88cef361622ec48e.png"></image>
<view v-if="isText" class="img-text">{{img[textKey]}}</view>
<view v-if="isMoreText" class="img-text more-text">
<view v-for="ele in moreTextKey"><text>{{ele.label}}</text><text :class="ele.label!==''?'font-gray':''">{{img[ele.key]||'-'}}</text></view>
</view>
</view>
<view class="upload-tab upload-add" @click="takePhoto" v-if="isUpload">
<uni-icons class="plusempty-btn" type="plusempty" size="32"></uni-icons>
</view>
<imgSyTab ref="imgSyRef" @save="saveSy"></imgSyTab>
<receiptsNews ref="receiptsNewsRef" :data="lookImgs"></receiptsNews>
</view>
props:{
data:{
type:Array,
default:[]
},
//是否可以上传
isUpload:{
type:Boolean,
default:true
},
//是否给图片添加水印
isSy:{
type:Boolean,
default:false
},
//是否在多条数据下上传
isMoreUpload:{
type:Boolean,
default:false
},
//当前第几条数据下
current:{
type:Number,
default:0
},
//是否为对象数组
isObjectArr:{
type:Boolean,
default:false
},
//是否有文本
isText:{
type:Boolean,
default:false
},
//是否多个文本
isMoreText:{
type:Boolean,
default:false
},
textKey:{
type:String,
default:'text'
},
moreTextKey:{
type:Array,
default:[]
}
},
data(){
return{
lookImgs:[]
}
},
methods:{
//删除上传照片
deleteImgFuc(cur){
this.$emit('delete',this.current,cur)
},
takePhoto() {
let that = this;
uni.chooseImage({
count: 1, // 默认9,设置图片的数量
mediaType: ['image'],
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['camera'], // 可以指定来源是相机还是相册,默认二者都有
success: (res) => {
// 成功选择图片后上传图片
const tempFilePaths = res.tempFilePaths;
if(that.isSy){
that.$refs.imgSyRef.addWaterMarking(res.tempFilePaths[0])
}else{
that.uploadImageFunc(res.tempFilePaths[0])
}
},
fail: (err) => {
console.log('Error while choosing image:', err);
}
});
},
saveSy(src){
if(this.isMoreUpload){
this.$emit('success',src,this.current,-1)
}else{
this.$emit('success',src)
}
},
uploadImageFunc(imagePath) {
let that = this;
// 转换文件为Base64
uni.getFileSystemManager().readFile({
filePath: imagePath,
encoding: 'base64',
success: res => {
// 成功获取Base64字符串
let base64Data = res.data;
yourApi({base64:base64Data,type:'2'}).then(r=>{
if(that.isMoreUpload){
that.$emit('success',r.url,that.current,-1)
}else{
that.$emit('success',r.url)
}
})
},
fail: err => {
// 转换失败的处理
console.error(err);
}
});
},
//预览图片
lookImgFuc(imgs,index){
if(this.isObjectArr){
this.lookImgs = imgs
}else{
this.lookImgs = imgs.map(ele=>{
return {imgUrl:ele}
});
}
this.$refs.receiptsNewsRef.openFuc(index);
},
}
.upload-img-box{
flex-wrap: wrap;
}
.upload-tab{
width: calc((100% - 64rpx)/4);
text-align: center;
border-radius: 16rpx;
position: relative;
margin-right: 16rpx;
margin-top: 16rpx;
&:nth-child(4n){
margin-right: 0;
}
.img-text{
font-size: 28rpx;
line-height: 42rpx;
color: #374151;
&.more-text{
text-align: left;
.font-gray{
margin-left: 4rpx;
color: #8A949F;
}
}
}
image{
width: 100%;
height: 148rpx;
border-radius: 16rpx;
border: 4rpx solid #eeeeee;
}
.delete-btn{
position: absolute;
top: -16rpx;
right: -8rpx;
width: 42rpx;
height: 42rpx;
border:none;
}
.plusempty-btn{
color: #000000 !important;
}
}
.upload-add{
background-color: #f5f5f5;
border-color: #f5f5f5;
height: 148rpx;
line-height: 148rpx;
border: 4rpx solid #eeeeee;
}
.more-text{
min-height: 214rpx;
}
水印内容imgSyTab.vue
<view class="img-sy">
<canvas canvas-id="img-canvas" class="canvas-img-box" disable-scroll="true" :style="'width:'+imgWidth+';height:'+imgHeight"></canvas>
</view>
data(){
return{
imgWidth:0,
imgHeight:0
}
},
methods:{
timeFunc(time){
let y = time.getFullYear(),m = time.getMonth()+1,d = time.getDate(),h = time.getHours(),miu = time.getMinutes(),sec = time.getSeconds();
m = m>9?m:'0'+m;
d = d>9?d:'0'+d;
h = h>9?h:'0'+h;
miu = miu>9?miu:'0'+miu;
sec = sec>9?sec:'0'+sec;
let timeA = y+'-'+m+'-'+d+' '+h+':'+miu+':'+sec;
return timeA
},
uploadImageFunc(imagePath) {
let that = this;
// 转换文件为Base64
uni.getFileSystemManager().readFile({
filePath: imagePath,
encoding: 'base64',
success: res => {
// 成功获取Base64字符串
let base64Data = res.data;
yourApi({base64:base64Data,type:'2'}).then(r=>{
that.$emit('save',r.url)
})
},
fail: err => {
// 转换失败的处理
console.error(err);
}
});
},
addWaterMarking(imageUrl) {
console.log(imageUrl);
let _this = this;
//获取原图片信息
uni.getImageInfo({
src: imageUrl,
success: async (res) => {
const imgWidth = res.width; // 图片的宽
const imgHeight = res.height; // 图片的高
_this.imgWidth = imgWidth+'px';
_this.imgHeight = imgHeight+'px';
const canvas = uni.createCanvasContext('img-canvas', this)
// 图片对象
const image = res.path
// 将图片绘制到 canvas 上
canvas.drawImage(res.path, 0, 0, imgWidth, imgHeight)
// 设置水印颜色
canvas.setFillStyle('rgba(0,0,0,0.8)');
canvas.setFontSize(36);
var text = _this.timeFunc(new Date());
var textWidth = canvas.measureText(text).width;
var x = 24;
var y = 88; // 文本的基线位置
canvas.fillText(text, x, y);
canvas.draw();
// 某些平台 canvas 绘制比较慢,需要等待绘制完成
await _this.sleep(500)
// 将 canvas 画布转换为图片地址
uni.canvasToTempFilePath({
destWidth: imgWidth,
destHeight: imgHeight,
fileType: 'jpg',
canvasId: 'img-canvas',
success: async (res) => {
// 上传图片操作
_this.uploadImageFunc(res.tempFilePath)
}
},_this)
}
})
},
sleep(millisecond) {
return new Promise((resolve) => {
setTimeout(resolve, millisecond)
})
},
}
.canvas-img-box{
position: fixed;
top: -99999rpx;
left: -99999rpx;
z-index: -99999;
}
图片预览receiptsNews.vue
<view>
<view class="news-dia" v-if="diaShow" @tap="diaShow=false">
<view class="news-dia-content" @tap.stop="diaShow=true">
<swiper class="swiper" circular :indicator-dots="true" :autoplay="false"
:current="curIndex">
<swiper-item v-for="(item,index) in data" :key="index">
<view class="swiper-item">
<view class="news-img" style="heigh:880rpx"> <image :src="item.imgUrl" mode="widthFix"></image></view>
</view>
</swiper-item>
</swiper>
</view>
</view>
</view>
props:{
//数据
data:{
type:Array,
default:[]
}
},
data(){
return{
diaShow:false,
curIndex:0
}
},
methods:{
openFuc(curIndex){
this.diaShow = true;
this.curIndex = curIndex;
},
closeFuc(){
this.diaShow = false;
}
}
.news-dia{
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.6);
z-index: 99999;
display: flex;
align-items: center;
justify-content: center;
.news-dia-content{
background-color: #ffffff;
border-radius: 44rpx;
width: 90%;
height: 948rpx;
overflow: hidden;
.swiper{
height: 100%;
}
.news-img{
height: 600rpx;
image{
width: 100%;
}
}
.news-tabs{
padding: 24rpx 32rpx;
}
.news-tab{
margin-top: 24rpx;
font-size: 32rpx;
color: #333333;
}
}
}