Compare commits
6 Commits
fix/jgh/03
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5a753e7822 | ||
|
|
66c5ea6284 | ||
| 8090d679b4 | |||
| 4965d6c40e | |||
| 56fb3ade00 | |||
| 8c6fb1190e |
20688
package-lock.json
generated
Normal file
20688
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -70,11 +70,11 @@
|
|||||||
"eslint-plugin-react": "^7.8.2",
|
"eslint-plugin-react": "^7.8.2",
|
||||||
"eslint-plugin-react-hooks": "^4.2.0",
|
"eslint-plugin-react-hooks": "^4.2.0",
|
||||||
"postcss": "^8.4.18",
|
"postcss": "^8.4.18",
|
||||||
"react-refresh": "^0.11.0",
|
"react-refresh": "^0.14.0",
|
||||||
"stylelint": "^14.4.0",
|
"stylelint": "^14.4.0",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"tsconfig-paths-webpack-plugin": "^4.0.1",
|
"tsconfig-paths-webpack-plugin": "^4.0.1",
|
||||||
"typescript": "^5.1.0",
|
"typescript": "^5.1.0",
|
||||||
"webpack": "5.78.0"
|
"webpack": "5.91.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"miniprogramRoot": "dist/",
|
"miniprogramRoot": "dist/",
|
||||||
"projectname": "playBallTogether",
|
"projectname": "playBallTogether",
|
||||||
"description": "playBallTogether",
|
"description": "playBallTogether",
|
||||||
"appid": "wx815b533167eb7b53",
|
"appid": "wx915ecf6c01bea4ec",
|
||||||
"setting": {
|
"setting": {
|
||||||
"urlCheck": true,
|
"urlCheck": true,
|
||||||
"es6": true,
|
"es6": true,
|
||||||
@@ -40,10 +40,21 @@
|
|||||||
"minifyWXML": true,
|
"minifyWXML": true,
|
||||||
"swc": true,
|
"swc": true,
|
||||||
"disableSWC": false,
|
"disableSWC": false,
|
||||||
"ignoreUploadUnusedFiles": true
|
"ignoreUploadUnusedFiles": true,
|
||||||
|
"compileWorklet": false,
|
||||||
|
"localPlugins": false,
|
||||||
|
"disableUseStrict": false,
|
||||||
|
"useCompilerPlugins": false,
|
||||||
|
"condition": false
|
||||||
},
|
},
|
||||||
"compileType": "miniprogram",
|
"compileType": "miniprogram",
|
||||||
"simulatorType": "wechat",
|
"simulatorType": "wechat",
|
||||||
"simulatorPluginLibVersion": {},
|
"simulatorPluginLibVersion": {},
|
||||||
"condition": {}
|
"condition": {},
|
||||||
|
"libVersion": "3.9.0",
|
||||||
|
"packOptions": {
|
||||||
|
"ignore": [],
|
||||||
|
"include": []
|
||||||
|
},
|
||||||
|
"editorSetting": {}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"libVersion": "3.9.0",
|
"libVersion": "3.15.1",
|
||||||
"projectname": "playBallTogether",
|
"projectname": "mini-programs",
|
||||||
"condition": {},
|
"condition": {},
|
||||||
"setting": {
|
"setting": {
|
||||||
"urlCheck": false,
|
"urlCheck": false,
|
||||||
|
|||||||
@@ -177,23 +177,32 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&-title {
|
&-title {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
gap: 2px;
|
gap: 2px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
color: rgba(255, 255, 255, 0.85);
|
color: rgba(255, 255, 255, 0.85);
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
line-height: 24px; /* 150% */
|
line-height: 24px; /* 150% */
|
||||||
|
|
||||||
&-arrow {
|
&-text {
|
||||||
width: 12px;
|
flex: 1;
|
||||||
height: 12px;
|
min-width: 0;
|
||||||
}
|
overflow: hidden;
|
||||||
}
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-arrow {
|
||||||
|
flex: 0 0 12px;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&-time-range {
|
&-time-range {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|||||||
@@ -86,12 +86,12 @@ export default function OrganizerInfo(props) {
|
|||||||
await LoginService.followUser(id);
|
await LoginService.followUser(id);
|
||||||
}
|
}
|
||||||
onUpdateUserInfo();
|
onUpdateUserInfo();
|
||||||
Taro.showToast({
|
(Taro as any).showToast({
|
||||||
title: `${nickname} ${follow ? "已取消关注" : "已关注"}`,
|
title: `${nickname} ${follow ? "已取消关注" : "已关注"}`,
|
||||||
icon: "success",
|
icon: "success",
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Taro.showToast({
|
(Taro as any).showToast({
|
||||||
title: `${nickname} ${follow ? "取消关注失败" : "关注失败"}`,
|
title: `${nickname} ${follow ? "取消关注失败" : "关注失败"}`,
|
||||||
icon: "error",
|
icon: "error",
|
||||||
});
|
});
|
||||||
@@ -193,13 +193,17 @@ export default function OrganizerInfo(props) {
|
|||||||
className={styles["recommend-games-list-item"]}
|
className={styles["recommend-games-list-item"]}
|
||||||
onClick={handleViewGame.bind(null, game.id)}
|
onClick={handleViewGame.bind(null, game.id)}
|
||||||
>
|
>
|
||||||
{/* game title */}
|
{/* game title */}
|
||||||
<View className={styles["recommend-games-list-item-title"]}>
|
<View className={styles["recommend-games-list-item-title"]}>
|
||||||
<Text>{game.title}</Text>
|
<Text
|
||||||
<Image
|
className={styles["recommend-games-list-item-title-text"]}
|
||||||
className={
|
>
|
||||||
styles["recommend-games-list-item-title-arrow"]
|
{game.title}
|
||||||
}
|
</Text>
|
||||||
|
<Image
|
||||||
|
className={
|
||||||
|
styles["recommend-games-list-item-title-arrow"]
|
||||||
|
}
|
||||||
src={img.ICON_DETAIL_ARROW_RIGHT}
|
src={img.ICON_DETAIL_ARROW_RIGHT}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -12,7 +12,12 @@ import WechatLogo from "@/static/detail/wechat_icon.svg";
|
|||||||
// import WechatTimeline from "@/static/detail/wechat_timeline.svg";
|
// import WechatTimeline from "@/static/detail/wechat_timeline.svg";
|
||||||
import LinkIcon from "@/static/detail/link.svg";
|
import LinkIcon from "@/static/detail/link.svg";
|
||||||
import CrossIcon from "@/static/detail/cross.svg";
|
import CrossIcon from "@/static/detail/cross.svg";
|
||||||
import { genNTRPRequirementText, navto, genGameLength } from "@/utils/helper";
|
import {
|
||||||
|
genNTRPRequirementText,
|
||||||
|
navto,
|
||||||
|
genGameLength,
|
||||||
|
formatGameStartTime,
|
||||||
|
} from "@/utils/helper";
|
||||||
import { waitForAuthInit } from "@/utils/authInit";
|
import { waitForAuthInit } from "@/utils/authInit";
|
||||||
import { useUserActions } from "@/store/userStore";
|
import { useUserActions } from "@/store/userStore";
|
||||||
import { OSS_BASE } from "@/config/api";
|
import { OSS_BASE } from "@/config/api";
|
||||||
@@ -107,7 +112,7 @@ export default forwardRef(({ id, from, detail, userInfo }, ref) => {
|
|||||||
const startTime = dayjs(start_time);
|
const startTime = dayjs(start_time);
|
||||||
const endTime = dayjs(end_time);
|
const endTime = dayjs(end_time);
|
||||||
const dayofWeek = DayOfWeekMap.get(startTime.day());
|
const dayofWeek = DayOfWeekMap.get(startTime.day());
|
||||||
const gameLength = `${endTime.diff(startTime, "hour")}小时`;
|
const gameLength = genGameLength(startTime, endTime);
|
||||||
const currentUserInfo = await ensureUserInfo();
|
const currentUserInfo = await ensureUserInfo();
|
||||||
try {
|
try {
|
||||||
const url = await generateShareImage({
|
const url = await generateShareImage({
|
||||||
@@ -119,7 +124,7 @@ export default forwardRef(({ id, from, detail, userInfo }, ref) => {
|
|||||||
skill_level_max,
|
skill_level_max,
|
||||||
)}`,
|
)}`,
|
||||||
gameDate: `${startTime.format("M月D日")} (${dayofWeek})`,
|
gameDate: `${startTime.format("M月D日")} (${dayofWeek})`,
|
||||||
gameTime: `${startTime.format("ah")}点 ${gameLength}`,
|
gameTime: `${formatGameStartTime(startTime)} ${gameLength}`,
|
||||||
venueName: location_name,
|
venueName: location_name,
|
||||||
venueImages: image_list ? image_list : [],
|
venueImages: image_list ? image_list : [],
|
||||||
});
|
});
|
||||||
@@ -133,11 +138,8 @@ export default forwardRef(({ id, from, detail, userInfo }, ref) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
useShareAppMessage(async (res) => {
|
useShareAppMessage(async () => {
|
||||||
await changeMessageType();
|
const url = shareImageUrl || (await generateShareImageUrl());
|
||||||
await ensureUserInfo();
|
|
||||||
const url = await generateShareImageUrl();
|
|
||||||
// console.log(res, "res");
|
|
||||||
return {
|
return {
|
||||||
title: detail.title,
|
title: detail.title,
|
||||||
imageUrl: url,
|
imageUrl: url,
|
||||||
@@ -164,7 +166,6 @@ export default forwardRef(({ id, from, detail, userInfo }, ref) => {
|
|||||||
const startTime = dayjs(start_time);
|
const startTime = dayjs(start_time);
|
||||||
const endTime = dayjs(end_time);
|
const endTime = dayjs(end_time);
|
||||||
const dayofWeek = DayOfWeekMap.get(startTime.day());
|
const dayofWeek = DayOfWeekMap.get(startTime.day());
|
||||||
// const gameLength = `${endTime.diff(startTime, "hour")}小时`;
|
|
||||||
const game_length = genGameLength(startTime, endTime);
|
const game_length = genGameLength(startTime, endTime);
|
||||||
let qrCodeUrl = "";
|
let qrCodeUrl = "";
|
||||||
try {
|
try {
|
||||||
@@ -193,7 +194,7 @@ export default forwardRef(({ id, from, detail, userInfo }, ref) => {
|
|||||||
title,
|
title,
|
||||||
locationName: location_name,
|
locationName: location_name,
|
||||||
date: `${startTime.format("M月D日")} (${dayofWeek})`,
|
date: `${startTime.format("M月D日")} (${dayofWeek})`,
|
||||||
time: `${startTime.format("ah")}点 ${game_length}`,
|
time: `${formatGameStartTime(startTime)} ${game_length}`,
|
||||||
qrCodeUrl,
|
qrCodeUrl,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -14,7 +14,11 @@ import WechatLogo from "@/static/detail/wechat_icon.svg";
|
|||||||
import WechatTimeline from "@/static/detail/wechat_timeline.svg";
|
import WechatTimeline from "@/static/detail/wechat_timeline.svg";
|
||||||
import { useUserActions } from "@/store/userStore";
|
import { useUserActions } from "@/store/userStore";
|
||||||
import { DayOfWeekMap } from "../detail/config";
|
import { DayOfWeekMap } from "../detail/config";
|
||||||
import { genNTRPRequirementText } from "@/utils/helper";
|
import {
|
||||||
|
genNTRPRequirementText,
|
||||||
|
genGameLength,
|
||||||
|
formatGameStartTime,
|
||||||
|
} from "@/utils/helper";
|
||||||
import { waitForAuthInit } from "@/utils/authInit";
|
import { waitForAuthInit } from "@/utils/authInit";
|
||||||
import { OSS_BASE } from "@/config/api";
|
import { OSS_BASE } from "@/config/api";
|
||||||
import styles from "./index.module.scss";
|
import styles from "./index.module.scss";
|
||||||
@@ -53,7 +57,7 @@ function SharePoster(props) {
|
|||||||
const startTime = dayjs(start_time);
|
const startTime = dayjs(start_time);
|
||||||
const endTime = dayjs(end_time);
|
const endTime = dayjs(end_time);
|
||||||
const dayofWeek = DayOfWeekMap.get(startTime.day());
|
const dayofWeek = DayOfWeekMap.get(startTime.day());
|
||||||
const gameLength = `${endTime.diff(startTime, "hour")}小时`;
|
const gameLength = genGameLength(startTime, endTime);
|
||||||
Taro.showLoading({ title: "生成中..." });
|
Taro.showLoading({ title: "生成中..." });
|
||||||
const qrCodeUrlRes = await DetailService.getQrCodeUrl({
|
const qrCodeUrlRes = await DetailService.getQrCodeUrl({
|
||||||
page: "game_pages/detail/index",
|
page: "game_pages/detail/index",
|
||||||
@@ -77,7 +81,7 @@ function SharePoster(props) {
|
|||||||
title,
|
title,
|
||||||
locationName: location_name,
|
locationName: location_name,
|
||||||
date: `${startTime.format("M月D日")} (${dayofWeek})`,
|
date: `${startTime.format("M月D日")} (${dayofWeek})`,
|
||||||
time: `${startTime.format("ah")}点 ${gameLength}`,
|
time: `${formatGameStartTime(startTime)} ${gameLength}`,
|
||||||
qrCodeUrl,
|
qrCodeUrl,
|
||||||
});
|
});
|
||||||
Taro.hideLoading();
|
Taro.hideLoading();
|
||||||
|
|||||||
@@ -30,6 +30,18 @@ import DownloadIcon from "@/static/ntrp/ntrp_download.svg";
|
|||||||
import ReTestIcon from "@/static/ntrp/ntrp_re-action.svg";
|
import ReTestIcon from "@/static/ntrp/ntrp_re-action.svg";
|
||||||
import styles from "./index.module.scss";
|
import styles from "./index.module.scss";
|
||||||
|
|
||||||
|
/** 微信小程序码 scene 最长 32 字符 */
|
||||||
|
const WX_SCENE_MAX_LEN = 32;
|
||||||
|
|
||||||
|
/** 分享图/太阳码:r={record_id},落地后在 NtrpEvaluate 归一化为 stage=result&id=&from_share=1 */
|
||||||
|
function buildNtrpShareScene(recordId: string | number): string {
|
||||||
|
const scene = `r=${recordId}`;
|
||||||
|
if (scene.length > WX_SCENE_MAX_LEN) {
|
||||||
|
console.warn("[ntrp-evaluate] share scene exceeds WeChat limit:", scene);
|
||||||
|
}
|
||||||
|
return scene;
|
||||||
|
}
|
||||||
|
|
||||||
const sourceTypeToTextMap = new Map([
|
const sourceTypeToTextMap = new Map([
|
||||||
[EvaluateScene.detail, "继续加入球局"],
|
[EvaluateScene.detail, "继续加入球局"],
|
||||||
[EvaluateScene.publish, "继续发布球局"],
|
[EvaluateScene.publish, "继续发布球局"],
|
||||||
@@ -485,7 +497,8 @@ function Test() {
|
|||||||
|
|
||||||
function Result() {
|
function Result() {
|
||||||
const { params } = useRouter();
|
const { params } = useRouter();
|
||||||
const { id } = params;
|
const { id, from_share } = params;
|
||||||
|
const fromShare = from_share === "1" || from_share === "true";
|
||||||
const userInfo = useUserInfo();
|
const userInfo = useUserInfo();
|
||||||
const { fetchUserInfo, updateUserInfo } = useUserActions();
|
const { fetchUserInfo, updateUserInfo } = useUserActions();
|
||||||
const { type, next, clear } = useEvaluate();
|
const { type, next, clear } = useEvaluate();
|
||||||
@@ -514,13 +527,14 @@ function Result() {
|
|||||||
init();
|
init();
|
||||||
}, [id]);
|
}, [id]);
|
||||||
|
|
||||||
// 获取二维码 - 调用接口生成分享二维码
|
// 获取二维码 - 太阳码携带当次 record_id,扫码进入分享结果页
|
||||||
async function fetchQRCode() {
|
async function fetchQRCode() {
|
||||||
try {
|
try {
|
||||||
// 调用接口生成二维码,分享当前页面
|
const recordId = id != null && id !== "" ? String(id) : "";
|
||||||
|
if (!recordId) return;
|
||||||
const qrCodeUrlRes = await DetailService.getQrCodeUrl({
|
const qrCodeUrlRes = await DetailService.getQrCodeUrl({
|
||||||
page: "other_pages/ntrp-evaluate/index",
|
page: "other_pages/ntrp-evaluate/index",
|
||||||
scene: `stage=${StageType.INTRO}`,
|
scene: buildNtrpShareScene(recordId),
|
||||||
});
|
});
|
||||||
setQrCodeUrl(qrCodeUrlRes.data.ossPath);
|
setQrCodeUrl(qrCodeUrlRes.data.ossPath);
|
||||||
// if (qrCodeUrlRes.code === 0 && qrCodeUrlRes.data?.qr_code_base64) {
|
// if (qrCodeUrlRes.code === 0 && qrCodeUrlRes.data?.qr_code_base64) {
|
||||||
@@ -536,29 +550,57 @@ function Result() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function getResultById() {
|
async function getResultById() {
|
||||||
const res = await evaluateService.getTestResult({ record_id: Number(id) });
|
const recordId = Number(id);
|
||||||
if (res.code === 0) {
|
if (!id || Number.isNaN(recordId) || recordId <= 0) {
|
||||||
setResult(res.data);
|
Taro.showToast({ title: "链接无效", icon: "none" });
|
||||||
|
Taro.redirectTo({
|
||||||
const sortOrder = res.data.sort || [];
|
url: `/other_pages/ntrp-evaluate/index?stage=${StageType.INTRO}`,
|
||||||
const abilities = res.data.radar_data.abilities;
|
});
|
||||||
const sortedKeys = sortOrder.filter((k) => k in abilities);
|
return;
|
||||||
const remainingKeys = Object.keys(abilities).filter(
|
}
|
||||||
(k) => !sortOrder.includes(k),
|
try {
|
||||||
|
const res = await evaluateService.getTestResult(
|
||||||
|
{
|
||||||
|
record_id: recordId,
|
||||||
|
...(fromShare ? { from_share: true } : {}),
|
||||||
|
},
|
||||||
|
fromShare ? { showToast: false } : undefined,
|
||||||
);
|
);
|
||||||
const allKeys = [...sortedKeys, ...remainingKeys];
|
if (res.code === 0) {
|
||||||
let radarData: [string, number][] = allKeys.map((key) => [
|
setResult(res.data);
|
||||||
key,
|
|
||||||
Math.min(
|
const sortOrder = res.data.sort || [];
|
||||||
100,
|
const abilities = res.data.radar_data.abilities;
|
||||||
Math.floor(
|
const sortedKeys = sortOrder.filter((k) => k in abilities);
|
||||||
(abilities[key].current_score / abilities[key].max_score) * 100,
|
const remainingKeys = Object.keys(abilities).filter(
|
||||||
|
(k) => !sortOrder.includes(k),
|
||||||
|
);
|
||||||
|
const allKeys = [...sortedKeys, ...remainingKeys];
|
||||||
|
const nextRadarData: [string, number][] = allKeys.map((key) => [
|
||||||
|
key,
|
||||||
|
Math.min(
|
||||||
|
100,
|
||||||
|
Math.floor(
|
||||||
|
(abilities[key].current_score / abilities[key].max_score) * 100,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
]);
|
||||||
]);
|
setRadarData(nextRadarData);
|
||||||
// 直接使用接口 sort 顺序,不经过 adjustRadarLabels 重新排序
|
if (!fromShare) {
|
||||||
setRadarData(radarData);
|
updateUserLevel(res.data.record_id, res.data.ntrp_level);
|
||||||
updateUserLevel(res.data.record_id, res.data.ntrp_level);
|
}
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
if (fromShare) {
|
||||||
|
Taro.showToast({
|
||||||
|
title: e?.message || "分享已失效或无权查看",
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
Taro.redirectTo({
|
||||||
|
url: `/other_pages/ntrp-evaluate/index?stage=${StageType.INTRO}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 本人结果页失败时 httpService 已 toast,此处不再重复
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -615,10 +657,11 @@ function Result() {
|
|||||||
// 确保二维码已获取,如果没有则重新获取
|
// 确保二维码已获取,如果没有则重新获取
|
||||||
let finalQrCodeUrl = qrCodeUrl;
|
let finalQrCodeUrl = qrCodeUrl;
|
||||||
if (!finalQrCodeUrl) {
|
if (!finalQrCodeUrl) {
|
||||||
// 直接调用接口获取二维码
|
const recordId = id != null && id !== "" ? String(id) : "";
|
||||||
|
if (!recordId) throw new Error("缺少测评记录");
|
||||||
const qrCodeUrlRes = await DetailService.getQrCodeUrl({
|
const qrCodeUrlRes = await DetailService.getQrCodeUrl({
|
||||||
page: "other_pages/ntrp-evaluate/index",
|
page: "other_pages/ntrp-evaluate/index",
|
||||||
scene: `stage=${StageType.INTRO}`,
|
scene: buildNtrpShareScene(recordId),
|
||||||
});
|
});
|
||||||
finalQrCodeUrl = qrCodeUrlRes.data.ossPath;
|
finalQrCodeUrl = qrCodeUrlRes.data.ossPath;
|
||||||
// if (qrCodeUrlRes.code === 0 && qrCodeUrlRes.data?.qr_code_base64) {
|
// if (qrCodeUrlRes.code === 0 && qrCodeUrlRes.data?.qr_code_base64) {
|
||||||
@@ -628,16 +671,20 @@ function Result() {
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用 RadarV2 的 generateFullImage 方法生成完整图片
|
const posterNickname = fromShare
|
||||||
const userNickname = (userInfo as any)?.nickname;
|
? result?.sharer_nickname || "好友"
|
||||||
const titleText = userNickname
|
: (userInfo as any)?.nickname;
|
||||||
? `${userNickname}的 NTRP 测试结果为`
|
const titleText = posterNickname
|
||||||
|
? `${posterNickname}的 NTRP 测试结果为`
|
||||||
: "你的 NTRP 测试结果为";
|
: "你的 NTRP 测试结果为";
|
||||||
|
const posterAvatar = fromShare
|
||||||
|
? result?.sharer_avatar_url || (userInfo as any)?.avatar_url
|
||||||
|
: (userInfo as any)?.avatar_url;
|
||||||
const imageUrl = await radarV2Ref.current?.generateFullImage({
|
const imageUrl = await radarV2Ref.current?.generateFullImage({
|
||||||
title: titleText,
|
title: titleText,
|
||||||
ntrpLevel: result?.ntrp_level,
|
ntrpLevel: result?.ntrp_level,
|
||||||
levelDescription: result?.level_description,
|
levelDescription: result?.level_description,
|
||||||
avatarUrl: (userInfo as any)?.avatar_url,
|
avatarUrl: posterAvatar,
|
||||||
qrCodeUrl: finalQrCodeUrl,
|
qrCodeUrl: finalQrCodeUrl,
|
||||||
bottomText: "长按识别二维码,快来加入,有你就有场!",
|
bottomText: "长按识别二维码,快来加入,有你就有场!",
|
||||||
width: 750, // 设计稿宽度
|
width: 750, // 设计稿宽度
|
||||||
@@ -651,8 +698,7 @@ function Result() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleSaveImage() {
|
async function handleSaveImage() {
|
||||||
console.log(userInfo);
|
if (!fromShare && !userInfo?.phone) {
|
||||||
if (!userInfo?.phone) {
|
|
||||||
handleAuth();
|
handleAuth();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -691,12 +737,18 @@ function Result() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
useShareAppMessage(async (res) => {
|
useShareAppMessage(async () => {
|
||||||
console.log("res", result);
|
const rid = result?.record_id ?? Number(id);
|
||||||
|
const sharePath =
|
||||||
|
rid && !Number.isNaN(Number(rid))
|
||||||
|
? `/other_pages/ntrp-evaluate/index?stage=${StageType.RESULT}&id=${rid}&from_share=1`
|
||||||
|
: `/other_pages/ntrp-evaluate/index?stage=${StageType.INTRO}`;
|
||||||
return {
|
return {
|
||||||
title: "来测一测你的NTRP等级吧",
|
title: result?.ntrp_level
|
||||||
|
? `来看看 NTRP ${formatNtrpDisplay(result.ntrp_level)} 的测评结果`
|
||||||
|
: "来测一测你的NTRP等级吧",
|
||||||
imageUrl: result?.level_img || undefined,
|
imageUrl: result?.level_img || undefined,
|
||||||
path: `/other_pages/ntrp-evaluate/index?stage=${StageType.INTRO}`,
|
path: sharePath,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -714,6 +766,18 @@ function Result() {
|
|||||||
|
|
||||||
function handleGo() {}
|
function handleGo() {}
|
||||||
|
|
||||||
|
const cardTitleText = fromShare
|
||||||
|
? result?.sharer_nickname
|
||||||
|
? `${result.sharer_nickname}的 NTRP 测试结果为`
|
||||||
|
: "好友分享的 NTRP 测试结果为"
|
||||||
|
: (userInfo as any)?.nickname
|
||||||
|
? `${(userInfo as any).nickname}的 NTRP 测试结果为`
|
||||||
|
: "你的 NTRP 测试结果为";
|
||||||
|
|
||||||
|
const cardAvatarUrl = fromShare
|
||||||
|
? result?.sharer_avatar_url || ""
|
||||||
|
: userInfo?.avatar_url || "";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View className={styles.resultContainer}>
|
<View className={styles.resultContainer}>
|
||||||
<CommonGuideBar />
|
<CommonGuideBar />
|
||||||
@@ -727,7 +791,7 @@ function Result() {
|
|||||||
<View className={styles.avatar}>
|
<View className={styles.avatar}>
|
||||||
<Image
|
<Image
|
||||||
className={styles.avatarUrl}
|
className={styles.avatarUrl}
|
||||||
src={userInfo?.avatar_url || ""}
|
src={cardAvatarUrl}
|
||||||
mode="aspectFill"
|
mode="aspectFill"
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
@@ -742,11 +806,7 @@ function Result() {
|
|||||||
</View>
|
</View>
|
||||||
<View className={styles.desc}>
|
<View className={styles.desc}>
|
||||||
<View className={styles.tip}>
|
<View className={styles.tip}>
|
||||||
<Text>
|
<Text>{cardTitleText}</Text>
|
||||||
{(userInfo as any)?.nickname
|
|
||||||
? `${(userInfo as any).nickname}的 NTRP 测试结果为`
|
|
||||||
: "你的 NTRP 测试结果为"}
|
|
||||||
</Text>
|
|
||||||
</View>
|
</View>
|
||||||
<View className={styles.levelWrap}>
|
<View className={styles.levelWrap}>
|
||||||
<Text>NTRP</Text>
|
<Text>NTRP</Text>
|
||||||
@@ -767,7 +827,7 @@ function Result() {
|
|||||||
<Text>重新测试</Text>
|
<Text>重新测试</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
{userInfo?.phone ? (
|
{userInfo?.phone && !fromShare ? (
|
||||||
<View className={styles.updateTip}>
|
<View className={styles.updateTip}>
|
||||||
<Text>
|
<Text>
|
||||||
你的 NTRP 水平已更新为{" "}
|
你的 NTRP 水平已更新为{" "}
|
||||||
@@ -775,11 +835,17 @@ function Result() {
|
|||||||
</Text>
|
</Text>
|
||||||
<Text className={styles.grayTip}>(可在个人信息中修改)</Text>
|
<Text className={styles.grayTip}>(可在个人信息中修改)</Text>
|
||||||
</View>
|
</View>
|
||||||
) : (
|
) : !userInfo?.phone ? (
|
||||||
<View className={styles.updateTip}>
|
<View className={styles.updateTip}>
|
||||||
<Text>登录「有场」小程序,查看匹配你的球局</Text>
|
<Text>登录「有场」小程序,查看匹配你的球局</Text>
|
||||||
</View>
|
</View>
|
||||||
)}
|
) : fromShare ? (
|
||||||
|
<View className={styles.updateTip}>
|
||||||
|
<Text className={styles.grayTip}>
|
||||||
|
以上为好友分享结果,去测一测你的 NTRP 吧
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
) : null}
|
||||||
<View className={styles.actions}>
|
<View className={styles.actions}>
|
||||||
<View className={styles.viewGame} onClick={handleGoon}>
|
<View className={styles.viewGame} onClick={handleGoon}>
|
||||||
<Button className={styles.viewGameBtn}>
|
<Button className={styles.viewGameBtn}>
|
||||||
@@ -821,14 +887,10 @@ function Result() {
|
|||||||
<RadarChartV2
|
<RadarChartV2
|
||||||
ref={radarV2Ref}
|
ref={radarV2Ref}
|
||||||
data={radarData}
|
data={radarData}
|
||||||
title={
|
title={cardTitleText}
|
||||||
(userInfo as any)?.nickname
|
|
||||||
? `${(userInfo as any).nickname}的 NTRP 测试结果为`
|
|
||||||
: "你的 NTRP 测试结果为"
|
|
||||||
}
|
|
||||||
ntrpLevel={result?.ntrp_level}
|
ntrpLevel={result?.ntrp_level}
|
||||||
levelDescription={result?.level_description}
|
levelDescription={result?.level_description}
|
||||||
avatarUrl={(userInfo as any)?.avatar_url}
|
avatarUrl={cardAvatarUrl}
|
||||||
qrCodeUrl={qrCodeUrl}
|
qrCodeUrl={qrCodeUrl}
|
||||||
bottomText="长按识别二维码,快来加入,有你就有场!"
|
bottomText="长按识别二维码,快来加入,有你就有场!"
|
||||||
/>
|
/>
|
||||||
@@ -844,9 +906,21 @@ const ComponentsMap = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function NtrpEvaluate() {
|
function NtrpEvaluate() {
|
||||||
// const { updateUserInfo } = useUserActions();
|
|
||||||
const { params } = useRouter();
|
const { params } = useRouter();
|
||||||
// const { redirect } = params;
|
|
||||||
|
// 太阳码 scene 仅支持 32 字符,用 r=id 落地后归一化为结果页 + from_share
|
||||||
|
useEffect(() => {
|
||||||
|
const r = params.r;
|
||||||
|
if (r && !params.stage) {
|
||||||
|
Taro.redirectTo({
|
||||||
|
url: `/other_pages/ntrp-evaluate/index?stage=${StageType.RESULT}&id=${encodeURIComponent(String(r))}&from_share=1`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [params.r, params.stage]);
|
||||||
|
|
||||||
|
if (params.r && !params.stage) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const stage = params.stage as StageType;
|
const stage = params.stage as StageType;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import httpService from "./httpService";
|
import httpService from "./httpService";
|
||||||
import type { ApiResponse } from "./httpService";
|
import type { ApiResponse, RequestConfig } from "./httpService";
|
||||||
|
|
||||||
export enum StageType {
|
export enum StageType {
|
||||||
INTRO = "intro",
|
INTRO = "intro",
|
||||||
@@ -59,6 +59,9 @@ export interface TestResultData {
|
|||||||
radar_data: RadarData;
|
radar_data: RadarData;
|
||||||
answers: Answer[];
|
answers: Answer[];
|
||||||
sort?: string[]; // 雷达图能力项排序,如 ["正手球质", "正手控制", ...]
|
sort?: string[]; // 雷达图能力项排序,如 ["正手球质", "正手控制", ...]
|
||||||
|
/** 分享查看时后端可返回测评归属用户,用于展示头像昵称 */
|
||||||
|
sharer_nickname?: string;
|
||||||
|
sharer_avatar_url?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 单条测试记录
|
// 单条测试记录
|
||||||
@@ -118,11 +121,15 @@ class EvaluateService {
|
|||||||
return httpService.post("/ntrp/history", {}, { showLoading: true });
|
return httpService.post("/ntrp/history", {}, { showLoading: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取测试详情
|
// 获取测试详情(from_share 为 true 时表示扫码/分享查看他人当次结果,需后端 /ntrp/detail 按 record_id 放行只读)
|
||||||
async getTestResult(req: {
|
async getTestResult(
|
||||||
record_id: number;
|
req: { record_id: number; from_share?: boolean },
|
||||||
}): Promise<ApiResponse<TestResultData>> {
|
httpConfig?: Partial<RequestConfig>,
|
||||||
return httpService.post("/ntrp/detail", req, { showLoading: true });
|
): Promise<ApiResponse<TestResultData>> {
|
||||||
|
return httpService.post("/ntrp/detail", req, {
|
||||||
|
showLoading: true,
|
||||||
|
...httpConfig,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取最后一次(最新)测试结果
|
// 获取最后一次(最新)测试结果
|
||||||
|
|||||||
@@ -164,3 +164,12 @@ export function genGameLength(startTime: Dayjs, endTime: Dayjs) {
|
|||||||
// 保留一位小数,去除末尾的0
|
// 保留一位小数,去除末尾的0
|
||||||
return `${parseFloat(totalHours.toFixed(1))}小时`;
|
return `${parseFloat(totalHours.toFixed(1))}小时`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function formatGameStartTime(startTime: Dayjs) {
|
||||||
|
if (!startTime || !startTime.isValid()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
const hour = startTime.hour();
|
||||||
|
const minute = startTime.minute();
|
||||||
|
return minute === 0 ? `${hour}点` : `${hour}点${minute}分`;
|
||||||
|
}
|
||||||
|
|||||||
@@ -115,16 +115,25 @@ function with_cache_bust(url: string): string {
|
|||||||
// 工具函数 - OffscreenCanvas 下加载图片(使用 offscreen.createImage)
|
// 工具函数 - OffscreenCanvas 下加载图片(使用 offscreen.createImage)
|
||||||
const loadImage = (src: string): Promise<any> => {
|
const loadImage = (src: string): Promise<any> => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
let timer: ReturnType<typeof setTimeout> | null = null
|
||||||
try {
|
try {
|
||||||
const off = runtime.offscreen
|
const off = runtime.offscreen
|
||||||
if (!off || typeof off.createImage !== 'function') {
|
if (!off || typeof off.createImage !== 'function') {
|
||||||
throw new Error('OffscreenCanvas 未初始化或不支持 createImage')
|
throw new Error('OffscreenCanvas 未初始化或不支持 createImage')
|
||||||
}
|
}
|
||||||
const img = off.createImage()
|
const img = off.createImage()
|
||||||
img.onload = () => resolve(img)
|
timer = setTimeout(() => reject(new Error(`图片加载超时: ${src}`)), 8000)
|
||||||
img.onerror = reject
|
img.onload = () => {
|
||||||
|
if (timer) clearTimeout(timer)
|
||||||
|
resolve(img)
|
||||||
|
}
|
||||||
|
img.onerror = (e: any) => {
|
||||||
|
if (timer) clearTimeout(timer)
|
||||||
|
reject(e)
|
||||||
|
}
|
||||||
img.src = with_cache_bust(src)
|
img.src = with_cache_bust(src)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
if (timer) clearTimeout(timer)
|
||||||
reject(e)
|
reject(e)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -413,7 +422,7 @@ const drawShareCard = async (ctx: any, data: ShareCardData, offscreen: any): Pro
|
|||||||
ctx.restore()
|
ctx.restore()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// 如果头像加载失败,绘制默认头像
|
// 如果头像加载失败,绘制默认头像
|
||||||
ctx.setFillStyle('#CCCCCC')
|
ctx.fillStyle = '#CCCCCC'
|
||||||
ctx.beginPath()
|
ctx.beginPath()
|
||||||
ctx.arc(avatarX + avatarSize / 2, avatarY + avatarSize / 2, avatarSize / 2, 0, 2 * Math.PI)
|
ctx.arc(avatarX + avatarSize / 2, avatarY + avatarSize / 2, avatarSize / 2, 0, 2 * Math.PI)
|
||||||
ctx.fill()
|
ctx.fill()
|
||||||
@@ -594,10 +603,12 @@ export async function generateShareImage(data: ShareCardData): Promise<string> {
|
|||||||
// 记录到 runtime(供 loadImage 使用)
|
// 记录到 runtime(供 loadImage 使用)
|
||||||
runtime.offscreen = offscreen
|
runtime.offscreen = offscreen
|
||||||
isDrawing = true
|
isDrawing = true
|
||||||
|
try {
|
||||||
const imagePath = await drawShareCard(ctx, data, offscreen)
|
const imagePath = await drawShareCard(ctx, data, offscreen)
|
||||||
isDrawing = false
|
return imagePath
|
||||||
return imagePath
|
} finally {
|
||||||
|
isDrawing = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default generateShareImage
|
export default generateShareImage
|
||||||
|
|||||||
Reference in New Issue
Block a user