This commit is contained in:
张成
2025-12-04 10:40:58 +08:00
parent 797e086431
commit a67383d12c
6 changed files with 331 additions and 188 deletions

View File

@@ -1,6 +1,6 @@
import Taro from "@tarojs/taro";
import { View, Canvas } from "@tarojs/components";
import { useEffect, forwardRef, useImperativeHandle } from "react";
import { forwardRef, useImperativeHandle } from "react";
import shareLogoSvg from "@/static/ntrp/ntrp_share_logo.png";
import docCopySvg from "@/static/ntrp/ntrp_doc_copy.svg";
@@ -29,15 +29,27 @@ export interface RadarChartV2Ref {
}
const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>((props, ref) => {
const { data, title, ntrpLevel, levelDescription, qrCodeUrl, bottomText } = props;
const { data } = props;
const maxValue = 100;
const levels = 5;
const radius = 100;
const center = { x: 160, y: 160 };
const radius = 100; // 半径 100直径 200圆圈保持 200*200
useEffect(() => {
if (data.length > 0) {
// 在 exportCanvasV2 中绘制雷达图的函数
function drawRadarChart(ctx: CanvasRenderingContext2D, radarX: number, radarY: number, radarSize: number) {
// 雷达图中心点在 350*600 画布中的位置
const center = {
x: radarX + radarSize / 2, // 75 + 100 = 175
y: radarY + radarSize / 2 // 252 + 100 = 352
};
// 启用抗锯齿
ctx.imageSmoothingEnabled = true;
ctx.imageSmoothingQuality = 'high';
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
// 解析数据
const { texts, vals } = data.reduce(
(res, item) => {
const [text, val] = item;
@@ -48,29 +60,6 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>((props, ref)
},
{ texts: [], vals: [] }
);
renderCanvas(texts, vals);
}
}, [data]);
function renderCanvas(labels, values) {
const query = Taro.createSelectorQuery();
query
.select("#radarCanvasV2")
.fields({ node: true, size: true })
.exec((res) => {
const canvas = res[0].node as HTMLCanvasElement;
const ctx = canvas.getContext("2d") as CanvasRenderingContext2D;
const dpr = Taro.getWindowInfo().pixelRatio;
canvas.width = res[0].width * dpr;
canvas.height = res[0].height * dpr;
ctx.scale(dpr, dpr);
// 启用抗锯齿,消除锯齿
ctx.imageSmoothingEnabled = true;
ctx.imageSmoothingQuality = 'high';
// 设置线条端点样式为圆形,减少锯齿
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
// === 绘制圆形网格 ===
for (let i = levels; i >= 1; i--) {
@@ -84,25 +73,21 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>((props, ref)
ctx.fillStyle = "#CAFCF0";
ctx.fill();
}
// 根据层级设置不同的线条颜色,中间圆圈使用更浅的颜色
// 根据层级设置不同的线条颜色
if (i === 1) {
// 最内圈使用最浅的颜色
ctx.strokeStyle = "#E5E5E5";
} else if (i <= 3) {
// 中间圆圈使用较浅的颜色
ctx.strokeStyle = "#E0E0E0";
} else {
// 外圈使用稍深但仍然较浅的颜色
ctx.strokeStyle = "#D5D5D5";
}
// 设置线条宽度为1px确保清晰
ctx.lineWidth = 1;
ctx.stroke();
}
// === 坐标轴 & 标签 ===
labels.forEach((label, i) => {
const angle = ((Math.PI * 2) / labels.length) * i - Math.PI / 2;
texts.forEach((label, i) => {
const angle = ((Math.PI * 2) / texts.length) * i - Math.PI / 2;
const x = center.x + radius * Math.cos(angle);
const y = center.y + radius * Math.sin(angle);
@@ -110,13 +95,12 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>((props, ref)
ctx.beginPath();
ctx.moveTo(center.x, center.y);
ctx.lineTo(x, y);
// 坐标轴线条也使用较浅的颜色,与圆圈保持一致
ctx.strokeStyle = "#E0E0E0";
ctx.lineWidth = 1;
ctx.stroke();
// 标签
const offset = 10;
// 标签 - 文字显示在圆圈外面
const offset = 10; // 文字距离圆圈的偏移量
const textX = center.x + (radius + offset) * Math.cos(angle);
const textY = center.y + (radius + offset) * Math.sin(angle);
@@ -124,6 +108,7 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>((props, ref)
ctx.fillStyle = "#333";
ctx.textBaseline = "middle";
// 调整文字对齐方式
if (
Math.abs(angle) < 0.01 ||
Math.abs(Math.abs(angle) - Math.PI) < 0.01
@@ -143,12 +128,9 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>((props, ref)
const usableRadius = radius - baseRadius;
ctx.beginPath();
values.forEach((val, i) => {
const angle = ((Math.PI * 2) / labels.length) * i - Math.PI / 2;
// 关键修改:值从第二个环开始
vals.forEach((val, i) => {
const angle = ((Math.PI * 2) / texts.length) * i - Math.PI / 2;
const r = baseRadius + (val / maxValue) * usableRadius;
const x = center.x + r * Math.cos(angle);
const y = center.y + r * Math.sin(angle);
@@ -161,7 +143,6 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>((props, ref)
ctx.strokeStyle = "#00c8b4";
ctx.lineWidth = 3;
ctx.stroke();
});
}
// 加载图片工具函数
@@ -208,28 +189,16 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>((props, ref)
}
useImperativeHandle(ref, () => ({
// 生成原始雷达图
// 生成原始雷达图(已废弃,现在直接在 exportCanvasV2 中绘制)
generateImage: () =>
new Promise((resolve, reject) => {
const query = Taro.createSelectorQuery();
query
.select("#radarCanvasV2")
.fields({ node: true, size: true })
.exec((res) => {
const canvas = res[0].node;
Taro.canvasToTempFilePath({
canvas,
success: (res) => resolve(res.tempFilePath),
fail: (err) => reject(err),
});
});
}),
Promise.resolve(""),
// 生成完整图片(包含标题、雷达图、底部文字和二维码)
generateFullImage: async (options: {
title?: string;
ntrpLevel?: string;
levelDescription?: string;
avatarUrl?: string;
qrCodeUrl?: string;
bottomText?: string;
width?: number;
@@ -237,25 +206,9 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>((props, ref)
}) => {
return new Promise(async (resolve, reject) => {
try {
// 默认尺寸,根据设计稿调整
const width = options.width || 750; // 设计稿宽度
const height = options.height || 1334; // 设计稿高度
// 先获取雷达图
const radarImageUrl = await new Promise<string>((resolveRadar, rejectRadar) => {
const query = Taro.createSelectorQuery();
query
.select("#radarCanvasV2")
.fields({ node: true, size: true })
.exec((res) => {
const canvas = res[0].node;
Taro.canvasToTempFilePath({
canvas,
success: (res) => resolveRadar(res.tempFilePath),
fail: (err) => rejectRadar(err),
});
});
});
// 固定尺寸350*600
const width = 350; // 固定宽度
const height = 600; // 固定高度
// 创建导出画布
const query = Taro.createSelectorQuery();
@@ -270,35 +223,36 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>((props, ref)
const canvas = res[0].node;
const ctx = canvas.getContext("2d");
const dpr = Taro.getWindowInfo().pixelRatio;
canvas.width = width * dpr;
canvas.height = height * dpr;
ctx.scale(dpr, dpr);
// 固定 canvas 尺寸,不使用 dpr
canvas.width = width;
canvas.height = height;
// 启用抗锯齿
ctx.imageSmoothingEnabled = true;
ctx.imageSmoothingQuality = 'high';
// 绘制背景
ctx.fillStyle = "#E9FDF8";
// 绘制背景 - 渐变背景
const gradient = ctx.createLinearGradient(0, 0, 0, height);
gradient.addColorStop(0, 'rgba(191, 255, 239, 1)');
gradient.addColorStop(1, 'rgba(242, 255, 252, 1)');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, width, height);
// 根据设计稿调整内边距和布局
const topPadding = 60; // 顶部内边距
const sidePadding = 40; // 左右内边距
const bottomPadding = 30; // 底部内边距
const topPadding = 24; // 设计稿顶部内边距
const sidePadding = 28; // 设计稿左右内边距Frame 1912055062 的 x: 28
let currentY = topPadding;
// 绘制用户头像和装饰图片 - 参考 Result 组件布局,居中显示
// 绘制用户头像和装饰图片 - 根据设计稿尺寸
if (options.avatarUrl) {
try {
const avatarSize = 50; // Result 组件使用 0.5 倍数,所以是 50px
const avatarSize = 43.46; // 设计稿头像尺寸
const avatarImg = await loadImage(canvas, options.avatarUrl);
// 头像区域总宽度(头像 + 装饰图片重叠部分)
const avatarWrapWidth = 50 + 44 - 10; // 头像宽度 + 装饰宽度 - 重叠部分
const avatarX = (width - avatarWrapWidth) / 2 + 10; // 居中,考虑装饰图片的位置
const avatarWrapWidth = 84.7; // 设计稿 Frame 1912055063 宽度
const avatarX = sidePadding + (294 - avatarWrapWidth) / 2; // 294 是 Frame 1912055062 宽度,居中
const avatarY = currentY;
// 绘制头像圆形背景
@@ -313,15 +267,14 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>((props, ref)
// 绘制头像(圆形裁剪)
ctx.beginPath();
ctx.arc(avatarX + avatarSize / 2, avatarY + avatarSize / 2, avatarSize / 2 - 1, 0, Math.PI * 2);
ctx.arc(avatarX + avatarSize / 2, avatarY + avatarSize / 2, avatarSize / 2 - 0.97, 0, Math.PI * 2);
ctx.clip();
const innerAvatarSize = 45; // 头像内部尺寸 90px * 0.5
ctx.drawImage(avatarImg, avatarX + 2.5, avatarY + 2.5, innerAvatarSize, innerAvatarSize);
ctx.drawImage(avatarImg, avatarX + 0.97, avatarY + 0.97, avatarSize - 1.94, avatarSize - 1.94);
ctx.restore();
// 绘制装饰图片DocCopy- 在头像右侧
const addonSize = 44; // 88px * 0.5
const addonX = avatarX + avatarSize - 10; // margin-left: -10px (20px * 0.5)
const addonSize = 48; // 设计稿装饰图片尺寸
const addonX = avatarX + avatarSize - 10; // 重叠部分
const addonY = avatarY;
const addonRotation = 8 * (Math.PI / 180); // 旋转 8 度
@@ -336,7 +289,7 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>((props, ref)
ctx.rotate(addonRotation);
// 绘制装饰图片背景(圆角矩形,带渐变)
const borderRadius = 10; // 20px * 0.5
const borderRadius = 9.66; // 设计稿圆角
ctx.fillStyle = "#FFFFFF";
ctx.beginPath();
roundRect(ctx, -addonSize / 2, -addonSize / 2, addonSize, addonSize, borderRadius);
@@ -344,16 +297,16 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>((props, ref)
// 添加渐变背景色
ctx.globalAlpha = 0.2;
ctx.fillStyle = "rgba(89, 255, 214, 1)"; // rgba(89, 255, 214, 0.2)
ctx.fillStyle = "rgba(89, 255, 214, 1)";
ctx.fill();
ctx.globalAlpha = 1.0;
ctx.strokeStyle = "#FFFFFF";
ctx.lineWidth = 2;
ctx.lineWidth = 1.93; // 设计稿 strokeWeight
ctx.stroke();
// 绘制装饰图片
const docSize = 24; // 48px * 0.5
const docSize = 26.18; // 设计稿内部图片尺寸
const docRotation = -7 * (Math.PI / 180); // 内部旋转 -7 度
ctx.rotate(docRotation);
ctx.drawImage(docCopyImg, -docSize / 2, -docSize / 2, docSize, docSize);
@@ -362,27 +315,27 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>((props, ref)
console.error("Failed to load docCopy image:", error);
}
currentY += avatarSize + 20; // 头像到底部文字区域的间距
currentY += 48 + 20; // 头像区域高度 + gap
} catch (error) {
console.error("Failed to load avatar image:", error);
}
}
// 绘制文字区域 - 完全参考 Result 组件样式,居中对齐
const textCenterX = width / 2;
const textGap = 6; // gap: 6px
// 绘制文字区域 - 根据设计稿样式,居中对齐
const textCenterX = sidePadding + 294 / 2; // Frame 1912055062 的中心
const textGap = 6; // 设计稿 gap: 6px
// 绘制标题 - 14px, font-weight: 300
// 绘制标题 - 14px, font-weight: 300, line-height: 1.2
if (options.title) {
ctx.fillStyle = "#000000";
ctx.font = "300 14px PingFang SC, sans-serif";
ctx.font = "300 14px Noto Sans SC, sans-serif";
ctx.textAlign = "center";
ctx.textBaseline = "top";
ctx.fillText(options.title, textCenterX, currentY);
currentY += 14 + textGap; // 行高 + gap
currentY += 14 * 1.2 + textGap; // line-height: 1.2 + gap
}
// 绘制 NTRP 等级 - 36px, font-weight: 900, "NTRP" 黑色,等级数字 #00e5ad, gap: 8px
// 绘制 NTRP 等级 - 36px, font-weight: 900, "NTRP" 黑色,等级数字 #00E5AD
if (options.ntrpLevel) {
ctx.font = "900 36px Noto Sans SC, sans-serif";
ctx.textBaseline = "top";
@@ -391,7 +344,7 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>((props, ref)
const levelText = formatNtrpDisplay(options.ntrpLevel);
const ntrpWidth = ctx.measureText(ntrpText).width;
const levelWidth = ctx.measureText(levelText).width;
const totalWidth = ntrpWidth + 8 + levelWidth; // gap: 8px
const totalWidth = ntrpWidth + levelWidth; // 设计稿中紧挨着
const startX = textCenterX - totalWidth / 2;
// 绘制 "NTRP"(黑色)
@@ -399,59 +352,58 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>((props, ref)
ctx.textAlign = "left";
ctx.fillText(ntrpText, startX, currentY);
// 绘制等级数字(#00e5ad
// 绘制等级数字(#00E5AD
ctx.fillStyle = "#00E5AD";
ctx.fillText(levelText, startX + ntrpWidth + 8, currentY);
ctx.fillText(levelText, startX + ntrpWidth, currentY);
currentY += 44 + textGap; // line-height: 44px + gap
currentY += 36 * 1.222 + textGap; // line-height: 1.222 + gap
}
// 绘制描述文本 - 16px, font-weight: 600
// 绘制描述文本 - 16px, font-weight: 600, line-height: 1.4
if (options.levelDescription) {
ctx.fillStyle = "#000000";
ctx.font = "600 16px PingFang SC, sans-serif";
ctx.textAlign = "center";
ctx.textBaseline = "top";
ctx.fillText(options.levelDescription, textCenterX, currentY);
currentY += 16 + 40; // 行高 + 到雷达图的间距
// 计算到雷达图的间距:雷达图 y: 252 - 当前 Y
const radarY = 252; // 设计稿雷达图位置
currentY = radarY; // 直接设置到雷达图位置
}
// 绘制雷达图 - 根据设计稿调整尺寸和位置
try {
const radarImg = await loadImage(canvas, radarImageUrl);
// 计算雷达图尺寸,根据设计稿调整
const radarMaxSize = width - sidePadding * 2;
const radarActualSize = Math.min(radarMaxSize, 670); // 设计稿雷达图尺寸
const radarX = (width - radarActualSize) / 2; // 居中
// 直接绘制雷达图 - 根据设计稿调整尺寸和位置
const radarSize = 200; // 固定雷达图尺寸为 200*200
const radarX = 75; // 设计稿雷达图 x 位置
const radarY = currentY;
ctx.drawImage(radarImg, radarX, radarY, radarActualSize, radarActualSize);
currentY += radarActualSize + 60; // 雷达图到底部的间距
} catch (error) {
console.error("Failed to load radar image:", error);
}
// 直接在 exportCanvasV2 中绘制雷达图
drawRadarChart(ctx, radarX, radarY, radarSize);
currentY += radarSize; // 雷达图高度
// 绘制底部区域 - 根据设计稿调整600px 高度)
const bottomAreaY = 527; // 设计稿底部区域 y 位置
const qrSize = 64; // 设计稿二维码尺寸64*64
const qrX = 276; // 设计稿二维码 x 位置
const qrY = 523; // 设计稿二维码 y 位置
// 绘制底部区域 - 根据设计稿调整
const qrSize = 100; // 设计稿二维码尺寸
const bottomTextContent = options.bottomText || "长按识别二维码,快来加入,有你就有场!";
// 计算底部区域布局 - 从底部向上计算
const bottomTextHeight = 28; // 底部文字高度
const bottomTextLineHeight = 28; // 行高
// 先绘制底部文字,确定文字的实际高度
// 绘制底部文字 - 设计稿fontSize: 12, fontWeight: 400, line-height: 1.5
ctx.fillStyle = "rgba(0, 0, 0, 0.45)";
ctx.font = "400 20px sans-serif"; // 设计稿字体大小
ctx.font = "400 12px Noto Sans SC, sans-serif";
ctx.textAlign = "left";
ctx.textBaseline = "bottom";
ctx.textBaseline = "top";
const textX = sidePadding;
const textY = height - bottomPadding;
const maxTextWidth = width - sidePadding * 2 - qrSize - 20; // 预留二维码间距
const textX = 20; // 设计稿文字 x 位置
const textY = bottomAreaY;
const maxTextWidth = qrX - textX - 10; // 二维码间距
// 计算文字行数
const bottomWords = bottomTextContent.split("");
let bottomLine = "";
let textLines = [];
let textLines: string[] = [];
const lineHeight = 12 * 1.5; // fontSize * line-height
for (let i = 0; i < bottomWords.length; i++) {
const testBottomLine = bottomLine + bottomWords[i];
const metrics = ctx.measureText(testBottomLine);
@@ -466,44 +418,26 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>((props, ref)
textLines.push(bottomLine);
}
const actualTextHeight = textLines.length * bottomTextLineHeight;
// 绘制底部文字
textLines.forEach((lineText, index) => {
ctx.fillText(lineText, textX, textY - (textLines.length - 1 - index) * bottomTextLineHeight);
ctx.fillText(lineText, textX, textY + index * lineHeight);
});
// 绘制底部 SVG logo 图片 - 在文字上方
const logoSvgY = height - bottomPadding - actualTextHeight - 40; // 在文字上方间距40px
// 绘制顶部标题和图标 - 设计稿 Frame 2036081426
const topTitleY = bottomAreaY - 4; // gap: 4px
const topTitleX = textX;
// 绘制图标28*28
try {
const logoSvgPath = shareLogoSvg;
// 先获取图片的实际尺寸,保持宽高比
const logoInfo = await getImageInfo(logoSvgPath);
const logoSvgImg = await loadImage(canvas, logoSvgPath);
// 根据设计稿,目标宽度为 235px按比例计算高度
const targetWidth = 235;
const aspectRatio = logoInfo.width / logoInfo.height;
const targetHeight = targetWidth / aspectRatio;
const logoSvgX = sidePadding; // 左边对齐
// 按实际宽高比绘制,避免拉伸
ctx.drawImage(
logoSvgImg,
logoSvgX,
logoSvgY,
targetWidth,
targetHeight
);
const iconSize = 28;
const iconImg = await loadImage(canvas, shareLogoSvg);
ctx.drawImage(iconImg, topTitleX, topTitleY - iconSize - 4, 235,iconSize);
} catch (error) {
console.error("Failed to load logo SVG:", error);
console.error("Failed to load icon:", error);
}
// 绘制二维码 - 右下角,与文字底部对齐
const qrX = width - sidePadding - qrSize;
const qrY = height - bottomPadding - qrSize;
// 绘制二维码 - 设计稿位置
if (options.qrCodeUrl) {
try {
@@ -536,16 +470,11 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>((props, ref)
return (
<View>
<Canvas
type="2d"
id="radarCanvasV2"
style={{ width: "320px", height: "320px", background: "transparent" }}
/>
{/* 隐藏的导出画布 */}
<Canvas
type="2d"
id="exportCanvasV2"
style={{ position: "fixed", top: "-9999px", left: "-9999px", width: "750px", height: "1334px" }}
style={{ position: "fixed", top: "-9999px", left: "-9999px", width: "350px", height: "600px" }}
/>
</View>
);

View File

@@ -0,0 +1,113 @@
.empty_state_page {
width: 100%;
min-height: 100vh;
background: #3f3f3f;
position: relative;
display: flex;
flex-direction: column;
overflow: hidden;
&__content {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
padding: 0;
position: relative;
min-height: calc(100vh - 98px);
}
// 背景图片
&__bg_image {
position: absolute;
top: 178px;
left: 84px;
width: 221.17px;
height: 200px;
background-image: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjIyIiBoZWlnaHQ9IjIwMCIgdmlld0JveD0iMCAwIDIyMiAyMDAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIyMjIiIGhlaWdodD0iMjAwIiBmaWxsPSIjRkZGRkZGIiBmaWxsLW9wYWNpdHk9IjAuMiIvPgo8L3N2Zz4K');
background-size: contain;
background-repeat: no-repeat;
background-position: center;
opacity: 0.2;
z-index: 0;
}
// 提示文字
&__tip {
position: absolute;
top: 418px;
left: 50%;
transform: translateX(-50%);
z-index: 1;
padding: 8px 16px;
background: rgba(0, 0, 0, 0.5);
border-radius: 4px;
white-space: nowrap;
&_text {
font-family: 'PingFang SC';
font-weight: 400;
font-size: 15px;
line-height: 1.6;
color: #ffffff;
text-align: center;
}
}
// 按钮区域
&__buttons {
position: absolute;
bottom: 110px;
left: 0;
right: 0;
display: flex;
flex-direction: column;
gap: 10px;
padding: 0 20px;
z-index: 1;
align-items: center;
}
// 按钮
&__button {
display: flex;
justify-content: center;
align-items: center;
width: 251px;
height: 52px;
border-radius: 16px;
box-shadow: 0px 8px 64px 0px rgba(0, 0, 0, 0.1),
0px 4px 36px 0px rgba(0, 0, 0, 0.25);
backdrop-filter: blur(32px);
cursor: pointer;
transition: opacity 0.3s;
&:active {
opacity: 0.8;
}
// 主要按钮(去看看其他球局)
&--primary {
background: linear-gradient(154deg, rgba(255, 255, 255, 1) 0%, rgba(234, 234, 234, 1) 100%);
border: 2px solid rgba(0, 0, 0, 0.06);
}
// 次要按钮(返回首页)
&--secondary {
background: linear-gradient(154deg, rgba(255, 255, 255, 1) 0%, rgba(234, 234, 234, 1) 100%);
border: 2px solid rgba(0, 0, 0, 0.06);
}
&_text {
font-family: 'DingTalk JinBuTi';
font-weight: 400;
font-size: 18px;
line-height: 1.11;
letter-spacing: -0.05em;
text-align: center;
color: #000000;
}
}
}

View File

@@ -0,0 +1,95 @@
import React, { useState, useEffect } from 'react';
import { View, Text } from '@tarojs/components';
import Taro from '@tarojs/taro';
import { useGlobalState } from '@/store/global';
import { GeneralNavbar } from '@/components';
import './index.scss';
interface EmptyStateProps {
onGoToOtherGames?: () => void;
onGoToHome?: () => void;
}
const EmptyState: React.FC<EmptyStateProps> = ({ onGoToOtherGames, onGoToHome }) => {
const { statusNavbarHeightInfo } = useGlobalState() || {};
const { totalHeight = 98 } = statusNavbarHeightInfo || {};
const [countdown, setCountdown] = useState(5);
// 倒计时自动返回
useEffect(() => {
if (countdown <= 0) {
handle_go_to_home();
return;
}
const timer = setTimeout(() => {
setCountdown(countdown - 1);
}, 1000);
return () => clearTimeout(timer);
}, [countdown]);
// 跳转到其他球局
const handle_go_to_other_games = () => {
if (onGoToOtherGames) {
onGoToOtherGames();
} else {
Taro.switchTab({
url: '/main_pages/index',
});
}
};
// 返回首页
const handle_go_to_home = () => {
if (onGoToHome) {
onGoToHome();
} else {
const pages = Taro.getCurrentPages();
if (pages.length <= 1) {
Taro.switchTab({
url: '/main_pages/index',
});
} else {
Taro.navigateBack();
}
}
};
return (
<View className="empty_state_page" style={{ paddingTop: `${totalHeight}px` }}>
<GeneralNavbar title="" showBack={true} />
<View className="empty_state_page__content">
{/* 背景图片 */}
<View className="empty_state_page__bg_image" />
{/* 提示文字 */}
<View className="empty_state_page__tip">
<Text className="empty_state_page__tip_text">
{countdown}s
</Text>
</View>
{/* 按钮区域 */}
<View className="empty_state_page__buttons">
<View
className="empty_state_page__button empty_state_page__button--primary"
onClick={handle_go_to_other_games}
>
<Text className="empty_state_page__button_text"></Text>
</View>
<View
className="empty_state_page__button empty_state_page__button--secondary"
onClick={handle_go_to_home}
>
<Text className="empty_state_page__button_text"></Text>
</View>
</View>
</View>
</View>
);
};
export default EmptyState;

View File

@@ -22,6 +22,7 @@ import Participants from "./components/Participants";
import SupplementalNotes from "./components/SupplementalNotes";
import OrganizerInfo from "./components/OrganizerInfo";
import SharePopup from "./components/SharePopup";
import EmptyState from "./components/EmptyState";
import { navto, toast } from "@/utils/helper";
import ArrowLeft from "@/static/detail/icon-arrow-left.svg";
// import Logo from "@/static/detail/icon-logo-go.svg";
@@ -29,6 +30,7 @@ import styles from "./index.module.scss";
function Index() {
const [detail, setDetail] = useState<any>({});
const [showEmptyState, setShowEmptyState] = useState(false);
const { params } = useRouter();
const [currentLocation, setCurrentLocation] = useState<[number, number]>([
0, 0,
@@ -95,10 +97,11 @@ function Index() {
if (res.code === 0) {
setDetail(res.data);
fetchUserInfoById(res.data.publisher_id);
setShowEmptyState(false);
}
} catch (e) {
if (e.message === "球局不存在") {
handleBack();
setShowEmptyState(true);
}
}
Taro.hideLoading();
@@ -172,6 +175,20 @@ function Index() {
setGlass(top > 20);
}, 16);
// 如果显示空状态,渲染空状态页面
if (showEmptyState) {
return (
<EmptyState
onGoToOtherGames={() => {
Taro.switchTab({
url: '/main_pages/index',
});
}}
onGoToHome={handleBack}
/>
);
}
return (
<ScrollView
className={styles["detail-page"]}

View File

@@ -636,7 +636,6 @@ function Result() {
try {
const url = await genCardImage();
Taro.saveImageToPhotosAlbum({ filePath: url });
Taro.showToast({ title: "保存成功" });
} catch (e) {
Taro.showToast({ title: "图片保存失败", icon: "none" });
}
@@ -750,16 +749,6 @@ function Result() {
</View>
</View>
</View>
<Canvas
type="2d"
id="exportCanvas"
style={{
width: "0px",
height: "0px",
position: "absolute",
left: "-9999px",
}}
/>
{/* 隐藏的 RadarV2 用于生成完整图片,不显示在界面上 */}
<View style={{ position: "absolute", top: "-9999px", left: "-9999px", width: "0px", height: "0px", overflow: "hidden" }}>
<RadarChartV2

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB