41 Commits

Author SHA1 Message Date
1973ec3faa input placeholder样式修改 2026-02-27 15:35:03 +08:00
abc2dfeecf 设置交易密码页面样式优化 2026-02-27 12:08:47 +08:00
bafb44ff06 设置交易密码页面样式优化 2026-02-27 12:06:40 +08:00
0e27d801a4 fix: 文案修改 2026-02-27 10:06:49 +08:00
0a0203e36d fix: 修复退出活动弹窗文案展示 2026-02-26 20:34:13 +08:00
2656c59475 feat: 退款政策接口获取 2026-02-26 19:43:40 +08:00
23eb9dc467 fix: 退款展示退款金额、球局详情页样式修复 2026-02-26 17:56:42 +08:00
44f971b1c2 往期球局排序处理 2026-02-25 16:42:14 +08:00
4a553c63fc 往期球局排序处理 2026-02-25 16:37:47 +08:00
张成
baa60bbfcb 添加内容过滤功能 2026-02-14 13:56:54 +08:00
张成
64f0267457 修改上传图片安全验证问题 2026-02-14 12:59:21 +08:00
张成
8688b6b82d 1 2026-02-13 11:30:08 +08:00
张成
1678f787a3 1 2026-02-13 11:30:03 +08:00
张成
3571740280 添加审核不展示二维码的功能 2026-02-13 10:29:18 +08:00
张成
b6801cdde2 Merge branch 'master' of https://git.bimwe.com/bimwe/mini-programs 2026-02-12 10:06:50 +08:00
张成
e1ebcd949b 1 2026-02-12 10:06:48 +08:00
张成
044e84a5b4 Merge branch 'master' of https://git.bimwe.com/bimwe/mini-programs 2026-02-11 23:23:37 +08:00
张成
7833c2f552 1 2026-02-11 23:23:36 +08:00
李瑞
9e4282545f Merge branch 'feat/juguohong/20260206' 2026-02-11 23:14:22 +08:00
李瑞
99c8026f61 数据为空时允许展示banner 2026-02-11 23:13:49 +08:00
2a9e8668a0 style: 修复订单详情页订单号超长导致的样式问题 2026-02-11 10:11:55 +08:00
08092a89ab 添加发布球局拦截 2026-02-11 09:09:53 +08:00
筱野
4f0cdad920 修改提示文案 2026-02-10 23:04:29 +08:00
05966b2acb 优化ntrp和性别picker偶尔选不上值 2026-02-10 18:00:26 +08:00
张成
4cf2b959b5 1 2026-02-10 12:42:42 +08:00
张成
43610dcf99 修复首页数据少的问题 2026-02-10 11:46:39 +08:00
张成
05aa820466 Merge branch 'master' of https://git.bimwe.com/bimwe/mini-programs 2026-02-09 22:03:59 +08:00
筱野
b154e31f8f Merge branch 'master' of https://git.bimwe.com/bimwe/mini-programs 2026-02-09 22:03:09 +08:00
筱野
669ee2fe4e 解决按钮问题与键盘弹出问题 2026-02-09 22:03:01 +08:00
张成
281ee2b746 Merge branch 'master' of https://git.bimwe.com/bimwe/mini-programs 2026-02-09 22:02:46 +08:00
张成
132c74d27c 1 2026-02-09 22:02:43 +08:00
李瑞
6b6a4c9480 替换玩法icon 2026-02-09 22:01:36 +08:00
筱野
0f8dd44f5a 解决按钮问题与键盘弹出问题 2026-02-09 21:16:48 +08:00
82ba753b8b Merge remote-tracking branch 'refs/remotes/origin/master' 2026-02-09 20:07:47 +08:00
159d81ed12 fix: 详情管理功能按钮逻辑修改 2026-02-09 20:07:05 +08:00
22965eedf3 个人简介和昵称修改后显示底部导航 2026-02-09 16:36:15 +08:00
49935dd049 优化省市区和占位图片 2026-02-09 13:53:19 +08:00
张成
cab90aa1cb 1 2026-02-09 13:25:13 +08:00
632da5112d feat: 删除小猫 2026-02-09 09:49:43 +08:00
28955e9da1 fix: 修改分享初始化逻辑、去除小猫图 2026-02-08 23:58:37 +08:00
70a66fabdc Merge branch 'feat/liujie' 2026-02-08 22:57:48 +08:00
88 changed files with 1297 additions and 834 deletions

2
.gitignore vendored
View File

@@ -8,4 +8,6 @@ node_modules/
src/config/env.ts src/config/env.ts
.vscode .vscode
*.http *.http
.cursor
.codewiz

View File

@@ -473,7 +473,7 @@ async function safeMarkAsRead(type, ids) {
}) })
} catch (err) { } catch (err) {
// 标记已读失败不影响用户体验,静默处理 // 标记已读失败不影响用户体验,静默处理
console.error('标记已读失败:', err) console.warn('标记已读失败:', err)
} }
} }
``` ```

View File

