diff --git a/src/app.config.ts b/src/app.config.ts index 5b5af4f..9816e39 100644 --- a/src/app.config.ts +++ b/src/app.config.ts @@ -57,6 +57,7 @@ export default defineAppConfig({ "ntrp-evaluate/index", // NTRP评估页 "enable_notification/index", // 开启消息通知 "emptyState/index", // 空状态页面 + "bannerDetail/index", // Banner 图片详情页 ], }, ], diff --git a/src/container/listContainer/index.tsx b/src/container/listContainer/index.tsx index ec5a412..a75bf0e 100644 --- a/src/container/listContainer/index.tsx +++ b/src/container/listContainer/index.tsx @@ -3,6 +3,7 @@ import ListCard from "@/components/ListCard"; import ListLoadError from "@/components/ListLoadError"; import ListCardSkeleton from "@/components/ListCardSkeleton"; import { useReachBottom } from "@tarojs/taro"; +import Taro from "@tarojs/taro"; import { useUserInfo, useUserActions, useLastTestResult } from "@/store/userStore"; import { NTRPTestEntryCard } from "@/components"; import { EvaluateScene } from "@/store/evaluateStore"; @@ -158,6 +159,35 @@ const ListContainer = (props) => { [evaluateFlag, data, hasTestInLastMonth, showNumber] ); + // 渲染 banner 卡片 + const renderBanner = (item, index) => { + if (!item?.banner_image_url) return null; + return ( + + { + const target = item.banner_detail_url; + if (target) { + (Taro as any).navigateTo({ + url: `/other_pages/bannerDetail/index?img=${encodeURIComponent(target)}`, + }); + } + }} + /> + + ); + }; + // 渲染列表 const renderList = () => { // 请求数据为空 @@ -181,6 +211,9 @@ const ListContainer = (props) => { return ( <> {memoizedList.map((match, index) => { + if (match.type === "banner") { + return renderBanner(match, index); + } if (match.type === "evaluateCard") { return ( diff --git a/src/main_pages/components/ListPageContent.tsx b/src/main_pages/components/ListPageContent.tsx index 58703b4..986fbf7 100644 --- a/src/main_pages/components/ListPageContent.tsx +++ b/src/main_pages/components/ListPageContent.tsx @@ -385,7 +385,9 @@ const ListPageContent: React.FC = ({ }; const handleRefresh = async () => { - + if (refreshing) { + return; + } setRefreshing(true); try { diff --git a/src/other_pages/bannerDetail/index.config.ts b/src/other_pages/bannerDetail/index.config.ts new file mode 100644 index 0000000..7d94eab --- /dev/null +++ b/src/other_pages/bannerDetail/index.config.ts @@ -0,0 +1,8 @@ +export default definePageConfig({ + navigationBarTitleText: '', + navigationStyle: 'custom', + navigationBarBackgroundColor: '#FFFFFF', + backgroundColor: '#FFFFFF', +}); + + diff --git a/src/other_pages/bannerDetail/index.scss b/src/other_pages/bannerDetail/index.scss new file mode 100644 index 0000000..e29887d --- /dev/null +++ b/src/other_pages/bannerDetail/index.scss @@ -0,0 +1,16 @@ +.banner_detail_page { + min-height: 100vh; + background: #ffffff; +} + +.banner_detail_content { + padding: 12px; +} + +.banner_detail_image { + width: 100%; + border-radius: 12px; + display: block; +} + + diff --git a/src/other_pages/bannerDetail/index.tsx b/src/other_pages/bannerDetail/index.tsx new file mode 100644 index 0000000..cc80521 --- /dev/null +++ b/src/other_pages/bannerDetail/index.tsx @@ -0,0 +1,36 @@ +import { useEffect, useState } from 'react'; +import { View, Image } from '@tarojs/components'; +import Taro from '@tarojs/taro'; +import { GeneralNavbar } from '@/components'; +import './index.scss'; + +function Index() { + const [imgUrl, setImgUrl] = useState(''); + + useEffect(() => { + const instance = (Taro as any).getCurrentInstance?.(); + const params = instance?.router?.params || {}; + const url = params?.img ? decodeURIComponent(params.img) : ''; + setImgUrl(url); + }, []); + + return ( + + + + {imgUrl ? ( + + ) : null} + + + ); +} + +export default Index; + + diff --git a/src/store/listStore.ts b/src/store/listStore.ts index 0c41c7c..046e9a2 100644 --- a/src/store/listStore.ts +++ b/src/store/listStore.ts @@ -11,6 +11,7 @@ import { getCityQrCode, getDistricts, } from "../services/listApi"; +import commonApi from "../services/commonApi"; import { ListActions, IFilterOptions, @@ -18,6 +19,27 @@ import { IPayload, } from "../../types/list/types"; +// 将 banner 按索引插入到 rows 的工具方法 +function insertBannersToRows(rows: any[], dictData: any) { + if (!Array.isArray(rows) || !dictData) return rows; + // 仅单张图片与单个索引 + const img = (dictData?.bannerListImage || "").trim(); + const indexRaw = (dictData?.bannerListIndex || "").toString().trim(); + if (!img) return rows; + // 固定采用 0 基索引 + const parsed = parseInt(indexRaw, 10); + const normalized = Number.isFinite(parsed) ? parsed : 0; + const resultRows = [...rows]; + const target = Math.max(0, Math.min(normalized, resultRows.length)); + resultRows.splice(target, 0, { + type: "banner", + id: `banner-${target}`, + banner_image_url: img, + banner_detail_url: (dictData?.bannerDetailImage || "").trim(), + } as any); + return resultRows; +} + function translateCityData(dataTree) { return dataTree.map((item) => { const { children, ...rest } = item; @@ -296,7 +318,31 @@ export const useListStore = create()((set, get) => ({ } } - const resData = (await fetchFn(reqParams)) || {}; + // 并发请求:列表接口 + 字典接口(仅第一页且非追加时插入 Banner) + const shouldInsertBanner = (currentPageState?.pageOption?.page || 1) === 1 && !isAppend; + const keys = "bannerListImage,bannerDetailImage,bannerListIndex"; + let resData: any = {}; + let dictData: any = null; + + 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; if (code !== 0) { setListData({ @@ -308,7 +354,14 @@ export const useListStore = create()((set, get) => ({ }); return Promise.reject(new Error('获取数据失败')); } - const { count, rows } = data; + const { count } = data; + let { rows } = data as any; + + // 将 banner 插入到指定位置(仅第一页且非追加) + if (shouldInsertBanner && Array.isArray(rows) && dictData) { + rows = insertBannersToRows(rows, dictData); + } + setListData({ error: '', data: rows || [], @@ -319,7 +372,7 @@ export const useListStore = create()((set, get) => ({ return Promise.resolve(); } catch (error) { setListData({ - error: "-1", + error: "", data: [], loading: false, count: 0, @@ -344,24 +397,30 @@ export const useListStore = create()((set, get) => ({ try { const searchParams = getSearchParams() || {}; + const keys = "bannerListImage,bannerDetailImage,bannerListIndex"; - // 调用常规列表接口 - const listParams = { - ...searchParams, - order: searchParams.order || "distance", - }; - const listRes = await getGamesList(listParams); - - // 调用智能排序列表接口 - const integrateParams = { - ...searchParams, - order: "", - seachOption: { - ...searchParams.seachOption, - isRefresh: true, - }, - }; - const integrateRes = await getGamesIntegrateList(integrateParams); + // 并发请求:常规列表、智能排序列表、字典 + const [listResSettled, integrateResSettled, dictSettled] = await Promise.allSettled([ + getGamesList({ + ...searchParams, + order: searchParams.order || "distance", + }), + getGamesIntegrateList({ + ...searchParams, + order: "", + seachOption: { + ...searchParams.seachOption, + isRefresh: true, + }, + }), + commonApi.getDictionaryManyKey(keys), + ]); + + const listRes = listResSettled.status === "fulfilled" ? listResSettled.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; @@ -369,7 +428,12 @@ export const useListStore = create()((set, get) => ({ const isIntegrate = distanceQuickFilter?.order === "0"; if (listRes?.code === 0 && listRes?.data) { - const { count, rows } = listRes.data; + const { count } = listRes.data; + let { rows } = listRes.data as any; + // 插入 banner(当为第一页时) + if ((currentPageState?.pageOption?.page || 1) === 1 && Array.isArray(rows) && dictData) { + rows = insertBannersToRows(rows, dictData); + } if (!isIntegrate) { // 如果当前是常规排序,更新常规列表数据 setListData({ @@ -383,7 +447,12 @@ export const useListStore = create()((set, get) => ({ } if (integrateRes?.code === 0 && integrateRes?.data) { - const { count, rows, recommendList } = integrateRes.data; + const { count } = integrateRes.data; + let { rows, recommendList } = integrateRes.data as any; + // 插入 banner(当为第一页时) + if ((currentPageState?.pageOption?.page || 1) === 1 && Array.isArray(rows) && dictData) { + rows = insertBannersToRows(rows, dictData); + } if (isIntegrate) { // 如果当前是智能排序,更新智能排序列表数据 setListData({