From 6f0c0c30faa6bf484ed2e9bf90dc70fc79d1d1b7 Mon Sep 17 00:00:00 2001 From: Ultrame <1019265060@qq.com> Date: Sun, 14 Sep 2025 11:46:29 +0800 Subject: [PATCH 01/90] =?UTF-8?q?=E5=AE=8C=E5=96=84=E4=B8=AA=E4=BA=BA?= =?UTF-8?q?=E9=A1=B5=EF=BC=88=E6=B5=8B=E8=AF=95NTRP=E6=B0=B4=E5=B9=B3?= =?UTF-8?q?=E6=9C=AA=E5=81=9A=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/UserInfo/index.scss | 65 ++++++++++--- src/components/UserInfo/index.tsx | 150 +++++++++++++++++++++++++---- src/config/api.ts | 3 +- src/services/userService.ts | 28 +++++- src/static/userInfo/info_edit.svg | 4 + src/user_pages/edit/index.scss | 30 +++++- src/user_pages/edit/index.tsx | 81 ++++++++++++---- src/user_pages/myself/index.tsx | 15 ++- 8 files changed, 317 insertions(+), 59 deletions(-) create mode 100644 src/static/userInfo/info_edit.svg diff --git a/src/components/UserInfo/index.scss b/src/components/UserInfo/index.scss index dfe55a1..d8feca9 100644 --- a/src/components/UserInfo/index.scss +++ b/src/components/UserInfo/index.scss @@ -210,7 +210,8 @@ gap: 8px; flex-wrap: wrap; - .tag_item { + .tag_item, + .button_edit { display: flex; align-items: center; gap: 4px; @@ -224,11 +225,7 @@ .tag_icon { width: 12px; height: 12px; - /* Frame 1912054928 */ - - - } .tag_text { @@ -240,15 +237,59 @@ color: #000000; } } + + .button_edit { + font-family: 'PingFang SC'; + font-weight: 500; + font-size: 11px; + line-height: 1.8em; + letter-spacing: -2.1%; + color: rgba(60, 60, 67, 0.6); + display: flex; + align-items: center; + position: relative; + padding-right: 20px; + + &::before, &::after { + content: ''; + width: 6px; + height: 1px; + display: inline-block; + background-color: rgba(60, 60, 67, 0.6); + position: absolute; + right: 12px; + transform: rotate(45deg); + margin-left: 4px; + } + &::after { + transform: rotate(-45deg); + translate: 4.2px 0; + } + } } - .bio_text { - font-family: 'PingFang SC'; - font-weight: 400; - font-size: 14px; - line-height: 1.571em; - color: rgba(0, 0, 0, 0.65); - white-space: pre-line; + .personal_profile { + .personal_profile_edit { + display: flex; + align-items: center; + gap: 4px; + cursor: pointer; + + .edit_icon { + width: 16px; + height: 16px; + } + } + + .bio_text { + font-family: 'PingFang SC'; + font-weight: 400; + font-size: 14px; + line-height: 1.571em; + color: rgba(0, 0, 0, 0.65); + white-space: pre-line; + } + } } } diff --git a/src/components/UserInfo/index.tsx b/src/components/UserInfo/index.tsx index 2cc71f1..3515970 100644 --- a/src/components/UserInfo/index.tsx +++ b/src/components/UserInfo/index.tsx @@ -1,8 +1,11 @@ -import React from 'react'; +import React, { useState } from 'react'; import Taro from '@tarojs/taro'; import { View, Text, Image, Button } from '@tarojs/components'; import './index.scss'; +import { EditModal } from '@/components'; +import { UserService } from '@/services/userService'; + // 用户信息接口 export interface UserInfo { id: string; @@ -21,9 +24,10 @@ export interface UserInfo { ntrp_level: string; phone?: string; gender?: string; - + bio?: string, latitude?: string, longitude?: string, + birthday?: string, } @@ -35,6 +39,7 @@ interface UserInfoCardProps { on_follow?: () => void; on_message?: () => void; on_share?: () => void; + set_user_info?: (info: UserInfo) => void; } @@ -51,8 +56,64 @@ export const UserInfoCard: React.FC = ({ is_following = false, on_follow, on_message, - on_share + on_share, + set_user_info, }) => { + console.log("用户信息xx:", user_info); + // 编辑个人简介弹窗状态 + const [edit_modal_visible, setEditModalVisible] = useState(false); + const [editing_field, setEditingField] = useState(''); + + // 表单状态 + const [form_data, setFormData] = useState({...user_info}); + + // 处理编辑弹窗 + const handle_open_edit_modal = (field: string) => { + if (field === 'nickname') { + // 手动输入 + setEditingField(field); + setEditModalVisible(true); + } else { + setEditingField(field); + setEditModalVisible(true); + } + }; + const handle_edit_modal_save = async (value: string) => { + try { + // 调用更新用户信息接口,只传递修改的字段 + const update_data = { [editing_field]: value }; + await UserService.update_user_info(update_data); + + // 更新本地状态 + setFormData(prev => { + const updated = { ...prev, [editing_field]: value }; + typeof set_user_info === 'function' && set_user_info(updated); + return updated; + }); + + // 关闭弹窗 + setEditModalVisible(false); + setEditingField(''); + + // 显示成功提示 + Taro.showToast({ + title: '保存成功', + icon: 'success' + }); + } catch (error) { + console.error('保存失败:', error); + Taro.showToast({ + title: '保存失败', + icon: 'error' + }); + } + }; + + const handle_edit_modal_cancel = () => { + setEditModalVisible(false); + setEditingField(''); + }; + return ( {/* 头像和基本信息 */} @@ -130,7 +191,6 @@ export const UserInfoCard: React.FC = ({ - {user_info.gender === "0" && ( = ({ src={require('../../static/userInfo/female.svg')} /> )} - - - - {user_info.ntrp_level || '未设置'} - - - {user_info.occupation || '未设置'} - - - - {user_info.location || '未设置'} + { + !user_info.gender && ( + + 选择性别 + + ) + } + {user_info.ntrp_level ? + + {/* */} + {user_info.ntrp_level} + : + + 测测你的NTRP水平 + + } + {user_info.occupation ? + + {user_info.occupation} + : + + 选择职业 + + } + {user_info.location ? + + {/* */} + {user_info.location} + : + + 选择地区 + + } + + + { + user_info.personal_profile ? ( + {user_info.personal_profile} + ) : ( + handle_open_edit_modal('personal_profile')}> + + 点击添加简介,让更多人了解你 + + ) + } - {user_info.personal_profile} + + {/* 编辑个人简介弹窗 */} + ); }; diff --git a/src/config/api.ts b/src/config/api.ts index 0ab74ab..1be3372 100644 --- a/src/config/api.ts +++ b/src/config/api.ts @@ -11,7 +11,8 @@ export const API_CONFIG = { FOLLOW: '/user/follow', UNFOLLOW: '/user/unfollow', HOSTED_GAMES: '/user/games', - PARTICIPATED_GAMES: '/user/participated' + PARTICIPATED_GAMES: '/user/participated', + PARSE_PHONE: '/user/parse_phone', }, // 文件上传接口 diff --git a/src/services/userService.ts b/src/services/userService.ts index afb3ea2..cb3aa57 100644 --- a/src/services/userService.ts +++ b/src/services/userService.ts @@ -28,6 +28,9 @@ interface UserDetailData { last_login_time: string; create_time: string; last_modify_time: string; + personal_profile: string; + occupation: string; + birthday: string; stats: { followers_count: number; following_count: number; @@ -226,12 +229,13 @@ export class UserService { participated: userData.stats?.participated_games_count || 0 }, - personal_profile: '', + personal_profile: userData.personal_profile || '', location: userData.city + userData.district|| '', - occupation: '', + occupation: userData.occupation || '', ntrp_level: '', phone: userData.phone || '', - gender: userData.gender || '' + gender: userData.gender || '', + birthday: userData.birthday || '', }; } else { throw new Error(response.message || '获取用户信息失败'); @@ -449,6 +453,24 @@ export class UserService { return require('../static/userInfo/default_avatar.svg'); } } + + // 解析用户手机号 + static async parse_phone(phone_code: string): Promise { + try { + const response = await httpService.post<{ phone: string }>(API_CONFIG.USER.PARSE_PHONE, { phone_code }, { + showLoading: true, + loadingText: '获取手机号中...' + }); + if (response.code === 0) { + return response.data.phone || ''; + } else { + throw new Error(response.message || '获取手机号失败'); + } + } catch (error) { + console.error('获取手机号失败:', error); + return ''; + } + } } // 从 loginService 移过来的用户相关方法 diff --git a/src/static/userInfo/info_edit.svg b/src/static/userInfo/info_edit.svg new file mode 100644 index 0000000..33b0741 --- /dev/null +++ b/src/static/userInfo/info_edit.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/user_pages/edit/index.scss b/src/user_pages/edit/index.scss index 28022cb..b9d3061 100644 --- a/src/user_pages/edit/index.scss +++ b/src/user_pages/edit/index.scss @@ -145,12 +145,13 @@ padding: 10px 12px; min-height: 44px; box-sizing: border-box; + gap: 20px; .item_left { display: flex; align-items: center; gap: 8px; - flex: 1; + flex-shrink: 0; .item_icon { width: 16px; @@ -172,6 +173,7 @@ gap: 8px; flex: 1; justify-content: flex-end; + overflow: hidden; .item_input { flex: 1; @@ -184,6 +186,7 @@ border: none; background: transparent; outline: none; + min-width: 0; &::placeholder { color: rgba(0, 0, 0, 0.4); @@ -196,6 +199,11 @@ font-size: 14px; line-height: 1.71em; color: #000000; + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + text-align: right; } .bio_textarea { @@ -222,6 +230,26 @@ height: 14px; opacity: 0.2; } + + Button { + padding: 0; + font-family: 'PingFang SC'; + font-weight: 600; + font-size: 14px; + line-height: 1.71em; + color: #000000; + border: none; + flex: 1; + text-align: right; + background-color: unset; + &.placeholer { + font-weight: 400; + color: rgba(60, 60, 67, 0.3); + } + &:after { + border: none; + } + } } .divider { diff --git a/src/user_pages/edit/index.tsx b/src/user_pages/edit/index.tsx index 5916367..7bb01fc 100644 --- a/src/user_pages/edit/index.tsx +++ b/src/user_pages/edit/index.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from 'react'; -import { View, Text, Image, ScrollView, Picker, Input } from '@tarojs/components'; +import { View, Text, Image, ScrollView, Picker, Input, Button } from '@tarojs/components'; import Taro from '@tarojs/taro'; import './index.scss'; import { UserInfo } from '@/components/UserInfo'; @@ -67,7 +67,7 @@ const EditProfilePage: React.FC = () => { ntrp_level: user_data.ntrp_level || 'NTRP 4.0', phone: user_data.phone || '', gender: user_data.gender || '', - birthday: '2000-01-01' // 默认生日,实际应该从用户数据获取 + birthday: user_data.birthday || '', // 默认生日,实际应该从用户数据获取 }); } catch (error) { console.error('加载用户信息失败:', error); @@ -180,9 +180,7 @@ const EditProfilePage: React.FC = () => { // 处理性别选择 const handle_gender_change = (e: any) => { const gender_value = e.detail.value; - // 使用工具函数转换:页面选择器值(0/1) -> 数据库值('0'/'1') - const gender_db_value = gender_value === 0 ? '0' : '1'; - handle_field_edit('gender', gender_db_value); + handle_field_edit('gender', gender_value); }; // 处理生日选择 @@ -191,6 +189,17 @@ const EditProfilePage: React.FC = () => { handle_field_edit('birthday', birthday_value); }; + // NTRP水平输入 - 实时更新本地状态 + const handle_ntrp_level_input = (e: any) => { + const ntrp_level_value = e.detail.value; + setFormData(prev => ({ ...prev, ntrp_level: ntrp_level_value })); + } + // NTRP水平输入 - 失去焦点时保存到服务器 + const handle_ntrp_level_blur = (e: any) => { + const ntrp_level_value = e.detail.value; + handle_field_edit('ntrp_level', ntrp_level_value); + } + // 处理职业输入 - 实时更新本地状态 const handle_occupation_input = (e: any) => { const occupation_value = e.detail.value; @@ -215,17 +224,17 @@ const EditProfilePage: React.FC = () => { handle_field_edit('location', location_value); }; - // 处理手机号输入 - 实时更新本地状态 - const handle_phone_input = (e: any) => { - const phone_value = e.detail.value; - setFormData(prev => ({ ...prev, phone: phone_value })); - }; + // // 处理手机号输入 - 实时更新本地状态 + // const handle_phone_input = (e: any) => { + // const phone_value = e.detail.value; + // setFormData(prev => ({ ...prev, phone: phone_value })); + // }; - // 处理手机号输入 - 失去焦点时保存到服务器 - const handle_phone_blur = (e: any) => { - const phone_value = e.detail.value; - handle_field_edit('phone', phone_value); - }; + // // 处理手机号输入 - 失去焦点时保存到服务器 + // const handle_phone_blur = (e: any) => { + // const phone_value = e.detail.value; + // handle_field_edit('phone', phone_value); + // }; // 处理退出登录 @@ -246,6 +255,29 @@ const EditProfilePage: React.FC = () => { }); }; + const onGetPhoneNumber = async (e) => { + if (!e.detail || !e.detail.code) { + Taro.showToast({ + title: '获取手机号失败,请重试', + icon: 'none', + duration: 2000 + }); + return; + } + console.log('用户手机号信息aaa:', e) + try { + const phone = await UserService.parse_phone(e.detail.code); + handle_field_edit('phone', phone); + } catch (e) { + console.error('解析手机号失败:', e); + Taro.showToast({ + title: '解析手机号失败,请重试', + icon: 'none', + duration: 2000 + }); + } + } + return ( {/* 导航栏 */} @@ -345,7 +377,7 @@ const EditProfilePage: React.FC = () => { - {form_data.personal_profile || '介绍一下自己'} + {form_data.personal_profile.replace(/\n/g, ' ') || '介绍一下自己'} @@ -370,6 +402,7 @@ const EditProfilePage: React.FC = () => { onInput={handle_location_input} onBlur={handle_location_blur} /> + @@ -381,7 +414,14 @@ const EditProfilePage: React.FC = () => { NTRP 水平 - {form_data.ntrp_level} + {/* {form_data.ntrp_level} */} + @@ -401,6 +441,7 @@ const EditProfilePage: React.FC = () => { onInput={handle_occupation_input} onBlur={handle_occupation_blur} /> + @@ -415,14 +456,16 @@ const EditProfilePage: React.FC = () => { 手机 - + /> */} + + diff --git a/src/user_pages/myself/index.tsx b/src/user_pages/myself/index.tsx index b392c3f..319fe74 100644 --- a/src/user_pages/myself/index.tsx +++ b/src/user_pages/myself/index.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from "react"; import { View, Text, Image, ScrollView } from "@tarojs/components"; -import Taro from "@tarojs/taro"; +import Taro, { useDidShow } from "@tarojs/taro"; import "./index.scss"; import GuideBar from "@/components/GuideBar"; import { UserInfoCard, UserInfo } from "@/components/UserInfo/index"; @@ -33,6 +33,7 @@ const MyselfPage: React.FC = () => { location: "加载中...", occupation: "加载中...", ntrp_level: "NTRP 3.0", + personal_profile: "加载中...", }); // 球局记录状态 @@ -77,9 +78,13 @@ const MyselfPage: React.FC = () => { }; // 页面加载时获取数据 - useEffect(() => { - load_user_data(); - }, [user_id]); + // useEffect(() => { + // load_user_data(); + // }, [user_id]); + + useDidShow(() => { + load_user_data(); // 确保从编辑页面返回时刷新数据 + }); // 切换标签页时重新加载球局数据 useEffect(() => { @@ -157,6 +162,7 @@ const MyselfPage: React.FC = () => { is_current_user={is_current_user} is_following={is_following} on_follow={handle_follow} + set_user_info={set_user_info} /> )} {/* 球局订单和收藏功能 */} @@ -208,6 +214,7 @@ const MyselfPage: React.FC = () => { loading={loading} error={null} reload={load_game_data} + loadMoreMatches={() => { }} /> From dbeb110f8c3df5cd9f2e4907c54783cd763c1ae4 Mon Sep 17 00:00:00 2001 From: Ultrame <1019265060@qq.com> Date: Sun, 14 Sep 2025 12:29:02 +0800 Subject: [PATCH 02/90] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=B8=AA=E4=BA=BA?= =?UTF-8?q?=E9=A1=B5=E4=B8=AD=E4=B8=AA=E4=BA=BA=E7=AE=80=E4=BB=8B=E5=BC=B9?= =?UTF-8?q?=E7=AA=97=E8=A2=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/container/listContainer/index.scss | 2 +- src/user_pages/myself/index.scss | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/container/listContainer/index.scss b/src/container/listContainer/index.scss index af26e6f..04bf2f4 100644 --- a/src/container/listContainer/index.scss +++ b/src/container/listContainer/index.scss @@ -5,7 +5,7 @@ flex-direction: column; gap: 5px; padding-bottom: 34px; - min-height: 100vh; + // min-height: 100vh; .recommendTextWrapper { display: flex; diff --git a/src/user_pages/myself/index.scss b/src/user_pages/myself/index.scss index 2b585e2..dd15bbf 100644 --- a/src/user_pages/myself/index.scss +++ b/src/user_pages/myself/index.scss @@ -11,7 +11,7 @@ // 主要内容区域 .main_content { - position: relative; + // position: relative; z-index: 5; flex: 1; margin-top: 0; From 997c5a6e285ec1b2e47213883d0e98dd5a14ffe7 Mon Sep 17 00:00:00 2001 From: Ultrame <1019265060@qq.com> Date: Sun, 14 Sep 2025 23:29:37 +0800 Subject: [PATCH 03/90] =?UTF-8?q?=E8=B7=B3=E8=BD=AC=E4=BB=96=E4=BA=BA?= =?UTF-8?q?=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.config.ts | 3 ++- src/components/UserInfo/index.tsx | 13 ++++++++----- src/game_pages/detail/index.tsx | 8 +++++++- src/user_pages/other/index.config.ts | 1 - src/user_pages/other/index.scss | 1 + src/user_pages/other/index.tsx | 11 ++++++++--- 6 files changed, 26 insertions(+), 11 deletions(-) diff --git a/src/app.config.ts b/src/app.config.ts index 17fe620..1a1b247 100644 --- a/src/app.config.ts +++ b/src/app.config.ts @@ -21,7 +21,8 @@ export default defineAppConfig({ root: "user_pages", pages: [ "myself/index", // 个人中心 - "edit/index", // 个人中心 + "edit/index", // 编辑个人中心 + "other/index", // 他人个人主页 ], }, // { diff --git a/src/components/UserInfo/index.tsx b/src/components/UserInfo/index.tsx index 3515970..0201dc4 100644 --- a/src/components/UserInfo/index.tsx +++ b/src/components/UserInfo/index.tsx @@ -24,10 +24,13 @@ export interface UserInfo { ntrp_level: string; phone?: string; gender?: string; - bio?: string, - latitude?: string, - longitude?: string, - birthday?: string, + bio?: string; + latitude?: string; + longitude?: string; + birthday?: string; + is_following?: boolean; + tags?: string[]; + ongoing_games?: string[]; } @@ -59,7 +62,7 @@ export const UserInfoCard: React.FC = ({ on_share, set_user_info, }) => { - console.log("用户信息xx:", user_info); + console.log("用户信息:", user_info); // 编辑个人简介弹窗状态 const [edit_modal_visible, setEditModalVisible] = useState(false); const [editing_field, setEditingField] = useState(''); diff --git a/src/game_pages/detail/index.tsx b/src/game_pages/detail/index.tsx index e3c929f..23e4f3e 100644 --- a/src/game_pages/detail/index.tsx +++ b/src/game_pages/detail/index.tsx @@ -855,6 +855,12 @@ function OrganizerInfo(props) { } }; + const viewOtherPage = () => { + Taro.navigateTo({ + url: `/user_pages/other/index?userid=${id}`, + }); + }; + return ( {/* orgnizer title */} @@ -863,7 +869,7 @@ function OrganizerInfo(props) { {/* organizer avatar and name */} - + {nickname} diff --git a/src/user_pages/other/index.config.ts b/src/user_pages/other/index.config.ts index 62cb01c..5dcd237 100644 --- a/src/user_pages/other/index.config.ts +++ b/src/user_pages/other/index.config.ts @@ -1,4 +1,3 @@ export default definePageConfig({ navigationBarTitleText: '用户主页', - navigationStyle: 'custom' }) \ No newline at end of file diff --git a/src/user_pages/other/index.scss b/src/user_pages/other/index.scss index 7283e94..a1c7fcb 100644 --- a/src/user_pages/other/index.scss +++ b/src/user_pages/other/index.scss @@ -23,6 +23,7 @@ flex-direction: column; gap: 16px; margin-bottom: 16px; + margin-top: 98px; // 基本信息 .basic_info { diff --git a/src/user_pages/other/index.tsx b/src/user_pages/other/index.tsx index 63222a5..62fbf7e 100644 --- a/src/user_pages/other/index.tsx +++ b/src/user_pages/other/index.tsx @@ -11,6 +11,7 @@ import { GameRecord, } from "@/components/UserInfo"; import { UserService } from "@/services/userService"; +import * as LoginService from "@/services/loginService"; const OtherUserPage: React.FC = () => { // 获取页面参数 @@ -34,6 +35,9 @@ const OtherUserPage: React.FC = () => { location: "北京朝阳", occupation: "金融从业者", ntrp_level: "NTRP 3.5", + is_following: false, + ongoing_games: [], + personal_profile: '', }); // 模拟球局数据 @@ -52,8 +56,9 @@ const OtherUserPage: React.FC = () => { const load_user_data = async () => { if (user_id) { try { - const user_data = await UserService.get_user_info(user_id); - setUserInfo(user_data); + // const user_data = await UserService.get_user_info(user_id); + const res = await LoginService.getUserInfoById(user_id); + setUserInfo(res.data as UserInfo); const games_data = await UserService.get_user_games( user_id, @@ -151,7 +156,7 @@ const OtherUserPage: React.FC = () => { - + {/* */} ); }; From db4286125a0ab00f4fa40da5007e8272f9357f1f Mon Sep 17 00:00:00 2001 From: Ultrame <1019265060@qq.com> Date: Mon, 15 Sep 2025 10:21:12 +0800 Subject: [PATCH 04/90] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=BC=BA=E9=99=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/game_pages/detail/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/game_pages/detail/index.tsx b/src/game_pages/detail/index.tsx index 23e4f3e..f9981b9 100644 --- a/src/game_pages/detail/index.tsx +++ b/src/game_pages/detail/index.tsx @@ -382,10 +382,10 @@ function GameInfo(props) { location_name, start_time, end_time, - weather = [{}], + weather, } = detail || {}; - const [{ iconDay, tempMax, tempMin }] = weather; + const [{ iconDay, tempMax, tempMin }] = weather || [{}]; const openMap = () => { Taro.openLocation({ From 91636855aafe8ccc1ea31008814186b8caa014e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=88=90?= Date: Mon, 15 Sep 2025 13:03:04 +0800 Subject: [PATCH 05/90] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=8E=92=E5=BA=8F?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/store/listStore.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/store/listStore.ts b/src/store/listStore.ts index 78dc9f3..dbdfefc 100644 --- a/src/store/listStore.ts +++ b/src/store/listStore.ts @@ -117,8 +117,8 @@ const commonStateDefaultValue = { // 快捷筛选数据 quickFilterData: [ { label: "智能排序", value: "0" }, - { label: "距离更近", value: "1" }, - { label: "时间更近", value: "2" }, + { label: "距离更近", value: "distance" }, + { label: "时间更近", value: "time" }, ], // 气泡日期范围 From 7a0bc71f9f88978df526ab5a84a554b6a2642b0c Mon Sep 17 00:00:00 2001 From: Ultrame <1019265060@qq.com> Date: Mon, 15 Sep 2025 17:30:48 +0800 Subject: [PATCH 06/90] =?UTF-8?q?=E4=BB=96=E4=BA=BA=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/UserInfo/index.scss | 7 +- src/components/UserInfo/index.tsx | 215 ++++++++++++++--------------- src/config/api.ts | 12 +- src/services/userService.ts | 12 +- src/static/userInfo/chat.svg | 7 + src/static/userInfo/following.svg | 5 + src/static/userInfo/unfollow.svg | 5 + src/user_pages/myself/index.tsx | 19 +-- src/user_pages/other/index.scss | 74 ++++++---- src/user_pages/other/index.tsx | 38 ++++- 10 files changed, 227 insertions(+), 167 deletions(-) create mode 100644 src/static/userInfo/chat.svg create mode 100644 src/static/userInfo/following.svg create mode 100644 src/static/userInfo/unfollow.svg diff --git a/src/components/UserInfo/index.scss b/src/components/UserInfo/index.scss index d8feca9..7a43528 100644 --- a/src/components/UserInfo/index.scss +++ b/src/components/UserInfo/index.scss @@ -137,6 +137,7 @@ .message_button { width: 40px; height: 40px; + padding: unset; background: #FFFFFF; border: 0.5px solid rgba(0, 0, 0, 0.12); border-radius: 999px; @@ -147,8 +148,8 @@ transition: all 0.3s ease; .button_icon { - width: 18px; - height: 18px; + width: 20px; + height: 20px; } } @@ -527,4 +528,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/components/UserInfo/index.tsx b/src/components/UserInfo/index.tsx index 0201dc4..5ac021f 100644 --- a/src/components/UserInfo/index.tsx +++ b/src/components/UserInfo/index.tsx @@ -1,10 +1,10 @@ -import React, { useState } from 'react'; -import Taro from '@tarojs/taro'; -import { View, Text, Image, Button } from '@tarojs/components'; -import './index.scss'; +import React, { useState } from "react"; +import Taro from "@tarojs/taro"; +import { View, Text, Image, Button } from "@tarojs/components"; +import "./index.scss"; -import { EditModal } from '@/components'; -import { UserService } from '@/services/userService'; +import { EditModal } from "@/components"; +import { UserService } from "@/services/userService"; // 用户信息接口 export interface UserInfo { @@ -33,7 +33,6 @@ export interface UserInfo { ongoing_games?: string[]; } - // 用户信息卡片组件属性 interface UserInfoCardProps { user_info: UserInfo; @@ -45,11 +44,10 @@ interface UserInfoCardProps { set_user_info?: (info: UserInfo) => void; } - // 处理编辑用户信息 const on_edit = () => { Taro.navigateTo({ - url: '/user_pages/edit/index' + url: "/user_pages/edit/index", }); }; // 用户信息卡片组件 @@ -62,17 +60,17 @@ export const UserInfoCard: React.FC = ({ on_share, set_user_info, }) => { - console.log("用户信息:", user_info); + console.log("UserInfoCard 用户信息:", user_info); // 编辑个人简介弹窗状态 const [edit_modal_visible, setEditModalVisible] = useState(false); - const [editing_field, setEditingField] = useState(''); + const [editing_field, setEditingField] = useState(""); // 表单状态 - const [form_data, setFormData] = useState({...user_info}); + const [form_data, setFormData] = useState({ ...user_info }); // 处理编辑弹窗 const handle_open_edit_modal = (field: string) => { - if (field === 'nickname') { + if (field === "nickname") { // 手动输入 setEditingField(field); setEditModalVisible(true); @@ -88,33 +86,33 @@ export const UserInfoCard: React.FC = ({ await UserService.update_user_info(update_data); // 更新本地状态 - setFormData(prev => { + setFormData((prev) => { const updated = { ...prev, [editing_field]: value }; - typeof set_user_info === 'function' && set_user_info(updated); + typeof set_user_info === "function" && set_user_info(updated); return updated; }); // 关闭弹窗 setEditModalVisible(false); - setEditingField(''); + setEditingField(""); // 显示成功提示 Taro.showToast({ - title: '保存成功', - icon: 'success' + title: "保存成功", + icon: "success", }); } catch (error) { - console.error('保存失败:', error); + console.error("保存失败:", error); Taro.showToast({ - title: '保存失败', - icon: 'error' + title: "保存失败", + icon: "error", }); } }; const handle_edit_modal_cancel = () => { setEditModalVisible(false); - setEditingField(''); + setEditingField(""); }; return ( @@ -128,11 +126,14 @@ export const UserInfoCard: React.FC = ({ {user_info.nickname} {user_info.join_date} - - + {is_current_user && ( + + + + )} {/* 统计数据 */} @@ -159,15 +160,17 @@ export const UserInfoCard: React.FC = ({ {/* 只有非当前用户才显示关注按钮 */} {!is_current_user && on_follow && ( )} @@ -176,7 +179,7 @@ export const UserInfoCard: React.FC = ({ )} @@ -193,74 +196,69 @@ export const UserInfoCard: React.FC = ({ {/* 标签和简介 */} - - {user_info.gender === "0" && ( - - )} - {user_info.gender === "1" && ( - - )} - { - !user_info.gender && ( - - 选择性别 - - ) - } - - {user_info.ntrp_level ? + {user_info.gender ? ( + + {user_info.gender === "0" && ( + + )} + {user_info.gender === "1" && ( + + )} + + ) : is_current_user ? ( + + 选择性别 + + ) : null} + {user_info.ntrp_level ? ( - {/* */} {user_info.ntrp_level} - : + + ) : is_current_user ? ( 测测你的NTRP水平 - } - {user_info.occupation ? + ) : null} + {user_info.occupation ? ( {user_info.occupation} - : + + ) : is_current_user ? ( 选择职业 - } - {user_info.location ? + ) : null} + {user_info.location ? ( - {/* */} {user_info.location} - : + + ) : is_current_user ? ( 选择地区 - } + ) : null} - { - user_info.personal_profile ? ( - {user_info.personal_profile} - ) : ( - handle_open_edit_modal('personal_profile')}> - - 点击添加简介,让更多人了解你 - - ) - } + {user_info.personal_profile ? ( + {user_info.personal_profile} + ) : is_current_user ? ( + handle_open_edit_modal("personal_profile")} + > + + 点击添加简介,让更多人了解你 + + ) : null} @@ -268,13 +266,13 @@ export const UserInfoCard: React.FC = ({ ); @@ -298,7 +296,7 @@ export interface GameRecord { current_participants: number; level_range: string; game_type: string; - images: string[]; + image_list: string[]; } // 球局卡片组件属性 @@ -312,20 +310,17 @@ interface GameCardProps { export const GameCard: React.FC = ({ game, on_click, - on_participant_click + on_participant_click, }) => { return ( - on_click(game.id)} - > + on_click(game.id)}> {/* 球局标题和类型 */} {game.title} @@ -348,12 +343,8 @@ export const GameCard: React.FC = ({ {/* 球局图片 */} - {game.images.map((image, index) => ( - + {game.image_list.map((image, index) => ( + ))} @@ -361,7 +352,7 @@ export const GameCard: React.FC = ({ - {game.participants.map((participant, index) => ( + {game.participants?.map((participant, index) => ( = ({ // 球局标签页组件属性 interface GameTabsProps { - active_tab: 'hosted' | 'participated'; - on_tab_change: (tab: 'hosted' | 'participated') => void; + active_tab: "hosted" | "participated"; + on_tab_change: (tab: "hosted" | "participated") => void; is_current_user: boolean; } @@ -403,21 +394,29 @@ interface GameTabsProps { export const GameTabs: React.FC = ({ active_tab, on_tab_change, - is_current_user + is_current_user, }) => { - const hosted_text = is_current_user ? '我主办的' : '他主办的'; - const participated_text = is_current_user ? '我参与的' : '他参与的'; + const hosted_text = is_current_user ? "我主办的" : "主办球局"; + const participated_text = is_current_user ? "我参与的" : "参与球局"; return ( - on_tab_change('hosted')}> + on_tab_change("hosted")} + > {hosted_text} - on_tab_change('participated')}> + on_tab_change("participated")} + > {participated_text} ); -}; \ No newline at end of file +}; diff --git a/src/config/api.ts b/src/config/api.ts index 1be3372..f54531e 100644 --- a/src/config/api.ts +++ b/src/config/api.ts @@ -3,25 +3,25 @@ import envConfig from './env'// API配置 export const API_CONFIG = { // 基础URL BASE_URL: envConfig.apiBaseURL, - + // 用户相关接口 USER: { DETAIL: '/user/detail', UPDATE: '/user/update', - FOLLOW: '/user/follow', - UNFOLLOW: '/user/unfollow', + FOLLOW: '/wch_users/follow', + UNFOLLOW: '/wch_users/unfollow', HOSTED_GAMES: '/user/games', PARTICIPATED_GAMES: '/user/participated', PARSE_PHONE: '/user/parse_phone', }, - + // 文件上传接口 UPLOAD: { AVATAR: '/gallery/upload', IMAGE: '/gallery/upload', OSS_IMG: '/gallery/upload_oss_img' }, - + // 球局相关接口 GAME: { LIST: '/game/list', @@ -38,4 +38,4 @@ export const REQUEST_CONFIG = { header: { 'Content-Type': 'application/json' } -}; \ No newline at end of file +}; diff --git a/src/services/userService.ts b/src/services/userService.ts index cb3aa57..5bc1650 100644 --- a/src/services/userService.ts +++ b/src/services/userService.ts @@ -252,7 +252,7 @@ export class UserService { try { // 过滤掉空字段 const filtered_data: Record = {}; - + Object.keys(update_data).forEach(key => { const value = update_data[key as keyof UserInfo]; // 只添加非空且非空字符串的字段 @@ -285,10 +285,10 @@ export class UserService { } // 获取用户主办的球局 - static async get_hosted_games(user_id: string): Promise { + static async get_hosted_games(userId: string): Promise { try { const response = await httpService.post(API_CONFIG.USER.HOSTED_GAMES, { - user_id + userId }, { showLoading: false @@ -308,10 +308,10 @@ export class UserService { } // 获取用户参与的球局 - static async get_participated_games(user_id: string): Promise { + static async get_participated_games(userId: string): Promise { try { const response = await httpService.post(API_CONFIG.USER.PARTICIPATED_GAMES, { - user_id + userId }, { showLoading: false @@ -522,4 +522,4 @@ export const get_user_info = (): any | null => { } catch (error) { return null; } -}; \ No newline at end of file +}; diff --git a/src/static/userInfo/chat.svg b/src/static/userInfo/chat.svg new file mode 100644 index 0000000..940b3f9 --- /dev/null +++ b/src/static/userInfo/chat.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/static/userInfo/following.svg b/src/static/userInfo/following.svg new file mode 100644 index 0000000..4932907 --- /dev/null +++ b/src/static/userInfo/following.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/static/userInfo/unfollow.svg b/src/static/userInfo/unfollow.svg new file mode 100644 index 0000000..b9517fe --- /dev/null +++ b/src/static/userInfo/unfollow.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/user_pages/myself/index.tsx b/src/user_pages/myself/index.tsx index 319fe74..07e3037 100644 --- a/src/user_pages/myself/index.tsx +++ b/src/user_pages/myself/index.tsx @@ -58,13 +58,14 @@ const MyselfPage: React.FC = () => { set_user_info(user_data); // 获取球局记录 - let games_data; - if (active_tab === "hosted") { - games_data = await UserService.get_hosted_games(user_id); - } else { - games_data = await UserService.get_participated_games(user_id); - } - set_game_records(games_data); + load_game_data(); + // let games_data; + // if (active_tab === "hosted") { + // games_data = await UserService.get_hosted_games(user_id); + // } else { + // games_data = await UserService.get_participated_games(user_id); + // } + // set_game_records(games_data); } catch (error) { console.error("加载用户数据失败:", error); Taro.showToast({ @@ -98,9 +99,9 @@ const MyselfPage: React.FC = () => { try { let games_data; if (active_tab === "hosted") { - games_data = await UserService.get_hosted_games(user_id); + games_data = await UserService.get_hosted_games(user_info.id); } else { - games_data = await UserService.get_participated_games(user_id); + games_data = await UserService.get_participated_games(user_info.id); } set_game_records(games_data); } catch (error) { diff --git a/src/user_pages/other/index.scss b/src/user_pages/other/index.scss index a1c7fcb..81b1b18 100644 --- a/src/user_pages/other/index.scss +++ b/src/user_pages/other/index.scss @@ -1,7 +1,11 @@ // 他人用户页面样式 .other_user_page { min-height: 100vh; - background: radial-gradient(circle at 50% 0%, rgba(238, 255, 220, 1) 0%, rgba(255, 255, 255, 1) 37%); + background: radial-gradient( + circle at 50% 0%, + rgba(238, 255, 220, 1) 0%, + rgba(255, 255, 255, 1) 37% + ); position: relative; overflow: hidden; box-sizing: border-box; @@ -36,7 +40,8 @@ height: 64px; border-radius: 50%; overflow: hidden; - box-shadow: 0px 8px 20px 0px rgba(0, 0, 0, 0.12), 0px 0px 1px 0px rgba(0, 0, 0, 0.2); + box-shadow: 0px 8px 20px 0px rgba(0, 0, 0, 0.12), + 0px 0px 1px 0px rgba(0, 0, 0, 0.2); .avatar { width: 100%; @@ -52,7 +57,7 @@ gap: 4px; .nickname { - font-family: 'PingFang SC'; + font-family: "PingFang SC"; font-weight: 600; font-size: 20px; line-height: 1.4em; @@ -61,7 +66,7 @@ } .join_date { - font-family: 'PingFang SC'; + font-family: "PingFang SC"; font-weight: 400; font-size: 14px; line-height: 1.4em; @@ -89,7 +94,7 @@ align-items: center; .stat_number { - font-family: 'PingFang SC'; + font-family: "PingFang SC"; font-weight: 600; font-size: 18px; line-height: 1.4em; @@ -98,7 +103,7 @@ } .stat_label { - font-family: 'PingFang SC'; + font-family: "PingFang SC"; font-weight: 500; font-size: 12px; line-height: 1.4em; @@ -126,7 +131,7 @@ transition: all 0.3s ease; &.following { - background: #FFFFFF; + background: #ffffff; color: #000000; } @@ -136,11 +141,11 @@ } .button_text { - font-family: 'PingFang SC'; + font-family: "PingFang SC"; font-weight: 600; font-size: 14px; line-height: 1.4em; - color: #FFFFFF; + color: #ffffff; .following & { color: #000000; @@ -151,7 +156,7 @@ .message_button { width: 40px; height: 40px; - background: #FFFFFF; + background: #ffffff; border: 0.5px solid rgba(0, 0, 0, 0.12); border-radius: 999px; display: flex; @@ -185,7 +190,7 @@ gap: 4px; padding: 6px 8px; height: 20px; - background: #FFFFFF; + background: #ffffff; border: 0.5px solid rgba(0, 0, 0, 0.16); border-radius: 999px; @@ -195,7 +200,7 @@ } .tag_text { - font-family: 'PingFang SC'; + font-family: "PingFang SC"; font-weight: 500; font-size: 11px; line-height: 1.8em; @@ -206,7 +211,7 @@ } .bio_text { - font-family: 'PingFang SC'; + font-family: "PingFang SC"; font-weight: 400; font-size: 14px; line-height: 1.571em; @@ -216,6 +221,17 @@ } } + .game_list_container { + .game_class_text { + font-family: "PingFang SC"; + font-weight: 600; + font-size: 20px; + line-height: 1.4em; + letter-spacing: 1.9%; + color: rgba(0, 0, 0, 0.85); + } + } + // 球局类型标签页 .game_tabs_section { margin-bottom: 16px; @@ -231,7 +247,7 @@ transition: all 0.3s ease; .tab_text { - font-family: 'PingFang SC'; + font-family: "PingFang SC"; font-weight: 600; font-size: 20px; line-height: 1.4em; @@ -265,7 +281,7 @@ margin-bottom: 16px; .date_text { - font-family: 'PingFang SC'; + font-family: "PingFang SC"; font-weight: 600; font-size: 14px; line-height: 1.4em; @@ -274,7 +290,7 @@ } .separator { - font-family: 'PingFang SC'; + font-family: "PingFang SC"; font-weight: 400; font-size: 18px; line-height: 1.4em; @@ -283,7 +299,7 @@ } .weekday_text { - font-family: 'PingFang SC'; + font-family: "PingFang SC"; font-weight: 600; font-size: 14px; line-height: 1.4em; @@ -300,7 +316,7 @@ padding: 0 5px 15px; .game_card { - background: #FFFFFF; + background: #ffffff; border: 0.5px solid rgba(0, 0, 0, 0.08); border-radius: 20px; padding: 0 0 12px; @@ -321,7 +337,7 @@ padding: 12px 15px 0; .game_title { - font-family: 'PingFang SC'; + font-family: "PingFang SC"; font-weight: 600; font-size: 16px; line-height: 1.5em; @@ -344,7 +360,7 @@ padding: 6px 15px 0; .time_text { - font-family: 'PingFang SC'; + font-family: "PingFang SC"; font-weight: 400; font-size: 12px; line-height: 1.5em; @@ -362,7 +378,7 @@ .location_text, .type_text, .distance_text { - font-family: 'PingFang SC'; + font-family: "PingFang SC"; font-weight: 400; font-size: 12px; line-height: 1.5em; @@ -370,7 +386,7 @@ } .separator { - font-family: 'PingFang SC'; + font-family: "PingFang SC"; font-weight: 400; font-size: 14px; line-height: 1.3em; @@ -392,7 +408,7 @@ width: 56.44px; height: 56.44px; border-radius: 9px; - border: 1.5px solid #FFFFFF; + border: 1.5px solid #ffffff; &:nth-child(1) { top: 4.18px; @@ -435,12 +451,12 @@ width: 20px; height: 20px; border-radius: 50%; - border: 1px solid #FFFFFF; + border: 1px solid #ffffff; } } .participants_count { - background: #FFFFFF; + background: #ffffff; border: 0.5px solid rgba(0, 0, 0, 0.16); border-radius: 999px; padding: 6px; @@ -450,7 +466,7 @@ justify-content: center; .count_text { - font-family: 'PingFang SC'; + font-family: "PingFang SC"; font-weight: 500; font-size: 11px; line-height: 1.8em; @@ -465,7 +481,7 @@ gap: 4px; .info_tag { - background: #FFFFFF; + background: #ffffff; border: 0.5px solid rgba(0, 0, 0, 0.16); border-radius: 999px; padding: 6px 8px; @@ -475,7 +491,7 @@ justify-content: center; .tag_text { - font-family: 'PingFang SC'; + font-family: "PingFang SC"; font-weight: 500; font-size: 11px; line-height: 1.8em; @@ -488,4 +504,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/user_pages/other/index.tsx b/src/user_pages/other/index.tsx index 62fbf7e..61d219c 100644 --- a/src/user_pages/other/index.tsx +++ b/src/user_pages/other/index.tsx @@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react"; import { View, Text, ScrollView } from "@tarojs/components"; import Taro from "@tarojs/taro"; import "./index.scss"; -import GuideBar from "@/components/GuideBar"; +// import GuideBar from "@/components/GuideBar"; import { UserInfoCard, GameCard, @@ -21,6 +21,7 @@ const OtherUserPage: React.FC = () => { // 模拟用户数据 const [user_info, setUserInfo] = useState({ id: user_id || "1", + gender:"", nickname: "网球爱好者", avatar: require("@/static/userInfo/default_avatar.svg"), join_date: "2024年3月加入", @@ -37,7 +38,7 @@ const OtherUserPage: React.FC = () => { ntrp_level: "NTRP 3.5", is_following: false, ongoing_games: [], - personal_profile: '', + personal_profile: "", }); // 模拟球局数据 @@ -48,7 +49,7 @@ const OtherUserPage: React.FC = () => { // 当前激活的标签页 const [active_tab, setActiveTab] = useState<"hosted" | "participated">( - "hosted", + "hosted" ); // 页面加载时获取用户信息 @@ -58,11 +59,36 @@ const OtherUserPage: React.FC = () => { try { // const user_data = await UserService.get_user_info(user_id); const res = await LoginService.getUserInfoById(user_id); - setUserInfo(res.data as UserInfo); + const { data: userData } = res; + // setUserInfo({...res.data as UserInfo, avatar: data.avatar_url || require("@/static/userInfo/default_avatar.svg")}); + setUserInfo({ + id: userData.user_code || user_id || "", + nickname: userData.nickname || "", + avatar: userData.avatar_url || "", + join_date: userData.subscribe_time + ? `${new Date(userData.subscribe_time).getFullYear()}年${ + new Date(userData.subscribe_time).getMonth() + 1 + }月加入` + : "", + stats: { + following: userData.stats?.following_count || 0, + friends: userData.stats?.followers_count || 0, + hosted: userData.stats?.hosted_games_count || 0, + participated: userData.stats?.participated_games_count || 0, + }, + + personal_profile: userData.personal_profile || "", + location: userData.city + userData.district || "", + occupation: userData.occupation || "", + ntrp_level: "", + phone: userData.phone || "", + gender: userData.gender || "", + birthday: userData.birthday || "", + }); const games_data = await UserService.get_user_games( user_id, - active_tab, + active_tab ); setGameRecords(games_data); } catch (error) { @@ -83,7 +109,7 @@ const OtherUserPage: React.FC = () => { try { const new_follow_status = await UserService.toggle_follow( user_info.id, - is_following, + is_following ); setIsFollowing(new_follow_status); Taro.showToast({ From f4c7840130cf12f0d1e3223b67008baf671a381c Mon Sep 17 00:00:00 2001 From: Ultrame <1019265060@qq.com> Date: Mon, 15 Sep 2025 21:13:13 +0800 Subject: [PATCH 07/90] =?UTF-8?q?=E4=BB=96=E4=BA=BA=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/UserInfo/index.scss | 26 ++++++---- src/components/UserInfo/index.tsx | 8 +-- src/services/userService.ts | 12 ++--- src/user_pages/myself/index.tsx | 13 +++-- src/user_pages/other/index.scss | 2 +- src/user_pages/other/index.tsx | 83 +++++++++++++++++++++--------- 6 files changed, 95 insertions(+), 49 deletions(-) diff --git a/src/components/UserInfo/index.scss b/src/components/UserInfo/index.scss index 7a43528..84ece5c 100644 --- a/src/components/UserInfo/index.scss +++ b/src/components/UserInfo/index.scss @@ -98,6 +98,8 @@ display: flex; align-items: center; gap: 12px; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 20px; .follow_button { display: flex; @@ -106,14 +108,22 @@ padding: 12px 16px 12px 12px; height: 40px; background: #000000; - border: 0.5px solid rgba(0, 0, 0, 0.06); - border-radius: 999px; + border: none; + outline: none; cursor: pointer; transition: all 0.3s ease; &.following { background: #FFFFFF; color: #000000; + + .button_text { + color: #000000 !important; + } + } + + &:after { + border: none; } .button_icon { @@ -127,10 +137,6 @@ font-size: 14px; line-height: 1.4em; color: #FFFFFF; - - .following & { - color: #000000; - } } } @@ -140,7 +146,7 @@ padding: unset; background: #FFFFFF; border: 0.5px solid rgba(0, 0, 0, 0.12); - border-radius: 999px; + border-radius: 50%; display: flex; align-items: center; justify-content: center; @@ -251,7 +257,8 @@ position: relative; padding-right: 20px; - &::before, &::after { + &::before, + &::after { content: ''; width: 6px; height: 1px; @@ -262,6 +269,7 @@ transform: rotate(45deg); margin-left: 4px; } + &::after { transform: rotate(-45deg); translate: 4.2px 0; @@ -528,4 +536,4 @@ } } } -} +} \ No newline at end of file diff --git a/src/components/UserInfo/index.tsx b/src/components/UserInfo/index.tsx index 5ac021f..b1fb3bd 100644 --- a/src/components/UserInfo/index.tsx +++ b/src/components/UserInfo/index.tsx @@ -8,7 +8,7 @@ import { UserService } from "@/services/userService"; // 用户信息接口 export interface UserInfo { - id: string; + id: string | number; nickname: string; avatar: string; join_date: string; @@ -169,20 +169,20 @@ export const UserInfoCard: React.FC = ({ ? "@/static/userInfo/following.svg" : "@/static/userInfo/unfollow.svg")} /> - + {is_following ? "已关注" : "关注"} )} {/* 只有非当前用户才显示消息按钮 */} - {!is_current_user && on_message && ( + {/* {!is_current_user && on_message && ( - )} + )} */} {/* 只有当前用户才显示分享按钮 */} {is_current_user && on_share && ( diff --git a/src/services/userService.ts b/src/services/userService.ts index 5bc1650..171bc94 100644 --- a/src/services/userService.ts +++ b/src/services/userService.ts @@ -218,7 +218,7 @@ export class UserService { if (response.code === 0) { const userData = response.data; return { - id: userData.user_code || user_id || '', + id: userData.user_code || userData.id || '', nickname: userData.nickname || '', avatar: userData.avatar_url || '', join_date: userData.subscribe_time ? `${new Date(userData.subscribe_time).getFullYear()}年${new Date(userData.subscribe_time).getMonth() + 1}月加入` : '', @@ -285,7 +285,7 @@ export class UserService { } // 获取用户主办的球局 - static async get_hosted_games(userId: string): Promise { + static async get_hosted_games(userId: string | number): Promise { try { const response = await httpService.post(API_CONFIG.USER.HOSTED_GAMES, { userId @@ -308,7 +308,7 @@ export class UserService { } // 获取用户参与的球局 - static async get_participated_games(userId: string): Promise { + static async get_participated_games(userId: string | number): Promise { try { const response = await httpService.post(API_CONFIG.USER.PARTICIPATED_GAMES, { userId @@ -332,7 +332,7 @@ export class UserService { } // 获取用户球局记录(兼容旧方法) - static async get_user_games(user_id: string, type: 'hosted' | 'participated'): Promise { + static async get_user_games(user_id: string | number, type: 'hosted' | 'participated'): Promise { if (type === 'hosted') { return this.get_hosted_games(user_id); } else { @@ -341,10 +341,10 @@ export class UserService { } // 关注/取消关注用户 - static async toggle_follow(user_id: string, is_following: boolean): Promise { + static async toggle_follow(following_id: string | number, is_following: boolean): Promise { try { const endpoint = is_following ? API_CONFIG.USER.UNFOLLOW : API_CONFIG.USER.FOLLOW; - const response = await httpService.post(endpoint, { user_id }, { + const response = await httpService.post(endpoint, { following_id }, { showLoading: true, loadingText: is_following ? '取消关注中...' : '关注中...' diff --git a/src/user_pages/myself/index.tsx b/src/user_pages/myself/index.tsx index 07e3037..1d76c67 100644 --- a/src/user_pages/myself/index.tsx +++ b/src/user_pages/myself/index.tsx @@ -19,7 +19,7 @@ const MyselfPage: React.FC = () => { // 用户信息状态 const [user_info, set_user_info] = useState({ - id: "1", + id: "", nickname: "加载中...", avatar: require("@/static/userInfo/default_avatar.svg"), join_date: "加载中...", @@ -54,11 +54,8 @@ const MyselfPage: React.FC = () => { set_loading(true); // 获取用户信息(包含统计数据) - const user_data = await UserService.get_user_info(user_id); + const user_data = await UserService.get_user_info(); set_user_info(user_data); - - // 获取球局记录 - load_game_data(); // let games_data; // if (active_tab === "hosted") { // games_data = await UserService.get_hosted_games(user_id); @@ -78,6 +75,12 @@ const MyselfPage: React.FC = () => { } }; + useEffect(() => { + if (user_info.id) { + load_game_data(); // 在 user_info 更新后调用 + } + }, [user_info]); + // 页面加载时获取数据 // useEffect(() => { // load_user_data(); diff --git a/src/user_pages/other/index.scss b/src/user_pages/other/index.scss index 81b1b18..c768750 100644 --- a/src/user_pages/other/index.scss +++ b/src/user_pages/other/index.scss @@ -158,7 +158,7 @@ height: 40px; background: #ffffff; border: 0.5px solid rgba(0, 0, 0, 0.12); - border-radius: 999px; + border-radius: 50%; display: flex; align-items: center; justify-content: center; diff --git a/src/user_pages/other/index.tsx b/src/user_pages/other/index.tsx index 61d219c..a1dc8ed 100644 --- a/src/user_pages/other/index.tsx +++ b/src/user_pages/other/index.tsx @@ -1,11 +1,12 @@ import React, { useState, useEffect } from "react"; import { View, Text, ScrollView } from "@tarojs/components"; +import ListContainer from "@/container/listContainer"; import Taro from "@tarojs/taro"; import "./index.scss"; // import GuideBar from "@/components/GuideBar"; import { UserInfoCard, - GameCard, + // GameCard, GameTabs, UserInfo, GameRecord, @@ -21,15 +22,15 @@ const OtherUserPage: React.FC = () => { // 模拟用户数据 const [user_info, setUserInfo] = useState({ id: user_id || "1", - gender:"", + gender: "", nickname: "网球爱好者", avatar: require("@/static/userInfo/default_avatar.svg"), join_date: "2024年3月加入", stats: { - following: 89, - friends: 15, - hosted: 12, - participated: 35, + following: 0, + friends: 0, + hosted: 0, + participated: 0, }, tags: ["北京朝阳", "金融从业者", "NTRP 3.5"], bio: "热爱网球的金融从业者,周末喜欢约球\n技术还在提升中,欢迎一起切磋\n平时在朝阳公园附近活动", @@ -47,6 +48,8 @@ const OtherUserPage: React.FC = () => { // 关注状态 const [is_following, setIsFollowing] = useState(false); + const [loading, set_loading] = useState(true); + // 当前激活的标签页 const [active_tab, setActiveTab] = useState<"hosted" | "participated">( "hosted" @@ -62,13 +65,12 @@ const OtherUserPage: React.FC = () => { const { data: userData } = res; // setUserInfo({...res.data as UserInfo, avatar: data.avatar_url || require("@/static/userInfo/default_avatar.svg")}); setUserInfo({ - id: userData.user_code || user_id || "", + id: user_id || "", nickname: userData.nickname || "", avatar: userData.avatar_url || "", join_date: userData.subscribe_time - ? `${new Date(userData.subscribe_time).getFullYear()}年${ - new Date(userData.subscribe_time).getMonth() + 1 - }月加入` + ? `${new Date(userData.subscribe_time).getFullYear()}年${new Date(userData.subscribe_time).getMonth() + 1 + }月加入` : "", stats: { following: userData.stats?.following_count || 0, @@ -85,12 +87,7 @@ const OtherUserPage: React.FC = () => { gender: userData.gender || "", birthday: userData.birthday || "", }); - - const games_data = await UserService.get_user_games( - user_id, - active_tab - ); - setGameRecords(games_data); + setIsFollowing(userData.is_following || false); } catch (error) { console.error("加载用户数据失败:", error); Taro.showToast({ @@ -102,7 +99,31 @@ const OtherUserPage: React.FC = () => { }; load_user_data(); - }, [user_id, active_tab]); + }, [user_id]); + + const load_game_data = async () => { + try { + set_loading(true); + const games_data = await UserService.get_user_games( + user_id || "", + active_tab + ); + setGameRecords(games_data); + } catch (error) { + console.error("加载球局数据失败:", error); + Taro.showToast({ + title: "加载失败,请重试", + icon: "error", + duration: 2000, + }); + } finally { + set_loading(false); + } + }; + + useEffect(() => { + load_game_data(); + }, [active_tab]); // 处理关注/取消关注 const handle_follow = async () => { @@ -134,11 +155,11 @@ const OtherUserPage: React.FC = () => { }; // 处理球局详情 - const handle_game_detail = (game_id: string) => { - Taro.navigateTo({ - url: `/game_pages/detail/index?id=${game_id}&from=personal`, - }); - }; + // const handle_game_detail = (game_id: string) => { + // Taro.navigateTo({ + // url: `/game_pages/detail/index?id=${game_id}&from=personal`, + // }); + // }; return ( @@ -170,8 +191,22 @@ const OtherUserPage: React.FC = () => { 星期六 + {/* 球局列表 */} + + + { }} + /> + + + {/* 球局卡片 */} - + {/* {game_records.map((game) => ( { on_click={handle_game_detail} /> ))} - + */} {/* */} From 8d639aed288b8d054f771494adf780b8ecd8d6c4 Mon Sep 17 00:00:00 2001 From: Ultrame <1019265060@qq.com> Date: Mon, 15 Sep 2025 21:50:37 +0800 Subject: [PATCH 08/90] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=90=83=E5=B1=80?= =?UTF-8?q?=E5=8F=82=E4=B8=8E=E8=80=85=E5=A4=B4=E5=83=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/userService.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/services/userService.ts b/src/services/userService.ts index 171bc94..20f6d63 100644 --- a/src/services/userService.ts +++ b/src/services/userService.ts @@ -18,7 +18,7 @@ interface UserDetailData { country: string; province: string; city: string; - district:string; + district: string; language: string; phone: string; is_subscribed: string; @@ -51,7 +51,7 @@ export interface UserInfoType { country: string province: string city: string - district:string + district: string language: string phone: string is_subscribed: string @@ -111,6 +111,11 @@ interface BackendGameData { venue_type: string; surface_type: string; }; + participants: { + user: { + avatar_url: string; + }; + }[]; } // 用户服务类 @@ -166,7 +171,8 @@ export class UserService { image_list: images, court_type: game.court_type || '未知', matchType: game.play_type || '不限', - shinei: game.court_type || '未知' + shinei: game.court_type || '未知', + participants: game.participants || [], }; }); } @@ -230,7 +236,7 @@ export class UserService { }, personal_profile: userData.personal_profile || '', - location: userData.city + userData.district|| '', + location: userData.city + userData.district || '', occupation: userData.occupation || '', ntrp_level: '', phone: userData.phone || '', @@ -373,8 +379,8 @@ export class UserService { latitude: 'latitude', longitude: 'longitude', province: 'province', - country:"country", - city:"city", + country: "country", + city: "city", personal_profile: 'personal_profile', occupation: 'occupation', ntrp_level: 'ntrp_level' From 66ba7b4b661be5fee8e8c57edb10fa0d1ab3fad4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=9D=B0?= Date: Mon, 15 Sep 2025 23:20:55 +0800 Subject: [PATCH 09/90] =?UTF-8?q?feat:=20=E8=AE=A2=E5=8D=95=E5=88=97?= =?UTF-8?q?=E8=A1=A8=20&=20=E7=90=83=E5=B1=80=E8=AF=A6=E6=83=85=E5=A4=B4?= =?UTF-8?q?=E5=83=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/UploadCover/index.tsx | 2 +- .../UploadCover/upload-source-popup.tsx | 2 +- src/game_pages/detail/index.scss | 9 +- src/game_pages/detail/index.tsx | 47 +- src/order_pages/orderDetail/index.tsx | 16 +- src/order_pages/orderList/index.module.scss | 333 ++++++++++--- src/order_pages/orderList/index.tsx | 443 +++++++++++++----- src/services/orderService.ts | 19 +- 8 files changed, 663 insertions(+), 208 deletions(-) diff --git a/src/components/UploadCover/index.tsx b/src/components/UploadCover/index.tsx index eaf9d51..07b4ff0 100644 --- a/src/components/UploadCover/index.tsx +++ b/src/components/UploadCover/index.tsx @@ -126,7 +126,7 @@ export default function UploadCover(props: UploadCoverProps) { value.map((item) => { return ( - + onDelete(item)} /> ) diff --git a/src/components/UploadCover/upload-source-popup.tsx b/src/components/UploadCover/upload-source-popup.tsx index 6858b44..ddd4004 100644 --- a/src/components/UploadCover/upload-source-popup.tsx +++ b/src/components/UploadCover/upload-source-popup.tsx @@ -128,7 +128,7 @@ export default forwardRef(function UploadImage(props: UploadImageProps, ref) { const isSelected = checkImageSelected(selectedImages, item) return ( handleImageClick(item)}> - + {isSelected ? ( diff --git a/src/game_pages/detail/index.scss b/src/game_pages/detail/index.scss index c8996da..da114ba 100644 --- a/src/game_pages/detail/index.scss +++ b/src/game_pages/detail/index.scss @@ -162,7 +162,6 @@ &-image { width: 28px; height: 28px; - border-radius: 50%; } } @@ -690,9 +689,11 @@ background: rgba(255, 255, 255, 0.16); flex: 0 0 auto; - &-avatar { + .participants-list-item-avatar { width: 60px; height: 60px; + border-radius: 50%; + overflow: hidden; } &-name { @@ -836,6 +837,8 @@ &-avatar { width: 40px; height: 40px; + border-radius: 50%; + object-fit: cover; } &-message { @@ -1012,6 +1015,8 @@ &-avatar { width: 20px; height: 20px; + border-radius: 50%; + object-fit: cover; } &-message { diff --git a/src/game_pages/detail/index.tsx b/src/game_pages/detail/index.tsx index f9981b9..29bc5bf 100644 --- a/src/game_pages/detail/index.tsx +++ b/src/game_pages/detail/index.tsx @@ -38,8 +38,8 @@ function insertDotInTags(tags: string[]) { } function GameTags(props) { - const { userInfo } = props; - const { avatar_url } = userInfo; + const { userInfo, handleViewUserInfo } = props; + const { avatar_url, id } = userInfo; const tags = [ { name: "🕙 急招", @@ -64,7 +64,9 @@ function GameTags(props) { {/* network image mock */} @@ -596,6 +598,7 @@ function VenueInfo(props) { > @@ -661,7 +664,7 @@ function GamePlayAndRequirement(props) { // 参与者 function Participants(props) { - const { detail = {}, handleJoinGame } = props; + const { detail = {}, handleJoinGame, handleViewUserInfo } = props; const participants = detail.participants || []; const { participant_count, @@ -724,9 +727,11 @@ function Participants(props) { participant_user_id === organizer_id ? "组织者" : "参与者"; return ( - {nickname || "未知"} @@ -808,7 +813,7 @@ function genRecommendGames(games, location, avatar) { avatar, applications: max_players, checkedApplications: current_players, - levelRequirements: `NTRP ${genNTRPRequirementText(skill_level_min, skill_level_max)}`, + levelRequirements: skill_level_max !== skill_level_min ? `${skill_level_min || '-'}至${skill_level_max || '-'}` : skill_level_min === 1 ? '无要求' : `${skill_level_min}以上`, playType: play_type, }; }); @@ -819,6 +824,7 @@ function OrganizerInfo(props) { userInfo, currentLocation: location, onUpdateUserInfo = () => {}, + handleViewUserInfo, } = props; const { id, @@ -855,11 +861,11 @@ function OrganizerInfo(props) { } }; - const viewOtherPage = () => { - Taro.navigateTo({ - url: `/user_pages/other/index?userid=${id}`, - }); - }; + function handleViewGame(gameId) { + Taro.navigateTo({ + url: `/game_pages/detail/index?id=${gameId}&from=current` + }) + } return ( @@ -869,7 +875,7 @@ function OrganizerInfo(props) { {/* organizer avatar and name */} - + {nickname} @@ -909,7 +915,7 @@ function OrganizerInfo(props) { {/* recommend games by organizer */} - {}}> + TA的更多活动 {recommendGames.map((game, index) => ( - + {/* game title */} {game.title} @@ -943,9 +949,11 @@ function OrganizerInfo(props) { {/* organizer avatar、applications、level requirements、play type */} - { e.stopPropagation(); handleViewUserInfo(id) }} /> @@ -1049,6 +1057,12 @@ function Index() { } } + function handleViewUserInfo(userId) { + Taro.navigateTo({ + url: `/user_pages/other/index?userid=${userId}` + }) + } + console.log("detail", detail); const backgroundImage = detail?.image_list?.[0] ? { backgroundImage: `url(${detail?.image_list?.[0]})` } @@ -1079,7 +1093,7 @@ function Index() { {/* content */} {/* avatar and tags */} - + {/* title */} {detail.title} @@ -1091,7 +1105,7 @@ function Index() { {/* gameplay requirements */} {/* participants */} - + {/* supplemental notes */} {/* organizer and recommend games by organizer */} @@ -1100,6 +1114,7 @@ function Index() { userInfo={userInfo} currentLocation={currentLocation} onUpdateUserInfo={onUpdateUserInfo} + handleViewUserInfo={handleViewUserInfo} /> {/* sticky bottom action bar */} { - const [, theTime] = item.application_time.split("undefined "); - const theTimeObj = dayjs(theTime); + const isLast = index === refund_policy.length - 1 + const theTimeObj = dayjs(isLast ? refund_policy.at(-2).deadline_formatted : item.deadline_formatted); const year = theTimeObj.format("YYYY"); const month = theTimeObj.format("M"); const day = theTimeObj.format("D"); - const time = theTimeObj.format("HH:MM"); + const time = theTimeObj.format("HH:mm"); return { - time: `${year}年${month}月${day}日${time}${index === 0 ? "前" : "后"}`, + time: `${year}年${month}月${day}日${time} ${isLast ? "后" : "前"}`, rule: item.refund_rule, }; }), diff --git a/src/order_pages/orderList/index.module.scss b/src/order_pages/orderList/index.module.scss index e42fc43..3426f13 100644 --- a/src/order_pages/orderList/index.module.scss +++ b/src/order_pages/orderList/index.module.scss @@ -1,81 +1,158 @@ @use "~@/scss/images.scss" as img; .container { - padding: 12px; + padding: 12px 12px 40px; background-color: #fafafa; - min-height: 100vh; + height: 100vh; + width: 100%; + box-sizing: border-box; + + .list { + height: 100%; + width: 100%; + position: relative; + background-color: #fff; + + // .bg { + // position: absolute; + // left: 0; + // top: 0; + // width: 100%; + // height: 100%; + // background-color: #fafafa; + // z-index: -1; + // } + + .endTips { + height: 40px; + display: flex; + align-items: center; + justify-content: center; + color: rgba(0, 0, 0, 0.8); + font-feature-settings: + "liga" off, + "clig" off; + font-family: "PingFang SC"; + font-size: 12px; + font-style: normal; + font-weight: 500; + line-height: 18px; + background-color: #f9f9f9; + } + } .orderItem { width: 100%; - height: 222px; + // height: 222px; background-color: #fff; border-radius: 12px; border: 1px solid rgba(0, 0, 0, 0.06); box-shadow: 0 4px 36px 0 rgba(0, 0, 0, 0.06); margin-bottom: 12px; - .orderTitle { - height: 18px; - padding: 15px 15px 12px; - border-bottom: 1px solid rgba(0, 0, 0, 0.06); - display: flex; - align-items: center; - justify-content: space-between; + // .orderTitle { + // height: 18px; + // padding: 15px 15px 12px; + // border-bottom: 1px solid rgba(0, 0, 0, 0.06); + // display: flex; + // align-items: center; + // justify-content: space-between; - .userInfo { + // .userInfo { + // display: flex; + // align-items: center; + // justify-content: flex-start; + // gap: 6px; + + // .avatar { + // width: 16px; + // height: 16px; + // } + + // .nickName { + // display: contents; + // .nickNameText { + // color: #000; + // font-feature-settings: + // "liga" off, + // "clig" off; + // font-family: "PingFang SC"; + // font-size: 12px; + // font-style: normal; + // font-weight: 500; + // line-height: 18px; + // } + // .arrowRight { + // width: 8px; + // height: 8px; + // } + // } + // } + + // .paidInfo { + // display: flex; + // align-items: center; + // justify-content: flex-end; + // gap: 8px; + + // .payTime { + // font-feature-settings: + // "liga" off, + // "clig" off; + // font-family: "PingFang SC"; + // font-size: 12px; + // font-style: normal; + // font-weight: 400; + // line-height: 18px; + + // &.paid { + // color: rgba(60, 60, 67, 0.6); + // } + + // &.pending { + // color: #000; + // } + // } + + // .payNum { + // font-feature-settings: + // "liga" off, + // "clig" off; + // font-family: "PingFang SC"; + // font-size: 12px; + // font-style: normal; + // font-weight: 600; + // line-height: 18px; + // &.paid { + // color: #000; + // } + + // &.pending { + // color: #ff3b30; + // } + // } + // } + // } + + .gameInfo { + height: 122px; + + .gameTitle { display: flex; align-items: center; - justify-content: flex-start; - gap: 6px; + justify-content: space-between; + padding: 12px 15px 0; - .avatar { - width: 16px; - height: 16px; - } - - .nickName { - display: contents; - .nickNameText { - color: #000; - font-feature-settings: - "liga" off, - "clig" off; - font-family: "PingFang SC"; - font-size: 12px; - font-style: normal; - font-weight: 500; - line-height: 18px; - } - .arrowRight { - width: 8px; - height: 8px; - } - } - } - - .paidInfo { - display: flex; - align-items: center; - justify-content: flex-end; - gap: 8px; - - .payTime { - font-feature-settings: - "liga" off, - "clig" off; + .title { + overflow: hidden; + color: #000; + font-feature-settings: 'liga' off, 'clig' off; + text-overflow: ellipsis; font-family: "PingFang SC"; - font-size: 12px; + font-size: 16px; font-style: normal; - font-weight: 400; - line-height: 18px; - - &.paid { - color: rgba(60, 60, 67, 0.6); - } - - &.pending { - color: #000; - } + font-weight: 600; + line-height: 24px; /* 150% */ } .payNum { @@ -96,14 +173,96 @@ } } } - } - .gameInfo { - height: 122px; + .gameTime { + padding: 6px 0 0 15px; + display: flex; + align-items: center; + justify-content: flex-start; + gap: 8px; + color: rgba(60, 60, 67, 0.60); + font-feature-settings: 'liga' off, 'clig' off; + text-overflow: ellipsis; + font-family: "PingFang SC"; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 18px; /* 150% */ + } + + .address { + padding: 6px 0 0 15px; + display: flex; + align-items: center; + justify-content: flex-start; + gap: 4px; + + color: rgba(60, 60, 67, 0.60); + font-feature-settings: 'liga' off, 'clig' off; + text-overflow: ellipsis; + font-family: "PingFang SC"; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 18px; /* 150% */ + } + + .gameOtherInfo { + padding: 8px 0 12px 15px; + height: 20px; + display: flex; + align-items: center; + justify-content: flex-start; + gap: 4px; + + .avatarCards { + display: flex; + align-items: center; + justify-content: flex-start; + height: 20px; + + .avatar { + width: 20px; + height: 20px; + border-radius: 50%; + border: 1px solid rgba(0, 0, 0, 0.06); + &+.avatar { + margin-left: -10px; + } + } + } + + .participantProgress, .levelReq, .playType { + display: flex; + height: 20px; + padding: 0px 8px; + align-items: center; + gap: 4px; + border-radius: 999px; + border: 0.5px solid rgba(0, 0, 0, 0.16); + background: #FFF; + color: #000; + font-feature-settings: 'liga' off, 'clig' off; + font-family: "PingFang SC"; + font-size: 11px; + font-style: normal; + font-weight: 500; + line-height: 20px; /* 181.818% */ + letter-spacing: -0.23px; + } + + .participantProgress { + color: #c4c4c7; + + .current { + color: #000; + } + } + } } .orderActions { - height: 28px; + min-height: 28px; padding: 12px 12px 15px; border-top: 1px solid rgba(0, 0, 0, 0.06); @@ -135,6 +294,9 @@ &:last-child { background: #000; color: #fff; + &.payNow { + background-color: #ff3b30; + } } } @@ -142,6 +304,7 @@ } .payNow { + background-color: #ff3b30; } } } @@ -215,3 +378,47 @@ } } } + +.dialogFooter { + // width: 100%; + width: calc(100% + 1px); + height: 44px; + box-sizing: border-box; + display: flex; + align-items: center; + justify-content: flex-end; + position: absolute; + // margin: 0 -24px -24px; + bottom: 0; + left: 0; + border-top: 1px solid rgba(0, 0, 0, 0.06); + border-bottom-left-radius: 16px; + border-bottom-right-radius: 16px; + overflow: hidden; + + & > .cancel, & > .confirm { + padding: 12px 10px; + height: 44px; + width: 50%; + text-align: center; + // border: 0.5px solid rgba(0, 0, 0, 0.06); + color: #000; + font-size: 16px; + font-style: normal; + font-weight: 600; + line-height: 20px; + + &:last-child { + background: #000; + color: #fff; + } + } + + & > .cancel { + border-radius: 0; + } + + & > .confirm { + border-radius: 0; + } +} diff --git a/src/order_pages/orderList/index.tsx b/src/order_pages/orderList/index.tsx index 070b52b..8f9adcd 100644 --- a/src/order_pages/orderList/index.tsx +++ b/src/order_pages/orderList/index.tsx @@ -1,5 +1,5 @@ -import React, { useState } from "react"; -import { View, Text, Button, Image } from "@tarojs/components"; +import React, { useState, useEffect } from "react"; +import { View, Text, Button, Image, ScrollView } from "@tarojs/components"; import Taro, { useDidShow } from "@tarojs/taro"; import { Avatar, Dialog } from "@nutui/nutui-react-taro"; import dayjs from "dayjs"; @@ -8,29 +8,98 @@ import orderService, { OrderStatus, CancelType } from "@/services/orderService"; import { withAuth } from "@/components"; import { payOrder } from "@/utils"; import styles from "./index.module.scss"; -import orderListArrowRight from "@/static/order/orderListArrowRight.svg"; +// import orderListArrowRight from "@/static/order/orderListArrowRight.svg"; +dayjs.locale("zh-cn"); + +const PAGESIZE = 20; + +// 将·作为连接符插入到标签文本之间 +function insertDotInTags(tags: string[]) { + return tags.join("-·-").split("-"); +} + +const diffDayMap = new Map([ + [0, "今天"], + [1, "明天"], + [2, "后天"], +]); + +const DayOfWeekMap = new Map([ + [0, "周日"], + [1, "周一"], + [2, "周二"], + [3, "周三"], + [4, "周四"], + [5, "周五"], + [6, "周六"], +]); + +function generateTimeMsg(game_info) { + const { start_time, end_time } = game_info; + const startTime = dayjs(start_time); + const endTime = dayjs(end_time); + const diffDay = startTime.startOf("day").diff(dayjs().startOf("day"), "day"); + + const dayofWeek = startTime.day(); + const gameLength = `${endTime.diff(startTime, "hour")}小时`; + return ( + <> + + {diffDay <= 2 + ? diffDayMap.get(diffDay) + : startTime.format("YYYY-MM-DD")} + + ({DayOfWeekMap.get(dayofWeek)}) + {gameLength} + + ); +} const OrderList = () => { - const [list, setList] = useState([]); + const [list, setList] = useState([]); + const [total, setTotal] = useState(0); - useDidShow(() => { - getOrders(); - }); + const end = list.length * PAGESIZE >= total; - async function getOrders() { - const res = await orderService.getOrderList(); - console.log(res); + useEffect(() => { + getOrders(1); + }, []); + + function addPageInfo(arr, page) { + return arr.map((item) => ({ ...item, page })); + } + + // clear 是否清除当前页后面的数据(如果有的话,没有也不影响) + async function getOrders(page, clear = true) { + const res = await orderService.getOrderList({ page, pageSize: PAGESIZE }); if (res.code === 0) { - setList(res.data.rows); + setTotal(res.data.count); + setList((prev) => { + const newList = [...prev]; + const index = page - 1 + newList.splice( + index, + clear ? newList.length - index : 1, + addPageInfo(res.data.rows, page) + ); + return newList; + }); } } - async function handlePayNow(gameId) { + function handleFetchNext() { + console.log("scroll"); + if (!end) { + getOrders(list.length + 1); + } + } + + async function handlePayNow(item) { try { - const unPaidRes = await orderService.getUnpaidOrder(gameId); + const unPaidRes = await orderService.getUnpaidOrder(item.game_info.id); if (unPaidRes.code === 0 && unPaidRes.data.has_unpaid_order) { await payOrder(unPaidRes.data.payment_params); - getOrders(); + getOrders(item.page, false); } else { throw new Error("支付调用失败"); } @@ -43,6 +112,12 @@ const OrderList = () => { } } + function handleViewGame(gameId) { + Taro.navigateTo({ + url: `/game_pages/detail/index?id=${gameId}&from=orderList`, + }); + } + function renderCancelContent(checkOrderInfo) { const { refund_policy = [] } = checkOrderInfo; const policyList = [ @@ -51,14 +126,18 @@ const OrderList = () => { rule: "退款规则", }, ...refund_policy.map((item, index) => { - const [, theTime] = item.application_time.split("undefined "); - const theTimeObj = dayjs(theTime); + const isLast = index === refund_policy.length - 1; + const theTimeObj = dayjs( + isLast + ? refund_policy.at(-2).deadline_formatted + : item.deadline_formatted + ); const year = theTimeObj.format("YYYY"); const month = theTimeObj.format("M"); const day = theTimeObj.format("D"); const time = theTimeObj.format("HH:MM"); return { - time: `${year}年${month}月${day}日${time}${index === 0 ? "前" : "后"}`, + time: `${year}年${month}月${day}日${time} ${isLast ? "后" : "前"}`, rule: item.refund_rule, }; }), @@ -81,34 +160,104 @@ const OrderList = () => { ); } + async function handleDeleteOrder(item) { + const { id: order_id } = item + // TODO:删除订单,刷新这一页,然后后面的全清除掉 + const onCancel = () => { + Dialog.close("cancelOrder"); + }; + const onConfirm = async () => { + try { + const deleteRes = await orderService.deleteOrder({ + order_id, + }); + if (deleteRes.code !== 0) { + throw new Error(deleteRes.message); + } + getOrders(item.page); + Taro.showToast({ + title: "删除成功", + icon: "none", + }) + } catch (e) { + Taro.showToast({ + title: e.message, + icon: "error", + }); + } finally { + Dialog.close("cancelOrder"); + } + }; + Dialog.open("cancelOrder", { + title: "确定删除订单吗?", + content: "删除订单后,您将无法恢复订单。请确认是否继续取消?", + footer: ( + + + + + ), + onConfirm, + onCancel, + }) + } + async function handleCancelOrder(item) { const { order_no, order_status, game_info, amount } = item; if (order_status === OrderStatus.PENDING) { + const onCancel = () => { + Dialog.close("cancelOrder"); + }; + const onConfirm = async () => { + try { + const cancelRes = await orderService.cancelUnpaidOrder({ + order_no, + cancel_reason: "用户主动取消", + }); + if (cancelRes.code !== 0) { + throw new Error(cancelRes.message); + } + getOrders(item.page, false); + Taro.showToast({ + title: "取消成功", + icon: "none", + }) + } catch (e) { + Taro.showToast({ + title: e.message, + icon: "error", + }); + } finally { + Dialog.close("cancelOrder"); + } + }; Dialog.open("cancelOrder", { title: "确定取消订单吗?", content: "取消订单后,您将无法恢复订单。请确认是否继续取消?", - onConfirm: async () => { - try { - const cancelRes = await orderService.cancelUnpaidOrder({ - order_no, - cancel_reason: "用户主动取消", - }); - if (cancelRes.code !== 0) { - throw new Error(cancelRes.message); - } - getOrders(); - } catch (e) { - Taro.showToast({ - title: e.message, - icon: "error", - }); - } finally { - Dialog.close("cancelOrder"); - } - }, - onCancel: () => { - Dialog.close("cancelOrder"); - }, + footer: ( + + + + + ), + onConfirm, + onCancel, }); return; } @@ -126,7 +275,11 @@ const OrderList = () => { if (refundRes.code !== 0) { throw new Error(refundRes.message); } - getOrders(); + getOrders(item.page, false); + Taro.showToast({ + title: "退出成功", + icon: "none", + }) } catch (e) { Taro.showToast({ title: e.message, @@ -150,89 +303,149 @@ const OrderList = () => { return ( - {list.map((item) => { - const unPay = item.order_status === OrderStatus.PENDING; - const expired = - item.order_status === OrderStatus.FINISHED || - [CancelType.TIMEOUT, CancelType.USER].includes(item.cancel_type); - const expiredTime = dayjs(item.expire_time).isSame(dayjs(), "day") - ? dayjs(item.expire_time).format("HH:mm:ss") - : dayjs(item.expire_time).format("YYYY-MM-DD HH:mm:ss"); - const showCancel = - item.order_status !== OrderStatus.FINISHED && - item.cancel_type === CancelType.NONE; + + {/* */} + {list.flat().map((item) => { + const unPay = item.order_status === OrderStatus.PENDING; + const expired = + item.order_status === OrderStatus.FINISHED || + [CancelType.TIMEOUT, CancelType.USER].includes(item.cancel_type); + // const expiredTime = dayjs(item.expire_time).isSame(dayjs(), "day") + // ? dayjs(item.expire_time).format("HH:mm:ss") + // : dayjs(item.expire_time).format("YYYY-MM-DD HH:mm:ss"); + const showCancel = + item.order_status === OrderStatus.PENDING && + item.cancel_type === CancelType.NONE; + const showQuit = + item.order_status === OrderStatus.PAID && + item.cancel_type === CancelType.NONE; - return ( - - - - - - Light - - - - {expired ? ( - "" - ) : ( - - - {unPay - ? `请在 ${expiredTime} 前支付` - : dayjs(item.pay_time).format("YYYY-MM-DD HH:mm:ss")} - - + + + {item?.game_info?.title} + - {unPay ? "待支付" : "已支付"} ¥ {item.amount} - + {unPay && !expired ? "待支付" : "已支付"} ¥{" "} + {item.amount} + + + + {generateTimeMsg(item.game_info)} + + + {insertDotInTags([location_name, court_type, "3.5km"]).map( + (text, index) => ( + {text} + ) + )} + + + {participants.length >= 0 ? ( + + {/* participants */[{ user: { avatar_url: 'https://img.yzcdn.cn/vant/cat.jpeg', id: 1 } }, { user: { avatar_url: 'https://img.yzcdn.cn/vant/cat.jpeg', id: 2 } }, { user: { avatar_url: 'https://img.yzcdn.cn/vant/cat.jpeg', id: 3 } }].map((participant) => { + const { + user: { avatar_url, id }, + } = participant; + return ; + })} + + ) : ( + "" + )} + + 报名人数 {current_players} + / + {max_players} + + + {skill_level_max !== skill_level_min + ? `${skill_level_min || "-"} 至 ${skill_level_max || "-"}` + : skill_level_min === 1 + ? "无要求" + : `${skill_level_min} 以上`} + + {play_type} + + + + + + {expired && ( + + )} + {showCancel && ( + + )} + {showQuit && ( + + )} + {unPay && !expired ? ( + + ) : ( + + )} - )} - - - {item?.game_info?.title} - - - - - {showCancel && ( - - )} - {unPay && !expired && ( - - )} - - ); - })} + ); + })} + {end && 已经到底了~} + ); diff --git a/src/services/orderService.ts b/src/services/orderService.ts index 8b2646f..fbf6a05 100644 --- a/src/services/orderService.ts +++ b/src/services/orderService.ts @@ -78,8 +78,8 @@ export interface GameOrderRes { // 发布球局类 class OrderService { // 查询订单列表 - async getOrderList() { - return httpService.post("/user/orders", {}, { showLoading: true }); + async getOrderList(pagination: { page: number, pageSize: number }) { + return httpService.post("/user/orders", pagination, { showLoading: true }); } // 获取订单详情 @@ -161,6 +161,21 @@ class OrderService { }, ); } + + // 删除订单 + async deleteOrder({ + order_id, + }: { + order_id: number; + }): Promise> { + return httpService.post( + "/payment/delete_order", + { order_id }, + { + showLoading: true, + }, + ); + } } // 导出认证服务实例 From 46f0dc4edf4832ead8b5c05a2a2491c67591eaef Mon Sep 17 00:00:00 2001 From: Ultrame <1019265060@qq.com> Date: Tue, 16 Sep 2025 00:02:08 +0800 Subject: [PATCH 10/90] =?UTF-8?q?=E7=BC=96=E8=BE=91=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E9=A1=B5=E5=9B=BE=E6=A0=87=E3=80=81=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E8=BF=94=E5=9B=9E=E6=8C=89=E9=92=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/static/userInfo/ball.svg | 12 ++++++++ src/static/userInfo/birthday.svg | 12 ++++++++ src/static/userInfo/business.svg | 6 ++++ src/static/userInfo/gender.svg | 7 +++++ src/static/userInfo/introduce.svg | 5 ++++ src/static/userInfo/phone.svg | 5 ++++ src/static/userInfo/user.svg | 12 ++++++++ src/static/userInfo/wallet.svg | 8 ++++++ src/user_pages/edit/index.config.ts | 1 + src/user_pages/edit/index.scss | 42 +++++++++++++++++++++++++++- src/user_pages/edit/index.tsx | 36 +++++++++++++++++------- src/user_pages/myself/index.tsx | 6 ++-- src/user_pages/other/index.config.ts | 1 + src/user_pages/other/index.tsx | 13 ++++++++- 14 files changed, 151 insertions(+), 15 deletions(-) create mode 100644 src/static/userInfo/ball.svg create mode 100644 src/static/userInfo/birthday.svg create mode 100644 src/static/userInfo/business.svg create mode 100644 src/static/userInfo/gender.svg create mode 100644 src/static/userInfo/introduce.svg create mode 100644 src/static/userInfo/phone.svg create mode 100644 src/static/userInfo/user.svg create mode 100644 src/static/userInfo/wallet.svg diff --git a/src/static/userInfo/ball.svg b/src/static/userInfo/ball.svg new file mode 100644 index 0000000..fefdde4 --- /dev/null +++ b/src/static/userInfo/ball.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/static/userInfo/birthday.svg b/src/static/userInfo/birthday.svg new file mode 100644 index 0000000..eaf7696 --- /dev/null +++ b/src/static/userInfo/birthday.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/static/userInfo/business.svg b/src/static/userInfo/business.svg new file mode 100644 index 0000000..25e6c16 --- /dev/null +++ b/src/static/userInfo/business.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/static/userInfo/gender.svg b/src/static/userInfo/gender.svg new file mode 100644 index 0000000..6546084 --- /dev/null +++ b/src/static/userInfo/gender.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/static/userInfo/introduce.svg b/src/static/userInfo/introduce.svg new file mode 100644 index 0000000..589eb4d --- /dev/null +++ b/src/static/userInfo/introduce.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/static/userInfo/phone.svg b/src/static/userInfo/phone.svg new file mode 100644 index 0000000..ae37ac9 --- /dev/null +++ b/src/static/userInfo/phone.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/static/userInfo/user.svg b/src/static/userInfo/user.svg new file mode 100644 index 0000000..03cb710 --- /dev/null +++ b/src/static/userInfo/user.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/static/userInfo/wallet.svg b/src/static/userInfo/wallet.svg new file mode 100644 index 0000000..12fd9d2 --- /dev/null +++ b/src/static/userInfo/wallet.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/user_pages/edit/index.config.ts b/src/user_pages/edit/index.config.ts index a951369..7f8c559 100644 --- a/src/user_pages/edit/index.config.ts +++ b/src/user_pages/edit/index.config.ts @@ -5,4 +5,5 @@ export default definePageConfig({ backgroundColor: '#f5f5f5', enablePullDownRefresh: false, disableScroll: false, + navigationStyle: 'custom', }) \ No newline at end of file diff --git a/src/user_pages/edit/index.scss b/src/user_pages/edit/index.scss index b9d3061..99c75ce 100644 --- a/src/user_pages/edit/index.scss +++ b/src/user_pages/edit/index.scss @@ -7,6 +7,46 @@ box-sizing: border-box; } +.custom-navbar { + height: 56px; + /* 通常与原生导航栏高度一致 */ + display: flex; + align-items: center; + justify-content: center; + // background-color: #fff; + color: #000; + padding-top: 44px; + /* 适配状态栏 */ + position: sticky; + top: 0; + z-index: 100; + overflow: hidden; + background-color: rgb(238, 255, 220); +} + +.detail-navigator { + height: 30px; + width: 80px; + border-radius: 15px; + position: absolute; + left: 12px; + box-sizing: border-box; + display: flex; + align-items: center; + + .detail-navigator-back { + height: 32px; + width: 50%; + display: flex; + justify-content: center; + + &>.detail-navigator-back-icon { + width: 32px; + height: 32px; + } + } +} + // 导航栏 .navbar { position: fixed; @@ -84,7 +124,7 @@ align-items: center; gap: 12px; margin-bottom: 48px; - margin-top: 98px; + // margin-top: 98px; .avatar_container { position: relative; diff --git a/src/user_pages/edit/index.tsx b/src/user_pages/edit/index.tsx index 7bb01fc..f2c7a19 100644 --- a/src/user_pages/edit/index.tsx +++ b/src/user_pages/edit/index.tsx @@ -7,6 +7,7 @@ import { UserService } from '@/services/userService'; import { clear_login_state } from '@/services/loginService'; import { convert_db_gender_to_display } from '@/utils/genderUtils'; import { EditModal } from '@/components'; +import img from "@/config/images"; const EditProfilePage: React.FC = () => { // 用户信息状态 @@ -264,7 +265,6 @@ const EditProfilePage: React.FC = () => { }); return; } - console.log('用户手机号信息aaa:', e) try { const phone = await UserService.parse_phone(e.detail.code); handle_field_edit('phone', phone); @@ -281,7 +281,16 @@ const EditProfilePage: React.FC = () => { return ( {/* 导航栏 */} - + + + { Taro.navigateBack() }}> + + + + {/* 主要内容 */} {loading ? ( @@ -311,7 +320,7 @@ const EditProfilePage: React.FC = () => { handle_open_edit_modal('nickname')}> - + 名字 @@ -332,7 +341,7 @@ const EditProfilePage: React.FC = () => { > - + 性别 @@ -355,7 +364,7 @@ const EditProfilePage: React.FC = () => { > - + 生日 @@ -372,7 +381,7 @@ const EditProfilePage: React.FC = () => { handle_open_edit_modal('personal_profile')}> - + 简介 @@ -391,7 +400,7 @@ const EditProfilePage: React.FC = () => { {/* 地区 */} - + 地区 @@ -410,7 +419,7 @@ const EditProfilePage: React.FC = () => { {/* NTRP水平 */} - + NTRP 水平 @@ -430,7 +439,7 @@ const EditProfilePage: React.FC = () => { {/* 职业 */} - + 职业 @@ -452,7 +461,7 @@ const EditProfilePage: React.FC = () => { - + 手机 @@ -472,6 +481,13 @@ const EditProfilePage: React.FC = () => { + {/* 注销账号 */} + + + 注销账号 + + + {/* 退出登录 */} diff --git a/src/user_pages/myself/index.tsx b/src/user_pages/myself/index.tsx index 1d76c67..d9d92e5 100644 --- a/src/user_pages/myself/index.tsx +++ b/src/user_pages/myself/index.tsx @@ -177,15 +177,15 @@ const MyselfPage: React.FC = () => { className="action_icon" src={require("@/static/userInfo/order_btn.svg")} /> - 我的订单 + 球局订单 - 收藏 + 钱包 diff --git a/src/user_pages/other/index.config.ts b/src/user_pages/other/index.config.ts index 5dcd237..bc1e99b 100644 --- a/src/user_pages/other/index.config.ts +++ b/src/user_pages/other/index.config.ts @@ -1,3 +1,4 @@ export default definePageConfig({ navigationBarTitleText: '用户主页', + navigationStyle: 'custom', }) \ No newline at end of file diff --git a/src/user_pages/other/index.tsx b/src/user_pages/other/index.tsx index a1dc8ed..87fab8d 100644 --- a/src/user_pages/other/index.tsx +++ b/src/user_pages/other/index.tsx @@ -1,7 +1,8 @@ import React, { useState, useEffect } from "react"; -import { View, Text, ScrollView } from "@tarojs/components"; +import { View, Text, ScrollView, Image } from "@tarojs/components"; import ListContainer from "@/container/listContainer"; import Taro from "@tarojs/taro"; +import img from "@/config/images"; import "./index.scss"; // import GuideBar from "@/components/GuideBar"; import { @@ -163,6 +164,16 @@ const OtherUserPage: React.FC = () => { return ( + + + { Taro.navigateBack() }}> + + + + {/* 主要内容 */} {/* 用户信息区域 */} From 4ef92e45693448bbb00e4e0ccc0a22d733d81a58 Mon Sep 17 00:00:00 2001 From: Ultrame <1019265060@qq.com> Date: Tue, 16 Sep 2025 00:04:51 +0800 Subject: [PATCH 11/90] =?UTF-8?q?=E7=BC=96=E8=BE=91=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E9=A1=B5=E5=9B=BE=E6=A0=87=E3=80=81=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E8=BF=94=E5=9B=9E=E6=8C=89=E9=92=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/user_pages/other/index.scss | 54 ++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/src/user_pages/other/index.scss b/src/user_pages/other/index.scss index c768750..8aa4539 100644 --- a/src/user_pages/other/index.scss +++ b/src/user_pages/other/index.scss @@ -1,16 +1,54 @@ // 他人用户页面样式 .other_user_page { min-height: 100vh; - background: radial-gradient( - circle at 50% 0%, - rgba(238, 255, 220, 1) 0%, - rgba(255, 255, 255, 1) 37% - ); + background: radial-gradient(circle at 50% 0%, + rgba(238, 255, 220, 1) 0%, + rgba(255, 255, 255, 1) 37%); position: relative; - overflow: hidden; + // overflow: hidden; box-sizing: border-box; } +.custom-navbar { + height: 56px; + /* 通常与原生导航栏高度一致 */ + display: flex; + align-items: center; + justify-content: center; + // background-color: #fff; + color: #000; + padding-top: 44px; + /* 适配状态栏 */ + position: sticky; + top: 0; + z-index: 100; + overflow: hidden; + background-color: rgb(238, 255, 220); +} + +.detail-navigator { + height: 30px; + width: 80px; + border-radius: 15px; + position: absolute; + left: 12px; + box-sizing: border-box; + display: flex; + align-items: center; + + .detail-navigator-back { + height: 32px; + width: 50%; + display: flex; + justify-content: center; + + &>.detail-navigator-back-icon { + width: 32px; + height: 32px; + } + } +} + // 主要内容区域 .main_content { position: relative; @@ -27,7 +65,7 @@ flex-direction: column; gap: 16px; margin-bottom: 16px; - margin-top: 98px; + // margin-top: 98px; // 基本信息 .basic_info { @@ -504,4 +542,4 @@ } } } -} +} \ No newline at end of file From 97ef21c366bfac78a05fdcbf47f0be1349fd9da1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=88=90?= Date: Tue, 16 Sep 2025 10:01:27 +0800 Subject: [PATCH 12/90] =?UTF-8?q?=E4=BF=AE=E6=94=B9=20=E5=88=97=E8=A1=A8?= =?UTF-8?q?=E9=A1=B5=E4=B8=8D=E8=A6=81=20load=20=20=EF=BC=8C=E6=8F=90?= =?UTF-8?q?=E9=AB=98=E6=B5=81=E7=95=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/listApi.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/listApi.ts b/src/services/listApi.ts index 4b9abd4..84692c5 100644 --- a/src/services/listApi.ts +++ b/src/services/listApi.ts @@ -25,7 +25,7 @@ export const getGamesList = async (params?: Record) => { */ // const isIntegrate = params?.order === '0'; // const fetchApi = isIntegrate ? '/games/integrate_list' : '/games/list' - return httpService.post('/games/list', params, { showLoading: true }) + return httpService.post('/games/list', params, { showLoading: false }) } catch (error) { console.error("列表数据获取失败:", error); throw error; @@ -39,7 +39,7 @@ export const getGamesList = async (params?: Record) => { */ export const getGamesIntegrateList = async (params?: Record) => { try { - return httpService.post('/games/integrate_list', params, { showLoading: true }) + return httpService.post('/games/integrate_list', params, { showLoading: false }) } catch (error) { console.error("列表数据获取失败:", error); throw error; From 4a00c7f1d864f255d0bae6bbed74660fd4c98c27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=88=90?= Date: Tue, 16 Sep 2025 10:52:54 +0800 Subject: [PATCH 13/90] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=B8=8D=E8=83=BD?= =?UTF-8?q?=E4=BD=BF=E7=94=A8code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/userService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/userService.ts b/src/services/userService.ts index 20f6d63..8402387 100644 --- a/src/services/userService.ts +++ b/src/services/userService.ts @@ -224,7 +224,7 @@ export class UserService { if (response.code === 0) { const userData = response.data; return { - id: userData.user_code || userData.id || '', + id: userData.id || '', nickname: userData.nickname || '', avatar: userData.avatar_url || '', join_date: userData.subscribe_time ? `${new Date(userData.subscribe_time).getFullYear()}年${new Date(userData.subscribe_time).getMonth() + 1}月加入` : '', From a045b395808e04e76573d2e09c5107dc2127f552 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=9D=B0?= Date: Tue, 16 Sep 2025 14:34:11 +0800 Subject: [PATCH 14/90] =?UTF-8?q?feat:=20=E8=AE=A2=E5=8D=95=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E5=9F=BA=E6=9C=AC=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Auth/index.tsx | 19 +- src/components/index.ts | 2 + src/components/refundPopup/index.module.scss | 132 ++++++++ src/components/refundPopup/index.tsx | 139 ++++++++ src/order_pages/orderDetail/index.module.scss | 71 ++++ src/order_pages/orderDetail/index.tsx | 251 +++++++++++++-- src/order_pages/orderList/index.module.scss | 68 ---- src/order_pages/orderList/index.tsx | 302 +++++++----------- src/services/orderService.ts | 6 + src/static/order/orderListClose.svg | 4 + src/utils/index.ts | 2 + src/utils/orderActions.ts | 68 ++++ src/utils/routeUtil.ts | 23 ++ 13 files changed, 799 insertions(+), 288 deletions(-) create mode 100644 src/components/refundPopup/index.module.scss create mode 100644 src/components/refundPopup/index.tsx create mode 100644 src/static/order/orderListClose.svg create mode 100644 src/utils/orderActions.ts create mode 100644 src/utils/routeUtil.ts diff --git a/src/components/Auth/index.tsx b/src/components/Auth/index.tsx index 9044f41..6cbf848 100644 --- a/src/components/Auth/index.tsx +++ b/src/components/Auth/index.tsx @@ -1,26 +1,9 @@ import React, { useEffect, useState } from "react"; import { View } from "@tarojs/components"; import Taro from "@tarojs/taro"; +import { getCurrentFullPath } from '@/utils'; import { check_login_status } from "@/services/loginService"; -export function getCurrentFullPath(): string { - const pages = Taro.getCurrentPages(); - const currentPage = pages.at(-1); - - if (currentPage) { - console.log(currentPage, "currentPage get"); - const route = currentPage.route; - const options = currentPage.options || {}; - - const query = Object.keys(options) - .map((key) => `${key}=${options[key]}`) - .join("&"); - - return query ? `/${route}?${query}` : `/${route}`; - } - return ""; -} - export default function withAuth

( WrappedComponent: React.ComponentType

, ) { diff --git a/src/components/index.ts b/src/components/index.ts index 2918423..abc1c4a 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -16,6 +16,7 @@ import EditModal from "./EditModal/index"; import withAuth from "./Auth"; import { CustomPicker, PopupPicker } from "./Picker"; import NTRPEvaluatePopup from "./NTRPEvaluatePopup"; +import RefundPopup from "./refundPopup"; export { ActivityTypeSwitch, @@ -37,4 +38,5 @@ export { CustomPicker, PopupPicker, NTRPEvaluatePopup, + RefundPopup, }; diff --git a/src/components/refundPopup/index.module.scss b/src/components/refundPopup/index.module.scss new file mode 100644 index 0000000..adbfc5a --- /dev/null +++ b/src/components/refundPopup/index.module.scss @@ -0,0 +1,132 @@ +.refundPolicy { + padding-top: 20px; + // .moduleTitle { + // display: flex; + // padding: 15px 0 8px; + // justify-content: space-between; + // align-items: center; + // align-self: stretch; + // color: #000; + // font-feature-settings: + // "liga" off, + // "clig" off; + // font-family: "PingFang SC"; + // font-size: 14px; + // font-style: normal; + // font-weight: 600; + // line-height: 20px; + // letter-spacing: -0.23px; + // } + + .specTips { + padding-bottom: 20px; + color: rgba(60, 60, 67, 0.60); + text-align: center; + font-feature-settings: 'liga' off, 'clig' off; + font-family: "PingFang SC"; + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 18px; + } + + .policyList { + border-radius: 12px; + border: 1px solid rgba(0, 0, 0, 0.06); + background: #fff; + box-shadow: 0 4px 36px 0 rgba(0, 0, 0, 0.06); + + .policyItem { + display: flex; + justify-content: space-around; + align-items: center; + color: #000; + text-align: center; + font-feature-settings: + "liga" off, + "clig" off; + font-family: "PingFang SC"; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 20px; + border-top: 1px solid rgba(0, 0, 0, 0.06); + + &:nth-child(1) { + color: #000; + text-align: center; + font-feature-settings: + "liga" off, + "clig" off; + font-family: "PingFang SC"; + font-size: 14px; + font-style: normal; + font-weight: 600; + line-height: 20px; + border: none; + } + + .time, + .rule { + width: 50%; + padding: 10px 12px; + } + + .rule { + border-left: 1px solid rgba(0, 0, 0, 0.06); + } + } + } +} + +.container { + padding: 0 15px 40px; + + .header { + padding: 24px 15px 0; + height: 24px; + display: flex; + align-items: center; + justify-content: center; + + .title { + color: #000; + text-align: center; + font-feature-settings: 'liga' off, 'clig' off; + font-family: "PingFang SC"; + font-size: 16px; + font-style: normal; + font-weight: 600; + line-height: 20px; + margin-left: auto; + } + + .closeIcon { + margin-left: auto; + width: 20px; + height: 20px; + } + } + + .action { + display: flex; + align-items: center; + justify-content: center; + margin-top: 20px; + padding: 2px 6px; + height: 52px; + border-radius: 16px; + border: 1px solid rgba(0, 0, 0, 0.06); + box-shadow: 0 8px 64px 0 rgba(0, 0, 0, 0.10); + backdrop-filter: blur(16px); + color: #fff; + background-color: #000; + font-feature-settings: 'liga' off, 'clig' off; + font-family: "PingFang SC"; + font-size: 16px; + font-style: normal; + font-weight: 600; + line-height: 20px; + letter-spacing: -0.23px; + } +} \ No newline at end of file diff --git a/src/components/refundPopup/index.tsx b/src/components/refundPopup/index.tsx new file mode 100644 index 0000000..b7354d9 --- /dev/null +++ b/src/components/refundPopup/index.tsx @@ -0,0 +1,139 @@ +import React, { useState, useRef, forwardRef, useImperativeHandle } from 'react'; +import { View, Text, Button, Image } from '@tarojs/components' +import Taro from '@tarojs/taro'; +import dayjs from 'dayjs' +import { CommonPopup } from '@/components'; +import orderService from '@/services/orderService'; +import styles from './index.module.scss' +import closeIcon from '@/static/order/orderListClose.svg' + +function genRefundNotice (refund_policy) { + if (refund_policy.length === 0) { + return {} + } + const now = dayjs() + const deadlines = refund_policy.map(item => dayjs(item.deadline_formatted)) + let matchPolicyIndex = deadlines.findIndex(d => d.isAfter(now)) + if (matchPolicyIndex === -1) { + matchPolicyIndex = refund_policy.length - 1 + } + const { deadline_formatted, price, refund_rate } = refund_policy[matchPolicyIndex] + if (refund_rate === 1) { + return { refundPrice: price, notice: `本次可全额退款, ¥${price} 将原路退回,请查收` } + } else if (refund_rate === 0) { + return { refundPrice: 0, notice: `当前退出不可退款,后续流程未明确,@麻真瑜` } + } + const refundPrice = price * refund_rate + const leftHours = dayjs(deadline_formatted).diff(dayjs(), 'hour') + return { refundPrice, notice: `距活动开始已不足${leftHours}h,当前退出您需扣除${price - refundPrice}元` } +} + +function renderCancelContent(checkOrderInfo) { + const { refund_policy = [] } = checkOrderInfo; + const policyList = [ + { + time: "申请退款时间", + rule: "退款规则", + }, + ...refund_policy.map((item) => { + return { + time: item.application_time, + rule: item.refund_rule, + }; + }), + ]; + const { notice } = genRefundNotice(refund_policy) + return ( + + {/* + 退款政策 + */} + {{notice}} + {/* 订单信息摘要 */} + + {policyList.map((item, index) => ( + + {item.time} + {item.rule} + + ))} + + + ); +} + +export type RefundRef = { + show: (item: any, callback: (result: boolean) => void) => void +} + +export default forwardRef(function RefundPopup(_props, ref) { + const [visible, setVisible] = useState(false) + const [checkOrderInfo, setCheckOrderInfo] = useState({}) + const [orderData, setOrderData] = useState({}) + const onDown = useRef<((result: boolean) => void) | null>(null) + + useImperativeHandle(ref, () => ({ + show: onShow, + })) + + async function onShow (orderItem, onFinish: (result: boolean) => void) { + const { + game_info, + } = orderItem + onDown.current = onFinish + setOrderData(orderItem) + const res = await orderService.getCheckOrderInfo(game_info.id); + setCheckOrderInfo(res.data); + setVisible(true) + } + + function onClose () { + setVisible(false) + onDown.current?.(false) + } + + async function handleConfirmQuit () { + const { order_no, amount } = orderData + try { + const refundRes = await orderService.applicateRefund({ + order_no, + refund_amount: amount, + refund_reason: "用户主动退款", + }); + if (refundRes.code !== 0) { + throw new Error(refundRes.message); + } + Taro.showToast({ + title: "退出成功", + icon: "none", + }) + onDown.current?.(true) + } catch (e) { + Taro.showToast({ + title: e.message, + icon: "error", + }); + } finally { + onClose() + } + } + return ( + + + + 退出活动 + + + {renderCancelContent(checkOrderInfo)} + + + + ) +}) \ No newline at end of file diff --git a/src/order_pages/orderDetail/index.module.scss b/src/order_pages/orderDetail/index.module.scss index d7aed40..3c518c1 100644 --- a/src/order_pages/orderDetail/index.module.scss +++ b/src/order_pages/orderDetail/index.module.scss @@ -229,7 +229,34 @@ .gameInfoActions { min-height: 12px; + padding: 0 12px; border-top: 0.5px solid rgba(0, 0, 0, 0.06); + display: flex; + align-items: center; + justify-content: flex-start; + gap: 10px; + + & > .button { + margin: 12px 0; + padding: 4px 10px; + height: 28px; + border-radius: 999px; + border: 0.5px solid rgba(0, 0, 0, 0.06); + color: #000; + font-size: 12px; + font-style: normal; + font-weight: 600; + line-height: 20px; + letter-spacing: -0.23px; + + &:first-child { + background: #000; + color: #fff; + &.payNow { + background-color: #ff3b30; + } + } + } } } @@ -432,3 +459,47 @@ font-weight: 600; line-height: normal; } + +.dialogFooter { + // width: 100%; + width: calc(100% + 1px); + height: 44px; + box-sizing: border-box; + display: flex; + align-items: center; + justify-content: flex-end; + position: absolute; + // margin: 0 -24px -24px; + bottom: 0; + left: 0; + border-top: 1px solid rgba(0, 0, 0, 0.06); + border-bottom-left-radius: 16px; + border-bottom-right-radius: 16px; + overflow: hidden; + + & > .cancel, & > .confirm { + padding: 12px 10px; + height: 44px; + width: 50%; + text-align: center; + // border: 0.5px solid rgba(0, 0, 0, 0.06); + color: #000; + font-size: 16px; + font-style: normal; + font-weight: 600; + line-height: 20px; + + &:last-child { + background: #000; + color: #fff; + } + } + + & > .cancel { + border-radius: 0; + } + + & > .confirm { + border-radius: 0; + } +} diff --git a/src/order_pages/orderDetail/index.tsx b/src/order_pages/orderDetail/index.tsx index 922e08f..0bfacfa 100644 --- a/src/order_pages/orderDetail/index.tsx +++ b/src/order_pages/orderDetail/index.tsx @@ -1,31 +1,80 @@ -import React, { useState } from "react"; +import React, { useState, useRef } from "react"; import { View, Text, Button, Image } from "@tarojs/components"; +import { Dialog } from "@nutui/nutui-react-taro"; import Taro, { useDidShow, useRouter } from "@tarojs/taro"; import dayjs from "dayjs"; +import classnames from "classnames"; import orderService, { + CancelType, GameOrderRes, OrderStatus, + RefundStatus, } from "@/services/orderService"; import { payOrder, delay, calculateDistance, getCurrentLocation, + getOrderStatus, + generateOrderActions, + reloadPage, } from "@/utils"; import detailService, { GameData } from "@/services/detailService"; -import { withAuth } from "@/components"; +import { withAuth, RefundPopup } from "@/components"; import img from "@/config/images"; import { DECLAIMER } from "./config"; import styles from "./index.module.scss"; dayjs.locale("zh-cn"); +const refundTextMap = new Map([ + [RefundStatus.NONE, "已支付"], + [RefundStatus.PENDING, "退款中"], + [RefundStatus.SUCCESS, "已退款"], +]); + +const gameNoticeMap = new Map([ + [ + "pending", + { title: "球局暂未开始", content: "球局开始前2小时,我们将通过短信通知你" }, + ], + [ + "pendinging", + { + title: "球局即将开始,请按时抵达球局", + content: "球局开始前2小时,我们将通过短信通知你", + }, + ], + ["progress", { title: "球局已开始", content: "友谊第一,比赛第二" }], + ["finish", { title: "球局已结束", content: "" }], +]); + +function genGameNotice(order_status, start_time) { + const startTime = dayjs(start_time); + let key = ""; + if (order_status === OrderStatus.FINISHED) { + key = "finish"; + } + const leftHour = startTime.diff(dayjs(), "hour"); + const start = startTime.isBefore(dayjs()); + if (start) { + key = "progress"; + } else if (leftHour > 2) { + key = "pending"; + } else if (leftHour < 2) { + key = "pendinging"; + } + return gameNoticeMap.get(key) || {}; +} + function GameInfo(props) { const { detail, currentLocation, orderDetail } = props; - const { order_status } = orderDetail; + const { order_status, refund_status } = orderDetail; const { latitude, longitude, location, location_name, start_time, end_time } = detail || {}; + const refundRef = useRef(null); + const openMap = () => { Taro.openLocation({ latitude, // 纬度(必填) @@ -52,14 +101,129 @@ function GameInfo(props) { const startDate = `${startMonth}月${startDay}日 ${theDayOfWeek}`; const gameRange = `${startTime.format("HH:mm")} - ${endTime.format("HH:mm")}`; + const orderStatus = getOrderStatus(orderDetail); + + const gameNotice = genGameNotice(order_status, start_time); + + function handleViewGame(gameId) { + Taro.navigateTo({ + url: `/game_pages/detail/index?id=${gameId}&from=orderList`, + }); + } + + async function handleDeleteOrder(item) { + const { order_id } = item; + // TODO:删除订单,刷新这一页,然后后面的全清除掉 + const onCancel = () => { + Dialog.close("detailCancelOrder"); + }; + const onConfirm = async () => { + try { + const deleteRes = await orderService.deleteOrder({ + order_id, + }); + if (deleteRes.code !== 0) { + throw new Error(deleteRes.message); + } + Taro.showToast({ + title: "删除成功", + icon: "none", + }); + delay(2000); + Taro.redirectTo({ url: "/order_pages/orderList/index" }); + } catch (e) { + Taro.showToast({ + title: e.message, + icon: "error", + }); + } finally { + Dialog.close("detailCancelOrder"); + } + }; + Dialog.open("detailCancelOrder", { + title: "确定删除订单吗?", + content: "删除订单后,您将无法恢复订单。请确认是否继续取消?", + footer: ( + + + + + ), + onConfirm, + onCancel, + }); + } + + async function handleCancelOrder(item) { + const { order_no } = item; + const onCancel = () => { + Dialog.close("detailCancelOrder"); + }; + const onConfirm = async () => { + try { + const cancelRes = await orderService.cancelUnpaidOrder({ + order_no, + cancel_reason: "用户主动取消", + }); + if (cancelRes.code !== 0) { + throw new Error(cancelRes.message); + } + reloadPage(); + Taro.showToast({ + title: "取消成功", + icon: "none", + }); + } catch (e) { + Taro.showToast({ + title: e.message, + icon: "error", + }); + } finally { + Dialog.close("detailCancelOrder"); + } + }; + Dialog.open("detailCancelOrder", { + title: "确定取消订单吗?", + content: "取消订单后,您将无法恢复订单。请确认是否继续取消?", + footer: ( + + + + + ), + onConfirm, + onCancel, + }); + } + + function handleQuit(item) { + if (refundRef.current) { + refundRef.current.show(item, (result) => { + if (result) { + reloadPage(); + } + }); + } + } + return ( - {Boolean(order_status) && order_status !== OrderStatus.PENDING && ( + {["progress", "expired"].includes(orderStatus) && ( <> - 已支付 ¥ 90 + + {refundTextMap.get(refund_status)} ¥ 90 + - 球局暂未开始 - 球局开始前2小时,我们将通过短信通知你 + {gameNotice.title} + {gameNotice.content && {gameNotice.content}} )} @@ -122,13 +286,36 @@ function GameInfo(props) { {/* Action bar */} - + + {orderDetail.order_id + ? generateOrderActions( + orderDetail, + { + handleDeleteOrder, + handleCancelOrder, + handleQuit, + handlePayNow: () => {}, + handleViewGame, + }, + "detail" + )?.map((obj) => ( + + )) + : ""} + +

+
); } function OrderMsg(props) { - const { detail, checkOrderInfo } = props; + const { detail, orderDetail, checkOrderInfo } = props; const { start_time, end_time, @@ -137,7 +324,8 @@ function OrderMsg(props) { wechat_contact, price, } = detail; - const { order_info: { registrant_nickname, registrant_phone } = {} } = checkOrderInfo; + const { order_no } = orderDetail; + const { order_info: { registrant_phone } = {} } = checkOrderInfo; const startTime = dayjs(start_time); const endTime = dayjs(end_time); const startYear = startTime.format("YYYY"); @@ -159,18 +347,30 @@ function OrderMsg(props) {
), }, - { - title: "报名人昵称", - content: registrant_nickname, - }, { title: "报名人电话", content: registrant_phone, }, + { + title: "组织人微信号", + content: wechat_contact, + }, + { + title: "组织人电话", + content: wechat_contact, + }, { title: "费用", content: `${price} 元 / 人`, }, + ...(order_no + ? [ + { + title: "订单号", + content: order_no, + }, + ] + : []), ]; return ( @@ -199,8 +399,12 @@ function RefundPolicy(props) { rule: "退款规则", }, ...refund_policy.map((item, index) => { - const isLast = index === refund_policy.length - 1 - const theTimeObj = dayjs(isLast ? refund_policy.at(-2).deadline_formatted : item.deadline_formatted); + const isLast = index === refund_policy.length - 1; + const theTimeObj = dayjs( + isLast + ? refund_policy.at(-2).deadline_formatted + : item.deadline_formatted + ); const year = theTimeObj.format("YYYY"); const month = theTimeObj.format("M"); const day = theTimeObj.format("D"); @@ -332,6 +536,9 @@ const OrderCheck = () => { ); } + + const { order_status, cancel_type } = orderDetail; + return ( {/* Game Date and Address */} @@ -341,14 +548,20 @@ const OrderCheck = () => { currentLocation={location} /> {/* Order message */} - + {/* Refund policy */} {/* Disclaimer */} - {(!id || orderDetail.order_status === OrderStatus.PENDING) && ( + {(!id || + (order_status === OrderStatus.PENDING && + cancel_type === CancelType.NONE)) && ( )} diff --git a/src/order_pages/orderList/index.module.scss b/src/order_pages/orderList/index.module.scss index 3426f13..b7474e2 100644 --- a/src/order_pages/orderList/index.module.scss +++ b/src/order_pages/orderList/index.module.scss @@ -311,74 +311,6 @@ } } -.refundPolicy { - .moduleTitle { - display: flex; - padding: 15px 0 8px; - justify-content: space-between; - align-items: center; - align-self: stretch; - color: #000; - font-feature-settings: - "liga" off, - "clig" off; - font-family: "PingFang SC"; - font-size: 14px; - font-style: normal; - font-weight: 600; - line-height: 20px; - letter-spacing: -0.23px; - } - - .policyList { - border-radius: 12px; - border: 1px solid rgba(0, 0, 0, 0.06); - background: #fff; - box-shadow: 0 4px 36px 0 rgba(0, 0, 0, 0.06); - - .policyItem { - display: flex; - justify-content: space-around; - align-items: center; - color: #000; - text-align: center; - font-feature-settings: - "liga" off, - "clig" off; - font-family: "PingFang SC"; - font-size: 12px; - font-style: normal; - font-weight: 400; - line-height: 20px; - border-top: 1px solid rgba(0, 0, 0, 0.06); - - &:nth-child(1) { - color: #000; - text-align: center; - font-feature-settings: - "liga" off, - "clig" off; - font-family: "PingFang SC"; - font-size: 14px; - font-style: normal; - font-weight: 600; - line-height: 20px; - border: none; - } - - .time, - .rule { - width: 50%; - padding: 10px 12px; - } - - .rule { - border-left: 1px solid rgba(0, 0, 0, 0.06); - } - } - } -} - .dialogFooter { // width: 100%; width: calc(100% + 1px); diff --git a/src/order_pages/orderList/index.tsx b/src/order_pages/orderList/index.tsx index 8f9adcd..fbc749a 100644 --- a/src/order_pages/orderList/index.tsx +++ b/src/order_pages/orderList/index.tsx @@ -1,14 +1,14 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useRef } from "react"; import { View, Text, Button, Image, ScrollView } from "@tarojs/components"; import Taro, { useDidShow } from "@tarojs/taro"; import { Avatar, Dialog } from "@nutui/nutui-react-taro"; import dayjs from "dayjs"; import classnames from "classnames"; import orderService, { OrderStatus, CancelType } from "@/services/orderService"; -import { withAuth } from "@/components"; -import { payOrder } from "@/utils"; +import { withAuth, RefundPopup } from "@/components"; +import { payOrder, generateOrderActions } from "@/utils"; import styles from "./index.module.scss"; -// import orderListArrowRight from "@/static/order/orderListArrowRight.svg"; + dayjs.locale("zh-cn"); const PAGESIZE = 20; @@ -45,11 +45,12 @@ function generateTimeMsg(game_info) { return ( <> - {diffDay <= 2 + {diffDay <= 2 && diffDay >= 0 ? diffDayMap.get(diffDay) : startTime.format("YYYY-MM-DD")} ({DayOfWeekMap.get(dayofWeek)}) + {startTime.format('ah')}点 {gameLength} ); @@ -58,6 +59,7 @@ function generateTimeMsg(game_info) { const OrderList = () => { const [list, setList] = useState([]); const [total, setTotal] = useState(0); + const refundRef = useRef(null) const end = list.length * PAGESIZE >= total; @@ -118,48 +120,6 @@ const OrderList = () => { }); } - function renderCancelContent(checkOrderInfo) { - const { refund_policy = [] } = checkOrderInfo; - const policyList = [ - { - time: "申请退款时间", - rule: "退款规则", - }, - ...refund_policy.map((item, index) => { - const isLast = index === refund_policy.length - 1; - const theTimeObj = dayjs( - isLast - ? refund_policy.at(-2).deadline_formatted - : item.deadline_formatted - ); - const year = theTimeObj.format("YYYY"); - const month = theTimeObj.format("M"); - const day = theTimeObj.format("D"); - const time = theTimeObj.format("HH:MM"); - return { - time: `${year}年${month}月${day}日${time} ${isLast ? "后" : "前"}`, - rule: item.refund_rule, - }; - }), - ]; - return ( - - - 退款政策 - - {/* 订单信息摘要 */} - - {policyList.map((item, index) => ( - - {item.time} - {item.rule} - - ))} - - - ); - } - async function handleDeleteOrder(item) { const { id: order_id } = item // TODO:删除订单,刷新这一页,然后后面的全清除掉 @@ -211,90 +171,65 @@ const OrderList = () => { } async function handleCancelOrder(item) { - const { order_no, order_status, game_info, amount } = item; - if (order_status === OrderStatus.PENDING) { - const onCancel = () => { - Dialog.close("cancelOrder"); - }; - const onConfirm = async () => { - try { - const cancelRes = await orderService.cancelUnpaidOrder({ - order_no, - cancel_reason: "用户主动取消", - }); - if (cancelRes.code !== 0) { - throw new Error(cancelRes.message); - } - getOrders(item.page, false); - Taro.showToast({ - title: "取消成功", - icon: "none", - }) - } catch (e) { - Taro.showToast({ - title: e.message, - icon: "error", - }); - } finally { - Dialog.close("cancelOrder"); + const { order_no } = item; + const onCancel = () => { + Dialog.close("cancelOrder"); + }; + const onConfirm = async () => { + try { + const cancelRes = await orderService.cancelUnpaidOrder({ + order_no, + cancel_reason: "用户主动取消", + }); + if (cancelRes.code !== 0) { + throw new Error(cancelRes.message); } - }; - Dialog.open("cancelOrder", { - title: "确定取消订单吗?", - content: "取消订单后,您将无法恢复订单。请确认是否继续取消?", - footer: ( - - - - - ), - onConfirm, - onCancel, - }); - return; - } - const res = await orderService.getCheckOrderInfo(game_info.id); + getOrders(item.page, false); + Taro.showToast({ + title: "取消成功", + icon: "none", + }) + } catch (e) { + Taro.showToast({ + title: e.message, + icon: "error", + }); + } finally { + Dialog.close("cancelOrder"); + } + }; Dialog.open("cancelOrder", { title: "确定取消订单吗?", - content: renderCancelContent(res.data), - onConfirm: async () => { - try { - const refundRes = await orderService.applicateRefund({ - order_no, - refund_amount: amount, - refund_reason: "用户主动退款", - }); - if (refundRes.code !== 0) { - throw new Error(refundRes.message); - } - getOrders(item.page, false); - Taro.showToast({ - title: "退出成功", - icon: "none", - }) - } catch (e) { - Taro.showToast({ - title: e.message, - icon: "error", - }); - } finally { - Dialog.close("cancelOrder"); - } - }, - onCancel: () => { - Dialog.close("cancelOrder"); - }, + content: "取消订单后,您将无法恢复订单。请确认是否继续取消?", + footer: ( + + + + + ), + onConfirm, + onCancel, }); } + function handleQuit (item) { + if (refundRef.current) { + refundRef.current.show(item, (result) => { + if (result) { + getOrders(item.page) + } + }) + } + } + function handleViewOrderDetail(orderId) { Taro.navigateTo({ url: `/order_pages/orderDetail/index?id=${orderId}`, @@ -314,19 +249,7 @@ const OrderList = () => { > {/* */} {list.flat().map((item) => { - const unPay = item.order_status === OrderStatus.PENDING; - const expired = - item.order_status === OrderStatus.FINISHED || - [CancelType.TIMEOUT, CancelType.USER].includes(item.cancel_type); - // const expiredTime = dayjs(item.expire_time).isSame(dayjs(), "day") - // ? dayjs(item.expire_time).format("HH:mm:ss") - // : dayjs(item.expire_time).format("YYYY-MM-DD HH:mm:ss"); - const showCancel = - item.order_status === OrderStatus.PENDING && - item.cancel_type === CancelType.NONE; - const showQuit = - item.order_status === OrderStatus.PAID && - item.cancel_type === CancelType.NONE; + const unPay = item.order_status === OrderStatus.PENDING && item.cancel_type === CancelType.NONE; const { game_info: { @@ -352,10 +275,10 @@ const OrderList = () => { - {unPay && !expired ? "待支付" : "已支付"} ¥{" "} + {unPay ? "待支付" : "已支付"} ¥{" "} {item.amount} @@ -372,18 +295,48 @@ const OrderList = () => { {participants.length >= 0 ? ( - {/* participants */[{ user: { avatar_url: 'https://img.yzcdn.cn/vant/cat.jpeg', id: 1 } }, { user: { avatar_url: 'https://img.yzcdn.cn/vant/cat.jpeg', id: 2 } }, { user: { avatar_url: 'https://img.yzcdn.cn/vant/cat.jpeg', id: 3 } }].map((participant) => { - const { - user: { avatar_url, id }, - } = participant; - return ; - })} + { + /* participants */ [ + { + user: { + avatar_url: "https://img.yzcdn.cn/vant/cat.jpeg", + id: 1, + }, + }, + { + user: { + avatar_url: "https://img.yzcdn.cn/vant/cat.jpeg", + id: 2, + }, + }, + { + user: { + avatar_url: "https://img.yzcdn.cn/vant/cat.jpeg", + id: 3, + }, + }, + ].map((participant) => { + const { + user: { avatar_url, id }, + } = participant; + return ( + + ); + }) + } ) : ( "" )} - 报名人数 {current_players} + + 报名人数 {current_players} + / {max_players} @@ -400,45 +353,27 @@ const OrderList = () => { - {expired && ( + {generateOrderActions( + item, + { + handleDeleteOrder, + handleCancelOrder, + handleQuit, + handlePayNow, + handleViewGame, + }, + "list" + )?.map((obj) => ( - )} - {showCancel && ( - - )} - {showQuit && ( - - )} - {unPay && !expired ? ( - - ) : ( - - )} + ))} @@ -447,6 +382,7 @@ const OrderList = () => { {end && 已经到底了~}
+
); }; diff --git a/src/services/orderService.ts b/src/services/orderService.ts index fbf6a05..787267e 100644 --- a/src/services/orderService.ts +++ b/src/services/orderService.ts @@ -22,6 +22,12 @@ export enum CancelType { TIMEOUT, // 超时 } +export enum RefundStatus { + NONE = 0, // 无退款 + PENDING, // 退款中 + SUCCESS, // 已退款 +} + export interface PayMentParams { order_id: number; order_no: string; diff --git a/src/static/order/orderListClose.svg b/src/static/order/orderListClose.svg new file mode 100644 index 0000000..5cde120 --- /dev/null +++ b/src/static/order/orderListClose.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/utils/index.ts b/src/utils/index.ts index af30b5d..08ea333 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -5,3 +5,5 @@ export * from "./processImage"; export * from "./timeUtils"; export * from "./tokenManager"; export * from "./order.pay"; +export * from './orderActions'; +export * from './routeUtil'; diff --git a/src/utils/orderActions.ts b/src/utils/orderActions.ts new file mode 100644 index 0000000..49f4e4b --- /dev/null +++ b/src/utils/orderActions.ts @@ -0,0 +1,68 @@ +import { OrderStatus, CancelType } from "@/services/orderService"; + +export function getOrderStatus(orderData) { + const { order_status, cancel_type } = orderData + const unPay = order_status === OrderStatus.PENDING && cancel_type === CancelType.NONE; + const expired = + order_status === OrderStatus.FINISHED || + [CancelType.TIMEOUT, CancelType.USER].includes(cancel_type); + + return unPay ? 'unpay' : expired ? 'expired' : 'progress' +} + +// scene: list、detail +export function generateOrderActions(orderData, actions, scene) { + + + const { handleDeleteOrder, handleCancelOrder, handleQuit, handlePayNow, handleViewGame } = actions + + const deleteOrder = { + text: '删除订单', + className: 'cancelOrder', + action: handleDeleteOrder.bind(null, orderData), + } + + const cancelOrder = { + text: '取消订单', + className: 'cancelOrder', + action: handleCancelOrder.bind(null, orderData), + } + + const quitGame = { + text: '退出活动', + className: 'cancelOrder', + action: handleQuit.bind(null, orderData), + } + + const payNow = { + text: '立即支付', + className: 'payNow', + action: handlePayNow.bind(null, orderData), + } + + const gameDetail = { + text: '球局详情', + className: 'gameDetail', + action: handleViewGame.bind(null, orderData.game_info.id), + } + + const key = getOrderStatus(orderData) + + if (scene === 'list') { + const actionMap = new Map([ + ['expired', [deleteOrder, gameDetail]], + ['progress', [quitGame, gameDetail]], + ['unpay', [cancelOrder, payNow]] + ]) + return actionMap.get(key) + } + + if (scene === 'detail') { + const actionMap = new Map([ + ['expired', [gameDetail, deleteOrder]], + ['progress', [gameDetail, quitGame]], + ['unpay', [cancelOrder]] + ]) + return actionMap.get(key) + } +} diff --git a/src/utils/routeUtil.ts b/src/utils/routeUtil.ts new file mode 100644 index 0000000..178655e --- /dev/null +++ b/src/utils/routeUtil.ts @@ -0,0 +1,23 @@ +import Taro from "@tarojs/taro"; + +export function getCurrentFullPath(): string { + const pages = Taro.getCurrentPages(); + const currentPage = pages.at(-1); + + if (currentPage) { + console.log(currentPage, "currentPage get"); + const route = currentPage.route; + const options = currentPage.options || {}; + + const query = Object.keys(options) + .map((key) => `${key}=${options[key]}`) + .join("&"); + + return query ? `/${route}?${query}` : `/${route}`; + } + return ""; +} + +export function reloadPage() { + Taro.reLaunch({ url: getCurrentFullPath() }) +} From 6516e1f0c90ecba780c62b3adf11523d377f8c4f Mon Sep 17 00:00:00 2001 From: Ultrame <1019265060@qq.com> Date: Tue, 16 Sep 2025 14:35:34 +0800 Subject: [PATCH 15/90] =?UTF-8?q?=E5=BE=80=E6=9C=9F=E7=90=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/UserInfo/index.tsx | 2 + src/user_pages/other/index.scss | 23 +++++++--- src/user_pages/other/index.tsx | 74 ++++++++++++++++++++++++++++--- 3 files changed, 89 insertions(+), 10 deletions(-) diff --git a/src/components/UserInfo/index.tsx b/src/components/UserInfo/index.tsx index b1fb3bd..7be961f 100644 --- a/src/components/UserInfo/index.tsx +++ b/src/components/UserInfo/index.tsx @@ -297,6 +297,8 @@ export interface GameRecord { level_range: string; game_type: string; image_list: string[]; + deadline_hours: number; + end_time: string; } // 球局卡片组件属性 diff --git a/src/user_pages/other/index.scss b/src/user_pages/other/index.scss index 8aa4539..1059e52 100644 --- a/src/user_pages/other/index.scss +++ b/src/user_pages/other/index.scss @@ -1,9 +1,11 @@ // 他人用户页面样式 .other_user_page { min-height: 100vh; - background: radial-gradient(circle at 50% 0%, - rgba(238, 255, 220, 1) 0%, - rgba(255, 255, 255, 1) 37%); + background: radial-gradient( + circle at 50% 0%, + rgba(238, 255, 220, 1) 0%, + rgba(255, 255, 255, 1) 37% + ); position: relative; // overflow: hidden; box-sizing: border-box; @@ -42,7 +44,7 @@ display: flex; justify-content: center; - &>.detail-navigator-back-icon { + & > .detail-navigator-back-icon { width: 32px; height: 32px; } @@ -542,4 +544,15 @@ } } } -} \ No newline at end of file +} + +.ended_game_text { + font-family: "PingFang SC"; + font-weight: 600; + font-size: 20px; + line-height: 1.4em; + letter-spacing: 1.9%; + color: rgba(0, 0, 0, 0.85); + transition: color 0.3s ease; + padding: 12px 15px; +} diff --git a/src/user_pages/other/index.tsx b/src/user_pages/other/index.tsx index 87fab8d..dc50e77 100644 --- a/src/user_pages/other/index.tsx +++ b/src/user_pages/other/index.tsx @@ -45,6 +45,8 @@ const OtherUserPage: React.FC = () => { // 模拟球局数据 const [game_records, setGameRecords] = useState([]); + // 往期球局 + const [ended_game_records, setEndedGameRecords] = useState([]); // 关注状态 const [is_following, setIsFollowing] = useState(false); @@ -70,8 +72,9 @@ const OtherUserPage: React.FC = () => { nickname: userData.nickname || "", avatar: userData.avatar_url || "", join_date: userData.subscribe_time - ? `${new Date(userData.subscribe_time).getFullYear()}年${new Date(userData.subscribe_time).getMonth() + 1 - }月加入` + ? `${new Date(userData.subscribe_time).getFullYear()}年${ + new Date(userData.subscribe_time).getMonth() + 1 + }月加入` : "", stats: { following: userData.stats?.following_count || 0, @@ -102,6 +105,25 @@ const OtherUserPage: React.FC = () => { load_user_data(); }, [user_id]); + const classifyGameRecords = ( + game_records: GameRecord[] + ): { notEndGames: GameRecord[]; finishedGames: GameRecord[] } => { + const timestamp = new Date().getTime(); + return game_records.reduce( + (result, cur) => { + const { end_time } = cur; + timestamp > new Date(end_time).getTime() + ? result.notEndGames.push(cur) + : result.finishedGames.push(cur); + return result; + }, + { + notEndGames: [] as GameRecord[], + finishedGames: [] as GameRecord[], + } + ); + }; + const load_game_data = async () => { try { set_loading(true); @@ -109,7 +131,9 @@ const OtherUserPage: React.FC = () => { user_id || "", active_tab ); - setGameRecords(games_data); + const { notEndGames, finishedGames } = classifyGameRecords(games_data); + setGameRecords(notEndGames); + setEndedGameRecords(finishedGames); } catch (error) { console.error("加载球局数据失败:", error); Taro.showToast({ @@ -166,7 +190,12 @@ const OtherUserPage: React.FC = () => { - { Taro.navigateBack() }}> + { + Taro.navigateBack(); + }} + > { loading={loading} error={null} reload={load_game_data} - loadMoreMatches={() => { }} + loadMoreMatches={() => {}} + /> + + + + {/* 球局卡片 */} + {/* + {game_records.map((game) => ( + + ))} + */} + + + {/* 往期球局 */} + 往期球局 + + + 5月29日 + / + 星期六 + + + {/* 球局列表 */} + + + {}} /> From 7dea93acde14ed1e1394fb794cef417fefff2afe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=9D=B0?= Date: Tue, 16 Sep 2025 17:30:30 +0800 Subject: [PATCH 16/90] =?UTF-8?q?feat:=20=E7=90=83=E5=B1=80=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E7=AE=A1=E7=90=86=20=E6=9C=AA=E5=AE=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../GameManagePopup/index.module.scss | 27 ++ src/components/GameManagePopup/index.tsx | 145 ++++++++ src/components/NTRPEvaluatePopup/index.tsx | 2 +- .../UploadCover/upload-source-popup.scss | 5 +- .../UploadCover/upload-source-popup.tsx | 1 + src/components/index.ts | 2 + src/game_pages/detail/index.scss | 42 ++- src/game_pages/detail/index.tsx | 339 +++++++++++------- src/order_pages/orderList/index.tsx | 102 +++--- src/services/detailService.ts | 25 +- 10 files changed, 480 insertions(+), 210 deletions(-) create mode 100644 src/components/GameManagePopup/index.module.scss create mode 100644 src/components/GameManagePopup/index.tsx diff --git a/src/components/GameManagePopup/index.module.scss b/src/components/GameManagePopup/index.module.scss new file mode 100644 index 0000000..24fe1dc --- /dev/null +++ b/src/components/GameManagePopup/index.module.scss @@ -0,0 +1,27 @@ +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: space-between; + padding-bottom: 40px; + + .button { + width: 100%; + padding: 20px 0; + display: flex; + justify-content: center; + align-items: center; + color: #000; + text-align: center; + font-feature-settings: 'liga' off, 'clig' off; + font-family: "PingFang SC"; + font-size: 16px; + font-style: normal; + font-weight: 600; + line-height: 20px; + + &:last-child { + border-top: 8px solid #f5f5f5; + } + } +} \ No newline at end of file diff --git a/src/components/GameManagePopup/index.tsx b/src/components/GameManagePopup/index.tsx new file mode 100644 index 0000000..12e6f34 --- /dev/null +++ b/src/components/GameManagePopup/index.tsx @@ -0,0 +1,145 @@ +import React, { + useState, + forwardRef, + useImperativeHandle, + useRef, +} from "react"; +import Taro from "@tarojs/taro"; +import { View } from "@tarojs/components"; +import CommonPopup from "../CommonPopup"; +import styles from "./index.module.scss"; +import detailService from "@/services/detailService"; +import { useUserInfo } from "@/store/userStore"; + +const CancelPopup = forwardRef((props, ref) => { + const [visible, setVisible] = useState(false); + const onFinish = useRef(null); + + useImperativeHandle(ref, () => ({ + show: (onAct) => { + onFinish.current = onAct; + setVisible(true); + }, + })); + + function onClose() { + setVisible(false); + } + return ( + <> + + + + + ); +}); + +export default forwardRef(function GameManagePopup(props, ref) { + const [visible, setVisible] = useState(false); + const [detail, setDetail] = useState({}); + const onStatusChange = useRef(null); + const cancelRef = useRef(null); + const userInfo = useUserInfo(); + + useImperativeHandle(ref, () => ({ + show: (gameDetail, onChange) => { + onStatusChange.current = onChange; + setDetail(gameDetail); + setVisible(true); + }, + })); + + function handleEditGame() { + Taro.navigateTo({ + url: `/publish_pages/publishBall/index?gameId=${detail.id}`, + }); + onClose() + } + + const handleRepubGame = handleEditGame; + + async function handleCancelGame() { + cancelRef.current.show(async (result) => { + if (result) { + try { + const res = await detailService.disbandGame({ + game_id: detail.id, + settle_reason: result, + }); + if (res.code === 0) { + Taro.showToast({ title: "活动取消成功" }); + onStatusChange.current?.(true); + } + } catch (e) { + Taro.showToast({ title: e.message, icon: "error" }); + } finally { + onClose(); + } + } + }); + } + + async function handleQuitGame() { + try { + const res = await detailService.organizerQuit({ + game_id: detail.id, + quit_reason: "组织者主动退出", + }); + if (res.code === 0) { + Taro.showToast({ title: "活动退出成功" }); + onStatusChange.current?.(true); + } + } catch (e) { + Taro.showToast({ title: e.message, icon: "error" }); + } finally { + onClose(); + } + } + + function onClose() { + setVisible(false); + } + + const hasJoin = (detail.participants || []).some(item => item.user.id === userInfo.id) + + return ( + <> + + + + 编辑活动 + + + 重新发布 + + + 取消活动 + + {!hasJoin && ( + + 退出活动 + + )} + + 取消 + + + + + + ); +}); diff --git a/src/components/NTRPEvaluatePopup/index.tsx b/src/components/NTRPEvaluatePopup/index.tsx index 724f678..2f9caa8 100644 --- a/src/components/NTRPEvaluatePopup/index.tsx +++ b/src/components/NTRPEvaluatePopup/index.tsx @@ -91,7 +91,7 @@ const NTRPEvaluatePopup = (props: NTRPEvaluatePopupProps, ref) => { - {showEntry && props.children} + {showEntry ? props.children : ''} ); }; diff --git a/src/components/UploadCover/upload-source-popup.scss b/src/components/UploadCover/upload-source-popup.scss index 999c934..bb8fbcd 100644 --- a/src/components/UploadCover/upload-source-popup.scss +++ b/src/components/UploadCover/upload-source-popup.scss @@ -31,7 +31,8 @@ } .upload-popup-scroll-view { - max-height: calc(100vh - 260px); + // max-height: calc(100vh - 260px); + height: 440px; overflow-y: auto; .upload-popup-image-list { @@ -124,7 +125,7 @@ display: flex; width: 100%; height: 62px; - padding: 8px 10px 10px 10px; + padding: 8px 10px 50px 10px; box-sizing: border-box; justify-content: center; align-items: flex-start; diff --git a/src/components/UploadCover/upload-source-popup.tsx b/src/components/UploadCover/upload-source-popup.tsx index ddd4004..8a832c0 100644 --- a/src/components/UploadCover/upload-source-popup.tsx +++ b/src/components/UploadCover/upload-source-popup.tsx @@ -121,6 +121,7 @@ export default forwardRef(function UploadImage(props: UploadImageProps, ref) { {images.length > 0 ? ( diff --git a/src/components/index.ts b/src/components/index.ts index abc1c4a..44bc01e 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -17,6 +17,7 @@ import withAuth from "./Auth"; import { CustomPicker, PopupPicker } from "./Picker"; import NTRPEvaluatePopup from "./NTRPEvaluatePopup"; import RefundPopup from "./refundPopup"; +import GameManagePopup from './GameManagePopup' export { ActivityTypeSwitch, @@ -39,4 +40,5 @@ export { PopupPicker, NTRPEvaluatePopup, RefundPopup, + GameManagePopup, }; diff --git a/src/game_pages/detail/index.scss b/src/game_pages/detail/index.scss index da114ba..d851af7 100644 --- a/src/game_pages/detail/index.scss +++ b/src/game_pages/detail/index.scss @@ -807,7 +807,7 @@ } &-organizer-recommend-games { - padding: 24px 15px 0; + padding: 24px 15px 10px; .organizer-title { overflow: hidden; @@ -1122,27 +1122,47 @@ } } - &-join-game { + .detail-main-action { display: flex; align-items: center; height: 52px; width: auto; - padding: 2px 6px; + // padding: 2px 6px; box-sizing: border-box; justify-content: center; gap: 12px; flex: 1 0 0; border-radius: 16px; - border: 1px solid rgba(0, 0, 0, 0.06); + // border: 1px solid rgba(0, 0, 0, 0.06); background: #fff; + overflow: hidden; - &-price { - font-family: "PoetsenOne"; - font-size: 28px; - font-weight: 400; - line-height: 24px; /* 114.286% */ - letter-spacing: -0.56px; - color: #000; + .sticky-bottom-bar-join-game { + margin-left: auto; + width: 151px; + display: flex; + align-items: center; + justify-content: center; + + &-price { + font-family: "PoetsenOne"; + font-size: 28px; + font-weight: 400; + line-height: 24px; /* 114.286% */ + letter-spacing: -0.56px; + color: #000; + } + } + + .game_manage { + width: 100px; + margin-left: auto; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + background: #000; + color: #fff; } } } diff --git a/src/game_pages/detail/index.tsx b/src/game_pages/detail/index.tsx index 29bc5bf..af9597c 100644 --- a/src/game_pages/detail/index.tsx +++ b/src/game_pages/detail/index.tsx @@ -16,7 +16,12 @@ import Taro, { import dayjs from "dayjs"; import "dayjs/locale/zh-cn"; // 导入API服务 -import { CommonPopup, withAuth, NTRPEvaluatePopup } from "@/components"; +import { + CommonPopup, + withAuth, + NTRPEvaluatePopup, + GameManagePopup, +} from "@/components"; import { EvaluateType, SceneType, @@ -210,7 +215,7 @@ const SharePopup = forwardRef( ); - }, + } ); function navto(url) { @@ -219,20 +224,18 @@ function navto(url) { }); } +function toast(message) { + Taro.showToast({ title: message, icon: "none" }); +} + // 底部操作栏 function StickyButton(props) { - const { handleShare, handleJoinGame, detail } = props; + const { handleShare, handleJoinGame, detail, onStatusChange } = props; const ntrpRef = useRef(null); - // const userInfo = useUserInfo(); - // const { id } = userInfo; - const { - id, - publisher_id, - match_status, - price, - user_action_status, - end_time, - } = detail || {}; + const { id, price, user_action_status, end_time, is_organizer } = + detail || {}; + + const gameManageRef = useRef(); function handleSelfEvaluate() { // TODO: 打开自评弹窗 @@ -240,13 +243,14 @@ function StickyButton(props) { } function generateTextAndAction( - user_action_status: null | { [key: string]: boolean }, + user_action_status: null | { [key: string]: boolean } ): undefined | { text: string | React.FC; action: () => void } { if (!user_action_status) { return; } + const displayPrice = is_organizer ? 0 : price; // user_action_status.can_assess = true; - user_action_status.can_join = true; + // user_action_status.can_join = true; const { can_assess, can_join, @@ -260,27 +264,27 @@ function StickyButton(props) { dayjs(end_time).isBefore(dayjs()) ) { return { - text: "球局已结束,查看其他球局", - action: navto.bind(null, "/game_pages/list/index"), + text: "活动已结束", + action: () => toast("活动已结束"), }; } if (waiting_start) { return { - text: "等待开始, 查看更多球局", - action: navto.bind(null, "/game_pages/list/index"), + text: () => ¥{displayPrice} 已加入, + action: () => toast("已加入"), }; } else if (is_substituting) { return { - text: "候补中,查看其他球局", - action: navto.bind(null, "/game_pages/list/index"), + text: () => ¥{displayPrice} 已加入候补, + action: () => toast("已加入候补"), }; } else if (can_pay) { return { - text: "继续支付", + text: () => ¥{price} 继续支付, action: async () => { const res = await OrderService.getUnpaidOrder(id); if (res.code === 0) { - Taro.navigateTo({ + navto({ url: `/order_pages/orderDetail/index?id=${res.data.order_info.order_id}`, }); } @@ -288,21 +292,13 @@ function StickyButton(props) { }; } else if (can_substitute) { return { - text: "立即候补", + text: () => ¥{displayPrice} 我要候补, action: handleJoinGame, }; } else if (can_join) { return { text: () => { - return ( - <> - 🎾 - 立即加入 - - ¥ {price} - - - ); + return ¥{displayPrice} 立即加入; }, action: handleJoinGame, }; @@ -315,7 +311,7 @@ function StickyButton(props) { scene={SceneType.DETAIL} displayCondition={DisplayConditionType.AUTO} > - NTRP自评 + ¥{displayPrice} 立即加入 ), action: handleSelfEvaluate, @@ -341,36 +337,49 @@ function StickyButton(props) { }; } - // const role = Number(publisher_id) === id ? "ownner" : "visitor"; - return ( - - - - - 分享 + <> + + + + + 分享 + + + { + Taro.showToast({ title: "To be continued", icon: "none" }); + }} + > + + 32 + - - { - Taro.showToast({ title: "To be continued", icon: "none" }); - }} - > - - 32 + + + + + {is_organizer && ( + { + gameManageRef.current.show(detail, onStatusChange); + }} + > + 管理 + + )} - - - - + + ); } @@ -612,10 +621,11 @@ function VenueInfo(props) { } function genNTRPRequirementText(min, max) { + console.log(min, max, "ntrp"); if (min && max && min !== max) { return `${min} - ${max} 之间`; - } else if (max === 1) { - return "没有要求"; + } else if (max === "1") { + return "无要求"; } else if (max) { return `${max} 以上`; } @@ -675,10 +685,9 @@ function Participants(props) { user_action_status; const showApplicationEntry = [can_pay, can_substitute, is_substituting, waiting_start].every( - (item) => !item, + (item) => !item ) && can_join; const leftCount = max_participants - participant_count; - const organizer_id = Number(detail.publisher_id); return ( @@ -694,7 +703,6 @@ function Participants(props) { className="participants-list-application" onClick={() => { handleJoinGame(); - // Taro.showToast({ title: "To be continued", icon: "none" }); }} > {participants.map((participant) => { const { + is_organizer, user: { avatar_url, nickname, @@ -723,15 +734,17 @@ function Participants(props) { id: participant_user_id, }, } = participant; - const role = - participant_user_id === organizer_id ? "组织者" : "参与者"; + const role = is_organizer ? "组织者" : "参与者"; return ( {nickname || "未知"} @@ -813,7 +826,12 @@ function genRecommendGames(games, location, avatar) { avatar, applications: max_players, checkedApplications: current_players, - levelRequirements: skill_level_max !== skill_level_min ? `${skill_level_min || '-'}至${skill_level_max || '-'}` : skill_level_min === 1 ? '无要求' : `${skill_level_min}以上`, + levelRequirements: + skill_level_max !== skill_level_min + ? `${skill_level_min || "-"}至${skill_level_max || "-"}` + : skill_level_min === "1" + ? "无要求" + : `${skill_level_min}以上`, playType: play_type, }; }); @@ -862,9 +880,9 @@ function OrganizerInfo(props) { }; function handleViewGame(gameId) { - Taro.navigateTo({ - url: `/game_pages/detail/index?id=${gameId}&from=current` - }) + navto({ + url: `/game_pages/detail/index?id=${gameId}&from=current`, + }); } return ( @@ -875,7 +893,12 @@ function OrganizerInfo(props) { {/* organizer avatar and name */} - + {nickname} @@ -914,66 +937,79 @@ function OrganizerInfo(props) { {/* recommend games by organizer */} - - - TA的更多活动 - - - - - {recommendGames.map((game, index) => ( - - {/* game title */} - - {game.title} - - - {/* game time and range */} - - {game.time} - {game.timeLength} - - {/* game location、vunue、distance */} - - {game.venue} - · - {game.venueType} - · - {game.distance} - - {/* organizer avatar、applications、level requirements、play type */} - - { e.stopPropagation(); handleViewUserInfo(id) }} - /> - - - - 报名人数 {game.checkedApplications}/{game.applications} - - - - {game.levelRequirements} - - - {game.playType} + {recommendGames.length > 0 && ( + + + TA的更多活动 + + + + + {recommendGames.map((game, index) => ( + + {/* game title */} + + {game.title} + + + {/* game time and range */} + + {game.time} + {game.timeLength} + + {/* game location、vunue、distance */} + + {game.venue} + · + {game.venueType} + · + {game.distance} + + {/* organizer avatar、applications、level requirements、play type */} + + { + e.stopPropagation(); + handleViewUserInfo(id); + }} + /> + + + + 报名人数 {game.checkedApplications}/ + {game.applications} + + + + {game.levelRequirements} + + + {game.playType} + - - ))} - - - + ))} + + + + )} ); } @@ -987,6 +1023,9 @@ function Index() { const { id, from } = params; const [userInfo, setUserInfo] = useState({}); // 组织者的userInfo const { fetchUserInfo } = useUserActions(); // 获取登录用户的userInfo + const myInfo = useUserInfo(); + + const isMyOwn = userInfo.id === myInfo.id; const sharePopupRef = useRef(null); @@ -1031,7 +1070,6 @@ function Index() { async function fetchUserInfoById(user_id) { const userDetailInfo = await LoginService.getUserInfoById(user_id); if (userDetailInfo.code === 0) { - // console.log(userDetailInfo.data); setUserInfo(userDetailInfo.data); } } @@ -1040,12 +1078,26 @@ function Index() { sharePopupRef.current.show(); } - const handleJoinGame = () => { - Taro.navigateTo({ + const handleJoinGame = async () => { + if (isMyOwn) { + const res = await DetailService.organizerJoin(Number(id)); + if (res.code === 0) { + toast("加入成功"); + fetchDetail(); + } + return; + } + navto({ url: `/order_pages/orderDetail/index?gameId=${id}`, }); }; + function onStatusChange(result) { + if (result) { + fetchDetail(); + } + } + function handleBack() { const pages = Taro.getCurrentPages(); if (pages.length <= 1) { @@ -1058,9 +1110,9 @@ function Index() { } function handleViewUserInfo(userId) { - Taro.navigateTo({ - url: `/user_pages/other/index?userid=${userId}` - }) + navto({ + url: `/user_pages/other/index?userid=${userId}`, + }); } console.log("detail", detail); @@ -1093,7 +1145,11 @@ function Index() { {/* content */} {/* avatar and tags */} - + {/* title */} {detail.title} @@ -1105,7 +1161,11 @@ function Index() { {/* gameplay requirements */} {/* participants */} - + {/* supplemental notes */} {/* organizer and recommend games by organizer */} @@ -1121,6 +1181,7 @@ function Index() { handleShare={handleShare} handleJoinGame={handleJoinGame} detail={detail} + onStatusChange={onStatusChange} /> {/* share popup */} ({DayOfWeekMap.get(dayofWeek)}) - {startTime.format('ah')}点 + {startTime.format("ah")}点 {gameLength} ); @@ -59,7 +59,7 @@ function generateTimeMsg(game_info) { const OrderList = () => { const [list, setList] = useState([]); const [total, setTotal] = useState(0); - const refundRef = useRef(null) + const refundRef = useRef(null); const end = list.length * PAGESIZE >= total; @@ -78,7 +78,7 @@ const OrderList = () => { setTotal(res.data.count); setList((prev) => { const newList = [...prev]; - const index = page - 1 + const index = page - 1; newList.splice( index, clear ? newList.length - index : 1, @@ -121,53 +121,49 @@ const OrderList = () => { } async function handleDeleteOrder(item) { - const { id: order_id } = item + const { id: order_id } = item; // TODO:删除订单,刷新这一页,然后后面的全清除掉 const onCancel = () => { - Dialog.close("cancelOrder"); - }; - const onConfirm = async () => { - try { - const deleteRes = await orderService.deleteOrder({ - order_id, - }); - if (deleteRes.code !== 0) { - throw new Error(deleteRes.message); - } - getOrders(item.page); - Taro.showToast({ - title: "删除成功", - icon: "none", - }) - } catch (e) { - Taro.showToast({ - title: e.message, - icon: "error", - }); - } finally { - Dialog.close("cancelOrder"); + Dialog.close("cancelOrder"); + }; + const onConfirm = async () => { + try { + const deleteRes = await orderService.deleteOrder({ + order_id, + }); + if (deleteRes.code !== 0) { + throw new Error(deleteRes.message); } - }; + getOrders(item.page); + Taro.showToast({ + title: "删除成功", + icon: "none", + }); + } catch (e) { + Taro.showToast({ + title: e.message, + icon: "error", + }); + } finally { + Dialog.close("cancelOrder"); + } + }; Dialog.open("cancelOrder", { title: "确定删除订单吗?", content: "删除订单后,您将无法恢复订单。请确认是否继续取消?", - footer: ( - - - - - ), - onConfirm, - onCancel, - }) + footer: ( + + + + + ), + onConfirm, + onCancel, + }); } async function handleCancelOrder(item) { @@ -188,7 +184,7 @@ const OrderList = () => { Taro.showToast({ title: "取消成功", icon: "none", - }) + }); } catch (e) { Taro.showToast({ title: e.message, @@ -206,11 +202,7 @@ const OrderList = () => { - @@ -220,13 +212,13 @@ const OrderList = () => { }); } - function handleQuit (item) { + function handleQuit(item) { if (refundRef.current) { refundRef.current.show(item, (result) => { if (result) { - getOrders(item.page) + getOrders(item.page); } - }) + }); } } @@ -249,7 +241,9 @@ const OrderList = () => { > {/* */} {list.flat().map((item) => { - const unPay = item.order_status === OrderStatus.PENDING && item.cancel_type === CancelType.NONE; + const unPay = + item.order_status === OrderStatus.PENDING && + item.cancel_type === CancelType.NONE; const { game_info: { diff --git a/src/services/detailService.ts b/src/services/detailService.ts index 8d03adf..2b8cf62 100644 --- a/src/services/detailService.ts +++ b/src/services/detailService.ts @@ -92,10 +92,8 @@ export interface UpdateLocationRes { city: string; district: string; } - -// 发布球局类 class GameDetailService { - // 用户登录 + // 查询球局详情 async getDetail(id: number): Promise> { return httpService.post( "/games/detail", @@ -114,6 +112,27 @@ class GameDetailService { showLoading: true, }); } + + // 组织者加入球局 + async organizerJoin(game_id: number): Promise> { + return httpService.post("/games/organizer_join", { game_id }, { + showLoading: true, + }); + } + + // 组织者退出球局 + async organizerQuit(req: { game_id: number, quit_reason: string }): Promise> { + return httpService.post("/games/organizer_quit", req, { + showLoading: true, + }); + } + + // 组织者解散球局 + async disbandGame(req: { game_id: number, settle_reason: string }): Promise> { + return httpService.post("/games/settle_game", req, { + showLoading: true, + }); + } } // 导出认证服务实例 From d6fb6ebc1b51cbdf73922c0990a7cf4ac2dd9049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=9D=B0?= Date: Tue, 16 Sep 2025 20:46:09 +0800 Subject: [PATCH 17/90] =?UTF-8?q?fix:=20=E9=80=80=E5=87=BA=E6=B4=BB?= =?UTF-8?q?=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/GameManagePopup/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/GameManagePopup/index.tsx b/src/components/GameManagePopup/index.tsx index 12e6f34..249987b 100644 --- a/src/components/GameManagePopup/index.tsx +++ b/src/components/GameManagePopup/index.tsx @@ -129,7 +129,7 @@ export default forwardRef(function GameManagePopup(props, ref) { 取消活动 - {!hasJoin && ( + {hasJoin && ( 退出活动 From 13bdc1a1f35dc550e93313506150b14a65cf8563 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=88=90?= Date: Tue, 16 Sep 2025 21:41:43 +0800 Subject: [PATCH 18/90] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20=E5=8F=91=E5=B8=83?= =?UTF-8?q?=E8=B7=B3=E8=BD=AC=E9=94=99=E8=AF=AF=20=EF=BC=8C=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E9=A1=B5=E7=BA=BF=E6=9D=A1=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/home_pages/index.tsx | 3 +-- src/publish_pages/publishBall/index.tsx | 4 ++-- src/user_pages/edit/index.scss | 16 +++++++++++++--- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/home_pages/index.tsx b/src/home_pages/index.tsx index 29b5fe7..dfdc15e 100644 --- a/src/home_pages/index.tsx +++ b/src/home_pages/index.tsx @@ -10,7 +10,6 @@ const HomePage: React.FC = () => { useEffect(() => { const handleLoginRedirect = async () => { const login_status = check_login_status(); - if (login_status) { try { // 先获取用户信息 @@ -32,7 +31,7 @@ const HomePage: React.FC = () => { return ( - + ); } diff --git a/src/publish_pages/publishBall/index.tsx b/src/publish_pages/publishBall/index.tsx index de070ed..8b0a5e6 100644 --- a/src/publish_pages/publishBall/index.tsx +++ b/src/publish_pages/publishBall/index.tsx @@ -278,7 +278,7 @@ const PublishBall: React.FC = () => { // 如果是畅打,则跳转第一个球局详情页,并自动分享 @刘杰 Taro.navigateTo({ // @ts-expect-error: id - url: `/pages/detail/index?id=${res.data.id || 1}&from=publish&autoShare=1` + url: `/game_pages/detail/index?id=${res.data.id || 1}&from=publish&autoShare=1` }) } else { Taro.showToast({ @@ -324,7 +324,7 @@ const PublishBall: React.FC = () => { // 如果是畅打,则跳转第一个球局详情页,并自动分享 @刘杰 Taro.navigateTo({ // @ts-expect-error: id - url: `/pages/detail/index?id=${res.data?.[0].id || 1}&from=publish&autoShare=1` + url: `/game_pages/detail/index?id=${res.data?.[0].id || 1}&from=publish&autoShare=1` }) } else { Taro.showToast({ diff --git a/src/user_pages/edit/index.scss b/src/user_pages/edit/index.scss index 99c75ce..08e6b2b 100644 --- a/src/user_pages/edit/index.scss +++ b/src/user_pages/edit/index.scss @@ -174,9 +174,7 @@ border-radius: 12px; overflow: hidden; - &:not(:first-child) { - border-top: 1px solid rgba(0, 0, 0, 0.06); - } + .form_item { display: flex; @@ -187,6 +185,11 @@ box-sizing: border-box; gap: 20px; + + + + + .item_left { display: flex; align-items: center; @@ -301,6 +304,13 @@ border-radius: 99px; } } + + .divider{ + height: 1px; + background-color: rgba(0, 0, 0, 0.06) ; + margin-left:35px; + box-sizing: border-box; + } } } From 5c6823e0b22c2245f8c905cd953857fd88c57aec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=88=90?= Date: Tue, 16 Sep 2025 21:54:23 +0800 Subject: [PATCH 19/90] =?UTF-8?q?=E8=83=8C=E6=99=AF=E9=A2=9C=E8=89=B2?= =?UTF-8?q?=E8=B0=83=E6=95=B4=E4=B8=80=E4=B8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/user_pages/myself/index.scss | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/user_pages/myself/index.scss b/src/user_pages/myself/index.scss index dd15bbf..b84a546 100644 --- a/src/user_pages/myself/index.scss +++ b/src/user_pages/myself/index.scss @@ -3,7 +3,17 @@ // 个人页面样式 .myself_page { min-height: 100vh; - background: radial-gradient(circle at 50% 0%, rgba(238, 255, 220, 1) 0%, rgba(255, 255, 255, 1) 37%); + background: radial-gradient(circle at 50% 0, + /* 光晕圆心在顶部中间 */ + rgba(173, 216, 230, 0.9) 0px, + /* 中间更深的浅蓝 */ + rgba(173, 216, 230, 0.5) 200px, + /* 100px 处开始淡化 */ + rgba(255, 255, 255, 1) 300px, + /* 到 200px 变成白色 */ + #ffffff 100% + /* 200px 以下全白 */ + ); position: relative; overflow: hidden; box-sizing: border-box; @@ -17,7 +27,7 @@ margin-top: 0; box-sizing: border-box; overflow-y: auto; - padding: 0px 15px 15px 15px ; + padding: 0px 15px 15px 15px; // 用户信息区域 .user_info_section { @@ -355,5 +365,4 @@ } } } -} - +} \ No newline at end of file From 44f61ce71b3d47394d31128833d3a4c54bd84385 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=9D=B0?= Date: Wed, 17 Sep 2025 11:28:01 +0800 Subject: [PATCH 20/90] =?UTF-8?q?fix:=20=E8=B7=AF=E7=94=B1=E8=B7=B3?= =?UTF-8?q?=E8=BD=AC=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/GameManagePopup/index.tsx | 2 +- src/game_pages/detail/index.tsx | 16 ++++------------ 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/components/GameManagePopup/index.tsx b/src/components/GameManagePopup/index.tsx index 249987b..446b95f 100644 --- a/src/components/GameManagePopup/index.tsx +++ b/src/components/GameManagePopup/index.tsx @@ -31,7 +31,7 @@ const CancelPopup = forwardRef((props, ref) => { visible={visible} showHeader={false} hideFooter - zIndex={1001} + zIndex={1002} enableDragToClose={false} onClose={onClose} > diff --git a/src/game_pages/detail/index.tsx b/src/game_pages/detail/index.tsx index af9597c..4ca90a9 100644 --- a/src/game_pages/detail/index.tsx +++ b/src/game_pages/detail/index.tsx @@ -284,9 +284,7 @@ function StickyButton(props) { action: async () => { const res = await OrderService.getUnpaidOrder(id); if (res.code === 0) { - navto({ - url: `/order_pages/orderDetail/index?id=${res.data.order_info.order_id}`, - }); + navto(`/order_pages/orderDetail/index?id=${res.data.order_info.order_id}`); } }, }; @@ -880,9 +878,7 @@ function OrganizerInfo(props) { }; function handleViewGame(gameId) { - navto({ - url: `/game_pages/detail/index?id=${gameId}&from=current`, - }); + navto(`/game_pages/detail/index?id=${gameId}&from=current`); } return ( @@ -1087,9 +1083,7 @@ function Index() { } return; } - navto({ - url: `/order_pages/orderDetail/index?gameId=${id}`, - }); + navto(`/order_pages/orderDetail/index?gameId=${id}`); }; function onStatusChange(result) { @@ -1110,9 +1104,7 @@ function Index() { } function handleViewUserInfo(userId) { - navto({ - url: `/user_pages/other/index?userid=${userId}`, - }); + navto(`/user_pages/other/index?userid=${userId}`); } console.log("detail", detail); From 9f4e0266872a435e37c94465d8a3196839328b64 Mon Sep 17 00:00:00 2001 From: Ultrame <1019265060@qq.com> Date: Wed, 17 Sep 2025 17:30:22 +0800 Subject: [PATCH 21/90] =?UTF-8?q?=E7=90=83=E5=B1=80=E6=97=A5=E6=9C=9F?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/userService.ts | 21 ++++++++++++++++----- src/user_pages/other/index.tsx | 29 +++++++++++++++++------------ 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/src/services/userService.ts b/src/services/userService.ts index 8402387..b517b1e 100644 --- a/src/services/userService.ts +++ b/src/services/userService.ts @@ -124,7 +124,7 @@ export class UserService { private static transform_game_data(backend_data: BackendGameData[]): any[] { return backend_data.map(game => { // 处理时间格式 - const start_time = new Date(game.start_time); + const start_time = new Date(game.start_time.replace(/\s/, 'T')); const date_time = this.format_date_time(start_time); // 处理图片数组 - 兼容两种数据格式 @@ -161,6 +161,7 @@ export class UserService { id: game.id, title: game.title || '未命名球局', start_time: date_time, + end_time: game.end_time || '', location: location, distance_km: parseFloat(distance.replace('km', '')) || 0, current_players: registered_count, @@ -177,14 +178,23 @@ export class UserService { }); } + private static is_date_in_this_week(date: Date): boolean { + const today = new Date(); + const firstDayOfWeek = new Date(today.setDate(today.getDate() - today.getDay())); + const lastDayOfWeek = new Date(firstDayOfWeek.setDate(firstDayOfWeek.getDate() + 6)); + + return date >= firstDayOfWeek && date <= lastDayOfWeek; + } + // 格式化时间显示 private static format_date_time(start_time: Date): string { const now = new Date(); - const today = new Date(now.getFullYear(), now.getMonth(), now.getDate()); + const today = new Date(now.getFullYear(), now.getMonth() + 1, now.getDate()); const tomorrow = new Date(today.getTime() + 24 * 60 * 60 * 1000); const day_after_tomorrow = new Date(today.getTime() + 2 * 24 * 60 * 60 * 1000); + const weekdays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']; - const start_date = new Date(start_time.getFullYear(), start_time.getMonth(), start_time.getDate()); + const start_date = new Date(start_time.getFullYear(), start_time.getMonth() + 1, start_time.getDate()); let date_str = ''; if (start_date.getTime() === today.getTime()) { @@ -193,9 +203,10 @@ export class UserService { date_str = '明天'; } else if (start_date.getTime() === day_after_tomorrow.getTime()) { date_str = '后天'; - } else { - const weekdays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']; + } else if(this.is_date_in_this_week(start_time)) { date_str = weekdays[start_time.getDay()]; + } else { + date_str = `${start_time.getFullYear()}-${(start_time.getMonth() + 1).toString().padStart(2, '0')}-${start_time.getDate().toString().padStart(2, '0')}`; } const time_str = `${start_time.getHours().toString().padStart(2, '0')}:${start_time.getMinutes().toString().padStart(2, '0')}`; diff --git a/src/user_pages/other/index.tsx b/src/user_pages/other/index.tsx index dc50e77..98813f2 100644 --- a/src/user_pages/other/index.tsx +++ b/src/user_pages/other/index.tsx @@ -108,11 +108,12 @@ const OtherUserPage: React.FC = () => { const classifyGameRecords = ( game_records: GameRecord[] ): { notEndGames: GameRecord[]; finishedGames: GameRecord[] } => { - const timestamp = new Date().getTime(); + const now = new Date().getTime(); return game_records.reduce( (result, cur) => { - const { end_time } = cur; - timestamp > new Date(end_time).getTime() + let { end_time } = cur; + end_time = end_time.replace(/\s/, "T"); + new Date(end_time).getTime() > now ? result.notEndGames.push(cur) : result.finishedGames.push(cur); return result; @@ -131,7 +132,11 @@ const OtherUserPage: React.FC = () => { user_id || "", active_tab ); - const { notEndGames, finishedGames } = classifyGameRecords(games_data); + const sorted_games = games_data.sort((a, b) => { + return new Date(b.start_time.replace(/\s/, 'T')).getTime() - new Date(a.start_time.replace(/\s/, 'T')).getTime(); + }); + console.log('xxxxxxxxxxxxxxxxxx', sorted_games) + const { notEndGames, finishedGames } = classifyGameRecords(sorted_games); setGameRecords(notEndGames); setEndedGameRecords(finishedGames); } catch (error) { @@ -225,14 +230,14 @@ const OtherUserPage: React.FC = () => { {/* 球局列表 */} - + {/* 5月29日 / 星期六 - + */} {/* 球局列表 */} - + {/* */} { loadMoreMatches={() => {}} /> - + {/* */} {/* 球局卡片 */} {/* @@ -260,14 +265,14 @@ const OtherUserPage: React.FC = () => { {/* 往期球局 */} 往期球局 - + {/* 5月29日 / 星期六 - + */} {/* 球局列表 */} - + {/* */} { loadMoreMatches={() => {}} /> - + {/* */} {/* 球局卡片 */} {/* From 9c56fd3f0e22e49e87dcb166c51dde3ab7c945d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=9D=B0?= Date: Wed, 17 Sep 2025 18:34:36 +0800 Subject: [PATCH 22/90] =?UTF-8?q?feat:=20=E7=90=83=E5=B1=80=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E6=93=8D=E4=BD=9Cdone?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../GameManagePopup/index.module.scss | 85 +++++++++++++++++ src/components/GameManagePopup/index.tsx | 94 ++++++++++++++++--- src/game_pages/detail/index.scss | 12 ++- src/game_pages/detail/index.tsx | 58 +++++++++--- src/order_pages/orderDetail/index.module.scss | 23 +++++ src/order_pages/orderDetail/index.tsx | 38 +++++++- src/order_pages/orderList/index.module.scss | 12 +++ src/order_pages/orderList/index.tsx | 13 ++- src/services/detailService.ts | 7 ++ src/utils/orderActions.ts | 5 +- 10 files changed, 315 insertions(+), 32 deletions(-) diff --git a/src/components/GameManagePopup/index.module.scss b/src/components/GameManagePopup/index.module.scss index 24fe1dc..eefab2e 100644 --- a/src/components/GameManagePopup/index.module.scss +++ b/src/components/GameManagePopup/index.module.scss @@ -24,4 +24,89 @@ border-top: 8px solid #f5f5f5; } } +} + +.centerContainer { + overflow: hidden; + .title { + padding-top: 24px; + color: #000; + text-align: center; + font-feature-settings: 'liga' off, 'clig' off; + font-family: "PingFang SC"; + font-size: 16px; + font-style: normal; + font-weight: 600; + line-height: 20px; + } + + .content { + padding: 24px 20px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + + .tips { + color: rgba(60, 60, 67, 0.60); + font-feature-settings: 'liga' off, 'clig' off; + font-family: "PingFang SC"; + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 24px; + } + + .cancelReason { + width: 100%; + margin-top: 8px; + padding: 8px; + border-radius: 4px; + background: #F0F0F0; + + .input { + width: 100%; + &:placeholder-shown { + color: rgba(60, 60, 67, 0.30); + font-feature-settings: 'liga' off, 'clig' off; + font-family: "PingFang SC"; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 24px; + } + } + } + } + + .actions { + display: flex; + justify-content: space-between; + align-items: center; + height: 44px; + border-top: 0.5px solid #CECECE; + background: #FFF; + margin-top: 2px; + + .confirm, .cancel { + width: 50%; + height: 44px; + display: flex; + justify-content: center; + align-items: center; + color: #000; + text-align: center; + font-feature-settings: 'liga' off, 'clig' off; + font-family: "PingFang SC"; + font-size: 16px; + font-style: normal; + font-weight: 600; + line-height: 20px; + + &.cancel { + background-color: #000; + color: #fff; + } + } + } } \ No newline at end of file diff --git a/src/components/GameManagePopup/index.tsx b/src/components/GameManagePopup/index.tsx index 446b95f..e4c7283 100644 --- a/src/components/GameManagePopup/index.tsx +++ b/src/components/GameManagePopup/index.tsx @@ -5,26 +5,56 @@ import React, { useRef, } from "react"; import Taro from "@tarojs/taro"; -import { View } from "@tarojs/components"; +import { View, Text, Input } from "@tarojs/components"; import CommonPopup from "../CommonPopup"; import styles from "./index.module.scss"; -import detailService from "@/services/detailService"; +import detailService, { MATCH_STATUS } from "@/services/detailService"; import { useUserInfo } from "@/store/userStore"; const CancelPopup = forwardRef((props, ref) => { + const { detail } = props; const [visible, setVisible] = useState(false); + const [cancelReason, setCancelReason] = useState(""); const onFinish = useRef(null); + const inputRef = useRef(null); + + const { current_players, participants = [], publisher_id } = detail; + const realParticipants = participants + .filter((item) => item.status === "joined") + .map((item) => item.user.id); + const hasOtherJoin = + current_players > 1 || + realParticipants.some((id) => id !== Number(publisher_id)); + // const hasOtherJoin = true; useImperativeHandle(ref, () => ({ show: (onAct) => { onFinish.current = onAct; setVisible(true); + setTimeout(() => { + inputRef.current.focus(); + }, 0); }, })); function onClose() { setVisible(false); + setCancelReason(""); } + + async function handleConfirm() { + if (!cancelReason) { + Taro.showToast({ title: "请输入取消原因", icon: "none" }); + return; + } + try { + await onFinish.current(cancelReason); + onClose(); + } catch (e) { + console.log(e, 1221); + } + } + return ( <> { zIndex={1002} enableDragToClose={false} onClose={onClose} + position="center" + style={{ + width: hasOtherJoin ? "360px" : "300px", + borderRadius: "16px", + }} > - + + 确定要取消活动吗? + + + {hasOtherJoin + ? "已有球友报名,取消后将为他们自动退款" + : "有100+球友正在浏览您的球局哦~"} + + {hasOtherJoin && ( + + setCancelReason(e.detail.value)} + /> + + )} + + + + 确认取消 + + + 再想想 + + + ); @@ -60,7 +124,7 @@ export default forwardRef(function GameManagePopup(props, ref) { Taro.navigateTo({ url: `/publish_pages/publishBall/index?gameId=${detail.id}`, }); - onClose() + onClose(); } const handleRepubGame = handleEditGame; @@ -79,11 +143,11 @@ export default forwardRef(function GameManagePopup(props, ref) { } } catch (e) { Taro.showToast({ title: e.message, icon: "error" }); - } finally { - onClose(); + return e; } } }); + onClose(); } async function handleQuitGame() { @@ -107,7 +171,13 @@ export default forwardRef(function GameManagePopup(props, ref) { setVisible(false); } - const hasJoin = (detail.participants || []).some(item => item.user.id === userInfo.id) + const hasJoin = (detail.participants || []) + .filter((item) => item.status === "joined") + .some((item) => item.user.id === userInfo.id); + + const finished = [MATCH_STATUS.FINISHED, MATCH_STATUS.CANCELED].includes( + detail.match_status + ); return ( <> @@ -126,9 +196,11 @@ export default forwardRef(function GameManagePopup(props, ref) { 重新发布 - - 取消活动 - + {!finished && ( + + 取消活动 + + )} {hasJoin && ( 退出活动 @@ -139,7 +211,7 @@ export default forwardRef(function GameManagePopup(props, ref) { - + ); }); diff --git a/src/game_pages/detail/index.scss b/src/game_pages/detail/index.scss index d851af7..1f92417 100644 --- a/src/game_pages/detail/index.scss +++ b/src/game_pages/detail/index.scss @@ -1130,19 +1130,26 @@ // padding: 2px 6px; box-sizing: border-box; justify-content: center; - gap: 12px; + // gap: 12px; flex: 1 0 0; border-radius: 16px; // border: 1px solid rgba(0, 0, 0, 0.06); background: #fff; overflow: hidden; + &.disabled { + background-color: #B4B4B4; + color: rgba(60, 60, 67, 0.60); + pointer-events: none; + } + .sticky-bottom-bar-join-game { margin-left: auto; - width: 151px; + // width: 151px; display: flex; align-items: center; justify-content: center; + flex: 1; &-price { font-family: "PoetsenOne"; @@ -1163,6 +1170,7 @@ justify-content: center; background: #000; color: #fff; + pointer-events: all; } } } diff --git a/src/game_pages/detail/index.tsx b/src/game_pages/detail/index.tsx index 4ca90a9..88f8fef 100644 --- a/src/game_pages/detail/index.tsx +++ b/src/game_pages/detail/index.tsx @@ -13,6 +13,7 @@ import Taro, { useShareTimeline, useDidShow, } from "@tarojs/taro"; +import classnames from 'classnames' import dayjs from "dayjs"; import "dayjs/locale/zh-cn"; // 导入API服务 @@ -27,7 +28,7 @@ import { SceneType, DisplayConditionType, } from "@/components/NTRPEvaluatePopup"; -import DetailService, { MATCH_STATUS } from "@/services/detailService"; +import DetailService, { MATCH_STATUS, IsSubstituteSupported } from "@/services/detailService"; import * as LoginService from "@/services/loginService"; import OrderService from "@/services/orderService"; import { getCurrentLocation, calculateDistance } from "@/utils/locationUtils"; @@ -228,11 +229,19 @@ function toast(message) { Taro.showToast({ title: message, icon: "none" }); } +function isFull (counts) { + const { max_players, current_players, max_substitute_players, current_substitute_count, is_substitute_supported } = counts + if (is_substitute_supported === IsSubstituteSupported.SUPPORT) { + return max_substitute_players === current_substitute_count + } + return max_players === current_players +} + // 底部操作栏 function StickyButton(props) { const { handleShare, handleJoinGame, detail, onStatusChange } = props; const ntrpRef = useRef(null); - const { id, price, user_action_status, end_time, is_organizer } = + const { id, price, user_action_status, match_status, start_time, end_time, is_organizer } = detail || {}; const gameManageRef = useRef(); @@ -244,7 +253,7 @@ function StickyButton(props) { function generateTextAndAction( user_action_status: null | { [key: string]: boolean } - ): undefined | { text: string | React.FC; action: () => void } { + ): undefined | { text: string | React.FC; action?: () => void; available?: boolean } { if (!user_action_status) { return; } @@ -259,13 +268,29 @@ function StickyButton(props) { is_substituting, waiting_start, } = user_action_status || {}; - if ( - Object.values(user_action_status).every((value) => !value) && - dayjs(end_time).isBefore(dayjs()) - ) { + if (MATCH_STATUS.CANCELED === match_status) { + return { + text: "活动已取消", + available: false, + // action: () => toast("活动已取消"), + }; + } else if (dayjs(end_time).isBefore(dayjs())) { return { text: "活动已结束", - action: () => toast("活动已结束"), + available: false, + // action: () => toast("活动已结束"), + }; + } else if (dayjs(start_time).isBefore(dayjs())) { + return { + text: "活动已开始", + available: false, + // action: () => toast("活动已开始"), + }; + } else if (isFull(detail)) { + return { + text: "活动已满员", + available: false, + // action: () => toast("活动已满员"), }; } if (waiting_start) { @@ -284,7 +309,9 @@ function StickyButton(props) { action: async () => { const res = await OrderService.getUnpaidOrder(id); if (res.code === 0) { - navto(`/order_pages/orderDetail/index?id=${res.data.order_info.order_id}`); + navto( + `/order_pages/orderDetail/index?id=${res.data.order_info.order_id}` + ); } }, }; @@ -317,7 +344,8 @@ function StickyButton(props) { } return { text: "球局无法加入", - action: () => {}, + available: false, + // action: () => {}, }; } @@ -325,7 +353,7 @@ function StickyButton(props) { return ""; } - const { text, action } = generateTextAndAction(user_action_status)!; + const { text, available = true, action = () => {} } = generateTextAndAction(user_action_status)!; let ActionText: React.FC | string = text; @@ -360,8 +388,12 @@ function StickyButton(props) { 32 - - + + {is_organizer && ( diff --git a/src/order_pages/orderDetail/index.module.scss b/src/order_pages/orderDetail/index.module.scss index 3c518c1..abe0d98 100644 --- a/src/order_pages/orderDetail/index.module.scss +++ b/src/order_pages/orderDetail/index.module.scss @@ -326,6 +326,17 @@ justify-content: center; gap: 8px; } + + .orderNo { + display: flex; + justify-content: flex-end; + align-items: center; + gap: 8px; + + .copy { + color: #007AFF; + } + } } } } @@ -503,3 +514,15 @@ border-radius: 0; } } + +.cancelTip { + padding: 12px 15px; + color: rgba(60, 60, 67, 0.60); + text-align: center; + font-feature-settings: 'liga' off, 'clig' off; + font-family: "PingFang SC"; + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 24px; +} diff --git a/src/order_pages/orderDetail/index.tsx b/src/order_pages/orderDetail/index.tsx index 0bfacfa..6890c8a 100644 --- a/src/order_pages/orderDetail/index.tsx +++ b/src/order_pages/orderDetail/index.tsx @@ -3,6 +3,7 @@ import { View, Text, Button, Image } from "@tarojs/components"; import { Dialog } from "@nutui/nutui-react-taro"; import Taro, { useDidShow, useRouter } from "@tarojs/taro"; import dayjs from "dayjs"; +import "dayjs/locale/zh-cn"; import classnames from "classnames"; import orderService, { CancelType, @@ -64,11 +65,13 @@ function genGameNotice(order_status, start_time) { } else if (leftHour < 2) { key = "pendinging"; } + return gameNoticeMap.get(key) || {}; } function GameInfo(props) { const { detail, currentLocation, orderDetail } = props; + console.log(orderDetail, "orderDetail"); const { order_status, refund_status } = orderDetail; const { latitude, longitude, location, location_name, start_time, end_time } = detail || {}; @@ -142,7 +145,11 @@ function GameInfo(props) { }; Dialog.open("detailCancelOrder", { title: "确定删除订单吗?", - content: "删除订单后,您将无法恢复订单。请确认是否继续取消?", + content: ( + + 删除订单后,您将无法恢复订单。请确认是否继续取消? + + ), footer: (