@@ -28,7 +28,7 @@ function formatSize(bytes) {
function analyze() { function analyze() {
if (!fs.existsSync(DIST_DIR)) { if (!fs.existsSync(DIST_DIR)) {
console.error('dist 目录不存在,请先执行 taro build --type weapp'); console.warn('dist 目录不存在,请先执行 taro build --type weapp');
return; return;
} }

View File

@@ -4,7 +4,11 @@ export default {
quiet: false, quiet: false,
stats: true stats: true
}, },
mini: {}, mini: {
webpackChain(chain) {
chain.devtool('source-map')
}
},
h5: {}, h5: {},
// 添加这个配置来显示完整错误信息 // 添加这个配置来显示完整错误信息
compiler: { compiler: {

128
config/env.ts Normal file
View File

@@ -0,0 +1,128 @@
import Taro from '@tarojs/taro'
// 环境类型
export type EnvType = 'development' | 'production'
// 环境配置接口
export interface EnvConfig {
name: string
apiBaseURL: string
timeout: number
enableLog: boolean
enableMock: boolean
// 客服配置
customerService: {
corpId: string
serviceUrl: string
phoneNumber?: string
email?: string
}
}
// 各环境配置
const envConfigs: Record<EnvType, EnvConfig> = {
// 开发环境
development: {
name: '开发环境',
// apiBaseURL: 'https://tennis.bimwe.com',
apiBaseURL: 'http://localhost:9098',
timeout: 15000,
enableLog: true,
enableMock: false,
// 客服配置
customerService: {
corpId: 'ww51fc969e8b76af82', // 企业ID
serviceUrl: 'https://work.weixin.qq.com/kfid/kfc64085b93243c5c91',
}
},
// 生产环境1
// production: {
// name: '生产环境1',
// apiBaseURL: 'https://tennis.bimwe.com',
// timeout: 10000,
// enableLog: false,
// enableMock: false,
// // 客服配置
// customerService: {
// corpId: 'ww51fc969e8b76af82', // 企业ID
// serviceUrl: 'https://work.weixin.qq.com/kfid/kfc64085b93243c5c91',
// }
// },
// 生产环境2
production: {
name: '生产环境2',
apiBaseURL: 'https://youchang.qiongjingtiyu.com',
timeout: 10000,
enableLog: false,
enableMock: false,
// 客服配置
customerService: {
corpId: 'ww9a2d9a5d9410c664', // 企业ID
serviceUrl: 'https://work.weixin.qq.com/kfid/kfcd355e162e0390684',
}
}
}
// 获取当前环境
export const getCurrentEnv = (): EnvType => {
// 在小程序环境中,使用默认逻辑判断环境
// 可以根据实际需要配置不同的判断逻辑
// 可以根据实际部署情况添加更多判断逻辑
// 比如通过 Taro.getEnv() 获取当前平台环境
const isProd = process.env.NODE_ENV === 'production'
if (isProd) {
return 'production'
} else {
return 'development'
}
}
// 获取当前环境配置
export const getCurrentConfig = (): EnvConfig => {
const env = getCurrentEnv()
return envConfigs[env]
}
// 获取指定环境配置
export const getEnvConfig = (env: EnvType): EnvConfig => {
return envConfigs[env]
}
// 是否为开发环境
export const isDevelopment = (): boolean => {
return getCurrentEnv() === 'development'
}
// 是否为生产环境
export const isProduction = (): boolean => {
return getCurrentEnv() === 'production'
}
// 环境配置调试信息
export const getEnvInfo = () => {
const config = getCurrentConfig()
return {
env: getCurrentEnv(),
config,
taroEnv: Taro.getEnv(),
platform: Taro.getEnv() === Taro.ENV_TYPE.WEAPP ? '微信小程序' :
Taro.getEnv() === Taro.ENV_TYPE.WEB ? 'Web' :
Taro.getEnv() === Taro.ENV_TYPE.RN ? 'React Native' : '未知'
}
}
// 导出当前环境配置(方便直接使用)
export default getCurrentConfig()

View File

@@ -11,7 +11,7 @@ const { envConfigs } = require(envConfigPath);
const config = envConfigs[appEnv]; const config = envConfigs[appEnv];
if (!config) { if (!config) {
console.error(`[sync-project-config] Unknown APP_ENV: ${appEnv}`); console.warn(`[sync-project-config] Unknown APP_ENV: ${appEnv}`);
process.exit(1); process.exit(1);
} }

View File

@@ -11,7 +11,7 @@ import dayjs from "dayjs";
import classnames from "classnames"; import classnames from "classnames";
import CommentServices from "@/services/commentServices"; import CommentServices from "@/services/commentServices";
import messageService from "@/services/messageService"; import messageService from "@/services/messageService";
import { delay } from "@/utils"; import { delay, getBackendErrorMsg } from "@/utils";
import type { import type {
BaseComment, BaseComment,
Comment, Comment,
@@ -342,7 +342,7 @@ export default forwardRef(function Comments(
try { try {
await messageService.markAsRead("comment", [message_id]); await messageService.markAsRead("comment", [message_id]);
} catch (e) { } catch (e) {
console.error("标记评论已读失败:", e); console.warn("标记评论已读失败:", e);
} }
} }
@@ -459,36 +459,48 @@ export default forwardRef(function Comments(
} }
async function createComment(val: string) { async function createComment(val: string) {
const res = await CommentServices.createComment({ game_id, content: val }); try {
if (res.code === 0) { const res = await CommentServices.createComment({ game_id, content: val });
setComments((prev) => { if (res.code === 0) {
commentCountUpdateRef.current?.(prev.length + 1); setComments((prev) => {
return [{ ...res.data, replies: [] }, ...prev]; commentCountUpdateRef.current?.(prev.length + 1);
}); return [{ ...res.data, replies: [] }, ...prev];
toast("发布成功"); });
toast("发布成功");
} else {
toast(getBackendErrorMsg(res, "评论失败"));
}
} catch (error) {
toast(getBackendErrorMsg(error, "评论失败"));
} }
} }
async function replyComment({ parent_id, reply_to_user_id, content }) { async function replyComment({ parent_id, reply_to_user_id, content }) {
const res = await CommentServices.replyComment({ try {
parent_id, const res = await CommentServices.replyComment({
reply_to_user_id, parent_id,
content, reply_to_user_id,
}); content,
if (res.code === 0) {
setComments((prev) => {
return prev.map((item) => {
if (item.id === parent_id) {
return {
...item,
replies: [res.data, ...item.replies],
reply_count: item.reply_count + 1,
};
}
return item;
});
}); });
toast("回复成功"); if (res.code === 0) {
setComments((prev) => {
return prev.map((item) => {
if (item.id === parent_id) {
return {
...item,
replies: [res.data, ...item.replies],
reply_count: item.reply_count + 1,
};
}
return item;
});
});
toast("回复成功");
} else {
toast(getBackendErrorMsg(res, "回复失败"));
}
} catch (error) {
toast(getBackendErrorMsg(error, "回复失败"));
} }
} }

View File

@@ -3,6 +3,12 @@
.common-popup { .common-popup {
position: fixed; position: fixed;
z-index: 9999 !important; z-index: 9999 !important;
padding: 0;
box-sizing: border-box;
max-height: calc(100vh - 10px);
display: flex;
flex-direction: column;
background-color: theme.$page-background-color;
&:global(.nut-popup-bottom.nut-popup-round) { &:global(.nut-popup-bottom.nut-popup-round) {
border-radius: 20px 20px 0 0 !important; border-radius: 20px 20px 0 0 !important;
} }
@@ -32,12 +38,7 @@
} }
} }
padding: 0;
box-sizing: border-box;
max-height: calc(100vh - 10px);
display: flex;
flex-direction: column;
background-color: theme.$page-background-color;
// .common-popup__header { // .common-popup__header {
// padding: 12px 16px; // padding: 12px 16px;

View File

@@ -48,7 +48,14 @@ const CustomPopup: React.FC<CustomPopupProps> = ({
const touchStartY = useRef(0) const touchStartY = useRef(0)
// 使用全局键盘状态 // 使用全局键盘状态
const { keyboardHeight, isKeyboardVisible, addListener, initializeKeyboardListener } = useKeyboardHeight() const { keyboardHeight, isKeyboardVisible, addListener, initializeKeyboardListener, setKeyboardVisible } = useKeyboardHeight()
// 当弹窗显示时,设置键盘为不可见
useEffect(() => {
if (visible) {
setKeyboardVisible(false)
}
}, [visible, setKeyboardVisible])
// 使用全局键盘状态监听 // 使用全局键盘状态监听
useEffect(() => { useEffect(() => {

View File

@@ -134,7 +134,7 @@ const DistanceQuickFilterV2 = (props) => {
throw new Error('获取位置信息失败'); throw new Error('获取位置信息失败');
} }
} catch (error: any) { } catch (error: any) {
console.error('重新定位失败:', error); console.warn('重新定位失败:', error);
(Taro as any).showToast({ (Taro as any).showToast({
title: error?.message || '定位失败,请检查定位权限', title: error?.message || '定位失败,请检查定位权限',
icon: 'none', icon: 'none',

View File

@@ -42,7 +42,7 @@ const FollowUserCard: React.FC<FollowUserCardProps> = ({ user, tabKey, onFollowC
onFollowChange?.(user.id, new_status); onFollowChange?.(user.id, new_status);
} catch (error) { } catch (error) {
console.error('关注操作失败:', error); console.warn('关注操作失败:', error);
Taro.showToast({ Taro.showToast({
title: '操作失败', title: '操作失败',
icon: 'none' icon: 'none'
@@ -67,7 +67,7 @@ const FollowUserCard: React.FC<FollowUserCardProps> = ({ user, tabKey, onFollowC
onBlockSuccess?.(user.id); onBlockSuccess?.(user.id);
} }
} catch (error) { } catch (error) {
console.error('删除推荐人员失败:', error); console.warn('删除推荐人员失败:', error);
Taro.showToast({ Taro.showToast({
title: '操作失败', title: '操作失败',
icon: 'none' icon: 'none'

View File

@@ -189,7 +189,8 @@ export default forwardRef(function GameManagePopup(props, ref) {
detail.match_status, detail.match_status,
); );
const inTwoHours = dayjs(detail.start_time).diff(dayjs(), "hour") < 2; // const inTwoHours = dayjs(detail.start_time).diff(dayjs(), "hour") < 2;
const beforeStart = dayjs(detail.start_time).isAfter(dayjs());
const hasOtherParticiappants = (detail.participants || []) const hasOtherParticiappants = (detail.participants || [])
.filter((item) => item.status === "joined") .filter((item) => item.status === "joined")
@@ -207,7 +208,7 @@ export default forwardRef(function GameManagePopup(props, ref) {
style={{ minHeight: "unset" }} style={{ minHeight: "unset" }}
> >
<View className={styles.container}> <View className={styles.container}>
{!finished && !inTwoHours && !hasOtherParticiappants && ( {!finished && !hasOtherParticiappants && beforeStart && (
<View className={styles.button} onClick={handleEditGame}> <View className={styles.button} onClick={handleEditGame}>
</View> </View>
@@ -217,12 +218,12 @@ export default forwardRef(function GameManagePopup(props, ref) {
</View> </View>
)} )}
{!finished && !inTwoHours && !hasOtherParticiappants && ( {!finished && beforeStart && (
<View className={styles.button} onClick={handleCancelGame}> <View className={styles.button} onClick={handleCancelGame}>
</View> </View>
)} )}
{!finished && hasJoin && ( {!finished && beforeStart && hasJoin && (
<View className={styles.button} onClick={handleQuitGame}> <View className={styles.button} onClick={handleQuitGame}>
退 退
</View> </View>

View File

@@ -15,7 +15,7 @@ const GamePlayType = (props: IProps) => {
const { name, onChange, value, options } = props; const { name, onChange, value, options } = props;
return ( return (
<View className={styles.gamePlayWrapper}> <View className={styles.gamePlayWrapper}>
<TitleComponent title="玩法" icon={<Image src={img.ICON_SITE} />} /> <TitleComponent title="玩法" icon={<Image src={img.ICON_GAME_PLAY} />} />
<Bubble <Bubble
options={options} options={options}
value={value} value={value}

View File

@@ -146,7 +146,7 @@ const HomeNavbar = (props: IProps) => {
console.log(`[HomeNavbar] 距离上次选择"继续浏览"还不到2小时剩余时间: ${Math.ceil((TWO_HOURS_MS - time_diff) / 1000 / 60)}分钟`); console.log(`[HomeNavbar] 距离上次选择"继续浏览"还不到2小时剩余时间: ${Math.ceil((TWO_HOURS_MS - time_diff) / 1000 / 60)}分钟`);
return false; return false;
} catch (error) { } catch (error) {
console.error('[HomeNavbar] 检查定位弹窗显示条件失败:', error); console.warn('[HomeNavbar] 检查定位弹窗显示条件失败:', error);
return true; // 出错时默认显示 return true; // 出错时默认显示
} }
}; };
@@ -239,7 +239,7 @@ const HomeNavbar = (props: IProps) => {
// console.log(`距离上次选择"继续浏览"还不到2小时剩余时间: ${Math.ceil((TWO_HOURS_MS - time_diff) / 1000 / 60)}分钟`); // console.log(`距离上次选择"继续浏览"还不到2小时剩余时间: ${Math.ceil((TWO_HOURS_MS - time_diff) / 1000 / 60)}分钟`);
// return false; // return false;
// } catch (error) { // } catch (error) {
// console.error('检查定位弹窗显示条件失败:', error); // console.warn('检查定位弹窗显示条件失败:', error);
// return true; // 出错时默认显示 // return true; // 出错时默认显示
// } // }
// }; // };
@@ -276,7 +276,7 @@ const HomeNavbar = (props: IProps) => {
(Taro as any).setStorageSync(CITY_CHANGE_TIME_KEY, current_time); (Taro as any).setStorageSync(CITY_CHANGE_TIME_KEY, current_time);
console.log(`[LocationDialog] 已记录用户切换城市的时间2小时内不再提示`); console.log(`[LocationDialog] 已记录用户切换城市的时间2小时内不再提示`);
} catch (error) { } catch (error) {
console.error('保存城市切换时间失败:', error); console.warn('保存城市切换时间失败:', error);
} }
console.log("切换到用户详情中的位置信息并更新缓存:", detectedProvince); console.log("切换到用户详情中的位置信息并更新缓存:", detectedProvince);
@@ -304,7 +304,7 @@ const HomeNavbar = (props: IProps) => {
(Taro as any).setStorageSync(LOCATION_DIALOG_DISMISS_TIME_KEY, current_time); (Taro as any).setStorageSync(LOCATION_DIALOG_DISMISS_TIME_KEY, current_time);
console.log(`[LocationDialog] 已记录用户选择"继续浏览"的时间2小时内不再提示`); console.log(`[LocationDialog] 已记录用户选择"继续浏览"的时间2小时内不再提示`);
} catch (error) { } catch (error) {
console.error('保存定位弹窗关闭时间失败:', error); console.warn('保存定位弹窗关闭时间失败:', error);
} }
// 关闭弹窗 // 关闭弹窗
@@ -409,7 +409,7 @@ const HomeNavbar = (props: IProps) => {
(Taro as any).setStorageSync(CITY_CHANGE_TIME_KEY, current_time); (Taro as any).setStorageSync(CITY_CHANGE_TIME_KEY, current_time);
console.log("已保存城市到缓存并记录切换时间:", _newArea, current_time); console.log("已保存城市到缓存并记录切换时间:", _newArea, current_time);
} catch (error) { } catch (error) {
console.error("保存城市缓存失败:", error); console.warn("保存城市缓存失败:", error);
} }
// 先调用列表接口(会使用更新后的 state.area // 先调用列表接口(会使用更新后的 state.area

View File

@@ -43,7 +43,7 @@ const ImageUpload: React.FC<ImageUploadProps> = ({
onChange([...images, ...newImages]) onChange([...images, ...newImages])
}, },
fail: (err) => { fail: (err) => {
console.error('选择图片失败:', err) console.warn('选择图片失败:', err)
} }
}) })
}, [images.length, maxCount, onChange]) }, [images.length, maxCount, onChange])

View File

@@ -5,8 +5,9 @@ import img from "../../config/images";
import { ListCardProps } from "../../../types/list/types"; import { ListCardProps } from "../../../types/list/types";
import { formatGameTime, calculateDuration } from "@/utils/timeUtils"; import { formatGameTime, calculateDuration } from "@/utils/timeUtils";
import { navigateTo } from "@/utils/navigation"; import { navigateTo } from "@/utils/navigation";
import images from '@/config/images' import images from "@/config/images";
import "./index.scss"; import "./index.scss";
import { OSS_BASE } from "@/config/api";
const ListCard: React.FC<ListCardProps> = ({ const ListCard: React.FC<ListCardProps> = ({
id, id,
@@ -45,7 +46,7 @@ const ListCard: React.FC<ListCardProps> = ({
className="image" className="image"
mode="aspectFill" mode="aspectFill"
lazyLoad lazyLoad
defaultSource={require("@/static/emptyStatus/publish-empty-card.png")} defaultSource={`${OSS_BASE}/front/ball/images/publish-empty-card.png`}
/> />
); );
}; };
@@ -67,7 +68,9 @@ const ListCard: React.FC<ListCardProps> = ({
const containerWidthPx = screenWidth - 130; const containerWidthPx = screenWidth - 130;
// 计算固定信息宽度 // 计算固定信息宽度
const extraInfo = `${court_type ? `${court_type}` : ''}${distance_km ? `${distance_km}km` : ''}`; const extraInfo = `${court_type ? `${court_type}` : ""}${
distance_km ? `${distance_km}km` : ""
}`;
// 估算字符宽度(基于 12px 字体) // 估算字符宽度(基于 12px 字体)
const getTextWidth = (text: string) => { const getTextWidth = (text: string) => {
@@ -98,7 +101,9 @@ const ListCard: React.FC<ListCardProps> = ({
let currentWidth = 0; let currentWidth = 0;
for (let i = 0; i < location.length; i++) { for (let i = 0; i < location.length; i++) {
const char = location[i]; const char = location[i];
const charWidth = /[\u4e00-\u9fa5\u3000-\u303f\uff00-\uffef]/.test(char) ? 12 : 6; const charWidth = /[\u4e00-\u9fa5\u3000-\u303f\uff00-\uffef]/.test(char)
? 12
: 6;
if (currentWidth + charWidth > availableWidth) { if (currentWidth + charWidth > availableWidth) {
break; break;
} }
@@ -106,7 +111,7 @@ const ListCard: React.FC<ListCardProps> = ({
maxChars++; maxChars++;
} }
return location.slice(0, maxChars) + '...'; return location.slice(0, maxChars) + "...";
}, [location, court_type, distance_km]); }, [location, court_type, distance_km]);
// 根据图片数量决定展示样式 // 根据图片数量决定展示样式
@@ -220,9 +225,10 @@ const ListCard: React.FC<ListCardProps> = ({
</Text> </Text>
</View> </View>
<View className="tag ntprTag"> <View className="tag ntprTag">
<Image src={images.ICON_LIST_NTPR} className='ntprIcon' /> <Image src={images.ICON_LIST_NTPR} className="ntprIcon" />
<Text className="tag-text"> <Text className="tag-text">
{Number(skill_level_min)?.toFixed(1)} - {Number(skill_level_max)?.toFixed(1)} {Number(skill_level_min)?.toFixed(1)} -{" "}
{Number(skill_level_max)?.toFixed(1)}
</Text> </Text>
{/* 分割线 */} {/* 分割线 */}
<View className="typeLine" /> <View className="typeLine" />
@@ -251,22 +257,16 @@ const ListCard: React.FC<ListCardProps> = ({
/> />
{/* <Text className="smoothTitle">{game_type}</Text> */} {/* <Text className="smoothTitle">{game_type}</Text> */}
</View> </View>
{ {venue_description && <View className="line" />}
venue_description && (<View className="line" />) {venue_description && (
} <View className="localAreaContainer">
{ <View className="localAreaTitle">:</View>
venue_description && <View className="localAreaWrapper">
( <Image className="localArea" src={venueImage} />
<Text className="localAreaText">{venue_description}</Text>
<View className="localAreaContainer">
<View className="localAreaTitle">:</View>
<View className="localAreaWrapper">
<Image className="localArea" src={venueImage} />
<Text className="localAreaText">{venue_description}</Text>
</View>
</View> </View>
) </View>
} )}
</View> </View>
)} )}
</View> </View>

View File

@@ -7,7 +7,6 @@
.listLoadErrorImg { .listLoadErrorImg {
width: 154px; width: 154px;
height: 154px;
} }
.listLoadErrorText { .listLoadErrorText {

View File

@@ -24,7 +24,6 @@ const ListLoadError = (props: IProps) => {
wrapperHeight = "", wrapperHeight = "",
width = "", width = "",
height = "", height = "",
scale = "",
} = props; } = props;
const handleReload = () => { const handleReload = () => {
reload && typeof reload === "function" && reload(); reload && typeof reload === "function" && reload();
@@ -34,7 +33,7 @@ const ListLoadError = (props: IProps) => {
<View className={styles.listLoadError} style={{ height: wrapperHeight }}> <View className={styles.listLoadError} style={{ height: wrapperHeight }}>
<Image <Image
className={styles.listLoadErrorImg} className={styles.listLoadErrorImg}
style={{ width, height, transform: `scale(${scale})` }} style={{ width, height }}
src={errorImg ? img[errorImg] : img.ICON_LIST_LOAD_ERROR} src={errorImg ? img[errorImg] : img.ICON_LIST_LOAD_ERROR}
/> />
{text && <Text className={styles.listLoadErrorText}>{text}</Text>} {text && <Text className={styles.listLoadErrorText}>{text}</Text>}

View File

@@ -67,12 +67,6 @@ const NTRPEvaluatePopup = (props: NTRPEvaluatePopupProps, ref) => {
const { updateUserInfo } = useUserActions(); const { updateUserInfo } = useUserActions();
const userInfo = useUserInfo(); const userInfo = useUserInfo();
const ntrpLevels = useNtrpLevels(); const ntrpLevels = useNtrpLevels();
const options = [
ntrpLevels.map((item) => ({
text: item,
value: item,
})),
];
const [evaCallback, setEvaCallback] = useState<EvaluateCallback>({ const [evaCallback, setEvaCallback] = useState<EvaluateCallback>({
type: "", type: "",
next: () => {}, next: () => {},
@@ -171,7 +165,7 @@ const NTRPEvaluatePopup = (props: NTRPEvaluatePopupProps, ref) => {
{visible && ( {visible && (
<Picker <Picker
visible visible
options={options} options={ntrpLevels}
defaultValue={[ntrp]} defaultValue={[ntrp]}
onChange={(val) => { onChange={(val) => {
console.log(val[0]); console.log(val[0]);

View File

@@ -52,7 +52,7 @@ const PopupPicker = ({
ntrpTested, ntrpTested,
}: PickerProps) => { }: PickerProps) => {
const [defaultValue, setDefaultValue] = useState<(string | number)[]>([]); const [defaultValue, setDefaultValue] = useState<(string | number)[]>([]);
const [defaultOptions, setDefaultOptions] = useState<PickerOption[][]>([]); const [defaultOptions, setDefaultOptions] = useState<PickerOption[][]>([...options]);
const [pickerCurrentValue, setPickerCurrentValue] = const [pickerCurrentValue, setPickerCurrentValue] =
useState<(string | number)[]>(value); useState<(string | number)[]>(value);

View File

@@ -3,7 +3,6 @@ import { View, Text, Image } from "@tarojs/components";
import Taro from "@tarojs/taro"; import Taro from "@tarojs/taro";
import { useUserInfo } from "@/store/userStore"; import { useUserInfo } from "@/store/userStore";
import { import {
useEvaluate,
EvaluateCallback, EvaluateCallback,
EvaluateScene, EvaluateScene,
} from "@/store/evaluateStore"; } from "@/store/evaluateStore";
@@ -15,6 +14,7 @@ import styles from "./index.module.scss";
import images from "@/config/images"; import images from "@/config/images";
import AiImportPopup from "@/publish_pages/publishBall/components/AiImportPopup"; import AiImportPopup from "@/publish_pages/publishBall/components/AiImportPopup";
import NTRPEvaluatePopup from "../NTRPEvaluatePopup"; import NTRPEvaluatePopup from "../NTRPEvaluatePopup";
import { useDictionaryStore } from "@/store/dictionaryStore";
export interface PublishMenuProps { export interface PublishMenuProps {
onPersonalPublish?: () => void; onPersonalPublish?: () => void;
@@ -30,6 +30,7 @@ const PublishMenu: React.FC<PublishMenuProps> = (props) => {
area area
} = useListState(); } = useListState();
const supportedCitiesList = useDictionaryStore((s) => s.getDictionaryValue('supported_cities')) || [];
// 使用 useEffect 监听 isVisible 变化,确保所有情况都能触发回调 // 使用 useEffect 监听 isVisible 变化,确保所有情况都能触发回调
useEffect(() => { useEffect(() => {
@@ -67,10 +68,10 @@ const PublishMenu: React.FC<PublishMenuProps> = (props) => {
}; };
const handleMenuItemClick = (type: "individual" | "group" | "ai") => { const handleMenuItemClick = (type: "individual" | "group" | "ai") => {
const [_, address] = area; const [_, address] = area;
if (address !== '上海市') { if (!supportedCitiesList.includes(address)) {
(Taro as any).showModal({ (Taro as any).showModal({
title: '提示', title: '提示',
content: '仅上海地区开放,您可加入社群或切换城市', content: '该城市尚未开放,您可加入社群或切换城市',
showCancel: false, showCancel: false,
confirmText: '知道了' confirmText: '知道了'
}) })

View File

@@ -256,7 +256,7 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>(
const bgImg = await loadImage(canvas, shareBgUrl); const bgImg = await loadImage(canvas, shareBgUrl);
ctx.drawImage(bgImg, 0, 0, width, height); ctx.drawImage(bgImg, 0, 0, width, height);
} catch (error) { } catch (error) {
console.error("Failed to load background image:", error); console.warn("Failed to load background image:", error);
// 如果加载失败,使用白色背景作为兜底 // 如果加载失败,使用白色背景作为兜底
ctx.fillStyle = "#FFFFFF"; ctx.fillStyle = "#FFFFFF";
ctx.fillRect(0, 0, width, height); ctx.fillRect(0, 0, width, height);
@@ -392,12 +392,12 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>(
); );
ctx.restore(); ctx.restore();
} catch (error) { } catch (error) {
console.error("Failed to load docCopy image:", error); console.warn("Failed to load docCopy image:", error);
} }
currentY += (48 + 20) * scale; // 头像区域高度 + gap currentY += (48 + 20) * scale; // 头像区域高度 + gap
} catch (error) { } catch (error) {
console.error("Failed to load avatar image:", error); console.warn("Failed to load avatar image:", error);
} }
} }
@@ -523,7 +523,7 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>(
iconSize, iconSize,
); );
} catch (error) { } catch (error) {
console.error("Failed to load icon:", error); console.warn("Failed to load icon:", error);
} }
// 绘制底部文字 // 绘制底部文字
@@ -601,7 +601,7 @@ const RadarChartV2 = forwardRef<RadarChartV2Ref, RadarChartV2Props>(
// 恢复上下文状态 // 恢复上下文状态
ctx.restore(); ctx.restore();
} catch (error) { } catch (error) {
console.error("Failed to load QR code:", error); console.warn("Failed to load QR code:", error);
} }
} }

View File

@@ -575,7 +575,7 @@ const ShareCardCanvas: React.FC<ShareCardCanvasProps> = ({
setTempImagePath(res.tempFilePath) setTempImagePath(res.tempFilePath)
}, },
fail: (error: any) => { fail: (error: any) => {
console.error('图片生成失败:', error) console.warn('图片生成失败:', error)
setIsDrawing(false) setIsDrawing(false)
reject(error) reject(error)
} }
@@ -595,7 +595,7 @@ const ShareCardCanvas: React.FC<ShareCardCanvasProps> = ({
console.log('Canvas绘制命令已发送') console.log('Canvas绘制命令已发送')
} catch (error) { } catch (error) {
console.error('绘制分享卡片失败:', error) console.warn('绘制分享卡片失败:', error)
setIsDrawing(false) // 绘制失败,重置状态 setIsDrawing(false) // 绘制失败,重置状态
Taro.showToast({ Taro.showToast({
title: '生成分享卡片失败', title: '生成分享卡片失败',

View File

@@ -16,7 +16,7 @@ const SubscribeNotificationTip: React.FC<SubscribeNotificationTipProps> = ({
navigateTo({ navigateTo({
url: '/other_pages/enable_notification/index', url: '/other_pages/enable_notification/index',
}).catch((err) => { }).catch((err) => {
console.error('跳转失败:', err); console.warn('跳转失败:', err);
}); });
}; };

View File

@@ -86,9 +86,9 @@ async function onChooseImageSuccess(tempFiles) {
...fileRes, ...fileRes,
...(height > IMAGE_MAX_SIZE.height ...(height > IMAGE_MAX_SIZE.height
? { ? {
width: Math.floor(IMAGE_MAX_SIZE.height * image_aspect_ratio), width: Math.floor(IMAGE_MAX_SIZE.height * image_aspect_ratio),
height: IMAGE_MAX_SIZE.height, height: IMAGE_MAX_SIZE.height,
} }
: { width: Math.floor(height * image_aspect_ratio), height }), : { width: Math.floor(height * image_aspect_ratio), height }),
}; };
} else { } else {
@@ -96,9 +96,9 @@ async function onChooseImageSuccess(tempFiles) {
...fileRes, ...fileRes,
...(width > IMAGE_MAX_SIZE.width ...(width > IMAGE_MAX_SIZE.width
? { ? {
width: IMAGE_MAX_SIZE.width, width: IMAGE_MAX_SIZE.width,
height: Math.floor(IMAGE_MAX_SIZE.width / image_aspect_ratio), height: Math.floor(IMAGE_MAX_SIZE.width / image_aspect_ratio),
} }
: { width, height: Math.floor(width / image_aspect_ratio) }), : { width, height: Math.floor(width / image_aspect_ratio) }),
}; };
} }
@@ -119,7 +119,6 @@ export default function UploadFromWx(props: UploadFromWxProps) {
sourceType: ["album", "camera"], sourceType: ["album", "camera"],
}).then(async (res) => { }).then(async (res) => {
const analyzedFiles = await onChooseImageSuccess(res.tempFiles); const analyzedFiles = await onChooseImageSuccess(res.tempFiles);
// cropping image to standard size
const compressedTempFiles = await compressImage(analyzedFiles); const compressedTempFiles = await compressImage(analyzedFiles);
let start = Date.now(); let start = Date.now();
@@ -130,19 +129,22 @@ export default function UploadFromWx(props: UploadFromWxProps) {
is_public: 1 as unknown as 0 | 1, is_public: 1 as unknown as 0 | 1,
id: (start++).toString(), id: (start++).toString(),
})); }));
const onFileUpdate = uploadApi.batchUpload(files).then((res) => {
return res.map((item) => ({ Taro.showLoading({ title: "上传中..." });
id: item.id, try {
url: item ? item.data.file_url : "", const uploadRes = await uploadApi.batchUpload(files);
})); const successful = uploadRes
}); .filter((item) => item.data != null)
onAdd( .map((item) => ({
files.map((item) => ({ id: item.id,
id: item.id, url: (item.data as { file_url: string }).file_url,
url: item.filePath, }));
})), onAdd(successful, Promise.resolve(successful));
onFileUpdate } catch (e) {
); console.warn("批量上传失败:", e);
} finally {
Taro.hideLoading();
}
}); });
}; };
return ( return (

View File

@@ -6,14 +6,19 @@ import "./index.scss";
import { EditModal } from "@/components"; import { EditModal } from "@/components";
import { UserService, PickerOption } from "@/services/userService"; import { UserService, PickerOption } from "@/services/userService";
import { PopupPicker } from "@/components/Picker/index"; import { PopupPicker } from "@/components/Picker/index";
import { useUserActions, useNicknameChangeStatus, useLastTestResult } from "@/store/userStore"; import {
useUserActions,
useNicknameChangeStatus,
useLastTestResult,
useUserInfo,
} from "@/store/userStore";
import { UserInfoType } from "@/services/userService"; import { UserInfoType } from "@/services/userService";
import { import {
useCities, useCities,
useProfessions, useProfessions,
useNtrpLevels, useNtrpLevels,
} from "@/store/pickerOptionsStore"; } from "@/store/pickerOptionsStore";
import { formatNtrpDisplay } from "@/utils/helper"; import { formatNtrpDisplay, getBackendErrorMsg } from "@/utils/helper";
import { useGlobalState } from "@/store/global"; import { useGlobalState } from "@/store/global";
// 用户信息接口 // 用户信息接口
@@ -69,7 +74,7 @@ const on_edit = () => {
// 用户信息卡片组件 // 用户信息卡片组件
const UserInfoCardComponent: React.FC<UserInfoCardProps> = ({ const UserInfoCardComponent: React.FC<UserInfoCardProps> = ({
editable = true, editable = true,
user_info, user_info: user_info_prop,
is_current_user, is_current_user,
is_following = false, is_following = false,
collapseProfile, collapseProfile,
@@ -80,9 +85,13 @@ const UserInfoCardComponent: React.FC<UserInfoCardProps> = ({
set_user_info, set_user_info,
onTab, onTab,
}) => { }) => {
const global_user_info = useUserInfo();
// 查看别人页面时用传入的 user_info个人页用全局 store
const user_info = is_current_user ? global_user_info : (user_info_prop ?? global_user_info);
const nickname_change_status = useNicknameChangeStatus(); const nickname_change_status = useNicknameChangeStatus();
const { setShowGuideBar } = useGlobalState(); const { setShowGuideBar } = useGlobalState();
const { updateUserInfo, updateNickname, fetchLastTestResult } = useUserActions(); const { updateUserInfo, updateNickname, fetchLastTestResult } =
useUserActions();
const ntrpLevels = useNtrpLevels(); const ntrpLevels = useNtrpLevels();
// 使用全局状态中的测试结果,避免重复调用接口 // 使用全局状态中的测试结果,避免重复调用接口
const lastTestResult = useLastTestResult(); const lastTestResult = useLastTestResult();
@@ -91,18 +100,16 @@ const UserInfoCardComponent: React.FC<UserInfoCardProps> = ({
const prevUserInfoRef = useRef<Partial<UserInfoType>>(); const prevUserInfoRef = useRef<Partial<UserInfoType>>();
useEffect(() => { useEffect(() => {
// 只在 user_info 真正变化时打印(通过 JSON 序列化比较)
const prevStr = JSON.stringify(prevUserInfoRef.current); const prevStr = JSON.stringify(prevUserInfoRef.current);
const currentStr = JSON.stringify(user_info); const currentStr = JSON.stringify(user_info);
if (prevStr !== currentStr) { if (prevStr !== currentStr) {
console.log("UserInfoCard 用户信息变化:", user_info);
prevUserInfoRef.current = user_info; prevUserInfoRef.current = user_info;
} }
// 如果全局状态中没有测试结果,则调用接口(使用请求锁,多个组件同时调用时只会请求一次) // 仅当前用户才拉取 NTRP 测试结果
if (!lastTestResult && user_info?.id) { if (is_current_user && !lastTestResult && user_info?.id) {
fetchLastTestResult(); fetchLastTestResult();
} }
}, [user_info?.id, lastTestResult, fetchLastTestResult]); }, [user_info?.id, lastTestResult, fetchLastTestResult, is_current_user]);
// 从全局状态中获取测试状态 // 从全局状态中获取测试状态
const ntrpTested = lastTestResult?.has_test_in_last_month || false; const ntrpTested = lastTestResult?.has_test_in_last_month || false;
@@ -117,11 +124,15 @@ const UserInfoCardComponent: React.FC<UserInfoCardProps> = ({
useState(false); useState(false);
// 表单状态 // 表单状态
const [form_data, set_form_data] = useState<Partial<UserInfoType>>({}); const [form_data, set_form_data] = useState<Partial<UserInfoType>>({ ...user_info });
useDidShow(() => { // useDidShow(() => {
// set_form_data({ ...user_info });
// });
useEffect(() => {
set_form_data({ ...user_info }); set_form_data({ ...user_info });
}); }, [user_info])
useEffect(() => { useEffect(() => {
const visibles = [ const visibles = [
@@ -129,6 +140,7 @@ const UserInfoCardComponent: React.FC<UserInfoCardProps> = ({
location_picker_visible, location_picker_visible,
ntrp_picker_visible, ntrp_picker_visible,
occupation_picker_visible, occupation_picker_visible,
edit_modal_visible,
]; ];
const allPickersClosed = visibles.every((item) => !item); const allPickersClosed = visibles.every((item) => !item);
// 所有选择器都关闭时,显示 GuideBar否则隐藏 // 所有选择器都关闭时,显示 GuideBar否则隐藏
@@ -138,6 +150,7 @@ const UserInfoCardComponent: React.FC<UserInfoCardProps> = ({
location_picker_visible, location_picker_visible,
ntrp_picker_visible, ntrp_picker_visible,
occupation_picker_visible, occupation_picker_visible,
edit_modal_visible,
]); ]);
// 职业数据 // 职业数据
@@ -237,10 +250,10 @@ const UserInfoCardComponent: React.FC<UserInfoCardProps> = ({
icon: "success", icon: "success",
}); });
} catch (error) { } catch (error) {
console.error("保存失败:", error); console.warn("保存失败:", error);
Taro.showToast({ Taro.showToast({
title: "保存失败", title: getBackendErrorMsg(error, "保存失败"),
icon: "error", icon: "none",
}); });
} }
}; };
@@ -280,10 +293,10 @@ const UserInfoCardComponent: React.FC<UserInfoCardProps> = ({
icon: "success", icon: "success",
}); });
} catch (error) { } catch (error) {
console.error("保存失败:", error); console.warn("保存失败:", error);
Taro.showToast({ Taro.showToast({
title: "保存失败", title: getBackendErrorMsg(error, "保存失败"),
icon: "error", icon: "none",
}); });
} }
}; };
@@ -295,8 +308,8 @@ const UserInfoCardComponent: React.FC<UserInfoCardProps> = ({
// 处理地区选择 // 处理地区选择
const handle_location_change = (e: any) => { const handle_location_change = (e: any) => {
const [country, province, city] = e; const [province, city, district] = e;
handle_field_edit({ country, province, city }); handle_field_edit({ province, city, district });
}; };
// 处理NTRP水平选择 // 处理NTRP水平选择
@@ -307,8 +320,8 @@ const UserInfoCardComponent: React.FC<UserInfoCardProps> = ({
// 处理职业选择 // 处理职业选择
const handle_occupation_change = (e: any) => { const handle_occupation_change = (e: any) => {
const [country, province, city] = e; const [firstVal, secondVal, thirdVal] = e;
handle_field_edit("occupation", `${country} ${province} ${city}`); handle_field_edit("occupation", `${firstVal} ${secondVal} ${thirdVal}`);
}; };
const handle_edit_modal_cancel = () => { const handle_edit_modal_cancel = () => {
// 关闭编辑弹窗时显示 GuideBar // 关闭编辑弹窗时显示 GuideBar
@@ -365,7 +378,6 @@ const UserInfoCardComponent: React.FC<UserInfoCardProps> = ({
urls: [url], urls: [url],
}); });
}; };
return ( return (
<View className="user_info_card"> <View className="user_info_card">
{/* 头像和基本信息 */} {/* 头像和基本信息 */}
@@ -406,11 +418,11 @@ const UserInfoCardComponent: React.FC<UserInfoCardProps> = ({
<View className="stats_section"> <View className="stats_section">
<View <View
className="stats_container" className="stats_container"
// style={{ // style={{
// marginBottom: `${ // marginBottom: `${
// collapseProfile && setMarginBottom ? "16px" : "unset" // collapseProfile && setMarginBottom ? "16px" : "unset"
// }`, // }`,
// }} // }}
> >
<View <View
className="stat_item clickable" className="stat_item clickable"
@@ -565,12 +577,12 @@ const UserInfoCardComponent: React.FC<UserInfoCardProps> = ({
<Text></Text> <Text></Text>
</View> </View>
) : null} ) : null}
{user_info.country || user_info.province || user_info.city ? ( {user_info.province || user_info.city || user_info.district ? (
<View <View
className="tag_item" className="tag_item"
onClick={() => editable && handle_open_edit_modal("location")} onClick={() => editable && handle_open_edit_modal("location")}
> >
<Text className="tag_text">{`${user_info.province}${user_info.city}`}</Text> <Text className="tag_text">{`${user_info.city}${user_info.district}`}</Text>
</View> </View>
) : is_current_user ? ( ) : is_current_user ? (
<View <View
@@ -643,16 +655,16 @@ const UserInfoCardComponent: React.FC<UserInfoCardProps> = ({
<PopupPicker <PopupPicker
showHeader={true} showHeader={true}
title="选择性别" title="选择性别"
options={[ options={
[ [
{ text: "男", value: "0" }, { text: "男", value: "0" },
{ text: "女", value: "1" }, { text: "女", value: "1" },
{ text: "保密", value: "2" }, { text: "保密", value: "2" },
], ]
]} }
visible={gender_picker_visible} visible={gender_picker_visible}
setvisible={setGenderPickerVisible} setvisible={setGenderPickerVisible}
value={form_data.gender === "" ? ["0"] : [form_data.gender]} value={!form_data.gender ? ["0"] : [form_data.gender]}
onChange={handle_gender_change} onChange={handle_gender_change}
/> />
)} )}
@@ -665,8 +677,8 @@ const UserInfoCardComponent: React.FC<UserInfoCardProps> = ({
visible={location_picker_visible} visible={location_picker_visible}
setvisible={setLocationPickerVisible} setvisible={setLocationPickerVisible}
value={ value={
form_data.country form_data.province
? [form_data.country, form_data.province, form_data.city] ? [form_data.province, form_data.city, form_data.district]
: getDefaultOption(cities) : getDefaultOption(cities)
} }
onChange={handle_location_change} onChange={handle_location_change}
@@ -678,15 +690,12 @@ const UserInfoCardComponent: React.FC<UserInfoCardProps> = ({
showHeader={true} showHeader={true}
title="选择 NTRP 自评水平" title="选择 NTRP 自评水平"
ntrpTested={ntrpTested} ntrpTested={ntrpTested}
options={ntrpLevels.map((level) => ({ options={ntrpLevels}
text: level,
value: level,
}))}
type="ntrp" type="ntrp"
img={user_info.avatar_url || ""} img={user_info.avatar_url || ""}
visible={ntrp_picker_visible} visible={ntrp_picker_visible}
setvisible={setNtrpPickerVisible} setvisible={setNtrpPickerVisible}
value={[form_data.ntrp_level || "2.5"]} value={!form_data.ntrp_level ? ["2.5"] : [form_data.ntrp_level]}
onChange={handle_ntrp_level_change} onChange={handle_ntrp_level_change}
/> />
)} )}
@@ -864,9 +873,8 @@ export const GameTabs: React.FC<GameTabsProps> = ({
<Text className="tab_text">{hosted_text}</Text> <Text className="tab_text">{hosted_text}</Text>
</View> </View>
<View <View
className={`tab_item ${ className={`tab_item ${active_tab === "participated" ? "active" : ""
active_tab === "participated" ? "active" : "" }`}
}`}
onClick={() => on_tab_change("participated")} onClick={() => on_tab_change("participated")}
> >
<Text className="tab_text">{participated_text}</Text> <Text className="tab_text">{participated_text}</Text>

View File

@@ -13,7 +13,7 @@ import orderService from "@/services/orderService";
import styles from "./index.module.scss"; import styles from "./index.module.scss";
import closeIcon from "@/static/order/orderListClose.svg"; import closeIcon from "@/static/order/orderListClose.svg";
function genRefundNotice(refund_policy) { function genRefundNotice(refund_policy, order_amount) {
if (refund_policy.length === 0) { if (refund_policy.length === 0) {
return {}; return {};
} }
@@ -23,8 +23,7 @@ function genRefundNotice(refund_policy) {
if (matchPolicyIndex === -1) { if (matchPolicyIndex === -1) {
matchPolicyIndex = refund_policy.length - 1; matchPolicyIndex = refund_policy.length - 1;
} }
const { deadline_formatted, price, refund_rate } = const { time_range, price, refund_rate } = refund_policy[matchPolicyIndex];
refund_policy[matchPolicyIndex];
if (refund_rate === 1) { if (refund_rate === 1) {
return { return {
refundPrice: price, refundPrice: price,
@@ -33,20 +32,18 @@ function genRefundNotice(refund_policy) {
} else if (refund_rate === 0) { } else if (refund_rate === 0) {
return { return {
refundPrice: 0, refundPrice: 0,
notice: `当前退出不可退款,后续流程未明确,@麻真瑜`, notice: `当前退出不可退款,¥${order_amount} 将不予退回`,
}; };
} }
const refundPrice = Number(Math.ceil(price * refund_rate * 100) / 100); // const refundPrice = Number(Math.ceil(price * refund_rate * 100) / 100);
const leftHours = dayjs(deadline_formatted).diff(dayjs(), "hour"); // const leftHours = dayjs(deadline_formatted).diff(dayjs(), "hour");
return { return {
refundPrice, refundPrice: price,
notice: `活动开始已不足${leftHours}h,当前退出需扣除${ notice: `活动开始${time_range},当前退出需扣除${Math.ceil((order_amount - price) * 100) / 100}`,
Math.floor((price - refundPrice) * 100) / 100
}`,
}; };
} }
function renderCancelContent(refund_policy = []) { function renderCancelContent(refund_policy = [], amount) {
const current = dayjs(); const current = dayjs();
const policyList = [ const policyList = [
{ {
@@ -65,7 +62,7 @@ function renderCancelContent(refund_policy = []) {
}), }),
]; ];
const targetIndex = policyList.findIndex((item) => item.beforeCurrent); const targetIndex = policyList.findIndex((item) => item.beforeCurrent);
const { notice } = genRefundNotice(refund_policy); const { notice } = genRefundNotice(refund_policy, amount);
return ( return (
<View className={styles.refundPolicy}> <View className={styles.refundPolicy}>
{/* <View className={styles.moduleTitle}> {/* <View className={styles.moduleTitle}>
@@ -80,7 +77,7 @@ function renderCancelContent(refund_policy = []) {
className={classnames( className={classnames(
styles.policyItem, styles.policyItem,
targetIndex > index && index !== 0 ? styles.pastItem : "", targetIndex > index && index !== 0 ? styles.pastItem : "",
targetIndex === index ? styles.currentItem : "" targetIndex === index ? styles.currentItem : "",
)} )}
> >
<View className={styles.time}> <View className={styles.time}>
@@ -169,7 +166,7 @@ export default forwardRef<RefundRef>(function RefundPopup(_props, ref) {
onClick={onClose} onClick={onClose}
/> />
</View> </View>
{renderCancelContent(refundPolicy)} {renderCancelContent(refundPolicy, orderData.amount)}
<Button className={styles.action} onClick={handleConfirmQuit}> <Button className={styles.action} onClick={handleConfirmQuit}>
退 退
</Button> </Button>

View File

@@ -1,74 +1,76 @@
import { OSS_BASE } from "@/config/api";
export default { export default {
ICON_REMOVE: require('@/static/publishBall/icon-remove.svg'), ICON_REMOVE: require("@/static/publishBall/icon-remove.svg"),
ICON_UPLOAD: require('@/static/publishBall/icon-upload.svg'), ICON_UPLOAD: require("@/static/publishBall/icon-upload.svg"),
ICON_LOCATION: require('@/static/publishBall/icon-location.svg'), ICON_LOCATION: require("@/static/publishBall/icon-location.svg"),
ICON_GAMEPLAY: require('@/static/publishBall/icon-gameplay.svg'), ICON_GAMEPLAY: require("@/static/publishBall/icon-gameplay.svg"),
ICON_PERSONAL: require('@/static/publishBall/icon-personal.svg'), ICON_PERSONAL: require("@/static/publishBall/icon-personal.svg"),
ICON_CHANGDA: require('@/static/publishBall/icon-changda.svg'), ICON_CHANGDA: require("@/static/publishBall/icon-changda.svg"),
ICON_COST: require('@/static/publishBall/icon-cost.svg'), ICON_COST: require("@/static/publishBall/icon-cost.svg"),
ICON_TIPS: require('@/static/publishBall/icon-tips.svg'), ICON_TIPS: require("@/static/publishBall/icon-tips.svg"),
ICON_ARROW_RIGHT: require('@/static/publishBall/icon-arrow-right.svg'), ICON_ARROW_RIGHT: require("@/static/publishBall/icon-arrow-right.svg"),
ICON_FILTER: require('@/static/list/icon-filter.svg'), ICON_FILTER: require("@/static/list/icon-filter.svg"),
ICON_FILTER_SELECTED: require('@/static/list/icon-filter-selected.svg'), ICON_FILTER_SELECTED: require("@/static/list/icon-filter-selected.svg"),
ICON_SEARCH: require('@/static/list/icon-search.svg'), ICON_SEARCH: require("@/static/list/icon-search.svg"),
ICON_PLAY: require('@/static/list/icon-play.svg'), ICON_PLAY: require("@/static/list/icon-play.svg"),
ICON_SITE: require('@/static/list/icon-site.svg'), ICON_SITE: require("@/static/list/icon-site.svg"),
ICON_ARROW_DOWN: require('@/static/list/icon-arrow-down.svg'), ICON_ARROW_DOWN: require("@/static/list/icon-arrow-down.svg"),
ICON_MENU_ITEM_SELECTED: require('@/static/list/icon-menu-item-selected.svg'), ICON_MENU_ITEM_SELECTED: require("@/static/list/icon-menu-item-selected.svg"),
ICON_ARROW_DOWN_WHITE: require('@/static/list/icon-arrow-down-white.svg'), ICON_ARROW_DOWN_WHITE: require("@/static/list/icon-arrow-down-white.svg"),
ICON_LIST_RIGHT_ARROW: require('@/static/list/icon-list-right-arrow.svg'), ICON_LIST_RIGHT_ARROW: require("@/static/list/icon-list-right-arrow.svg"),
ICON_ARROW_LEFT: require('@/static/detail/icon-arrow-left.svg'), ICON_ARROW_LEFT: require("@/static/detail/icon-arrow-left.svg"),
ICON_LOGO_GO: require('@/static/detail/icon-logo-go.svg'), ICON_LOGO_GO: require("@/static/detail/icon-logo-go.svg"),
ICON_MAP: require('@/static/publishBall/icon-map.svg'), ICON_MAP: require("@/static/publishBall/icon-map.svg"),
ICON_STADIUM: require('@/static/publishBall/icon-stadium.svg'), ICON_STADIUM: require("@/static/publishBall/icon-stadium.svg"),
ICON_ARRORW_SMALL: require('@/static/publishBall/icon-arrow-small.svg'), ICON_ARRORW_SMALL: require("@/static/publishBall/icon-arrow-small.svg"),
ICON_MAP_SEARCH: require('@/static/publishBall/icon-map-search.svg'), ICON_MAP_SEARCH: require("@/static/publishBall/icon-map-search.svg"),
ICON_HEART_CIRCLE: require('@/static/publishBall/icon-heartcircle.png'), ICON_HEART_CIRCLE: require("@/static/publishBall/icon-heartcircle.png"),
ICON_ADD: require('@/static/publishBall/icon-add.svg'), ICON_ADD: require("@/static/publishBall/icon-add.svg"),
ICON_COPY: require('@/static/publishBall/icon-arrow-right.svg'), ICON_COPY: require("@/static/publishBall/icon-arrow-right.svg"),
ICON_DELETE: require('@/static/publishBall/icon-delete.svg'), ICON_DELETE: require("@/static/publishBall/icon-delete.svg"),
ICON_RIGHT_MAX: require('@/static/publishBall/icon-right-max.svg'), ICON_RIGHT_MAX: require("@/static/publishBall/icon-right-max.svg"),
ICON_PLUS: require('@/static/publishBall/icon-plus.svg'), ICON_PLUS: require("@/static/publishBall/icon-plus.svg"),
ICON_GROUP: require('@/static/publishBall/icon-group.svg'), ICON_GROUP: require("@/static/publishBall/icon-group.svg"),
ICON_PERSON: require('@/static/publishBall/icon-person.svg'), ICON_PERSON: require("@/static/publishBall/icon-person.svg"),
ICON_PUBLISH: require('@/static/publishBall/icon-publish.png'), ICON_PUBLISH: require("@/static/publishBall/icon-publish.png"),
ICON_CIRCLE_UNSELECT: require('@/static/publishBall/icon-circle-unselect.svg'), ICON_CIRCLE_UNSELECT: require("@/static/publishBall/icon-circle-unselect.svg"),
ICON_CIRCLE_SELECT: require('@/static/publishBall/icon-circle-select-ring.svg'), ICON_CIRCLE_SELECT: require("@/static/publishBall/icon-circle-select-ring.svg"),
ICON_CIRCLE_SELECT_ARROW: require('@/static/publishBall/icon-circle-select-arrow.svg'), ICON_CIRCLE_SELECT_ARROW: require("@/static/publishBall/icon-circle-select-arrow.svg"),
ICON_LOGO: require('@/static/logo.svg'), ICON_LOGO: require("@/static/logo.svg"),
ICON_CHANGE: require('@/static/list/icon-change.svg'), ICON_CHANGE: require("@/static/list/icon-change.svg"),
ICON_DETAIL_MAP: require('@/static/detail/icon-map.svg'), ICON_DETAIL_MAP: require("@/static/detail/icon-map.svg"),
ICON_DETAIL_ARROW_RIGHT: require('@/static/detail/icon-arrow-right.svg'), ICON_DETAIL_ARROW_RIGHT: require("@/static/detail/icon-arrow-right.svg"),
ICON_DETAIL_NOTICE: require('@/static/detail/icon-notice.svg'), ICON_DETAIL_NOTICE: require("@/static/detail/icon-notice.svg"),
ICON_DETAIL_APPLICATION_ADD: require('@/static/detail/icon-application-add.svg'), ICON_DETAIL_APPLICATION_ADD: require("@/static/detail/icon-application-add.svg"),
ICON_DETAIL_COMMENT: require('@/static/detail/icon-comment.svg'), ICON_DETAIL_COMMENT: require("@/static/detail/icon-comment.svg"),
ICON_DETAIL_COMMENT_LIGHT: require('@/static/detail/icon-comment-light.svg'), ICON_DETAIL_COMMENT_LIGHT: require("@/static/detail/icon-comment-light.svg"),
ICON_DETAIL_SHARE: require('@/static/detail/icon-share-light.svg'), ICON_DETAIL_SHARE: require("@/static/detail/icon-share-light.svg"),
ICON_GUIDE_BAR_PUBLISH: require('@/static/common/guide-bar-publish.svg'), ICON_GUIDE_BAR_PUBLISH: require("@/static/common/guide-bar-publish.svg"),
ICON_NAVIGATOR_BACK: require('@/static/common/navigator-back.svg'), ICON_NAVIGATOR_BACK: require("@/static/common/navigator-back.svg"),
ICON_LIST_PLAYING_GAME: require('@/static/list/icon-paying-game.svg'), ICON_LIST_PLAYING_GAME: require("@/static/list/icon-paying-game.svg"),
ICON_LIST_LOAD_ERROR: require('@/static/list/icon-load-error.svg'), ICON_LIST_LOAD_ERROR: require("@/static/list/icon-load-error.svg"),
ICON_LIST_RELOAD: require('@/static/list/icon-reload.svg'), ICON_LIST_RELOAD: require("@/static/list/icon-reload.svg"),
ICON_LIST_EMPTY: require('@/static/emptyStatus/publish-empty.png'), ICON_LIST_EMPTY: require("@/static/emptyStatus/publish-empty.png"),
ICON_LIST_EMPTY_CARD: require('@/static/emptyStatus/publish-empty-card.png'), ICON_LIST_EMPTY_CARD: `${OSS_BASE}/front/ball/images/publish-empty-card.png`,
ICON_LIST_SEARCH_SEARCH: require('@/static/search/icon-search.svg'), ICON_LIST_SEARCH_SEARCH: require("@/static/search/icon-search.svg"),
ICON_LIST_SEARCH_BACK: require('@/static/search/icon-back.svg'), ICON_LIST_SEARCH_BACK: require("@/static/search/icon-back.svg"),
ICON_LIST_SEARCH_CLEAR: require('@/static/search/icon-search-clear.svg'), ICON_LIST_SEARCH_CLEAR: require("@/static/search/icon-search-clear.svg"),
ICON_LIST_SEARCH_CLEAR_HISTORY: require('@/static/search/icon-clear-history.svg'), ICON_LIST_SEARCH_CLEAR_HISTORY: require("@/static/search/icon-clear-history.svg"),
ICON_LIST_SEARCH_SUGGESTION: require('@/static/search/icon-search-suggestion.svg'), ICON_LIST_SEARCH_SUGGESTION: require("@/static/search/icon-search-suggestion.svg"),
ICON_LIST_INPUT_LOGO: require('@/static/list/icon-input-logo.svg'), ICON_LIST_INPUT_LOGO: require("@/static/list/icon-input-logo.svg"),
ICON_IMPORTANT_BTN: require('@/static/publishBall/icon-important-btn.svg'), ICON_IMPORTANT_BTN: require("@/static/publishBall/icon-important-btn.svg"),
ICON_IMPORTANT_BLACK: require('@/static/publishBall/icon-important-black.svg'), ICON_IMPORTANT_BLACK: require("@/static/publishBall/icon-important-black.svg"),
ICON_ARROW_RIGHT_WHITE: require('@/static/publishBall/icon-arrow-right-white.svg'), ICON_ARROW_RIGHT_WHITE: require("@/static/publishBall/icon-arrow-right-white.svg"),
ICON_ARROW_RIGHT_BLACK: require('@/static/publishBall/icon-arrow-right-black.svg'), ICON_ARROW_RIGHT_BLACK: require("@/static/publishBall/icon-arrow-right-black.svg"),
ICON_EXAMINATION: require('@/static/userInfo/examination.svg'), ICON_EXAMINATION: require("@/static/userInfo/examination.svg"),
ICON_ARROW_GREEN: require('@/static/userInfo/arrow-green.svg'), ICON_ARROW_GREEN: require("@/static/userInfo/arrow-green.svg"),
ICON_COPY: require('@/static/publishBall/icon-copy.svg'), ICON_COPY: require("@/static/publishBall/icon-copy.svg"),
ICON_UPLOAD_IMG: require('@/static/publishBall/icon-upload-img.svg'), ICON_UPLOAD_IMG: require("@/static/publishBall/icon-upload-img.svg"),
ICON_UPLOAD_SUCCESS: require('@/static/publishBall/icon-upload-success.svg'), ICON_UPLOAD_SUCCESS: require("@/static/publishBall/icon-upload-success.svg"),
ICON_CLOSE: require('@/static/publishBall/icon-close.svg'), ICON_CLOSE: require("@/static/publishBall/icon-close.svg"),
ICON_LIST_NTPR: require('@/static/list/ntpr.svg'), ICON_LIST_NTPR: require("@/static/list/ntpr.svg"),
ICON_LIST_CHANGDA: require('@/static/list/icon-changda.svg'), ICON_LIST_CHANGDA: require("@/static/list/icon-changda.svg"),
ICON_LIST_CHANGDA_QIuju: require('@/static/list/changdaqiuju.png'), ICON_LIST_CHANGDA_QIuju: require("@/static/list/changdaqiuju.png"),
ICON_RELOCATE: require('@/static/list/icon-relocate.svg'), ICON_RELOCATE: require("@/static/list/icon-relocate.svg"),
} ICON_GAME_PLAY: require("@/static/list/icon_game_type.svg"),
};

View File

@@ -4,7 +4,11 @@ import ListLoadError from "@/components/ListLoadError";
import ListCardSkeleton from "@/components/ListCardSkeleton"; import ListCardSkeleton from "@/components/ListCardSkeleton";
import { useReachBottom } from "@tarojs/taro"; import { useReachBottom } from "@tarojs/taro";
import Taro from "@tarojs/taro"; import Taro from "@tarojs/taro";
import { useUserInfo, useUserActions, useLastTestResult } from "@/store/userStore"; import {
useUserInfo,
useUserActions,
useLastTestResult,
} from "@/store/userStore";
import { NTRPTestEntryCard } from "@/components"; import { NTRPTestEntryCard } from "@/components";
import { EvaluateScene } from "@/store/evaluateStore"; import { EvaluateScene } from "@/store/evaluateStore";
import { waitForAuthInit } from "@/utils/authInit"; import { waitForAuthInit } from "@/utils/authInit";
@@ -46,7 +50,11 @@ const ListContainer = (props) => {
const { fetchUserInfo, fetchLastTestResult } = useUserActions(); const { fetchUserInfo, fetchLastTestResult } = useUserActions();
// 使用全局状态中的测试结果,避免重复调用接口 // 使用全局状态中的测试结果,避免重复调用接口
const lastTestResult = useLastTestResult(); const lastTestResult = useLastTestResult();
const { bannerListImage, bannerDetailImage, bannerListIndex = 0 } = useDictionaryStore((s) => s.bannerDict) || {}; const {
bannerListImage,
bannerDetailImage,
bannerListIndex = 0,
} = useDictionaryStore((s) => s.bannerDict) || {};
useReachBottom(() => { useReachBottom(() => {
// 加载更多方法 // 加载更多方法
if (loading) { if (loading) {
@@ -102,7 +110,7 @@ const ListContainer = (props) => {
// 先等待静默登录完成 // 先等待静默登录完成
await waitForAuthInit(); await waitForAuthInit();
// 然后再获取用户信息 // 然后再获取用户信息
const userInfoId = userInfo && 'id' in userInfo ? userInfo.id : null; const userInfoId = userInfo && "id" in userInfo ? userInfo.id : null;
if (!userInfoId) { if (!userInfoId) {
await fetchUserInfo(); await fetchUserInfo();
return; // 等待下一次 useEffect 触发(此时 userInfo.id 已有值) return; // 等待下一次 useEffect 触发(此时 userInfo.id 已有值)
@@ -113,7 +121,13 @@ const ListContainer = (props) => {
} }
}; };
init(); init();
}, [evaluateFlag, enableHomeCards, userInfo, lastTestResult, fetchLastTestResult]); }, [
evaluateFlag,
enableHomeCards,
userInfo,
lastTestResult,
fetchLastTestResult,
]);
// 从全局状态中获取测试状态 // 从全局状态中获取测试状态
const hasTestInLastMonth = lastTestResult?.has_test_in_last_month || false; const hasTestInLastMonth = lastTestResult?.has_test_in_last_month || false;
@@ -132,46 +146,60 @@ const ListContainer = (props) => {
); );
}; };
// 插入 banner 卡片 // showNumber 为 0 表示尚未同步,不参与截断;截断时只限制「数据条数」,插卡不占数据条数
const shouldLimitByShowNumber = showNumber > 0;
// 插入 banner 卡片(在 bannerListIndex 位置插入,不替换数据)
function insertBannerCard(list) { function insertBannerCard(list) {
if (!bannerListImage) return list; if (!bannerListImage) return list;
if (!list || !Array.isArray(list)) return list ?? []; if (!list || !Array.isArray(list)) {
list = [];
}
const idx = Number(bannerListIndex);
return [ return [
...list.slice(0, Number(bannerListIndex)), ...list.slice(0, idx),
{ type: "banner", banner_image_url: bannerListImage, banner_detail_url: bannerDetailImage }, {
...list.slice(Number(bannerListIndex)) type: "banner",
banner_image_url: bannerListImage,
banner_detail_url: bannerDetailImage,
},
...list.slice(idx),
]; ];
} }
// 对于没有ntrp等级的用户每个月展示一次, 插在第二个位置后面 // 对于没有 ntrp 等级的用户每个月展示一次插在第 2 条数据后面;插卡是插入不替换,保留全部 showNumber 条数据
// insertBannerCard 需在最后统一执行,否则前面分支直接 return 时 banner 不会被插入
function insertEvaluateCard(list) { function insertEvaluateCard(list) {
let result: any[]; if (!list || !Array.isArray(list)) return insertBannerCard(list ?? []);
if (!evaluateFlag) { const limitedList = shouldLimitByShowNumber
result = showNumber !== undefined ? list.slice(0, showNumber) : list; ? list.slice(0, showNumber)
} else if (!list || list.length === 0) { : list;
result = list;
} else if (hasTestInLastMonth) { if (!evaluateFlag || hasTestInLastMonth) {
result = showNumber !== undefined ? list.slice(0, showNumber) : list; return insertBannerCard(limitedList);
} else if (list.length <= 2) {
result = [...list, { type: "evaluateCard" }];
} else {
const [item1, item2, ...rest] = list;
result = [
item1,
item2,
{ type: "evaluateCard" },
...(showNumber !== undefined ? rest.slice(0, showNumber - 3) : rest),
];
} }
if (limitedList.length <= 2) {
return insertBannerCard([...limitedList, { type: "evaluateCard" }]);
}
const [item1, item2, ...rest] = limitedList;
const result = [item1, item2, { type: "evaluateCard" }, ...rest];
return insertBannerCard(result); return insertBannerCard(result);
} }
const memoizedList = useMemo( const memoizedList = useMemo(
() => (enableHomeCards ? insertEvaluateCard(data) : data), () => (enableHomeCards ? insertEvaluateCard(data) : data),
[enableHomeCards, evaluateFlag, data, hasTestInLastMonth, showNumber, bannerListImage, bannerDetailImage, bannerListIndex] [
enableHomeCards,
evaluateFlag,
data,
hasTestInLastMonth,
showNumber,
bannerListImage,
bannerDetailImage,
bannerListIndex,
]
); );
// 渲染 banner 卡片 // 渲染 banner 卡片
@@ -186,7 +214,9 @@ const ListContainer = (props) => {
const target = item.banner_detail_url; const target = item.banner_detail_url;
if (target) { if (target) {
(Taro as any).navigateTo({ (Taro as any).navigateTo({
url: `/other_pages/bannerDetail/index?img=${encodeURIComponent(target)}`, url: `/other_pages/bannerDetail/index?img=${encodeURIComponent(
target
)}`,
}); });
} }
}} }}
@@ -199,15 +229,16 @@ const ListContainer = (props) => {
backgroundPosition: "center", backgroundPosition: "center",
backgroundRepeat: "no-repeat", backgroundRepeat: "no-repeat",
}} }}
> ></View>
</View>
); );
}; };
const showNoData = isShowNoData && !loading && memoizedList?.length === 0;
// 渲染列表 // 渲染列表
const renderList = () => { const renderList = () => {
// 请求数据为空 // 请求数据为空
if (isShowNoData) { if (showNoData) {
return ( return (
<ListLoadError <ListLoadError
reload={reload} reload={reload}
@@ -232,7 +263,10 @@ const ListContainer = (props) => {
} }
if (enableHomeCards && match?.type === "evaluateCard") { if (enableHomeCards && match?.type === "evaluateCard") {
return ( return (
<NTRPTestEntryCard key={`evaluate-${index}`} type={EvaluateScene.list} /> <NTRPTestEntryCard
key={`evaluate-${index}`}
type={EvaluateScene.list}
/>
); );
} }
return <ListCard key={match?.id || index} {...match} />; return <ListCard key={match?.id || index} {...match} />;

View File

@@ -48,8 +48,8 @@ function genRecommendGames(games, location, avatar) {
formatNtrpDisplay(skill_level_max) || "-" formatNtrpDisplay(skill_level_max) || "-"
}` }`
: skill_level_min === "1" : skill_level_min === "1"
? "无要求" ? "无要求"
: `${formatNtrpDisplay(skill_level_min)}以上`, : `${formatNtrpDisplay(skill_level_min)}以上`,
playType: play_type, playType: play_type,
}; };
}); });
@@ -220,7 +220,9 @@ export default function OrganizerInfo(props) {
> >
<Text>{game.venue}</Text> <Text>{game.venue}</Text>
<Text>·</Text> <Text>·</Text>
<Text>{game.venueType}</Text> <Text style={{ whiteSpace: "nowrap" }}>
{game.venueType}
</Text>
<Text>·</Text> <Text>·</Text>
<Text>{game.distance}</Text> <Text>{game.distance}</Text>
</View> </View>
@@ -247,7 +249,7 @@ export default function OrganizerInfo(props) {
styles[ styles[
"recommend-games-list-item-addon-message-applications" "recommend-games-list-item-addon-message-applications"
], ],
styles.joinMsg styles.joinMsg,
)} )}
> >
<Text></Text> <Text></Text>

View File

@@ -25,9 +25,10 @@ dayjs.locale("zh-cn");
// 分享弹窗 // 分享弹窗
export default forwardRef(({ id, from, detail, userInfo }, ref) => { export default forwardRef(({ id, from, detail, userInfo }, ref) => {
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const [publishFlag, setPublishFlag] = useState(false);
const [shareImageUrl, setShareImageUrl] = useState(""); const [shareImageUrl, setShareImageUrl] = useState("");
const { fetchUserInfo } = useUserActions(); const { fetchUserInfo } = useUserActions();
const publishFlag = from === "publish";
// const posterRef = useRef(); // const posterRef = useRef();
const { max_participants, participant_count } = detail || {}; const { max_participants, participant_count } = detail || {};
@@ -57,18 +58,20 @@ export default forwardRef(({ id, from, detail, userInfo }, ref) => {
} }
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
show: async (publish_flag = false) => { show: async () => {
setPublishFlag(publish_flag);
if (publish_flag) {
try {
const url = await generateShareImageUrl();
setShareImageUrl(url);
} catch (e) {}
}
setVisible(true); setVisible(true);
}, },
})); }));
useEffect(() => {
if (from === "publish") {
generateShareImageUrl().then((url) => {
setShareImageUrl(url);
setVisible(true);
});
}
}, [from]);
async function generateShareImageUrl() { async function generateShareImageUrl() {
const { const {
play_type, play_type,
@@ -106,7 +109,7 @@ export default forwardRef(({ id, from, detail, userInfo }, ref) => {
// console.log(res, "res"); // console.log(res, "res");
return { return {
title: detail.title, title: detail.title,
imageUrl: url || "https://img.yzcdn.cn/vant/cat.jpeg", imageUrl: url,
path: `/game_pages/detail/index?id=${id}&from=share`, path: `/game_pages/detail/index?id=${id}&from=share`,
}; };
}); });
@@ -142,6 +145,7 @@ export default forwardRef(({ id, from, detail, userInfo }, ref) => {
const qrCodeUrl = qrCodeUrlRes.data.ossPath; const qrCodeUrl = qrCodeUrlRes.data.ossPath;
await delay(100); await delay(100);
// Taro.showLoading({ title: "生成中..." }); // Taro.showLoading({ title: "生成中..." });
console.log('url', qrCodeUrl)
const url = await generatePosterImage({ const url = await generatePosterImage({
playType: play_type, playType: play_type,
ntrp: `NTRP ${genNTRPRequirementText(skill_level_min, skill_level_max)}`, ntrp: `NTRP ${genNTRPRequirementText(skill_level_min, skill_level_max)}`,
@@ -157,6 +161,8 @@ export default forwardRef(({ id, from, detail, userInfo }, ref) => {
time: `${startTime.format("ah")}${gameLength}`, time: `${startTime.format("ah")}${gameLength}`,
qrCodeUrl, qrCodeUrl,
}); });
console.log('urlend', url)
// Taro.hideLoading(); // Taro.hideLoading();
Taro.showShareImageMenu({ Taro.showShareImageMenu({
path: url, path: url,
@@ -183,7 +189,6 @@ export default forwardRef(({ id, from, detail, userInfo }, ref) => {
function onClose() { function onClose() {
setVisible(false); setVisible(false);
setPublishFlag(false);
} }
return ( return (

View File

@@ -54,12 +54,6 @@ function Index() {
await waitForAuthInit(); await waitForAuthInit();
// 然后再获取用户信息 // 然后再获取用户信息
await fetchUserInfo(); await fetchUserInfo();
await delay(1000);
if (from === "publish") {
handleShare(true);
}
}; };
init(); init();
}, []); }, []);
@@ -92,7 +86,7 @@ function Index() {
// handleShare(true); // handleShare(true);
// } // }
} catch (error) { } catch (error) {
console.error("用户位置更新失败", error); console.warn("用户位置更新失败", error);
} }
}; };
@@ -126,8 +120,12 @@ function Index() {
} }
} }
function handleShare(flag = false) { function handleShare() {
sharePopupRef.current.show(flag); if (!detail.id) {
toast("球局未加载完成,请稍后再试");
return false;
}
sharePopupRef.current.show();
} }
const handleJoinGame = async () => { const handleJoinGame = async () => {
@@ -293,13 +291,15 @@ function Index() {
currentUserInfo={myInfo} currentUserInfo={myInfo}
/> />
{/* share popup */} {/* share popup */}
<SharePopup {detail.id && myInfo.id && (
ref={sharePopupRef} <SharePopup
id={id as string} ref={sharePopupRef}
from={from as string} id={id as string}
detail={detail} from={from as string}
userInfo={myInfo} detail={detail}
/> userInfo={myInfo}
/>
)}
</View> </View>
</ScrollView> </ScrollView>
); );

View File

@@ -17,14 +17,14 @@ const HomePage: React.FC = () => {
if (loginResult.success) { if (loginResult.success) {
// 静默登录成功,获取用户信息 // 静默登录成功,获取用户信息
fetchUserInfo().catch((error) => { fetchUserInfo().catch((error) => {
console.error("获取用户信息失败:", error); console.warn("获取用户信息失败:", error);
}); });
checkNicknameChangeStatus().catch((error) => { checkNicknameChangeStatus().catch((error) => {
console.error("检查昵称变更状态失败:", error); console.warn("检查昵称变更状态失败:", error);
}); });
} }
} catch (error) { } catch (error) {
console.error("静默登录失败:", error); console.warn("静默登录失败:", error);
// 静默登录失败不影响使用 // 静默登录失败不影响使用
} }

View File

@@ -1,6 +1,7 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { View, Text, Button, Image } from "@tarojs/components"; import { View, Text, Button, Image } from "@tarojs/components";
import Taro, { useRouter } from "@tarojs/taro"; import Taro, { useRouter } from "@tarojs/taro";
import { GeneralNavbar } from "@/components";
import { import {
wechat_auth_login, wechat_auth_login,
save_login_state, save_login_state,
@@ -171,6 +172,8 @@ const LoginPage: React.FC = () => {
<View className="bg_overlay"></View> <View className="bg_overlay"></View>
</View> </View>
<GeneralNavbar title="" showBack={true} showAvatar={false} onBack={handle_return_home} />
{/* 主要内容 */} {/* 主要内容 */}
<View className="login_main_content"> <View className="login_main_content">
{/* 品牌区域 */} {/* 品牌区域 */}
@@ -216,9 +219,9 @@ const LoginPage: React.FC = () => {
<Text className="button_text"></Text> <Text className="button_text"></Text>
</Button> </Button>
<View className="return_home_button link_button" onClick={handle_return_home}> {/* <View className="return_home_button link_button" onClick={handle_return_home}>
<Text className="button_text">返回首页</Text> <Text className="button_text">返回首页</Text>
</View> </View> */}
{/* 用户协议复选框 */} {/* 用户协议复选框 */}
<View className="terms_checkbox_section"> <View className="terms_checkbox_section">

View File

@@ -139,7 +139,7 @@ const VerificationPage: React.FC = () => {
}); });
} }
} catch (error) { } catch (error) {
console.error("发送验证码异常:", error); console.warn("发送验证码异常:", error);
Taro.showToast({ Taro.showToast({
title: "发送失败,请重试", title: "发送失败,请重试",
icon: "none", icon: "none",

View File

@@ -67,6 +67,8 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
} = store; } = store;
const supportedCitiesList = useDictionaryStore((s) => s.getDictionaryValue('supported_cities', ['上海市'])) || []; const supportedCitiesList = useDictionaryStore((s) => s.getDictionaryValue('supported_cities', ['上海市'])) || [];
// 首页是否展示二维码,由 getDictionaryManyKey 的 show_home_qrcode 控制,默认 true 保持原样
const showHomeQrcode = useDictionaryStore((s) => s.getDictionaryValue('show_home_qrcode', true));
const { const {
isShowFilterPopup, isShowFilterPopup,
@@ -227,18 +229,16 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
useEffect(() => { useEffect(() => {
// 分批异步执行初始化操作,避免阻塞首屏渲染 // 分批异步执行初始化操作,避免阻塞首屏渲染
// 1. 立即执行:获取城市、二维码和行政区列表(轻量操作)
getCities(); getCities();
getCityQrCode(); if (showHomeQrcode) getCityQrCode();
getDistricts(); // 新增:获取行政区列表 getDistricts();
// 只有当页面激活时才加载位置和列表数据
if (isActive) { if (isActive) {
getLocation().catch((error) => { getLocation().catch((error) => {
console.error('获取位置信息失败:', error); console.warn('获取位置信息失败:', error);
}); });
} }
}, [isActive]); }, [isActive, showHomeQrcode]);
// 记录上一次的城市,用于检测城市变化 // 记录上一次的城市,用于检测城市变化
const prevAreaRef = useRef<[string, string] | null>(null); const prevAreaRef = useRef<[string, string] | null>(null);
@@ -309,7 +309,7 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
lastLoadedAreaRef.current = [...currentArea] as [string, string]; lastLoadedAreaRef.current = [...currentArea] as [string, string];
} }
} catch (error) { } catch (error) {
console.error("重新加载数据失败:", error); console.warn("重新加载数据失败:", error);
} }
}, delayMs); }, delayMs);
prevIsActiveRef.current = isActive; prevIsActiveRef.current = isActive;
@@ -375,7 +375,7 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
await updateUserLocation(location.latitude, location.longitude, isFirstCall); await updateUserLocation(location.latitude, location.longitude, isFirstCall);
hasUpdatedLocationRef.current = true; hasUpdatedLocationRef.current = true;
} catch (error) { } catch (error) {
console.error("更新用户位置失败:", error); console.warn("更新用户位置失败:", error);
} }
} }
// 先调用列表接口 // 先调用列表接口
@@ -462,7 +462,7 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
await getMatchesData(); await getMatchesData();
await fetchGetGamesCount(); await fetchGetGamesCount();
} catch (error) { } catch (error) {
console.error("刷新列表失败:", error); console.warn("刷新列表失败:", error);
} }
}; };
@@ -477,7 +477,7 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
const { fetchDictionary } = useDictionaryStore.getState(); const { fetchDictionary } = useDictionaryStore.getState();
await fetchDictionary(); await fetchDictionary();
} catch (error) { } catch (error) {
console.error("初始化字典数据失败:", error); console.warn("初始化字典数据失败:", error);
} }
}; };
@@ -537,7 +537,13 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
return ( return (
<> <>
{shouldShowNoGames ? ( {shouldShowNoGames ? (
renderCityQrcode() showHomeQrcode ? (
renderCityQrcode()
) : (
<View className={styles.cqContainer}>
<Text></Text>
</View>
)
) : ( ) : (
<View ref={scrollContextRef}> <View ref={scrollContextRef}>
<View className={styles.listPage} style={{ paddingTop: totalHeight }}> <View className={styles.listPage} style={{ paddingTop: totalHeight }}>
@@ -610,7 +616,7 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
try { try {
await loadMoreMatches(); await loadMoreMatches();
} catch (error) { } catch (error) {
console.error("加载更多失败:", error); console.warn("加载更多失败:", error);
} finally { } finally {
loadingMoreRef.current = false; loadingMoreRef.current = false;
} }

View File

@@ -11,12 +11,16 @@ import { EvaluateScene } from "@/store/evaluateStore";
import { useUserInfo, useUserActions } from "@/store/userStore"; import { useUserInfo, useUserActions } from "@/store/userStore";
import { usePickerOption } from "@/store/pickerOptionsStore"; import { usePickerOption } from "@/store/pickerOptionsStore";
import { useGlobalState } from "@/store/global"; import { useGlobalState } from "@/store/global";
import { useListState } from "@/store/listStore";
import { useDictionaryStore } from "@/store/dictionaryStore";
interface MyselfPageContentProps { interface MyselfPageContentProps {
isActive?: boolean; isActive?: boolean;
} }
const MyselfPageContent: React.FC<MyselfPageContentProps> = ({ isActive = true }) => { const MyselfPageContent: React.FC<MyselfPageContentProps> = ({
isActive = true,
}) => {
const pickerOption = usePickerOption(); const pickerOption = usePickerOption();
const { statusNavbarHeightInfo } = useGlobalState() || {}; const { statusNavbarHeightInfo } = useGlobalState() || {};
const { totalHeight = 98 } = statusNavbarHeightInfo || {}; const { totalHeight = 98 } = statusNavbarHeightInfo || {};
@@ -39,6 +43,10 @@ const MyselfPageContent: React.FC<MyselfPageContentProps> = ({ isActive = true }
const [collapseProfile, setCollapseProfile] = useState(false); const [collapseProfile, setCollapseProfile] = useState(false);
const [refreshing, setRefreshing] = useState(false); const [refreshing, setRefreshing] = useState(false);
const { area } = useListState();
const supportedCitiesList =
useDictionaryStore((s) => s.getDictionaryValue("supported_cities")) || [];
useEffect(() => { useEffect(() => {
pickerOption.getCities(); pickerOption.getCities();
pickerOption.getProfessions(); pickerOption.getProfessions();
@@ -65,20 +73,21 @@ const MyselfPageContent: React.FC<MyselfPageContentProps> = ({ isActive = true }
game_records: TennisMatch[] game_records: TennisMatch[]
): { notEndGames: TennisMatch[]; finishedGames: TennisMatch[] } => { ): { notEndGames: TennisMatch[]; finishedGames: TennisMatch[] } => {
const now = new Date().getTime(); const now = new Date().getTime();
return game_records.reduce(
(result, cur) => { // 使用for
let { end_time } = cur; const notEndGames: TennisMatch[] = [];
end_time = end_time.replace(/\s/, "T"); const finishedGames: TennisMatch[] = [];
new Date(end_time).getTime() > now for (const game of game_records) {
? result.notEndGames.push(cur) const { end_time } = game;
: result.finishedGames.push(cur); const end_time_str = end_time.replace(/\s/, "T");
return result; new Date(end_time_str).getTime() > now
}, ? notEndGames.push(game)
{ : finishedGames.unshift(game);
notEndGames: [] as TennisMatch[], }
finishedGames: [] as TennisMatch[],
} console.log("notEndGames", notEndGames);
);
return { notEndGames, finishedGames };
}, },
[] []
); );
@@ -95,6 +104,7 @@ const MyselfPageContent: React.FC<MyselfPageContentProps> = ({ isActive = true }
} else { } else {
games_data = await UserService.get_participated_games(user_info.id); games_data = await UserService.get_participated_games(user_info.id);
} }
const sorted_games = games_data.sort((a, b) => { const sorted_games = games_data.sort((a, b) => {
return ( return (
new Date(a.original_start_time.replace(/\s/, "T")).getTime() - new Date(a.original_start_time.replace(/\s/, "T")).getTime() -
@@ -102,10 +112,12 @@ const MyselfPageContent: React.FC<MyselfPageContentProps> = ({ isActive = true }
); );
}); });
const { notEndGames, finishedGames } = classifyGameRecords(sorted_games); const { notEndGames, finishedGames } = classifyGameRecords(sorted_games);
console.log("notEndGames", notEndGames);
set_game_records(notEndGames); set_game_records(notEndGames);
setEndedGameRecords(finishedGames); setEndedGameRecords(finishedGames);
} catch (error) { } catch (error) {
console.error("加载球局数据失败:", error); console.warn("加载球局数据失败:", error);
} }
}, [active_tab, user_info, classifyGameRecords]); }, [active_tab, user_info, classifyGameRecords]);
@@ -138,7 +150,7 @@ const MyselfPageContent: React.FC<MyselfPageContentProps> = ({ isActive = true }
duration: 1500, duration: 1500,
}); });
} catch (error) { } catch (error) {
console.error("关注操作失败:", error); console.warn("关注操作失败:", error);
(Taro as any).showToast({ (Taro as any).showToast({
title: "操作失败,请重试", title: "操作失败,请重试",
icon: "error", icon: "error",
@@ -148,6 +160,16 @@ const MyselfPageContent: React.FC<MyselfPageContentProps> = ({ isActive = true }
}; };
const goPublish = () => { const goPublish = () => {
const [_, address] = area;
if (!supportedCitiesList.includes(address)) {
(Taro as any).showModal({
title: "提示",
content: "该城市尚未开放,您可加入社群或切换城市",
showCancel: false,
confirmText: "知道了",
});
return;
}
(Taro as any).navigateTo({ (Taro as any).navigateTo({
url: "/publish_pages/publishBall/index", url: "/publish_pages/publishBall/index",
}); });
@@ -176,7 +198,7 @@ const MyselfPageContent: React.FC<MyselfPageContentProps> = ({ isActive = true }
try { try {
await Promise.all([fetchUserInfo(), load_game_data()]); await Promise.all([fetchUserInfo(), load_game_data()]);
} catch (error) { } catch (error) {
console.error("刷新失败:", error); console.warn("刷新失败:", error);
(Taro as any).showToast({ (Taro as any).showToast({
title: "刷新失败,请重试", title: "刷新失败,请重试",
icon: "none", icon: "none",
@@ -288,9 +310,8 @@ const MyselfPageContent: React.FC<MyselfPageContentProps> = ({ isActive = true }
overflow: "hidden", overflow: "hidden",
}} }}
listLoadErrorWrapperHeight="fit-content" listLoadErrorWrapperHeight="fit-content"
listLoadErrorWidth="320px" listLoadErrorWidth="410px"
listLoadErrorHeight="152px" listLoadErrorHeight="185px"
listLoadErrorScale="1.2"
defaultShowNum={3} defaultShowNum={3}
/> />
</ScrollView> </ScrollView>
@@ -312,9 +333,8 @@ const MyselfPageContent: React.FC<MyselfPageContentProps> = ({ isActive = true }
collapse={true} collapse={true}
style={{ paddingBottom: "90px", overflow: "hidden" }} style={{ paddingBottom: "90px", overflow: "hidden" }}
listLoadErrorWrapperHeight="fit-content" listLoadErrorWrapperHeight="fit-content"
listLoadErrorWidth="320px" listLoadErrorWidth="410px"
listLoadErrorHeight="152px" listLoadErrorHeight="185px"
listLoadErrorScale="1.2"
defaultShowNum={3} defaultShowNum={3}
/> />
</ScrollView> </ScrollView>

View File

@@ -1,6 +1,7 @@
export default definePageConfig({ export default definePageConfig({
navigationBarTitleText: '首页', navigationBarTitleText: '首页',
navigationStyle: 'custom', navigationStyle: 'custom',
navigationBarBackgroundColor: '#FAFAFA' navigationBarBackgroundColor: '#FAFAFA',
}) enableShareAppMessage: true,
})

View File

@@ -1,6 +1,7 @@
import React, { useState, useEffect, useCallback } from "react"; import React, { useState, useEffect, useCallback } from "react";
import { View } from "@tarojs/components"; import { View } from "@tarojs/components";
import Taro from "@tarojs/taro"; import Taro, { useRouter, useShareAppMessage } from "@tarojs/taro";
import { OSS_BASE } from "@/config/api";
import { wechat_auth_login, save_login_state } from "@/services/loginService"; import { wechat_auth_login, save_login_state } from "@/services/loginService";
import { useUserActions } from "@/store/userStore"; import { useUserActions } from "@/store/userStore";
import { useGlobalState } from "@/store/global"; import { useGlobalState } from "@/store/global";
@@ -18,7 +19,11 @@ import { useDictionaryStore } from "@/store/dictionaryStore";
type TabType = "list" | "message" | "personal"; type TabType = "list" | "message" | "personal";
const MainPage: React.FC = () => { const MainPage: React.FC = () => {
const [currentTab, setCurrentTab] = useState<TabType>("list"); const { params } = useRouter();
const [currentTab, setCurrentTab] = useState<TabType>(() => {
const tab = params?.tab as TabType | undefined;
return tab === "list" || tab === "message" || tab === "personal" ? tab : "list";
});
const [isPublishMenuVisible, setIsPublishMenuVisible] = useState(false); const [isPublishMenuVisible, setIsPublishMenuVisible] = useState(false);
const [isDistanceFilterVisible, setIsDistanceFilterVisible] = useState(false); const [isDistanceFilterVisible, setIsDistanceFilterVisible] = useState(false);
const [isCityPickerVisible, setIsCityPickerVisible] = useState(false); const [isCityPickerVisible, setIsCityPickerVisible] = useState(false);
@@ -35,6 +40,14 @@ const MainPage: React.FC = () => {
const { showGuideBar, guideBarZIndex, setShowGuideBar, setGuideBarZIndex } = const { showGuideBar, guideBarZIndex, setShowGuideBar, setGuideBarZIndex } =
useGlobalState(); useGlobalState();
// 从分享链接进入时根据 ?tab= 定位到对应 tab
useEffect(() => {
const tab = params?.tab as TabType | undefined;
if (tab === "list" || tab === "message" || tab === "personal") {
setCurrentTab(tab);
}
}, [params?.tab]);
// 初始化:自动微信授权并获取用户信息 // 初始化:自动微信授权并获取用户信息
useEffect(() => { useEffect(() => {
const init = async () => { const init = async () => {
@@ -55,7 +68,7 @@ const MainPage: React.FC = () => {
return; return;
} }
} catch (error) { } catch (error) {
console.error("微信授权异常:", error); console.warn("微信授权异常:", error);
setAuthErrorMessage("微信授权失败,请重试"); setAuthErrorMessage("微信授权失败,请重试");
setShowAuthError(true); setShowAuthError(true);
return; return;
@@ -68,7 +81,7 @@ const MainPage: React.FC = () => {
await fetchUserInfo(); await fetchUserInfo();
await checkNicknameChangeStatus(); await checkNicknameChangeStatus();
} catch (error) { } catch (error) {
console.error("获取用户信息失败:", error); console.warn("获取用户信息失败:", error);
} }
} }
}; };
@@ -153,6 +166,43 @@ const MainPage: React.FC = () => {
[] []
); );
// 分享:按 tab 用 map 对应文案与分享图
const share_config: Record<
TabType,
{ title: string; image_path: string; query: string }
> = {
list: {
title: "有你就有场,发现身边好球友和好球局",
image_path: "system/share_home.png",
query: "?tab=list",
},
personal: {
title: "快来有场,约我一起打网球~",
image_path: "system/share_self.png",
query: "?tab=personal",
},
message: {
title: "查看球友动态",
image_path: "system/share_home.png",
query: "?tab=message",
},
};
useShareAppMessage(() => {
const config = share_config[currentTab] ?? {
title: "约球",
image_path: "system/share_home.png",
query: "",
};
// const imageUrl = OSS_BASE
// ? `${OSS_BASE.replace(/\/$/, "")}/${config.image_path}`
// : "";
return {
title: config.title,
path: "/main_pages/index" + config.query,
// imageUrl,
};
});
// 滚动到顶部 // 滚动到顶部
const scrollToTop = useCallback(() => { const scrollToTop = useCallback(() => {
// 如果当前是列表页,触发列表页内部滚动 // 如果当前是列表页,触发列表页内部滚动

View File

@@ -40,7 +40,9 @@
border-bottom: 0.5px solid rgba(0, 0, 0, 0.06); border-bottom: 0.5px solid rgba(0, 0, 0, 0.06);
padding: 8px 12px; padding: 8px 12px;
color: #000; color: #000;
font-feature-settings: "liga" off, "clig" off; font-feature-settings:
"liga" off,
"clig" off;
font-family: "PingFang SC"; font-family: "PingFang SC";
font-size: 14px; font-size: 14px;
font-style: normal; font-style: normal;
@@ -57,7 +59,9 @@
align-items: flex-start; align-items: flex-start;
color: rgba(60, 60, 67, 0.6); color: rgba(60, 60, 67, 0.6);
font-feature-settings: "liga" off, "clig" off; font-feature-settings:
"liga" off,
"clig" off;
font-family: "PingFang SC"; font-family: "PingFang SC";
font-size: 12px; font-size: 12px;
font-style: normal; font-style: normal;
@@ -117,7 +121,9 @@
align-items: center; align-items: center;
background: #ff3b30; background: #ff3b30;
color: #fff; color: #fff;
font-feature-settings: "liga" off, "clig" off; font-feature-settings:
"liga" off,
"clig" off;
font-family: "SF Compact Rounded"; font-family: "SF Compact Rounded";
font-style: normal; font-style: normal;
font-weight: 600; font-weight: 600;
@@ -133,7 +139,9 @@
box-sizing: border-box; box-sizing: border-box;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
font-feature-settings: "liga" off, "clig" off; font-feature-settings:
"liga" off,
"clig" off;
font-family: "SF Compact Rounded"; font-family: "SF Compact Rounded";
font-size: 22px; font-size: 22px;
font-style: normal; font-style: normal;
@@ -154,7 +162,9 @@
.date { .date {
color: #000; color: #000;
font-feature-settings: "liga" off, "clig" off; font-feature-settings:
"liga" off,
"clig" off;
font-family: "PingFang SC"; font-family: "PingFang SC";
font-size: 16px; font-size: 16px;
font-style: normal; font-style: normal;
@@ -164,7 +174,9 @@
.venueTime { .venueTime {
color: rgba(0, 0, 0, 0.8); color: rgba(0, 0, 0, 0.8);
font-feature-settings: "liga" off, "clig" off; font-feature-settings:
"liga" off,
"clig" off;
font-family: "PingFang SC"; font-family: "PingFang SC";
font-size: 12px; font-size: 12px;
font-style: normal; font-style: normal;
@@ -191,7 +203,9 @@
align-items: center; align-items: center;
gap: 12px; gap: 12px;
color: rgba(0, 0, 0, 0.8); color: rgba(0, 0, 0, 0.8);
font-feature-settings: "liga" off, "clig" off; font-feature-settings:
"liga" off,
"clig" off;
font-family: "PingFang SC"; font-family: "PingFang SC";
font-size: 12px; font-size: 12px;
font-style: normal; font-style: normal;
@@ -235,7 +249,9 @@
gap: 4px; gap: 4px;
color: #000; color: #000;
text-align: center; text-align: center;
font-feature-settings: "liga" off, "clig" off; font-feature-settings:
"liga" off,
"clig" off;
font-family: "PingFang SC"; font-family: "PingFang SC";
font-size: 16px; font-size: 16px;
font-style: normal; font-style: normal;
@@ -251,7 +267,9 @@
&Address { &Address {
color: rgba(0, 0, 0, 0.8); color: rgba(0, 0, 0, 0.8);
text-align: center; text-align: center;
font-feature-settings: "liga" off, "clig" off; font-feature-settings:
"liga" off,
"clig" off;
font-family: "PingFang SC"; font-family: "PingFang SC";
font-size: 12px; font-size: 12px;
font-style: normal; font-style: normal;
@@ -270,7 +288,9 @@
justify-content: flex-start; justify-content: flex-start;
gap: 4px; gap: 4px;
color: var(--Labels-Secondary, rgba(60, 60, 67, 0.6)); color: var(--Labels-Secondary, rgba(60, 60, 67, 0.6));
font-feature-settings: "liga" off, "clig" off; font-feature-settings:
"liga" off,
"clig" off;
font-family: "PingFang SC"; font-family: "PingFang SC";
font-size: 12px; font-size: 12px;
font-style: normal; font-style: normal;
@@ -307,7 +327,9 @@
& > .buttonText { & > .buttonText {
color: #000; color: #000;
font-feature-settings: "liga" off, "clig" off; font-feature-settings:
"liga" off,
"clig" off;
font-family: "PingFang SC"; font-family: "PingFang SC";
font-size: 12px; font-size: 12px;
font-style: normal; font-style: normal;
@@ -347,7 +369,9 @@
align-items: center; align-items: center;
align-self: stretch; align-self: stretch;
color: #000; color: #000;
font-feature-settings: "liga" off, "clig" off; font-feature-settings:
"liga" off,
"clig" off;
font-family: "PingFang SC"; font-family: "PingFang SC";
font-size: 14px; font-size: 14px;
font-style: normal; font-style: normal;
@@ -373,7 +397,9 @@
width: 120px; width: 120px;
display: inline-block; display: inline-block;
color: rgba(60, 60, 67, 0.6); color: rgba(60, 60, 67, 0.6);
font-feature-settings: "liga" off, "clig" off; font-feature-settings:
"liga" off,
"clig" off;
font-family: "PingFang SC"; font-family: "PingFang SC";
font-size: 14px; font-size: 14px;
font-style: normal; font-style: normal;
@@ -383,7 +409,9 @@
.content { .content {
color: #000; color: #000;
font-feature-settings: "liga" off, "clig" off; font-feature-settings:
"liga" off,
"clig" off;
font-family: "PingFang SC"; font-family: "PingFang SC";
font-size: 14px; font-size: 14px;
font-style: normal; font-style: normal;
@@ -400,9 +428,10 @@
.orderNo { .orderNo {
display: flex; display: flex;
justify-content: flex-end; flex-direction: column;
align-items: center; justify-content: center;
gap: 8px; align-items: flex-end;
gap: 0px;
.copy { .copy {
color: #007aff; color: #007aff;
@@ -421,7 +450,9 @@
align-items: center; align-items: center;
align-self: stretch; align-self: stretch;
color: #000; color: #000;
font-feature-settings: "liga" off, "clig" off; font-feature-settings:
"liga" off,
"clig" off;
font-family: "PingFang SC"; font-family: "PingFang SC";
font-size: 14px; font-size: 14px;
font-style: normal; font-style: normal;
@@ -442,7 +473,9 @@
align-items: center; align-items: center;
color: #000; color: #000;
text-align: center; text-align: center;
font-feature-settings: "liga" off, "clig" off; font-feature-settings:
"liga" off,
"clig" off;
font-family: "PingFang SC"; font-family: "PingFang SC";
font-size: 12px; font-size: 12px;
font-style: normal; font-style: normal;
@@ -491,7 +524,9 @@
&:nth-child(1) { &:nth-child(1) {
color: #000; color: #000;
text-align: center; text-align: center;
font-feature-settings: "liga" off, "clig" off; font-feature-settings:
"liga" off,
"clig" off;
font-family: "PingFang SC"; font-family: "PingFang SC";
font-size: 14px; font-size: 14px;
font-style: normal; font-style: normal;
@@ -531,7 +566,9 @@
align-items: center; align-items: center;
align-self: stretch; align-self: stretch;
color: #000; color: #000;
font-feature-settings: "liga" off, "clig" off; font-feature-settings:
"liga" off,
"clig" off;
font-family: "PingFang SC"; font-family: "PingFang SC";
font-size: 14px; font-size: 14px;
font-style: normal; font-style: normal;
@@ -567,7 +604,9 @@
background: #000; background: #000;
box-shadow: 0 8px 64px 0 rgba(0, 0, 0, 0.1); box-shadow: 0 8px 64px 0 rgba(0, 0, 0, 0.1);
backdrop-filter: blur(16px); backdrop-filter: blur(16px);
font-feature-settings: "liga" off, "clig" off; font-feature-settings:
"liga" off,
"clig" off;
font-family: "PingFang SC"; font-family: "PingFang SC";
font-size: 16px; font-size: 16px;
font-style: normal; font-style: normal;
@@ -600,7 +639,9 @@
text-align: center; text-align: center;
// border: 0.5px solid rgba(0, 0, 0, 0.06); // border: 0.5px solid rgba(0, 0, 0, 0.06);
color: #000; color: #000;
font-feature-settings: "liga" off, "clig" off; font-feature-settings:
"liga" off,
"clig" off;
font-family: "PingFang SC"; font-family: "PingFang SC";
font-size: 16px; font-size: 16px;
font-style: normal; font-style: normal;
@@ -626,7 +667,9 @@
padding: 12px 15px; padding: 12px 15px;
color: rgba(60, 60, 67, 0.6); color: rgba(60, 60, 67, 0.6);
text-align: center; text-align: center;
font-feature-settings: "liga" off, "clig" off; font-feature-settings:
"liga" off,
"clig" off;
font-family: "PingFang SC"; font-family: "PingFang SC";
font-size: 16px; font-size: 16px;
font-style: normal; font-style: normal;

View File

@@ -10,6 +10,7 @@ import orderService, {
GameOrderRes, GameOrderRes,
OrderStatus, OrderStatus,
refundTextMap, refundTextMap,
RefundStatus,
} from "@/services/orderService"; } from "@/services/orderService";
import { debounce } from "@tarojs/runtime"; import { debounce } from "@tarojs/runtime";
import { import {
@@ -76,7 +77,7 @@ function genGameNotice(order_status, start_time) {
function GameInfo(props) { function GameInfo(props) {
const { detail, currentLocation, orderDetail, init } = props; const { detail, currentLocation, orderDetail, init } = props;
const { order_status, refund_status, amount } = orderDetail; const { order_status, refund_status, amount, refund_amount } = orderDetail;
const { const {
latitude, latitude,
longitude, longitude,
@@ -111,7 +112,7 @@ function GameInfo(props) {
const startTime = dayjs(start_time); const startTime = dayjs(start_time);
const endTime = dayjs(end_time); const endTime = dayjs(end_time);
const game_length = Number( const game_length = Number(
(endTime.diff(startTime, "minutes") / 60).toFixed() (endTime.diff(startTime, "minutes") / 60).toFixed(),
); );
const startMonth = startTime.format("M"); const startMonth = startTime.format("M");
@@ -244,7 +245,10 @@ function GameInfo(props) {
<View className={styles.gameInfoContainer}> <View className={styles.gameInfoContainer}>
{["refund", "progress", "expired"].includes(orderStatus) && ( {["refund", "progress", "expired"].includes(orderStatus) && (
<View className={styles.paidInfo}> <View className={styles.paidInfo}>
{refundTextMap.get(refund_status)} ¥ {amount} {refundTextMap.get(refund_status)} ¥{" "}
{[RefundStatus.PENDING, RefundStatus.SUCCESS].includes(refund_status)
? refund_amount
: amount}
</View> </View>
)} )}
{["progress", "expired"].includes(orderStatus) && {["progress", "expired"].includes(orderStatus) &&
@@ -344,7 +348,7 @@ function GameInfo(props) {
handlePayNow: () => {}, handlePayNow: () => {},
handleViewGame, handleViewGame,
}, },
"detail" "detail",
)?.map((obj) => ( )?.map((obj) => (
<View className={classnames(styles.button, styles[obj.className])}> <View className={classnames(styles.button, styles[obj.className])}>
<Text className={styles.buttonText}>{obj.text}</Text> <Text className={styles.buttonText}>{obj.text}</Text>
@@ -504,7 +508,7 @@ function RefundPolicy(props) {
const theTimeObj = dayjs( const theTimeObj = dayjs(
isLast isLast
? refund_policy.at(-2).deadline_formatted ? refund_policy.at(-2).deadline_formatted
: item.deadline_formatted : item.deadline_formatted,
); );
const year = theTimeObj.format("YYYY"); const year = theTimeObj.format("YYYY");
const month = theTimeObj.format("M"); const month = theTimeObj.format("M");
@@ -531,7 +535,7 @@ function RefundPolicy(props) {
className={classnames( className={classnames(
styles.policyItem, styles.policyItem,
targetIndex > index && index !== 0 ? styles.pastItem : "", targetIndex > index && index !== 0 ? styles.pastItem : "",
targetIndex === index ? styles.currentItem : "" targetIndex === index ? styles.currentItem : "",
)} )}
> >
<View className={styles.time}> <View className={styles.time}>

View File

@@ -9,6 +9,7 @@ import orderService, {
OrderStatus, OrderStatus,
CancelType, CancelType,
refundTextMap, refundTextMap,
RefundStatus,
} from "@/services/orderService"; } from "@/services/orderService";
import { getStorage, removeStorage, setStorage } from "@/store/storage"; import { getStorage, removeStorage, setStorage } from "@/store/storage";
import { useGlobalStore } from "@/store/global"; import { useGlobalStore } from "@/store/global";
@@ -101,7 +102,7 @@ const OrderList = () => {
newList.splice( newList.splice(
index, index,
clear ? newList.length - index : 1, clear ? newList.length - index : 1,
addPageInfo(res.data.rows, page) addPageInfo(res.data.rows, page),
); );
return newList; return newList;
}); });
@@ -264,13 +265,17 @@ const OrderList = () => {
}); });
} }
function handleQuit(item) { async function handleQuit(item) {
if (refundRef.current) { if (refundRef.current) {
refundRef.current.show(item, (result) => { const res = await orderService.getRefundPolicy({ order_id: item.id });
if (result) { refundRef.current.show(
getOrders(item.page); { ...item, refund_policy: res.data.refund_policy },
} (result) => {
}); if (result) {
getOrders(item.page);
}
},
);
} }
} }
@@ -293,7 +298,7 @@ const OrderList = () => {
> >
<GeneralNavbar <GeneralNavbar
title="球局订单" title="球局订单"
backgroundColor="transparent" backgroundColor="#ffffff"
titleClassName={styles.titleClassName} titleClassName={styles.titleClassName}
className={styles.navbar} className={styles.navbar}
/> />
@@ -316,7 +321,7 @@ const OrderList = () => {
item.order_status === OrderStatus.PENDING && item.order_status === OrderStatus.PENDING &&
item.cancel_type === CancelType.NONE; item.cancel_type === CancelType.NONE;
const canceled = [CancelType.USER, CancelType.TIMEOUT].includes( const canceled = [CancelType.USER, CancelType.TIMEOUT].includes(
item.cancel_type item.cancel_type,
); );
const { game_info } = item; const { game_info } = item;
@@ -349,7 +354,7 @@ const OrderList = () => {
<View <View
className={classnames( className={classnames(
styles.payNum, styles.payNum,
styles[unPay ? "pending" : "paid"] styles[unPay ? "pending" : "paid"],
)} )}
> >
<Text> <Text>
@@ -358,7 +363,15 @@ const OrderList = () => {
: refundTextMap.get(item.refund_status)} : refundTextMap.get(item.refund_status)}
</Text>{" "} </Text>{" "}
<View className={styles.amount}> <View className={styles.amount}>
¥ <Text>{item.amount}</Text> ¥{" "}
<Text>
{[
RefundStatus.PENDING,
RefundStatus.SUCCESS,
].includes(item.refund_status)
? item.refund_amount
: item.amount}
</Text>
</View> </View>
</View> </View>
)} )}
@@ -370,7 +383,7 @@ const OrderList = () => {
{insertDotInTags([location_name, court_type, "3.5km"]).map( {insertDotInTags([location_name, court_type, "3.5km"]).map(
(text, index) => ( (text, index) => (
<Text key={index}>{text}</Text> <Text key={index}>{text}</Text>
) ),
)} )}
</View> </View>
<View className={styles.gameOtherInfo}> <View className={styles.gameOtherInfo}>
@@ -426,12 +439,12 @@ const OrderList = () => {
handlePayNow, handlePayNow,
handleViewGame, handleViewGame,
}, },
"list" "list",
)?.map((obj) => ( )?.map((obj) => (
<View <View
className={classnames( className={classnames(
styles.button, styles.button,
styles[obj.className] styles[obj.className],
)} )}
> >
<Text className={styles.buttonText}>{obj.text}</Text> <Text className={styles.buttonText}>{obj.text}</Text>

View File

@@ -77,7 +77,7 @@ const CommentReply = () => {
if (allCommentIds.length > 0) { if (allCommentIds.length > 0) {
// 使用统一接口标记已读传入所有评论ID // 使用统一接口标记已读传入所有评论ID
messageService.markAsRead('comment', allCommentIds).catch(e => { messageService.markAsRead('comment', allCommentIds).catch(e => {
console.error("标记评论已读失败:", e); console.warn("标记评论已读失败:", e);
}); });
} }
} }
@@ -221,7 +221,7 @@ const CommentReply = () => {
if (allCommentIds.length > 0) { if (allCommentIds.length > 0) {
messageService.markAsRead('comment', allCommentIds).catch(e => { messageService.markAsRead('comment', allCommentIds).catch(e => {
console.error("标记评论已读失败:", e); console.warn("标记评论已读失败:", e);
}); });
} }
} }
@@ -249,7 +249,7 @@ const CommentReply = () => {
<View className="comment-left"> <View className="comment-left">
<Image <Image
className="user-avatar" className="user-avatar"
src={item.user_avatar || "https://img.yzcdn.cn/vant/cat.jpeg"} src={item.user_avatar }
mode="aspectFill" mode="aspectFill"
onClick={(e) => handleUserClick(e, item.user_id)} onClick={(e) => handleUserClick(e, item.user_id)}
/> />

View File

@@ -21,7 +21,7 @@ const EnableNotificationPage: React.FC = () => {
setQrCodeUrl(res.data.ServiceAccountQRCode); setQrCodeUrl(res.data.ServiceAccountQRCode);
} }
} catch (error) { } catch (error) {
console.error('获取二维码失败:', error); console.warn('获取二维码失败:', error);
} }
}; };
fetchQRCode(); fetchQRCode();

View File

@@ -62,7 +62,7 @@ const NewFollow = () => {
if (allFanIds.length > 0) { if (allFanIds.length > 0) {
// 使用统一接口标记已读传入所有关注者ID // 使用统一接口标记已读传入所有关注者ID
messageService.markAsRead('follow', allFanIds).catch(e => { messageService.markAsRead('follow', allFanIds).catch(e => {
console.error("标记关注已读失败:", e); console.warn("标记关注已读失败:", e);
}); });
} }
} else { } else {
@@ -168,7 +168,7 @@ const NewFollow = () => {
if (allFanIds.length > 0) { if (allFanIds.length > 0) {
messageService.markAsRead('follow', allFanIds).catch(e => { messageService.markAsRead('follow', allFanIds).catch(e => {
console.error("标记关注已读失败:", e); console.warn("标记关注已读失败:", e);
}); });
} }
} else { } else {

View File

@@ -99,7 +99,7 @@ function isOnCancelEmpty(onCancelFunc) {
const normalized = funcString.replace(/\s/g, ""); const normalized = funcString.replace(/\s/g, "");
return emptyFunctionPatterns.includes(normalized); return emptyFunctionPatterns.includes(normalized);
} catch (error) { } catch (error) {
console.error("检查 onCancel 函数时出错:", error); console.warn("检查 onCancel 函数时出错:", error);
return false; return false;
} }
} }
@@ -531,7 +531,7 @@ function Result() {
// setQrCodeUrl(tempFilePath); // setQrCodeUrl(tempFilePath);
// } // }
} catch (error) { } catch (error) {
console.error("获取二维码失败:", error); console.warn("获取二维码失败:", error);
} }
} }
@@ -645,7 +645,7 @@ function Result() {
}); });
return imageUrl; return imageUrl;
} catch (error) { } catch (error) {
console.error("生成图片失败:", error); console.warn("生成图片失败:", error);
throw error; throw error;
} }
} }

View File

@@ -88,7 +88,7 @@ const AiImportPopup: React.FC<AiImportPopupProps> = ({
}) })
} }
} catch (error) { } catch (error) {
console.error('获取剪切板失败:', error) console.warn('获取剪切板失败:', error)
Taro.showToast({ Taro.showToast({
title: '读取剪切板失败,请手动输入', title: '读取剪切板失败,请手动输入',
icon: 'error', icon: 'error',
@@ -163,7 +163,7 @@ const AiImportPopup: React.FC<AiImportPopupProps> = ({
} }
} }
} catch (error) { } catch (error) {
console.error('选择图片失败:', error) console.warn('选择图片失败:', error)
if (!(typeof error === 'object' && error.errMsg && error.errMsg.includes('fail cancel'))) { if (!(typeof error === 'object' && error.errMsg && error.errMsg.includes('fail cancel'))) {
setUploadFailCount(prev => prev + 1) setUploadFailCount(prev => prev + 1)
Taro.showToast({ Taro.showToast({

View File

@@ -53,7 +53,7 @@ const SelectStadium: React.FC<SelectStadiumProps> = ({
} }
} }
} catch (error) { } catch (error) {
console.error('获取场馆列表失败:', error) console.warn('获取场馆列表失败:', error)
} finally { } finally {
setLoading(false) setLoading(false)
} }
@@ -107,7 +107,7 @@ const SelectStadium: React.FC<SelectStadiumProps> = ({
setShowDetail(true) setShowDetail(true)
}, },
fail: (err: { errMsg: string }) => { fail: (err: { errMsg: string }) => {
console.error('选择位置失败:', err) console.warn('选择位置失败:', err)
const { errMsg } = err || {}; const { errMsg } = err || {};
if (!errMsg.includes('fail cancel')) { if (!errMsg.includes('fail cancel')) {
Taro.showToast({ Taro.showToast({

View File

@@ -152,7 +152,7 @@ const StadiumDetail = forwardRef<StadiumDetailRef, StadiumDetailProps>(({
}) })
}, },
fail: (err: { errMsg: string }) => { fail: (err: { errMsg: string }) => {
console.error('选择位置失败:', err) console.warn('选择位置失败:', err)
const { errMsg } = err || {}; const { errMsg } = err || {};
if (!errMsg.includes('fail cancel')) { if (!errMsg.includes('fail cancel')) {
Taro.showToast({ Taro.showToast({
@@ -199,13 +199,6 @@ const StadiumDetail = forwardRef<StadiumDetailRef, StadiumDetailProps>(({
} }
} }
// 当键盘显示时触发 changeTextarea
useEffect(() => {
if (isKeyboardVisible) {
changeTextarea(true)
}
}, [isKeyboardVisible])
const changePicker = (value:boolean) => { const changePicker = (value:boolean) => {
setOpenPicker(value); setOpenPicker(value);
} }
@@ -237,7 +230,7 @@ const StadiumDetail = forwardRef<StadiumDetailRef, StadiumDetailProps>(({
<View className='stadium-item-right'> <View className='stadium-item-right'>
<View className='stadium-name'>{formData.name}</View> <View className='stadium-name'>{formData.name}</View>
<View className='stadium-address'> <View className='stadium-address'>
<Text>{calculateDistance(formData.istance || null)} · </Text> <Text>{calculateDistance(formData.istance || null) + ' · '}</Text>
<Text>{formData.address}</Text> <Text>{formData.address}</Text>
<Image src={images.ICON_ARRORW_SMALL} className='stadium-map-icon' /> <Image src={images.ICON_ARRORW_SMALL} className='stadium-map-icon' />
</View> </View>
@@ -269,11 +262,13 @@ const StadiumDetail = forwardRef<StadiumDetailRef, StadiumDetailProps>(({
<TextareaTag <TextareaTag
value={formData[item.prop]} value={formData[item.prop]}
onChange={(value) => { onChange={(value) => {
//changeTextarea(true)
updateFormData(item.prop, value) updateFormData(item.prop, value)
}} }}
// onBlur={() => changeTextarea(false)} // onBlur={() => {
onFocus={() => changeTextarea(true)} // }}
onFocus={() => {
changeTextarea(true)
}}
placeholder='有其他场地信息可备注' placeholder='有其他场地信息可备注'
options={(item.options || []).map((o) => ({ label: o, value: o }))} options={(item.options || []).map((o) => ({ label: o, value: o }))}
/> />

View File

@@ -13,7 +13,7 @@ import {
} from "../../config/formSchema/publishBallFormSchema"; } from "../../config/formSchema/publishBallFormSchema";
import { PublishBallFormData } from "../../../types/publishBall"; import { PublishBallFormData } from "../../../types/publishBall";
import PublishService from "@/services/publishService"; import PublishService from "@/services/publishService";
import { getNextHourTime, getEndTime, delay } from "@/utils"; import { getNextHourTime, getEndTime, delay, getBackendErrorMsg } from "@/utils";
import { useGlobalState } from "@/store/global"; import { useGlobalState } from "@/store/global";
import GeneralNavbar from "@/components/GeneralNavbar"; import GeneralNavbar from "@/components/GeneralNavbar";
import images from "@/config/images"; import images from "@/config/images";
@@ -364,104 +364,27 @@ const PublishBall: React.FC = () => {
}; };
// 提交表单 // 提交表单
const handleSubmit = async () => { const handleSubmit = async () => {
// 基础验证
const params = getParams(); const params = getParams();
const { republish } = params || {}; const { republish } = params || {};
if (activityType === "individual") { if (activityType === "individual") {
const isValid = validateFormData(formData[0]); const isValid = validateFormData(formData[0]);
if (!isValid || publishLoading) { if (!isValid || publishLoading) return;
return;
}
setPublishLoading(true); setPublishLoading(true);
const { try {
activityInfo,
descriptionInfo,
is_substitute_supported,
timeRange,
players,
skill_level,
image_list,
wechat,
id,
...rest
} = formData[0];
const { min, max, organizer_joined } = players;
const options = {
...rest,
...activityInfo,
...descriptionInfo,
...timeRange,
max_players: max,
min_players: min,
organizer_joined: organizer_joined === true ? 1 : 0,
skill_level_min: skill_level[0],
skill_level_max: skill_level[1],
image_list: image_list.map((item) => item.url),
is_wechat_contact: wechat.is_wechat_contact ? 1 : 0,
wechat_contact: wechat.wechat_contact || wechat.default_wechat_contact,
is_substitute_supported: is_substitute_supported ? "1" : "0",
...(republish === "0" ? { id } : {}),
};
const res =
republish === "0"
? await PublishService.gamesUpdate(options)
: await PublishService.createPersonal(options);
const successText = republish === "0" ? "更新成功" : "发布成功";
if (res.code === 0 && res.data) {
Taro.showToast({
title: successText,
icon: "success",
});
delay(1000);
// 如果是个人球局,则跳转到详情页,并自动分享
// 如果是畅打,则跳转第一个球局详情页,并自动分享 @刘杰
const id = (res as any).data?.id;
// 如果是编辑,就返回,否则就是新发布
if (republish === "0") {
Taro.navigateBack();
} else {
// 使用 redirectTo 替换当前页面,避免返回时回到发布页面
Taro.redirectTo({
// @ts-expect-error: id
url: `/game_pages/detail/index?id=${
id || 1
}&from=publish&autoShare=1`,
});
}
} else {
Taro.showToast({
title: res.message,
icon: "none",
});
setPublishLoading(false);
}
}
if (activityType === "group") {
const isValid = formData.every((item) => validateFormData(item));
if (!isValid || publishLoading) {
return;
}
setPublishLoading(true);
if (checkAdjacentDataSame(formData)) {
Taro.showToast({
title: "信息不可与前序场完全一致",
icon: "none",
});
return;
}
const options = formData.map((item) => {
const { const {
activityInfo, activityInfo,
descriptionInfo, descriptionInfo,
is_substitute_supported,
timeRange, timeRange,
players, players,
skill_level, skill_level,
is_substitute_supported, image_list,
wechat,
id, id,
...rest ...rest
} = item; } = formData[0];
const { min, max, organizer_joined } = players; const { min, max, organizer_joined } = players;
return { const options = {
...rest, ...rest,
...activityInfo, ...activityInfo,
...descriptionInfo, ...descriptionInfo,
@@ -471,38 +394,108 @@ const PublishBall: React.FC = () => {
organizer_joined: organizer_joined === true ? 1 : 0, organizer_joined: organizer_joined === true ? 1 : 0,
skill_level_min: skill_level[0], skill_level_min: skill_level[0],
skill_level_max: skill_level[1], skill_level_max: skill_level[1],
image_list: image_list.map((item) => item.url),
is_wechat_contact: wechat.is_wechat_contact ? 1 : 0,
wechat_contact: wechat.wechat_contact || wechat.default_wechat_contact,
is_substitute_supported: is_substitute_supported ? "1" : "0", is_substitute_supported: is_substitute_supported ? "1" : "0",
image_list: item.image_list.map((img) => img.url),
...(republish === "0" ? { id } : {}), ...(republish === "0" ? { id } : {}),
}; };
}); const res =
const successText = republish === "0" ? "更新成功" : "发布成功";
const res =
republish === "0"
? await PublishService.gamesUpdate(options[0])
: await PublishService.create_play_pmoothlys({ rows: options });
if (res.code === 0 && res.data) {
Taro.showToast({
title: successText,
icon: "success",
});
delay(1000);
// 如果是个人球局,则跳转到详情页,并自动分享
// 如果是畅打,则跳转第一个球局详情页,并自动分享 @刘杰
const id =
republish === "0" republish === "0"
? (res as any).data?.id ? await PublishService.gamesUpdate(options)
: (res as any).data?.[0]?.id; : await PublishService.createPersonal(options);
// 使用 redirectTo 替换当前页面,避免返回时回到发布页面 const successText = republish === "0" ? "更新成功" : "发布成功";
Taro.redirectTo({ if (res.code === 0 && res.data) {
// @ts-expect-error: id Taro.showToast({ title: successText, icon: "success" });
url: `/game_pages/detail/index?id=${ delay(1000);
id || 1 const id = (res as any).data?.id;
}&from=publish&autoShare=1`, if (republish === "0") {
}); Taro.navigateBack();
} else { } else {
Taro.redirectTo({
url: `/game_pages/detail/index?id=${id || 1}&from=publish&autoShare=1`,
});
}
} else {
Taro.showToast({
title: getBackendErrorMsg(res, "发布失败"),
icon: "none",
});
setPublishLoading(false);
}
} catch (error) {
Taro.showToast({ Taro.showToast({
title: res.message, title: getBackendErrorMsg(error, "发布失败"),
icon: "none",
});
setPublishLoading(false);
}
return;
}
if (activityType === "group") {
const isValid = formData.every((item) => validateFormData(item));
if (!isValid || publishLoading) return;
if (checkAdjacentDataSame(formData)) {
Taro.showToast({
title: "信息不可与前序场完全一致",
icon: "none",
});
return;
}
setPublishLoading(true);
try {
const options = formData.map((item) => {
const {
activityInfo,
descriptionInfo,
timeRange,
players,
skill_level,
is_substitute_supported,
id,
...rest
} = item;
const { min, max, organizer_joined } = players;
return {
...rest,
...activityInfo,
...descriptionInfo,
...timeRange,
max_players: max,
min_players: min,
organizer_joined: organizer_joined === true ? 1 : 0,
skill_level_min: skill_level[0],
skill_level_max: skill_level[1],
is_substitute_supported: is_substitute_supported ? "1" : "0",
image_list: item.image_list.map((img) => img.url),
...(republish === "0" ? { id } : {}),
};
});
const successText = republish === "0" ? "更新成功" : "发布成功";
const res =
republish === "0"
? await PublishService.gamesUpdate(options[0])
: await PublishService.create_play_pmoothlys({ rows: options });
if (res.code === 0 && res.data) {
Taro.showToast({ title: successText, icon: "success" });
delay(1000);
const id =
republish === "0"
? (res as any).data?.id
: (res as any).data?.[0]?.id;
Taro.redirectTo({
url: `/game_pages/detail/index?id=${id || 1}&from=publish&autoShare=1`,
});
} else {
Taro.showToast({
title: getBackendErrorMsg(res, "发布失败"),
icon: "none",
});
setPublishLoading(false);
}
} catch (error) {
Taro.showToast({
title: getBackendErrorMsg(error, "发布失败"),
icon: "none", icon: "none",
}); });
setPublishLoading(false); setPublishLoading(false);

View File

@@ -169,7 +169,7 @@ class GameDetailService {
} }
async getLinkUrl(req: { path: string, query: string }): Promise<ApiResponse<{ url_link: string, path: string, query: string }>> { async getLinkUrl(req: { path: string, query: string }): Promise<ApiResponse<{ url_link: string, path: string, query: string }>> {
return httpService.post('/user/generate_url_link', req, { showLoading: true }) return httpService.post('/user/generate_url_link', req, { showLoading: false })
} }
} }

View File

@@ -51,7 +51,7 @@ export class FollowService {
throw new Error(response.message || '获取互关列表失败'); throw new Error(response.message || '获取互关列表失败');
} }
} catch (error) { } catch (error) {
console.error('获取互关列表失败:', error); console.warn('获取互关列表失败:', error);
return { list: [], total: 0 }; return { list: [], total: 0 };
} }
} }
@@ -79,7 +79,7 @@ export class FollowService {
throw new Error(response.message || '获取粉丝列表失败'); throw new Error(response.message || '获取粉丝列表失败');
} }
} catch (error) { } catch (error) {
console.error('获取粉丝列表失败:', error); console.warn('获取粉丝列表失败:', error);
return { list: [], total: 0 }; return { list: [], total: 0 };
} }
} }
@@ -107,7 +107,7 @@ export class FollowService {
throw new Error(response.message || '获取新增粉丝列表失败'); throw new Error(response.message || '获取新增粉丝列表失败');
} }
} catch (error) { } catch (error) {
console.error('获取新增粉丝列表失败:', error); console.warn('获取新增粉丝列表失败:', error);
return { list: [], total: 0 }; return { list: [], total: 0 };
} }
} }
@@ -135,7 +135,7 @@ export class FollowService {
throw new Error(response.message || '获取关注列表失败'); throw new Error(response.message || '获取关注列表失败');
} }
} catch (error) { } catch (error) {
console.error('获取关注列表失败:', error); console.warn('获取关注列表失败:', error);
return { list: [], total: 0 }; return { list: [], total: 0 };
} }
} }
@@ -178,7 +178,7 @@ export class FollowService {
throw new Error(response.message || '关注失败'); throw new Error(response.message || '关注失败');
} }
} catch (error) { } catch (error) {
console.error('关注失败:', error); console.warn('关注失败:', error);
throw error; throw error;
} }
} }
@@ -201,7 +201,7 @@ export class FollowService {
throw new Error(response.message || '取消关注失败'); throw new Error(response.message || '取消关注失败');
} }
} catch (error) { } catch (error) {
console.error('取消关注失败:', error); console.warn('取消关注失败:', error);
throw error; throw error;
} }
} }
@@ -231,7 +231,7 @@ export class FollowService {
throw new Error(response.message || '获取推荐用户失败'); throw new Error(response.message || '获取推荐用户失败');
} }
} catch (error) { } catch (error) {
console.error('获取推荐用户失败:', error); console.warn('获取推荐用户失败:', error);
return { list: [], total: 0 }; return { list: [], total: 0 };
} }
} }
@@ -251,7 +251,7 @@ export class FollowService {
return 'none'; return 'none';
} }
} catch (error) { } catch (error) {
console.error('检查关注状态失败:', error); console.warn('检查关注状态失败:', error);
return 'none'; return 'none';
} }
} }
@@ -269,7 +269,7 @@ export class FollowService {
throw new Error(response.message || '屏蔽推荐用户失败'); throw new Error(response.message || '屏蔽推荐用户失败');
} }
} catch (error) { } catch (error) {
console.error('屏蔽推荐用户失败:', error); console.warn('屏蔽推荐用户失败:', error);
throw error; throw error;
} }
} }

View File

@@ -27,7 +27,7 @@ export const getGamesList = async (params?: Record<string, any>) => {
// const fetchApi = isIntegrate ? '/games/integrate_list' : '/games/list' // const fetchApi = isIntegrate ? '/games/integrate_list' : '/games/list'
return httpService.post('/games/list', params, { showLoading: false }) return httpService.post('/games/list', params, { showLoading: false })
} catch (error) { } catch (error) {
console.error("列表数据获取失败:", error); console.warn("列表数据获取失败:", error);
throw error; throw error;
} }
}; };
@@ -41,7 +41,7 @@ export const getGamesIntegrateList = async (params?: Record<string, any>) => {
try { try {
return httpService.post('/games/integrate_list', params, { showLoading: false }) return httpService.post('/games/integrate_list', params, { showLoading: false })
} catch (error) { } catch (error) {
console.error("列表数据获取失败:", error); console.warn("列表数据获取失败:", error);
throw error; throw error;
} }
}; };
@@ -55,7 +55,7 @@ export const getGamesCount = async (params?: Record<string, any>) => {
try { try {
return httpService.post('/games/count', params, { showLoading: false }) return httpService.post('/games/count', params, { showLoading: false })
} catch (error) { } catch (error) {
console.error("列表数量获取失败:", error); console.warn("列表数量获取失败:", error);
throw error; throw error;
} }
}; };
@@ -71,7 +71,7 @@ export const getSearchHistory = async (params) => {
return httpService.post('/games/search_history', params, { showLoading: false }) return httpService.post('/games/search_history', params, { showLoading: false })
} catch (error) { } catch (error) {
// 捕获并打印错误信息 // 捕获并打印错误信息
console.error("历史记录获取失败:", error); console.warn("历史记录获取失败:", error);
// 抛出错误以便上层处理 // 抛出错误以便上层处理
throw error; throw error;
} }
@@ -87,7 +87,7 @@ export const clearHistory = async (params) => {
return httpService.post('/games/search_history/delete_all', params, { showLoading: false }) return httpService.post('/games/search_history/delete_all', params, { showLoading: false })
} catch (error) { } catch (error) {
// 捕获并打印错误信息 // 捕获并打印错误信息
console.error("清除历史记录失败:", error); console.warn("清除历史记录失败:", error);
// 抛出错误以便上层处理 // 抛出错误以便上层处理
throw error; throw error;
} }
@@ -104,7 +104,7 @@ export const searchSuggestion = async (params) => {
return httpService.post('/games/search_recommendations', params) return httpService.post('/games/search_recommendations', params)
} catch (error) { } catch (error) {
// 捕获并打印错误信息 // 捕获并打印错误信息
console.error("搜索建议获取失败:", error); console.warn("搜索建议获取失败:", error);
// 抛出错误以便上层处理 // 抛出错误以便上层处理
throw error; throw error;
} }
@@ -116,7 +116,7 @@ export const getCities = async () => {
return httpService.post('/cities/tree', {}) return httpService.post('/cities/tree', {})
} catch (error) { } catch (error) {
// 捕获并打印错误信息 // 捕获并打印错误信息
console.error("城市列表获取失败:", error); console.warn("城市列表获取失败:", error);
// 抛出错误以便上层处理 // 抛出错误以便上层处理
throw error; throw error;
} }
@@ -127,7 +127,7 @@ export const getCityQrCode = async () => {
return httpService.post('/hot_city_qr/list', {}) return httpService.post('/hot_city_qr/list', {})
} catch (error) { } catch (error) {
// 捕获并打印错误信息 // 捕获并打印错误信息
console.error("城市二维码获取失败:", error); console.warn("城市二维码获取失败:", error);
// 抛出错误以便上层处理 // 抛出错误以便上层处理
throw error; throw error;
} }
@@ -140,7 +140,7 @@ export const getDistricts = async (params: { province: string; city: string }) =
return httpService.post('/cities/cities', params) return httpService.post('/cities/cities', params)
} catch (error) { } catch (error) {
// 捕获并打印错误信息 // 捕获并打印错误信息
console.error("行政区列表获取失败:", error); console.warn("行政区列表获取失败:", error);
// 抛出错误以便上层处理 // 抛出错误以便上层处理
throw error; throw error;
} }

View File

@@ -122,7 +122,7 @@ export const wechat_auth_login = async (
}; };
} }
} catch (error) { } catch (error) {
console.error("微信授权登录失败:", error); console.warn("微信授权登录失败:", error);
return { return {
success: false, success: false,
message: "微信授权失败,请重试", message: "微信授权失败,请重试",
@@ -160,7 +160,7 @@ export const phone_auth_login = async (
await useUser.getState().fetchUserInfo(); await useUser.getState().fetchUserInfo();
await useUser.getState().checkNicknameChangeStatus(); await useUser.getState().checkNicknameChangeStatus();
} catch (error) { } catch (error) {
console.error("更新用户信息到 store 失败:", error); console.warn("更新用户信息到 store 失败:", error);
} }
return { return {
@@ -178,7 +178,7 @@ export const phone_auth_login = async (
}; };
} }
} catch (error) { } catch (error) {
console.error("手机号登录失败:", error); console.warn("手机号登录失败:", error);
return { return {
success: false, success: false,
message: error.message, message: error.message,
@@ -206,7 +206,7 @@ export const send_sms_code = async (phone: string): Promise<SmsResponse> => {
}; };
} }
} catch (error) { } catch (error) {
console.error("发送短信失败:", error); console.warn("发送短信失败:", error);
return { return {
success: false, success: false,
message: error.message, message: error.message,
@@ -232,7 +232,7 @@ export const verify_sms_code = async (
user_info: response.data?.userInfo, user_info: response.data?.userInfo,
}; };
} catch (error) { } catch (error) {
console.error("验证验证码失败:", error); console.warn("验证验证码失败:", error);
return { return {
success: false, success: false,
message: error.message, message: error.message,
@@ -255,7 +255,7 @@ export const save_login_state = (token: string, user_info: WechatUserInfo) => {
Taro.setStorageSync("is_logged_in", true); Taro.setStorageSync("is_logged_in", true);
Taro.setStorageSync("login_time", Date.now()); Taro.setStorageSync("login_time", Date.now());
} catch (error) { } catch (error) {
console.error("保存登录状态失败:", error); console.warn("保存登录状态失败:", error);
} }
}; };
@@ -270,7 +270,7 @@ export const clear_login_state = () => {
Taro.removeStorageSync("is_logged_in"); Taro.removeStorageSync("is_logged_in");
Taro.removeStorageSync("login_time"); Taro.removeStorageSync("login_time");
} catch (error) { } catch (error) {
console.error("清除登录状态失败:", error); console.warn("清除登录状态失败:", error);
} }
}; };
@@ -374,7 +374,7 @@ export const refresh_login_status = async (): Promise<boolean> => {
// 检查本地存储的登录状态 // 检查本地存储的登录状态
return check_login_status(); return check_login_status();
} catch (error) { } catch (error) {
console.error("刷新登录状态失败:", error); console.warn("刷新登录状态失败:", error);
return false; return false;
} }
}; };
@@ -385,7 +385,7 @@ export const updateUserPhone = async (payload: ChangePhoneParams) => {
const response = await httpService.post("/user/update_phone", payload); const response = await httpService.post("/user/update_phone", payload);
return response; return response;
} catch (error) { } catch (error) {
console.error("更新用户手机号失败:", error); console.warn("更新用户手机号失败:", error);
throw error; throw error;
} }
}; };
@@ -402,7 +402,7 @@ export const getUserInfoById = async (id) => {
); );
return response; return response;
} catch (error) { } catch (error) {
console.error("获取用户信息失败:", error); console.warn("获取用户信息失败:", error);
throw error; throw error;
} }
}; };
@@ -415,7 +415,7 @@ export const followUser = async (following_id) => {
}); });
return response; return response;
} catch (error) { } catch (error) {
console.error("关注失败:", error); console.warn("关注失败:", error);
throw error; throw error;
} }
}; };
@@ -428,7 +428,7 @@ export const unFollowUser = async (following_id) => {
}); });
return response; return response;
} catch (error) { } catch (error) {
console.error("取消关注失败:", error); console.warn("取消关注失败:", error);
throw error; throw error;
} }
}; };
@@ -464,7 +464,7 @@ export const silentLogin = async (): Promise<LoginResponse> => {
console.log("微信登录结果:", login_result); console.log("微信登录结果:", login_result);
if (!login_result.code) { if (!login_result.code) {
console.error("微信登录失败未获取到code"); console.warn("微信登录失败未获取到code");
return { return {
success: false, success: false,
message: "微信登录失败", message: "微信登录失败",
@@ -506,14 +506,14 @@ export const silentLogin = async (): Promise<LoginResponse> => {
user_info, user_info,
}; };
} else { } else {
console.error("静默登录失败:", auth_response.message); console.warn("静默登录失败:", auth_response.message);
return { return {
success: false, success: false,
message: auth_response.message || "静默登录失败", message: auth_response.message || "静默登录失败",
}; };
} }
} catch (error) { } catch (error) {
console.error("静默登录异常:", error); console.warn("静默登录异常:", error);
return { return {
success: false, success: false,
message: "静默登录失败,请重试", message: "静默登录失败,请重试",

View File

@@ -188,6 +188,21 @@ class OrderService {
}, },
); );
} }
// 获取退款政策
async getRefundPolicy({
order_id,
}: {
order_id: number;
}): Promise<ApiResponse<any>> {
return httpService.post(
"/payment/order_refund_policy",
{ order_id },
{
showLoading: true,
},
);
}
} }
// 导出认证服务实例 // 导出认证服务实例

View File

@@ -39,19 +39,28 @@ export interface uploadFileResponseData {
updated_at: string, updated_at: string,
} }
// 从上传错误中取出可展示的文案
function get_upload_error_msg(error: any): string {
if (!error) return "上传失败";
const msg =
error?.message ||
(typeof error?.error === "string" ? error.error : error?.error?.message) ||
(error?.data?.message ?? error?.data?.msg) ||
"";
return (msg && String(msg).trim()) || "上传失败";
}
// 发布球局类 // 发布球局类
class UploadApi { class UploadApi {
async upload(req: UploadFilesData): Promise<{ id: string, data: uploadFileResponseData }> { async upload(req: UploadFilesData): Promise<{ id: string, data: uploadFileResponseData }> {
const fullUrl = `${envConfig.apiBaseURL}/api/gallery/upload`;
let fullUrl = `${envConfig.apiBaseURL}/api/gallery/upload` const authHeader = tokenManager.getAuthHeader();
const { id, ...rest } = req;
const authHeader = tokenManager.getAuthHeader()
const { id, ...rest } = req
try { try {
const res = await Taro.uploadFile({ const res = await Taro.uploadFile({
url: fullUrl, url: fullUrl,
filePath: rest.filePath, filePath: rest.filePath,
name: 'file', name: "file",
formData: { formData: {
description: rest.description, description: rest.description,
tags: rest.tags, tags: rest.tags,
@@ -59,12 +68,17 @@ class UploadApi {
}, },
header: authHeader, header: authHeader,
}); });
return { const parsed = JSON.parse(res.data);
id, if (parsed.code !== 0) {
data: JSON.parse(res.data).data, const msg = get_upload_error_msg(parsed);
Taro.showToast({ title: msg, icon: "none" });
throw new Error(msg);
} }
return { id, data: parsed.data };
} catch (error) { } catch (error) {
throw { id, error } const msg = get_upload_error_msg(error);
Taro.showToast({ title: msg, icon: "none" });
throw { id, error };
} }
} }
@@ -103,7 +117,7 @@ class UploadApi {
throw new Error(result.message || '上传失败'); throw new Error(result.message || '上传失败');
} }
} catch (error) { } catch (error) {
console.error('上传图片失败:', error); console.warn('上传图片失败:', error);
throw error; throw error;
} }
} }

