Compare commits
57 Commits
54b7a27af5
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 69248d33c8 | |||
| 1973ec3faa | |||
| abc2dfeecf | |||
| bafb44ff06 | |||
| 0e27d801a4 | |||
| 0a0203e36d | |||
| 2656c59475 | |||
| 23eb9dc467 | |||
| 44f971b1c2 | |||
| 4a553c63fc | |||
|
|
baa60bbfcb | ||
|
|
64f0267457 | ||
|
|
8688b6b82d | ||
|
|
1678f787a3 | ||
|
|
3571740280 | ||
|
|
b6801cdde2 | ||
|
|
e1ebcd949b | ||
|
|
044e84a5b4 | ||
|
|
7833c2f552 | ||
|
|
9e4282545f | ||
|
|
99c8026f61 | ||
| 2a9e8668a0 | |||
| 08092a89ab | |||
|
|
4f0cdad920 | ||
| 05966b2acb | |||
|
|
4cf2b959b5 | ||
|
|
43610dcf99 | ||
|
|
05aa820466 | ||
|
|
b154e31f8f | ||
|
|
669ee2fe4e | ||
|
|
281ee2b746 | ||
|
|
132c74d27c | ||
|
|
6b6a4c9480 | ||
|
|
0f8dd44f5a | ||
| 82ba753b8b | |||
| 159d81ed12 | |||
| 22965eedf3 | |||
| 49935dd049 | |||
|
|
cab90aa1cb | ||
| 632da5112d | |||
| 28955e9da1 | |||
| 70a66fabdc | |||
| c47ebce43c | |||
| b0f4b5713d | |||
| f7f10f5d15 | |||
|
|
2bcdd93479 | ||
|
|
af2c472030 | ||
|
|
8d0ed5b1b3 | ||
|
|
e99986c52a | ||
|
|
4b2f6707cc | ||
|
|
a019fe473b | ||
|
|
1d0d2edaa2 | ||
| 5926e096b5 | |||
|
|
e07f2ad2d1 | ||
|
|
bfc6a149f0 | ||
|
|
6f73bb6d99 | ||
|
|
744169fe34 |
2
.env.dev_local
Normal file
2
.env.dev_local
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
APP_ENV=dev_local
|
||||||
|
TARO_APP_ID=wx815b533167eb7b53
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -8,4 +8,6 @@ node_modules/
|
|||||||
src/config/env.ts
|
src/config/env.ts
|
||||||
.vscode
|
.vscode
|
||||||
*.http
|
*.http
|
||||||
|
.cursor
|
||||||
|
.codewiz
|
||||||
|
|
||||||
|
|||||||
@@ -473,7 +473,7 @@ async function safeMarkAsRead(type, ids) {
|
|||||||
})
|
})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// 标记已读失败不影响用户体验,静默处理
|
// 标记已读失败不影响用户体验,静默处理
|
||||||
console.error('标记已读失败:', err)
|
console.warn('标记已读失败:', err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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: {
|
||||||
|
|||||||
79
config/env.config.ts
Normal file
79
config/env.config.ts
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
/**
|
||||||
|
* 统一环境配置(dev/sit/pr)
|
||||||
|
* 构建时通过 APP_ENV 选择,defineConstants 注入业务代码
|
||||||
|
* project.config.json 的 appid 由 scripts/sync-project-config.js 同步
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type EnvType = "dev" | "dev_local" | "sit" | "pr";
|
||||||
|
|
||||||
|
export interface EnvConfig {
|
||||||
|
name: string;
|
||||||
|
apiBaseURL: string;
|
||||||
|
ossBaseURL: string;
|
||||||
|
appid: string;
|
||||||
|
timeout: number;
|
||||||
|
enableLog: boolean;
|
||||||
|
enableMock: boolean;
|
||||||
|
customerService: {
|
||||||
|
corpId: string;
|
||||||
|
serviceUrl: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseConfig = {
|
||||||
|
apiBaseURL: "https://tennis.bimwe.com",
|
||||||
|
ossBaseURL: "https://bimwe.oss-cn-shanghai.aliyuncs.com",
|
||||||
|
appid: "wx815b533167eb7b53", // 测试号
|
||||||
|
timeout: 15000,
|
||||||
|
enableLog: true,
|
||||||
|
enableMock: false,
|
||||||
|
customerService: {
|
||||||
|
corpId: "ww51fc969e8b76af82",
|
||||||
|
serviceUrl: "https://work.weixin.qq.com/kfid/kfc64085b93243c5c91",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const envConfigs: Record<EnvType, EnvConfig> = {
|
||||||
|
// 本地开发:API 指向本地或测试服
|
||||||
|
dev: {
|
||||||
|
name: "DEV",
|
||||||
|
// apiBaseURL: "http://localhost:9098",
|
||||||
|
...baseConfig
|
||||||
|
},
|
||||||
|
// 本地联调:API 指向本机
|
||||||
|
dev_local: {
|
||||||
|
name: "DEV_LOCAL",
|
||||||
|
|
||||||
|
...Object.assign(baseConfig, {
|
||||||
|
apiBaseURL: "http://localhost:9098",
|
||||||
|
})
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
// SIT 测试环境
|
||||||
|
sit: {
|
||||||
|
name: "SIT",
|
||||||
|
...Object.assign(baseConfig, {
|
||||||
|
apiBaseURL: "https://tennis.bimwe.com",
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// PR 生产环境
|
||||||
|
pr: {
|
||||||
|
name: "PR",
|
||||||
|
apiBaseURL: "https://youchang.qiongjingtiyu.com",
|
||||||
|
ossBaseURL: "https://youchang2026.oss-cn-shanghai.aliyuncs.com",
|
||||||
|
appid: "wx915ecf6c01bea4ec", // 生产小程序 appid,按实际填写
|
||||||
|
timeout: 10000,
|
||||||
|
enableLog: false,
|
||||||
|
enableMock: false,
|
||||||
|
customerService: {
|
||||||
|
corpId: "ww9a2d9a5d9410c664",
|
||||||
|
serviceUrl: "https://work.weixin.qq.com/kfid/kfcd355e162e0390684",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getEnvConfig(env: EnvType): EnvConfig {
|
||||||
|
return envConfigs[env];
|
||||||
|
}
|
||||||
128
config/env.ts
Normal file
128
config/env.ts
Normal 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()
|
||||||
@@ -2,11 +2,21 @@ import { defineConfig, type UserConfigExport } from '@tarojs/cli'
|
|||||||
import TsconfigPathsPlugin from 'tsconfig-paths-webpack-plugin'
|
import TsconfigPathsPlugin from 'tsconfig-paths-webpack-plugin'
|
||||||
import devConfig from './dev'
|
import devConfig from './dev'
|
||||||
import prodConfig from './prod'
|
import prodConfig from './prod'
|
||||||
// import vitePluginImp from 'vite-plugin-imp'
|
import { getEnvConfig, type EnvType } from './env.config'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
|
||||||
|
// 环境:dev(本地) | dev_local(联调) | sit(测试) | pr(生产)
|
||||||
|
const ENV_LIST: EnvType[] = ['dev', 'dev_local', 'sit', 'pr']
|
||||||
|
|
||||||
// https://taro-docs.jd.com/docs/next/config#defineconfig-辅助函数
|
// https://taro-docs.jd.com/docs/next/config#defineconfig-辅助函数
|
||||||
export default defineConfig<'webpack5'>(async (merge, { command, mode }) => {
|
export default defineConfig<'webpack5'>(async (merge, { command, mode }) => {
|
||||||
|
const appEnv = (
|
||||||
|
(ENV_LIST.includes(mode as EnvType) ? mode : process.env.APP_ENV) ||
|
||||||
|
(process.env.NODE_ENV === 'production' ? 'pr' : 'dev')
|
||||||
|
) as EnvType
|
||||||
|
|
||||||
|
const envConfig = getEnvConfig(appEnv)
|
||||||
|
|
||||||
const baseConfig: UserConfigExport<'webpack5'> = {
|
const baseConfig: UserConfigExport<'webpack5'> = {
|
||||||
projectName: 'playBallTogether',
|
projectName: 'playBallTogether',
|
||||||
date: '2025-8-9',
|
date: '2025-8-9',
|
||||||
@@ -22,6 +32,13 @@ export default defineConfig<'webpack5'>(async (merge, { command, mode }) => {
|
|||||||
outputRoot: 'dist',
|
outputRoot: 'dist',
|
||||||
plugins: ['@tarojs/plugin-html'],
|
plugins: ['@tarojs/plugin-html'],
|
||||||
defineConstants: {
|
defineConstants: {
|
||||||
|
'process.env.APP_ENV': JSON.stringify(appEnv),
|
||||||
|
'process.env.API_BASE_URL': JSON.stringify(envConfig.apiBaseURL),
|
||||||
|
'process.env.OSS_BASE_URL': JSON.stringify(envConfig.ossBaseURL),
|
||||||
|
'process.env.ENABLE_LOG': JSON.stringify(envConfig.enableLog),
|
||||||
|
'process.env.TIMEOUT': JSON.stringify(envConfig.timeout),
|
||||||
|
'process.env.CUSTOMER_CORP_ID': JSON.stringify(envConfig.customerService.corpId),
|
||||||
|
'process.env.CUSTOMER_SERVICE_URL': JSON.stringify(envConfig.customerService.serviceUrl),
|
||||||
},
|
},
|
||||||
alias: {
|
alias: {
|
||||||
'@': path.resolve(__dirname, '..', 'src'),
|
'@': path.resolve(__dirname, '..', 'src'),
|
||||||
@@ -76,6 +93,9 @@ export default defineConfig<'webpack5'>(async (merge, { command, mode }) => {
|
|||||||
},
|
},
|
||||||
// @ts-expect-error: Taro 类型定义缺少 mini.hot
|
// @ts-expect-error: Taro 类型定义缺少 mini.hot
|
||||||
hot: true,
|
hot: true,
|
||||||
|
projectConfig: {
|
||||||
|
appid: envConfig.appid,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
h5: {
|
h5: {
|
||||||
publicPath: '/',
|
publicPath: '/',
|
||||||
|
|||||||
35
package.json
35
package.json
@@ -10,32 +10,17 @@
|
|||||||
"framework": "React"
|
"framework": "React"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "npm run build:weapp ",
|
"dev": "npm run dev:weapp",
|
||||||
"dev": "npm run dev:weapp ",
|
"dev:local": "npm run dev:weapp:dev_local",
|
||||||
"build:weapp": "taro build --type weapp --mode production",
|
"dev:weapp": "node scripts/sync-project-config.js dev && taro build --type weapp --mode dev --watch",
|
||||||
"build:swan": "taro build --type swan",
|
"dev:weapp:dev_local": "node scripts/sync-project-config.js dev_local && taro build --type weapp --mode dev_local --watch",
|
||||||
"build:alipay": "taro build --type alipay",
|
"build": "npm run build:weapp",
|
||||||
"build:tt": "taro build --type tt",
|
"build:weapp": "node scripts/sync-project-config.js pr && taro build --type weapp --mode pr",
|
||||||
"build:h5": "taro build --type h5",
|
"build:sit": "node scripts/sync-project-config.js sit && taro build --type weapp --mode sit",
|
||||||
"build:rn": "taro build --type rn",
|
"build:pr": "node scripts/sync-project-config.js pr && taro build --type weapp --mode pr",
|
||||||
"build:qq": "taro build --type qq",
|
"dev:h5": "npm run build:h5 -- --watch"
|
||||||
"build:jd": "taro build --type jd",
|
|
||||||
"build:quickapp": "taro build --type quickapp",
|
|
||||||
"dev:weapp": "npm run build:weapp -- --watch",
|
|
||||||
"dev:swan": "npm run build:swan -- --watch",
|
|
||||||
"dev:alipay": "npm run build:alipay -- --watch",
|
|
||||||
"dev:tt": "npm run build:tt -- --watch",
|
|
||||||
"dev:h5": "npm run build:h5 -- --watch",
|
|
||||||
"dev:rn": "npm run build:rn -- --watch",
|
|
||||||
"dev:qq": "npm run build:qq -- --watch",
|
|
||||||
"dev:jd": "npm run build:jd -- --watch",
|
|
||||||
"dev:quickapp": "npm run build:quickapp -- --watch"
|
|
||||||
},
|
},
|
||||||
"browserslist": [
|
"browserslist": ["last 3 versions", "Android >= 4.1", "ios >= 8"],
|
||||||
"last 3 versions",
|
|
||||||
"Android >= 4.1",
|
|
||||||
"ios >= 8"
|
|
||||||
],
|
|
||||||
"author": "",
|
"author": "",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/plugin-transform-runtime": "^7.28.3",
|
"@babel/plugin-transform-runtime": "^7.28.3",
|
||||||
|
|||||||
25
scripts/sync-project-config.js
Normal file
25
scripts/sync-project-config.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
require('ts-node/register/transpile-only');
|
||||||
|
|
||||||
|
const envArg = process.argv[2];
|
||||||
|
const appEnv = envArg || process.env.APP_ENV || (process.env.NODE_ENV === 'production' ? 'pr' : 'dev');
|
||||||
|
|
||||||
|
const envConfigPath = path.resolve(__dirname, '../config/env.config.ts');
|
||||||
|
const { envConfigs } = require(envConfigPath);
|
||||||
|
|
||||||
|
const config = envConfigs[appEnv];
|
||||||
|
if (!config) {
|
||||||
|
console.warn(`[sync-project-config] Unknown APP_ENV: ${appEnv}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const projectConfigPath = path.resolve(__dirname, '../project.config.json');
|
||||||
|
const projectConfigRaw = fs.readFileSync(projectConfigPath, 'utf-8');
|
||||||
|
const projectConfig = JSON.parse(projectConfigRaw);
|
||||||
|
|
||||||
|
projectConfig.appid = config.appid;
|
||||||
|
|
||||||
|
fs.writeFileSync(projectConfigPath, JSON.stringify(projectConfig, null, 2) + '\n', 'utf-8');
|
||||||
|
console.log(`[sync-project-config] project.config.json appid -> ${config.appid} (${appEnv})`);
|
||||||
@@ -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, "回复失败"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
215
src/components/CustomPopup/CustomPopup.tsx
Normal file
215
src/components/CustomPopup/CustomPopup.tsx
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
import React, { useRef, useState, useEffect } from 'react'
|
||||||
|
import type { CSSProperties, ReactNode } from 'react'
|
||||||
|
import { View, Text } from '@tarojs/components'
|
||||||
|
import { Button } from '@nutui/nutui-react-taro'
|
||||||
|
import { useKeyboardHeight } from '@/store/keyboardStore'
|
||||||
|
import styles from './index.module.scss'
|
||||||
|
|
||||||
|
export interface CustomPopupProps {
|
||||||
|
visible: boolean
|
||||||
|
onClose: () => void
|
||||||
|
title?: ReactNode
|
||||||
|
showHeader?: boolean
|
||||||
|
hideFooter?: boolean
|
||||||
|
cancelText?: string
|
||||||
|
confirmText?: string
|
||||||
|
onCancel?: () => void
|
||||||
|
onConfirm?: () => void
|
||||||
|
children?: ReactNode
|
||||||
|
className?: string
|
||||||
|
style?: CSSProperties
|
||||||
|
// 与 CommonPopup 保持入参一致
|
||||||
|
position?: 'center' | 'bottom' | 'top' | 'left' | 'right'
|
||||||
|
round?: boolean
|
||||||
|
zIndex?: number
|
||||||
|
enableDragToClose?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const CustomPopup: React.FC<CustomPopupProps> = ({
|
||||||
|
visible,
|
||||||
|
onClose,
|
||||||
|
title,
|
||||||
|
showHeader = false,
|
||||||
|
hideFooter = false,
|
||||||
|
cancelText = '返回',
|
||||||
|
confirmText = '完成',
|
||||||
|
onCancel,
|
||||||
|
onConfirm,
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
style,
|
||||||
|
position = 'bottom',
|
||||||
|
round = true,
|
||||||
|
zIndex,
|
||||||
|
enableDragToClose = true,
|
||||||
|
}) => {
|
||||||
|
const [dragOffset, setDragOffset] = useState(0)
|
||||||
|
const [isDragging, setIsDragging] = useState(false)
|
||||||
|
const touchStartY = useRef(0)
|
||||||
|
|
||||||
|
// 使用全局键盘状态
|
||||||
|
const { keyboardHeight, isKeyboardVisible, addListener, initializeKeyboardListener, setKeyboardVisible } = useKeyboardHeight()
|
||||||
|
|
||||||
|
// 当弹窗显示时,设置键盘为不可见
|
||||||
|
useEffect(() => {
|
||||||
|
if (visible) {
|
||||||
|
setKeyboardVisible(false)
|
||||||
|
}
|
||||||
|
}, [visible, setKeyboardVisible])
|
||||||
|
|
||||||
|
// 使用全局键盘状态监听
|
||||||
|
useEffect(() => {
|
||||||
|
// 初始化全局键盘监听器
|
||||||
|
initializeKeyboardListener()
|
||||||
|
|
||||||
|
// 添加本地监听器
|
||||||
|
const removeListener = addListener((height, visible) => {
|
||||||
|
console.log('CustomPopup 收到键盘变化:', height, visible)
|
||||||
|
})
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
removeListener()
|
||||||
|
}
|
||||||
|
}, [initializeKeyboardListener, addListener])
|
||||||
|
|
||||||
|
if (!visible) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
if (onCancel) {
|
||||||
|
onCancel()
|
||||||
|
} else {
|
||||||
|
onClose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleTouchStart = (e: any) => {
|
||||||
|
if (!enableDragToClose) return
|
||||||
|
|
||||||
|
touchStartY.current = e.touches[0].clientY
|
||||||
|
setIsDragging(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleTouchMove = (e: any) => {
|
||||||
|
if (!enableDragToClose || !isDragging) return
|
||||||
|
|
||||||
|
const currentY = e.touches[0].clientY
|
||||||
|
const deltaY = currentY - touchStartY.current
|
||||||
|
|
||||||
|
if (deltaY > 0) {
|
||||||
|
setDragOffset(Math.min(deltaY, 100))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleTouchEnd = () => {
|
||||||
|
if (!enableDragToClose || !isDragging) return
|
||||||
|
|
||||||
|
setIsDragging(false)
|
||||||
|
|
||||||
|
if (dragOffset > 50) {
|
||||||
|
onClose()
|
||||||
|
}
|
||||||
|
|
||||||
|
setDragOffset(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
const overlayAlignItems =
|
||||||
|
position === 'center'
|
||||||
|
? 'center'
|
||||||
|
: position === 'top'
|
||||||
|
? 'flex-start'
|
||||||
|
: 'flex-end'
|
||||||
|
|
||||||
|
const handleOverlayClick = () => {
|
||||||
|
onClose()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 阻止弹窗内的触摸事件冒泡
|
||||||
|
const handleTouchMoveInPopup = (e: any) => {
|
||||||
|
if (!isKeyboardVisible) {
|
||||||
|
e.stopPropagation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
className={styles['custom-popup-overlay']}
|
||||||
|
style={{ zIndex: zIndex ?? undefined, alignItems: overlayAlignItems }}
|
||||||
|
onClick={handleOverlayClick}
|
||||||
|
>
|
||||||
|
<View className={styles['custom-popup-move']} onTouchMove={handleTouchMoveInPopup} catchMove></View>
|
||||||
|
<View
|
||||||
|
className={`${styles['custom-popup']} ${className ? className : ''}`}
|
||||||
|
style={{
|
||||||
|
...style,
|
||||||
|
paddingBottom: isKeyboardVisible ? `${keyboardHeight}px` : undefined,
|
||||||
|
}}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{enableDragToClose && (
|
||||||
|
<View
|
||||||
|
className={styles['custom-popup__drag-handle-container']}
|
||||||
|
onTouchStart={handleTouchStart}
|
||||||
|
onTouchMove={handleTouchMove}
|
||||||
|
onTouchEnd={handleTouchEnd}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
className={styles['custom-popup__drag-handle']}
|
||||||
|
style={{
|
||||||
|
transform: `translateX(-50%) translateY(${dragOffset * 0.3}px)`,
|
||||||
|
opacity: isDragging ? 0.8 : 1,
|
||||||
|
transition: isDragging ? 'none' : 'all 0.3s ease-out',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{showHeader && (
|
||||||
|
<View className={styles['custom-popup__header']}>
|
||||||
|
{typeof title === 'string' ? (
|
||||||
|
<Text className={styles['custom-popup__title']}>{title}</Text>
|
||||||
|
) : (
|
||||||
|
title
|
||||||
|
)}
|
||||||
|
<View className={styles['close_button']} onClick={onClose}>
|
||||||
|
<View className={styles['close_icon']}>
|
||||||
|
<View className={styles['close_line']} />
|
||||||
|
<View className={styles['close_line']} />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<View className={styles['custom-popup__body']}>{children}</View>
|
||||||
|
|
||||||
|
{!hideFooter && !isKeyboardVisible && (
|
||||||
|
<View className={styles['custom-popup__footer']}>
|
||||||
|
<Button
|
||||||
|
className={`${styles['custom-popup__btn']} ${styles['custom-popup__btn-cancel']}`}
|
||||||
|
type="default"
|
||||||
|
size="small"
|
||||||
|
onClick={handleCancel}
|
||||||
|
>
|
||||||
|
{cancelText}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
className={`${styles['custom-popup__btn']} ${styles['custom-popup__btn-confirm']}`}
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
onClick={onConfirm}
|
||||||
|
>
|
||||||
|
{confirmText}
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CustomPopup
|
||||||
|
|
||||||
155
src/components/CustomPopup/index.module.scss
Normal file
155
src/components/CustomPopup/index.module.scss
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
@use "~@/scss/themeColor.scss" as theme;
|
||||||
|
|
||||||
|
.custom-popup-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
z-index: 999;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.custom-popup-move{
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 998;
|
||||||
|
}
|
||||||
|
.custom-popup {
|
||||||
|
position: relative;
|
||||||
|
z-index: 999;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: theme.$page-background-color;
|
||||||
|
border-radius: 20px 20px 0 0;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: padding-bottom 0.3s ease;
|
||||||
|
.custom-popup__drag-handle-container {
|
||||||
|
position: relative;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-popup__drag-handle {
|
||||||
|
position: absolute;
|
||||||
|
top: 6px;
|
||||||
|
left: 50%;
|
||||||
|
width: 90px;
|
||||||
|
height: 30px;
|
||||||
|
z-index: 10;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "";
|
||||||
|
width: 32px;
|
||||||
|
height: 4px;
|
||||||
|
background-color: rgba(22, 24, 35, 0.2);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-popup__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 16px 20px;
|
||||||
|
|
||||||
|
.custom-popup__title {
|
||||||
|
font-family: "PingFang SC";
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 22px;
|
||||||
|
line-height: 1.27em;
|
||||||
|
color: #000000;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close_button {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||||
|
border-radius: 50%;
|
||||||
|
cursor: pointer;
|
||||||
|
box-shadow: 0px 4px 36px 0px rgba(0, 0, 0, 0.06);
|
||||||
|
|
||||||
|
.close_icon {
|
||||||
|
position: relative;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
|
||||||
|
.close_line {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
width: 17px;
|
||||||
|
height: 3px;
|
||||||
|
border-radius: 3px;
|
||||||
|
background: #000000;
|
||||||
|
transform: translate(-50%, -50%) rotate(45deg);
|
||||||
|
|
||||||
|
&:nth-child(2) {
|
||||||
|
transform: translate(-50%, -50%) rotate(-45deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-popup__body {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
max-height: 80vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-popup__footer {
|
||||||
|
padding: 8px 10px 0 10px;
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
background: #fafafa;
|
||||||
|
padding-bottom: max(10px, env(safe-area-inset-bottom));
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-popup__btn {
|
||||||
|
flex: 1;
|
||||||
|
font-feature-settings: "liga" off, "clig" off;
|
||||||
|
font-family: "PingFang SC";
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-popup__btn-cancel {
|
||||||
|
background: #f5f6f7;
|
||||||
|
color: #1f2329;
|
||||||
|
border: none;
|
||||||
|
width: 154px;
|
||||||
|
height: 44px;
|
||||||
|
border-radius: 12px !important;
|
||||||
|
border: 0.5px solid rgba(0, 0, 0, 0.06);
|
||||||
|
background: #fff;
|
||||||
|
padding: 4px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-popup__btn-confirm {
|
||||||
|
width: 154px;
|
||||||
|
height: 44px;
|
||||||
|
border: 0.5px solid rgba(0, 0, 0, 0.06);
|
||||||
|
background: #000;
|
||||||
|
border-radius: 12px !important;
|
||||||
|
padding: 4px 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
4
src/components/CustomPopup/index.ts
Normal file
4
src/components/CustomPopup/index.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import CustomPopup from './CustomPopup'
|
||||||
|
export default CustomPopup
|
||||||
|
export * from './CustomPopup'
|
||||||
|
|
||||||
@@ -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',
|
||||||
|
|||||||
@@ -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'
|
||||||
|
|||||||
@@ -13,7 +13,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: 16px;
|
font-size: 16px;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
@@ -32,7 +34,9 @@
|
|||||||
padding-top: 24px;
|
padding-top: 24px;
|
||||||
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;
|
||||||
@@ -48,8 +52,10 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
.tips {
|
.tips {
|
||||||
color: rgba(60, 60, 67, 0.60);
|
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: 16px;
|
font-size: 16px;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
@@ -62,13 +68,15 @@
|
|||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background: #F0F0F0;
|
background: #f0f0f0;
|
||||||
|
|
||||||
.input {
|
.input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
&:placeholder-shown {
|
&:placeholder-shown {
|
||||||
color: rgba(60, 60, 67, 0.30);
|
color: rgba(60, 60, 67, 0.3);
|
||||||
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;
|
||||||
@@ -84,11 +92,12 @@
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 44px;
|
height: 44px;
|
||||||
border-top: 0.5px solid #CECECE;
|
border-top: 0.5px solid #cecece;
|
||||||
background: #FFF;
|
background: #fff;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
|
|
||||||
.confirm, .cancel {
|
.confirm,
|
||||||
|
.cancel {
|
||||||
width: 50%;
|
width: 50%;
|
||||||
height: 44px;
|
height: 44px;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -96,7 +105,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: 16px;
|
font-size: 16px;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
|
|||||||
@@ -186,10 +186,11 @@ export default forwardRef(function GameManagePopup(props, ref) {
|
|||||||
.some((item) => item.user.id === userInfo.id);
|
.some((item) => item.user.id === userInfo.id);
|
||||||
|
|
||||||
const finished = [MATCH_STATUS.FINISHED, MATCH_STATUS.CANCELED].includes(
|
const finished = [MATCH_STATUS.FINISHED, MATCH_STATUS.CANCELED].includes(
|
||||||
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}>
|
||||||
{!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>
|
||||||
)}
|
)}
|
||||||
{!inTwoHours && !hasOtherParticiappants && (
|
{!finished && beforeStart && (
|
||||||
<View className={styles.button} onClick={handleCancelGame}>
|
<View className={styles.button} onClick={handleCancelGame}>
|
||||||
取消活动
|
取消活动
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
{hasJoin && (
|
{!finished && beforeStart && hasJoin && (
|
||||||
<View className={styles.button} onClick={handleQuitGame}>
|
<View className={styles.button} onClick={handleQuitGame}>
|
||||||
退出活动
|
退出活动
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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])
|
||||||
|
|||||||
@@ -53,7 +53,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.location-position {
|
.location-position {
|
||||||
flex: 1;
|
// flex: 1;
|
||||||
min-width: 0; // 允许缩小
|
min-width: 0; // 允许缩小
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|||||||
@@ -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]);
|
||||||
|
|
||||||
// 根据图片数量决定展示样式
|
// 根据图片数量决定展示样式
|
||||||
@@ -127,10 +132,10 @@ const ListCard: React.FC<ListCardProps> = ({
|
|||||||
return (
|
return (
|
||||||
<View className="double-image">
|
<View className="double-image">
|
||||||
<View className="image-container">
|
<View className="image-container">
|
||||||
{renderItemImage(image_list?.[0])}
|
{renderItemImage(image_list?.[1])}
|
||||||
</View>
|
</View>
|
||||||
<View className="image-container">
|
<View className="image-container">
|
||||||
{renderItemImage(image_list?.[1])}
|
{renderItemImage(image_list?.[0])}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
.listLoadErrorImg {
|
.listLoadErrorImg {
|
||||||
width: 154px;
|
width: 154px;
|
||||||
height: 154px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.listLoadErrorText {
|
.listLoadErrorText {
|
||||||
|
|||||||
@@ -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>}
|
||||||
|
|||||||
@@ -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]);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import React, { useState, useEffect, useRef } from "react";
|
import React, { useState, useEffect, useRef } from "react";
|
||||||
import CommonPopup from "@/components/CommonPopup";
|
import CommonPopup from "@/components/CommonPopup";
|
||||||
import { View } from "@tarojs/components";
|
import { View } from "@tarojs/components";
|
||||||
|
import Taro from "@tarojs/taro";
|
||||||
import CalendarUI, {
|
import CalendarUI, {
|
||||||
CalendarUIRef,
|
CalendarUIRef,
|
||||||
} from "@/components/Picker/CalendarUI/CalendarUI";
|
} from "@/components/Picker/CalendarUI/CalendarUI";
|
||||||
@@ -47,6 +48,13 @@ const DialogCalendarCard: React.FC<DialogCalendarCardProps> = ({
|
|||||||
onClose();
|
onClose();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!selected) {
|
||||||
|
Taro.showToast({
|
||||||
|
title: '请选择日期',
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
// 年份选择完成后,进入月份选择
|
// 年份选择完成后,进入月份选择
|
||||||
setType("time");
|
setType("time");
|
||||||
} else if (type === "month") {
|
} else if (type === "month") {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React, { useState, useEffect, useRef } from "react";
|
import React, { useState, useEffect, useRef } from "react";
|
||||||
import CommonPopup from "@/components/CommonPopup";
|
import CommonPopup from "@/components/CommonPopup";
|
||||||
|
import Taro from "@tarojs/taro";
|
||||||
import { View } from "@tarojs/components";
|
import { View } from "@tarojs/components";
|
||||||
import CalendarUI, {
|
import CalendarUI, {
|
||||||
CalendarUIRef,
|
CalendarUIRef,
|
||||||
@@ -32,6 +33,13 @@ const DayDialog: React.FC<DayDialogProps> = ({
|
|||||||
} | null>(null);
|
} | null>(null);
|
||||||
const handleConfirm = () => {
|
const handleConfirm = () => {
|
||||||
console.log(selected, 'selectedselected');
|
console.log(selected, 'selectedselected');
|
||||||
|
if (!selected) {
|
||||||
|
Taro.showToast({
|
||||||
|
title: '请选择日期',
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
const finalDate = dayjs(selected as Date).format("YYYY-MM-DD");
|
const finalDate = dayjs(selected as Date).format("YYYY-MM-DD");
|
||||||
if (onChange){
|
if (onChange){
|
||||||
onChange(finalDate)
|
onChange(finalDate)
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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: '知道了'
|
||||||
})
|
})
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -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: '生成分享卡片失败',
|
||||||
|
|||||||
@@ -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);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ const TextareaTag: React.FC<TextareaTagProps> = ({
|
|||||||
autoHeight={true}
|
autoHeight={true}
|
||||||
onFocus={onFocus}
|
onFocus={onFocus}
|
||||||
onBlur={onBlur}
|
onBlur={onBlur}
|
||||||
|
adjustPosition={false}
|
||||||
/>
|
/>
|
||||||
<View className={`char-count${isOverflow ? ' char-count--error' : ''}`}>
|
<View className={`char-count${isOverflow ? ' char-count--error' : ''}`}>
|
||||||
{value.description.length}/{maxLength}
|
{value.description.length}/{maxLength}
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import NumberInterval from "./NumberInterval";
|
|||||||
import TimeSelector from "./TimeSelector";
|
import TimeSelector from "./TimeSelector";
|
||||||
import TitleTextarea from "./TitleTextarea";
|
import TitleTextarea from "./TitleTextarea";
|
||||||
import CommonPopup from "./CommonPopup";
|
import CommonPopup from "./CommonPopup";
|
||||||
|
import CustomPopup from "./CustomPopup";
|
||||||
import { CalendarUI, DialogCalendarCard } from "./Picker";
|
import { CalendarUI, DialogCalendarCard } from "./Picker";
|
||||||
import CommonDialog from "./CommonDialog";
|
import CommonDialog from "./CommonDialog";
|
||||||
import PublishMenu from "./PublishMenu/PublishMenu";
|
import PublishMenu from "./PublishMenu/PublishMenu";
|
||||||
@@ -37,6 +38,7 @@ export {
|
|||||||
TimeSelector,
|
TimeSelector,
|
||||||
TitleTextarea,
|
TitleTextarea,
|
||||||
CommonPopup,
|
CommonPopup,
|
||||||
|
CustomPopup,
|
||||||
DialogCalendarCard,
|
DialogCalendarCard,
|
||||||
CalendarUI,
|
CalendarUI,
|
||||||
CommonDialog,
|
CommonDialog,
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -1,135 +1,61 @@
|
|||||||
import Taro from '@tarojs/taro'
|
import Taro from "@tarojs/taro";
|
||||||
|
|
||||||
// 环境类型
|
/**
|
||||||
export type EnvType = 'development' | 'production'
|
* 环境配置:从 config/env.config.ts 经 defineConstants 注入
|
||||||
|
* 构建时由 config/index.ts 根据 APP_ENV 选择并注入
|
||||||
|
*/
|
||||||
|
export type EnvType = "dev" | "dev_local" | "sit" | "pr";
|
||||||
|
|
||||||
// 环境配置接口
|
|
||||||
export interface EnvConfig {
|
export interface EnvConfig {
|
||||||
name: string
|
name: string;
|
||||||
apiBaseURL: string
|
apiBaseURL: string;
|
||||||
ossBaseURL: string
|
ossBaseURL: string;
|
||||||
timeout: number
|
timeout: number;
|
||||||
enableLog: boolean
|
enableLog: boolean;
|
||||||
enableMock: boolean
|
enableMock: boolean;
|
||||||
// 客服配置
|
|
||||||
customerService: {
|
customerService: {
|
||||||
corpId: string
|
corpId: string;
|
||||||
serviceUrl: string
|
serviceUrl: string;
|
||||||
phoneNumber?: string
|
};
|
||||||
email?: string
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 各环境配置
|
// 从 defineConstants 注入的编译时常量读取
|
||||||
const envConfigs: Record<EnvType, EnvConfig> = {
|
const getInjectedConfig = (): EnvConfig => ({
|
||||||
|
name: process.env.APP_ENV || "dev",
|
||||||
|
apiBaseURL: process.env.API_BASE_URL || "",
|
||||||
// 开发环境
|
ossBaseURL: process.env.OSS_BASE_URL || "",
|
||||||
development: {
|
timeout: Number(process.env.TIMEOUT) || 10000,
|
||||||
name: '开发环境',
|
enableLog: process.env.ENABLE_LOG === "true",
|
||||||
apiBaseURL: 'https://tennis.bimwe.com',
|
enableMock: false,
|
||||||
ossBaseURL: 'https://bimwe.oss-cn-shanghai.aliyuncs.com',
|
customerService: {
|
||||||
//apiBaseURL: 'http://localhost:9098',
|
corpId: process.env.CUSTOMER_CORP_ID || "",
|
||||||
timeout: 15000,
|
serviceUrl: process.env.CUSTOMER_SERVICE_URL || "",
|
||||||
enableLog: true,
|
|
||||||
enableMock: false,
|
|
||||||
// 客服配置
|
|
||||||
customerService: {
|
|
||||||
corpId: 'ww51fc969e8b76af82', // 企业ID
|
|
||||||
serviceUrl: 'https://work.weixin.qq.com/kfid/kfc64085b93243c5c91',
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getCurrentEnv = (): EnvType =>
|
||||||
|
(process.env.APP_ENV as EnvType) || "dev";
|
||||||
|
|
||||||
|
export const getCurrentConfig = (): EnvConfig => getInjectedConfig();
|
||||||
|
|
||||||
|
export const isDevelopment = (): boolean =>
|
||||||
|
getCurrentEnv() === "dev" || getCurrentEnv() === "dev_local" || getCurrentEnv() === "sit";
|
||||||
|
|
||||||
|
export const isProduction = (): boolean => getCurrentEnv() === "pr";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 生产环境1
|
|
||||||
production: {
|
|
||||||
name: '生产环境1',
|
|
||||||
apiBaseURL: 'https://tennis.bimwe.com',
|
|
||||||
ossBaseURL: 'https://bimwe.oss-cn-shanghai.aliyuncs.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',
|
|
||||||
// ossBaseURL: 'https://youchang2026.oss-cn-shanghai.aliyuncs.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 = () => {
|
export const getEnvInfo = () => {
|
||||||
const config = getCurrentConfig()
|
const config = getCurrentConfig();
|
||||||
return {
|
return {
|
||||||
env: getCurrentEnv(),
|
env: getCurrentEnv(),
|
||||||
config,
|
config,
|
||||||
taroEnv: Taro.getEnv(),
|
taroEnv: (Taro as any).getEnv?.(),
|
||||||
platform: Taro.getEnv() === Taro.ENV_TYPE.WEAPP ? '微信小程序' :
|
platform:
|
||||||
Taro.getEnv() === Taro.ENV_TYPE.WEB ? 'Web' :
|
(Taro as any).getEnv?.() === (Taro as any).ENV_TYPE?.WEAPP
|
||||||
Taro.getEnv() === Taro.ENV_TYPE.RN ? 'React Native' : '未知'
|
? "微信小程序"
|
||||||
}
|
: (Taro as any).getEnv?.() === (Taro as any).ENV_TYPE?.WEB
|
||||||
}
|
? "Web"
|
||||||
|
: "未知",
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
// 导出当前环境配置(方便直接使用)
|
export default getCurrentConfig();
|
||||||
export default getCurrentConfig()
|
|
||||||
|
|||||||
@@ -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"),
|
||||||
|
};
|
||||||
|
|||||||
@@ -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} />;
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ function isFull(counts) {
|
|||||||
function matchNtrpRequestment(
|
function matchNtrpRequestment(
|
||||||
target?: string,
|
target?: string,
|
||||||
min?: string,
|
min?: string,
|
||||||
max?: string
|
max?: string,
|
||||||
): boolean {
|
): boolean {
|
||||||
// 目标值为空或 undefined
|
// 目标值为空或 undefined
|
||||||
if (!target?.trim()) return true;
|
if (!target?.trim()) return true;
|
||||||
@@ -110,7 +110,7 @@ export default function Participants(props) {
|
|||||||
user_action_status;
|
user_action_status;
|
||||||
const showApplicationEntry =
|
const showApplicationEntry =
|
||||||
[can_pay, can_substitute, is_substituting, waiting_start].every(
|
[can_pay, can_substitute, is_substituting, waiting_start].every(
|
||||||
(item) => !item
|
(item) => !item,
|
||||||
) &&
|
) &&
|
||||||
can_join &&
|
can_join &&
|
||||||
dayjs(start_time).isAfter(dayjs());
|
dayjs(start_time).isAfter(dayjs());
|
||||||
@@ -138,7 +138,7 @@ export default function Participants(props) {
|
|||||||
|
|
||||||
Taro.navigateTo({
|
Taro.navigateTo({
|
||||||
url: `/login_pages/index/index?redirect=${encodeURIComponent(
|
url: `/login_pages/index/index?redirect=${encodeURIComponent(
|
||||||
fullPath
|
fullPath,
|
||||||
)}`,
|
)}`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -153,7 +153,7 @@ export default function Participants(props) {
|
|||||||
const matchNtrpReq = matchNtrpRequestment(
|
const matchNtrpReq = matchNtrpRequestment(
|
||||||
userInfo?.ntrp_level,
|
userInfo?.ntrp_level,
|
||||||
skill_level_min,
|
skill_level_min,
|
||||||
skill_level_max
|
skill_level_max,
|
||||||
);
|
);
|
||||||
|
|
||||||
function handleSelfEvaluate() {
|
function handleSelfEvaluate() {
|
||||||
@@ -180,7 +180,7 @@ export default function Participants(props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function generateTextAndAction(
|
function generateTextAndAction(
|
||||||
user_action_status: null | { [key: string]: boolean }
|
user_action_status: null | { [key: string]: boolean },
|
||||||
):
|
):
|
||||||
| undefined
|
| undefined
|
||||||
| { text: string | React.FC; action?: () => void; available?: boolean } {
|
| { text: string | React.FC; action?: () => void; available?: boolean } {
|
||||||
@@ -259,7 +259,7 @@ export default function Participants(props) {
|
|||||||
const res = await OrderService.getUnpaidOrder(id);
|
const res = await OrderService.getUnpaidOrder(id);
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
navto(
|
navto(
|
||||||
`/order_pages/orderDetail/index?id=${res.data.order_info.order_id}`
|
`/order_pages/orderDetail/index?id=${res.data.order_info.order_id}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
@@ -296,10 +296,11 @@ export default function Participants(props) {
|
|||||||
const { action = () => {} } = generateTextAndAction(user_action_status)!;
|
const { action = () => {} } = generateTextAndAction(user_action_status)!;
|
||||||
|
|
||||||
const leftCount = max_participants - participant_count;
|
const leftCount = max_participants - participant_count;
|
||||||
const leftSubstituteCount = (max_substitute_players || 0) - (substitute_count || 0);
|
const leftSubstituteCount =
|
||||||
|
(max_substitute_players || 0) - (substitute_count || 0);
|
||||||
const showSubstituteApplicationEntry =
|
const showSubstituteApplicationEntry =
|
||||||
[can_pay, can_join, is_substituting, waiting_start].every(
|
[can_pay, can_join, is_substituting, waiting_start].every(
|
||||||
(item) => !item
|
(item) => !item,
|
||||||
) &&
|
) &&
|
||||||
can_substitute &&
|
can_substitute &&
|
||||||
dayjs(start_time).isAfter(dayjs());
|
dayjs(start_time).isAfter(dayjs());
|
||||||
@@ -336,7 +337,7 @@ export default function Participants(props) {
|
|||||||
refresherBackground="#FAFAFA"
|
refresherBackground="#FAFAFA"
|
||||||
className={classnames(
|
className={classnames(
|
||||||
styles["participants-list-scroll"],
|
styles["participants-list-scroll"],
|
||||||
showApplicationEntry ? styles.withApplication : ""
|
showApplicationEntry ? styles.withApplication : "",
|
||||||
)}
|
)}
|
||||||
scrollX
|
scrollX
|
||||||
>
|
>
|
||||||
@@ -377,14 +378,14 @@ export default function Participants(props) {
|
|||||||
src={avatar_url}
|
src={avatar_url}
|
||||||
onClick={handleViewUserInfo.bind(
|
onClick={handleViewUserInfo.bind(
|
||||||
null,
|
null,
|
||||||
participant_user_id
|
participant_user_id,
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<Text className={styles["participants-list-item-name"]}>
|
<Text className={styles["participants-list-item-name"]}>
|
||||||
{nickname || "未知"}
|
{nickname || "未知"}
|
||||||
</Text>
|
</Text>
|
||||||
<Text className={styles["participants-list-item-level"]}>
|
<Text className={styles["participants-list-item-level"]}>
|
||||||
{displayNtrp}
|
NTRP {displayNtrp}
|
||||||
</Text>
|
</Text>
|
||||||
<Text className={styles["participants-list-item-role"]}>
|
<Text className={styles["participants-list-item-role"]}>
|
||||||
{role}
|
{role}
|
||||||
@@ -400,97 +401,107 @@ export default function Participants(props) {
|
|||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
{/* 候补区域 */}
|
{/* 候补区域 */}
|
||||||
{max_substitute_players > 0 && (substitute_count > 0 || showSubstituteApplicationEntry) && (
|
{max_substitute_players > 0 &&
|
||||||
<View className={styles["detail-page-content-participants"]}>
|
(substitute_count > 0 || showSubstituteApplicationEntry) && (
|
||||||
<View className={styles["participants-title"]}>
|
<View className={styles["detail-page-content-participants"]}>
|
||||||
<Text>候补</Text>
|
<View className={styles["participants-title"]}>
|
||||||
<Text>·</Text>
|
<Text>候补</Text>
|
||||||
<Text>{leftSubstituteCount > 0 ? `剩余空位 ${leftSubstituteCount}` : "已满员"}</Text>
|
<Text>·</Text>
|
||||||
</View>
|
<Text>
|
||||||
<View className={styles["participants-list"]}>
|
{leftSubstituteCount > 0
|
||||||
{/* 候补申请入口 */}
|
? `剩余空位 ${leftSubstituteCount}`
|
||||||
{showSubstituteApplicationEntry && (
|
: "已满员"}
|
||||||
<View
|
</Text>
|
||||||
className={styles["participants-list-application"]}
|
</View>
|
||||||
onClick={() => {
|
<View className={styles["participants-list"]}>
|
||||||
action?.();
|
{/* 候补申请入口 */}
|
||||||
}}
|
{showSubstituteApplicationEntry && (
|
||||||
>
|
<View
|
||||||
<Image
|
className={styles["participants-list-application"]}
|
||||||
className={styles["participants-list-application-icon"]}
|
onClick={() => {
|
||||||
src={img.ICON_DETAIL_APPLICATION_ADD}
|
action?.();
|
||||||
/>
|
}}
|
||||||
<Text className={styles["participants-list-application-text"]}>
|
>
|
||||||
申请候补
|
<Image
|
||||||
</Text>
|
className={styles["participants-list-application-icon"]}
|
||||||
</View>
|
src={img.ICON_DETAIL_APPLICATION_ADD}
|
||||||
)}
|
/>
|
||||||
{/* 候补成员列表 */}
|
<Text
|
||||||
<ScrollView
|
className={styles["participants-list-application-text"]}
|
||||||
refresherBackground="#FAFAFA"
|
>
|
||||||
className={classnames(
|
申请候补
|
||||||
styles["participants-list-scroll"],
|
</Text>
|
||||||
showSubstituteApplicationEntry ? styles.withApplication : ""
|
</View>
|
||||||
)}
|
)}
|
||||||
scrollX
|
{/* 候补成员列表 */}
|
||||||
>
|
<ScrollView
|
||||||
<View
|
refresherBackground="#FAFAFA"
|
||||||
className={styles["participants-list-scroll-content"]}
|
className={classnames(
|
||||||
style={{
|
styles["participants-list-scroll"],
|
||||||
width: `${
|
showSubstituteApplicationEntry ? styles.withApplication : "",
|
||||||
Math.max(substitute_members.length, 1) * 103 + (Math.max(substitute_members.length, 1) - 1) * 8
|
)}
|
||||||
}px`,
|
scrollX
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{substitute_members.map((substitute) => {
|
<View
|
||||||
const {
|
className={styles["participants-list-scroll-content"]}
|
||||||
is_organizer,
|
style={{
|
||||||
user: {
|
width: `${
|
||||||
avatar_url,
|
Math.max(substitute_members.length, 1) * 103 +
|
||||||
nickname,
|
(Math.max(substitute_members.length, 1) - 1) * 8
|
||||||
level,
|
}px`,
|
||||||
ntrp_level,
|
}}
|
||||||
id: substitute_user_id,
|
>
|
||||||
},
|
{substitute_members.map((substitute) => {
|
||||||
} = substitute;
|
const {
|
||||||
const role = is_organizer ? "组织者" : "参与者";
|
is_organizer,
|
||||||
// 优先使用 ntrp_level,如果没有则使用 level
|
user: {
|
||||||
const ntrpValue = ntrp_level || level;
|
avatar_url,
|
||||||
// 格式化显示 NTRP,如果没有值则显示"初学者"
|
nickname,
|
||||||
const displayNtrp = ntrpValue
|
level,
|
||||||
? formatNtrpDisplay(ntrpValue)
|
ntrp_level,
|
||||||
: "初学者";
|
id: substitute_user_id,
|
||||||
return (
|
},
|
||||||
<View
|
} = substitute;
|
||||||
key={substitute.id}
|
const role = is_organizer ? "组织者" : "参与者";
|
||||||
className={styles["participants-list-item"]}
|
// 优先使用 ntrp_level,如果没有则使用 level
|
||||||
>
|
const ntrpValue = ntrp_level || level;
|
||||||
<Image
|
// 格式化显示 NTRP,如果没有值则显示"初学者"
|
||||||
className={styles["participants-list-item-avatar"]}
|
const displayNtrp = ntrpValue
|
||||||
mode="aspectFill"
|
? formatNtrpDisplay(ntrpValue)
|
||||||
src={avatar_url}
|
: "初学者";
|
||||||
onClick={handleViewUserInfo.bind(
|
return (
|
||||||
null,
|
<View
|
||||||
substitute_user_id
|
key={substitute.id}
|
||||||
)}
|
className={styles["participants-list-item"]}
|
||||||
/>
|
>
|
||||||
<Text className={styles["participants-list-item-name"]}>
|
<Image
|
||||||
{nickname || "未知"}
|
className={styles["participants-list-item-avatar"]}
|
||||||
</Text>
|
mode="aspectFill"
|
||||||
<Text className={styles["participants-list-item-level"]}>
|
src={avatar_url}
|
||||||
{displayNtrp}
|
onClick={handleViewUserInfo.bind(
|
||||||
</Text>
|
null,
|
||||||
<Text className={styles["participants-list-item-role"]}>
|
substitute_user_id,
|
||||||
{role}
|
)}
|
||||||
</Text>
|
/>
|
||||||
</View>
|
<Text className={styles["participants-list-item-name"]}>
|
||||||
);
|
{nickname || "未知"}
|
||||||
})}
|
</Text>
|
||||||
</View>
|
<Text
|
||||||
</ScrollView>
|
className={styles["participants-list-item-level"]}
|
||||||
|
>
|
||||||
|
{displayNtrp}
|
||||||
|
</Text>
|
||||||
|
<Text className={styles["participants-list-item-role"]}>
|
||||||
|
{role}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</View>
|
||||||
|
</ScrollView>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
)}
|
||||||
)}
|
|
||||||
<NTRPEvaluatePopup type={EvaluateScene.detail} ref={ntrpRef} showGuide />
|
<NTRPEvaluatePopup type={EvaluateScene.detail} ref={ntrpRef} showGuide />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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);
|
||||||
// 静默登录失败不影响使用
|
// 静默登录失败不影响使用
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,23 @@
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.link_button
|
||||||
|
{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 12px;
|
||||||
|
height: 52px;
|
||||||
|
border-radius: 16px;
|
||||||
|
border: none;
|
||||||
|
position: relative;
|
||||||
|
font-size: 16px;
|
||||||
|
|
||||||
|
.button_text {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 背景图片和渐变覆盖层
|
// 背景图片和渐变覆盖层
|
||||||
.background_image {
|
.background_image {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|||||||
@@ -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,
|
||||||
@@ -155,6 +156,11 @@ const LoginPage: React.FC = () => {
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 返回首页
|
||||||
|
const handle_return_home = () => {
|
||||||
|
Taro.navigateTo({ url: "/main_pages/index" });
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View className="login_page">
|
<View className="login_page">
|
||||||
<View className="background_image">
|
<View className="background_image">
|
||||||
@@ -166,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">
|
||||||
{/* 品牌区域 */}
|
{/* 品牌区域 */}
|
||||||
@@ -211,6 +219,10 @@ 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}>
|
||||||
|
<Text className="button_text">返回首页</Text>
|
||||||
|
</View> */}
|
||||||
|
|
||||||
{/* 用户协议复选框 */}
|
{/* 用户协议复选框 */}
|
||||||
<View className="terms_checkbox_section">
|
<View className="terms_checkbox_section">
|
||||||
<View className="checkbox_container" onClick={handle_toggle_terms}>
|
<View className="checkbox_container" onClick={handle_toggle_terms}>
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
export default definePageConfig({
|
export default definePageConfig({
|
||||||
navigationBarTitleText: '首页',
|
navigationBarTitleText: '首页',
|
||||||
navigationStyle: 'custom',
|
navigationStyle: 'custom',
|
||||||
navigationBarBackgroundColor: '#FAFAFA'
|
navigationBarBackgroundColor: '#FAFAFA',
|
||||||
})
|
enableShareAppMessage: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -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(() => {
|
||||||
// 如果当前是列表页,触发列表页内部滚动
|
// 如果当前是列表页,触发列表页内部滚动
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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}>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
.enable_notification_page {
|
.enable_notification_page {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
// min-height: 100vh;
|
height: 100%;
|
||||||
// background: radial-gradient(circle at 50% 0%, rgba(191, 255, 239, 1) 0%, rgba(255, 255, 255, 1) 37%);
|
background: radial-gradient(circle at 50% 0%, rgba(191, 255, 239, 1) 0%, rgba(255, 255, 255, 1) 37%);
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
@@ -34,6 +36,7 @@
|
|||||||
box-shadow: 0px 4px 20px 0px rgba(0, 0, 0, 0.08);
|
box-shadow: 0px 4px 20px 0px rgba(0, 0, 0, 0.08);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
background: #ffffff;
|
||||||
|
|
||||||
// 第三个卡片(最上面)
|
// 第三个卡片(最上面)
|
||||||
&--3 {
|
&--3 {
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import { useGlobalState } from "@/store/global";
|
|||||||
import { delay, getCurrentFullPath } from "@/utils";
|
import { delay, getCurrentFullPath } from "@/utils";
|
||||||
import { formatNtrpDisplay } from "@/utils/helper";
|
import { formatNtrpDisplay } from "@/utils/helper";
|
||||||
import { waitForAuthInit } from "@/utils/authInit";
|
import { waitForAuthInit } from "@/utils/authInit";
|
||||||
import httpService from "@/services/httpService";
|
// import httpService from "@/services/httpService";
|
||||||
import DetailService from "@/services/detailService";
|
import DetailService from "@/services/detailService";
|
||||||
import { OSS_BASE } from "@/config/api";
|
import { OSS_BASE } from "@/config/api";
|
||||||
import CloseIcon from "@/static/ntrp/ntrp_close_icon.svg";
|
import CloseIcon from "@/static/ntrp/ntrp_close_icon.svg";
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { useState, useEffect } from 'react'
|
import React, { useState, useEffect } from 'react'
|
||||||
import { View, Text, Textarea, Image } from '@tarojs/components'
|
import { View, Text, Textarea, Image } from '@tarojs/components'
|
||||||
import Taro from '@tarojs/taro'
|
import Taro from '@tarojs/taro'
|
||||||
import { ConfigProvider, Loading, Popup, Toast } from '@nutui/nutui-react-taro'
|
import { ConfigProvider, Loading, Toast } from '@nutui/nutui-react-taro'
|
||||||
import styles from './index.module.scss'
|
import styles from './index.module.scss'
|
||||||
import uploadFiles from '@/services/uploadFiles'
|
import uploadFiles from '@/services/uploadFiles'
|
||||||
import publishService from '@/services/publishService'
|
import publishService from '@/services/publishService'
|
||||||
@@ -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',
|
||||||
@@ -109,7 +109,10 @@ const AiImportPopup: React.FC<AiImportPopupProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleTextChange = (e: any) => {
|
const handleTextChange = (e: any) => {
|
||||||
setText(e.detail.value)
|
const text = e.detail.value;
|
||||||
|
const maxAllowedLength = 120;
|
||||||
|
const truncatedVal = text.length > maxAllowedLength ? text.slice(0, maxAllowedLength) : text
|
||||||
|
setText(truncatedVal)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用全局键盘状态监听
|
// 使用全局键盘状态监听
|
||||||
@@ -160,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({
|
||||||
@@ -191,73 +194,90 @@ const AiImportPopup: React.FC<AiImportPopupProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const showManualButton = uploadFailCount >= maxFailCount
|
const showManualButton = uploadFailCount >= maxFailCount
|
||||||
|
if (!visible) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 阻止弹窗内的触摸事件冒泡
|
||||||
|
const handleTouchMoveInPopup = (e) => {
|
||||||
|
if (!isKeyboardVisible) {
|
||||||
|
e.stopPropagation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popup
|
<View
|
||||||
visible={visible}
|
className={styles.aiImportPopupOverlay}
|
||||||
position="bottom"
|
>
|
||||||
round={true}
|
<View className={styles.aiImportPopupWrapper} onTouchMove={handleTouchMoveInPopup} catchMove></View>
|
||||||
closeable={false}
|
<View
|
||||||
onClose={closePopupBefore}
|
className={styles.aiImportPopup}
|
||||||
className={styles.aiImportPopup}
|
style={{ paddingBottom: isKeyboardVisible ? `${keyboardHeight}px` : undefined }}
|
||||||
style={{ paddingBottom: isKeyboardVisible ? `${keyboardHeight}px` : undefined }}
|
>
|
||||||
>
|
<View className={styles.popupContent}>
|
||||||
<View className={styles.popupContent}>
|
{/* 头部 */}
|
||||||
{/* 头部 */}
|
<View className={styles.header}>
|
||||||
<View className={styles.header}>
|
<View className={styles.titleContainer}>
|
||||||
<View className={styles.titleContainer}>
|
<Image src={images.ICON_IMPORTANT_BLACK} className={styles.lightningIcon} />
|
||||||
<Image src={images.ICON_IMPORTANT_BLACK} className={styles.lightningIcon} />
|
<Text className={styles.title}>智能导入球局信息</Text>
|
||||||
<Text className={styles.title}>智能导入球局信息</Text>
|
|
||||||
</View>
|
|
||||||
<View className={styles.closeButton} onClick={closePopupBefore}>
|
|
||||||
<Image src={images.ICON_CLOSE} className={styles.lightningIcon} />
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* 文本域 */}
|
|
||||||
<View className={styles.textAreaContainer}>
|
|
||||||
<Textarea
|
|
||||||
className={styles.textArea}
|
|
||||||
value={text}
|
|
||||||
onInput={handleTextChange}
|
|
||||||
onFocus={() => {}}
|
|
||||||
onBlur={() => {}}
|
|
||||||
placeholder="在此「粘贴识别」或输入文本,智能拆分球局时间、费用、地点和其他信息,并帮你智能生成球局标题"
|
|
||||||
maxlength={-1}
|
|
||||||
showConfirmBar={false}
|
|
||||||
placeholderClass={styles.textArea_placeholder}
|
|
||||||
autoHeight
|
|
||||||
// 关闭系统自动上推,改为手动根据键盘高度加内边距
|
|
||||||
adjustPosition={false}
|
|
||||||
/>
|
|
||||||
<View className={styles.charCount}>
|
|
||||||
<Text className={`${styles.charCountText} ${isCharCountExceeded ? styles.charCountTextExceeded : ''}`}>
|
|
||||||
{text.length}/100
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* 图片识别按钮 */}
|
|
||||||
<View className={styles.imageRecognitionContainer}>
|
|
||||||
<View className={`${styles.imageRecognitionButton} ${uploadLoading ? styles.uploadLoadingContainer : ''}`} onClick={handleImageRecognition}>
|
|
||||||
{
|
|
||||||
uploadLoading ? (<Image src={images.ICON_UPLOAD_SUCCESS} className={styles.cameraIcon} />) : (<Image src={images.ICON_UPLOAD_IMG} className={styles.cameraIcon} />)
|
|
||||||
}
|
|
||||||
<Text className={styles.imageRecognitionText}>图片识别</Text>
|
|
||||||
<Text className={styles.imageRecognitionDesc}>{uploadLoading ? '已上传 1 张图片' : '支持订场截图/小红书笔记截图等图片'}</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* 底部按钮 */}
|
|
||||||
<View className={styles.bottomButtons}>
|
|
||||||
{showManualButton && (
|
|
||||||
<View className={styles.manualButton} onClick={handleManualPublish}>
|
|
||||||
<Text className={styles.manualButtonText}>手动发布球局</Text>
|
|
||||||
</View>
|
</View>
|
||||||
)}
|
<View className={styles.closeButton} onClick={closePopupBefore}>
|
||||||
<View className={styles.pasteButton} onClick={handlePasteAndRecognize}>
|
<Image src={images.ICON_CLOSE} className={styles.lightningIcon} />
|
||||||
{
|
</View>
|
||||||
loading ? (
|
</View>
|
||||||
|
|
||||||
|
{/* 文本域 */}
|
||||||
|
<View className={styles.textAreaContainer}>
|
||||||
|
<Textarea
|
||||||
|
className={styles.textArea}
|
||||||
|
value={text}
|
||||||
|
onInput={handleTextChange}
|
||||||
|
onFocus={() => {}}
|
||||||
|
onBlur={() => {}}
|
||||||
|
placeholder="在此「粘贴识别」或输入文本,智能拆分球局时间、费用、地点和其他信息,并帮你智能生成球局标题"
|
||||||
|
maxlength={-1}
|
||||||
|
showConfirmBar={false}
|
||||||
|
placeholderClass={styles.textArea_placeholder}
|
||||||
|
autoHeight
|
||||||
|
// 关闭系统自动上推,改为手动根据键盘高度加内边距
|
||||||
|
adjustPosition={false}
|
||||||
|
/>
|
||||||
|
<View className={styles.charCount}>
|
||||||
|
<Text className={`${styles.charCountText} ${isCharCountExceeded ? styles.charCountTextExceeded : ''}`}>
|
||||||
|
{text.length}/100
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* 图片识别按钮 */}
|
||||||
|
<View className={styles.imageRecognitionContainer}>
|
||||||
|
<View
|
||||||
|
className={`${styles.imageRecognitionButton} ${
|
||||||
|
uploadLoading ? styles.uploadLoadingContainer : ''
|
||||||
|
}`}
|
||||||
|
onClick={handleImageRecognition}
|
||||||
|
>
|
||||||
|
{uploadLoading ? (
|
||||||
|
<Image src={images.ICON_UPLOAD_SUCCESS} className={styles.cameraIcon} />
|
||||||
|
) : (
|
||||||
|
<Image src={images.ICON_UPLOAD_IMG} className={styles.cameraIcon} />
|
||||||
|
)}
|
||||||
|
<Text className={styles.imageRecognitionText}>图片识别</Text>
|
||||||
|
<Text className={styles.imageRecognitionDesc}>
|
||||||
|
{uploadLoading ? '已上传 1 张图片' : '支持订场截图/小红书笔记截图等图片'}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* 底部按钮 */}
|
||||||
|
<View className={styles.bottomButtons}>
|
||||||
|
{showManualButton && (
|
||||||
|
<View className={styles.manualButton} onClick={handleManualPublish}>
|
||||||
|
<Text className={styles.manualButtonText}>手动发布球局</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
<View className={styles.pasteButton} onClick={handlePasteAndRecognize}>
|
||||||
|
{loading ? (
|
||||||
<View className={styles.loadingContainer}>
|
<View className={styles.loadingContainer}>
|
||||||
<ConfigProvider theme={{ nutuiLoadingIconColor: '#fff', nutuiLoadingIconSize: '20px' }}>
|
<ConfigProvider theme={{ nutuiLoadingIconColor: '#fff', nutuiLoadingIconSize: '20px' }}>
|
||||||
<Loading type="circular" />
|
<Loading type="circular" />
|
||||||
@@ -269,13 +289,13 @@ const AiImportPopup: React.FC<AiImportPopupProps> = ({
|
|||||||
<Image src={images.ICON_COPY} className={styles.clipboardIcon} />
|
<Image src={images.ICON_COPY} className={styles.clipboardIcon} />
|
||||||
<Text className={styles.pasteButtonText}>粘贴并识别</Text>
|
<Text className={styles.pasteButtonText}>粘贴并识别</Text>
|
||||||
</>
|
</>
|
||||||
)
|
)}
|
||||||
}
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
<Toast id="toast" />
|
||||||
</View>
|
</View>
|
||||||
<Toast id="toast" />
|
</View>
|
||||||
</Popup>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,34 @@
|
|||||||
@use '~@/scss/themeColor.scss' as theme;
|
@use '~@/scss/themeColor.scss' as theme;
|
||||||
|
|
||||||
|
.aiImportPopupOverlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
z-index: 9998;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aiImportPopupWrapper {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 9998;
|
||||||
|
}
|
||||||
.aiImportPopup {
|
.aiImportPopup {
|
||||||
background-color: #fff;
|
width: 100%;
|
||||||
&:global(.nut-popup-bottom.nut-popup-round) {
|
background-color:#fafafa;
|
||||||
border-radius: 20px 20px 0 0!important;
|
border-radius: 16px 16px 0 0;
|
||||||
}
|
position: relative;
|
||||||
|
z-index: 9999;
|
||||||
.popupContent {
|
.popupContent {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background: #fff;
|
|
||||||
border-radius: 16px 16px 0 0;
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
max-height: 80vh;
|
max-height: 80vh;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { View, Text, Input, ScrollView, Image } from '@tarojs/components'
|
|||||||
import Taro from '@tarojs/taro'
|
import Taro from '@tarojs/taro'
|
||||||
import { Loading } from '@nutui/nutui-react-taro'
|
import { Loading } from '@nutui/nutui-react-taro'
|
||||||
import StadiumDetail, { StadiumDetailRef } from './StadiumDetail'
|
import StadiumDetail, { StadiumDetailRef } from './StadiumDetail'
|
||||||
import { CommonPopup } from '../../../../components'
|
import { CommonPopup, CustomPopup } from '../../../../components'
|
||||||
import { getLocation } from '@/utils/locationUtils'
|
import { getLocation } from '@/utils/locationUtils'
|
||||||
import PublishService from '@/services/publishService'
|
import PublishService from '@/services/publishService'
|
||||||
import images from '@/config/images'
|
import images from '@/config/images'
|
||||||
@@ -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({
|
||||||
@@ -188,24 +188,20 @@ const SelectStadium: React.FC<SelectStadiumProps> = ({
|
|||||||
// 如果显示详情页面
|
// 如果显示详情页面
|
||||||
if (showDetail && selectedStadium) {
|
if (showDetail && selectedStadium) {
|
||||||
return (
|
return (
|
||||||
<CommonPopup
|
<CustomPopup
|
||||||
visible={visible}
|
visible={visible}
|
||||||
onClose={handleCancel}
|
onClose={handleCancel}
|
||||||
cancelText="返回"
|
cancelText="返回"
|
||||||
confirmText="确认"
|
confirmText="确认"
|
||||||
className="select-stadium-popup"
|
|
||||||
onCancel={handleDetailCancel}
|
onCancel={handleDetailCancel}
|
||||||
onConfirm={handleConfirm}
|
onConfirm={handleConfirm}
|
||||||
position="bottom"
|
|
||||||
//style={{ paddingBottom: keyboardVisible ? `20px` : undefined }}
|
|
||||||
round
|
|
||||||
>
|
>
|
||||||
<StadiumDetail
|
{/* 内容区域 */}
|
||||||
ref={stadiumDetailRef}
|
<StadiumDetail
|
||||||
stadium={selectedStadium}
|
ref={stadiumDetailRef}
|
||||||
//onAnyInput={handleAnyInput}
|
stadium={selectedStadium}
|
||||||
/>
|
/>
|
||||||
</CommonPopup>
|
</CustomPopup>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
.stadium-detail-scroll{
|
.stadium-detail-scroll{
|
||||||
height:60vh;
|
max-height:60vh;
|
||||||
}
|
}
|
||||||
// 已选球场
|
// 已选球场
|
||||||
// 场馆列表
|
// 场馆列表
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import React, { useState, useCallback, forwardRef, useImperativeHandle } from 'react'
|
import React, { useState, useCallback, forwardRef, useImperativeHandle, useEffect } from 'react'
|
||||||
import Taro from '@tarojs/taro'
|
import Taro from '@tarojs/taro'
|
||||||
import { View, Text, Image, ScrollView } from '@tarojs/components'
|
import { View, Text, Image, ScrollView } from '@tarojs/components'
|
||||||
import images from '@/config/images'
|
import images from '@/config/images'
|
||||||
import TextareaTag from '@/components/TextareaTag'
|
import TextareaTag from '@/components/TextareaTag'
|
||||||
// import CoverImageUpload, { type CoverImage } from '@/components/ImageUpload'
|
// import CoverImageUpload, { type CoverImage } from '@/components/ImageUpload'
|
||||||
import UploadCover, { type CoverImageValue } from '@/components/UploadCover'
|
import UploadCover, { type CoverImageValue } from '@/components/UploadCover'
|
||||||
|
import { useKeyboardHeight } from '@/store/keyboardStore'
|
||||||
import { useDictionaryActions } from '@/store/dictionaryStore'
|
import { useDictionaryActions } from '@/store/dictionaryStore'
|
||||||
|
|
||||||
import './StadiumDetail.scss'
|
import './StadiumDetail.scss'
|
||||||
@@ -69,12 +70,16 @@ const StadiumDetail = forwardRef<StadiumDetailRef, StadiumDetailProps>(({
|
|||||||
stadium,
|
stadium,
|
||||||
onAnyInput
|
onAnyInput
|
||||||
}, ref) => {
|
}, ref) => {
|
||||||
const [openPicker, setOpenPicker] = useState(false);
|
const [openPicker, setOpenPicker] = useState(false); //为了解决上传图片时按钮样式问题
|
||||||
const [scrollTop, setScrollTop] = useState(0);
|
const [scrollTop, setScrollTop] = useState(0);
|
||||||
const { getDictionaryValue } = useDictionaryActions()
|
const { getDictionaryValue } = useDictionaryActions()
|
||||||
const court_type = getDictionaryValue('court_type') || []
|
const court_type = getDictionaryValue('court_type') || []
|
||||||
const court_surface = getDictionaryValue('court_surface') || []
|
const court_surface = getDictionaryValue('court_surface') || []
|
||||||
const supplementary_information = getDictionaryValue('supplementary_information') || []
|
const supplementary_information = getDictionaryValue('supplementary_information') || []
|
||||||
|
|
||||||
|
// 使用全局键盘状态
|
||||||
|
const { keyboardHeight, isKeyboardVisible, addListener, initializeKeyboardListener } = useKeyboardHeight()
|
||||||
|
|
||||||
const stadiumInfo = [
|
const stadiumInfo = [
|
||||||
{
|
{
|
||||||
label: '场地类型',
|
label: '场地类型',
|
||||||
@@ -147,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({
|
||||||
@@ -171,19 +176,40 @@ const StadiumDetail = forwardRef<StadiumDetailRef, StadiumDetailProps>(({
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
const changeTextarea = (value) => {
|
// 使用全局键盘状态监听
|
||||||
|
useEffect(() => {
|
||||||
|
// 初始化全局键盘监听器
|
||||||
|
initializeKeyboardListener()
|
||||||
|
|
||||||
|
// 添加本地监听器
|
||||||
|
const removeListener = addListener((height, visible) => {
|
||||||
|
console.log('AiImportPopup 收到键盘变化:', height, visible)
|
||||||
|
})
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
removeListener()
|
||||||
|
}
|
||||||
|
}, [initializeKeyboardListener, addListener])
|
||||||
|
|
||||||
|
const changeTextarea = (value: boolean) => {
|
||||||
if (value) {
|
if (value) {
|
||||||
// 先滚动到底部
|
// 先滚动到底部
|
||||||
setScrollTop(scrollTop ? scrollTop + 1 : 9999);
|
setScrollTop(140);
|
||||||
// 使用 setTimeout 确保滚动后再更新 openPicker
|
// 使用 setTimeout 确保滚动后再更新 openPicker
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const changePicker = (value) => {
|
const changePicker = (value:boolean) => {
|
||||||
setOpenPicker(value);
|
setOpenPicker(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(stadium,'stadiumstadium');
|
console.log(stadium,'stadiumstadium');
|
||||||
|
|
||||||
|
// 计算滚动区域的最大高度
|
||||||
|
const scrollMaxHeight = isKeyboardVisible
|
||||||
|
? `calc(100vh - ${keyboardHeight+40}px)`
|
||||||
|
: '60vh'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View className='stadium-detail'>
|
<View className='stadium-detail'>
|
||||||
<ScrollView
|
<ScrollView
|
||||||
@@ -191,6 +217,7 @@ const StadiumDetail = forwardRef<StadiumDetailRef, StadiumDetailProps>(({
|
|||||||
refresherBackground="#FAFAFA"
|
refresherBackground="#FAFAFA"
|
||||||
scrollY={!openPicker}
|
scrollY={!openPicker}
|
||||||
scrollTop={scrollTop}
|
scrollTop={scrollTop}
|
||||||
|
style={{ maxHeight: scrollMaxHeight }}
|
||||||
>
|
>
|
||||||
{/* 已选球场 */}
|
{/* 已选球场 */}
|
||||||
<View
|
<View
|
||||||
@@ -203,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>
|
||||||
@@ -235,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 }))}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -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);
|
||||||
@@ -783,6 +776,7 @@ const PublishBall: React.FC = () => {
|
|||||||
>
|
>
|
||||||
<GeneralNavbar
|
<GeneralNavbar
|
||||||
title={titleBar}
|
title={titleBar}
|
||||||
|
backgroundColor={'#FAFAFA'}
|
||||||
className={styles["publish-ball-navbar"]}
|
className={styles["publish-ball-navbar"]}
|
||||||
/>
|
/>
|
||||||
<View
|
<View
|
||||||
|
|||||||
@@ -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 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -263,14 +263,14 @@ export const save_login_state = (token: string, user_info: WechatUserInfo) => {
|
|||||||
export const clear_login_state = () => {
|
export const clear_login_state = () => {
|
||||||
try {
|
try {
|
||||||
// 使用 tokenManager 清除令牌
|
// 使用 tokenManager 清除令牌
|
||||||
tokenManager.clearTokens();
|
// tokenManager.clearTokens();
|
||||||
|
|
||||||
// 清除其他登录状态
|
// 清除其他登录状态
|
||||||
Taro.removeStorageSync("user_info");
|
Taro.removeStorageSync("user_info");
|
||||||
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: "静默登录失败,请重试",
|
||||||
|
|||||||
@@ -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,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 导出认证服务实例
|
// 导出认证服务实例
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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 |
13
src/static/list/icon_game_type.svg
Normal file
13
src/static/list/icon_game_type.svg
Normal 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 |
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 [];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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 });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
|||||||
@@ -66,6 +66,34 @@ const DownloadBillRecords: React.FC = () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handlePreviewFile = (fileUrl: string) => {
|
||||||
|
wx.downloadFile({
|
||||||
|
url: fileUrl,
|
||||||
|
success: (res) => {
|
||||||
|
if (res.statusCode === 200) {
|
||||||
|
// 确保文件路径正确并添加扩展名
|
||||||
|
const filePath = res.tempFilePath;
|
||||||
|
wx.openDocument({
|
||||||
|
filePath: filePath,
|
||||||
|
fileType: 'xlsx', // 指定文件类型为xlsx
|
||||||
|
showMenu: true, // 显示右上角菜单按钮
|
||||||
|
success: (openRes) => {
|
||||||
|
console.log('打开文档成功');
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.warn('打开文档失败', err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.warn('下载失败,状态码:', res.statusCode);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.warn('下载失败', err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<View className="download-bill-records-page">
|
<View className="download-bill-records-page">
|
||||||
{/* 导航栏 */}
|
{/* 导航栏 */}
|
||||||
@@ -111,7 +139,7 @@ const DownloadBillRecords: React.FC = () => {
|
|||||||
</View>
|
</View>
|
||||||
<View className="info-item">
|
<View className="info-item">
|
||||||
<Text></Text>
|
<Text></Text>
|
||||||
<Text className="btn">查看材料</Text>
|
<Text className="btn" onClick={() => handlePreviewFile(record.file_url)}>查看材料</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)) : <EmptyState text="暂无数据" />}
|
)) : <EmptyState text="暂无数据" />}
|
||||||
|
|||||||
@@ -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}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -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'
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
|
|
||||||
.qrcode {
|
.qrcode {
|
||||||
width: 240px;
|
width: 240px;
|
||||||
|
height: 240px;
|
||||||
margin: 32px 0 -20px;
|
margin: 32px 0 -20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ const QueryTransactions = () => {
|
|||||||
setSearchHistory(response.data);
|
setSearchHistory(response.data);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.warn(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
padding: 0 20px;
|
||||||
.custom-navbar {
|
.custom-navbar {
|
||||||
height: 56px;
|
height: 56px;
|
||||||
/* 通常与原生导航栏高度一致 */
|
/* 通常与原生导航栏高度一致 */
|
||||||
@@ -17,6 +18,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 +50,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,7 +78,6 @@
|
|||||||
letter-spacing: 0px;
|
letter-spacing: 0px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
color: #3c3c4366;
|
color: #3c3c4366;
|
||||||
padding: 0 20px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
@@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user