Compare commits
16 Commits
fix/jgh/03
...
8090d679b4
| Author | SHA1 | Date | |
|---|---|---|---|
| 8090d679b4 | |||
| 4965d6c40e | |||
| 56fb3ade00 | |||
| 8c6fb1190e | |||
|
|
4dc8e84f5c | ||
| b84c3bb409 | |||
| fa41842e75 | |||
| 3bbb64d58c | |||
| 58cf46e93d | |||
|
|
8004b26bd1 | ||
|
|
98baa371ee | ||
|
|
f87859da0e | ||
| d3390d5e81 | |||
|
|
883ce3c2c4 | ||
|
|
63bcf6fe86 | ||
| a68da08c85 |
@@ -32,36 +32,52 @@ const FilterPopup = (props: FilterPopupProps) => {
|
||||
const { timeBubbleData, gamesNum } = store;
|
||||
|
||||
/**
|
||||
* @description 处理字典选项
|
||||
* @param dictionaryValue 字典选项
|
||||
* @returns 选项列表
|
||||
* @description 日期排序
|
||||
* @param a 日期字符串
|
||||
* @param b 日期字符串
|
||||
* @returns 日期差值
|
||||
*/
|
||||
// const [selectedDates, setSelectedDates] = useState<String[]>([])
|
||||
const sortByDate = (a: string, b: string) => {
|
||||
return new Date(a).getTime() - new Date(b).getTime();
|
||||
}
|
||||
|
||||
const handleDateChange = (dates: Date[]) => {
|
||||
let times: String[] = [];
|
||||
if (dates.length > 1) {
|
||||
times = [dayjs(dates[0]).format('YYYY-MM-DD'), dayjs(dates[dates.length - 1]).format('YYYY-MM-DD')]
|
||||
onChange({
|
||||
'dateRange': times,
|
||||
})
|
||||
// ================================ 日期处理 ================================
|
||||
// 默认是是当前日期为开始日期,结束日期为当前日期 + 30天
|
||||
const defaultDateRange = [dayjs().format('YYYY-MM-DD'), dayjs().add(1, 'M').format('YYYY-MM-DD')];
|
||||
// 处理空数组的情况
|
||||
if (!dates.length) {
|
||||
onChange({ dateRange: defaultDateRange });
|
||||
return;
|
||||
}
|
||||
if (Array.isArray(dates)) {
|
||||
|
||||
const currentDay = dayjs(dates[0]).format('YYYY-MM-DD');
|
||||
if (filterOptions.dateRange.length === 0 || filterOptions.dateRange.length === 2) {
|
||||
times.push(currentDay);
|
||||
// 处理多日期范围选择(超过1个日期)
|
||||
if (dates.length > 1) {
|
||||
const dateRange = [
|
||||
dayjs(dates[0]).format('YYYY-MM-DD'),
|
||||
dayjs(dates[dates.length - 1]).format('YYYY-MM-DD')
|
||||
];
|
||||
onChange({ dateRange });
|
||||
return;
|
||||
}
|
||||
// 处理单个日期选择
|
||||
const currentFilterOptionsDateRange = Array.isArray(filterOptions?.dateRange)
|
||||
? filterOptions.dateRange
|
||||
: defaultDateRange;
|
||||
// 当前选择的日期
|
||||
const currentDay = dayjs(dates?.[0]).format('YYYY-MM-DD');
|
||||
// 当 dates 每次只返回单个日期时,使用已选范围判断是“第一次点”还是“第二次点”
|
||||
let dateRange: string[];
|
||||
if (
|
||||
currentFilterOptionsDateRange.length === 2 &&
|
||||
currentFilterOptionsDateRange?.[0] === currentFilterOptionsDateRange?.[1]
|
||||
) {
|
||||
// 已是单日,点击当前日期扩展为日期范围
|
||||
dateRange = [currentFilterOptionsDateRange[0], currentDay].sort(sortByDate);
|
||||
} else {
|
||||
times = [...filterOptions.dateRange, currentDay].sort(
|
||||
(a, b) => new Date(a).getTime() - new Date(b).getTime()
|
||||
)
|
||||
// 默认区间/已选区间/异常状态,点击当前日期统一收敛为单日
|
||||
dateRange = [currentDay, currentDay];
|
||||
}
|
||||
}
|
||||
|
||||
onChange({
|
||||
'dateRange': times,
|
||||
})
|
||||
onChange({ dateRange });
|
||||
}
|
||||
|
||||
const handleOptions = (dictionaryValue: []) => {
|
||||
|
||||
@@ -1,41 +1,13 @@
|
||||
import Taro from "@tarojs/taro";
|
||||
import dayjs, { Dayjs } from "dayjs";
|
||||
import "dayjs/locale/zh-cn";
|
||||
import { calculateDistance } from "@/utils";
|
||||
import { calculateDistance, genGameLength } from "@/utils";
|
||||
import { View, Image, Text, Map } from "@tarojs/components";
|
||||
import img from "@/config/images";
|
||||
import styles from "./index.module.scss";
|
||||
|
||||
dayjs.locale("zh-cn");
|
||||
|
||||
function genGameLength(startTime: Dayjs, endTime: Dayjs) {
|
||||
if (!startTime || !endTime) {
|
||||
return "";
|
||||
}
|
||||
const totalMinutes = endTime.diff(startTime, "minute");
|
||||
const totalHours = totalMinutes / 60;
|
||||
|
||||
if (totalHours >= 24) {
|
||||
const days = Math.floor(totalHours / 24);
|
||||
const remainingHours = totalHours % 24;
|
||||
|
||||
if (remainingHours === 0) {
|
||||
return `${days}天`;
|
||||
}
|
||||
|
||||
// 保留一位小数
|
||||
const displayHours = parseFloat(remainingHours.toFixed(1));
|
||||
return `${days}天${displayHours}小时`;
|
||||
}
|
||||
|
||||
// 如果是整数小时,不显示小数点
|
||||
if (Number.isInteger(totalHours)) {
|
||||
return `${totalHours}小时`;
|
||||
}
|
||||
// 保留一位小数,去除末尾的0
|
||||
return `${parseFloat(totalHours.toFixed(1))}小时`;
|
||||
}
|
||||
|
||||
function genGameRange(startTime: Dayjs, endTime: Dayjs) {
|
||||
if (!startTime || !endTime) {
|
||||
return "";
|
||||
|
||||
@@ -189,7 +189,16 @@
|
||||
font-weight: 600;
|
||||
line-height: 24px; /* 150% */
|
||||
|
||||
&-text {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&-arrow {
|
||||
flex: 0 0 12px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
@@ -86,12 +86,12 @@ export default function OrganizerInfo(props) {
|
||||
await LoginService.followUser(id);
|
||||
}
|
||||
onUpdateUserInfo();
|
||||
Taro.showToast({
|
||||
(Taro as any).showToast({
|
||||
title: `${nickname} ${follow ? "已取消关注" : "已关注"}`,
|
||||
icon: "success",
|
||||
});
|
||||
} catch (e) {
|
||||
Taro.showToast({
|
||||
(Taro as any).showToast({
|
||||
title: `${nickname} ${follow ? "取消关注失败" : "关注失败"}`,
|
||||
icon: "error",
|
||||
});
|
||||
@@ -195,7 +195,11 @@ export default function OrganizerInfo(props) {
|
||||
>
|
||||
{/* game title */}
|
||||
<View className={styles["recommend-games-list-item-title"]}>
|
||||
<Text>{game.title}</Text>
|
||||
<Text
|
||||
className={styles["recommend-games-list-item-title-text"]}
|
||||
>
|
||||
{game.title}
|
||||
</Text>
|
||||
<Image
|
||||
className={
|
||||
styles["recommend-games-list-item-title-arrow"]
|
||||
|
||||
@@ -489,7 +489,7 @@ export default function Participants(props) {
|
||||
<Text
|
||||
className={styles["participants-list-item-level"]}
|
||||
>
|
||||
{displayNtrp}
|
||||
NTRP {displayNtrp}
|
||||
</Text>
|
||||
<Text className={styles["participants-list-item-role"]}>
|
||||
{role}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { forwardRef, useState, useEffect, useImperativeHandle } from "react";
|
||||
import { View, Button, Image, Text } from "@tarojs/components";
|
||||
import Taro, { useShareAppMessage } from "@tarojs/taro";
|
||||
import dayjs from "dayjs";
|
||||
import dayjs, { Dayjs } from "dayjs";
|
||||
import "dayjs/locale/zh-cn";
|
||||
import classnames from "classnames";
|
||||
import { generateShareImage } from "@/utils";
|
||||
@@ -12,7 +12,12 @@ import WechatLogo from "@/static/detail/wechat_icon.svg";
|
||||
// import WechatTimeline from "@/static/detail/wechat_timeline.svg";
|
||||
import LinkIcon from "@/static/detail/link.svg";
|
||||
import CrossIcon from "@/static/detail/cross.svg";
|
||||
import { genNTRPRequirementText, navto } from "@/utils/helper";
|
||||
import {
|
||||
genNTRPRequirementText,
|
||||
navto,
|
||||
genGameLength,
|
||||
formatGameStartTime,
|
||||
} from "@/utils/helper";
|
||||
import { waitForAuthInit } from "@/utils/authInit";
|
||||
import { useUserActions } from "@/store/userStore";
|
||||
import { OSS_BASE } from "@/config/api";
|
||||
@@ -28,7 +33,19 @@ export default forwardRef(({ id, from, detail, userInfo }, ref) => {
|
||||
const [shareImageUrl, setShareImageUrl] = useState("");
|
||||
const { fetchUserInfo } = useUserActions();
|
||||
|
||||
async function ensureUserInfo() {
|
||||
if (userInfo?.avatar_url && userInfo?.nickname) {
|
||||
return userInfo;
|
||||
}
|
||||
const fetchedUserInfo = await fetchUserInfo();
|
||||
return {
|
||||
avatar_url: fetchedUserInfo?.avatar_url || userInfo?.avatar_url || "",
|
||||
nickname: fetchedUserInfo?.nickname || userInfo?.nickname || "",
|
||||
};
|
||||
}
|
||||
|
||||
const publishFlag = from === "publish";
|
||||
|
||||
// const posterRef = useRef();
|
||||
const { max_participants, participant_count } = detail || {};
|
||||
|
||||
@@ -50,6 +67,16 @@ export default forwardRef(({ id, from, detail, userInfo }, ref) => {
|
||||
withShareTicket: false, // 是否需要返回 shareTicket
|
||||
isUpdatableMessage: true, // 是否是动态消息(需要服务端配置过模版)
|
||||
activityId: res.data.activity_id, // 动态消息的活动 id
|
||||
templateInfo: {
|
||||
parameterList: [
|
||||
{
|
||||
name: "member_count",
|
||||
value: (participant_count ?? 0).toString(),
|
||||
},
|
||||
{ name: "room_limit", value: (max_participants ?? 0).toString() },
|
||||
],
|
||||
templateId: "666F374D69D16C932E45D7E7D9F10CEF6177F5F5",
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -85,28 +112,34 @@ export default forwardRef(({ id, from, detail, userInfo }, ref) => {
|
||||
const startTime = dayjs(start_time);
|
||||
const endTime = dayjs(end_time);
|
||||
const dayofWeek = DayOfWeekMap.get(startTime.day());
|
||||
const gameLength = `${endTime.diff(startTime, "hour")}小时`;
|
||||
console.log(userInfo, "userInfo");
|
||||
const gameLength = genGameLength(startTime, endTime);
|
||||
const currentUserInfo = await ensureUserInfo();
|
||||
try {
|
||||
const url = await generateShareImage({
|
||||
userAvatar: userInfo.avatar_url,
|
||||
userNickname: userInfo.nickname,
|
||||
userAvatar: currentUserInfo.avatar_url,
|
||||
userNickname: currentUserInfo.nickname,
|
||||
gameType: play_type,
|
||||
skillLevel: `NTRP ${genNTRPRequirementText(
|
||||
skill_level_min,
|
||||
skill_level_max,
|
||||
)}`,
|
||||
gameDate: `${startTime.format("M月D日")} (${dayofWeek})`,
|
||||
gameTime: `${startTime.format("ah")}点 ${gameLength}`,
|
||||
gameTime: `${formatGameStartTime(startTime)} ${gameLength}`,
|
||||
venueName: location_name,
|
||||
venueImages: image_list ? image_list : [],
|
||||
});
|
||||
if (!url) {
|
||||
throw new Error("生成分享图片失败,URL 为空");
|
||||
}
|
||||
return url;
|
||||
} catch (e) {
|
||||
console.error("生成分享卡片失败", e);
|
||||
return `${OSS_BASE}/system/game_dou_di_tu.png`;
|
||||
}
|
||||
}
|
||||
|
||||
useShareAppMessage(async (res) => {
|
||||
await changeMessageType();
|
||||
const url = await generateShareImageUrl();
|
||||
// console.log(res, "res");
|
||||
useShareAppMessage(async () => {
|
||||
const url = shareImageUrl || (await generateShareImageUrl());
|
||||
return {
|
||||
title: detail.title,
|
||||
imageUrl: url,
|
||||
@@ -128,12 +161,12 @@ export default forwardRef(({ id, from, detail, userInfo }, ref) => {
|
||||
} = detail || {};
|
||||
// 先等待静默登录完成
|
||||
await waitForAuthInit();
|
||||
const userInfo = await fetchUserInfo();
|
||||
const { avatar_url, nickname } = userInfo;
|
||||
const currentUserInfo = await ensureUserInfo();
|
||||
const { avatar_url, nickname } = currentUserInfo;
|
||||
const startTime = dayjs(start_time);
|
||||
const endTime = dayjs(end_time);
|
||||
const dayofWeek = DayOfWeekMap.get(startTime.day());
|
||||
const gameLength = `${endTime.diff(startTime, "hour")}小时`;
|
||||
const game_length = genGameLength(startTime, endTime);
|
||||
let qrCodeUrl = "";
|
||||
try {
|
||||
const qrCodeUrlRes = await DetailService.getQrCodeUrl({
|
||||
@@ -161,7 +194,7 @@ export default forwardRef(({ id, from, detail, userInfo }, ref) => {
|
||||
title,
|
||||
locationName: location_name,
|
||||
date: `${startTime.format("M月D日")} (${dayofWeek})`,
|
||||
time: `${startTime.format("ah")}点 ${gameLength}`,
|
||||
time: `${formatGameStartTime(startTime)} ${game_length}`,
|
||||
qrCodeUrl,
|
||||
});
|
||||
} catch (e) {
|
||||
|
||||
@@ -24,12 +24,12 @@ function isFull(counts) {
|
||||
} = counts;
|
||||
|
||||
if (
|
||||
max_players === current_players &&
|
||||
current_players >= max_players &&
|
||||
is_substitute_supported === IsSubstituteSupported.NOTSUPPORT
|
||||
) {
|
||||
return true;
|
||||
} else if (
|
||||
max_players === current_players &&
|
||||
current_players >= max_players &&
|
||||
is_substitute_supported === IsSubstituteSupported.SUPPORT
|
||||
) {
|
||||
return max_substitute_players === current_substitute_count;
|
||||
@@ -45,7 +45,7 @@ function RmbIcon() {
|
||||
function matchNtrpRequestment(
|
||||
target?: string,
|
||||
min?: string,
|
||||
max?: string
|
||||
max?: string,
|
||||
): boolean {
|
||||
// 目标值为空或 undefined
|
||||
if (!target?.trim()) return true;
|
||||
@@ -123,7 +123,7 @@ export default function StickyButton(props) {
|
||||
|
||||
Taro.navigateTo({
|
||||
url: `/login_pages/index/index?redirect=${encodeURIComponent(
|
||||
fullPath
|
||||
fullPath,
|
||||
)}`,
|
||||
});
|
||||
}
|
||||
@@ -138,7 +138,7 @@ export default function StickyButton(props) {
|
||||
const matchNtrpReq = matchNtrpRequestment(
|
||||
ntrp_level,
|
||||
skill_level_min,
|
||||
skill_level_max
|
||||
skill_level_max,
|
||||
);
|
||||
|
||||
const gameManageRef = useRef();
|
||||
@@ -173,7 +173,7 @@ export default function StickyButton(props) {
|
||||
}, [getCommentCount]);
|
||||
|
||||
function generateTextAndAction(
|
||||
user_action_status: null | { [key: string]: boolean }
|
||||
user_action_status: null | { [key: string]: boolean },
|
||||
):
|
||||
| undefined
|
||||
| { text: string | React.FC; action?: () => void; available?: boolean } {
|
||||
@@ -271,7 +271,7 @@ export default function StickyButton(props) {
|
||||
const res = await OrderService.getUnpaidOrder(id);
|
||||
if (res.code === 0) {
|
||||
navto(
|
||||
`/order_pages/orderDetail/index?id=${res.data.order_info.order_id}`
|
||||
`/order_pages/orderDetail/index?id=${res.data.order_info.order_id}`,
|
||||
);
|
||||
}
|
||||
}),
|
||||
@@ -387,7 +387,7 @@ export default function StickyButton(props) {
|
||||
<View
|
||||
className={classnames(
|
||||
styles["detail-main-action"],
|
||||
available ? "" : styles.disabled
|
||||
available ? "" : styles.disabled,
|
||||
)}
|
||||
>
|
||||
<View
|
||||
|
||||
@@ -14,7 +14,11 @@ import WechatLogo from "@/static/detail/wechat_icon.svg";
|
||||
import WechatTimeline from "@/static/detail/wechat_timeline.svg";
|
||||
import { useUserActions } from "@/store/userStore";
|
||||
import { DayOfWeekMap } from "../detail/config";
|
||||
import { genNTRPRequirementText } from "@/utils/helper";
|
||||
import {
|
||||
genNTRPRequirementText,
|
||||
genGameLength,
|
||||
formatGameStartTime,
|
||||
} from "@/utils/helper";
|
||||
import { waitForAuthInit } from "@/utils/authInit";
|
||||
import { OSS_BASE } from "@/config/api";
|
||||
import styles from "./index.module.scss";
|
||||
@@ -53,7 +57,7 @@ function SharePoster(props) {
|
||||
const startTime = dayjs(start_time);
|
||||
const endTime = dayjs(end_time);
|
||||
const dayofWeek = DayOfWeekMap.get(startTime.day());
|
||||
const gameLength = `${endTime.diff(startTime, "hour")}小时`;
|
||||
const gameLength = genGameLength(startTime, endTime);
|
||||
Taro.showLoading({ title: "生成中..." });
|
||||
const qrCodeUrlRes = await DetailService.getQrCodeUrl({
|
||||
page: "game_pages/detail/index",
|
||||
@@ -77,7 +81,7 @@ function SharePoster(props) {
|
||||
title,
|
||||
locationName: location_name,
|
||||
date: `${startTime.format("M月D日")} (${dayofWeek})`,
|
||||
time: `${startTime.format("ah")}点 ${gameLength}`,
|
||||
time: `${formatGameStartTime(startTime)} ${gameLength}`,
|
||||
qrCodeUrl,
|
||||
});
|
||||
Taro.hideLoading();
|
||||
|
||||
@@ -22,3 +22,135 @@ export const DECLAIMER = `
|
||||
发起人临时失联/爽约;发起人恶意删除队员,GO!支持全额退款
|
||||
参与者爽约不通知,不可退款但鼓励用户评分机制中反馈,平台将限制其部分功能使用(如发起权限、报名权限等)。
|
||||
`;
|
||||
|
||||
interface RegInsChildTipType {
|
||||
text: string
|
||||
strong?: boolean
|
||||
}
|
||||
|
||||
interface RegInsChildTableType {
|
||||
refundApplicationTime: string
|
||||
participantRefundableAmount: string
|
||||
liquidatedDamages: string
|
||||
}
|
||||
|
||||
interface RegInsChildType {
|
||||
title: string
|
||||
desc: string
|
||||
table?: RegInsChildTableType[]
|
||||
tips: RegInsChildTipType[]
|
||||
}
|
||||
|
||||
interface RegInsType {
|
||||
title: string,
|
||||
desc: string,
|
||||
children: RegInsChildType[]
|
||||
}
|
||||
|
||||
export const RegistrationInstructions: RegInsType = {
|
||||
title: '报名须知',
|
||||
desc: '请在确认支付前仔细阅读以下内容,完成支付即视为您已同意本须知全部内容。',
|
||||
children: [
|
||||
{
|
||||
title: '一、退款规则',
|
||||
desc: '',
|
||||
table: [
|
||||
{
|
||||
refundApplicationTime: '申请退款时间',
|
||||
participantRefundableAmount: '参与者可退',
|
||||
liquidatedDamages: '违约金',
|
||||
},
|
||||
{
|
||||
refundApplicationTime: '活动开始前24小时',
|
||||
participantRefundableAmount: '报名费 100%',
|
||||
liquidatedDamages: '无',
|
||||
},
|
||||
{
|
||||
refundApplicationTime: '活动开始前12~24小时',
|
||||
participantRefundableAmount: '报名费 50%',
|
||||
liquidatedDamages: '报名费 50%',
|
||||
},
|
||||
{
|
||||
refundApplicationTime: '活动开始前12小时内',
|
||||
participantRefundableAmount: '报名费 20%',
|
||||
liquidatedDamages: '报名费 80%',
|
||||
},
|
||||
{
|
||||
refundApplicationTime: '未申请 / 直接缺席',
|
||||
participantRefundableAmount: '0%',
|
||||
liquidatedDamages: '视为放弃,全归组织者',
|
||||
},
|
||||
],
|
||||
tips: [
|
||||
{
|
||||
text: '以上时间节点以提交申请时间为准,非活动开始时间;',
|
||||
strong: false,
|
||||
},
|
||||
{
|
||||
text: '退款申请入口:活动详情页 > 退出活动',
|
||||
},
|
||||
{
|
||||
text: '退款原路退回至微信支付账户,到账时间 1~5 个工作日;',
|
||||
},
|
||||
{
|
||||
text: '违约金由组织者(95%)与平台(5%)按比例分配。其中组织者所得部分用于补偿其因人数临时变动产生的场地费损失,平台所得部分用于覆盖违约事务的处理成本;',
|
||||
},
|
||||
{
|
||||
text: '未申请退款直接缺席的,报名费于活动结束后自动结算给组织者,平台不参与分配',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '二、特殊情形退款',
|
||||
desc: '以下特殊情形可申请全额退款,需联系客服并提供相关证明材料:',
|
||||
tips: [
|
||||
{
|
||||
text: '活动当天遭遇极端恶劣天气(台风、暴雨红色预警等);',
|
||||
},
|
||||
{
|
||||
text: '球场临时关闭或其他不可抗力导致活动无法进行;',
|
||||
},
|
||||
{
|
||||
text: '参与者本人突发疾病或意外(需提供医院证明)。',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '三、活动取消规则',
|
||||
desc: '',
|
||||
tips: [
|
||||
{
|
||||
text: '到达活动开始时间时,报名人数仍未达到最低成局人数,活动自动取消,已付款参与者全额退款;',
|
||||
},
|
||||
{
|
||||
text: '组织者主动取消活动,所有已付款参与者全额退款;',
|
||||
},
|
||||
{
|
||||
text: '以上退款均由系统自动处理,无需申请。',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '四、免责声明',
|
||||
desc: '',
|
||||
tips: [
|
||||
{
|
||||
text: '本平台仅为网球约球信息撮合平台,不直接提供场地或运动服务,不对活动中的人身安全及财物损失承担责任;',
|
||||
},
|
||||
{
|
||||
text: '网球运动存在固有运动风险,请在参与前评估自身身体状况,患有心脏病、高血压等基础疾病者请在医生许可下参与;',
|
||||
},
|
||||
{
|
||||
text: '平台强烈建议参与者购买运动意外保险;',
|
||||
strong: true,
|
||||
},
|
||||
{
|
||||
text: '因组织者或场地方原因导致活动变更或取消,平台将协助处理但不承担连带责任;',
|
||||
},
|
||||
{
|
||||
text: '本平台不对因网络故障、系统维护或不可抗力导致的服务中断承担责任。',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -424,6 +424,7 @@
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.orderNo {
|
||||
@@ -544,21 +545,160 @@
|
||||
.time {
|
||||
text-align: left;
|
||||
padding-left: 30px;
|
||||
border-right: 1px solid rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.rule {
|
||||
border-left: 1px solid rgba(0, 0, 0, 0.06);
|
||||
// .rule {
|
||||
// border-left: 1px solid rgba(0, 0, 0, 0.06);
|
||||
// }
|
||||
}
|
||||
}
|
||||
.refundTip {
|
||||
margin-top: 16px;
|
||||
color: rgba(60, 60, 67, 0.6);
|
||||
text-align: center;
|
||||
font-feature-settings:
|
||||
"liga" off,
|
||||
"clig" off;
|
||||
font-family: "PingFang SC";
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
.declaimer {
|
||||
.disclaimer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
margin-top: 16px;
|
||||
|
||||
.disclaimerTitle {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: #000;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.disclaimerDesc {
|
||||
font-size: 12px;
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.disclaimerSection {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
padding-bottom: 100px;
|
||||
|
||||
.sectionTitle {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: #000;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.sectionDesc {
|
||||
font-size: 12px;
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.tableContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
// gap: 8px;
|
||||
margin: 8px 0;
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||
background: #fff;
|
||||
box-shadow: 0 4px 36px 0 rgba(0, 0, 0, 0.06);
|
||||
overflow: hidden;
|
||||
|
||||
.tableRow {
|
||||
display: flex;
|
||||
min-height: 44px;
|
||||
|
||||
&:first-child {
|
||||
.tableCell {
|
||||
color: #000;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.tableCell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
line-height: 18px;
|
||||
word-break: break-word;
|
||||
padding: 4px 0;
|
||||
|
||||
&:not(:last-child) {
|
||||
border-right: 1px solid rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
&:nth-child(1) {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
flex: 0.6;
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.tipText {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tipsList {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
margin: 8px 0;
|
||||
|
||||
.tipItem {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
|
||||
&::before {
|
||||
content: "•";
|
||||
margin-right: 6px;
|
||||
margin-top: -2px;
|
||||
font-size: 12px;
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
flex-shrink: 0;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.tipText {
|
||||
font-size: 12px;
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.tipTextStrong {
|
||||
font-size: 12px;
|
||||
color: #000;
|
||||
font-weight: bold;
|
||||
line-height: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
padding: 15px 0 0;
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useState, useRef } from "react";
|
||||
import { View, Text, Button, Image } from "@tarojs/components";
|
||||
import { Dialog } from "@nutui/nutui-react-taro";
|
||||
import Taro, { useDidShow, useRouter } from "@tarojs/taro";
|
||||
import dayjs from "dayjs";
|
||||
import dayjs, { Dayjs } from "dayjs";
|
||||
import "dayjs/locale/zh-cn";
|
||||
import classnames from "classnames";
|
||||
import orderService, {
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
getOrderStatus,
|
||||
generateOrderActions,
|
||||
isPhoneNumber,
|
||||
genGameLength,
|
||||
} from "@/utils";
|
||||
import { getStorage, setStorage } from "@/store/storage";
|
||||
import { useGlobalStore } from "@/store/global";
|
||||
@@ -32,7 +33,7 @@ import img from "@/config/images";
|
||||
import CustomerIcon from "@/static/order/customer.svg";
|
||||
import { handleCustomerService } from "@/services/userService";
|
||||
import { requireLoginWithPhone } from "@/utils/helper";
|
||||
import { DECLAIMER } from "./config";
|
||||
import { RegistrationInstructions } from "./config";
|
||||
import styles from "./index.module.scss";
|
||||
|
||||
dayjs.locale("zh-cn");
|
||||
@@ -75,6 +76,17 @@ function genGameNotice(order_status, start_time) {
|
||||
return gameNoticeMap.get(key) || {};
|
||||
}
|
||||
|
||||
function genGameRange(startTime: Dayjs, endTime: Dayjs) {
|
||||
if (!startTime || !endTime) {
|
||||
return "";
|
||||
}
|
||||
// 如果跨天(自然日)
|
||||
if (!startTime.isSame(endTime, "day")) {
|
||||
return `${startTime.format("HH:mm")} - ${endTime.format("MM月DD日 HH:mm")}`;
|
||||
}
|
||||
return `${startTime.format("HH:mm")} - ${endTime.format("HH:mm")}`;
|
||||
}
|
||||
|
||||
function GameInfo(props) {
|
||||
const { detail, currentLocation, orderDetail, init } = props;
|
||||
const { order_status, refund_status, amount, refund_amount } = orderDetail;
|
||||
@@ -111,15 +123,17 @@ function GameInfo(props) {
|
||||
|
||||
const startTime = dayjs(start_time);
|
||||
const endTime = dayjs(end_time);
|
||||
const game_length = Number(
|
||||
(endTime.diff(startTime, "minutes") / 60).toFixed(),
|
||||
);
|
||||
// const game_length = Number(
|
||||
// (endTime.diff(startTime, "minutes") / 60).toFixed(),
|
||||
// );
|
||||
const game_length = genGameLength(startTime, endTime);
|
||||
|
||||
const startMonth = startTime.format("M");
|
||||
const startDay = startTime.format("D");
|
||||
const theDayOfWeek = startTime.format("dddd");
|
||||
const startDate = `${startMonth}月${startDay}日 ${theDayOfWeek}`;
|
||||
const gameRange = `${startTime.format("HH:mm")} - ${endTime.format("HH:mm")}`;
|
||||
// const gameRange = `${startTime.format("HH:mm")} - ${endTime.format("HH:mm")}`;
|
||||
const gameRange = genGameRange(startTime, endTime);
|
||||
|
||||
const orderStatus = getOrderStatus(orderDetail);
|
||||
|
||||
@@ -277,7 +291,7 @@ function GameInfo(props) {
|
||||
<View className={styles.gameInfoDateWeatherCalendarDateDate}>
|
||||
<View className={styles.date}>{startDate}</View>
|
||||
<View className={styles.venueTime}>
|
||||
{gameRange} ({game_length}小时)
|
||||
{gameRange} ({game_length})
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
@@ -550,15 +564,66 @@ function RefundPolicy(props) {
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
<Text className={styles.refundTip}>
|
||||
以上时间节点以提交申请时间为准。违约金由组织者(95%)与平台(5%)分配,用于补偿场地损失及处理成本。活动结束48小时后,1个工作日内系统自动结算至组织者账户。未申请退款直接缺席,报名费全额归组织者。
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
function Disclaimer() {
|
||||
return (
|
||||
<View className={styles.declaimer}>
|
||||
<Text className={styles.title}>免责声明</Text>
|
||||
<Text className={styles.content}>{DECLAIMER}</Text>
|
||||
<View className={styles.disclaimer}>
|
||||
<View className={styles.disclaimerTitle}>
|
||||
<Text>{RegistrationInstructions.title}</Text>
|
||||
</View>
|
||||
<View className={styles.disclaimerDesc}>
|
||||
<Text>{RegistrationInstructions.desc}</Text>
|
||||
</View>
|
||||
{RegistrationInstructions.children.map((section, sectionIndex) => (
|
||||
<View key={sectionIndex} className={styles.disclaimerSection}>
|
||||
<View className={styles.sectionTitle}>
|
||||
<Text>{section.title}</Text>
|
||||
</View>
|
||||
{section.desc && (
|
||||
<View className={styles.sectionDesc}>
|
||||
<Text>{section.desc}</Text>
|
||||
</View>
|
||||
)}
|
||||
{section.table && (
|
||||
<View className={styles.tableContainer}>
|
||||
{section.table.map((row, rowIndex) => (
|
||||
<View key={rowIndex} className={styles.tableRow}>
|
||||
<View className={styles.tableCell}>
|
||||
<Text>{row.refundApplicationTime}</Text>
|
||||
</View>
|
||||
<View className={styles.tableCell}>
|
||||
<Text>{row.participantRefundableAmount}</Text>
|
||||
</View>
|
||||
<View className={styles.tableCell}>
|
||||
<Text>{row.liquidatedDamages}</Text>
|
||||
</View>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
)}
|
||||
{section.tips && (
|
||||
<View className={styles.tipsList}>
|
||||
{section.tips.map((tip, tipIndex) => (
|
||||
<View key={tipIndex} className={styles.tipItem}>
|
||||
<Text
|
||||
className={
|
||||
tip.strong ? styles.tipTextStrong : styles.tipText
|
||||
}
|
||||
>
|
||||
{tip.text}
|
||||
</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import img from '@/config/images';
|
||||
import { FormFieldConfig } from '@/config/formSchema/publishBallFormSchema';
|
||||
import SelectStadium from '../SelectStadium/SelectStadium'
|
||||
import { Stadium } from '../SelectStadium/StadiumDetail'
|
||||
import { normalize_address } from '@/utils/locationUtils'
|
||||
import './FormBasicInfo.scss'
|
||||
|
||||
type PlayGame = {
|
||||
@@ -54,7 +55,7 @@ const FormBasicInfo: React.FC<FormBasicInfoProps> = ({
|
||||
onChange({...value,
|
||||
venue_id,
|
||||
location_name: name,
|
||||
location: address,
|
||||
location: normalize_address(address || ''),
|
||||
latitude,
|
||||
longitude,
|
||||
court_type,
|
||||
|
||||
@@ -4,7 +4,7 @@ import Taro from '@tarojs/taro'
|
||||
import { Loading } from '@nutui/nutui-react-taro'
|
||||
import StadiumDetail, { StadiumDetailRef } from './StadiumDetail'
|
||||
import { CommonPopup, CustomPopup } from '../../../../components'
|
||||
import { getLocation } from '@/utils/locationUtils'
|
||||
import { getLocation, normalize_address } from '@/utils/locationUtils'
|
||||
import PublishService from '@/services/publishService'
|
||||
import images from '@/config/images'
|
||||
import './SelectStadium.scss'
|
||||
@@ -100,7 +100,7 @@ const SelectStadium: React.FC<SelectStadiumProps> = ({
|
||||
success: (res) => {
|
||||
setSelectedStadium({
|
||||
name: res.name,
|
||||
address: res.address,
|
||||
address: normalize_address(res.address || ''),
|
||||
longitude: res.longitude,
|
||||
latitude: res.latitude
|
||||
})
|
||||
|
||||
@@ -7,6 +7,7 @@ import TextareaTag from '@/components/TextareaTag'
|
||||
import UploadCover, { type CoverImageValue } from '@/components/UploadCover'
|
||||
import { useKeyboardHeight } from '@/store/keyboardStore'
|
||||
import { useDictionaryActions } from '@/store/dictionaryStore'
|
||||
import { normalize_address } from '@/utils/locationUtils'
|
||||
|
||||
import './StadiumDetail.scss'
|
||||
|
||||
@@ -145,7 +146,7 @@ const StadiumDetail = forwardRef<StadiumDetailRef, StadiumDetailProps>(({
|
||||
setFormData({
|
||||
...formData,
|
||||
name: res.name,
|
||||
address: res.address,
|
||||
address: normalize_address(res.address || ''),
|
||||
latitude: res.latitude,
|
||||
longitude: res.longitude,
|
||||
istance: null
|
||||
|
||||
@@ -381,6 +381,7 @@ const PublishBall: React.FC = () => {
|
||||
image_list,
|
||||
wechat,
|
||||
id,
|
||||
title,
|
||||
...rest
|
||||
} = formData[0];
|
||||
const { min, max, organizer_joined } = players;
|
||||
@@ -389,6 +390,7 @@ const PublishBall: React.FC = () => {
|
||||
...activityInfo,
|
||||
...descriptionInfo,
|
||||
...timeRange,
|
||||
title: title?.replace(/\n/g, ''),
|
||||
max_players: max,
|
||||
min_players: min,
|
||||
organizer_joined: organizer_joined === true ? 1 : 0,
|
||||
@@ -453,6 +455,7 @@ const PublishBall: React.FC = () => {
|
||||
skill_level,
|
||||
is_substitute_supported,
|
||||
id,
|
||||
title,
|
||||
...rest
|
||||
} = item;
|
||||
const { min, max, organizer_joined } = players;
|
||||
@@ -461,6 +464,7 @@ const PublishBall: React.FC = () => {
|
||||
...activityInfo,
|
||||
...descriptionInfo,
|
||||
...timeRange,
|
||||
title: title?.replace(/\n/g, ' '),
|
||||
max_players: max,
|
||||
min_players: min,
|
||||
organizer_joined: organizer_joined === true ? 1 : 0,
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
<svg width="24" height="32" viewBox="0 0 24 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_7504_18610)">
|
||||
<path d="M12.3076 0.0971837C12.7381 0.438102 12.2325 2.22445 12.328 2.76282C13.7134 2.51292 15.1145 2.01928 16.5062 1.81874C16.8928 1.76321 17.9933 1.57501 17.89 2.28307C17.8274 2.71346 16.6377 4.52912 16.73 4.62167C17.9667 4.78673 19.2128 4.97647 20.4214 5.28962C20.8346 5.39607 21.9226 5.57501 21.8209 6.15503C21.7223 6.71192 20.0394 8.18974 20.0848 8.37331C21.1211 9.25414 22.4017 9.9838 23.402 10.8909C23.712 11.1716 24.119 11.457 23.9671 11.926C23.8028 12.4366 22.0009 12.6541 21.5469 12.9965C21.525 13.1215 22.552 14.7351 22.7069 15.0204C22.8995 15.3752 23.8028 17.0613 23.7871 17.3236C23.726 18.2754 22.051 17.5827 21.4827 17.6383C21.6596 18.3648 21.8131 19.1408 21.8929 19.8843C21.9774 20.6679 22.2514 21.6753 21.0632 21.4932C21.237 29.4763 12.3624 34.5546 5.2928 30.6641C-0.183159 27.6514 -1.69069 20.6001 2.09614 15.6467C2.21199 15.494 2.76459 14.9402 2.78964 14.8801C2.81782 14.8122 2.73642 14.2831 2.75364 14.0902C2.80999 13.4624 3.15439 12.8824 3.58802 12.4366C2.80686 11.693 1.97874 10.8461 1.52945 9.85268C1.36977 9.50096 1.30559 9.0027 1.16 8.70189C1.04103 8.45661 0.72324 8.31623 0.593307 8.0216C0.190985 7.11762 0.835953 6.08561 1.87228 6.20902C2.38262 6.26919 2.4609 6.6317 3.13091 6.54069C4.43807 6.36637 5.06425 4.00154 5.53545 2.98804C5.73583 2.5592 6.59839 0.737369 6.94749 0.59082C7.976 0.161974 7.82102 2.05939 8.23899 2.54994C9.08747 1.94986 9.8702 1.2526 10.7281 0.664866C11.0991 0.410335 11.8724 -0.251447 12.3076 0.0925559V0.0971837ZM11.1993 6.0563C10.9629 6.20285 10.2944 5.55804 10.022 5.41149C8.99824 4.86232 7.73648 4.72348 6.62814 5.09988C5.22236 5.57809 5.13626 6.89394 3.68039 7.42923C3.52854 7.48477 3.02133 7.55418 2.9775 7.59738C2.94462 7.62977 2.87418 7.99846 2.75364 8.16814C2.6331 8.33475 2.4061 8.51832 2.19477 8.55071C2.40297 9.81874 3.62873 11.7146 5.15661 11.0189C5.63407 10.8014 5.73583 10.263 5.89707 10.1643C6.15694 10.0069 6.41367 10.1658 6.45437 10.4466C6.57178 11.258 5.30689 11.9676 4.57113 11.9213L5.26776 12.3702C6.99289 13.2865 8.93719 13.6429 10.8846 13.3575C10.9989 13.322 11.2165 12.5245 11.3605 12.3116C11.5515 12.0278 11.9178 11.8072 12.1182 11.5418C12.5033 11.0297 12.6598 9.76166 12.6066 9.13228C12.5847 8.87466 12.3452 8.2931 12.4297 8.13266C12.9792 7.84265 13.5897 7.80409 13.8793 7.14693C14.5384 5.64905 12.6035 4.40879 11.4904 5.55033C11.3777 5.66602 11.2259 6.03934 11.2008 6.05476L11.1993 6.0563ZM12.8665 12.2808C12.2513 12.418 11.6439 13.683 12.1511 14.1273C12.7882 14.6826 14.2582 13.5488 14.6981 13.049C14.4006 12.5183 13.4473 12.1512 12.8665 12.2808ZM6.45594 13.7802C6.16163 13.7324 5.8642 13.6645 5.58085 13.575C4.81847 13.3313 4.25647 12.7312 3.85572 13.7324C3.66786 14.1998 3.64751 14.823 4.27213 14.9387C4.7809 15.0328 6.34323 14.4142 6.45594 13.7817V13.7802ZM10.9973 13.8511C9.58215 14.1921 8.2343 14.1859 6.80817 13.919C6.76746 14.8091 5.6012 15.4477 4.79656 15.5526C4.52573 15.5881 4.07645 15.5156 3.87294 15.6066C3.74144 15.6653 3.02603 16.4165 2.89609 16.5708C0.767073 19.1192 0.635574 22.5885 2.06484 25.5118C2.36853 26.1319 2.43428 26.3756 3.20918 26.4512C5.43839 26.6672 6.16633 25.3606 7.9713 24.6016C11.4137 23.1562 15.7767 24.2237 17.6051 27.5526C20.7376 24.0617 20.4417 18.7906 17.2576 15.4323C16.9257 15.0837 15.7328 14.118 15.3117 13.9699C15.0675 13.8835 14.8123 14.297 14.6292 14.4296C13.3659 15.3521 11.3589 15.9105 10.9989 13.8527L10.9973 13.8511ZM3.59115 27.6915C3.55985 27.8103 3.64125 27.8288 3.69447 27.8982C4.0968 28.4103 4.98284 29.0305 5.55267 29.3729C8.98102 31.4354 13.5036 31.1145 16.5719 28.5553C15.415 25.5164 11.647 24.6232 8.74463 25.6429C6.98819 26.2599 5.62468 28.0972 3.59115 27.6915Z" fill="black"/>
|
||||
<path d="M7.69734 6.77362C8.84169 6.70883 9.28002 8.38257 8.18264 8.8361C6.57804 9.49788 6.06457 6.86463 7.69734 6.77362Z" fill="black"/>
|
||||
</g>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="_图层_2" data-name="图层 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 74.87 89.85">
|
||||
<defs>
|
||||
<clipPath id="clip0_7504_18610">
|
||||
<rect width="24" height="32" fill="white"/>
|
||||
</clipPath>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #e6ff54;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<g id="_图层_1-2" data-name=" 图层 1">
|
||||
<path class="cls-1" d="M39.79,82.22h0c-3.1,1.7-6.7,2.6-10.3,2.6-9.9,0-18.3-6.7-21-15.7,1.7,1.4,3.6,2.3,5.6,2.7,4.7,1,8.3,0,11.8-1,3.1-.8,5.9-1.6,9.7-1.2,3.9.5,6.6,2.6,7.3,5.7.6,2.7-.5,5.4-3,6.9h-.1Z"/>
|
||||
<path class="cls-1" d="M51.29,62.92c0,.9,0,1.9-.2,2.8-.5,3.7-1.8,7.2-4,10.2,0-.5,0-1-.2-1.5-1.1-4.9-5.3-8.3-10.9-9-4.5-.6-8.1.4-11.3,1.3-3.3,1-6.2,1.7-9.9,1-3.9-.8-7.6-4.7-6.6-10.4h0c0-.6.3-1.1.5-1.7,0-.4.3-.8.5-1.2,1.1.2,2.2.2,3.3,0h.4c2.6-.9,4.4-2.8,4.8-5.2v-.2c4.8,1.4,10.4,1.3,15.4,0h0c.2-.2.4-.2.6-.3,0,1,.5,2,1.1,2.9,1.1,1.6,2.9,2.6,5,2.8,1.1,0,2.2,0,3.3-.4h0c1-.2,2-.7,3-1.3.6-.4,1.1-.8,1.6-1.2,2.3,3.5,3.5,7.6,3.5,11.9v-.3l.1-.2Z"/>
|
||||
<g>
|
||||
<path d="M74.59,37.52c-2.1-6.1-6.4-11.7-12.2-15.9h0c1.7-2,2.6-3,4.5-4.6.5-.4.7-1,.6-1.6,0-.6-.4-1.1-1-1.4-6.5-3.2-12.1-4.7-18.9-5,.9-2.4,1.8-4.1,3-6.4.3-.6.3-1.3,0-1.9-.4-.6-1.1-.8-1.8-.7-7.6,1.7-13.8,4-20.9,8.1-1.9-2.4-4.1-4.3-6.8-6-.5-.3-1.2-.3-1.8,0s-.9.9-.9,1.5c0,5.6-1,9.9-3.2,14.4-1.6,3.3-3.3,5.7-4.4,7.4-1.3,1.9-2.1,2.9-3,3.4-1.35-1.86-4.14-2.86-6.42-.58-1.03,1.03-1.51,2.46-1.33,3.91.21,1.67,1.05,2.27,2.25,2.87.7,2.3,1.7,4.5,2.9,6.3-1.4,1.1-2.3,2.7-2.7,4.5-.6,2.7,0,5.3,1.9,7.2-1.2,3.1-1.8,6.4-1.8,9.8,0,14.9,13.39,27.65,27,27,6.31-.3,9.55-.58,15.79-5.21,1.57-1.16,3.02-2.5,4.26-4.01,3.31-4.03,5.89-8.65,6.65-13.38,1.2.4,2.4.9,3.3,1.3.4.2.8.2,1.3,0h.2c.5-.2.8-.6,1-1.1.9-2.9,1.4-7.5,1.6-10.6,1.5,0,3.1.3,5.2.7.5,0,1.1,0,1.5-.4s.6-.9.6-1.4c-.2-5.6-1-10-2.5-14.3,1.8-.8,3.3-1.4,5.1-1.8.5,0,.9-.4,1.1-.9.2-.4.3-.9,0-1.4v.2h-.1ZM44.09,41.82c.7,0,1.2.3,1.7.8l.17.17c.26.26.52,1.15.72,1.96.29,1.21-.08,2.48-.97,3.36,0,0-.01.01-.02.02-.4.4-.8.7-1.3,1-1.4.9-2.8,1.3-4.1,1.2-1,0-1.8-.5-2.3-1.2-.5-.8-.7-1.6-.5-2.4,0-.4.2-.8.4-1.2.2-.3.4-.6.6-.9h0c.2-.3.5-.6.8-.8s.6-.4.9-.6c.8-.5,1.6-.9,2.3-1.1.48-.09.65-.19,1.21-.27l.39-.03ZM7.09,34.12h.4c3.7-.5,5.6-3.2,7.3-5.8,1.3-1.8,2.6-3.8,4.8-5.3,3.7-2.5,8.6-2.6,12.6,0,.3.2.6.2,1,.2h.4c.5,0,.9-.5,1.1-.9.6-1.3,1.7-2.2,3-2.4,1.1-.2,2.1,0,2.8.7.8.7,1.2,1.8,1,2.9-.2,1.2-1.2,2.1-2.6,2.7-.6.2-.9.6-1.1,1.2-.2.6,0,1.2.3,1.6,2.1,2.7,2.9,6.5,2.1,9.5,0,.3-.2.6-.3.9-.6.3-1.1.6-1.7,1-1.6,1-2.8,2.2-3.5,3.6,0,0-.2.3-.2.5l-1.1.3c-1.1.4-2.2.7-3.4.9-2.7.5-5.5.6-8.2.2-1.2-.2-2.3-.4-3.4-.8h0c-.4,0-.8-.2-1.2-.4-.3,0-.5-.2-.8-.3h-.3c-.2,0-.4-.2-.7-.3-.2,0-.4-.2-.6-.3.2,0,.4-.2.6-.4,0,0,.2,0,.3-.2,1.1-.9,2.1-2.1,2.7-3.7.2-.5.2-1,0-1.5s-.6-.9-1.1-1c-1-.4-2.2,0-2.6,1.1-.6,1.4-1.5,2.3-2.6,2.4-.5,0-1.1,0-1.7-.3h0c-1.3-1.6-2.3-3.5-3-5.6v-.2h-.2l-.1-.3ZM7.79,49.72c-.2-.2-.3-.5-.5-.9h0c0-.4-.2-.8,0-1.4h0v-.5c.3-1.4,1.2-2,1.8-2.3h.7c.2,0,.3.3.5.4.2.2.5.3.7.5.4.2.7.5,1.1.7.5.3,1,.6,1.5.8,0,0,.2,0,.4.2h0v1.1c-.2,1.1-1.1,2-2.5,2.3h-.5c-.8,0-1.6,0-2.3-.3-.3-.2-.6-.4-.8-.6s0,0-.2-.2h.2l-.1.2ZM39.79,82.22h0c-3.1,1.7-6.7,2.6-10.3,2.6-9.9,0-18.3-6.7-21-15.7,1.7,1.4,3.6,2.3,5.6,2.7,4.7,1,8.3,0,11.8-1,3.1-.8,5.9-1.6,9.7-1.2,3.9.5,6.6,2.6,7.3,5.7.6,2.7-.5,5.4-3,6.9h-.1ZM47.09,75.92c0-.5,0-1-.2-1.5-1.1-4.9-5.3-8.3-10.9-9-4.5-.6-8.1.4-11.3,1.3-3.3,1-6.2,1.7-9.9,1-3.9-.8-7.6-4.7-6.6-10.4h0c0-.6.3-1.1.5-1.7,0-.4.3-.8.5-1.2,1.1.2,2.2.2,3.3,0h.4c2.6-.9,4.4-2.8,4.8-5.2v-.2c4.8,1.4,10.2,1.4,15.1,0,0,0,.6-.3.8-.4,0,1,.5,2,1.1,2.9,1.1,1.6,2.9,2.6,5,2.8,1.1,0,3.4-.3,3.4-.3,1-.3,2-.8,3-1.4.6-.4,1.1-.8,1.6-1.2,2.3,3.5,3.52,7.6,3.5,11.9-.01,2.59,0,1.9-.2,2.8,0,0-1.7,6.8-3.9,9.8Z"/>
|
||||
<path d="M24.69,34.92c-1.6.2-3.1-.9-3.3-2.5-.3-1.6.8-3.1,2.4-3.3h0c1.6-.2,3.1.9,3.3,2.5.3,1.6-.8,3.1-2.4,3.3Z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.6 KiB |
@@ -1,6 +1,7 @@
|
||||
import Taro from "@tarojs/taro";
|
||||
import { check_login_status, get_user_info } from "@/services/loginService";
|
||||
import { useUser } from "@/store/userStore";
|
||||
import { Dayjs } from "dayjs";
|
||||
|
||||
// 普通函数,不调用 useLoad
|
||||
export const sceneRedirectLogic = (options, defaultPage: string) => {
|
||||
@@ -135,3 +136,40 @@ export function genNTRPRequirementText(min, max) {
|
||||
export function isPhoneNumber(str) {
|
||||
return /^1[3-9]\d{9}$/.test(str);
|
||||
}
|
||||
|
||||
export function genGameLength(startTime: Dayjs, endTime: Dayjs) {
|
||||
if (!startTime || !endTime) {
|
||||
return "";
|
||||
}
|
||||
const totalMinutes = endTime.diff(startTime, "minute");
|
||||
const totalHours = totalMinutes / 60;
|
||||
|
||||
if (totalHours >= 24) {
|
||||
const days = Math.floor(totalHours / 24);
|
||||
const remainingHours = totalHours % 24;
|
||||
|
||||
if (remainingHours === 0) {
|
||||
return `${days}天`;
|
||||
}
|
||||
|
||||
// 保留一位小数
|
||||
const displayHours = parseFloat(remainingHours.toFixed(1));
|
||||
return `${days}天${displayHours}小时`;
|
||||
}
|
||||
|
||||
// 如果是整数小时,不显示小数点
|
||||
if (Number.isInteger(totalHours)) {
|
||||
return `${totalHours}小时`;
|
||||
}
|
||||
// 保留一位小数,去除末尾的0
|
||||
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}分`;
|
||||
}
|
||||
|
||||
@@ -14,10 +14,20 @@ export interface LocationInfo {
|
||||
name?: string
|
||||
}
|
||||
|
||||
/** 规范化地址:去掉类似“上海市上海市...”的重复前缀 */
|
||||
export const normalize_address = (address: string): string => {
|
||||
if (!address) return ''
|
||||
// 去空格(包含全角空格)
|
||||
const trimmed = address.replace(/[\s\u3000]+/g, '')
|
||||
// 处理 “xx市xx市...” / “xx省xx省...” 等连续重复
|
||||
// 例:上海市上海市静安区... -> 上海市静安区...
|
||||
return trimmed.replace(/^(.{2,6}?[市省])\1+/, '$1')
|
||||
}
|
||||
|
||||
// 获取当前位置
|
||||
export const getCurrentLocation = (): Promise<LocationInfo> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
Taro.getLocation({
|
||||
;(Taro as any).getLocation({
|
||||
type: 'wgs84',
|
||||
success: (res) => {
|
||||
console.log('===获取地理位置', res)
|
||||
@@ -27,7 +37,7 @@ export const getCurrentLocation = (): Promise<LocationInfo> => {
|
||||
resolve({
|
||||
latitude: res.latitude,
|
||||
longitude: res.longitude,
|
||||
address
|
||||
address: normalize_address(address)
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
@@ -48,12 +58,12 @@ export const getCurrentLocation = (): Promise<LocationInfo> => {
|
||||
// 选择地图位置
|
||||
export const chooseLocation = (): Promise<LocationInfo> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
Taro.chooseLocation({
|
||||
;(Taro as any).chooseLocation({
|
||||
success: (res) => {
|
||||
resolve({
|
||||
latitude: res.latitude,
|
||||
longitude: res.longitude,
|
||||
address: res.address,
|
||||
address: normalize_address(res.address),
|
||||
name: res.name
|
||||
})
|
||||
},
|
||||
@@ -66,7 +76,7 @@ export const chooseLocation = (): Promise<LocationInfo> => {
|
||||
|
||||
export const getLocation = (): Promise<Location> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
Taro.getLocation({
|
||||
;(Taro as any).getLocation({
|
||||
success: (res) => {
|
||||
resolve({
|
||||
latitude: res.latitude,
|
||||
|
||||
@@ -106,19 +106,34 @@ const drawLabel = (ctx: any, x: number, y: number, width: number, height: number
|
||||
ctx.restore()
|
||||
}
|
||||
|
||||
/** 给图片 URL 加随机参数,避免同一链接二次加载不触发 onload */
|
||||
function with_cache_bust(url: string): string {
|
||||
const sep = url.includes('?') ? '&' : '?';
|
||||
return `${url}${sep}_t=${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
||||
}
|
||||
|
||||
// 工具函数 - OffscreenCanvas 下加载图片(使用 offscreen.createImage)
|
||||
const loadImage = (src: string): Promise<any> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let timer: ReturnType<typeof setTimeout> | null = null
|
||||
try {
|
||||
const off = runtime.offscreen
|
||||
if (!off || typeof off.createImage !== 'function') {
|
||||
throw new Error('OffscreenCanvas 未初始化或不支持 createImage')
|
||||
}
|
||||
const img = off.createImage()
|
||||
img.onload = () => resolve(img)
|
||||
img.onerror = reject
|
||||
img.src = src
|
||||
timer = setTimeout(() => reject(new Error(`图片加载超时: ${src}`)), 8000)
|
||||
img.onload = () => {
|
||||
if (timer) clearTimeout(timer)
|
||||
resolve(img)
|
||||
}
|
||||
img.onerror = (e: any) => {
|
||||
if (timer) clearTimeout(timer)
|
||||
reject(e)
|
||||
}
|
||||
img.src = with_cache_bust(src)
|
||||
} catch (e) {
|
||||
if (timer) clearTimeout(timer)
|
||||
reject(e)
|
||||
}
|
||||
})
|
||||
@@ -407,7 +422,7 @@ const drawShareCard = async (ctx: any, data: ShareCardData, offscreen: any): Pro
|
||||
ctx.restore()
|
||||
} catch (error) {
|
||||
// 如果头像加载失败,绘制默认头像
|
||||
ctx.setFillStyle('#CCCCCC')
|
||||
ctx.fillStyle = '#CCCCCC'
|
||||
ctx.beginPath()
|
||||
ctx.arc(avatarX + avatarSize / 2, avatarY + avatarSize / 2, avatarSize / 2, 0, 2 * Math.PI)
|
||||
ctx.fill()
|
||||
@@ -588,10 +603,12 @@ export async function generateShareImage(data: ShareCardData): Promise<string> {
|
||||
// 记录到 runtime(供 loadImage 使用)
|
||||
runtime.offscreen = offscreen
|
||||
isDrawing = true
|
||||
|
||||
try {
|
||||
const imagePath = await drawShareCard(ctx, data, offscreen)
|
||||
isDrawing = false
|
||||
return imagePath
|
||||
} finally {
|
||||
isDrawing = false
|
||||
}
|
||||
}
|
||||
|
||||
export default generateShareImage
|
||||
|
||||
Reference in New Issue
Block a user