SFH5/pagesb/invitation/index.vue
2026-03-16 11:10:28 +08:00

395 lines
12 KiB
Vue

<template>
<view class="invitation-wrap" :class="[`${themeInfo.theme}-theme`]">
<navBar></navBar>
<!-- <canvas type="2d" id="posterCanvas" :style="{ width: `${state.canvasWidth}px`, height: `${state.canvasHeight}px` }"></canvas> -->
<view class="QrCode" :style="{ height: `${state.canvasHeight}px` }">
<image class="qrcodeImg" :src="state.qrCodeBase64"></image>
</view>
<view class="bottom-wrap">
<view class="btn-wrap" @click="saveImageToAlbum(state.qrCodeBase64)">
<view class="icon-wrap">
<uv-icon name="download" size="30" color="#1B493E"></uv-icon>
</view>
<view class="text">{{ $t("referrerInfo.loadQrCode") }}</view>
</view>
<!-- <view class="btn-wrap" @click="saveImageToAlbum(state.posterFilePath)">
<view class="icon-wrap">
<uv-icon name="photo" size="30" color="#1B493E"></uv-icon>
</view>
<view class="text">{{ $t("referrerInfo.loadPoster") }}</view>
</view> -->
<view class="btn-wrap">
<view class="icon-wrap">
<uv-icon name="weixin-fill" size="30" color="#1B493E"></uv-icon>
</view>
<button open-type="share" class="share-btn"></button>
<view class="text">{{ $t("referrerInfo.forwardInvitation") }}</view>
</view>
</view>
<canvas type="2d" id="qrCodeCanvas" class="qrCode-hide" style="width: 300px; height: 300px;"></canvas>
</view>
</template>
<script setup>
import { ref, reactive, nextTick } from 'vue';
import { onLoad, onShareAppMessage } from '@dcloudio/uni-app';
import { navbarHeightAndStatusBarHeight } from '@/utils/common.js';
import navBar from '@/components/navBar.vue';
// 主题色配置
import { useMainStore } from '@/store/index.js';
const { themeInfo, storeState } = useMainStore();
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
import { getClientCustomerApi } from '@/Apis/clientCustomer.js';
const clientCustomerApi = getClientCustomerApi();
const state = reactive({
qrCodeBase64: '',
qrCodeUrl: '',
posterFilePath: '',
qrCodeFilePath: '',
canvasWidth: 750,
canvasHeight: 800
});
const getQrCodeBase64 = () => {
return new Promise((resolve, reject) => {
clientCustomerApi.GetMediatorQrCode().then((res) => {
if (res.code == 200) {
state.qrCodeBase64 = res.data;
resolve(true);
} else {
resolve(false);
}
}).catch(reject);
});
}
const getPosterImage = async () => {
uni.showLoading({ title: t('toast.generating'), mask: true });
try {
const canvas = await drawCanvas();
state.posterFilePath = await canvasToTempFile(canvas);
} catch (error) {
console.error('生成失败:', error);
uni.showToast({ title: t('toast.generateFail'), icon: 'none' });
} finally {
uni.hideLoading();
}
};
// 初始化Canvas
const initCanvas = (canvasId) => {
return new Promise((resolve, reject) => {
uni.createSelectorQuery()
.select(`#${canvasId}`)
.fields({ node: true, size: true })
.exec((res) => {
if (res && res[0]) {
const canvas = res[0].node;
canvas.width = res[0].width;
canvas.height = res[0].height;
resolve(canvas);
} else {
reject(new Error('Canvas初始化失败'));
}
});
});
};
const drawCanvas = () => {
return new Promise(async (resolve, reject) => {
try {
await nextTick();
const canvas = await initCanvas('posterCanvas');
const ctx = canvas.getContext('2d');
const dpr = uni.getWindowInfo().pixelRatio || 1;
const width = canvas.width;
const height = canvas.height;
const displayWidth = width * dpr;;
const displayHeight = height * dpr;
canvas.width = displayWidth;
canvas.height = displayHeight;
ctx.scale(dpr, dpr);
let x = 0;
let y = 0;
let text = '';
let textMetrics = 0;
let imgHeight = 0;
ctx.fillStyle = '#1B493E';
ctx.fillRect(0, 0, width, height);
ctx.font = 'bold 36px PingFang SC';
ctx.fillStyle = '#FFFFFF';
text = '青春公寓 · 学生专享';
textMetrics = ctx.measureText(text);
x = (width - textMetrics.width) / 2;
y = 70;
ctx.fillText(text, x, y);
ctx.font = '14px PingFang SC';
ctx.fillStyle = '#FFFFFF';
text = '智能门禁 | 近校选址 | 极速网络';
textMetrics = ctx.measureText(text);
x = (width - textMetrics.width) / 2;
y += 46;
ctx.fillText(text, x, y);
const drawImage = (ctx) => {
return new Promise((resolve) => {
const img = canvas.createImage();
img.src = 'https://elitesysapartment.oss-cn-hongkong.aliyuncs.com/2025/0814/posterRoomImg.jpg';
img.onload = () => {
x = 20;
y += 50;
imgHeight = height - y - 150;
ctx.drawImage(img, x, y, width - x * 2, imgHeight);
resolve(true);
};
});
}
const drawPriceCards = (ctx) => {
const cardData = [
{ title: '单人房', price: '$ 17,625/月起' },
{ title: '双人房', price: '$ 8,378.95/月起' },
{ title: '三人房', price: '$ 5375/月起' }
];
let startY = y;
let startX = x + (width - x * 2) / 2;
const cardWidth = (width - x * 2) / 2;
const cardGap = 20;
const cardHeight = (imgHeight - cardGap * 2) / 3;
cardData.forEach((item, index) => {
// 绘制卡片背景
ctx.fillStyle = "rgba(0, 0, 0, 0.2)";
ctx.fillRect(startX, startY, cardWidth, cardHeight, 10);
ctx.fill();
// 绘制标题
ctx.font = "bold 18px PingFang SC";
ctx.fillStyle = "#FFFFFF";
ctx.fillText(item.title, startX + 15, startY + 40);
// 绘制价格
ctx.font = "16px PingFang SC";
ctx.fillStyle = "#FFFFFF";
ctx.fillText(item.price, startX + 15, startY + 70);
startY += cardHeight + cardGap;
});
}
const drawBottom = (ctx) => {
return new Promise((resolve) => {
y = y + imgHeight + 40;
ctx.fillStyle = '#FFFFFF';
ctx.fillRect(0, y, width, height);
ctx.font = "bold 18px PingFang SC";
ctx.fillStyle = "#1B493E";
ctx.fillText("ONE PACE", 40, height - 60);
ctx.fillText("一步居", 40, height - 30);
const imgWidth = 80;
const imgY = (height - y - imgWidth) / 2;
const imgX = (width - 40 - imgWidth);
const qrCodeImage = canvas.createImage();
qrCodeImage.src = state.qrCodeBase64;
qrCodeImage.onload = () => {
ctx.drawImage(qrCodeImage, imgX, y + imgY, imgWidth, imgWidth);
resolve(true);
};
});
}
await drawImage(ctx);
// drawPriceCards(ctx);
await drawBottom(ctx);
resolve(canvas);
} catch {
reject(false);
}
});
}
// Canvas转临时文件
const canvasToTempFile = (canvas) => {
return new Promise((resolve, reject) => {
uni.canvasToTempFilePath({
canvas: canvas,
success: res => resolve(res.tempFilePath),
fail: (error => {
console.log(error);
reject(false);
})
});
});
};
// 保存到相册
const saveImageToAlbum = (base64Path) => {
// if (!filePath) return;
// uni.saveImageToPhotosAlbum({
// filePath,
// success: () => {
// uni.showToast({ title: 'Success', icon: 'none' });
// },
// fail: (err) => {
// console.log(err, '保存图片到相册失败');
// }
// });
const fs = uni.getFileSystemManager();
const filePath = `${wx.env.USER_DATA_PATH}/temp.png`;
fs.writeFile({
filePath,
data: base64Path.replace(/^data:image\/\w+;base64,/, ''),
encoding: 'base64',
success() {
uni.saveImageToPhotosAlbum({
filePath,
success() {
uni.showToast({ title: '保存成功' });
}
});
}
});
};
const getQrCodeImage = async () => {
try {
await nextTick();
const canvas = await initCanvas('qrCodeCanvas');
const ctx = canvas.getContext('2d');
const dpr = uni.getWindowInfo().pixelRatio || 1;
const width = canvas.width;
const height = canvas.height;
const displayWidth = width * dpr;
const displayHeight = height * dpr;
canvas.width = displayWidth;
canvas.height = displayHeight;
ctx.scale(dpr, dpr);
const qrCodeImage = canvas.createImage();
qrCodeImage.src = state.qrCodeBase64;
qrCodeImage.onload = async () => {
ctx.drawImage(qrCodeImage, 0, 0, width, height);
state.qrCodeFilePath = await canvasToTempFile(canvas);
};
} catch (error) {
console.error('生成二维码失败:', error);
}
}
onShareAppMessage(() => {
return {
// imageUrl: 'https://elitesysapartment.oss-cn-hongkong.aliyuncs.com/miniProgram/2025/0520/banner1.jpg',
title: '使用该链接注册登录,绑定专属中介',
path: `/pages/index/index?mediatorId=${storeState.userInfo.id}`
};
});
onLoad(async () => {
// 设置 canvas 的宽高
const systemInfo = uni.getWindowInfo();
state.canvasWidth = systemInfo.screenWidth;
state.canvasHeight = systemInfo.screenHeight - (navbarHeightAndStatusBarHeight()?.navbarHeight || 80) - 100;
await getQrCodeBase64();
// getPosterImage();
// getQrCodeImage();
});
</script>
<style lang="scss" scoped>
@import '@/static/style/theme.scss';
.invitation-wrap {
position: relative;
width: 100%;
height: 100vh;
background: #F5F5EF;
overflow: hidden;
.QrCode{
display: flex;
justify-content: center;
align-items: center;
.qrcodeImg{
width: 500rpx;
height: 500rpx;
}
}
.qrCode-hide {
position: fixed;
left: 7500rpx;
}
.bottom-wrap {
display: flex;
align-items: center;
justify-content: space-around;
position: fixed;
bottom: 0;
left: 0;
width: 100%;
height: 160rpx;
border-top: 1px solid #EEEEEE;
background: #FFFFFF;
// background: rgba(0, 0, 0, 0.3);
.btn-wrap {
position: relative;
display: flex;
align-items: center;
flex-direction: column;
.icon-wrap {
display: flex;
align-items: center;
justify-content: center;
width: 80rpx;
height: 80rpx;
}
.share-btn {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
opacity: 0;
}
.text {
color: #666666;
font-size: 28rpx;
margin-top: 10rpx;
}
}
}
}
</style>