<template>
<view class="container">
<picker :range="communities" @change="onCommunityChange">
<view class="picker">选择小区:{{ selectedCommunity || '请选择' }}</view>
</picker>
<input class="input" placeholder="请输入详细位置" v-model="location" />
<textarea class="textarea" placeholder="请输入工作内容" v-model="content"></textarea>
<view class="section-title">上传照片(带水印)</view>
<view class="image-preview">
<view class="image-item" v-for="(img, index) in images" :key="index">
<image :src="img" class="preview-img" @click="previewImage(index)" />
<view class="delete-btn" @click.stop="removeImage(index)">×</view>
</view>
</view>
<button class="upload-btn" @click="chooseImages">选择照片</button>
<button class="submit-btn" @click="submit">提交</button>
<!-- 隐藏 canvas 用于绘制 -->
<canvas
canvas-id="watermarkCanvas"
id="watermarkCanvas"
type="2d"
style="position: fixed; top: -9999px; left: -9999px; width: 1px; height: 1px;"
></canvas>
</view>
</template>
<script>
export default {
data() {
return {
communities: ['小区A', '小区B', '小区C'],
selectedCommunity: '',
location: '',
content: '',
images: []
};
},
methods: {
onCommunityChange(e) {
this.selectedCommunity = this.communities[e.detail.value];
},
chooseImages() {
if (!this.selectedCommunity || !this.location || !this.content) {
uni.showToast({ title: '请填写完整信息', icon: 'none' });
return;
}
uni.chooseImage({
count: 3,
sourceType: ['camera', 'album'],
success: async (res) => {
const filePaths = res.tempFilePaths;
for (let path of filePaths) {
try {
const watermarked = await this.addWatermark(path);
this.images.push(watermarked);
} catch (e) {
console.warn('添加水印失败,使用原图', e);
this.images.push(path);
}
}
}
});
},
async addWatermark(imagePath) {
console.log('开始获取图片信息:', imagePath);
const imageInfo = await new Promise((resolve, reject) => {
uni.getImageInfo({
src: imagePath,
success: resolve,
fail: reject
});
});
const width = imageInfo.width;
const height = imageInfo.height;
console.log('图片宽高:', width, height);
// 设置 canvas 宽高
this.$nextTick(() => {
const canvasEl = document.getElementById('watermarkCanvas');
if (canvasEl) {
canvasEl.width = width;
canvasEl.height = height;
canvasEl.style.width = width + 'px';
canvasEl.style.height = height + 'px';
}
});
const ctx = uni.createCanvasContext('watermarkCanvas', this);
console.log('开始绘制 canvas');
const now = new Date();
const timeStr = `${now.getFullYear()}-${(now.getMonth() + 1).toString().padStart(2, '0')}-${now.getDate().toString().padStart(2, '0')} ${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`;
ctx.clearRect(0, 0, width, height);
ctx.drawImage(imagePath, 0, 0, width, height);
const overlayHeight = 120;
ctx.setFillStyle('rgba(0, 0, 0, 0.45)');
ctx.fillRect(0, height - overlayHeight, width, overlayHeight);
ctx.setFillStyle('#fff');
ctx.setFontSize(28);
ctx.setTextBaseline('top');
const lineHeight = 36;
ctx.fillText(`时间:${timeStr}`, 20, height - overlayHeight + 10);
ctx.fillText(`地点:${this.selectedCommunity} ${this.location}`, 20, height - overlayHeight + 10 + lineHeight);
ctx.fillText(`内容:${this.content}`, 20, height - overlayHeight + 10 + lineHeight * 2);
return new Promise((resolve, reject) => {
setTimeout(() => {
ctx.draw(false, () => {
console.log('canvas.draw 完成,开始导出');
setTimeout(() => {
uni.canvasToTempFilePath({
canvasId: 'watermarkCanvas',
destWidth: width,
destHeight: height,
success: (res) => {
console.log('导出成功:', res.tempFilePath);
resolve(res.tempFilePath);
},
fail: (err) => {
console.error('导出失败:', err);
reject(err);
}
}, this);
}, 300);
});
// 超时保护
setTimeout(() => reject(new Error('canvas.draw() 超时')), 5000);
}, 300); // 延时以确保 canvas 尺寸同步完成
});
},
previewImage(index) {
uni.previewImage({
urls: this.images,
current: this.images[index]
});
},
removeImage(index) {
this.images.splice(index, 1);
},
submit() {
if (!this.images.length) {
uni.showToast({ title: '请先添加照片', icon: 'none' });
return;
}
uni.showToast({ title: '提交成功(演示)', icon: 'success' });
}
}
};
</script>
<style scoped>
.container {
padding: 30rpx;
background-color: #f4f6f8;
height: 100vh;
overflow: auto;
}
.picker, .input, .textarea {
padding: 20rpx;
margin-bottom: 30rpx;
background-color: #fff;
border: 1px solid #ccc;
border-radius: 10rpx;
}
.textarea {
height: 150rpx;
}
.section-title {
font-weight: bold;
margin: 20rpx 0 10rpx;
}
.image-preview {
display: flex;
flex-wrap: wrap;
gap: 10rpx;
margin-bottom: 20rpx;
}
.image-item {
position: relative;
}
.preview-img {
width: 150rpx;
height: 150rpx;
border-radius: 10rpx;
object-fit: cover;
border: 1rpx solid #ddd;
}
.delete-btn {
position: absolute;
top: -10rpx;
right: -10rpx;
width: 40rpx;
height: 40rpx;
background-color: rgba(0, 0, 0, 0.6);
color: white;
text-align: center;
line-height: 40rpx;
font-size: 30rpx;
border-radius: 50%;
z-index: 10;
}
.upload-btn, .submit-btn {
background-color: #007aff;
color: white;
padding: 20rpx;
border-radius: 10rpx;
text-align: center;
margin-bottom: 20rpx;
}
.canvas {
position: fixed;
left: -9999px;
top: -9999px;
width: 1px;
height: 1px;
}
</style>
这是我的代码,你看看怎么盖