From 801dfb2963755d7df1b7695d6189e9cb9b624661 Mon Sep 17 00:00:00 2001 From: Ultrame <1019265060@qq.com> Date: Mon, 20 Oct 2025 16:32:48 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E6=97=A5=E6=9C=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ListCard/index.tsx | 46 +++--- src/container/listContainer/index.tsx | 12 +- src/utils/timeUtils.ts | 159 +++++++++++--------- types/list/types.ts | 206 +++++++++++++------------- 4 files changed, 227 insertions(+), 196 deletions(-) diff --git a/src/components/ListCard/index.tsx b/src/components/ListCard/index.tsx index 0336f5b..5bf8d6a 100644 --- a/src/components/ListCard/index.tsx +++ b/src/components/ListCard/index.tsx @@ -8,6 +8,7 @@ import "./index.scss"; const ListCard: React.FC = ({ id, title, + original_start_time, start_time, end_time, location, @@ -25,15 +26,14 @@ const ListCard: React.FC = ({ 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 = ({ {/* 时间信息 */} - {formatGameTime(start_time)} + {formatGameTime(original_start_time || start_time)} {/* 时长 如 2小时 */} {end_time && ( - {calculateDuration(start_time, end_time)} + + {" "} + {calculateDuration(original_start_time || start_time, end_time)} + )} @@ -176,24 +179,23 @@ const ListCard: React.FC = ({ {renderImages()} {/* 畅打球局 */} - {isShowSmoothPlayingGame && - - - {game_type} + {isShowSmoothPlayingGame && ( + + + + {game_type} + + + 场馆方: + + + {venue_description} + - - 场馆方: - - - {venue_description} - - } + )} ); }; diff --git a/src/container/listContainer/index.tsx b/src/container/listContainer/index.tsx index db54b4c..93ee7b4 100644 --- a/src/container/listContainer/index.tsx +++ b/src/container/listContainer/index.tsx @@ -34,7 +34,7 @@ const ListContainer = (props) => { return; } // timerRef.current = setTimeout(() => { - loadMoreMatches(); + loadMoreMatches(); // }, 500); }); @@ -86,7 +86,15 @@ const ListContainer = (props) => { const renderList = (list) => { // 请求数据为空 if (isShowNoData) { - return ; + return ( + + ); } // 渲染数据 diff --git a/src/utils/timeUtils.ts b/src/utils/timeUtils.ts index 5cef7a1..d422578 100644 --- a/src/utils/timeUtils.ts +++ b/src/utils/timeUtils.ts @@ -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().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 => { 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] => { 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) + const day = dayjs().add(i, "day").toDate(); + dayList.push(day); } - return dayList -} + 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') - - return `${hour12}:${minuteStr} ${period}` -} + const minuteStr = minute.toString().padStart(2, "0"); + + return `${hour12}:${minuteStr} ${period}`; +}; /** * 格式化时间显示(相对时间) @@ -87,21 +87,23 @@ export const getTime = (time: string): string => { */ export const formatRelativeTime = (timeStr: string): string => { if (!timeStr) return ""; - + const date = new Date(timeStr); const now = new Date(); - + // 获取日期部分(年-月-日),忽略时间 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); const nowStr = getDateString(now); - + // 计算日期差 const dateObj = new Date(dateStr); const nowObj = new Date(nowStr); @@ -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; -} \ No newline at end of file +}; diff --git a/types/list/types.ts b/types/list/types.ts index c29ae66..927661f 100644 --- a/types/list/types.ts +++ b/types/list/types.ts @@ -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 + data: TennisMatch[]; + suggestionList: string[]; + isShowSuggestion: boolean; + searchHistory: { id: number; title: string }[]; + searchHistoryParams: Record; } 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, isFirstLoad?: Boolean, isAppend?: boolean) => Promise + fetchMatches: ( + params?: Record, + isFirstLoad?: Boolean, + isAppend?: boolean + ) => Promise; // getIntegrateListData: (params?: Record) => Promise - getMatchesData: () => void - clearError: () => void - updateState: (payload: Record) => void - updateListPageState: (payload: Record) => void - updateSearchPageState: (payload: Record) => void - updateFilterOptions: (payload: Record) => void - clearFilterOptions: () => void - getSearchHistory: () => Promise - clearHistory: () => void - searchSuggestion: (val: string) => Promise - getSearchParams: () => Record - loadMoreMatches: () => void - initialFilterSearch: (isSearchData?: boolean) => void - setListData: (payload: IPayload) => void - fetchGetGamesCount: () => Promise - getCurrentPageState: () => { currentPageState: any; currentPageKey: string } - updateCurrentPageState: (payload: Record) => void - updateDistanceQuickFilter: (payload: Record) => void - getCities: () => Promise - getCityQrCode: () => Promise - updateArea: (payload: [string, string, string]) => void + getMatchesData: () => void; + clearError: () => void; + updateState: (payload: Record) => void; + updateListPageState: (payload: Record) => void; + updateSearchPageState: (payload: Record) => void; + updateFilterOptions: (payload: Record) => void; + clearFilterOptions: () => void; + getSearchHistory: () => Promise; + clearHistory: () => void; + searchSuggestion: (val: string) => Promise; + getSearchParams: () => Record; + loadMoreMatches: () => void; + initialFilterSearch: (isSearchData?: boolean) => void; + setListData: (payload: IPayload) => void; + fetchGetGamesCount: () => Promise; + getCurrentPageState: () => { currentPageState: any; currentPageKey: string }; + updateCurrentPageState: (payload: Record) => void; + updateDistanceQuickFilter: (payload: Record) => void; + getCities: () => Promise; + getCityQrCode: () => Promise; + 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; @@ -232,4 +236,4 @@ export interface ListCardProps { venue_description: string; game_type: string; type: string; -} \ No newline at end of file +}