version temp2

This commit is contained in:
2025-10-24 17:08:42 +08:00
parent c1dd5551f0
commit 1a919d57ba
193 changed files with 15044 additions and 15687 deletions

View File

@@ -1,66 +1,86 @@
<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-popup v-model:show="show" round position="bottom" :style="{ maxHeight: '95vh' }">
<div class="qrcode-popup-container">
<div class="qrcode-content">
<van-swipe class="poster-swiper rounded-lg sm: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-lg sm:rounded-xl 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"
>
<div v-if="mode === 'promote'"
class="swipe-tip text-center text-gray-700 text-xs sm:text-sm mb-1 sm:mb-2 px-2">
<span class="swipe-icon"></span> 左右滑动切换海报
<span class="swipe-icon"></span>
</div>
<van-divider>分享到好友</van-divider>
<van-divider class="my-2 sm:my-3">分享到好友</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 class="flex items-center justify-around pb-3 sm:pb-4 px-4">
<!-- 微信环境显示分享保存和复制按钮 -->
<template v-if="isWeChat">
<!-- <div class="flex flex-col items-center justify-center cursor-pointer" @click="shareToFriend">
<img src="@/assets/images/icon_share_friends.svg"
class="share-icon w-9 h-9 sm:w-10 sm:h-10 rounded-full" />
<div class="text-center mt-1 text-gray-600 text-xs">
分享给好友
</div>
</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 class="flex flex-col items-center justify-center cursor-pointer" @click="shareToTimeline">
<img src="@/assets/images/icon_share_wechat.svg"
class="share-icon w-9 h-9 sm:w-10 sm: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 cursor-pointer" @click="savePosterForWeChat">
<img src="@/assets/images/icon_share_img.svg"
class="share-icon w-9 h-9 sm:w-10 sm:h-10 rounded-full" />
<div class="text-center mt-1 text-gray-600 text-xs">
保存图片
</div>
</div>
</div>
<div class="flex flex-col items-center justify-center cursor-pointer" @click="copyUrl">
<img src="@/assets/images/icon_share_url.svg"
class="share-icon w-9 h-9 sm:w-10 sm:h-10 rounded-full" />
<div class="text-center mt-1 text-gray-600 text-xs">
复制链接
</div>
</div>
</template>
<!-- 非微信环境显示保存和复制按钮 -->
<template v-else>
<div class="flex flex-col items-center justify-center cursor-pointer" @click="savePoster">
<img src="@/assets/images/icon_share_img.svg"
class="share-icon w-9 h-9 sm:w-10 sm: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 cursor-pointer" @click="copyUrl">
<img src="@/assets/images/icon_share_url.svg"
class="share-icon w-9 h-9 sm:w-10 sm:h-10 rounded-full" />
<div class="text-center mt-1 text-gray-600 text-xs">
复制链接
</div>
</div>
</template>
</div>
</div>
</van-popup>
<!-- 图片保存指引遮罩层 -->
<ImageSaveGuide :show="showImageGuide" :image-url="currentImageUrl" :title="imageGuideTitle"
@close="closeImageGuide" />
</template>
<script setup>
import { ref, watch, nextTick, computed, onMounted, toRefs } from "vue";
import QRCode from "qrcode";
import { showToast } from "vant";
import { useWeixinShare } from "@/composables/useWeixinShare";
import ImageSaveGuide from "./ImageSaveGuide.vue";
const props = defineProps({
linkIdentifier: {
@@ -77,6 +97,19 @@ const posterCanvasRefs = ref([]); // 用于绘制海报的canvas数组
const currentIndex = ref(0); // 当前显示的海报索引
const postersGenerated = ref([]); // 标记海报是否已经生成过将在onMounted中初始化
const show = defineModel("show");
// 微信环境检测
const isWeChat = computed(() => {
return /MicroMessenger/i.test(navigator.userAgent);
});
// 微信分享功能
const { configWeixinShare } = useWeixinShare();
// 图片保存指引遮罩层相关
const showImageGuide = ref(false);
const currentImageUrl = ref('');
const imageGuideTitle = ref('');
const url = computed(() => {
const baseUrl = window.location.origin; // 获取当前站点的域名
return mode.value === "promote"
@@ -91,13 +124,13 @@ const posterImages = ref([]);
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
{ x: 138, y: 954, size: 220 }, // tg_qrcode_1.png
{ x: 138, y: 954, size: 220 }, // tg_qrcode_2.jpg
{ x: 138, y: 954, size: 220 }, // tg_qrcode_3.jpg
],
// invitation模式的配置 (yq_qrcode)
invitation: [
{ x: 360, y: -1370, size: 360 }, // yq_qrcode_1.png
{ x: 138, y: 954, size: 220 }, // yq_qrcode_1.png
],
});
@@ -115,7 +148,7 @@ const loadPosterImages = async () => {
const basePrefix = mode.value === "promote" ? "tg_qrcode_" : "yq_qrcode_";
// 根据模式确定要加载的图片编号
const imageNumbers = mode.value === "promote" ? [1, 4, 8] : [1];
const imageNumbers = mode.value === "promote" ? [1, 2, 3] : [1];
// 加载图片
for (const i of imageNumbers) {
@@ -236,22 +269,216 @@ watch(show, (newVal) => {
}
});
// 分享到微信
const toPromote = () => {
// 这里可以实现微信分享的功能比如调用微信JS-SDK等
console.log("分享到微信好友");
// 分享给好友
const shareToFriend = () => {
if (!isWeChat.value) {
showToast({ message: "请在微信中打开" });
return;
}
const shareUrl = generalUrl();
const shareConfig = {
title: mode.value === "promote"
? "哈密大数据查询 - 推广链接"
: "哈密大数据查询 - 邀请链接",
desc: mode.value === "promote"
? "扫码查看哈密大数据查询推广信息"
: "扫码申请哈密大数据查询代理权限",
link: shareUrl,
imgUrl: "https://hm.tianyuandb.com/logo.jpg"
};
configWeixinShare(shareConfig);
// 显示分享指引
showShareGuide("好友");
};
// 保存海报图片
// 分享到朋友圈
const shareToTimeline = () => {
if (!isWeChat.value) {
showToast({ message: "请在微信中打开" });
return;
}
const shareUrl = generalUrl();
const shareConfig = {
title: mode.value === "promote"
? "哈密大数据查询 - 推广链接"
: "哈密大数据查询 - 邀请链接",
desc: mode.value === "promote"
? "扫码查看哈密大数据查询推广信息"
: "扫码申请哈密大数据查询代理权限",
link: shareUrl,
imgUrl: "https://hm.tianyuandb.com/logo.jpg"
};
configWeixinShare(shareConfig);
// 显示分享指引
showShareGuide("朋友圈");
};
// 显示分享指引
const showShareGuide = (target) => {
// 设置遮罩层内容
currentImageUrl.value = ''; // 分享指引不需要图片
imageGuideTitle.value = `分享到${target}`;
// 显示遮罩层
showImageGuide.value = true;
};
// 微信环境保存图片
const savePosterForWeChat = () => {
const canvas = posterCanvasRefs.value[currentIndex.value];
const dataURL = canvas.toDataURL("image/png");
// 设置遮罩层内容
currentImageUrl.value = dataURL;
imageGuideTitle.value = '保存图片到相册';
// 显示遮罩层
showImageGuide.value = true;
};
// 关闭图片保存指引
const closeImageGuide = () => {
showImageGuide.value = false;
};
// 保存海报图片 - 多种保存方式(非微信环境)
const savePoster = () => {
const canvas = posterCanvasRefs.value[currentIndex.value];
const dataURL = canvas.toDataURL("image/png"); // 获取 canvas 内容为图片
const dataURL = canvas.toDataURL("image/png");
// 检测环境
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
if (isMobile) {
// 手机浏览器环境
saveForMobile(dataURL);
} else {
// PC浏览器环境
saveForPC(dataURL);
}
};
// PC浏览器保存方式
const saveForPC = (dataURL) => {
const a = document.createElement("a");
a.href = dataURL;
a.download = "天远数据查询.png";
a.download = "哈密大数据查询海报.png";
a.click();
};
// 手机浏览器保存方式
const saveForMobile = async (dataURL) => {
// 方法1: 尝试使用 File System Access API (Chrome 86+)
const fileSystemSuccess = await saveWithFileSystemAPI(dataURL);
if (fileSystemSuccess) return;
// 方法2: 尝试使用 Blob 和 URL.createObjectURL
try {
const blob = dataURLToBlob(dataURL);
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "哈密大数据查询海报.png";
a.style.display = "none";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
// 清理 URL 对象
setTimeout(() => URL.revokeObjectURL(url), 100);
showToast({ message: "图片已保存到相册" });
} catch (error) {
console.error("Blob保存失败:", error);
// 方法3: 尝试使用 share API (支持分享到其他应用)
const shareSuccess = await tryShareAPI(dataURL);
if (!shareSuccess) {
// 方法4: 降级到长按保存提示
showLongPressTip(dataURL);
}
}
};
// 显示长按保存提示(非微信环境使用)
const showLongPressTip = (dataURL) => {
// 设置遮罩层内容
currentImageUrl.value = dataURL;
imageGuideTitle.value = '保存图片到相册';
// 显示遮罩层
showImageGuide.value = true;
};
// 将 dataURL 转换为 Blob
const dataURLToBlob = (dataURL) => {
const arr = dataURL.split(',');
const mime = arr[0].match(/:(.*?);/)[1];
const bstr = atob(arr[1]);
let n = bstr.length;
const u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], { type: mime });
};
// 备用保存方法 - 使用 File System Access API (现代浏览器)
const saveWithFileSystemAPI = async (dataURL) => {
if ('showSaveFilePicker' in window) {
try {
const blob = dataURLToBlob(dataURL);
const fileHandle = await window.showSaveFilePicker({
suggestedName: '哈密大数据查询海报.png',
types: [{
description: 'PNG images',
accept: { 'image/png': ['.png'] }
}]
});
const writable = await fileHandle.createWritable();
await writable.write(blob);
await writable.close();
showToast({ message: "图片已保存" });
return true;
} catch (error) {
console.error("File System API 保存失败:", error);
return false;
}
}
return false;
};
// 尝试使用 Share API
const tryShareAPI = async (dataURL) => {
if (navigator.share && navigator.canShare) {
try {
const blob = dataURLToBlob(dataURL);
const file = new File([blob], '哈密大数据查询海报.png', { type: 'image/png' });
if (navigator.canShare({ files: [file] })) {
await navigator.share({
title: '哈密大数据查询海报',
text: '分享海报图片',
files: [file]
});
showToast({ message: "图片已分享" });
return true;
}
} catch (error) {
console.error("Share API 失败:", error);
}
}
return false;
};
const generalUrl = () => {
return url.value + encodeURIComponent(linkIdentifier.value);
};
@@ -291,11 +518,64 @@ const copyToClipboard = (text) => {
</script>
<style lang="scss" scoped>
.qrcode-popup-container {
display: flex;
flex-direction: column;
max-height: 95vh;
overflow: hidden;
}
.qrcode-content {
flex-shrink: 0;
padding: 0.75rem;
}
/* 小屏设备优化 */
@media (max-width: 375px) {
.qrcode-content {
padding: 0.5rem;
}
}
/* 中等及以上屏幕 */
@media (min-width: 640px) {
.qrcode-content {
padding: 1rem;
}
}
.poster-swiper {
height: 500px;
height: calc(95vh - 180px);
min-height: 300px;
max-height: 500px;
width: 100%;
}
/* 小屏设备:更小的海报高度 */
@media (max-width: 375px) {
.poster-swiper {
height: calc(95vh - 160px);
min-height: 280px;
max-height: 400px;
}
}
/* 中等屏幕 */
@media (min-width: 640px) and (max-width: 767px) {
.poster-swiper {
height: calc(95vh - 190px);
max-height: 520px;
}
}
/* 大屏幕 */
@media (min-width: 768px) {
.poster-swiper {
height: calc(95vh - 200px);
max-height: 600px;
}
}
.poster-canvas {
width: 100%;
height: 100%;
@@ -304,30 +584,61 @@ const copyToClipboard = (text) => {
.swipe-tip {
animation: fadeInOut 2s infinite;
flex-shrink: 0;
}
.swipe-icon {
display: inline-block;
animation: slideLeftRight 1.5s infinite;
font-size: 14px;
}
@media (min-width: 640px) {
.swipe-icon {
font-size: 16px;
}
}
.share-icon {
transition: transform 0.2s ease;
}
.share-icon:active {
transform: scale(0.95);
}
@keyframes fadeInOut {
0%,
100% {
opacity: 0.5;
}
50% {
opacity: 1;
}
}
@keyframes slideLeftRight {
0%,
100% {
transform: translateX(0);
}
50% {
transform: translateX(5px);
}
}
/* 优化 van-divider 在小屏幕上的间距 */
:deep(.van-divider) {
margin: 0.5rem 0;
}
@media (min-width: 640px) {
:deep(.van-divider) {
margin: 0.75rem 0;
}
}
</style>