Compare commits
3 Commits
f7f10f5d15
...
70a66fabdc
| Author | SHA1 | Date | |
|---|---|---|---|
| 70a66fabdc | |||
| c47ebce43c | |||
| b0f4b5713d |
@@ -22,7 +22,7 @@ export interface EnvConfig {
|
|||||||
|
|
||||||
const baseConfig = {
|
const baseConfig = {
|
||||||
apiBaseURL: "https://tennis.bimwe.com",
|
apiBaseURL: "https://tennis.bimwe.com",
|
||||||
ossBaseURL: "https://bimwe-oss.oss-cn-shanghai.aliyuncs.com",
|
ossBaseURL: "https://bimwe.oss-cn-shanghai.aliyuncs.com",
|
||||||
appid: "wx815b533167eb7b53", // 测试号
|
appid: "wx815b533167eb7b53", // 测试号
|
||||||
timeout: 15000,
|
timeout: 15000,
|
||||||
enableLog: true,
|
enableLog: true,
|
||||||
|
|||||||
@@ -13,7 +13,9 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
color: #000;
|
color: #000;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-feature-settings: 'liga' off, 'clig' off;
|
font-feature-settings:
|
||||||
|
"liga" off,
|
||||||
|
"clig" off;
|
||||||
font-family: "PingFang SC";
|
font-family: "PingFang SC";
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
@@ -32,7 +34,9 @@
|
|||||||
padding-top: 24px;
|
padding-top: 24px;
|
||||||
color: #000;
|
color: #000;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-feature-settings: 'liga' off, 'clig' off;
|
font-feature-settings:
|
||||||
|
"liga" off,
|
||||||
|
"clig" off;
|
||||||
font-family: "PingFang SC";
|
font-family: "PingFang SC";
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
@@ -48,8 +52,10 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
.tips {
|
.tips {
|
||||||
color: rgba(60, 60, 67, 0.60);
|
color: rgba(60, 60, 67, 0.6);
|
||||||
font-feature-settings: 'liga' off, 'clig' off;
|
font-feature-settings:
|
||||||
|
"liga" off,
|
||||||
|
"clig" off;
|
||||||
font-family: "PingFang SC";
|
font-family: "PingFang SC";
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
@@ -62,13 +68,15 @@
|
|||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background: #F0F0F0;
|
background: #f0f0f0;
|
||||||
|
|
||||||
.input {
|
.input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
&:placeholder-shown {
|
&:placeholder-shown {
|
||||||
color: rgba(60, 60, 67, 0.30);
|
color: rgba(60, 60, 67, 0.3);
|
||||||
font-feature-settings: 'liga' off, 'clig' off;
|
font-feature-settings:
|
||||||
|
"liga" off,
|
||||||
|
"clig" off;
|
||||||
font-family: "PingFang SC";
|
font-family: "PingFang SC";
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
@@ -84,11 +92,12 @@
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 44px;
|
height: 44px;
|
||||||
border-top: 0.5px solid #CECECE;
|
border-top: 0.5px solid #cecece;
|
||||||
background: #FFF;
|
background: #fff;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
|
|
||||||
.confirm, .cancel {
|
.confirm,
|
||||||
|
.cancel {
|
||||||
width: 50%;
|
width: 50%;
|
||||||
height: 44px;
|
height: 44px;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -96,7 +105,9 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
color: #000;
|
color: #000;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-feature-settings: 'liga' off, 'clig' off;
|
font-feature-settings:
|
||||||
|
"liga" off,
|
||||||
|
"clig" off;
|
||||||
font-family: "PingFang SC";
|
font-family: "PingFang SC";
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ export default forwardRef(function GameManagePopup(props, ref) {
|
|||||||
.some((item) => item.user.id === userInfo.id);
|
.some((item) => item.user.id === userInfo.id);
|
||||||
|
|
||||||
const finished = [MATCH_STATUS.FINISHED, MATCH_STATUS.CANCELED].includes(
|
const finished = [MATCH_STATUS.FINISHED, MATCH_STATUS.CANCELED].includes(
|
||||||
detail.match_status
|
detail.match_status,
|
||||||
);
|
);
|
||||||
|
|
||||||
const inTwoHours = dayjs(detail.start_time).diff(dayjs(), "hour") < 2;
|
const inTwoHours = dayjs(detail.start_time).diff(dayjs(), "hour") < 2;
|
||||||
@@ -207,7 +207,7 @@ export default forwardRef(function GameManagePopup(props, ref) {
|
|||||||
style={{ minHeight: "unset" }}
|
style={{ minHeight: "unset" }}
|
||||||
>
|
>
|
||||||
<View className={styles.container}>
|
<View className={styles.container}>
|
||||||
{!inTwoHours && !hasOtherParticiappants && (
|
{!finished && !inTwoHours && !hasOtherParticiappants && (
|
||||||
<View className={styles.button} onClick={handleEditGame}>
|
<View className={styles.button} onClick={handleEditGame}>
|
||||||
编辑活动
|
编辑活动
|
||||||
</View>
|
</View>
|
||||||
@@ -217,12 +217,12 @@ export default forwardRef(function GameManagePopup(props, ref) {
|
|||||||
重新发布
|
重新发布
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
{!inTwoHours && !hasOtherParticiappants && (
|
{!finished && !inTwoHours && !hasOtherParticiappants && (
|
||||||
<View className={styles.button} onClick={handleCancelGame}>
|
<View className={styles.button} onClick={handleCancelGame}>
|
||||||
取消活动
|
取消活动
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
{hasJoin && (
|
{!finished && hasJoin && (
|
||||||
<View className={styles.button} onClick={handleQuitGame}>
|
<View className={styles.button} onClick={handleQuitGame}>
|
||||||
退出活动
|
退出活动
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -29,18 +29,24 @@ export interface RadarChartV2Ref {
|
|||||||
}) => Promise<string>;
|
}) => Promise<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>((props, ref) => {
|
const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>(
|
||||||
|
(props, ref) => {
|
||||||
const { data } = props;
|
const { data } = props;
|
||||||
|
|
||||||
const maxValue = 100;
|
const maxValue = 100;
|
||||||
const levels = 5;
|
const levels = 5;
|
||||||
|
|
||||||
// 在 exportCanvasV2 中绘制雷达图的函数
|
// 在 exportCanvasV2 中绘制雷达图的函数
|
||||||
function drawRadarChart(ctx: CanvasRenderingContext2D, radarX: number, radarY: number, radarSize: number) {
|
function drawRadarChart(
|
||||||
|
ctx: CanvasRenderingContext2D,
|
||||||
|
radarX: number,
|
||||||
|
radarY: number,
|
||||||
|
radarSize: number,
|
||||||
|
) {
|
||||||
// 雷达图中心点位置(radarSize 已经是2倍图尺寸)
|
// 雷达图中心点位置(radarSize 已经是2倍图尺寸)
|
||||||
const center = {
|
const center = {
|
||||||
x: radarX + radarSize / 2,
|
x: radarX + radarSize / 2,
|
||||||
y: radarY + radarSize / 2
|
y: radarY + radarSize / 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
// 计算实际半径(radarSize 是直径,半径是直径的一半)
|
// 计算实际半径(radarSize 是直径,半径是直径的一半)
|
||||||
@@ -48,9 +54,9 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>((props, ref)
|
|||||||
|
|
||||||
// 启用抗锯齿
|
// 启用抗锯齿
|
||||||
ctx.imageSmoothingEnabled = true;
|
ctx.imageSmoothingEnabled = true;
|
||||||
ctx.imageSmoothingQuality = 'high';
|
ctx.imageSmoothingQuality = "high";
|
||||||
ctx.lineCap = 'round';
|
ctx.lineCap = "round";
|
||||||
ctx.lineJoin = 'round';
|
ctx.lineJoin = "round";
|
||||||
|
|
||||||
// 解析数据
|
// 解析数据
|
||||||
const { texts, vals } = data.reduce(
|
const { texts, vals } = data.reduce(
|
||||||
@@ -61,7 +67,7 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>((props, ref)
|
|||||||
vals: [...res.vals, val],
|
vals: [...res.vals, val],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
{ texts: [], vals: [] }
|
{ texts: [], vals: [] },
|
||||||
);
|
);
|
||||||
|
|
||||||
// === 绘制圆形网格 ===
|
// === 绘制圆形网格 ===
|
||||||
@@ -148,25 +154,39 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>((props, ref)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取图片信息(宽高)
|
// 获取图片信息(宽高)
|
||||||
function getImageInfo(src: string): Promise<{ width: number; height: number }> {
|
function getImageInfo(
|
||||||
|
src: string,
|
||||||
|
): Promise<{ width: number; height: number }> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
(Taro as any).getImageInfo({
|
(Taro as any).getImageInfo({
|
||||||
src,
|
src,
|
||||||
success: (res: any) => resolve({ width: res.width, height: res.height }),
|
success: (res: any) =>
|
||||||
|
resolve({ width: res.width, height: res.height }),
|
||||||
fail: reject,
|
fail: reject,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 绘制圆角矩形
|
// 绘制圆角矩形
|
||||||
function roundRect(ctx: any, x: number, y: number, width: number, height: number, radius: number) {
|
function roundRect(
|
||||||
|
ctx: any,
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
radius: number,
|
||||||
|
) {
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.moveTo(x + radius, y);
|
ctx.moveTo(x + radius, y);
|
||||||
ctx.lineTo(x + width - radius, y);
|
ctx.lineTo(x + width - radius, y);
|
||||||
ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
|
ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
|
||||||
ctx.lineTo(x + width, y + height - radius);
|
ctx.lineTo(x + width, y + height - radius);
|
||||||
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
|
ctx.quadraticCurveTo(
|
||||||
|
x + width,
|
||||||
|
y + height,
|
||||||
|
x + width - radius,
|
||||||
|
y + height,
|
||||||
|
);
|
||||||
ctx.lineTo(x + radius, y + height);
|
ctx.lineTo(x + radius, y + height);
|
||||||
ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
|
ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
|
||||||
ctx.lineTo(x, y + radius);
|
ctx.lineTo(x, y + radius);
|
||||||
@@ -187,8 +207,7 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>((props, ref)
|
|||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
// 生成原始雷达图(已废弃,现在直接在 exportCanvasV2 中绘制)
|
// 生成原始雷达图(已废弃,现在直接在 exportCanvasV2 中绘制)
|
||||||
generateImage: () =>
|
generateImage: () => Promise.resolve(""),
|
||||||
Promise.resolve(""),
|
|
||||||
|
|
||||||
// 生成完整图片(包含标题、雷达图、底部文字和二维码)
|
// 生成完整图片(包含标题、雷达图、底部文字和二维码)
|
||||||
generateFullImage: async (options: {
|
generateFullImage: async (options: {
|
||||||
@@ -229,7 +248,7 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>((props, ref)
|
|||||||
|
|
||||||
// 启用抗锯齿
|
// 启用抗锯齿
|
||||||
ctx.imageSmoothingEnabled = true;
|
ctx.imageSmoothingEnabled = true;
|
||||||
ctx.imageSmoothingQuality = 'high';
|
ctx.imageSmoothingQuality = "high";
|
||||||
|
|
||||||
// 绘制背景 - 使用 share_bg.png 背景图,撑满整个画布(从 OSS 动态加载)
|
// 绘制背景 - 使用 share_bg.png 背景图,撑满整个画布(从 OSS 动态加载)
|
||||||
try {
|
try {
|
||||||
@@ -253,18 +272,28 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>((props, ref)
|
|||||||
if (options.avatarUrl) {
|
if (options.avatarUrl) {
|
||||||
try {
|
try {
|
||||||
const avatarSize = 43.46 * scale; // 设计稿头像尺寸
|
const avatarSize = 43.46 * scale; // 设计稿头像尺寸
|
||||||
const avatarImg = await loadImage(canvas, options.avatarUrl);
|
const avatarImg = await loadImage(
|
||||||
|
canvas,
|
||||||
|
options.avatarUrl,
|
||||||
|
);
|
||||||
const avatarInfo = await getImageInfo(options.avatarUrl);
|
const avatarInfo = await getImageInfo(options.avatarUrl);
|
||||||
|
|
||||||
// 头像区域总宽度(头像 + 装饰图片重叠部分)
|
// 头像区域总宽度(头像 + 装饰图片重叠部分)
|
||||||
const avatarWrapWidth = 84.7 * scale; // 设计稿 Frame 1912055063 宽度
|
const avatarWrapWidth = 84.7 * scale; // 设计稿 Frame 1912055063 宽度
|
||||||
const avatarX = sidePadding + (294 * scale - avatarWrapWidth) / 2; // 294 是 Frame 1912055062 宽度,居中
|
const avatarX =
|
||||||
|
sidePadding + (294 * scale - avatarWrapWidth) / 2; // 294 是 Frame 1912055062 宽度,居中
|
||||||
const avatarY = currentY;
|
const avatarY = currentY;
|
||||||
|
|
||||||
// 绘制头像圆形背景
|
// 绘制头像圆形背景
|
||||||
ctx.save();
|
ctx.save();
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc(avatarX + avatarSize / 2, avatarY + avatarSize / 2, avatarSize / 2, 0, Math.PI * 2);
|
ctx.arc(
|
||||||
|
avatarX + avatarSize / 2,
|
||||||
|
avatarY + avatarSize / 2,
|
||||||
|
avatarSize / 2,
|
||||||
|
0,
|
||||||
|
Math.PI * 2,
|
||||||
|
);
|
||||||
ctx.fillStyle = "#FFFFFF";
|
ctx.fillStyle = "#FFFFFF";
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
ctx.strokeStyle = "#EFEFEF";
|
ctx.strokeStyle = "#EFEFEF";
|
||||||
@@ -273,7 +302,8 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>((props, ref)
|
|||||||
|
|
||||||
// 计算头像绘制尺寸,保持宽高比
|
// 计算头像绘制尺寸,保持宽高比
|
||||||
const innerSize = avatarSize - 1.94 * scale; // 内部可用尺寸
|
const innerSize = avatarSize - 1.94 * scale; // 内部可用尺寸
|
||||||
const avatarAspectRatio = avatarInfo.width / avatarInfo.height;
|
const avatarAspectRatio =
|
||||||
|
avatarInfo.width / avatarInfo.height;
|
||||||
let drawWidth = innerSize;
|
let drawWidth = innerSize;
|
||||||
let drawHeight = innerSize;
|
let drawHeight = innerSize;
|
||||||
let drawX = avatarX + 0.97 * scale;
|
let drawX = avatarX + 0.97 * scale;
|
||||||
@@ -292,9 +322,21 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>((props, ref)
|
|||||||
|
|
||||||
// 绘制头像(圆形裁剪)
|
// 绘制头像(圆形裁剪)
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc(avatarX + avatarSize / 2, avatarY + avatarSize / 2, avatarSize / 2 - 0.97 * scale, 0, Math.PI * 2);
|
ctx.arc(
|
||||||
|
avatarX + avatarSize / 2,
|
||||||
|
avatarY + avatarSize / 2,
|
||||||
|
avatarSize / 2 - 0.97 * scale,
|
||||||
|
0,
|
||||||
|
Math.PI * 2,
|
||||||
|
);
|
||||||
ctx.clip();
|
ctx.clip();
|
||||||
ctx.drawImage(avatarImg, drawX, drawY, drawWidth, drawHeight);
|
ctx.drawImage(
|
||||||
|
avatarImg,
|
||||||
|
drawX,
|
||||||
|
drawY,
|
||||||
|
drawWidth,
|
||||||
|
drawHeight,
|
||||||
|
);
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
|
|
||||||
// 绘制装饰图片(DocCopy)- 在头像右侧
|
// 绘制装饰图片(DocCopy)- 在头像右侧
|
||||||
@@ -317,7 +359,14 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>((props, ref)
|
|||||||
const borderRadius = 9.66 * scale; // 设计稿圆角
|
const borderRadius = 9.66 * scale; // 设计稿圆角
|
||||||
ctx.fillStyle = "#FFFFFF";
|
ctx.fillStyle = "#FFFFFF";
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
roundRect(ctx, -addonSize / 2, -addonSize / 2, addonSize, addonSize, borderRadius);
|
roundRect(
|
||||||
|
ctx,
|
||||||
|
-addonSize / 2,
|
||||||
|
-addonSize / 2,
|
||||||
|
addonSize,
|
||||||
|
addonSize,
|
||||||
|
borderRadius,
|
||||||
|
);
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
|
|
||||||
// 添加渐变背景色
|
// 添加渐变背景色
|
||||||
@@ -334,7 +383,13 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>((props, ref)
|
|||||||
const docSize = 26.18 * scale; // 设计稿内部图片尺寸
|
const docSize = 26.18 * scale; // 设计稿内部图片尺寸
|
||||||
const docRotation = -7 * (Math.PI / 180); // 内部旋转 -7 度
|
const docRotation = -7 * (Math.PI / 180); // 内部旋转 -7 度
|
||||||
ctx.rotate(docRotation);
|
ctx.rotate(docRotation);
|
||||||
ctx.drawImage(docCopyImg, -docSize / 2, -docSize / 2, docSize, docSize);
|
ctx.drawImage(
|
||||||
|
docCopyImg,
|
||||||
|
-docSize / 2,
|
||||||
|
-docSize / 2,
|
||||||
|
docSize,
|
||||||
|
docSize,
|
||||||
|
);
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to load docCopy image:", error);
|
console.error("Failed to load docCopy image:", error);
|
||||||
@@ -409,7 +464,9 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>((props, ref)
|
|||||||
const qrX = 276 * scale; // 设计稿二维码 x 位置
|
const qrX = 276 * scale; // 设计稿二维码 x 位置
|
||||||
const qrY = 523 * scale; // 设计稿二维码 y 位置
|
const qrY = 523 * scale; // 设计稿二维码 y 位置
|
||||||
|
|
||||||
const bottomTextContent = options.bottomText || "长按识别二维码,快来加入,有你就有场!";
|
const bottomTextContent =
|
||||||
|
options.bottomText ||
|
||||||
|
"长按识别二维码,快来加入,有你就有场!";
|
||||||
|
|
||||||
// 绘制底部文字 - 设计稿:fontSize: 12, fontWeight: 400, line-height: 1.5(2倍图)
|
// 绘制底部文字 - 设计稿:fontSize: 12, fontWeight: 400, line-height: 1.5(2倍图)
|
||||||
ctx.fillStyle = "rgba(0, 0, 0, 0.45)";
|
ctx.fillStyle = "rgba(0, 0, 0, 0.45)";
|
||||||
@@ -458,7 +515,13 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>((props, ref)
|
|||||||
const iconImg = await loadImage(canvas, shareLogoSvg);
|
const iconImg = await loadImage(canvas, shareLogoSvg);
|
||||||
// 图标位置:文字顶部上方 iconSize + gap
|
// 图标位置:文字顶部上方 iconSize + gap
|
||||||
const iconY = textY - iconSize - iconGap;
|
const iconY = textY - iconSize - iconGap;
|
||||||
ctx.drawImage(iconImg, topTitleX, iconY, 235 * scale, iconSize);
|
ctx.drawImage(
|
||||||
|
iconImg,
|
||||||
|
topTitleX,
|
||||||
|
iconY,
|
||||||
|
235 * scale,
|
||||||
|
iconSize,
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to load icon:", error);
|
console.error("Failed to load icon:", error);
|
||||||
}
|
}
|
||||||
@@ -468,7 +531,6 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>((props, ref)
|
|||||||
ctx.fillText(lineText, textX, textY + index * lineHeight);
|
ctx.fillText(lineText, textX, textY + index * lineHeight);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// 绘制二维码 - 设计稿位置(带白色背景、边框、阴影和圆角)
|
// 绘制二维码 - 设计稿位置(带白色背景、边框、阴影和圆角)
|
||||||
|
|
||||||
if (options.qrCodeUrl) {
|
if (options.qrCodeUrl) {
|
||||||
@@ -517,10 +579,23 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>((props, ref)
|
|||||||
// 绘制二维码图片(在圆角矩形内)
|
// 绘制二维码图片(在圆角矩形内)
|
||||||
ctx.save();
|
ctx.save();
|
||||||
// 创建圆角裁剪区域
|
// 创建圆角裁剪区域
|
||||||
roundRect(ctx, qrInnerX, qrInnerY, qrInnerSize, qrInnerSize, borderRadius - borderWidth);
|
roundRect(
|
||||||
|
ctx,
|
||||||
|
qrInnerX,
|
||||||
|
qrInnerY,
|
||||||
|
qrInnerSize,
|
||||||
|
qrInnerSize,
|
||||||
|
borderRadius - borderWidth,
|
||||||
|
);
|
||||||
ctx.clip();
|
ctx.clip();
|
||||||
// 绘制二维码图片
|
// 绘制二维码图片
|
||||||
ctx.drawImage(qrImg, qrInnerX, qrInnerY, qrInnerSize, qrInnerSize);
|
ctx.drawImage(
|
||||||
|
qrImg,
|
||||||
|
qrInnerX,
|
||||||
|
qrInnerY,
|
||||||
|
qrInnerSize,
|
||||||
|
qrInnerSize,
|
||||||
|
);
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
|
|
||||||
// 恢复上下文状态
|
// 恢复上下文状态
|
||||||
@@ -533,8 +608,8 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>((props, ref)
|
|||||||
// 导出图片
|
// 导出图片
|
||||||
Taro.canvasToTempFilePath({
|
Taro.canvasToTempFilePath({
|
||||||
canvas,
|
canvas,
|
||||||
fileType: 'png',
|
fileType: "png",
|
||||||
quality: 1,
|
quality: 0.7,
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
resolve(res.tempFilePath);
|
resolve(res.tempFilePath);
|
||||||
},
|
},
|
||||||
@@ -556,13 +631,19 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>((props, ref)
|
|||||||
<Canvas
|
<Canvas
|
||||||
type="2d"
|
type="2d"
|
||||||
id="exportCanvasV2"
|
id="exportCanvasV2"
|
||||||
style={{ position: "fixed", top: "-9999px", left: "-9999px", width: "700px", height: "1200px" }}
|
style={{
|
||||||
|
position: "fixed",
|
||||||
|
top: "-9999px",
|
||||||
|
left: "-9999px",
|
||||||
|
width: "700px",
|
||||||
|
height: "1200px",
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
RadarChartV2.displayName = "RadarChartV2";
|
RadarChartV2.displayName = "RadarChartV2";
|
||||||
|
|
||||||
export default RadarChartV2;
|
export default RadarChartV2;
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ function isFull(counts) {
|
|||||||
function matchNtrpRequestment(
|
function matchNtrpRequestment(
|
||||||
target?: string,
|
target?: string,
|
||||||
min?: string,
|
min?: string,
|
||||||
max?: string
|
max?: string,
|
||||||
): boolean {
|
): boolean {
|
||||||
// 目标值为空或 undefined
|
// 目标值为空或 undefined
|
||||||
if (!target?.trim()) return true;
|
if (!target?.trim()) return true;
|
||||||
@@ -110,7 +110,7 @@ export default function Participants(props) {
|
|||||||
user_action_status;
|
user_action_status;
|
||||||
const showApplicationEntry =
|
const showApplicationEntry =
|
||||||
[can_pay, can_substitute, is_substituting, waiting_start].every(
|
[can_pay, can_substitute, is_substituting, waiting_start].every(
|
||||||
(item) => !item
|
(item) => !item,
|
||||||
) &&
|
) &&
|
||||||
can_join &&
|
can_join &&
|
||||||
dayjs(start_time).isAfter(dayjs());
|
dayjs(start_time).isAfter(dayjs());
|
||||||
@@ -138,7 +138,7 @@ export default function Participants(props) {
|
|||||||
|
|
||||||
Taro.navigateTo({
|
Taro.navigateTo({
|
||||||
url: `/login_pages/index/index?redirect=${encodeURIComponent(
|
url: `/login_pages/index/index?redirect=${encodeURIComponent(
|
||||||
fullPath
|
fullPath,
|
||||||
)}`,
|
)}`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -153,7 +153,7 @@ export default function Participants(props) {
|
|||||||
const matchNtrpReq = matchNtrpRequestment(
|
const matchNtrpReq = matchNtrpRequestment(
|
||||||
userInfo?.ntrp_level,
|
userInfo?.ntrp_level,
|
||||||
skill_level_min,
|
skill_level_min,
|
||||||
skill_level_max
|
skill_level_max,
|
||||||
);
|
);
|
||||||
|
|
||||||
function handleSelfEvaluate() {
|
function handleSelfEvaluate() {
|
||||||
@@ -180,7 +180,7 @@ export default function Participants(props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function generateTextAndAction(
|
function generateTextAndAction(
|
||||||
user_action_status: null | { [key: string]: boolean }
|
user_action_status: null | { [key: string]: boolean },
|
||||||
):
|
):
|
||||||
| undefined
|
| undefined
|
||||||
| { text: string | React.FC; action?: () => void; available?: boolean } {
|
| { text: string | React.FC; action?: () => void; available?: boolean } {
|
||||||
@@ -259,7 +259,7 @@ export default function Participants(props) {
|
|||||||
const res = await OrderService.getUnpaidOrder(id);
|
const res = await OrderService.getUnpaidOrder(id);
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
navto(
|
navto(
|
||||||
`/order_pages/orderDetail/index?id=${res.data.order_info.order_id}`
|
`/order_pages/orderDetail/index?id=${res.data.order_info.order_id}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
@@ -296,10 +296,11 @@ export default function Participants(props) {
|
|||||||
const { action = () => {} } = generateTextAndAction(user_action_status)!;
|
const { action = () => {} } = generateTextAndAction(user_action_status)!;
|
||||||
|
|
||||||
const leftCount = max_participants - participant_count;
|
const leftCount = max_participants - participant_count;
|
||||||
const leftSubstituteCount = (max_substitute_players || 0) - (substitute_count || 0);
|
const leftSubstituteCount =
|
||||||
|
(max_substitute_players || 0) - (substitute_count || 0);
|
||||||
const showSubstituteApplicationEntry =
|
const showSubstituteApplicationEntry =
|
||||||
[can_pay, can_join, is_substituting, waiting_start].every(
|
[can_pay, can_join, is_substituting, waiting_start].every(
|
||||||
(item) => !item
|
(item) => !item,
|
||||||
) &&
|
) &&
|
||||||
can_substitute &&
|
can_substitute &&
|
||||||
dayjs(start_time).isAfter(dayjs());
|
dayjs(start_time).isAfter(dayjs());
|
||||||
@@ -336,7 +337,7 @@ export default function Participants(props) {
|
|||||||
refresherBackground="#FAFAFA"
|
refresherBackground="#FAFAFA"
|
||||||
className={classnames(
|
className={classnames(
|
||||||
styles["participants-list-scroll"],
|
styles["participants-list-scroll"],
|
||||||
showApplicationEntry ? styles.withApplication : ""
|
showApplicationEntry ? styles.withApplication : "",
|
||||||
)}
|
)}
|
||||||
scrollX
|
scrollX
|
||||||
>
|
>
|
||||||
@@ -377,14 +378,14 @@ export default function Participants(props) {
|
|||||||
src={avatar_url}
|
src={avatar_url}
|
||||||
onClick={handleViewUserInfo.bind(
|
onClick={handleViewUserInfo.bind(
|
||||||
null,
|
null,
|
||||||
participant_user_id
|
participant_user_id,
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<Text className={styles["participants-list-item-name"]}>
|
<Text className={styles["participants-list-item-name"]}>
|
||||||
{nickname || "未知"}
|
{nickname || "未知"}
|
||||||
</Text>
|
</Text>
|
||||||
<Text className={styles["participants-list-item-level"]}>
|
<Text className={styles["participants-list-item-level"]}>
|
||||||
{displayNtrp}
|
NTRP {displayNtrp}
|
||||||
</Text>
|
</Text>
|
||||||
<Text className={styles["participants-list-item-role"]}>
|
<Text className={styles["participants-list-item-role"]}>
|
||||||
{role}
|
{role}
|
||||||
@@ -400,12 +401,17 @@ export default function Participants(props) {
|
|||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
{/* 候补区域 */}
|
{/* 候补区域 */}
|
||||||
{max_substitute_players > 0 && (substitute_count > 0 || showSubstituteApplicationEntry) && (
|
{max_substitute_players > 0 &&
|
||||||
|
(substitute_count > 0 || showSubstituteApplicationEntry) && (
|
||||||
<View className={styles["detail-page-content-participants"]}>
|
<View className={styles["detail-page-content-participants"]}>
|
||||||
<View className={styles["participants-title"]}>
|
<View className={styles["participants-title"]}>
|
||||||
<Text>候补</Text>
|
<Text>候补</Text>
|
||||||
<Text>·</Text>
|
<Text>·</Text>
|
||||||
<Text>{leftSubstituteCount > 0 ? `剩余空位 ${leftSubstituteCount}` : "已满员"}</Text>
|
<Text>
|
||||||
|
{leftSubstituteCount > 0
|
||||||
|
? `剩余空位 ${leftSubstituteCount}`
|
||||||
|
: "已满员"}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className={styles["participants-list"]}>
|
<View className={styles["participants-list"]}>
|
||||||
{/* 候补申请入口 */}
|
{/* 候补申请入口 */}
|
||||||
@@ -420,7 +426,9 @@ export default function Participants(props) {
|
|||||||
className={styles["participants-list-application-icon"]}
|
className={styles["participants-list-application-icon"]}
|
||||||
src={img.ICON_DETAIL_APPLICATION_ADD}
|
src={img.ICON_DETAIL_APPLICATION_ADD}
|
||||||
/>
|
/>
|
||||||
<Text className={styles["participants-list-application-text"]}>
|
<Text
|
||||||
|
className={styles["participants-list-application-text"]}
|
||||||
|
>
|
||||||
申请候补
|
申请候补
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
@@ -430,7 +438,7 @@ export default function Participants(props) {
|
|||||||
refresherBackground="#FAFAFA"
|
refresherBackground="#FAFAFA"
|
||||||
className={classnames(
|
className={classnames(
|
||||||
styles["participants-list-scroll"],
|
styles["participants-list-scroll"],
|
||||||
showSubstituteApplicationEntry ? styles.withApplication : ""
|
showSubstituteApplicationEntry ? styles.withApplication : "",
|
||||||
)}
|
)}
|
||||||
scrollX
|
scrollX
|
||||||
>
|
>
|
||||||
@@ -438,7 +446,8 @@ export default function Participants(props) {
|
|||||||
className={styles["participants-list-scroll-content"]}
|
className={styles["participants-list-scroll-content"]}
|
||||||
style={{
|
style={{
|
||||||
width: `${
|
width: `${
|
||||||
Math.max(substitute_members.length, 1) * 103 + (Math.max(substitute_members.length, 1) - 1) * 8
|
Math.max(substitute_members.length, 1) * 103 +
|
||||||
|
(Math.max(substitute_members.length, 1) - 1) * 8
|
||||||
}px`,
|
}px`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -471,13 +480,15 @@ export default function Participants(props) {
|
|||||||
src={avatar_url}
|
src={avatar_url}
|
||||||
onClick={handleViewUserInfo.bind(
|
onClick={handleViewUserInfo.bind(
|
||||||
null,
|
null,
|
||||||
substitute_user_id
|
substitute_user_id,
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<Text className={styles["participants-list-item-name"]}>
|
<Text className={styles["participants-list-item-name"]}>
|
||||||
{nickname || "未知"}
|
{nickname || "未知"}
|
||||||
</Text>
|
</Text>
|
||||||
<Text className={styles["participants-list-item-level"]}>
|
<Text
|
||||||
|
className={styles["participants-list-item-level"]}
|
||||||
|
>
|
||||||
{displayNtrp}
|
{displayNtrp}
|
||||||
</Text>
|
</Text>
|
||||||
<Text className={styles["participants-list-item-role"]}>
|
<Text className={styles["participants-list-item-role"]}>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import { useGlobalState } from "@/store/global";
|
|||||||
import { delay, getCurrentFullPath } from "@/utils";
|
import { delay, getCurrentFullPath } from "@/utils";
|
||||||
import { formatNtrpDisplay } from "@/utils/helper";
|
import { formatNtrpDisplay } from "@/utils/helper";
|
||||||
import { waitForAuthInit } from "@/utils/authInit";
|
import { waitForAuthInit } from "@/utils/authInit";
|
||||||
import httpService from "@/services/httpService";
|
// import httpService from "@/services/httpService";
|
||||||
import DetailService from "@/services/detailService";
|
import DetailService from "@/services/detailService";
|
||||||
import { OSS_BASE } from "@/config/api";
|
import { OSS_BASE } from "@/config/api";
|
||||||
import CloseIcon from "@/static/ntrp/ntrp_close_icon.svg";
|
import CloseIcon from "@/static/ntrp/ntrp_close_icon.svg";
|
||||||
|
|||||||
@@ -282,7 +282,9 @@ function drawTextWrap(
|
|||||||
/** 核心纯函数:生成海报图片 */
|
/** 核心纯函数:生成海报图片 */
|
||||||
export async function generatePosterImage(data: any): Promise<string> {
|
export async function generatePosterImage(data: any): Promise<string> {
|
||||||
console.log("start !!!!");
|
console.log("start !!!!");
|
||||||
const dpr = Taro.getWindowInfo().pixelRatio;
|
// const dpr = Taro.getWindowInfo().pixelRatio;
|
||||||
|
const dpr = 1;
|
||||||
|
// console.log(dpr, 'dpr')
|
||||||
const width = 600;
|
const width = 600;
|
||||||
const height = 1000;
|
const height = 1000;
|
||||||
|
|
||||||
@@ -433,7 +435,7 @@ export async function generatePosterImage(data: any): Promise<string> {
|
|||||||
const { tempFilePath } = await Taro.canvasToTempFilePath({
|
const { tempFilePath } = await Taro.canvasToTempFilePath({
|
||||||
canvas,
|
canvas,
|
||||||
fileType: 'png',
|
fileType: 'png',
|
||||||
quality: 1,
|
quality: 0.7,
|
||||||
});
|
});
|
||||||
return tempFilePath;
|
return tempFilePath;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user