修改 列表 时间格式化问题

This commit is contained in:
张成
2025-10-18 20:17:31 +08:00
parent da72c92458
commit 9f5fdfd1a5
5 changed files with 239 additions and 30 deletions

View File

@@ -2,12 +2,14 @@ import { View, Text, Image } from "@tarojs/components";
import Taro from "@tarojs/taro"; import Taro from "@tarojs/taro";
import img from "../../config/images"; import img from "../../config/images";
import { ListCardProps } from "../../../types/list/types"; import { ListCardProps } from "../../../types/list/types";
import { formatGameTime, calculateDuration } from "@/utils/timeUtils";
import "./index.scss"; import "./index.scss";
const ListCard: React.FC<ListCardProps> = ({ const ListCard: React.FC<ListCardProps> = ({
id, id,
title, title,
start_time, start_time,
end_time,
location, location,
distance_km, distance_km,
current_players, current_players,
@@ -112,7 +114,11 @@ const ListCard: React.FC<ListCardProps> = ({
{/* 时间信息 */} {/* 时间信息 */}
<View className="date-time"> <View className="date-time">
<Text>{start_time}</Text> <Text>{formatGameTime(start_time)}</Text>
{/* 时长 如 2小时 */}
{end_time && (
<Text> {calculateDuration(start_time, end_time)}</Text>
)}
</View> </View>
{/* 地点,室内外,距离 */} {/* 地点,室内外,距离 */}

View File

@@ -8,24 +8,16 @@ const LoginPage: React.FC = () => {
const [is_loading, set_is_loading] = useState(false); const [is_loading, set_is_loading] = useState(false);
const [agree_terms, set_agree_terms] = useState(false); const [agree_terms, set_agree_terms] = useState(false);
const [show_terms_layer, set_show_terms_layer] = useState(false); const [show_terms_layer, set_show_terms_layer] = useState(false);
const [pending_login_type, set_pending_login_type] = useState<'wechat' | 'phone' | null>(null); // 记录待执行的登录类型
const [pending_phone_event, set_pending_phone_event] = useState<any>(null); // 记录微信登录的事件数据
const { params: { redirect } } = useRouter(); const { params: { redirect } } = useRouter();
// 微信授权登录 // 执行微信登录的核心逻辑
const handle_wechat_login = async (e: any) => { const execute_wechat_login = async (e: any) => {
if (!agree_terms) {
set_show_terms_layer(true);
Taro.showToast({
title: '请先同意用户协议',
icon: 'none',
duration: 2000
});
return;
}
// 检查是否获取到手机号 // 检查是否获取到手机号
if (!e.detail || !e.detail.code) { if (!e.detail || !e.detail.code) {
Taro.showToast({ Taro.showToast({
@@ -68,9 +60,12 @@ const LoginPage: React.FC = () => {
} }
}; };
// 手机号验证码登录 // 微信授权登录
const handle_phone_login = async () => { const handle_wechat_login = async (e: any) => {
if (!agree_terms) { if (!agree_terms) {
// 记录待执行的登录类型和事件数据
set_pending_login_type('wechat');
set_pending_phone_event(e);
set_show_terms_layer(true); set_show_terms_layer(true);
Taro.showToast({ Taro.showToast({
title: '请先同意用户协议', title: '请先同意用户协议',
@@ -80,16 +75,54 @@ const LoginPage: React.FC = () => {
return; return;
} }
// 如果已同意条款,直接执行登录
await execute_wechat_login(e);
};
// 执行手机号登录的核心逻辑
const execute_phone_login = () => {
// 跳转到验证码页面 // 跳转到验证码页面
Taro.navigateTo({ Taro.navigateTo({
url: `/login_pages/verification/index?redirect=${redirect}` url: `/login_pages/verification/index?redirect=${redirect}`
}); });
}; };
// 同意协议并关闭浮层 // 手机号验证码登录
const handle_phone_login = async () => {
if (!agree_terms) {
// 记录待执行的登录类型
set_pending_login_type('phone');
set_show_terms_layer(true);
Taro.showToast({
title: '请先同意用户协议',
icon: 'none',
duration: 2000
});
return;
}
// 如果已同意条款,直接执行登录
execute_phone_login();
};
// 同意协议并关闭浮层,继续执行未完成的登录
const handle_agree_terms = () => { const handle_agree_terms = () => {
set_agree_terms(true); set_agree_terms(true);
set_show_terms_layer(false); set_show_terms_layer(false);
// 根据待执行的登录类型,继续执行登录
if (pending_login_type === 'wechat' && pending_phone_event) {
// 继续执行微信登录
execute_wechat_login(pending_phone_event);
// 清空待执行状态
set_pending_login_type(null);
set_pending_phone_event(null);
} else if (pending_login_type === 'phone') {
// 继续执行手机号登录
execute_phone_login();
// 清空待执行状态
set_pending_login_type(null);
}
}; };
// 切换协议同意状态(复选框用) // 切换协议同意状态(复选框用)

View File

@@ -10,8 +10,10 @@ import {
import "./index.scss"; import "./index.scss";
const VerificationPage: React.FC = () => { const VerificationPage: React.FC = () => {
const [phone, setPhone] = useState(""); const [phone, setPhone] = useState(""); // 存储纯数字的手机号
const [verification_code, setVerificationCode] = useState(""); const [display_phone, setDisplayPhone] = useState(""); // 显示带空格的手机号
const [verification_code, setVerificationCode] = useState(""); // 存储纯数字的验证码
const [display_code, setDisplayCode] = useState(""); // 显示带空格的验证码
const [countdown, setCountdown] = useState(0); const [countdown, setCountdown] = useState(0);
const [can_send_code, setCanSendCode] = useState(true); const [can_send_code, setCanSendCode] = useState(true);
const [is_loading, setIsLoading] = useState(false); const [is_loading, setIsLoading] = useState(false);
@@ -23,6 +25,62 @@ const VerificationPage: React.FC = () => {
params: { redirect }, params: { redirect },
} = useRouter(); } = useRouter();
// 格式化手机号为 3-4-4 格式
const formatPhone = (value: string): string => {
// 移除所有非数字字符
const numbers = value.replace(/\D/g, "");
// 按 3-4-4 格式添加空格
if (numbers.length <= 3) {
return numbers;
} else if (numbers.length <= 7) {
return `${numbers.slice(0, 3)} ${numbers.slice(3)}`;
} else {
return `${numbers.slice(0, 3)} ${numbers.slice(3, 7)} ${numbers.slice(7, 11)}`;
}
};
// 格式化验证码为 3-3 格式
const formatCode = (value: string): string => {
// 移除所有非数字字符
const numbers = value.replace(/\D/g, "");
// 按 3-3 格式添加空格
if (numbers.length <= 3) {
return numbers;
} else {
return `${numbers.slice(0, 3)} ${numbers.slice(3, 6)}`;
}
};
// 处理手机号输入
const handlePhoneInput = (e) => {
const inputValue = e.detail.value;
// 移除所有非数字字符
const numbers = inputValue.replace(/\D/g, "");
// 限制最多11位
const limitedNumbers = numbers.slice(0, 11);
// 保存纯数字版本用于提交
setPhone(limitedNumbers);
// 保存格式化版本用于显示
setDisplayPhone(formatPhone(limitedNumbers));
};
// 处理验证码输入
const handleCodeInput = (e) => {
const inputValue = e.detail.value;
// 移除所有非数字字符
const numbers = inputValue.replace(/\D/g, "");
// 限制最多6位
const limitedNumbers = numbers.slice(0, 6);
// 保存纯数字版本用于提交
setVerificationCode(limitedNumbers);
// 保存格式化版本用于显示
setDisplayCode(formatCode(limitedNumbers));
};
// 计算登录按钮是否应该启用 // 计算登录按钮是否应该启用
const can_login = const can_login =
phone.length === 11 && verification_code.length === 6 && !is_loading; phone.length === 11 && verification_code.length === 6 && !is_loading;
@@ -69,6 +127,7 @@ const VerificationPage: React.FC = () => {
setCodeInputFocus(true); setCodeInputFocus(true);
// 清空验证码,让用户重新输入 // 清空验证码,让用户重新输入
setVerificationCode(""); setVerificationCode("");
setDisplayCode("");
console.log("设置验证码输入框聚焦"); console.log("设置验证码输入框聚焦");
}, 500); // 延迟500ms确保Toast显示完成后再聚焦 }, 500); // 延迟500ms确保Toast显示完成后再聚焦
} else { } else {
@@ -232,12 +291,12 @@ const VerificationPage: React.FC = () => {
<View className="input_container"> <View className="input_container">
<Input <Input
className="phone_input" className="phone_input"
type="number" type="text"
placeholder="输入中国内地手机号" placeholder="输入中国内地手机号"
placeholderClass="input_placeholder" placeholderClass="input_placeholder"
value={phone} value={display_phone}
onInput={(e) => setPhone(e.detail.value)} onInput={handlePhoneInput}
maxlength={11} maxlength={13}
/> />
<View className="char_count"> <View className="char_count">
<Text <Text
@@ -262,11 +321,11 @@ const VerificationPage: React.FC = () => {
placeholderClass="input_placeholder" placeholderClass="input_placeholder"
placeholderStyle="color:#999999;" placeholderStyle="color:#999999;"
focus={code_input_focus} focus={code_input_focus}
value={verification_code} value={display_code}
onInput={(e) => setVerificationCode(e.detail.value)} onInput={handleCodeInput}
onFocus={() => setCodeInputFocus(true)} onFocus={() => setCodeInputFocus(true)}
onBlur={() => setCodeInputFocus(false)} onBlur={() => setCodeInputFocus(false)}
maxlength={6} maxlength={7}
/> />
<View className="char_count"> <View className="char_count">
<Text <Text

View File

@@ -139,10 +139,10 @@ export const formatRelativeTime = (timeStr: string): string => {
*/ */
export const formatShortRelativeTime = (timeStr: string): string => { export const formatShortRelativeTime = (timeStr: string): string => {
if (!timeStr) return ""; if (!timeStr) return "";
const date = new Date(timeStr); const date = new Date(timeStr);
const now = new Date(); const now = new Date();
// 获取日期部分(年-月-日),忽略时间 // 获取日期部分(年-月-日),忽略时间
const getDateString = (d: Date) => { const getDateString = (d: Date) => {
const year = d.getFullYear(); const year = d.getFullYear();
@@ -150,10 +150,10 @@ export const formatShortRelativeTime = (timeStr: string): string => {
const day = d.getDate(); const day = d.getDate();
return `${year}-${month.toString().padStart(2, '0')}-${day.toString().padStart(2, '0')}`; return `${year}-${month.toString().padStart(2, '0')}-${day.toString().padStart(2, '0')}`;
}; };
const dateStr = getDateString(date); const dateStr = getDateString(date);
const nowStr = getDateString(now); const nowStr = getDateString(now);
// 计算日期差 // 计算日期差
const dateObj = new Date(dateStr); const dateObj = new Date(dateStr);
const nowObj = new Date(nowStr); const nowObj = new Date(nowStr);
@@ -165,7 +165,7 @@ export const formatShortRelativeTime = (timeStr: string): string => {
const diff = now.getTime() - date.getTime(); const diff = now.getTime() - date.getTime();
const minutes = Math.floor(diff / (1000 * 60)); const minutes = Math.floor(diff / (1000 * 60));
const hours = Math.floor(diff / (1000 * 60 * 60)); const hours = Math.floor(diff / (1000 * 60 * 60));
if (minutes < 1) { if (minutes < 1) {
return "刚刚"; return "刚刚";
} else if (minutes < 60) { } else if (minutes < 60) {
@@ -182,4 +182,114 @@ export const formatShortRelativeTime = (timeStr: string): string => {
const day = date.getDate(); const day = date.getDate();
return `${month}${day}`; return `${month}${day}`;
} }
}
/**
* 格式化球局时间显示(例如:明天(周五)下午5点
* @param timeStr 时间字符串
* @returns 格式化后的时间字符串
*/
export const formatGameTime = (timeStr: string): string => {
if (!timeStr) return "";
const date = new Date(timeStr);
const now = new Date();
// 获取星期几
const weekDays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
const weekDay = weekDays[date.getDay()];
// 获取小时和分钟
const hours = date.getHours();
const minutes = date.getMinutes();
// 判断上午/下午/晚上
let period = '';
let displayHour = hours;
if (hours >= 0 && hours < 6) {
period = '凌晨';
} else if (hours >= 6 && hours < 12) {
period = '上午';
} else if (hours >= 12 && hours < 18) {
period = '下午';
displayHour = hours === 12 ? 12 : hours - 12;
} else {
period = '晚上';
displayHour = hours - 12;
}
// 格式化时间部分
const timeStr2 = minutes === 0 ? `${displayHour}` : `${displayHour}${minutes}`;
// 获取日期部分(年-月-日),忽略时间
const getDateString = (d: Date) => {
const year = d.getFullYear();
const month = d.getMonth() + 1;
const day = d.getDate();
return `${year}-${month.toString().padStart(2, '0')}-${day.toString().padStart(2, '0')}`;
};
const dateStr = getDateString(date);
const nowStr = getDateString(now);
// 计算日期差
const dateObj = new Date(dateStr);
const nowObj = new Date(nowStr);
const diffTime = dateObj.getTime() - nowObj.getTime();
const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
// 根据日期差返回不同格式
if (diffDays === 0) {
// 今天
return `今天(${weekDay})${period}${timeStr2}`;
} else if (diffDays === 1) {
// 明天
return `明天(${weekDay})${period}${timeStr2}`;
} else if (diffDays === 2) {
// 后天
return `后天(${weekDay})${period}${timeStr2}`;
} else if (diffDays > 2 && diffDays <= 7) {
// 一周内
return `${weekDay}${period}${timeStr2}`;
} else {
// 超过一周,显示具体日期
const month = date.getMonth() + 1;
const day = date.getDate();
return `${month}${day}日(${weekDay})${period}${timeStr2}`;
}
}
/**
* 计算时长(结束时间 - 开始时间)
* @param startTime 开始时间字符串
* @param endTime 结束时间字符串
* @returns 格式化后的时长字符串2小时、1.5小时、30分钟
*/
export const calculateDuration = (startTime: string, endTime: string): string => {
if (!startTime || !endTime) return "";
const start = new Date(startTime);
const end = new Date(endTime);
// 计算时间差(毫秒)
const diffMs = end.getTime() - start.getTime();
// 转换为分钟
const diffMinutes = Math.floor(diffMs / (1000 * 60));
// 转换为小时
const hours = Math.floor(diffMinutes / 60);
const minutes = diffMinutes % 60;
// 格式化输出
if (hours > 0 && minutes > 0) {
return `${hours}.5小时`;
} else if (hours > 0) {
return `${hours}小时`;
} else if (minutes > 0) {
return `${minutes}分钟`;
} else {
return "";
}
} }

View File

@@ -207,6 +207,7 @@ export interface ListCardProps {
id: string | number, id: string | number,
title: string, title: string,
start_time: string, start_time: string,
end_time?: string, // 结束时间
location: string, location: string,
distance_km: number, distance_km: number,
current_players: number, current_players: number,