View File

@@ -2,7 +2,7 @@ import { UserInfo } from "@/components/UserInfo";
import { API_CONFIG } from "@/config/api"; import { API_CONFIG } from "@/config/api";
import httpService, { ApiResponse } from "./httpService"; import httpService, { ApiResponse } from "./httpService";
import uploadFiles from "./uploadFiles"; import uploadFiles from "./uploadFiles";
import * as Taro from "@tarojs/taro"; import * as Taro from "@tarojs/taro";
import getCurrentConfig from "@/config/env"; import getCurrentConfig from "@/config/env";
import { clear_login_state } from "@/services/loginService"; import { clear_login_state } from "@/services/loginService";
@@ -151,6 +151,7 @@ interface BackendGameData {
longitude: string; longitude: string;
venue_type: string; venue_type: string;
surface_type: string; surface_type: string;
distance_km: string;
}; };
participants: { participants: {
user: { user: {
@@ -206,7 +207,7 @@ export class UserService {
latitude = parseFloat(game.venue_dtl.latitude) || latitude; latitude = parseFloat(game.venue_dtl.latitude) || latitude;
longitude = parseFloat(game.venue_dtl.longitude) || longitude; longitude = parseFloat(game.venue_dtl.longitude) || longitude;
} }
const distance = this.calculate_distance(latitude, longitude);
// 处理地点信息 - 优先使用venue_dtl中的信息 // 处理地点信息 - 优先使用venue_dtl中的信息
let location = game.location_name || game.location || "未知地点"; let location = game.location_name || game.location || "未知地点";
@@ -227,7 +228,7 @@ export class UserService {
original_start_time: game.start_time, original_start_time: game.start_time,
end_time: game.end_time || "", end_time: game.end_time || "",
location: location, location: location,
distance_km: parseFloat(distance.replace("km", "")) || 0, distance_km: game.venue_dtl?.distance_km ,
current_players: registered_count, current_players: registered_count,
max_players: max_count, max_players: max_count,
skill_level_min: parseInt(game.skill_level_min) || 0, skill_level_min: parseInt(game.skill_level_min) || 0,
@@ -303,20 +304,7 @@ export class UserService {
return `${date_str} ${time_str}`; return `${date_str} ${time_str}`;
} }
// 计算距离(模拟实现,实际需要根据用户位置计算)
private static calculate_distance(
latitude: number,
longitude: number
): string {
if (latitude === 0 && longitude === 0) {
return "未知距离";
}
// 这里应该根据用户当前位置计算实际距离
// 暂时返回模拟距离
const distances = ["1.2km", "2.5km", "3.8km", "5.1km", "7.3km"];
return distances[Math.floor(Math.random() * distances.length)];
}
// 获取用户信息 // 获取用户信息
static async get_user_info(user_id?: string): Promise<UserInfo> { static async get_user_info(user_id?: string): Promise<UserInfo> {
try { try {
@@ -359,13 +347,11 @@ export class UserService {
last_location_province: userData.last_location_province || "", last_location_province: userData.last_location_province || "",
last_location_city: userData.last_location_city || "", last_location_city: userData.last_location_city || "",
}; };
} else { } else {
throw new Error(response.message || "获取用户信息失败"); throw new Error(response.message || "获取用户信息失败");
} }
} catch (error) { } catch (error) {
console.error("获取用户信息失败:", error); console.warn("获取用户信息失败:", error);
// 返回默认用户信息 // 返回默认用户信息
return {} as UserInfo; return {} as UserInfo;
} }
@@ -406,7 +392,7 @@ export class UserService {
throw new Error(response.message || "更新用户信息失败"); throw new Error(response.message || "更新用户信息失败");
} }
} catch (error) { } catch (error) {
console.error("更新用户信息失败:", error); console.warn("更新用户信息失败:", error);
throw error; throw error;
} }
} }
@@ -431,7 +417,7 @@ export class UserService {
throw new Error(response.message || "获取主办球局失败"); throw new Error(response.message || "获取主办球局失败");
} }
} catch (error) { } catch (error) {
console.error("获取主办球局失败:", error); console.warn("获取主办球局失败:", error);
// 返回符合ListContainer data格式的模拟数据 // 返回符合ListContainer data格式的模拟数据
return []; return [];
} }
@@ -457,7 +443,7 @@ export class UserService {
throw new Error(response.message || "获取参与球局失败"); throw new Error(response.message || "获取参与球局失败");
} }
} catch (error) { } catch (error) {
console.error("获取参与球局失败:", error); console.warn("获取参与球局失败:", error);
// 返回符合ListContainer data格式的模拟数据 // 返回符合ListContainer data格式的模拟数据
return []; return [];
} }
@@ -499,7 +485,7 @@ export class UserService {
throw new Error(response.message || "操作失败"); throw new Error(response.message || "操作失败");
} }
} catch (error) { } catch (error) {
console.error("关注操作失败:", error); console.warn("关注操作失败:", error);
throw error; throw error;
} }
} }
@@ -557,7 +543,7 @@ export class UserService {
throw new Error(response.message || "更新用户信息失败"); throw new Error(response.message || "更新用户信息失败");
} }
} catch (error) { } catch (error) {
console.error("保存用户信息失败:", error); console.warn("保存用户信息失败:", error);
throw error; throw error;
} }
} }
@@ -587,26 +573,16 @@ export class UserService {
throw new Error(response.message || "获取用户动态失败"); throw new Error(response.message || "获取用户动态失败");
} }
} catch (error) { } catch (error) {
console.error("获取用户动态失败:", error); console.warn("获取用户动态失败:", error);
return []; return [];
} }
} }
// 上传头像 // 上传头像:仅在上传成功且 save 成功时返回 ossPath失败则抛出由调用方处理避免误调 user/update
static async upload_avatar(file_path: string): Promise<string> { static async upload_avatar(file_path: string): Promise<string> {
try { const result = await uploadFiles.upload_oss_img(file_path);
// 先上传文件到服务器 await this.save_user_info({ avatar: result.ossPath });
const result = await uploadFiles.upload_oss_img(file_path); return result.ossPath;
await this.save_user_info({ avatar: result.ossPath });
// 使用新的响应格式中的file_url字段
return result.ossPath;
} catch (error) {
console.error("头像上传失败:", error);
// 如果上传失败,返回默认头像
return require("../static/userInfo/default_avatar.svg");
}
} }
// 解析用户手机号 // 解析用户手机号
@@ -626,7 +602,7 @@ export class UserService {
throw new Error(response.message || "获取手机号失败"); throw new Error(response.message || "获取手机号失败");
} }
} catch (error) { } catch (error) {
console.error("获取手机号失败:", error); console.warn("获取手机号失败:", error);
return ""; return "";
} }
} }
@@ -642,7 +618,7 @@ export class UserService {
throw new Error(message || "获取职业树失败"); throw new Error(message || "获取职业树失败");
} }
} catch (error) { } catch (error) {
console.error("获取职业树失败:", error); console.warn("获取职业树失败:", error);
return []; return [];
} }
} }
@@ -658,7 +634,7 @@ export class UserService {
throw new Error(message || "获取城市树失败"); throw new Error(message || "获取城市树失败");
} }
} catch (error) { } catch (error) {
console.error("获取职业树失败:", error); console.warn("获取职业树失败:", error);
return []; return [];
} }
} }
@@ -679,7 +655,7 @@ export class UserService {
throw new Error(message || "注销账户失败"); throw new Error(message || "注销账户失败");
} }
} catch (error) { } catch (error) {
console.error("注销账户失败:", error); console.warn("注销账户失败:", error);
} }
} }
} }
@@ -694,7 +670,7 @@ export const fetchUserProfile = async (): Promise<
const response = await httpService.post("user/detail"); const response = await httpService.post("user/detail");
return response; return response;
} catch (error) { } catch (error) {
console.error("获取用户信息失败:", error); console.warn("获取用户信息失败:", error);
throw error; throw error;
} }
}; };
@@ -709,7 +685,7 @@ export const checkNicknameChangeStatus = async (): Promise<
); );
return response; return response;
} catch (error) { } catch (error) {
console.error("获取昵称修改状态失败:", error); console.warn("获取昵称修改状态失败:", error);
throw error; throw error;
} }
}; };
@@ -721,7 +697,7 @@ export const updateNickname = async (nickname: string) => {
}); });
return response; return response;
} catch (error) { } catch (error) {
console.error("昵称修改失败:", error); console.warn("昵称修改失败:", error);
throw error; throw error;
} }
}; };
@@ -732,7 +708,7 @@ export const updateUserProfile = async (payload: Partial<UserInfoType>) => {
const response = await httpService.post("/user/update", payload); const response = await httpService.post("/user/update", payload);
return response; return response;
} catch (error) { } catch (error) {
console.error("更新用户信息失败:", error); console.warn("更新用户信息失败:", error);
throw error; throw error;
} }
}; };
@@ -747,11 +723,11 @@ export const updateUserLocation = async (
const response = await httpService.post("/user/update_location", { const response = await httpService.post("/user/update_location", {
latitude, latitude,
longitude, longitude,
force force,
}); });
return response; return response;
} catch (error) { } catch (error) {
console.error("更新用户坐标位置失败:", error); console.warn("更新用户坐标位置失败:", error);
throw error; throw error;
} }
}; };
@@ -788,13 +764,13 @@ export const handleCustomerService = async (): Promise<void> => {
console.log("打开客服成功:", res); console.log("打开客服成功:", res);
}, },
fail: (error) => { fail: (error) => {
console.error("打开客服失败:", error); console.warn("打开客服失败:", error);
// 如果官方客服不可用,显示备用联系方式 // 如果官方客服不可用,显示备用联系方式
showCustomerServiceFallback(customerService); showCustomerServiceFallback(customerService);
}, },
}); });
} catch (error) { } catch (error) {
console.error("客服功能异常:", error); console.warn("客服功能异常:", error);
// 备用方案:显示联系信息 // 备用方案:显示联系信息
showCustomerServiceFallback(); showCustomerServiceFallback();
} }
@@ -824,7 +800,7 @@ const showCustomerServiceFallback = (customerInfo?: any) => {
phoneNumber: customerInfo.phoneNumber, phoneNumber: customerInfo.phoneNumber,
}); });
} catch (error) { } catch (error) {
console.error("拨打电话失败:", error); console.warn("拨打电话失败:", error);
Taro.showToast({ Taro.showToast({
title: "拨打电话失败", title: "拨打电话失败",
icon: "none", icon: "none",
@@ -841,7 +817,7 @@ const showCustomerServiceFallback = (customerInfo?: any) => {
icon: "success", icon: "success",
}); });
} catch (error) { } catch (error) {
console.error("复制邮箱失败:", error); console.warn("复制邮箱失败:", error);
Taro.showToast({ Taro.showToast({
title: "复制失败", title: "复制失败",
icon: "none", icon: "none",

View File

@@ -39,7 +39,7 @@ export class WalletService {
throw new Error(response.message || "获取钱包信息失败"); throw new Error(response.message || "获取钱包信息失败");
} }
} catch (error) { } catch (error) {
console.error("获取钱包信息失败:", error); console.warn("获取钱包信息失败:", error);
// 返回模拟数据 // 返回模拟数据
return { return {
balance: 1588.80, balance: 1588.80,
@@ -62,7 +62,7 @@ export class WalletService {
throw new Error(response.message || "获取交易记录失败"); throw new Error(response.message || "获取交易记录失败");
} }
} catch (error) { } catch (error) {
console.error("获取交易记录失败:", error); console.warn("获取交易记录失败:", error);
// 返回模拟数据 // 返回模拟数据
return [ return [
{ {
@@ -107,7 +107,7 @@ export class WalletService {
throw new Error(response.message || "提现申请提交失败"); throw new Error(response.message || "提现申请提交失败");
} }
} catch (error) { } catch (error) {
console.error("提现申请提交失败:", error); console.warn("提现申请提交失败:", error);
throw error; throw error;
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

View File

@@ -0,0 +1,13 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_3006_15051)">
<path d="M12.8375 3.17871C11.6157 2.03414 9.9731 1.33331 8.16683 1.33331C4.3929 1.33331 1.3335 4.39271 1.3335 8.16665C1.3335 11.9406 4.3929 15 8.16683 15C10.0384 15 11.7343 14.2475 12.9684 13.0287L8.00016 7.99998L12.8375 3.17871Z" stroke="black" stroke-width="1.33333" stroke-linejoin="round"/>
<path d="M13.3333 9.33335C14.0697 9.33335 14.6667 8.73639 14.6667 8.00002C14.6667 7.26365 14.0697 6.66669 13.3333 6.66669C12.597 6.66669 12 7.26365 12 8.00002C12 8.73639 12.597 9.33335 13.3333 9.33335Z" stroke="black" stroke-width="1.33333" stroke-linejoin="round"/>
<path d="M5.6665 4.33331V6.99998" stroke="black" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M4.3335 5.66669H7.00016" stroke="black" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_3006_15051">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -93,7 +93,7 @@ function MyComponent() {
console.log('请求成功!') console.log('请求成功!')
} catch (error) { } catch (error) {
console.error('请求失败:', error) console.warn('请求失败:', error)
} finally { } finally {
setLoading(false) setLoading(false)
} }

View File

@@ -35,17 +35,22 @@ export const useDictionaryStore = create<DictionaryState>()((set, get) => ({
set({ isLoading: true, error: null }) set({ isLoading: true, error: null })
try { try {
const keys = 'publishing_requirements,court_type,court_surface,supplementary_information,game_play,fabu_tip,supported_cities,bannerListImage,bannerDetailImage,bannerListIndex'; const keys = 'publishing_requirements,court_type,court_surface,supplementary_information,game_play,fabu_tip,supported_cities,show_home_qrcode,bannerListImage,bannerDetailImage,bannerListIndex';
const response = await commonApi.getDictionaryManyKey(keys) const response = await commonApi.getDictionaryManyKey(keys)
if (response.code === 0 && response.data) { if (response.code === 0 && response.data) {
const dictionaryData = {}; const dictionaryData: DictionaryData = {};
keys.split(',').forEach(key => { keys.split(',').forEach(key => {
const list = response.data[key]; const raw = response.data[key];
// 单值配置首页是否展示二维码1/0 或 true/false
if (key === 'show_home_qrcode') {
dictionaryData[key] = raw === '1' || raw === 1 || raw === true;
return;
}
// supported_cities 格式如 "上海市||北京市",用 || 分割 // supported_cities 格式如 "上海市||北京市",用 || 分割
const listData = key === 'supported_cities' const listData = key === 'supported_cities'
? (list ? String(list).split('||').map((s) => s.trim()).filter(Boolean) : []) ? (raw ? String(raw).split('||').map((s) => s.trim()).filter(Boolean) : [])
: (list ? list.split('|') : []); : (raw ? String(raw).split('|') : []);
dictionaryData[key] = listData; dictionaryData[key] = listData;
}) })
set({ set({
@@ -71,7 +76,7 @@ export const useDictionaryStore = create<DictionaryState>()((set, get) => ({
error: errorMessage, error: errorMessage,
isLoading: false isLoading: false
}) })
console.error('获取字典数据失败:', error) console.warn('获取字典数据失败:', error)
} }
}, },

View File

@@ -76,7 +76,7 @@ export const useKeyboardStore = create<KeyboardStore>((set, get) => ({
try { try {
listener(height, true) listener(height, true)
} catch (error) { } catch (error) {
console.error('键盘监听器执行错误:', error) console.warn('键盘监听器执行错误:', error)
} }
}) })
}) })
@@ -92,7 +92,7 @@ export const useKeyboardStore = create<KeyboardStore>((set, get) => ({
try { try {
listener(0, false) listener(0, false)
} catch (error) { } catch (error) {
console.error('键盘监听器执行错误:', error) console.warn('键盘监听器执行错误:', error)
} }
}) })
}) })
@@ -150,12 +150,13 @@ export const useKeyboardStore = create<KeyboardStore>((set, get) => ({
// 导出便捷的 hooks // 导出便捷的 hooks
export const useKeyboardHeight = () => { export const useKeyboardHeight = () => {
const { keyboardHeight, isKeyboardVisible, addListener, initializeKeyboardListener } = useKeyboardStore() const { keyboardHeight, isKeyboardVisible, addListener, initializeKeyboardListener, setKeyboardVisible } = useKeyboardStore()
return { return {
keyboardHeight, keyboardHeight,
isKeyboardVisible, isKeyboardVisible,
addListener, addListener,
initializeKeyboardListener initializeKeyboardListener,
setKeyboardVisible
} }
} }

View File

@@ -410,7 +410,7 @@ export const useListStore = create<TennisStore>()((set, get) => ({
return Promise.resolve(); return Promise.resolve();
} catch (error) { } catch (error) {
console.error("更新列表数据失败:", error); console.warn("更新列表数据失败:", error);
return Promise.reject(error); return Promise.reject(error);
} }
}, },
@@ -724,7 +724,7 @@ export const useListStore = create<TennisStore>()((set, get) => ({
} }
return []; return [];
} catch (error) { } catch (error) {
console.error("获取行政区列表失败:", error); console.warn("获取行政区列表失败:", error);
return []; return [];
} }
}, },

View File

@@ -46,7 +46,7 @@ export const useMessageStore = create<MessageStore>()((set, get) => ({
set({ loading: false }); set({ loading: false });
} }
} catch (e) { } catch (e) {
console.error("获取红点信息失败:", e); console.warn("获取红点信息失败:", e);
set({ loading: false }); set({ loading: false });
} }
}, },

