hookehuyr

refactor(api): 提取地图活动API并新增展位地图画廊页面

迁移扫码打卡相关API至独立的map_activity.js文件,更新ScanCheckinDetail和ScanCheckinList页面的导入路径。新增BoothMapGallery页面,支持加载状态、空数据提示、网格展位图展示与图片预览,完善新API的文档注释。
...@@ -12,9 +12,6 @@ const Api = { ...@@ -12,9 +12,6 @@ const Api = {
12 GET_POSTER_DETAIL: '/srv/?a=map&t=poster', 12 GET_POSTER_DETAIL: '/srv/?a=map&t=poster',
13 GET_ACTIVITY_STATUS: '/srv/?a=map&t=get_map_url', 13 GET_ACTIVITY_STATUS: '/srv/?a=map&t=get_map_url',
14 SAVE_POSTER_BACKGROUND: '/srv/?a=map&t=save_poster_background', 14 SAVE_POSTER_BACKGROUND: '/srv/?a=map&t=save_poster_background',
15 - GET_SCAN_STAGE_LIST: '/srv/?a=map_activity&t=scan_stage_list',
16 - GET_SCAN_STAGE_DETAIL: '/srv/?a=map_activity&t=scan_stage_detail',
17 - SUBMIT_SCAN_CHECKIN: '/srv/?a=map_activity&t=checkin',
18 } 15 }
19 16
20 /** 17 /**
...@@ -72,51 +69,3 @@ export const getActivityStatusAPI = params => fn(fetch.get(Api.GET_ACTIVITY_STAT ...@@ -72,51 +69,3 @@ export const getActivityStatusAPI = params => fn(fetch.get(Api.GET_ACTIVITY_STAT
72 * @returns {Object} response.data - 响应数据 69 * @returns {Object} response.data - 响应数据
73 */ 70 */
74 export const savePosterBackgroundAPI = params => fn(fetch.post(Api.SAVE_POSTER_BACKGROUND, params)) 71 export const savePosterBackgroundAPI = params => fn(fetch.post(Api.SAVE_POSTER_BACKGROUND, params))
75 -
76 -/**
77 - * @description: 获取扫码关卡列表
78 - * @param {Object} params - 请求参数
79 - * @param {string} [params.page] - 页码,从 0 开始
80 - * @param {string} [params.limit] - 每页条数
81 - * @param {string} [params.activity_id] - 活动ID
82 - * @returns {number} response.code - 响应状态码
83 - * @returns {string} response.msg - 响应消息
84 - * @returns {Object} response.data - 响应数据
85 - * @returns {Array} response.data.stages - 关卡列表
86 - * @returns {number} response.data.stages[].id - 关卡ID
87 - * @returns {string} response.data.stages[].title - 关卡标题
88 - * @returns {boolean} response.data.stages[].is_checked - 是否已打卡
89 - */
90 -export const getScanStageListAPI = params => fn(fetch.get(Api.GET_SCAN_STAGE_LIST, params))
91 -
92 -/**
93 - * @description: 获取扫码关卡详情
94 - * @param {Object} params - 请求参数
95 - * @param {string} params.id - 关卡ID
96 - * @returns {number} response.code - 响应状态码
97 - * @returns {string} response.msg - 响应消息
98 - * @returns {Object} response.data - 响应数据
99 - * @returns {number} response.data.id - 关卡ID
100 - * @returns {string} response.data.title - 关卡标题
101 - * @returns {Array<string>} response.data.banner - 轮播图
102 - * @returns {boolean} response.data.is_checked - 是否已打卡
103 - * @returns {string} response.data.discount_title - 打卡点底部优惠标题
104 - * @returns {string} response.data.note - 简介
105 - * @returns {string} response.data.introduction - 底部富文本
106 - * @returns {boolean} response.data.geo_enabled - 是否启用地理位置限制
107 - * @returns {number} response.data.center_lng - 经度
108 - * @returns {number} response.data.center_lat - 纬度
109 - * @returns {number} response.data.radius_meters - 半径,单位米
110 - */
111 -export const getScanStageDetailAPI = params => fn(fetch.get(Api.GET_SCAN_STAGE_DETAIL, params))
112 -
113 -/**
114 - * @description: 提交扫码打卡
115 - * @param {Object} params - 请求参数
116 - * @param {string} params.activity_id - 活动ID
117 - * @param {string} params.detail_id - 关卡ID
118 - * @param {string} [params.openid] - 用户openid,接口定义中为可选
119 - * @returns {number} response.code - 响应状态码
120 - * @returns {string} response.msg - 响应消息
121 - */
122 -export const submitScanCheckinAPI = params => fn(fetch.post(Api.SUBMIT_SCAN_CHECKIN, params))
......
...@@ -7,6 +7,9 @@ const Api = { ...@@ -7,6 +7,9 @@ const Api = {
7 List: '/srv/?a=map_activity&t=list', 7 List: '/srv/?a=map_activity&t=list',
8 Poster: '/srv/?a=map_activity&t=poster', 8 Poster: '/srv/?a=map_activity&t=poster',
9 SavePosterBackground: '/srv/?a=map_activity&t=save_poster_background', 9 SavePosterBackground: '/srv/?a=map_activity&t=save_poster_background',
10 + ScanStageList: '/srv/?a=map_activity&t=scan_stage_list',
11 + ScanStageDetail: '/srv/?a=map_activity&t=scan_stage_detail',
12 + SubmitScanCheckin: '/srv/?a=map_activity&t=checkin',
10 } 13 }
11 14
12 /** 15 /**
...@@ -36,6 +39,7 @@ export const checkinAPI = params => fn(fetch.post(Api.Checkin, params)) ...@@ -36,6 +39,7 @@ export const checkinAPI = params => fn(fetch.post(Api.Checkin, params))
36 url: string; // 地图网址 39 url: string; // 地图网址
37 id: integer; // 活动ID 40 id: integer; // 活动ID
38 type: string; // 打卡类型,MAP=地图打卡,QR_CODE=扫码打卡 41 type: string; // 打卡类型,MAP=地图打卡,QR_CODE=扫码打卡
42 + booth_images: Array<string>; // 打卡点图片列表
39 cover: string; // 封面图 43 cover: string; // 封面图
40 begin_date: string; // 开始时间 44 begin_date: string; // 开始时间
41 end_date: string; // 结束时间 45 end_date: string; // 结束时间
...@@ -88,6 +92,64 @@ export const isCheckedAPI = params => fn(fetch.get(Api.IsChecked, params)) ...@@ -88,6 +92,64 @@ export const isCheckedAPI = params => fn(fetch.get(Api.IsChecked, params))
88 export const listAPI = params => fn(fetch.get(Api.List, params)) 92 export const listAPI = params => fn(fetch.get(Api.List, params))
89 93
90 /** 94 /**
95 + * @description 获取扫码关卡列表
96 + * @param {Object} params 请求参数
97 + * @param {string} [params.page] 页码,从 0 开始
98 + * @param {string} [params.limit] 每页条数
99 + * @param {string} [params.activity_id] 活动ID
100 + * @returns {Promise<{
101 + * code: number;
102 + * msg: string;
103 + * data: {
104 + * stages: Array<{
105 + * id: number;
106 + * title: string;
107 + * is_checked: boolean;
108 + * }>;
109 + * };
110 + * }>}
111 + */
112 +export const getScanStageListAPI = params => fn(fetch.get(Api.ScanStageList, params))
113 +
114 +/**
115 + * @description 获取扫码关卡详情
116 + * @param {Object} params 请求参数
117 + * @param {string} params.id 关卡ID
118 + * @returns {Promise<{
119 + * code: number;
120 + * msg: string;
121 + * data: {
122 + * id: number;
123 + * title: string;
124 + * banner: Array<string>;
125 + * is_checked: boolean;
126 + * discount_title: string;
127 + * note: string;
128 + * introduction: string;
129 + * geo_enabled: boolean;
130 + * center_lng: number;
131 + * center_lat: number;
132 + * radius_meters: number;
133 + * };
134 + * }>}
135 + */
136 +export const getScanStageDetailAPI = params => fn(fetch.get(Api.ScanStageDetail, params))
137 +
138 +/**
139 + * @description 提交扫码打卡
140 + * @param {Object} params 请求参数
141 + * @param {string} params.activity_id 活动ID
142 + * @param {string} params.detail_id 关卡ID
143 + * @param {string} [params.openid] 用户openid
144 + * @returns {Promise<{
145 + * code: number;
146 + * msg: string;
147 + * data: any;
148 + * }>}
149 + */
150 +export const submitScanCheckinAPI = params => fn(fetch.post(Api.SubmitScanCheckin, params))
151 +
152 +/**
91 * @description 获取海报 153 * @description 获取海报
92 * @remark 154 * @remark
93 * @param {Object} params 请求参数 155 * @param {Object} params 请求参数
......
...@@ -5,6 +5,13 @@ ...@@ -5,6 +5,13 @@
5 box-sizing: border-box; 5 box-sizing: border-box;
6 } 6 }
7 7
8 +.booth-map-gallery-status {
9 + padding: 120rpx 24rpx;
10 + text-align: center;
11 + font-size: 28rpx;
12 + color: #6b7280;
13 +}
14 +
8 .booth-map-gallery-grid { 15 .booth-map-gallery-grid {
9 column-count: 2; 16 column-count: 2;
10 column-gap: 20rpx; 17 column-gap: 20rpx;
......
1 <template> 1 <template>
2 <view class="booth-map-gallery-page"> 2 <view class="booth-map-gallery-page">
3 - <view class="booth-map-gallery-grid"> 3 + <view v-if="loading" class="booth-map-gallery-status">加载中...</view>
4 +
5 + <view v-else-if="imageList.length === 0" class="booth-map-gallery-status"> 暂无展位图 </view>
6 +
7 + <view v-else class="booth-map-gallery-grid">
4 <view 8 <view
5 v-for="(item, index) in imageList" 9 v-for="(item, index) in imageList"
6 :key="item.id" 10 :key="item.id"
...@@ -15,28 +19,72 @@ ...@@ -15,28 +19,72 @@
15 19
16 <script setup> 20 <script setup>
17 import { ref } from 'vue' 21 import { ref } from 'vue'
18 -import Taro from '@tarojs/taro' 22 +import Taro, { useLoad } from '@tarojs/taro'
19 import './index.less' 23 import './index.less'
24 +import { detailAPI } from '@/api/map_activity'
20 25
21 -const imageList = ref([ 26 +const imageList = ref([])
22 - { 27 +const loading = ref(false)
23 - id: 'booth-01', 28 +const activityId = ref('')
24 - url: 'https://cdn.ipadbiz.cn/lls_prog/images/check_detail_img.png?imageMogr2/strip/quality/60', 29 +
25 - mode: 'widthFix', 30 +const isApiSuccess = code => Number(code) === 1
26 - }, 31 +
27 - { 32 +const mapBoothImages = boothImages =>
28 - id: 'booth-02', 33 + boothImages
29 - url: 'https://cdn.ipadbiz.cn/lls_prog/images/welcome_8.jpg?imageMogr2/strip/quality/60', 34 + .filter(item => typeof item === 'string' && item.trim() !== '')
30 - mode: 'widthFix', 35 + .map((url, index) => ({
31 - }, 36 + id: `booth-${index}`,
32 - { 37 + url,
33 - id: 'booth-03',
34 - url: 'https://cdn.ipadbiz.cn/lls_prog/images/check_detail_img.png?imageMogr2/strip/quality/60',
35 mode: 'widthFix', 38 mode: 'widthFix',
36 - }, 39 + }))
37 -]) 40 +
41 +const fetchBoothImages = async () => {
42 + if (!activityId.value) {
43 + imageList.value = []
44 + Taro.showToast({
45 + title: '缺少活动信息',
46 + icon: 'none',
47 + })
48 + return
49 + }
50 +
51 + loading.value = true
52 +
53 + try {
54 + const result = await detailAPI({ id: activityId.value })
55 +
56 + if (!isApiSuccess(result?.code)) {
57 + Taro.showToast({
58 + title: result?.msg || '获取展位图失败',
59 + icon: 'none',
60 + })
61 + imageList.value = []
62 + return
63 + }
64 +
65 + imageList.value = mapBoothImages(result?.data?.booth_images || [])
66 + } catch (error) {
67 + console.error('[BoothMapGallery] 获取展位图失败:', error)
68 + imageList.value = []
69 + Taro.showToast({
70 + title: '获取展位图失败',
71 + icon: 'none',
72 + })
73 + } finally {
74 + loading.value = false
75 + }
76 +}
77 +
78 +useLoad(options => {
79 + activityId.value = options.activityId || options.activity_id || options.id || ''
80 + fetchBoothImages()
81 +})
38 82
39 const previewImage = index => { 83 const previewImage = index => {
84 + if (imageList.value.length === 0) {
85 + return
86 + }
87 +
40 Taro.previewImage({ 88 Taro.previewImage({
41 current: imageList.value[index].url, 89 current: imageList.value[index].url,
42 urls: imageList.value.map(item => item.url), 90 urls: imageList.value.map(item => item.url),
......
...@@ -57,7 +57,7 @@ import './index.less' ...@@ -57,7 +57,7 @@ import './index.less'
57 import RichTextRenderer from '@/components/RichTextRenderer.vue' 57 import RichTextRenderer from '@/components/RichTextRenderer.vue'
58 import { getCurrentPageFullPath } from '@/utils/authRedirect' 58 import { getCurrentPageFullPath } from '@/utils/authRedirect'
59 import { getMyFamiliesAPI } from '@/api/family' 59 import { getMyFamiliesAPI } from '@/api/family'
60 -import { getScanStageDetailAPI, submitScanCheckinAPI } from '@/api/map' 60 +import { getScanStageDetailAPI, submitScanCheckinAPI } from '@/api/map_activity'
61 import { getUserProfileAPI } from '@/api/user' 61 import { getUserProfileAPI } from '@/api/user'
62 import { verifyCheckinRangeWithCurrentLocation } from '@/utils/checkinLocation' 62 import { verifyCheckinRangeWithCurrentLocation } from '@/utils/checkinLocation'
63 import { 63 import {
......
1 <!-- 1 <!--
2 * @Date: 2026-05-19 14:40:21 2 * @Date: 2026-05-19 14:40:21
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2026-05-19 15:03:23 4 + * @LastEditTime: 2026-05-21 10:25:21
5 * @FilePath: /lls_program/src/pages/ScanCheckinList/index.vue 5 * @FilePath: /lls_program/src/pages/ScanCheckinList/index.vue
6 * @Description: 文件描述 6 * @Description: 文件描述
7 --> 7 -->
...@@ -68,7 +68,7 @@ import Taro, { useLoad } from '@tarojs/taro' ...@@ -68,7 +68,7 @@ import Taro, { useLoad } from '@tarojs/taro'
68 import { IconFont, Scan2 } from '@nutui/icons-vue-taro' 68 import { IconFont, Scan2 } from '@nutui/icons-vue-taro'
69 import './index.less' 69 import './index.less'
70 import BottomNav from '@/components/BottomNav.vue' 70 import BottomNav from '@/components/BottomNav.vue'
71 -import { getScanStageListAPI } from '@/api/map' 71 +import { getScanStageListAPI } from '@/api/map_activity'
72 72
73 const pointList = ref([]) 73 const pointList = ref([])
74 const activityId = ref('') 74 const activityId = ref('')
......