格式化日期

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

View File

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

View File

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