View File

@@ -5,7 +5,7 @@ import { UserService } from "@/services/userService";
export interface PickerOptionState { export interface PickerOptionState {
cities: any[]; cities: any[];
professions: any[]; professions: any[];
ntrpLevels: string[]; ntrpLevels: any[];
getCities: () => Promise<any>; getCities: () => Promise<any>;
getProfessions: () => Promise<any>; getProfessions: () => Promise<any>;
} }
@@ -13,7 +13,40 @@ export interface PickerOptionState {
export const usePickerOption = create<PickerOptionState>((set) => ({ export const usePickerOption = create<PickerOptionState>((set) => ({
cities: [], cities: [],
professions: [], professions: [],
ntrpLevels: ["1.5", "2.0", "2.5", "3.0", "3.5", "4.0", "4.5", "4.5+"], ntrpLevels: [
{
text: "1.5",
value: "1.5",
},
{
text: "2.0",
value: "2.0",
},
{
text: "2.5",
value: "2.5",
},
{
text: "3.0",
value: "3.0",
},
{
text: "3.5",
value: "3.5",
},
{
text: "4.0",
value: "4.0",
},
{
text: "4.5",
value: "4.5",
},
{
text: "4.5+",
value: "4.5+",
},
],
getCities: async () => { getCities: async () => {
try { try {
const res = await UserService.getCities(); const res = await UserService.getCities();

View File

@@ -5,7 +5,7 @@ export const setStorage = (key: string, data: any) => {
try { try {
Taro.setStorageSync(key, JSON.stringify(data)) Taro.setStorageSync(key, JSON.stringify(data))
} catch (error) { } catch (error) {
console.error('保存数据失败:', error) console.warn('保存数据失败:', error)
} }
} }
@@ -17,7 +17,7 @@ export const getStorage = <T>(key: string): T | null => {
} }
return null return null
} catch (error) { } catch (error) {
console.error('读取数据失败:', error) console.warn('读取数据失败:', error)
return null return null
} }
} }
@@ -26,7 +26,7 @@ export const removeStorage = (key: string) => {
try { try {
Taro.removeStorageSync(key) Taro.removeStorageSync(key)
} catch (error) { } catch (error) {
console.error('清除数据失败:', error) console.warn('清除数据失败:', error)
} }
} }
@@ -34,6 +34,6 @@ export const clearAllStorage = () => {
try { try {
Taro.clearStorageSync() Taro.clearStorageSync()
} catch (error) { } catch (error) {
console.error('清除所有数据失败:', error) console.warn('清除所有数据失败:', error)
} }
} }

View File

@@ -8,8 +8,11 @@ import {
NicknameChangeStatus, NicknameChangeStatus,
updateNickname as updateNicknameApi, updateNickname as updateNicknameApi,
} from "@/services/userService"; } from "@/services/userService";
import evaluateService, { LastTimeTestResult } from "@/services/evaluateService"; import evaluateService, {
LastTimeTestResult,
} from "@/services/evaluateService";
import { useListStore } from "./listStore"; import { useListStore } from "./listStore";
import { getBackendErrorMsg } from "@/utils/helper";
export interface UserState { export interface UserState {
user: UserInfoType | {}; user: UserInfoType | {};
@@ -23,7 +26,6 @@ export interface UserState {
fetchLastTestResult: () => Promise<LastTimeTestResult | null>; fetchLastTestResult: () => Promise<LastTimeTestResult | null>;
} }
const getTimeNextDate = (time: string) => { const getTimeNextDate = (time: string) => {
const date = new Date(time); const date = new Date(time);
date.setDate(date.getDate() + 1); date.setDate(date.getDate() + 1);
@@ -51,8 +53,6 @@ export const useUser = create<UserState>()((set) => ({
const cachedCity = (Taro as any).getStorageSync?.(CITY_CACHE_KEY); const cachedCity = (Taro as any).getStorageSync?.(CITY_CACHE_KEY);
if (cachedCity && Array.isArray(cachedCity) && cachedCity.length === 2) { if (cachedCity && Array.isArray(cachedCity) && cachedCity.length === 2) {
// 如果有缓存的城市,使用缓存,不更新 area // 如果有缓存的城市,使用缓存,不更新 area
console.log("[userStore] 检测到缓存的城市,使用缓存,不更新 area"); console.log("[userStore] 检测到缓存的城市,使用缓存,不更新 area");
@@ -66,7 +66,10 @@ export const useUser = create<UserState>()((set) => ({
// 只有当 area 不存在时才使用用户信息中的位置 // 只有当 area 不存在时才使用用户信息中的位置
if (!currentArea) { if (!currentArea) {
const newArea: [string, string] = [userData.last_location_province||"", userData.last_location_city||""]; const newArea: [string, string] = [
userData.last_location_province || "",
userData.last_location_city || "",
];
listStore.updateArea(newArea); listStore.updateArea(newArea);
// 保存到缓存 // 保存到缓存
useUser.getState().updateCache(newArea); useUser.getState().updateCache(newArea);
@@ -75,7 +78,7 @@ export const useUser = create<UserState>()((set) => ({
return userData; return userData;
} catch (error) { } catch (error) {
console.error("获取用户信息失败:", error); console.warn("获取用户信息失败:", error);
return undefined; return undefined;
} }
}, },
@@ -85,7 +88,7 @@ export const useUser = create<UserState>()((set) => ({
try { try {
(Taro as any).setStorageSync?.(CITY_CACHE_KEY, newArea); (Taro as any).setStorageSync?.(CITY_CACHE_KEY, newArea);
} catch (error) { } catch (error) {
console.error("保存城市缓存失败:", error); console.warn("保存城市缓存失败:", error);
} }
}, },
@@ -102,8 +105,14 @@ export const useUser = create<UserState>()((set) => ({
const listStore = useListStore.getState(); const listStore = useListStore.getState();
const currentArea = listStore.area; const currentArea = listStore.area;
// 只有当 area 不存在或与 userLastLocationProvince 不一致时才更新 // 只有当 area 不存在或与 userLastLocationProvince 不一致时才更新
if (!currentArea || currentArea[1] !== userInfo.last_location_province) { if (
const newArea: [string, string] = [userInfo.last_location_province || "", userInfo.last_location_city || ""]; !currentArea ||
currentArea[1] !== userInfo.last_location_province
) {
const newArea: [string, string] = [
userInfo.last_location_province || "",
userInfo.last_location_city || "",
];
listStore.updateArea(newArea); listStore.updateArea(newArea);
} }
} }
@@ -114,7 +123,11 @@ export const useUser = create<UserState>()((set) => ({
// 只有在更新头像等需要服务器返回新URL的字段时才需要重新获取 // 只有在更新头像等需要服务器返回新URL的字段时才需要重新获取
// 如果需要确保数据一致性,可以在特定场景下手动调用 fetchUserInfo // 如果需要确保数据一致性,可以在特定场景下手动调用 fetchUserInfo
} catch (error) { } catch (error) {
console.error("更新用户信息失败:", error); console.warn("更新用户信息失败:", error);
Taro.showToast({
title: getBackendErrorMsg(error, "保存失败"),
icon: "none",
});
throw error; throw error;
} }
}, },
@@ -127,7 +140,10 @@ export const useUser = create<UserState>()((set) => ({
// 如果已经有状态数据且不是强制更新,跳过,避免重复调用 // 如果已经有状态数据且不是强制更新,跳过,避免重复调用
if (!force) { if (!force) {
const currentState = useUser.getState(); const currentState = useUser.getState();
if (currentState.nicknameChangeStatus && Object.keys(currentState.nicknameChangeStatus).length > 0) { if (
currentState.nicknameChangeStatus &&
Object.keys(currentState.nicknameChangeStatus).length > 0
) {
return; return;
} }
} }
@@ -142,7 +158,7 @@ export const useUser = create<UserState>()((set) => ({
}, },
}); });
} catch (error) { } catch (error) {
console.error("检查昵称变更状态失败:", error); console.warn("检查昵称变更状态失败:", error);
} finally { } finally {
isCheckingNicknameStatus = false; isCheckingNicknameStatus = false;
} }
@@ -156,7 +172,7 @@ export const useUser = create<UserState>()((set) => ({
user: { ...state.user, nickname }, user: { ...state.user, nickname },
})); }));
} catch (error) { } catch (error) {
console.error("更新用户昵称失败:", error); console.warn("更新用户昵称失败:", error);
} }
}, },
// NTRP 测试结果缓存 // NTRP 测试结果缓存
@@ -190,7 +206,7 @@ export const useUser = create<UserState>()((set) => ({
} }
return null; return null;
} catch (error) { } catch (error) {
console.error("获取NTRP测试结果失败:", error); console.warn("获取NTRP测试结果失败:", error);
return null; return null;
} finally { } finally {
isFetchingLastTestResult = false; isFetchingLastTestResult = false;

View File

@@ -25,7 +25,7 @@ const TestPage: React.FC = () => {
} }
}, },
fail(err) { fail(err) {
console.error('订阅失败:', err); console.warn('订阅失败:', err);
Taro.showToast({ Taro.showToast({
title: '订阅失败', title: '订阅失败',
icon: 'error', icon: 'error',

View File

@@ -206,11 +206,11 @@ const DownloadBill: React.FC = () => {
} }
}, },
fail: function (err) { fail: function (err) {
console.error("文件下载失败:", err); console.warn("文件下载失败:", err);
}, },
}); });
} catch (error) { } catch (error) {
console.error(error); console.warn(error);
} }
}; };
const handleDownloadBill = async () => { const handleDownloadBill = async () => {
@@ -240,11 +240,11 @@ const DownloadBill: React.FC = () => {
} }
}, },
fail: function (err) { fail: function (err) {
console.error("文件下载失败:", err); console.warn("文件下载失败:", err);
}, },
}); });
} catch (error) { } catch (error) {
console.error(error); console.warn(error);
} }
}; };
return ( return (

View File

@@ -82,15 +82,15 @@ const DownloadBillRecords: React.FC = () => {
console.log('打开文档成功'); console.log('打开文档成功');
}, },
fail: (err) => { fail: (err) => {
console.error('打开文档失败', err); console.warn('打开文档失败', err);
} }
}); });
} else { } else {
console.error('下载失败,状态码:', res.statusCode); console.warn('下载失败,状态码:', res.statusCode);
} }
}, },
fail: (err) => { fail: (err) => {
console.error('下载失败', err); console.warn('下载失败', err);
} }
}); });
} }

