10 Commits

Author SHA1 Message Date
李瑞
4dc8e84f5c 修改日期筛选交互 2026-03-27 22:30:46 +08:00
b84c3bb409 fix: 时间展示修复 2026-03-25 06:03:07 +08:00
fa41842e75 fix: 逻辑补全 2026-03-20 23:24:22 +08:00
3bbb64d58c style: 修复地址展示的问题 2026-03-20 23:10:23 +08:00
58cf46e93d fix: 修复订单详情页球局时间展示问题、修复候补列表NTRP文案缺失的问题 2026-03-20 22:56:18 +08:00
张成
8004b26bd1 换logo 2026-03-19 15:00:08 +08:00
张成
98baa371ee 1 2026-03-16 11:25:50 +08:00
张成
f87859da0e 1 2026-03-11 15:33:26 +08:00
d3390d5e81 fix: debug 分享卡片生成问题 2026-03-11 11:48:40 +08:00
筱野
883ce3c2c4 修改标题换行替换 2026-03-10 21:34:45 +08:00
15 changed files with 203 additions and 110 deletions

View File

@@ -32,36 +32,52 @@ const FilterPopup = (props: FilterPopupProps) => {
const { timeBubbleData, gamesNum } = store; const { timeBubbleData, gamesNum } = store;
/** /**
* @description 处理字典选项 * @description 日期排序
* @param dictionaryValue 字典选项 * @param a 日期字符串
* @returns 选项列表 * @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[]) => { const handleDateChange = (dates: Date[]) => {
let times: String[] = []; // ================================ 日期处理 ================================
if (dates.length > 1) { // 默认是是当前日期为开始日期,结束日期为当前日期 + 30天
times = [dayjs(dates[0]).format('YYYY-MM-DD'), dayjs(dates[dates.length - 1]).format('YYYY-MM-DD')] const defaultDateRange = [dayjs().format('YYYY-MM-DD'), dayjs().add(1, 'M').format('YYYY-MM-DD')];
onChange({ // 处理空数组的情况
'dateRange': times, if (!dates.length) {
}) onChange({ dateRange: defaultDateRange });
return; return;
} }
if (Array.isArray(dates)) { // 处理多日期范围选择超过1个日期
if (dates.length > 1) {
const currentDay = dayjs(dates[0]).format('YYYY-MM-DD'); const dateRange = [
if (filterOptions.dateRange.length === 0 || filterOptions.dateRange.length === 2) { dayjs(dates[0]).format('YYYY-MM-DD'),
times.push(currentDay); dayjs(dates[dates.length - 1]).format('YYYY-MM-DD')
} else { ];
times = [...filterOptions.dateRange, currentDay].sort( onChange({ dateRange });
(a, b) => new Date(a).getTime() - new Date(b).getTime() return;
)
}
} }
// 处理单个日期选择
onChange({ const currentFilterOptionsDateRange = Array.isArray(filterOptions?.dateRange)
'dateRange': times, ? 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 {
// 默认区间/已选区间/异常状态,点击当前日期统一收敛为单日
dateRange = [currentDay, currentDay];
}
onChange({ dateRange });
} }
const handleOptions = (dictionaryValue: []) => { const handleOptions = (dictionaryValue: []) => {

View File

@@ -1,41 +1,13 @@
import Taro from "@tarojs/taro"; import Taro from "@tarojs/taro";
import dayjs, { Dayjs } from "dayjs"; import dayjs, { Dayjs } from "dayjs";
import "dayjs/locale/zh-cn"; import "dayjs/locale/zh-cn";
import { calculateDistance } from "@/utils"; import { calculateDistance, genGameLength } from "@/utils";
import { View, Image, Text, Map } from "@tarojs/components"; import { View, Image, Text, Map } from "@tarojs/components";
import img from "@/config/images"; import img from "@/config/images";
import styles from "./index.module.scss"; import styles from "./index.module.scss";
dayjs.locale("zh-cn"); 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) { function genGameRange(startTime: Dayjs, endTime: Dayjs) {
if (!startTime || !endTime) { if (!startTime || !endTime) {
return ""; return "";

View File

@@ -489,7 +489,7 @@ export default function Participants(props) {
<Text <Text
className={styles["participants-list-item-level"]} 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}

View File

@@ -1,7 +1,7 @@
import { forwardRef, useState, useEffect, useImperativeHandle } from "react"; import { forwardRef, useState, useEffect, useImperativeHandle } from "react";
import { View, Button, Image, Text } from "@tarojs/components"; import { View, Button, Image, Text } from "@tarojs/components";
import Taro, { useShareAppMessage } from "@tarojs/taro"; import Taro, { useShareAppMessage } from "@tarojs/taro";
import dayjs from "dayjs"; import dayjs, { Dayjs } from "dayjs";
import "dayjs/locale/zh-cn"; import "dayjs/locale/zh-cn";
import classnames from "classnames"; import classnames from "classnames";
import { generateShareImage } from "@/utils"; import { generateShareImage } from "@/utils";
@@ -12,7 +12,7 @@ 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 } from "@/utils/helper"; import { genNTRPRequirementText, navto, genGameLength } 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";
@@ -28,7 +28,19 @@ export default forwardRef(({ id, from, detail, userInfo }, ref) => {
const [shareImageUrl, setShareImageUrl] = useState(""); const [shareImageUrl, setShareImageUrl] = useState("");
const { fetchUserInfo } = useUserActions(); 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 publishFlag = from === "publish";
// const posterRef = useRef(); // const posterRef = useRef();
const { max_participants, participant_count } = detail || {}; const { max_participants, participant_count } = detail || {};
@@ -50,6 +62,16 @@ export default forwardRef(({ id, from, detail, userInfo }, ref) => {
withShareTicket: false, // 是否需要返回 shareTicket withShareTicket: false, // 是否需要返回 shareTicket
isUpdatableMessage: true, // 是否是动态消息(需要服务端配置过模版) isUpdatableMessage: true, // 是否是动态消息(需要服务端配置过模版)
activityId: res.data.activity_id, // 动态消息的活动 id 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) { } catch (e) {
@@ -86,25 +108,34 @@ export default forwardRef(({ id, from, detail, userInfo }, ref) => {
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 = `${endTime.diff(startTime, "hour")}小时`;
console.log(userInfo, "userInfo"); const currentUserInfo = await ensureUserInfo();
const url = await generateShareImage({ try {
userAvatar: userInfo.avatar_url, const url = await generateShareImage({
userNickname: userInfo.nickname, userAvatar: currentUserInfo.avatar_url,
gameType: play_type, userNickname: currentUserInfo.nickname,
skillLevel: `NTRP ${genNTRPRequirementText( gameType: play_type,
skill_level_min, skillLevel: `NTRP ${genNTRPRequirementText(
skill_level_max, skill_level_min,
)}`, skill_level_max,
gameDate: `${startTime.format("M月D日")} (${dayofWeek})`, )}`,
gameTime: `${startTime.format("ah")} ${gameLength}`, gameDate: `${startTime.format("M月D日")} (${dayofWeek})`,
venueName: location_name, gameTime: `${startTime.format("ah")}${gameLength}`,
venueImages: image_list ? image_list : [], venueName: location_name,
}); venueImages: image_list ? image_list : [],
return url; });
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) => { useShareAppMessage(async (res) => {
await changeMessageType(); await changeMessageType();
await ensureUserInfo();
const url = await generateShareImageUrl(); const url = await generateShareImageUrl();
// console.log(res, "res"); // console.log(res, "res");
return { return {
@@ -128,12 +159,13 @@ export default forwardRef(({ id, from, detail, userInfo }, ref) => {
} = detail || {}; } = detail || {};
// 先等待静默登录完成 // 先等待静默登录完成
await waitForAuthInit(); await waitForAuthInit();
const userInfo = await fetchUserInfo(); const currentUserInfo = await ensureUserInfo();
const { avatar_url, nickname } = userInfo; const { avatar_url, nickname } = currentUserInfo;
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 = `${endTime.diff(startTime, "hour")}小时`;
const game_length = genGameLength(startTime, endTime);
let qrCodeUrl = ""; let qrCodeUrl = "";
try { try {
const qrCodeUrlRes = await DetailService.getQrCodeUrl({ const qrCodeUrlRes = await DetailService.getQrCodeUrl({
@@ -161,7 +193,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")}${gameLength}`, time: `${startTime.format("ah")}${game_length}`,
qrCodeUrl, qrCodeUrl,
}); });
} catch (e) { } catch (e) {

View File

@@ -24,12 +24,12 @@ function isFull(counts) {
} = counts; } = counts;
if ( if (
max_players === current_players && current_players >= max_players &&
is_substitute_supported === IsSubstituteSupported.NOTSUPPORT is_substitute_supported === IsSubstituteSupported.NOTSUPPORT
) { ) {
return true; return true;
} else if ( } else if (
max_players === current_players && current_players >= max_players &&
is_substitute_supported === IsSubstituteSupported.SUPPORT is_substitute_supported === IsSubstituteSupported.SUPPORT
) { ) {
return max_substitute_players === current_substitute_count; return max_substitute_players === current_substitute_count;
@@ -45,7 +45,7 @@ function RmbIcon() {
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;
@@ -123,7 +123,7 @@ export default function StickyButton(props) {
Taro.navigateTo({ Taro.navigateTo({
url: `/login_pages/index/index?redirect=${encodeURIComponent( url: `/login_pages/index/index?redirect=${encodeURIComponent(
fullPath fullPath,
)}`, )}`,
}); });
} }
@@ -138,7 +138,7 @@ export default function StickyButton(props) {
const matchNtrpReq = matchNtrpRequestment( const matchNtrpReq = matchNtrpRequestment(
ntrp_level, ntrp_level,
skill_level_min, skill_level_min,
skill_level_max skill_level_max,
); );
const gameManageRef = useRef(); const gameManageRef = useRef();
@@ -173,7 +173,7 @@ export default function StickyButton(props) {
}, [getCommentCount]); }, [getCommentCount]);
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 } {
@@ -271,7 +271,7 @@ export default function StickyButton(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}`,
); );
} }
}), }),
@@ -387,7 +387,7 @@ export default function StickyButton(props) {
<View <View
className={classnames( className={classnames(
styles["detail-main-action"], styles["detail-main-action"],
available ? "" : styles.disabled available ? "" : styles.disabled,
)} )}
> >
<View <View

View File

@@ -424,6 +424,7 @@
align-items: flex-end; align-items: flex-end;
justify-content: center; justify-content: center;
gap: 8px; gap: 8px;
word-break: break-all;
} }
.orderNo { .orderNo {

View File

@@ -2,7 +2,7 @@ import React, { useState, useRef } from "react";
import { View, Text, Button, Image } from "@tarojs/components"; import { View, Text, Button, Image } from "@tarojs/components";
import { Dialog } from "@nutui/nutui-react-taro"; import { Dialog } from "@nutui/nutui-react-taro";
import Taro, { useDidShow, useRouter } from "@tarojs/taro"; import Taro, { useDidShow, useRouter } from "@tarojs/taro";
import dayjs from "dayjs"; import dayjs, { Dayjs } from "dayjs";
import "dayjs/locale/zh-cn"; import "dayjs/locale/zh-cn";
import classnames from "classnames"; import classnames from "classnames";
import orderService, { import orderService, {
@@ -21,6 +21,7 @@ import {
getOrderStatus, getOrderStatus,
generateOrderActions, generateOrderActions,
isPhoneNumber, isPhoneNumber,
genGameLength,
} from "@/utils"; } from "@/utils";
import { getStorage, setStorage } from "@/store/storage"; import { getStorage, setStorage } from "@/store/storage";
import { useGlobalStore } from "@/store/global"; import { useGlobalStore } from "@/store/global";
@@ -75,6 +76,17 @@ function genGameNotice(order_status, start_time) {
return gameNoticeMap.get(key) || {}; 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) { function GameInfo(props) {
const { detail, currentLocation, orderDetail, init } = props; const { detail, currentLocation, orderDetail, init } = props;
const { order_status, refund_status, amount, refund_amount } = orderDetail; const { order_status, refund_status, amount, refund_amount } = orderDetail;
@@ -111,15 +123,17 @@ function GameInfo(props) {
const startTime = dayjs(start_time); const startTime = dayjs(start_time);
const endTime = dayjs(end_time); const endTime = dayjs(end_time);
const game_length = Number( // const game_length = Number(
(endTime.diff(startTime, "minutes") / 60).toFixed(), // (endTime.diff(startTime, "minutes") / 60).toFixed(),
); // );
const game_length = genGameLength(startTime, endTime);
const startMonth = startTime.format("M"); const startMonth = startTime.format("M");
const startDay = startTime.format("D"); const startDay = startTime.format("D");
const theDayOfWeek = startTime.format("dddd"); const theDayOfWeek = startTime.format("dddd");
const startDate = `${startMonth}${startDay}${theDayOfWeek}`; 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); const orderStatus = getOrderStatus(orderDetail);
@@ -277,7 +291,7 @@ function GameInfo(props) {
<View className={styles.gameInfoDateWeatherCalendarDateDate}> <View className={styles.gameInfoDateWeatherCalendarDateDate}>
<View className={styles.date}>{startDate}</View> <View className={styles.date}>{startDate}</View>
<View className={styles.venueTime}> <View className={styles.venueTime}>
{gameRange} {game_length} {gameRange} {game_length}
</View> </View>
</View> </View>
</View> </View>

View File

@@ -5,6 +5,7 @@ import img from '@/config/images';
import { FormFieldConfig } from '@/config/formSchema/publishBallFormSchema'; import { FormFieldConfig } from '@/config/formSchema/publishBallFormSchema';
import SelectStadium from '../SelectStadium/SelectStadium' import SelectStadium from '../SelectStadium/SelectStadium'
import { Stadium } from '../SelectStadium/StadiumDetail' import { Stadium } from '../SelectStadium/StadiumDetail'
import { normalize_address } from '@/utils/locationUtils'
import './FormBasicInfo.scss' import './FormBasicInfo.scss'
type PlayGame = { type PlayGame = {
@@ -54,7 +55,7 @@ const FormBasicInfo: React.FC<FormBasicInfoProps> = ({
onChange({...value, onChange({...value,
venue_id, venue_id,
location_name: name, location_name: name,
location: address, location: normalize_address(address || ''),
latitude, latitude,
longitude, longitude,
court_type, court_type,

View File

@@ -4,7 +4,7 @@ import Taro from '@tarojs/taro'
import { Loading } from '@nutui/nutui-react-taro' import { Loading } from '@nutui/nutui-react-taro'
import StadiumDetail, { StadiumDetailRef } from './StadiumDetail' import StadiumDetail, { StadiumDetailRef } from './StadiumDetail'
import { CommonPopup, CustomPopup } from '../../../../components' import { CommonPopup, CustomPopup } from '../../../../components'
import { getLocation } from '@/utils/locationUtils' import { getLocation, normalize_address } from '@/utils/locationUtils'
import PublishService from '@/services/publishService' import PublishService from '@/services/publishService'
import images from '@/config/images' import images from '@/config/images'
import './SelectStadium.scss' import './SelectStadium.scss'
@@ -100,7 +100,7 @@ const SelectStadium: React.FC<SelectStadiumProps> = ({
success: (res) => { success: (res) => {
setSelectedStadium({ setSelectedStadium({
name: res.name, name: res.name,
address: res.address, address: normalize_address(res.address || ''),
longitude: res.longitude, longitude: res.longitude,
latitude: res.latitude latitude: res.latitude
}) })

View File

@@ -7,6 +7,7 @@ import TextareaTag from '@/components/TextareaTag'
import UploadCover, { type CoverImageValue } from '@/components/UploadCover' import UploadCover, { type CoverImageValue } from '@/components/UploadCover'
import { useKeyboardHeight } from '@/store/keyboardStore' import { useKeyboardHeight } from '@/store/keyboardStore'
import { useDictionaryActions } from '@/store/dictionaryStore' import { useDictionaryActions } from '@/store/dictionaryStore'
import { normalize_address } from '@/utils/locationUtils'
import './StadiumDetail.scss' import './StadiumDetail.scss'
@@ -145,7 +146,7 @@ const StadiumDetail = forwardRef<StadiumDetailRef, StadiumDetailProps>(({
setFormData({ setFormData({
...formData, ...formData,
name: res.name, name: res.name,
address: res.address, address: normalize_address(res.address || ''),
latitude: res.latitude, latitude: res.latitude,
longitude: res.longitude, longitude: res.longitude,
istance: null istance: null

View File

@@ -381,6 +381,7 @@ const PublishBall: React.FC = () => {
image_list, image_list,
wechat, wechat,
id, id,
title,
...rest ...rest
} = formData[0]; } = formData[0];
const { min, max, organizer_joined } = players; const { min, max, organizer_joined } = players;
@@ -389,6 +390,7 @@ const PublishBall: React.FC = () => {
...activityInfo, ...activityInfo,
...descriptionInfo, ...descriptionInfo,
...timeRange, ...timeRange,
title: title?.replace(/\n/g, ''),
max_players: max, max_players: max,
min_players: min, min_players: min,
organizer_joined: organizer_joined === true ? 1 : 0, organizer_joined: organizer_joined === true ? 1 : 0,
@@ -453,6 +455,7 @@ const PublishBall: React.FC = () => {
skill_level, skill_level,
is_substitute_supported, is_substitute_supported,
id, id,
title,
...rest ...rest
} = item; } = item;
const { min, max, organizer_joined } = players; const { min, max, organizer_joined } = players;
@@ -461,6 +464,7 @@ const PublishBall: React.FC = () => {
...activityInfo, ...activityInfo,
...descriptionInfo, ...descriptionInfo,
...timeRange, ...timeRange,
title: title?.replace(/\n/g, ' '),
max_players: max, max_players: max,
min_players: min, min_players: min,
organizer_joined: organizer_joined === true ? 1 : 0, organizer_joined: organizer_joined === true ? 1 : 0,

View File

@@ -1,11 +1,18 @@
<svg width="24" height="32" viewBox="0 0 24 32" fill="none" xmlns="http://www.w3.org/2000/svg"> <?xml version="1.0" encoding="UTF-8"?>
<g clip-path="url(#clip0_7504_18610)"> <svg id="_图层_2" data-name="图层 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 74.87 89.85">
<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"/> <defs>
<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"/> <style>
</g> .cls-1 {
<defs> fill: #e6ff54;
<clipPath id="clip0_7504_18610"> }
<rect width="24" height="32" fill="white"/> </style>
</clipPath> </defs>
</defs> <g id="_图层_1-2" data-name=" 图层 1">
</svg> <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

View File

@@ -1,6 +1,7 @@
import Taro from "@tarojs/taro"; import Taro from "@tarojs/taro";
import { check_login_status, get_user_info } from "@/services/loginService"; import { check_login_status, get_user_info } from "@/services/loginService";
import { useUser } from "@/store/userStore"; import { useUser } from "@/store/userStore";
import { Dayjs } from "dayjs";
// 普通函数,不调用 useLoad // 普通函数,不调用 useLoad
export const sceneRedirectLogic = (options, defaultPage: string) => { export const sceneRedirectLogic = (options, defaultPage: string) => {
@@ -135,3 +136,31 @@ export function genNTRPRequirementText(min, max) {
export function isPhoneNumber(str) { export function isPhoneNumber(str) {
return /^1[3-9]\d{9}$/.test(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))}小时`;
}

View File

@@ -14,10 +14,20 @@ export interface LocationInfo {
name?: string 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> => { export const getCurrentLocation = (): Promise<LocationInfo> => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
Taro.getLocation({ ;(Taro as any).getLocation({
type: 'wgs84', type: 'wgs84',
success: (res) => { success: (res) => {
console.log('===获取地理位置', res) console.log('===获取地理位置', res)
@@ -27,7 +37,7 @@ export const getCurrentLocation = (): Promise<LocationInfo> => {
resolve({ resolve({
latitude: res.latitude, latitude: res.latitude,
longitude: res.longitude, longitude: res.longitude,
address address: normalize_address(address)
}) })
}) })
.catch(() => { .catch(() => {
@@ -48,12 +58,12 @@ export const getCurrentLocation = (): Promise<LocationInfo> => {
// 选择地图位置 // 选择地图位置
export const chooseLocation = (): Promise<LocationInfo> => { export const chooseLocation = (): Promise<LocationInfo> => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
Taro.chooseLocation({ ;(Taro as any).chooseLocation({
success: (res) => { success: (res) => {
resolve({ resolve({
latitude: res.latitude, latitude: res.latitude,
longitude: res.longitude, longitude: res.longitude,
address: res.address, address: normalize_address(res.address),
name: res.name name: res.name
}) })
}, },
@@ -66,7 +76,7 @@ export const chooseLocation = (): Promise<LocationInfo> => {
export const getLocation = (): Promise<Location> => { export const getLocation = (): Promise<Location> => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
Taro.getLocation({ ;(Taro as any).getLocation({
success: (res) => { success: (res) => {
resolve({ resolve({
latitude: res.latitude, latitude: res.latitude,

View File

@@ -106,6 +106,12 @@ const drawLabel = (ctx: any, x: number, y: number, width: number, height: number
ctx.restore() 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 // 工具函数 - 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) => {
@@ -117,7 +123,7 @@ const loadImage = (src: string): Promise<any> => {
const img = off.createImage() const img = off.createImage()
img.onload = () => resolve(img) img.onload = () => resolve(img)
img.onerror = reject img.onerror = reject
img.src = src img.src = with_cache_bust(src)
} catch (e) { } catch (e) {
reject(e) reject(e)
} }