格式化日期

This commit is contained in:
2025-10-20 16:32:48 +08:00
parent 9297263dbb
commit 801dfb2963
4 changed files with 227 additions and 196 deletions

View File

@@ -8,6 +8,7 @@ import "./index.scss";
const ListCard: React.FC<ListCardProps> = ({ const ListCard: React.FC<ListCardProps> = ({
id, id,
title, title,
original_start_time,
start_time, start_time,
end_time, end_time,
location, location,
@@ -25,15 +26,14 @@ const ListCard: React.FC<ListCardProps> = ({
venue_description, venue_description,
game_type, // 球局类型 game_type, // 球局类型
}) => { }) => {
// 参与者要前三个数据 // 参与者要前三个数据
const participantsImageList = participants?.slice(0, 3) || []; const participantsImageList = participants?.slice(0, 3) || [];
// 场地 要第一个数据 // 场地 要第一个数据
// const venueImageList = venue_image_list?.slice(0, 1) || []; // const venueImageList = venue_image_list?.slice(0, 1) || [];
const venueImage = venue_image_list?.[0]?.url || ''; const venueImage = venue_image_list?.[0]?.url || "";
// 是否显示畅打球局 // 是否显示畅打球局
const isShowSmoothPlayingGame = game_type === '畅打球局'; const isShowSmoothPlayingGame = game_type === "畅打球局";
const renderItemImage = (src: string) => { const renderItemImage = (src: string) => {
return ( return (
@@ -114,10 +114,13 @@ const ListCard: React.FC<ListCardProps> = ({
{/* 时间信息 */} {/* 时间信息 */}
<View className="date-time"> <View className="date-time">
<Text>{formatGameTime(start_time)}</Text> <Text>{formatGameTime(original_start_time || start_time)}</Text>
{/* 时长 如 2小时 */} {/* 时长 如 2小时 */}
{end_time && ( {end_time && (
<Text> {calculateDuration(start_time, end_time)}</Text> <Text>
{" "}
{calculateDuration(original_start_time || start_time, end_time)}
</Text>
)} )}
</View> </View>
@@ -176,7 +179,8 @@ const ListCard: React.FC<ListCardProps> = ({
<View className="image-section">{renderImages()}</View> <View className="image-section">{renderImages()}</View>
</View> </View>
{/* 畅打球局 */} {/* 畅打球局 */}
{isShowSmoothPlayingGame && <View className="smoothPlayingGame"> {isShowSmoothPlayingGame && (
<View className="smoothPlayingGame">
<View className="smoothWrapper"> <View className="smoothWrapper">
<Image <Image
src={img.ICON_LIST_PLAYING_GAME} src={img.ICON_LIST_PLAYING_GAME}
@@ -187,13 +191,11 @@ const ListCard: React.FC<ListCardProps> = ({
<View className="line" /> <View className="line" />
<View className="localAreaTitle">:</View> <View className="localAreaTitle">:</View>
<View className="localAreaWrapper"> <View className="localAreaWrapper">
<Image <Image className="localArea" src={venueImage} />
className="localArea"
src={venueImage}
/>
<Text className="localAreaText">{venue_description}</Text> <Text className="localAreaText">{venue_description}</Text>
</View> </View>
</View>} </View>
)}
</View> </View>
); );
}; };

View File

@@ -86,7 +86,15 @@ const ListContainer = (props) => {
const renderList = (list) => { const renderList = (list) => {
// 请求数据为空 // 请求数据为空
if (isShowNoData) { if (isShowNoData) {
return <ListLoadError reload={reload} errorImg={errorImg} btnText={btnText} btnImg={btnImg} text={emptyText || "暂无数据"} />; return (
<ListLoadError
reload={reload}
errorImg={errorImg}
btnText={btnText}
btnImg={btnImg}
text={emptyText || "暂无数据"}
/>
);
} }
// 渲染数据 // 渲染数据

View File

@@ -1,14 +1,14 @@
import dayjs from 'dayjs' import dayjs from "dayjs";
/** /**
* 获取下一个整点时间 * 获取下一个整点时间
* @returns 格式为 YYYY-MM-DD HH:mm 的字符串 * @returns 格式为 YYYY-MM-DD HH:mm 的字符串
*/ */
export const getNextHourTime = (): string => { export const getNextHourTime = (): string => {
const now = dayjs() const now = dayjs();
const nextHour = now.add(1, 'hour').startOf('hour') const nextHour = now.add(1, "hour").startOf("hour");
return nextHour.format('YYYY-MM-DD HH:mm') return nextHour.format("YYYY-MM-DD HH:mm");
} };
/** /**
* 根据开始时间计算结束时间2小时后 * 根据开始时间计算结束时间2小时后
@@ -16,69 +16,69 @@ export const getNextHourTime = (): string => {
* @returns 格式为 YYYY-MM-DD HH:mm 的字符串 * @returns 格式为 YYYY-MM-DD HH:mm 的字符串
*/ */
export const getEndTime = (startTime: string): string => { export const getEndTime = (startTime: string): string => {
const startDateTime = dayjs(startTime) const startDateTime = dayjs(startTime);
const endDateTime = startDateTime.add(2, 'hour') const endDateTime = startDateTime.add(2, "hour");
return endDateTime.format('YYYY-MM-DD HH:mm') return endDateTime.format("YYYY-MM-DD HH:mm");
} };
export const getDateStr = (date: Date): string => { export const getDateStr = (date: Date): string => {
return dayjs(date).format('YYYY-MM-DD HH:mm') return dayjs(date).format("YYYY-MM-DD HH:mm");
} };
export const getDate = (date: string): string => { export const getDate = (date: string): string => {
return dayjs(date).format('YYYY年MM月DD日') return dayjs(date).format("YYYY年MM月DD日");
} };
export const getDay = (date?: string | Date): string => { export const getDay = (date?: string | Date): string => {
if (!date) { if (!date) {
return dayjs().format('YYYY-MM-DD') return dayjs().format("YYYY-MM-DD");
} }
return dayjs(date).format('YYYY-MM-DD') return dayjs(date).format("YYYY-MM-DD");
} };
export const getMonth = (date?: string | Date): string => { export const getMonth = (date?: string | Date): string => {
if (!date) { if (!date) {
return dayjs().format('MM月 YYYY') return dayjs().format("MM月 YYYY");
} }
return dayjs(date).format('MM月 YYYY') return dayjs(date).format("MM月 YYYY");
} };
export const getWeekend = (date?: string | Date): [Date, Date] => { export const getWeekend = (date?: string | Date): [Date, Date] => {
const today = dayjs(date); const today = dayjs(date);
const currentDayOfWeek = today.day(); const currentDayOfWeek = today.day();
console.log('currentDayOfWeek', currentDayOfWeek) console.log("currentDayOfWeek", currentDayOfWeek);
const saturdayOffset = 6 - currentDayOfWeek const saturdayOffset = 6 - currentDayOfWeek;
const sundayOffset = 7 - currentDayOfWeek const sundayOffset = 7 - currentDayOfWeek;
const sat = today.add(saturdayOffset, 'day') const sat = today.add(saturdayOffset, "day");
const sun = today.add(sundayOffset, 'day') const sun = today.add(sundayOffset, "day");
return [sat.toDate(), sun.toDate()] return [sat.toDate(), sun.toDate()];
} };
export const getWeekendOfCurrentWeek = (days = 7): Date[] => { export const getWeekendOfCurrentWeek = (days = 7): Date[] => {
const dayList: Date[] = []; const dayList: Date[] = [];
for (let i = 0; i < days; i++) { for (let i = 0; i < days; i++) {
const day = dayjs().add(i, 'day').toDate() const day = dayjs().add(i, "day").toDate();
dayList.push(day) dayList.push(day);
} }
return dayList return dayList;
} };
export const getTime = (time: string): string => { export const getTime = (time: string): string => {
const timeObj = dayjs(time) const timeObj = dayjs(time);
const hour = timeObj.hour() const hour = timeObj.hour();
const minute = timeObj.minute() const minute = timeObj.minute();
// 判断是上午还是下午 // 判断是上午还是下午
const period = hour <= 12 ? 'AM' : 'PM' const period = hour <= 12 ? "AM" : "PM";
// 转换为12小时制 // 转换为12小时制
const hour12 = hour === 0 ? 0 : hour > 12 ? hour - 12 : hour const hour12 = hour === 0 ? 0 : hour > 12 ? hour - 12 : hour;
// 格式化分钟,保证两位数 // 格式化分钟,保证两位数
const minuteStr = minute.toString().padStart(2, '0') const minuteStr = minute.toString().padStart(2, "0");
return `${hour12}:${minuteStr} ${period}` return `${hour12}:${minuteStr} ${period}`;
} };
/** /**
* 格式化时间显示(相对时间) * 格式化时间显示(相对时间)
@@ -96,7 +96,9 @@ export const formatRelativeTime = (timeStr: string): string => {
const year = d.getFullYear(); const year = d.getFullYear();
const month = d.getMonth() + 1; const month = d.getMonth() + 1;
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);
@@ -112,12 +114,16 @@ export const formatRelativeTime = (timeStr: string): string => {
// 今天 // 今天
const hours = date.getHours(); const hours = date.getHours();
const minutes = date.getMinutes(); const minutes = date.getMinutes();
return `今天 ${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`; return `今天 ${hours.toString().padStart(2, "0")}:${minutes
.toString()
.padStart(2, "0")}`;
} else if (diffDays === 1) { } else if (diffDays === 1) {
// 昨天 // 昨天
const hours = date.getHours(); const hours = date.getHours();
const minutes = date.getMinutes(); const minutes = date.getMinutes();
return `昨天 ${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`; return `昨天 ${hours.toString().padStart(2, "0")}:${minutes
.toString()
.padStart(2, "0")}`;
} else if (diffDays < 7) { } else if (diffDays < 7) {
// 一周内显示天数 // 一周内显示天数
return `${diffDays}天前`; return `${diffDays}天前`;
@@ -128,9 +134,13 @@ export const formatRelativeTime = (timeStr: string): string => {
const day = date.getDate(); const day = date.getDate();
const hours = date.getHours(); const hours = date.getHours();
const minutes = date.getMinutes(); const minutes = date.getMinutes();
return `${year}-${month.toString().padStart(2, '0')}-${day.toString().padStart(2, '0')} ${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`; return `${year}-${month.toString().padStart(2, "0")}-${day
.toString()
.padStart(2, "0")} ${hours.toString().padStart(2, "0")}:${minutes
.toString()
.padStart(2, "0")}`;
} }
} };
/** /**
* 格式化时间显示(简短相对时间) * 格式化时间显示(简短相对时间)
@@ -148,7 +158,9 @@ export const formatShortRelativeTime = (timeStr: string): string => {
const year = d.getFullYear(); const year = d.getFullYear();
const month = d.getMonth() + 1; const month = d.getMonth() + 1;
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);
@@ -182,7 +194,7 @@ export const formatShortRelativeTime = (timeStr: string): string => {
const day = date.getDate(); const day = date.getDate();
return `${month}${day}`; return `${month}${day}`;
} }
} };
/** /**
* 格式化球局时间显示(例如:明天(周五)下午5点 * 格式化球局时间显示(例如:明天(周五)下午5点
@@ -196,7 +208,7 @@ export const formatGameTime = (timeStr: string): string => {
const now = new Date(); const now = new Date();
// 获取星期几 // 获取星期几
const weekDays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']; const weekDays = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"];
const weekDay = weekDays[date.getDay()]; const weekDay = weekDays[date.getDay()];
// 获取小时和分钟 // 获取小时和分钟
@@ -204,30 +216,33 @@ export const formatGameTime = (timeStr: string): string => {
const minutes = date.getMinutes(); const minutes = date.getMinutes();
// 判断上午/下午/晚上 // 判断上午/下午/晚上
let period = ''; let period = "";
let displayHour = hours; let displayHour = hours;
if (hours >= 0 && hours < 6) { if (hours >= 0 && hours < 6) {
period = '凌晨'; period = "凌晨";
} else if (hours >= 6 && hours < 12) { } else if (hours >= 6 && hours < 12) {
period = '上午'; period = "上午";
} else if (hours >= 12 && hours < 18) { } else if (hours >= 12 && hours < 18) {
period = '下午'; period = "下午";
displayHour = hours === 12 ? 12 : hours - 12; displayHour = hours === 12 ? 12 : hours - 12;
} else { } else {
period = '晚上'; period = "晚上";
displayHour = hours - 12; displayHour = hours - 12;
} }
// 格式化时间部分 // 格式化时间部分
const timeStr2 = minutes === 0 ? `${displayHour}` : `${displayHour}${minutes}`; const timeStr2 =
minutes === 0 ? `${displayHour}` : `${displayHour}${minutes}`;
// 获取日期部分(年-月-日),忽略时间 // 获取日期部分(年-月-日),忽略时间
const getDateString = (d: Date) => { const getDateString = (d: Date) => {
const year = d.getFullYear(); const year = d.getFullYear();
const month = d.getMonth() + 1; const month = d.getMonth() + 1;
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);
@@ -258,7 +273,7 @@ export const formatGameTime = (timeStr: string): string => {
const day = date.getDate(); const day = date.getDate();
return `${month}${day}日(${weekDay})${period}${timeStr2}`; return `${month}${day}日(${weekDay})${period}${timeStr2}`;
} }
} };
/** /**
* 计算时长(结束时间 - 开始时间) * 计算时长(结束时间 - 开始时间)
@@ -266,7 +281,10 @@ export const formatGameTime = (timeStr: string): string => {
* @param endTime 结束时间字符串 * @param endTime 结束时间字符串
* @returns 格式化后的时长字符串2小时、1.5小时、30分钟 * @returns 格式化后的时长字符串2小时、1.5小时、30分钟
*/ */
export const calculateDuration = (startTime: string, endTime: string): string => { export const calculateDuration = (
startTime: string,
endTime: string
): string => {
if (!startTime || !endTime) return ""; if (!startTime || !endTime) return "";
const start = new Date(startTime); const start = new Date(startTime);
@@ -292,20 +310,19 @@ export const calculateDuration = (startTime: string, endTime: string): string =>
} else { } else {
return ""; return "";
} }
} };
export const getOneMonth = (): Date[] => { export const getOneMonth = (): Date[] => {
const dates: Date[] = []; const dates: Date[] = [];
const currentDate = dayjs(); const currentDate = dayjs();
const nextMonth = dayjs().add(1, 'month'); const nextMonth = dayjs().add(1, "month");
// 从当前日期开始,遍历到下个月的同一天 // 从当前日期开始,遍历到下个月的同一天
let date = currentDate; let date = currentDate;
while (date.isBefore(nextMonth) || date.isSame(nextMonth, 'day')) { while (date.isBefore(nextMonth) || date.isSame(nextMonth, "day")) {
dates.push(date.toDate()); dates.push(date.toDate());
date = date.add(1, 'day'); date = date.add(1, "day");
} }
return dates; return dates;
} };

View File

@@ -1,58 +1,58 @@
// 网球比赛数据接口 // 网球比赛数据接口
export interface TennisMatch { export interface TennisMatch {
id: number id: number;
title: string title: string;
dateTime: string dateTime: string;
location: string location: string;
distance: string distance: string;
registeredCount: number registeredCount: number;
maxCount: number maxCount: number;
skillLevel: string skillLevel: string;
matchType: string matchType: string;
images: string[] images: string[];
shinei: string, shinei: string;
end_time: string, end_time: string;
} }
export interface IFilterOptions { export interface IFilterOptions {
dateRange: [string, string], // 日期区间 dateRange: [string, string]; // 日期区间
timeSlot?: string, // 时间段 timeSlot?: string; // 时间段
ntrp?: [number, number], // NTRP 水平区间 ntrp?: [number, number]; // NTRP 水平区间
venueType?: string, // 场地类型 venueType?: string; // 场地类型
playType?: string, // 玩法 playType?: string; // 玩法
distanceFilter?: string distanceFilter?: string;
} }
// 页面状态接口 // 页面状态接口
export interface PageState { export interface PageState {
data: TennisMatch[] data: TennisMatch[];
recommendList: TennisMatch[] recommendList: TennisMatch[];
isShowFilterPopup: boolean isShowFilterPopup: boolean;
filterOptions: IFilterOptions filterOptions: IFilterOptions;
distanceQuickFilter: { distanceQuickFilter: {
distanceFilter: string distanceFilter: string;
order: string order: string;
} };
filterCount: number filterCount: number;
pageOption: { pageOption: {
page: number page: number;
pageSize: number pageSize: number;
} };
gamesNum: number gamesNum: number;
isHasMoreData: boolean isHasMoreData: boolean;
isShowNoData: boolean isShowNoData: boolean;
} }
// 列表页状态 // 列表页状态
export interface ListPageState extends PageState { export interface ListPageState extends PageState {
isShowInputCustomerNavBar: boolean isShowInputCustomerNavBar: boolean;
} }
// 搜索页状态 // 搜索页状态
export interface SearchPageState extends PageState { export interface SearchPageState extends PageState {
data: TennisMatch[] data: TennisMatch[];
suggestionList: string[] suggestionList: string[];
isShowSuggestion: boolean isShowSuggestion: boolean;
searchHistory: { id: number, title: string }[] searchHistory: { id: number; title: string }[];
searchHistoryParams: Record<string, any> searchHistoryParams: Record<string, any>;
} }
export interface CityTree { export interface CityTree {
@@ -62,60 +62,64 @@ export interface CityTree {
} }
export interface CityQrCodeItem { export interface CityQrCodeItem {
id: number, id: number;
city_name: string, city_name: string;
qr_code_url: string, qr_code_url: string;
description: string, description: string;
sort_order: number sort_order: number;
} }
// 主状态接口 // 主状态接口
export interface ListState { export interface ListState {
currentPage: string currentPage: string;
isSearchResult: boolean isSearchResult: boolean;
listPageState: ListPageState listPageState: ListPageState;
searchPageState: SearchPageState searchPageState: SearchPageState;
location: { location: {
latitude: number latitude: number;
longitude: number longitude: number;
} };
loading: boolean loading: boolean;
error: string | null error: string | null;
searchValue: string searchValue: string;
distanceData: any[] distanceData: any[];
quickFilterData: any[] quickFilterData: any[];
timeBubbleData: BubbleOption[] timeBubbleData: BubbleOption[];
dateRangeOptions: BubbleOption[] dateRangeOptions: BubbleOption[];
gamesNum: number gamesNum: number;
cities: CityTree[] cities: CityTree[];
cityQrCode: CityQrCodeItem[] cityQrCode: CityQrCodeItem[];
area: [string, string, string] area: [string, string, string];
} }
export interface ListActions { export interface ListActions {
fetchMatches: (params?: Record<string, any>, isFirstLoad?: Boolean, isAppend?: boolean) => Promise<void> fetchMatches: (
params?: Record<string, any>,
isFirstLoad?: Boolean,
isAppend?: boolean
) => Promise<void>;
// getIntegrateListData: (params?: Record<string, any>) => Promise<void> // getIntegrateListData: (params?: Record<string, any>) => Promise<void>
getMatchesData: () => void getMatchesData: () => void;
clearError: () => void clearError: () => void;
updateState: (payload: Record<string, any>) => void updateState: (payload: Record<string, any>) => void;
updateListPageState: (payload: Record<string, any>) => void updateListPageState: (payload: Record<string, any>) => void;
updateSearchPageState: (payload: Record<string, any>) => void updateSearchPageState: (payload: Record<string, any>) => void;
updateFilterOptions: (payload: Record<string, any>) => void updateFilterOptions: (payload: Record<string, any>) => void;
clearFilterOptions: () => void clearFilterOptions: () => void;
getSearchHistory: () => Promise<void> getSearchHistory: () => Promise<void>;
clearHistory: () => void clearHistory: () => void;
searchSuggestion: (val: string) => Promise<void> searchSuggestion: (val: string) => Promise<void>;
getSearchParams: () => Record<string, any> getSearchParams: () => Record<string, any>;
loadMoreMatches: () => void loadMoreMatches: () => void;
initialFilterSearch: (isSearchData?: boolean) => void initialFilterSearch: (isSearchData?: boolean) => void;
setListData: (payload: IPayload) => void setListData: (payload: IPayload) => void;
fetchGetGamesCount: () => Promise<void> fetchGetGamesCount: () => Promise<void>;
getCurrentPageState: () => { currentPageState: any; currentPageKey: string } getCurrentPageState: () => { currentPageState: any; currentPageKey: string };
updateCurrentPageState: (payload: Record<string, any>) => void updateCurrentPageState: (payload: Record<string, any>) => void;
updateDistanceQuickFilter: (payload: Record<string, any>) => void updateDistanceQuickFilter: (payload: Record<string, any>) => void;
getCities: () => Promise<void> getCities: () => Promise<void>;
getCityQrCode: () => Promise<void> getCityQrCode: () => Promise<void>;
updateArea: (payload: [string, string, string]) => void updateArea: (payload: [string, string, string]) => void;
} }
export interface IPayload { export interface IPayload {
@@ -182,7 +186,7 @@ export interface BubbleProps {
export interface BubbleItemProps { export interface BubbleItemProps {
option: BubbleOption; option: BubbleOption;
isSelected: boolean; isSelected: boolean;
size: 'small' | 'medium' | 'large'; size: "small" | "medium" | "large";
disabled: boolean; disabled: boolean;
onClick: (option: BubbleOption) => void; onClick: (option: BubbleOption) => void;
itemClassName?: string; itemClassName?: string;
@@ -198,29 +202,29 @@ export interface FilterPopupProps {
onClear: () => void; onClear: () => void;
visible: boolean; visible: boolean;
onClose: () => void; onClose: () => void;
statusNavbarHeigh: number statusNavbarHeigh: number;
} }
// 列表卡片 // 列表卡片
export interface ListCardProps { export interface ListCardProps {
id: string | number, id: string | number;
title: string, title: string;
start_time: string, original_start_time?: string;
end_time?: string, // 结束时间 start_time: string;
location: string, end_time?: string; // 结束时间
distance_km: number, location: string;
current_players: number, distance_km: number;
max_players: number, current_players: number;
skill_level_min: number, max_players: number;
skill_level_max: number, skill_level_min: number;
play_type: string, skill_level_max: number;
image_list: string[], play_type: string;
court_type: string, image_list: string[];
court_type: string;
matchType: string; matchType: string;
shinei: string; shinei: string;
showSkeleton?: boolean; showSkeleton?: boolean;
key?: string key?: string;
participants: { participants: {
user: { user: {
avatar_url: string; avatar_url: string;