View File

@@ -22,6 +22,7 @@ import {
} from "@/store/pickerOptionsStore"; } from "@/store/pickerOptionsStore";
import { handleCustomerService } from "@/services/userService"; import { handleCustomerService } from "@/services/userService";
import evaluateService from "@/services/evaluateService"; import evaluateService from "@/services/evaluateService";
import { getBackendErrorMsg } from "@/utils/helper";
const EditProfilePage: React.FC = () => { const EditProfilePage: React.FC = () => {
const { updateUserInfo, updateNickname } = useUserActions(); const { updateUserInfo, updateNickname } = useUserActions();
@@ -44,6 +45,7 @@ const EditProfilePage: React.FC = () => {
country: info?.country ?? "", country: info?.country ?? "",
province: info?.province ?? "", province: info?.province ?? "",
city: info?.city ?? "", city: info?.city ?? "",
district: info?.district ?? "",
}; };
}; };
const [form_data, setFormData] = useState(getInitialFormData()); const [form_data, setFormData] = useState(getInitialFormData());
@@ -85,6 +87,7 @@ const EditProfilePage: React.FC = () => {
country: info?.country ?? "", country: info?.country ?? "",
province: info?.province ?? "", province: info?.province ?? "",
city: info?.city ?? "", city: info?.city ?? "",
district: info?.district ?? "",
}); });
} }
@@ -140,7 +143,7 @@ const EditProfilePage: React.FC = () => {
// city: user_data.city || "", // city: user_data.city || "",
// }); // });
// } catch (error) { // } catch (error) {
// console.error("加载用户信息失败:", error); // console.warn("加载用户信息失败:", error);
// Taro.showToast({ // Taro.showToast({
// title: "加载用户信息失败", // title: "加载用户信息失败",
// icon: "error", // icon: "error",
@@ -167,9 +170,9 @@ const EditProfilePage: React.FC = () => {
icon: "success", icon: "success",
}); });
} catch (error) { } catch (error) {
console.error("头像上传失败:", error); console.warn("头像上传失败:", error);
Taro.showToast({ Taro.showToast({
title: "头像上传失败", title: error.message,
icon: "none", icon: "none",
}); });
} }
@@ -244,10 +247,10 @@ const EditProfilePage: React.FC = () => {
icon: "success", icon: "success",
}); });
} catch (error) { } catch (error) {
console.error("保存失败:", error); console.warn("保存失败:", error);
Taro.showToast({ Taro.showToast({
title: "保存失败", title: getBackendErrorMsg(error, "保存失败"),
icon: "error", icon: "none",
}); });
} }
}; };
@@ -305,10 +308,10 @@ const EditProfilePage: React.FC = () => {
icon: "success", icon: "success",
}); });
} catch (error) { } catch (error) {
console.error("保存失败:", error); console.warn("保存失败:", error);
Taro.showToast({ Taro.showToast({
title: "保存失败", title: getBackendErrorMsg(error, "保存失败"),
icon: "error", icon: "none",
}); });
} }
}; };
@@ -358,11 +361,11 @@ const EditProfilePage: React.FC = () => {
}); });
return; return;
} }
const [country, province, city] = e; const [province, city, district] = e;
handle_field_edit({ handle_field_edit({
country: String(country ?? ""),
province: String(province ?? ""), province: String(province ?? ""),
city: String(city ?? ""), city: String(city ?? ""),
district: String(district ?? ""),
}); });
}; };
@@ -436,7 +439,7 @@ const EditProfilePage: React.FC = () => {
const phone = await UserService.parse_phone(e.detail.code); const phone = await UserService.parse_phone(e.detail.code);
handle_field_edit("phone", phone); handle_field_edit("phone", phone);
} catch (e) { } catch (e) {
console.error("解析手机号失败:", e); console.warn("解析手机号失败:", e);
Taro.showToast({ Taro.showToast({
title: "解析手机号失败,请重试", title: "解析手机号失败,请重试",
icon: "none", icon: "none",
@@ -562,9 +565,8 @@ const EditProfilePage: React.FC = () => {
</View> </View>
<View className="item_right"> <View className="item_right">
<Text <Text
className={`item_value ${ className={`item_value ${form_data.gender ? "" : "placeholder"
form_data.gender ? "" : "placeholder" }`}
}`}
> >
{convert_db_gender_to_display(form_data.gender)} {convert_db_gender_to_display(form_data.gender)}
</Text> </Text>
@@ -595,9 +597,8 @@ const EditProfilePage: React.FC = () => {
</View> </View>
<View className="item_right"> <View className="item_right">
<Text <Text
className={`item_value ${ className={`item_value ${form_data.birthday ? "" : "placeholder"
form_data.birthday ? "" : "placeholder" }`}
}`}
> >
{form_data.birthday || "选择生日"} {form_data.birthday || "选择生日"}
</Text> </Text>
@@ -626,9 +627,8 @@ const EditProfilePage: React.FC = () => {
</View> </View>
<View className="item_right"> <View className="item_right">
<Text <Text
className={`item_value ${ className={`item_value ${form_data.personal_profile ? "" : "placeholder"
form_data.personal_profile ? "" : "placeholder" }`}
}`}
> >
{form_data.personal_profile.replace(/\n/g, " ") || {form_data.personal_profile.replace(/\n/g, " ") ||
"介绍一下自己"} "介绍一下自己"}
@@ -659,16 +659,17 @@ const EditProfilePage: React.FC = () => {
</View> </View>
<View className="item_right"> <View className="item_right">
<Text <Text
className={`item_value ${ className={`item_value ${form_data.province ||
form_data.country || form_data.city ||
form_data.province || form_data.district
form_data.city ? ""
? "" : "placehoder"
: "placehoder" }`}
}`}
> >
{form_data.country || form_data.province || form_data.city {form_data.province ||
? `${form_data.country} ${form_data.province} ${form_data.city}` form_data.city ||
form_data.district
? `${form_data.province} ${form_data.city} ${form_data.district}`
: "选择所在地区"} : "选择所在地区"}
</Text> </Text>
<Image <Image
@@ -693,9 +694,8 @@ const EditProfilePage: React.FC = () => {
</View> </View>
<View className="item_right"> <View className="item_right">
<Text <Text
className={`item_value ${ className={`item_value ${form_data.ntrp_level ? "" : "placeholder"
form_data.ntrp_level ? "" : "placeholder" }`}
}`}
> >
{form_data.ntrp_level || "测测你的 NTRP 水平"} {form_data.ntrp_level || "测测你的 NTRP 水平"}
</Text> </Text>
@@ -720,14 +720,12 @@ const EditProfilePage: React.FC = () => {
<Text className="item_label"></Text> <Text className="item_label"></Text>
</View> </View>
<View <View
className={`item_right ${ className={`item_right ${form_data.occupation ? "" : "placeholder"
form_data.occupation ? "" : "placeholder" }`}
}`}
> >
<Text <Text
className={`item_value ${ className={`item_value ${form_data.occupation ? "" : "placeholder"
form_data.occupation ? "" : "placeholder" }`}
}`}
> >
{form_data.occupation || "填写你的职业"} {form_data.occupation || "填写你的职业"}
</Text> </Text>
@@ -767,9 +765,9 @@ const EditProfilePage: React.FC = () => {
> >
{form_data.phone {form_data.phone
? form_data.phone.replace( ? form_data.phone.replace(
/(\d{3})(\d{4})(\d{4})/, /(\d{3})(\d{4})(\d{4})/,
"$1 $2 $3" "$1 $2 $3"
) )
: "未绑定"} : "未绑定"}
</Button> </Button>
<Image <Image
@@ -885,8 +883,8 @@ const EditProfilePage: React.FC = () => {
visible={location_picker_visible} visible={location_picker_visible}
setvisible={setLocationPickerVisible} setvisible={setLocationPickerVisible}
value={ value={
form_data.country form_data.province
? [form_data.country, form_data.province, form_data.city] ? [form_data.province, form_data.city, form_data.district]
: getDefaultOption(cities) : getDefaultOption(cities)
} }
onChange={handle_location_change} onChange={handle_location_change}
@@ -899,15 +897,12 @@ const EditProfilePage: React.FC = () => {
title="选择 NTRP 自评水平" title="选择 NTRP 自评水平"
confirmText="保存" confirmText="保存"
ntrpTested={ntrpTested} ntrpTested={ntrpTested}
options={ntrpLevels.map((level) => ({ options={ntrpLevels}
text: level,
value: level,
}))}
type="ntrp" type="ntrp"
// img={(user_info as UserInfoType)?.avatar_url} // img={(user_info as UserInfoType)?.avatar_url}
visible={ntrp_picker_visible} visible={ntrp_picker_visible}
setvisible={setNtrpPickerVisible} setvisible={setNtrpPickerVisible}
value={[form_data.ntrp_level || "2.5"]} value={!form_data.ntrp_level ? ["2.5"] : [form_data.ntrp_level]}
onChange={handle_ntrp_level_change} onChange={handle_ntrp_level_change}
/> />
)} )}

View File

@@ -85,7 +85,7 @@ const FollowPage: React.FC = () => {
})); }));
} catch (error) { } catch (error) {
console.error(`加载${TAB_CONFIG.find(t => t.key === tab)?.label}列表失败:`, error); console.warn(`加载${TAB_CONFIG.find(t => t.key === tab)?.label}列表失败:`, error);
Taro.showToast({ Taro.showToast({
title: '加载失败', title: '加载失败',
icon: 'none' icon: 'none'
@@ -163,7 +163,7 @@ const FollowPage: React.FC = () => {
} }
} catch (error) { } catch (error) {
console.error('关注操作失败:', error); console.warn('关注操作失败:', error);
Taro.showToast({ Taro.showToast({
title: '操作失败', title: '操作失败',
icon: 'none' icon: 'none'
@@ -202,7 +202,7 @@ const FollowPage: React.FC = () => {
try { try {
load_user_list(default_tab, true); load_user_list(default_tab, true);
} catch (error) { } catch (error) {
console.error('初始化加载失败:', error); console.warn('初始化加载失败:', error);
Taro.showToast({ Taro.showToast({
title: '初始化失败', title: '初始化失败',
icon: 'none' icon: 'none'
@@ -243,7 +243,7 @@ const FollowPage: React.FC = () => {
// icon: 'success' // icon: 'success'
// }); // });
} catch (error) { } catch (error) {
console.error('取消关注失败:', error); console.warn('取消关注失败:', error);
Taro.showToast({ Taro.showToast({
title: '操作失败', title: '操作失败',
icon: 'none' icon: 'none'

View File

@@ -62,7 +62,7 @@ const MyselfPage: React.FC = () => {
// // } // // }
// // set_game_records(games_data); // // set_game_records(games_data);
// } catch (error) { // } catch (error) {
// console.error("加载用户数据失败:", error); // console.warn("加载用户数据失败:", error);
// Taro.showToast({ // Taro.showToast({
// title: "加载失败,请重试", // title: "加载失败,请重试",
// icon: "error", // icon: "error",
@@ -89,11 +89,10 @@ const MyselfPage: React.FC = () => {
await fetchUserInfo(); await fetchUserInfo();
} }
// 获取测试结果 // 获取测试结果
const res = await evaluateService.getLastResult(); const res = await evaluateService.getLastResult();
if (res.code === 0) { if (res.code === 0) {
console.log( "getLastResult", res.data); console.log("getLastResult", res.data);
setHasTestInLastMonth(res.data.has_test_in_last_month); setHasTestInLastMonth(res.data.has_test_in_last_month);
} }
}; };
@@ -110,25 +109,28 @@ const MyselfPage: React.FC = () => {
}); });
// 分类球局数据(使用 useCallback 包装,避免每次渲染都创建新函数) // 分类球局数据(使用 useCallback 包装,避免每次渲染都创建新函数)
const classifyGameRecords = useCallback(( const classifyGameRecords = useCallback(
game_records: TennisMatch[] (
): { notEndGames: TennisMatch[]; finishedGames: TennisMatch[] } => { game_records: TennisMatch[]
const now = new Date().getTime(); ): { notEndGames: TennisMatch[]; finishedGames: TennisMatch[] } => {
return game_records.reduce( const now = new Date().getTime();
(result, cur) => { return game_records.reduce(
let { end_time } = cur; (result, cur) => {
end_time = end_time.replace(/\s/, "T"); let { end_time } = cur;
new Date(end_time).getTime() > now end_time = end_time.replace(/\s/, "T");
? result.notEndGames.push(cur) new Date(end_time).getTime() > now
: result.finishedGames.push(cur); ? result.notEndGames.push(cur)
return result; : result.finishedGames.unshift(cur);
}, return result;
{ },
notEndGames: [] as TennisMatch[], {
finishedGames: [] as TennisMatch[], notEndGames: [] as TennisMatch[],
} finishedGames: [] as TennisMatch[],
); }
}, []); );
},
[]
);
// 加载球局数据(使用 useCallback 包装,避免每次渲染都创建新函数) // 加载球局数据(使用 useCallback 包装,避免每次渲染都创建新函数)
const load_game_data = useCallback(async () => { const load_game_data = useCallback(async () => {
@@ -150,7 +152,7 @@ const MyselfPage: React.FC = () => {
setEndedGameRecords(finishedGames); setEndedGameRecords(finishedGames);
// set_game_records(games_data); // set_game_records(games_data);
} catch (error) { } catch (error) {
console.error("加载球局数据失败:", error); console.warn("加载球局数据失败:", error);
} }
}, [active_tab, user_info, classifyGameRecords]); }, [active_tab, user_info, classifyGameRecords]);
@@ -176,7 +178,7 @@ const MyselfPage: React.FC = () => {
duration: 1500, duration: 1500,
}); });
} catch (error) { } catch (error) {
console.error("关注操作失败:", error); console.warn("关注操作失败:", error);
Taro.showToast({ Taro.showToast({
title: "操作失败,请重试", title: "操作失败,请重试",
icon: "error", icon: "error",
@@ -272,7 +274,7 @@ const MyselfPage: React.FC = () => {
</View> </View>
{/* 球局列表 */} {/* 球局列表 */}
<View className="game_list_section" > <View className="game_list_section">
<ScrollView scrollY refresherBackground="#FAFAFA"> <ScrollView scrollY refresherBackground="#FAFAFA">
<ListContainer <ListContainer
data={game_records} data={game_records}

View File

@@ -117,7 +117,7 @@ const OtherUserPage: React.FC = () => {
}); });
setIsFollowing(userData.is_following || false); setIsFollowing(userData.is_following || false);
} catch (error) { } catch (error) {
console.error("加载用户数据失败:", error); console.warn("加载用户数据失败:", error);
Taro.showToast({ Taro.showToast({
title: "加载失败", title: "加载失败",
icon: "none", icon: "none",
@@ -141,7 +141,7 @@ const OtherUserPage: React.FC = () => {
end_time = end_time.replace(/\s/, "T"); end_time = end_time.replace(/\s/, "T");
new Date(end_time).getTime() > now new Date(end_time).getTime() > now
? result.notEndGames.push(cur) ? result.notEndGames.push(cur)
: result.finishedGames.push(cur); : result.finishedGames.unshift(cur);
return result; return result;
}, },
{ {
@@ -169,7 +169,7 @@ const OtherUserPage: React.FC = () => {
setGameRecords(notEndGames); setGameRecords(notEndGames);
setEndedGameRecords(finishedGames); setEndedGameRecords(finishedGames);
} catch (error) { } catch (error) {
console.error("加载球局数据失败:", error); console.warn("加载球局数据失败:", error);
Taro.showToast({ Taro.showToast({
title: "加载失败,请重试", title: "加载失败,请重试",
icon: "error", icon: "error",
@@ -200,7 +200,7 @@ const OtherUserPage: React.FC = () => {
duration: 1500, duration: 1500,
}); });
} catch (error) { } catch (error) {
console.error("关注操作失败:", error); console.warn("关注操作失败:", error);
Taro.showToast({ Taro.showToast({
title: "操作失败", title: "操作失败",
icon: "none", icon: "none",
@@ -235,7 +235,7 @@ const OtherUserPage: React.FC = () => {
try { try {
await Promise.all([load_user_data(), load_game_data()]); await Promise.all([load_user_data(), load_game_data()]);
} catch (error) { } catch (error) {
console.error("刷新失败:", error); console.warn("刷新失败:", error);
} finally { } finally {
setRefreshing(false); setRefreshing(false);
} }
@@ -329,9 +329,8 @@ const OtherUserPage: React.FC = () => {
overflow: "hidden", overflow: "hidden",
}} }}
listLoadErrorWrapperHeight="fit-content" listLoadErrorWrapperHeight="fit-content"
listLoadErrorWidth="320px" listLoadErrorWidth="410px"
listLoadErrorHeight="152px" listLoadErrorHeight="185px"
listLoadErrorScale="1.2"
defaultShowNum={3} defaultShowNum={3}
/> />
</ScrollView> </ScrollView>
@@ -375,9 +374,8 @@ const OtherUserPage: React.FC = () => {
collapse={true} collapse={true}
style={{ paddingBottom: "90px", overflow: "hidden" }} style={{ paddingBottom: "90px", overflow: "hidden" }}
listLoadErrorWrapperHeight="fit-content" listLoadErrorWrapperHeight="fit-content"
listLoadErrorWidth="320px" listLoadErrorWidth="410px"
listLoadErrorHeight="152px" listLoadErrorHeight="185px"
listLoadErrorScale="1.2"
defaultShowNum={3} defaultShowNum={3}
/> />
</ScrollView> </ScrollView>

View File

@@ -84,7 +84,7 @@ const QueryTransactions = () => {
setSearchHistory(response.data); setSearchHistory(response.data);
} }
} catch (e) { } catch (e) {
console.error(e); console.warn(e);
} }
}; };
/** /**

View File

@@ -17,6 +17,7 @@
top: 0; top: 0;
z-index: 100; z-index: 100;
background-color: #f5f5f5; background-color: #f5f5f5;
padding: 0 20px;
} }
.detail-navigator { .detail-navigator {
@@ -48,18 +49,22 @@
} }
.form-item { .form-item {
padding: 20px; padding: 16px 0;
box-sizing: border-box; box-sizing: border-box;
height: 50px; height: 50px;
display: flex; display: flex;
gap: 10px; gap: 20px;
align-items: center; align-items: center;
border-bottom: 1px solid #0000000d; border-bottom: 1px solid #0000000d;
font-size: 14px; font-size: 14px;
.form-label { .form-label {
width: 56px;
text-align: right; text-align: right;
font-weight: 600;
}
Input {
flex: 1;
} }
} }
@@ -106,6 +111,5 @@
border-radius: 16px; border-radius: 16px;
font-size: 16px; font-size: 16px;
font-weight: 600; font-weight: 600;
transform: translateX(20px);
} }
} }

View File

@@ -67,10 +67,12 @@ const SetTransactionPassword: React.FC = () => {
const { new_password, confirm_password, sms_code } = formData; const { new_password, confirm_password, sms_code } = formData;
if (handleType === "set") { if (handleType === "set") {
setValid( setValid(
(sms_code !== "") && (new_password.length === 6) && (confirm_password.length === 6) sms_code !== "" &&
new_password.length === 6 &&
confirm_password.length === 6
); );
} else { } else {
setValid((new_password.length === 6) && (confirm_password.length === 6)); setValid(new_password.length === 6 && confirm_password.length === 6);
} }
}, [formData]); }, [formData]);
@@ -97,7 +99,7 @@ const SetTransactionPassword: React.FC = () => {
icon: "success", icon: "success",
}); });
let delta = handleType === "set" ? 1 : 2; let delta = handleType === "set" ? 1 : 2;
Taro.navigateBack({ delta }) Taro.navigateBack({ delta });
} catch (error) { } catch (error) {
Taro.showToast({ Taro.showToast({
title: "设置交易密码失败", title: "设置交易密码失败",
@@ -166,13 +168,11 @@ const SetTransactionPassword: React.FC = () => {
Taro.navigateBack(); Taro.navigateBack();
}} }}
/> />
<View <View className="form-item" style={{ marginTop: `${totalHeight}px` }}>
className="form-item"
style={{ marginTop: `${totalHeight}px` }}
>
<Text className="form-label"></Text> <Text className="form-label"></Text>
<Input <Input
placeholder="请输入交易密码" placeholder="请输入交易密码"
placeholderStyle="color: #d9d9d9;"
password password
type="number" type="number"
maxlength={6} maxlength={6}
@@ -185,6 +185,7 @@ const SetTransactionPassword: React.FC = () => {
<Text className="form-label"></Text> <Text className="form-label"></Text>
<Input <Input
placeholder="请再次输入交易密码" placeholder="请再次输入交易密码"
placeholderStyle="color: #d9d9d9;"
password password
type="number" type="number"
maxlength={6} maxlength={6}
@@ -198,6 +199,7 @@ const SetTransactionPassword: React.FC = () => {
<Text className="form-label"></Text> <Text className="form-label"></Text>
<Input <Input
placeholder="请输入验证码" placeholder="请输入验证码"
placeholderStyle="color: #d9d9d9;"
type="number" type="number"
onInput={(e) => { onInput={(e) => {
handleInput(e, "sms_code"); handleInput(e, "sms_code");

View File

@@ -3,6 +3,7 @@
box-sizing: border-box; box-sizing: border-box;
height: 100vh; height: 100vh;
overflow-y: hidden; overflow-y: hidden;
padding: 0 20px;
.custom-navbar { .custom-navbar {
height: 56px; height: 56px;
/* 通常与原生导航栏高度一致 */ /* 通常与原生导航栏高度一致 */
@@ -48,18 +49,22 @@
} }
.form-item { .form-item {
padding: 20px; padding: 16px 0;
box-sizing: border-box; box-sizing: border-box;
height: 50px; height: 50px;
display: flex; display: flex;
gap: 10px; gap: 20px;
align-items: center; align-items: center;
border-bottom: 1px solid #0000000d; border-bottom: 1px solid #0000000d;
font-size: 14px; font-size: 14px;
.form-label { .form-label {
width: 56px;
text-align: right; text-align: right;
font-weight: 600;
}
Input {
flex: 1;
} }
} }
@@ -95,6 +100,5 @@
border-radius: 16px; border-radius: 16px;
font-size: 16px; font-size: 16px;
font-weight: 600; font-weight: 600;
transform: translateX(20px);
} }
} }

View File

@@ -139,10 +139,7 @@ const ValidPhone: React.FC = () => {
Taro.navigateBack(); Taro.navigateBack();
}} }}
/> />
<View <View className="form-item" style={{ marginTop: `${totalHeight}px` }}>
className="form-item"
style={{ marginTop: `${totalHeight}px` }}
>
<Text className="form-label"></Text> <Text className="form-label"></Text>
<Input defaultValue={formData.phone} type="number" disabled></Input> <Input defaultValue={formData.phone} type="number" disabled></Input>
</View> </View>
@@ -150,6 +147,7 @@ const ValidPhone: React.FC = () => {
<Text className="form-label"></Text> <Text className="form-label"></Text>
<Input <Input
placeholder="请输入验证码" placeholder="请输入验证码"
placeholderStyle="color: #d9d9d9;"
type="number" type="number"
onInput={(e) => { onInput={(e) => {
handleInput(e, "sms_code"); handleInput(e, "sms_code");

View File

@@ -195,7 +195,7 @@ const WalletPage: React.FC = () => {
const res = await httpService.post("/wallet/check_password_status"); const res = await httpService.post("/wallet/check_password_status");
set_password_status(res.data.is_password_set); set_password_status(res.data.is_password_set);
} catch (e) { } catch (e) {
console.error("检查交易密码状态失败:", e); console.warn("检查交易密码状态失败:", e);
} }
}; };
@@ -218,7 +218,7 @@ const WalletPage: React.FC = () => {
total_withdraw, total_withdraw,
}); });
} catch (error: any) { } catch (error: any) {
console.error("加载钱包数据失败:", error); console.warn("加载钱包数据失败:", error);
let errorMessage = "加载失败,请重试"; let errorMessage = "加载失败,请重试";
if ( if (
@@ -262,7 +262,7 @@ const WalletPage: React.FC = () => {
set_transactions([]); set_transactions([]);
} }
} catch (error: any) { } catch (error: any) {
console.error("加载交易记录失败:", error); console.warn("加载交易记录失败:", error);
set_transactions([]); set_transactions([]);
let errorMessage = "加载交易记录失败"; let errorMessage = "加载交易记录失败";
@@ -500,6 +500,7 @@ const WalletPage: React.FC = () => {
{/* 顶部导航栏 */} {/* 顶部导航栏 */}
<GeneralNavbar <GeneralNavbar
title={pageTitle} title={pageTitle}
backgroundColor="#ffffff"
showBack={true} showBack={true}
showAvatar={false} showAvatar={false}
onBack={() => { onBack={() => {

View File

@@ -49,7 +49,7 @@ const Withdrawal: React.FC = () => {
}, [initializeKeyboardListener, addListener]); }, [initializeKeyboardListener, addListener]);
const [showTips, setShowTips] = useState(false); const [showTips, setShowTips] = useState(false);
const [tipsText, setTipsText] = useState<string>(""); const [tipsText, setTipsText] = useState<string>("");
const [inputValue, setInputValue] = useState<string>("0.00"); const [inputValue, setInputValue] = useState<string>("");
const [walletInfo, setWalletInfo] = useState<WalletInfo>({ const [walletInfo, setWalletInfo] = useState<WalletInfo>({
balance: "0.00", balance: "0.00",
}); });
@@ -65,8 +65,8 @@ const Withdrawal: React.FC = () => {
const [inputValueObj, setInputValueObj] = useState({ const [inputValueObj, setInputValueObj] = useState({
integer: "0", integer: "0",
decimal: "00" decimal: "00",
}) });
useDidShow(() => { useDidShow(() => {
load_wallet_data(); load_wallet_data();
@@ -87,11 +87,11 @@ const Withdrawal: React.FC = () => {
}, [show_withdraw_popup]); }, [show_withdraw_popup]);
useEffect(() => { useEffect(() => {
const value = Number(inputValue).toFixed(2).split(".") const value = Number(inputValue).toFixed(2).split(".");
const integer = value[0] const integer = value[0];
const decimal = value[1] const decimal = value[1];
setInputValueObj({ integer, decimal }) setInputValueObj({ integer, decimal });
}, [inputValue]) }, [inputValue]);
const validateWithdrawAmount = (amount: string) => { const validateWithdrawAmount = (amount: string) => {
if (Number(amount) > Number(walletInfo.balance)) { if (Number(amount) > Number(walletInfo.balance)) {
@@ -135,7 +135,7 @@ const Withdrawal: React.FC = () => {
total_withdraw, total_withdraw,
}); });
} catch (error: any) { } catch (error: any) {
console.error("加载钱包数据失败:", error); console.warn("加载钱包数据失败:", error);
let errorMessage = "加载失败,请重试"; let errorMessage = "加载失败,请重试";
if ( if (
@@ -171,7 +171,7 @@ const Withdrawal: React.FC = () => {
setMapErrorCodes(mapErrorCodes); setMapErrorCodes(mapErrorCodes);
} }
} catch (error: any) { } catch (error: any) {
console.error("获取提现错误码失败:", error); console.warn("获取提现错误码失败:", error);
} }
}; };
const handleWithdraw = async () => { const handleWithdraw = async () => {
@@ -307,11 +307,11 @@ const Withdrawal: React.FC = () => {
showBack={true} showBack={true}
showAvatar={false} showAvatar={false}
onBack={() => { onBack={() => {
const pages = Taro.getCurrentPages() const pages = Taro.getCurrentPages();
const prevPage = pages[pages.length - 2] const prevPage = pages[pages.length - 2];
prevPage.setData({ prevPage.setData({
updateList: withdrawSuccess updateList: withdrawSuccess,
}) });
Taro.navigateBack(); Taro.navigateBack();
}} }}
/> />
@@ -325,6 +325,7 @@ const Withdrawal: React.FC = () => {
<Input <Input
type="digit" type="digit"
placeholder="0.00" placeholder="0.00"
placeholderStyle="color:rgba(120, 120, 128, 0.12);"
cursorColor="#000" cursorColor="#000"
value={inputValue} value={inputValue}
onInput={handleInput} onInput={handleInput}

View File

@@ -33,7 +33,7 @@ const initUserAuthCore = async (): Promise<void> => {
await fetchUserInfo(); await fetchUserInfo();
await checkNicknameChangeStatus(); await checkNicknameChangeStatus();
} catch (error) { } catch (error) {
console.error("获取用户信息失败:", error); console.warn("获取用户信息失败:", error);
} }
} else { } else {
// 未登录,尝试静默登录 // 未登录,尝试静默登录
@@ -45,12 +45,12 @@ const initUserAuthCore = async (): Promise<void> => {
await checkNicknameChangeStatus(); await checkNicknameChangeStatus();
} }
} catch (error) { } catch (error) {
console.error("静默登录失败:", error); console.warn("静默登录失败:", error);
// 静默登录失败不影响使用 // 静默登录失败不影响使用
} }
} }
} catch (error) { } catch (error) {
console.error("初始化用户授权失败:", error); console.warn("初始化用户授权失败:", error);
} finally { } finally {
isInitializingAuth = false; isInitializingAuth = false;
authInitPromise = null; authInitPromise = null;

View File

@@ -35,21 +35,48 @@ export function base64ToTempFilePath(base64Data: string): Promise<string> {
} }
interface TaroGetImageInfo {
getImageInfo(option: {
src: string;
success?: (res: { width: number; height: number }) => void;
fail?: (err: unknown) => void;
}): void;
}
/** 获取图片宽高 */ /** 获取图片宽高 */
function getImageWh(src: string): Promise<{ width: number; height: number }> { function getImageWh(src: string): Promise<{ width: number; height: number }> {
return new Promise((resolve) => { return new Promise((resolve, reject) => {
Taro.getImageInfo({ (Taro as TaroGetImageInfo).getImageInfo({
src, src,
success: ({ width, height }) => resolve({ width, height }), success: ({ width, height }) => resolve({ width, height }),
fail: (e) => reject(e),
}); });
}); });
} }
/** 加载图片 */ /** 加载图片 */
function loadImage(canvas: any, src: string): Promise<any> { function loadImage(canvas: any, src: string): Promise<any> {
return new Promise((resolve) => { return new Promise((resolve, reject) => {
let timer: any;
const img = canvas.createImage(); const img = canvas.createImage();
img.onload = () => resolve(img);
img.crossOrigin = "anonymous"
img.onload = () => {
clearTimeout(timer);
resolve(img);
};
img.onerror = () => {
clearTimeout(timer);
console.log('img error', src)
}
timer = setTimeout(() => {
reject(new Error(`Image load timeout: ${src}`));
}, 8000);
img.src = src; img.src = src;
}); });
} }
@@ -137,6 +164,7 @@ async function drawRotateCoverImage(
rotate = 0 // 旋转角度(弧度) rotate = 0 // 旋转角度(弧度)
) { ) {
const { width, height } = await getImageWh(src); const { width, height } = await getImageWh(src);
console.log('width', width, 'height', height)
const scale = Math.max(w / width, h / height); const scale = Math.max(w / width, h / height);
const newW = width * scale; const newW = width * scale;
const newH = height * scale; const newH = height * scale;
@@ -170,6 +198,7 @@ async function drawRotateCoverImage(
// 绘制 cover // 绘制 cover
ctx.drawImage(img, offsetX, offsetY, newW, newH); ctx.drawImage(img, offsetX, offsetY, newW, newH);
console.log('drawImage', offsetX, offsetY, newW, newH)
ctx.restore(); ctx.restore();
} }
@@ -281,6 +310,8 @@ function drawTextWrap(
/** 核心纯函数:生成海报图片 */ /** 核心纯函数:生成海报图片 */
export async function generatePosterImage(data: any): Promise<string> { export async function generatePosterImage(data: any): Promise<string> {
console.log("start !!!!"); console.log("start !!!!");
// const dpr = Taro.getWindowInfo().pixelRatio; // const dpr = Taro.getWindowInfo().pixelRatio;
const dpr = 1; const dpr = 1;
@@ -288,19 +319,30 @@ export async function generatePosterImage(data: any): Promise<string> {
const width = 600; const width = 600;
const height = 1000; const height = 1000;
console.log('width', width, 'height', height)
const canvas = Taro.createOffscreenCanvas({ type: "2d", width: width * dpr, height: height * dpr }); const canvas = Taro.createOffscreenCanvas({ type: "2d", width: width * dpr, height: height * dpr });
const ctx = canvas.getContext("2d"); const ctx = canvas.getContext("2d");
ctx.scale(dpr, dpr); ctx.scale(dpr, dpr);
console.log('ctx', ctx)
// 背景渐变 // 背景渐变
roundRectGradient(ctx, 0, 0, width, height, 24, "#BFFFEF", "#F2FFFC"); roundRectGradient(ctx, 0, 0, width, height, 24, "#BFFFEF", "#F2FFFC");
console.log('bgUrl', bgUrl)
const bgImg = await loadImage(canvas, bgUrl); const bgImg = await loadImage(canvas, bgUrl);
ctx.drawImage(bgImg, 0, 0, width, height); ctx.drawImage(bgImg, 0, 0, width, height);
console.log('bgUrlend', )
roundRotateRect(ctx, 70, 100, width - 140, width - 140, 20, '#fff', deg2rad(-6)); roundRotateRect(ctx, 70, 100, width - 140, width - 140, 20, '#fff', deg2rad(-6));
// 顶部图片 // 顶部图片
const mainImg = await loadImage(canvas, data.mainCoursal); const mainImg = await loadImage(canvas, data.mainCoursal);
console.log('mainCoursal', data.mainCoursal)
await drawRotateCoverImage( await drawRotateCoverImage(
ctx, ctx,
canvas, canvas,
@@ -369,6 +411,8 @@ export async function generatePosterImage(data: any): Promise<string> {
left = 20; left = 20;
const dateImg = await loadImage(canvas, dateIcon); const dateImg = await loadImage(canvas, dateIcon);
console.log('dateIcon', dateIcon)
await drawCoverImage( await drawCoverImage(
ctx, ctx,
canvas, canvas,
@@ -395,6 +439,7 @@ export async function generatePosterImage(data: any): Promise<string> {
left = 20; left = 20;
top += 24; top += 24;
const mapImg = await loadImage(canvas, mapIcon); const mapImg = await loadImage(canvas, mapIcon);
await drawCoverImage(ctx, canvas, mapIcon, mapImg, left, top, 40, 40, 12); await drawCoverImage(ctx, canvas, mapIcon, mapImg, left, top, 40, 40, 12);
@@ -431,11 +476,13 @@ export async function generatePosterImage(data: any): Promise<string> {
ctx.font = "400 20px sans-serif"; ctx.font = "400 20px sans-serif";
ctx.fillText("长按识别二维码,快来加入,有你就有场!", left, height - 30/* top */); ctx.fillText("长按识别二维码,快来加入,有你就有场!", left, height - 30/* top */);
console.log('canvas', canvas)
// 导出图片 // 导出图片
const { tempFilePath } = await Taro.canvasToTempFilePath({ const { tempFilePath } = await Taro.canvasToTempFilePath({
canvas, canvas,
fileType: 'png', fileType: 'png',
quality: 0.7, quality: 0.7,
}); });
console.log('tempFilePath', tempFilePath)
return tempFilePath; return tempFilePath;
} }

View File

@@ -23,7 +23,7 @@ export const sceneRedirectLogic = (options, defaultPage: string) => {
url: query ? `/${defaultPage}?${query}` : `/${defaultPage}`, url: query ? `/${defaultPage}?${query}` : `/${defaultPage}`,
}); });
} catch (e) { } catch (e) {
console.error(e); console.warn(e);
} }
}; };
@@ -94,6 +94,19 @@ export function toast(message) {
Taro.showToast({ title: message, icon: "none" }); Taro.showToast({ title: message, icon: "none" });
} }
/** 从接口/请求错误中取出后端返回的文案,用于保存失败等场景的 toast */
export function getBackendErrorMsg(error: any, fallback = "操作失败"): string {
if (error == null) return fallback;
const msg =
error?.message ||
(typeof error?.error === "string" ? error.error : error?.error?.message) ||
error?.data?.message ||
error?.data?.msg ||
"";
const s = String(msg).trim();
return s || fallback;
}
// 将·作为连接符插入到标签文本之间 // 将·作为连接符插入到标签文本之间
export function insertDotInTags(tags: string[]) { export function insertDotInTags(tags: string[]) {
if (!tags) return []; if (!tags) return [];

View File

@@ -533,7 +533,6 @@ const drawShareCard = async (ctx: any, data: ShareCardData, offscreen: any): Pro
const locationPath = await loadImage(`${OSS_BASE}/front/ball/images/adc9a167-2ea9-4e3b-b963-6a894a1fd91b.jpg`) const locationPath = await loadImage(`${OSS_BASE}/front/ball/images/adc9a167-2ea9-4e3b-b963-6a894a1fd91b.jpg`)
ctx.drawImage(locationPath, iconX, locationInfoY, iconSize, iconSize) ctx.drawImage(locationPath, iconX, locationInfoY, iconSize, iconSize)
drawBoldText(ctx, data.venueName, danDaX, locationInfoY + 10, locationFontSize, '#000000') drawBoldText(ctx, data.venueName, danDaX, locationInfoY + 10, locationFontSize, '#000000')
try { try {
const wxAny: any = (typeof (globalThis as any) !== 'undefined' && (globalThis as any).wx) ? (globalThis as any).wx : null const wxAny: any = (typeof (globalThis as any) !== 'undefined' && (globalThis as any).wx) ? (globalThis as any).wx : null
if (wxAny && typeof wxAny.canvasToTempFilePath === 'function') { if (wxAny && typeof wxAny.canvasToTempFilePath === 'function') {
@@ -554,7 +553,7 @@ const drawShareCard = async (ctx: any, data: ShareCardData, offscreen: any): Pro
console.log('Canvas绘制命令已发送') console.log('Canvas绘制命令已发送')
} catch (error) { } catch (error) {
console.error('绘制分享卡片失败:', error) console.warn('绘制分享卡片失败:', error)
Taro.showToast({ Taro.showToast({
title: '生成分享卡片失败', title: '生成分享卡片失败',
icon: 'none' icon: 'none'

View File

@@ -16,7 +16,7 @@ class TokenManager {
try { try {
return Taro.getStorageSync(TOKEN_KEY) return Taro.getStorageSync(TOKEN_KEY)
} catch (error) { } catch (error) {
console.error('获取访问令牌失败:', error) console.warn('获取访问令牌失败:', error)
return null return null
} }
} }
@@ -26,7 +26,7 @@ class TokenManager {
try { try {
return Taro.getStorageSync(REFRESH_TOKEN_KEY) return Taro.getStorageSync(REFRESH_TOKEN_KEY)
} catch (error) { } catch (error) {
console.error('获取刷新令牌失败:', error) console.warn('获取刷新令牌失败:', error)
return null return null
} }
} }
@@ -36,7 +36,7 @@ class TokenManager {
try { try {
return Taro.getStorageSync(TOKEN_EXPIRES_KEY) return Taro.getStorageSync(TOKEN_EXPIRES_KEY)
} catch (error) { } catch (error) {
console.error('获取令牌过期时间失败:', error) console.warn('获取令牌过期时间失败:', error)
return null return null
} }
} }
@@ -54,7 +54,7 @@ class TokenManager {
Taro.setStorageSync(TOKEN_EXPIRES_KEY, tokenInfo.expiresAt) Taro.setStorageSync(TOKEN_EXPIRES_KEY, tokenInfo.expiresAt)
} }
} catch (error) { } catch (error) {
console.error('设置令牌失败:', error) console.warn('设置令牌失败:', error)
} }
} }
@@ -65,7 +65,7 @@ class TokenManager {
Taro.removeStorageSync(REFRESH_TOKEN_KEY) Taro.removeStorageSync(REFRESH_TOKEN_KEY)
Taro.removeStorageSync(TOKEN_EXPIRES_KEY) Taro.removeStorageSync(TOKEN_EXPIRES_KEY)
} catch (error) { } catch (error) {
console.error('清除令牌失败:', error) console.warn('清除令牌失败:', error)
} }
} }

View File

@@ -17,7 +17,7 @@ export function saveImage(url) {
await Taro.saveImageToPhotosAlbum({ filePath }) await Taro.saveImageToPhotosAlbum({ filePath })
Taro.showToast({ title: "保存成功" }) Taro.showToast({ title: "保存成功" })
} catch (e) { } catch (e) {
console.error(e) console.warn(e)
Taro.showToast({ title: "图片保存失败", icon: "none" }) Taro.showToast({ title: "图片保存失败", icon: "none" })
} }
} }