395 lines
12 KiB
Vue
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>
|