Files
tydata-webview-v2/src/components/QRcode.vue

334 lines
10 KiB
Vue
Raw Normal View History

2025-09-27 17:41:14 +08:00
<template>
<van-popup v-model:show="show" round position="bottom">
<div class="max-h-[calc(100vh-100px)] m-4">
<div class="p-4">
<van-swipe
class="poster-swiper rounded-xl shadow"
indicator-color="white"
@change="onSwipeChange"
>
<van-swipe-item
v-for="(_, index) in posterImages"
:key="index"
>
<canvas
:ref="(el) => (posterCanvasRefs[index] = el)"
class="poster-canvas rounded-xl h-[800px] m-auto"
></canvas>
</van-swipe-item>
</van-swipe>
</div>
<div
v-if="mode === 'promote'"
class="swipe-tip text-center text-gray-700 text-sm mb-2"
>
<span class="swipe-icon"></span> 左右滑动切换海报
<span class="swipe-icon"></span>
</div>
<van-divider>分享到好友</van-divider>
<div class="flex items-center justify-around">
<div
class="flex flex-col items-center justify-center"
@click="savePoster"
>
<img
src="@/assets/images/icon_share_img.svg"
class="w-10 h-10 rounded-full"
/>
<div class="text-center mt-1 text-gray-600 text-xs">
保存图片
</div>
</div>
<div
class="flex flex-col items-center justify-center"
@click="copyUrl"
>
<img
src="@/assets/images/icon_share_url.svg"
class="w-10 h-10 rounded-full"
/>
<div class="text-center mt-1 text-gray-600 text-xs">
复制链接
</div>
</div>
</div>
</div>
</van-popup>
</template>
<script setup>
import { ref, watch, nextTick, computed, onMounted, toRefs } from "vue";
import QRCode from "qrcode";
import { showToast } from "vant";
const props = defineProps({
linkIdentifier: {
type: String,
required: true,
},
mode: {
type: String,
default: "promote", // 例如 "promote" | "invitation"
},
});
const { linkIdentifier, mode } = toRefs(props);
const posterCanvasRefs = ref([]); // 用于绘制海报的canvas数组
const currentIndex = ref(0); // 当前显示的海报索引
const postersGenerated = ref([]); // 标记海报是否已经生成过将在onMounted中初始化
const show = defineModel("show");
const url = computed(() => {
const baseUrl = window.location.origin; // 获取当前站点的域名
return mode.value === "promote"
? `${baseUrl}/agent/promotionInquire/` // 使用动态的域名
: `${baseUrl}/agent/invitationAgentApply/`;
});
// 海报图片数组
const posterImages = ref([]);
// QR码位置配置为每个海报单独配置
const qrCodePositions = ref({
// promote模式的配置 (tg_qrcode)
promote: [
{ x: 180, y: 1440, size: 300 }, // tg_qrcode_1.png
{ x: 525, y: 1955, size: 500 }, // tg_qrcode_4.jpg
{ x: 255, y: 940, size: 250 }, // tg_qrcode_8.jpg
],
// invitation模式的配置 (yq_qrcode)
invitation: [
{ x: 360, y: -1370, size: 360 }, // yq_qrcode_1.png
],
});
// 处理轮播图切换事件
const onSwipeChange = (index) => {
currentIndex.value = index;
if (!postersGenerated.value[index]) {
generatePoster(index);
}
};
// 加载海报图片
const loadPosterImages = async () => {
const images = [];
const basePrefix = mode.value === "promote" ? "tg_qrcode_" : "yq_qrcode_";
2025-10-24 14:39:32 +08:00
// 根据模式确定要加载的图片编号
const imageNumbers = mode.value === "promote" ? [1, 4, 8] : [1];
2025-09-27 17:41:14 +08:00
// 加载图片
2025-10-24 14:39:32 +08:00
for (const i of imageNumbers) {
2025-09-27 17:41:14 +08:00
// 尝试加载 .png 文件
try {
const module = await import(
`@/assets/images/${basePrefix}${i}.png`
);
images.push(module.default);
continue; // 如果成功加载了 png则跳过后续的 jpg 尝试
} catch (error) {
console.warn(
`Image ${basePrefix}${i}.png not found, trying jpg...`
);
}
// 如果 .png 不存在,尝试加载 .jpg 文件
try {
const module = await import(
`@/assets/images/${basePrefix}${i}.jpg`
);
images.push(module.default);
} catch (error) {
console.warn(
`Image ${basePrefix}${i}.jpg not found either, using fallback.`
);
if (i === 1) {
// 如果第一张也不存在,创建一个空白图片
const emptyImg = new Image();
emptyImg.width = 600;
emptyImg.height = 800;
images.push(emptyImg.src);
} else if (images.length > 0) {
images.push(images[0]);
}
}
}
return images;
};
onMounted(async () => {
posterImages.value = await loadPosterImages();
// 根据加载的图片数量初始化postersGenerated数组
postersGenerated.value = Array(posterImages.value.length).fill(false);
});
// 生成海报并合成二维码
const generatePoster = async (index) => {
// 如果已经生成过海报,就直接返回
if (postersGenerated.value[index]) return;
// 确保 DOM 已经渲染完成
await nextTick();
const canvas = posterCanvasRefs.value[index];
if (!canvas) return; // 如果 canvas 元素为空则直接返回
const ctx = canvas.getContext("2d");
// 1. 加载海报图片
const posterImg = new Image();
posterImg.src = posterImages.value[index];
posterImg.onload = () => {
// 设置 canvas 尺寸与海报图一致
canvas.width = posterImg.width;
canvas.height = posterImg.height;
// 2. 绘制海报图片
ctx.drawImage(posterImg, 0, 0);
// 3. 生成二维码
QRCode.toDataURL(
generalUrl(),
{ width: 150, margin: 0 },
(err, qrCodeUrl) => {
if (err) {
console.error(err);
return;
}
// 4. 加载二维码图片
const qrCodeImg = new Image();
qrCodeImg.src = qrCodeUrl;
qrCodeImg.onload = () => {
// 获取当前海报的二维码位置配置
const positions = qrCodePositions.value[mode.value];
const position = positions[index] || positions[0]; // 如果没有对应索引的配置,则使用第一个配置
// 计算Y坐标负值表示从底部算起的位置
const qrY =
position.y < 0
? posterImg.height + position.y
: position.y;
// 绘制二维码
ctx.drawImage(
qrCodeImg,
position.x,
qrY,
position.size,
position.size
);
// 标记海报已生成
postersGenerated.value[index] = true;
};
}
);
};
};
// 监听 show 变化show 为 true 时生成海报
watch(show, (newVal) => {
if (newVal && !postersGenerated.value[currentIndex.value]) {
generatePoster(currentIndex.value); // 当弹窗显示且当前海报未生成时生成海报
}
});
// 分享到微信
const toPromote = () => {
// 这里可以实现微信分享的功能比如调用微信JS-SDK等
console.log("分享到微信好友");
};
// 保存海报图片
const savePoster = () => {
const canvas = posterCanvasRefs.value[currentIndex.value];
const dataURL = canvas.toDataURL("image/png"); // 获取 canvas 内容为图片
const a = document.createElement("a");
a.href = dataURL;
a.download = "天远数据查询.png";
a.click();
};
const generalUrl = () => {
return url.value + encodeURIComponent(linkIdentifier.value);
};
const copyUrl = () => {
copyToClipboard(generalUrl());
};
// 复制链接
const copyToClipboard = (text) => {
if (navigator.clipboard && window.isSecureContext) {
// 支持 Clipboard API
navigator.clipboard
.writeText(text)
.then(() => {
showToast({ message: "链接已复制!" });
})
.catch((err) => {
console.error("复制失败:", err);
});
} else {
// 对于不支持 Clipboard API 的浏览器,使用 fallback 方法
const textArea = document.createElement("textarea");
textArea.value = text;
document.body.appendChild(textArea);
textArea.select();
try {
document.execCommand("copy");
showToast({ message: "链接已复制!" });
} catch (err) {
console.error("复制失败:", err);
} finally {
document.body.removeChild(textArea);
}
}
};
</script>
<style lang="scss" scoped>
.poster-swiper {
height: 500px;
width: 100%;
}
.poster-canvas {
width: 100%;
height: 100%;
object-fit: contain;
}
.swipe-tip {
animation: fadeInOut 2s infinite;
}
.swipe-icon {
display: inline-block;
animation: slideLeftRight 1.5s infinite;
}
@keyframes fadeInOut {
0%,
100% {
opacity: 0.5;
}
50% {
opacity: 1;
}
}
@keyframes slideLeftRight {
0%,
100% {
transform: translateX(0);
}
50% {
transform: translateX(5px);
}
}
</style>