Compare commits
10 Commits
1cbec87f77
...
feature/ju
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
49f53d60ed | ||
|
|
4c75368fe8 | ||
|
|
8abf6e6f2b | ||
|
|
0c83aab053 | ||
|
|
6d57654005 | ||
|
|
dade2e2491 | ||
|
|
4a6ac73ad7 | ||
|
|
de8677c64c | ||
|
|
3ab647f7c6 | ||
|
|
fa328f893d |
@@ -2,7 +2,7 @@
|
|||||||
"miniprogramRoot": "dist/",
|
"miniprogramRoot": "dist/",
|
||||||
"projectname": "playBallTogether",
|
"projectname": "playBallTogether",
|
||||||
"description": "playBallTogether",
|
"description": "playBallTogether",
|
||||||
"appid": "wx815b533167eb7b53",
|
"appid": "wx915ecf6c01bea4ec",
|
||||||
"setting": {
|
"setting": {
|
||||||
"urlCheck": true,
|
"urlCheck": true,
|
||||||
"es6": true,
|
"es6": true,
|
||||||
|
|||||||
@@ -21,6 +21,6 @@ page {
|
|||||||
font-family: "Quicksand";
|
font-family: "Quicksand";
|
||||||
// 注意:此路径来自 @/config/api.ts 中的 OSS_BASE_URL 配置
|
// 注意:此路径来自 @/config/api.ts 中的 OSS_BASE_URL 配置
|
||||||
// 如需修改,请更新配置文件中的 OSS_BASE_URL
|
// 如需修改,请更新配置文件中的 OSS_BASE_URL
|
||||||
src: url("https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/other/57dc951f-f10e-45b7-9157-0b1e468187fd.ttf") format("truetype");
|
src: url("https://youchang2026.oss-cn-shanghai.aliyuncs.com/front/ball/other/57dc951f-f10e-45b7-9157-0b1e468187fd.ttf") format("truetype");
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,8 @@
|
|||||||
border-radius: 20px 20px 0 0 !important;
|
border-radius: 20px 20px 0 0 !important;
|
||||||
}
|
}
|
||||||
.common-popup__drag-handle-container {
|
.common-popup__drag-handle-container {
|
||||||
position: position;
|
position: relative;
|
||||||
|
height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.common-popup__drag-handle {
|
.common-popup__drag-handle {
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import {
|
|||||||
EvaluateCallback,
|
EvaluateCallback,
|
||||||
EvaluateScene,
|
EvaluateScene,
|
||||||
} from "@/store/evaluateStore";
|
} from "@/store/evaluateStore";
|
||||||
|
import { useListState } from "@/store/listStore";
|
||||||
|
|
||||||
import { navigateTo, redirectTo, navigateBack } from "@/utils/navigation";
|
import { navigateTo, redirectTo, navigateBack } from "@/utils/navigation";
|
||||||
import { requireLoginWithPhone } from "@/utils/helper";
|
import { requireLoginWithPhone } from "@/utils/helper";
|
||||||
import styles from "./index.module.scss";
|
import styles from "./index.module.scss";
|
||||||
@@ -24,6 +26,11 @@ const PublishMenu: React.FC<PublishMenuProps> = (props) => {
|
|||||||
const { onVisibleChange } = props;
|
const { onVisibleChange } = props;
|
||||||
const [isVisible, setIsVisible] = useState(false);
|
const [isVisible, setIsVisible] = useState(false);
|
||||||
|
|
||||||
|
const {
|
||||||
|
area
|
||||||
|
} = useListState();
|
||||||
|
|
||||||
|
|
||||||
// 使用 useEffect 监听 isVisible 变化,确保所有情况都能触发回调
|
// 使用 useEffect 监听 isVisible 变化,确保所有情况都能触发回调
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onVisibleChange?.(isVisible);
|
onVisibleChange?.(isVisible);
|
||||||
@@ -59,6 +66,16 @@ const PublishMenu: React.FC<PublishMenuProps> = (props) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
const handleMenuItemClick = (type: "individual" | "group" | "ai") => {
|
const handleMenuItemClick = (type: "individual" | "group" | "ai") => {
|
||||||
|
const [_, address] = area;
|
||||||
|
if (address !== '上海') {
|
||||||
|
(Taro as any).showModal({
|
||||||
|
title: '提示',
|
||||||
|
content: '仅上海地区开放,您可加入社群或切换城市',
|
||||||
|
showCancel: false,
|
||||||
|
confirmText: '知道了'
|
||||||
|
})
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!userInfo.ntrp_level) {
|
if (!userInfo.ntrp_level) {
|
||||||
ntrpRef.current.show({
|
ntrpRef.current.show({
|
||||||
type: EvaluateScene.publish,
|
type: EvaluateScene.publish,
|
||||||
|
|||||||
@@ -29,8 +29,10 @@ const TextareaTag: React.FC<TextareaTagProps> = ({
|
|||||||
// 处理文本输入变化
|
// 处理文本输入变化
|
||||||
const handleTextChange = useCallback((val: string) => {
|
const handleTextChange = useCallback((val: string) => {
|
||||||
console.log(val,'e.detail.value')
|
console.log(val,'e.detail.value')
|
||||||
onChange({...value, description: val})
|
const maxAllowedLength = Math.floor(maxLength * 1.2)
|
||||||
}, [onChange])
|
const truncatedVal = val.length > maxAllowedLength ? val.slice(0, maxAllowedLength) : val
|
||||||
|
onChange({...value, description: truncatedVal})
|
||||||
|
}, [onChange, maxLength, value])
|
||||||
|
|
||||||
// 处理标签选择变化
|
// 处理标签选择变化
|
||||||
const handleTagChange = useCallback((selectedTags: string[]) => {
|
const handleTagChange = useCallback((selectedTags: string[]) => {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import envConfig from './env'// API配置
|
import envConfig from './env'// API配置
|
||||||
|
|
||||||
// OSS 基础路径配置
|
// OSS 基础路径配置
|
||||||
export const OSS_BASE_URL = 'https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball'
|
export const OSS_BASE_URL = 'https://youchang2026.oss-cn-shanghai.aliyuncs.com/front/ball'
|
||||||
|
|
||||||
export const API_CONFIG = {
|
export const API_CONFIG = {
|
||||||
// 基础URL
|
// 基础URL
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ export default function Participants(props) {
|
|||||||
// id: 18,
|
// id: 18,
|
||||||
// nickname: "小猫开刀削面店往猫毛里面下面条",
|
// nickname: "小猫开刀削面店往猫毛里面下面条",
|
||||||
// avatar_url:
|
// avatar_url:
|
||||||
// "https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/d284060f-248b-4d58-a153-4d37c0ca77c8.jpg",
|
// "https://youchang2026.oss-cn-shanghai.aliyuncs.com/front/ball/images/d284060f-248b-4d58-a153-4d37c0ca77c8.jpg",
|
||||||
// phone: "18513125687",
|
// phone: "18513125687",
|
||||||
// ntrp_level: "1.5",
|
// ntrp_level: "1.5",
|
||||||
// },
|
// },
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
|
|||||||
area,
|
area,
|
||||||
cityQrCode,
|
cityQrCode,
|
||||||
districts,
|
districts,
|
||||||
|
fetchMatches,
|
||||||
gamesNum, // 新增:获取球局数量
|
gamesNum, // 新增:获取球局数量
|
||||||
} = store;
|
} = store;
|
||||||
|
|
||||||
@@ -77,6 +78,7 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
|
|||||||
pageOption,
|
pageOption,
|
||||||
isShowNoData,
|
isShowNoData,
|
||||||
} = listPageState || {};
|
} = listPageState || {};
|
||||||
|
console.log('===matches', matches)
|
||||||
|
|
||||||
const scrollContextRef = useRef(null);
|
const scrollContextRef = useRef(null);
|
||||||
const scrollViewRef = useRef(null);
|
const scrollViewRef = useRef(null);
|
||||||
@@ -92,6 +94,8 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
|
|||||||
// 记录上一次加载数据时的城市,用于检测城市变化
|
// 记录上一次加载数据时的城市,用于检测城市变化
|
||||||
const lastLoadedAreaRef = useRef<[string, string] | null>(null);
|
const lastLoadedAreaRef = useRef<[string, string] | null>(null);
|
||||||
const prevIsActiveRef = useRef(isActive);
|
const prevIsActiveRef = useRef(isActive);
|
||||||
|
// 首次加载标记:避免切回 tab 时使用 isRefresh 导致智能排序顺序抖动
|
||||||
|
const hasLoadedOnceRef = useRef(false);
|
||||||
|
|
||||||
// 处理距离筛选显示/隐藏
|
// 处理距离筛选显示/隐藏
|
||||||
const handleDistanceFilterVisibleChange = useCallback(
|
const handleDistanceFilterVisibleChange = useCallback(
|
||||||
@@ -230,9 +234,16 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
|
|||||||
|
|
||||||
// 只有当页面激活时才加载位置和列表数据
|
// 只有当页面激活时才加载位置和列表数据
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
getLocation().catch((error) => {
|
const firstLoad = !hasLoadedOnceRef.current;
|
||||||
console.error('获取位置信息失败:', error);
|
getLocation(firstLoad)
|
||||||
});
|
.then(() => {
|
||||||
|
if (firstLoad) {
|
||||||
|
hasLoadedOnceRef.current = true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('获取位置信息失败:', error);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}, [isActive]);
|
}, [isActive]);
|
||||||
|
|
||||||
@@ -359,7 +370,7 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const getLocation = async () => {
|
const getLocation = async (useRefresh = true) => {
|
||||||
const location = await getCurrentLocationInfo();
|
const location = await getCurrentLocationInfo();
|
||||||
updateState({ location });
|
updateState({ location });
|
||||||
if (location && location.latitude && location.longitude) {
|
if (location && location.latitude && location.longitude) {
|
||||||
@@ -370,7 +381,7 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 先调用列表接口
|
// 先调用列表接口
|
||||||
await getMatchesData();
|
await fetchMatches({}, useRefresh);
|
||||||
// 列表接口完成后,再调用数量接口
|
// 列表接口完成后,再调用数量接口
|
||||||
await fetchGetGamesCount();
|
await fetchGetGamesCount();
|
||||||
// 初始数据加载完成后,记录当前城市
|
// 初始数据加载完成后,记录当前城市
|
||||||
|
|||||||
@@ -64,11 +64,17 @@ const MessagePageContent: React.FC<MessagePageContentProps> = ({ isActive = true
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 只有当页面激活且未加载过数据时才加载接口
|
// 当切换到消息 tab 时,调用红点信息接口
|
||||||
|
useEffect(() => {
|
||||||
|
if (isActive) {
|
||||||
|
fetchReddotInfo();
|
||||||
|
}
|
||||||
|
}, [isActive]);
|
||||||
|
|
||||||
|
// 只有当页面激活且未加载过数据时才加载通知列表
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isActive && !hasLoaded) {
|
if (isActive && !hasLoaded) {
|
||||||
getNoticeList();
|
getNoticeList();
|
||||||
fetchReddotInfo();
|
|
||||||
setHasLoaded(true);
|
setHasLoaded(true);
|
||||||
}
|
}
|
||||||
}, [isActive, hasLoaded]);
|
}, [isActive, hasLoaded]);
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import MessagePageContent from "./components/MessagePageContent";
|
|||||||
import MyselfPageContent from "./components/MyselfPageContent";
|
import MyselfPageContent from "./components/MyselfPageContent";
|
||||||
import "./index.scss";
|
import "./index.scss";
|
||||||
import FamilyContext from "@/context";
|
import FamilyContext from "@/context";
|
||||||
|
import { useDictionaryStore } from "@/store/dictionaryStore";
|
||||||
|
|
||||||
type TabType = "list" | "message" | "personal";
|
type TabType = "list" | "message" | "personal";
|
||||||
|
|
||||||
@@ -66,6 +67,12 @@ const MainPage: React.FC = () => {
|
|||||||
try {
|
try {
|
||||||
await fetchUserInfo();
|
await fetchUserInfo();
|
||||||
await checkNicknameChangeStatus();
|
await checkNicknameChangeStatus();
|
||||||
|
// 启动时预取 Banner 字典(与业务无强依赖,失败不影响主流程)
|
||||||
|
try {
|
||||||
|
await useDictionaryStore.getState().fetchBannerDictionary();
|
||||||
|
} catch (e) {
|
||||||
|
console.error("预取 Banner 字典失败:", e);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("获取用户信息失败:", error);
|
console.error("获取用户信息失败:", error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,14 +71,12 @@ const CommentReply = () => {
|
|||||||
|
|
||||||
setCommentList(mappedList);
|
setCommentList(mappedList);
|
||||||
|
|
||||||
// 获取未读评论ID并标记已读
|
// 获取所有评论ID列表并标记已读(传入所有ID,包括已读和未读)
|
||||||
const unreadIds = res.data.rows
|
const allCommentIds = res.data.rows.map((item: any) => item.id);
|
||||||
.filter((item: any) => item.is_read === 0)
|
|
||||||
.map((item: any) => item.id);
|
|
||||||
|
|
||||||
if (unreadIds.length > 0) {
|
if (allCommentIds.length > 0) {
|
||||||
// 使用统一接口标记已读
|
// 使用统一接口标记已读,传入所有评论ID
|
||||||
messageService.markAsRead('comment', unreadIds).catch(e => {
|
messageService.markAsRead('comment', allCommentIds).catch(e => {
|
||||||
console.error("标记评论已读失败:", e);
|
console.error("标记评论已读失败:", e);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -217,6 +215,15 @@ const CommentReply = () => {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
setCommentList(mappedList);
|
setCommentList(mappedList);
|
||||||
|
|
||||||
|
// 获取所有评论ID列表并标记已读(传入所有ID)
|
||||||
|
const allCommentIds = res.data.rows.map((item: any) => item.id);
|
||||||
|
|
||||||
|
if (allCommentIds.length > 0) {
|
||||||
|
messageService.markAsRead('comment', allCommentIds).catch(e => {
|
||||||
|
console.error("标记评论已读失败:", e);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
|
|||||||
@@ -56,14 +56,12 @@ const NewFollow = () => {
|
|||||||
|
|
||||||
setFollowList(mappedList);
|
setFollowList(mappedList);
|
||||||
|
|
||||||
// 获取未读关注ID并标记已读
|
// 获取所有关注者ID列表并标记已读(传入所有ID,包括已读和未读)
|
||||||
const unreadFanIds = res.list
|
const allFanIds = res.list.map((item: any) => item.id);
|
||||||
.filter((item: any) => item.is_read === 0)
|
|
||||||
.map((item: any) => item.id);
|
|
||||||
|
|
||||||
if (unreadFanIds.length > 0) {
|
if (allFanIds.length > 0) {
|
||||||
// 使用统一接口标记已读
|
// 使用统一接口标记已读,传入所有关注者ID
|
||||||
messageService.markAsRead('follow', unreadFanIds).catch(e => {
|
messageService.markAsRead('follow', allFanIds).catch(e => {
|
||||||
console.error("标记关注已读失败:", e);
|
console.error("标记关注已读失败:", e);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -164,6 +162,15 @@ const NewFollow = () => {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
setFollowList(mappedList);
|
setFollowList(mappedList);
|
||||||
|
|
||||||
|
// 获取所有关注者ID列表并标记已读(传入所有ID)
|
||||||
|
const allFanIds = res.list.map((item: any) => item.id);
|
||||||
|
|
||||||
|
if (allFanIds.length > 0) {
|
||||||
|
messageService.markAsRead('follow', allFanIds).catch(e => {
|
||||||
|
console.error("标记关注已读失败:", e);
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// 如果没有数据,设置为空数组以显示空状态
|
// 如果没有数据,设置为空数组以显示空状态
|
||||||
setFollowList([]);
|
setFollowList([]);
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding-bottom: env(safe-area-inset-bottom);
|
padding-bottom: env(safe-area-inset-bottom);
|
||||||
|
box-sizing: border-box;
|
||||||
// 搜索区域
|
// 搜索区域
|
||||||
.search-section {
|
.search-section {
|
||||||
background: #f5f5f5;
|
background: #f5f5f5;
|
||||||
|
|||||||
@@ -106,8 +106,15 @@ const SelectStadium: React.FC<SelectStadiumProps> = ({
|
|||||||
})
|
})
|
||||||
setShowDetail(true)
|
setShowDetail(true)
|
||||||
},
|
},
|
||||||
fail: (err) => {
|
fail: (err: { errMsg: string }) => {
|
||||||
console.error('选择位置失败:', err)
|
console.error('选择位置失败:', err)
|
||||||
|
const { errMsg } = err || {};
|
||||||
|
if (!errMsg.includes('fail cancel')) {
|
||||||
|
Taro.showToast({
|
||||||
|
title: errMsg,
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,12 +145,15 @@ const StadiumDetail = forwardRef<StadiumDetailRef, StadiumDetailProps>(({
|
|||||||
istance: null
|
istance: null
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
fail: (err) => {
|
fail: (err: { errMsg: string }) => {
|
||||||
console.error('选择位置失败:', err)
|
console.error('选择位置失败:', err)
|
||||||
Taro.showToast({
|
const { errMsg } = err || {};
|
||||||
title: '位置选择失败',
|
if (!errMsg.includes('fail cancel')) {
|
||||||
icon: 'error'
|
Taro.showToast({
|
||||||
})
|
title: errMsg,
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
export default definePageConfig({
|
export default definePageConfig({
|
||||||
navigationStyle: 'custom'
|
navigationStyle: 'custom',
|
||||||
|
// 禁止原生页面滚动,改用内部自定义滚动容器,避免顶部/底部回弹后中间无法继续滚动的问题
|
||||||
|
disableScroll: true,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ const PublishBall: React.FC = () => {
|
|||||||
defaultFormData,
|
defaultFormData,
|
||||||
]);
|
]);
|
||||||
const [checked, setChecked] = useState(true);
|
const [checked, setChecked] = useState(true);
|
||||||
|
const [publishLoading, setPublishLoading] = useState(false);
|
||||||
const [titleBar, setTitleBar] = useState("发布球局");
|
const [titleBar, setTitleBar] = useState("发布球局");
|
||||||
// 控制是否响应全局键盘(由具体输入框 focus/blur 控制)
|
// 控制是否响应全局键盘(由具体输入框 focus/blur 控制)
|
||||||
const [shouldReactToKeyboard, setShouldReactToKeyboard] = useState(false);
|
const [shouldReactToKeyboard, setShouldReactToKeyboard] = useState(false);
|
||||||
@@ -372,9 +373,10 @@ const PublishBall: React.FC = () => {
|
|||||||
const { republish } = params || {};
|
const { republish } = params || {};
|
||||||
if (activityType === "individual") {
|
if (activityType === "individual") {
|
||||||
const isValid = validateFormData(formData[0]);
|
const isValid = validateFormData(formData[0]);
|
||||||
if (!isValid) {
|
if (!isValid || publishLoading) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
setPublishLoading(true);
|
||||||
const {
|
const {
|
||||||
activityInfo,
|
activityInfo,
|
||||||
descriptionInfo,
|
descriptionInfo,
|
||||||
@@ -435,13 +437,15 @@ const PublishBall: React.FC = () => {
|
|||||||
title: res.message,
|
title: res.message,
|
||||||
icon: "none",
|
icon: "none",
|
||||||
});
|
});
|
||||||
|
setPublishLoading(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (activityType === "group") {
|
if (activityType === "group") {
|
||||||
const isValid = formData.every((item) => validateFormData(item));
|
const isValid = formData.every((item) => validateFormData(item));
|
||||||
if (!isValid) {
|
if (!isValid || publishLoading) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
setPublishLoading(true);
|
||||||
if (checkAdjacentDataSame(formData)) {
|
if (checkAdjacentDataSame(formData)) {
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: "信息不可与前序场完全一致",
|
title: "信息不可与前序场完全一致",
|
||||||
@@ -505,6 +509,7 @@ const PublishBall: React.FC = () => {
|
|||||||
title: res.message,
|
title: res.message,
|
||||||
icon: "none",
|
icon: "none",
|
||||||
});
|
});
|
||||||
|
setPublishLoading(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,6 +14,13 @@ interface DictionaryState {
|
|||||||
fetchDictionary: () => Promise<void>
|
fetchDictionary: () => Promise<void>
|
||||||
getDictionaryValue: (key: string, defaultValue?: any) => any
|
getDictionaryValue: (key: string, defaultValue?: any) => any
|
||||||
clearDictionary: () => void
|
clearDictionary: () => void
|
||||||
|
// banner 字典(单独管理,保持原始值)
|
||||||
|
bannerDict: {
|
||||||
|
bannerListImage: string
|
||||||
|
bannerDetailImage: string
|
||||||
|
bannerListIndex: string
|
||||||
|
} | null
|
||||||
|
fetchBannerDictionary: () => Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建字典Store
|
// 创建字典Store
|
||||||
@@ -22,6 +29,7 @@ export const useDictionaryStore = create<DictionaryState>()((set, get) => ({
|
|||||||
dictionaryData: {},
|
dictionaryData: {},
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
error: null,
|
error: null,
|
||||||
|
bannerDict: null,
|
||||||
|
|
||||||
// 获取字典数据
|
// 获取字典数据
|
||||||
fetchDictionary: async () => {
|
fetchDictionary: async () => {
|
||||||
@@ -56,6 +64,27 @@ export const useDictionaryStore = create<DictionaryState>()((set, get) => ({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 获取 Banner 字典(启动时或手动调用)
|
||||||
|
fetchBannerDictionary: async () => {
|
||||||
|
try {
|
||||||
|
const keys = 'bannerListImage,bannerDetailImage,bannerListIndex';
|
||||||
|
const response = await commonApi.getDictionaryManyKey(keys)
|
||||||
|
if (response.code === 0 && response.data) {
|
||||||
|
const data = response.data || {};
|
||||||
|
set({
|
||||||
|
bannerDict: {
|
||||||
|
bannerListImage: data.bannerListImage || '',
|
||||||
|
bannerDetailImage: data.bannerDetailImage || '',
|
||||||
|
bannerListIndex: (data.bannerListIndex ?? '').toString(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// 保持静默,避免影响启动流程
|
||||||
|
console.error('获取 Banner 字典失败:', error)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// 获取字典值
|
// 获取字典值
|
||||||
getDictionaryValue: (key: string, defaultValue?: any) => {
|
getDictionaryValue: (key: string, defaultValue?: any) => {
|
||||||
const { dictionaryData } = get()
|
const { dictionaryData } = get()
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ import {
|
|||||||
getCityQrCode,
|
getCityQrCode,
|
||||||
getDistricts,
|
getDistricts,
|
||||||
} from "../services/listApi";
|
} from "../services/listApi";
|
||||||
import commonApi from "../services/commonApi";
|
// 不再在这里请求 banner 字典,统一由 dictionaryStore 启动时获取
|
||||||
|
import { useDictionaryStore } from "./dictionaryStore";
|
||||||
import {
|
import {
|
||||||
ListActions,
|
ListActions,
|
||||||
IFilterOptions,
|
IFilterOptions,
|
||||||
@@ -19,17 +20,16 @@ import {
|
|||||||
IPayload,
|
IPayload,
|
||||||
} from "../../types/list/types";
|
} from "../../types/list/types";
|
||||||
|
|
||||||
// 将 banner 按索引插入到 rows 的工具方法
|
// 将 banner 按索引插入到列表的工具方法(0基;长度不足则插末尾;先移除已存在的 banner)
|
||||||
function insertBannersToRows(rows: any[], dictData: any) {
|
function insertBannersToRows(rows: any[], dictData: any) {
|
||||||
if (!Array.isArray(rows) || !dictData) return rows;
|
if (!Array.isArray(rows) || !dictData) return rows;
|
||||||
// 仅单张图片与单个索引
|
|
||||||
const img = (dictData?.bannerListImage || "").trim();
|
const img = (dictData?.bannerListImage || "").trim();
|
||||||
const indexRaw = (dictData?.bannerListIndex || "").toString().trim();
|
const indexRaw = (dictData?.bannerListIndex || "").toString().trim();
|
||||||
if (!img) return rows;
|
if (!img) return rows;
|
||||||
// 固定采用 0 基索引
|
|
||||||
const parsed = parseInt(indexRaw, 10);
|
const parsed = parseInt(indexRaw, 10);
|
||||||
const normalized = Number.isFinite(parsed) ? parsed : 0;
|
const normalized = Number.isFinite(parsed) ? parsed : 0;
|
||||||
const resultRows = [...rows];
|
// 先移除已有的 banner,确保列表中仅一条 banner
|
||||||
|
const resultRows = rows?.filter((item) => item?.type !== "banner") || [];
|
||||||
const target = Math.max(0, Math.min(normalized, resultRows.length));
|
const target = Math.max(0, Math.min(normalized, resultRows.length));
|
||||||
resultRows.splice(target, 0, {
|
resultRows.splice(target, 0, {
|
||||||
type: "banner",
|
type: "banner",
|
||||||
@@ -272,10 +272,14 @@ export const useListStore = create<TennisStore>()((set, get) => ({
|
|||||||
const currentPageState = state.isSearchResult ? state.searchPageState : state.listPageState;
|
const currentPageState = state.isSearchResult ? state.searchPageState : state.listPageState;
|
||||||
const currentData = currentPageState?.data || [];
|
const currentData = currentPageState?.data || [];
|
||||||
const newData = isAppend ? [...currentData, ...(data || [])] : (data || []);
|
const newData = isAppend ? [...currentData, ...(data || [])] : (data || []);
|
||||||
|
// 从字典缓存获取 banner,并将其插入到最终列表指定位置(全局索引)
|
||||||
|
const dictData = useDictionaryStore.getState().bannerDict;
|
||||||
|
const processedData = dictData ? insertBannersToRows(newData, dictData) : newData;
|
||||||
state.updateCurrentPageState({
|
state.updateCurrentPageState({
|
||||||
data: newData,
|
data: processedData,
|
||||||
isHasMoreData,
|
isHasMoreData,
|
||||||
isShowNoData: newData?.length === 0,
|
// 使用插入后的最终数据判断是否显示空状态,避免有 banner 时仍显示空
|
||||||
|
isShowNoData: processedData?.length === 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
set({
|
set({
|
||||||
@@ -318,30 +322,8 @@ export const useListStore = create<TennisStore>()((set, get) => ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 并发请求:列表接口 + 字典接口(仅第一页且非追加时插入 Banner)
|
|
||||||
const shouldInsertBanner = (currentPageState?.pageOption?.page || 1) === 1 && !isAppend;
|
|
||||||
const keys = "bannerListImage,bannerDetailImage,bannerListIndex";
|
|
||||||
let resData: any = {};
|
let resData: any = {};
|
||||||
let dictData: any = null;
|
resData = (await fetchFn(reqParams)) || {};
|
||||||
|
|
||||||
if (shouldInsertBanner) {
|
|
||||||
const [listResult, dictResult] = await Promise.allSettled([
|
|
||||||
fetchFn(reqParams),
|
|
||||||
commonApi.getDictionaryManyKey(keys),
|
|
||||||
]);
|
|
||||||
// 列表接口请求成功
|
|
||||||
if (listResult.status === "fulfilled") {
|
|
||||||
resData = listResult.value || {};
|
|
||||||
} else {
|
|
||||||
throw listResult.reason || new Error("获取数据失败");
|
|
||||||
}
|
|
||||||
// 字典接口请求成功
|
|
||||||
if (dictResult.status === "fulfilled" && (dictResult.value as any)?.code === 0) {
|
|
||||||
dictData = (dictResult.value as any)?.data || null;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
resData = (await fetchFn(reqParams)) || {};
|
|
||||||
}
|
|
||||||
|
|
||||||
const { data = {}, code } = resData;
|
const { data = {}, code } = resData;
|
||||||
if (code !== 0) {
|
if (code !== 0) {
|
||||||
@@ -357,11 +339,6 @@ export const useListStore = create<TennisStore>()((set, get) => ({
|
|||||||
const { count } = data;
|
const { count } = data;
|
||||||
let { rows } = data as any;
|
let { rows } = data as any;
|
||||||
|
|
||||||
// 将 banner 插入到指定位置(仅第一页且非追加)
|
|
||||||
if (shouldInsertBanner && Array.isArray(rows) && dictData) {
|
|
||||||
rows = insertBannersToRows(rows, dictData);
|
|
||||||
}
|
|
||||||
|
|
||||||
setListData({
|
setListData({
|
||||||
error: '',
|
error: '',
|
||||||
data: rows || [],
|
data: rows || [],
|
||||||
@@ -397,10 +374,9 @@ export const useListStore = create<TennisStore>()((set, get) => ({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const searchParams = getSearchParams() || {};
|
const searchParams = getSearchParams() || {};
|
||||||
const keys = "bannerListImage,bannerDetailImage,bannerListIndex";
|
|
||||||
|
|
||||||
// 并发请求:常规列表、智能排序列表、字典
|
// 并发请求:常规列表、智能排序列表
|
||||||
const [listResSettled, integrateResSettled, dictSettled] = await Promise.allSettled([
|
const [listResSettled, integrateResSettled] = await Promise.allSettled([
|
||||||
getGamesList({
|
getGamesList({
|
||||||
...searchParams,
|
...searchParams,
|
||||||
order: searchParams.order || "distance",
|
order: searchParams.order || "distance",
|
||||||
@@ -413,14 +389,10 @@ export const useListStore = create<TennisStore>()((set, get) => ({
|
|||||||
isRefresh: true,
|
isRefresh: true,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
commonApi.getDictionaryManyKey(keys),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const listRes = listResSettled.status === "fulfilled" ? listResSettled.value : null;
|
const listRes = listResSettled.status === "fulfilled" ? listResSettled.value : null;
|
||||||
const integrateRes = integrateResSettled.status === "fulfilled" ? integrateResSettled.value : null;
|
const integrateRes = integrateResSettled.status === "fulfilled" ? integrateResSettled.value : null;
|
||||||
const dictData = dictSettled.status === "fulfilled" && (dictSettled.value as any)?.code === 0
|
|
||||||
? (dictSettled.value as any)?.data
|
|
||||||
: null;
|
|
||||||
|
|
||||||
// 根据当前排序方式更新对应的数据
|
// 根据当前排序方式更新对应的数据
|
||||||
const currentPageState = state.isSearchResult ? state.searchPageState : state.listPageState;
|
const currentPageState = state.isSearchResult ? state.searchPageState : state.listPageState;
|
||||||
@@ -430,10 +402,6 @@ export const useListStore = create<TennisStore>()((set, get) => ({
|
|||||||
if (listRes?.code === 0 && listRes?.data) {
|
if (listRes?.code === 0 && listRes?.data) {
|
||||||
const { count } = listRes.data;
|
const { count } = listRes.data;
|
||||||
let { rows } = listRes.data as any;
|
let { rows } = listRes.data as any;
|
||||||
// 插入 banner(当为第一页时)
|
|
||||||
if ((currentPageState?.pageOption?.page || 1) === 1 && Array.isArray(rows) && dictData) {
|
|
||||||
rows = insertBannersToRows(rows, dictData);
|
|
||||||
}
|
|
||||||
if (!isIntegrate) {
|
if (!isIntegrate) {
|
||||||
// 如果当前是常规排序,更新常规列表数据
|
// 如果当前是常规排序,更新常规列表数据
|
||||||
setListData({
|
setListData({
|
||||||
@@ -449,10 +417,6 @@ export const useListStore = create<TennisStore>()((set, get) => ({
|
|||||||
if (integrateRes?.code === 0 && integrateRes?.data) {
|
if (integrateRes?.code === 0 && integrateRes?.data) {
|
||||||
const { count } = integrateRes.data;
|
const { count } = integrateRes.data;
|
||||||
let { rows, recommendList } = integrateRes.data as any;
|
let { rows, recommendList } = integrateRes.data as any;
|
||||||
// 插入 banner(当为第一页时)
|
|
||||||
if ((currentPageState?.pageOption?.page || 1) === 1 && Array.isArray(rows) && dictData) {
|
|
||||||
rows = insertBannersToRows(rows, dictData);
|
|
||||||
}
|
|
||||||
if (isIntegrate) {
|
if (isIntegrate) {
|
||||||
// 如果当前是智能排序,更新智能排序列表数据
|
// 如果当前是智能排序,更新智能排序列表数据
|
||||||
setListData({
|
setListData({
|
||||||
|
|||||||
@@ -76,7 +76,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
width: 78px;
|
width: 90px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
border: 1px solid rgba(0, 0, 0, 0.06);
|
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -64,7 +64,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
width: 78px;
|
width: 90px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
border: 1px solid rgba(0, 0, 0, 0.06);
|
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
Reference in New Issue
Block a user