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({