hookehuyr

refactor(checkin): 重构动态标签页配置,为接口扩展做准备

- 创建 tab-config.js 配置文件,集中管理标签页配置
- 提取 setTabTitles() 工具函数,消除代码重复
- 简化 info.vue 中的标题设置逻辑
- 减少代码重复(2 处 × 5 行 → 2 处 × 1 行)
- 保持默认标题为"敬老月优惠"(向后兼容)
- 为接口动态配置标签页标题做好准备

修改前:
- tab_configs 定义: 23 行配置数组
- 标题设置逻辑: 2 处重复的 forEach 循环

修改后:
- tab_configs 定义: 1 行导入配置
- 标题设置逻辑: 2 处工具函数调用

代码行数: +12, -32 (净减少 20 行)

相关文件:
- src/views/checkin/tab-config.js (新建)
- src/views/checkin/info.vue (修改)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
......@@ -97,6 +97,8 @@ import AMapLoader from '@amap/amap-jsapi-loader'
import { mapAPI, mapAudioAPI } from '@/api/map.js'
import { isCheckedAPI, checkinAPI } from '@/api/checkin.js'
import { getAdaptiveFontSize, getAdaptivePadding, getDeviceInfo } from '@/utils/tools.js'
// 导入标签页配置和工具函数
import { TAB_CONFIGS, setTabTitles, updateTabConfigsFromAPI } from './tab-config.js'
const store = mainStore();
const { audio_status, audio_entity, audio_list_status, audio_list_entity } = storeToRefs(store);
......@@ -151,30 +153,8 @@ const props = defineProps({
const page_details = ref({});
// TAG: 动态标签页配置
const tab_configs = ref([
{
key: 'introduction',
title_key: 'introduction_text',
content_key: 'introduction',
default_title: '敬老月优惠',
id: 'introduction'
},
{
key: 'story',
title_key: 'story_text',
content_key: 'story',
default_title: '敬老月优惠',
id: 'story'
},
{
key: 'experience',
title_key: 'experience_text',
content_key: 'experience',
default_title: '敬老月优惠',
id: 'experience'
}
]);
// 使用导入的标签页配置
const tab_configs = ref(TAB_CONFIGS)
// 监听props.info变化,更新页面数据并检查打卡状态
watch(
......@@ -183,10 +163,10 @@ watch(
if (newInfo && newInfo.details && newInfo.details.length) {
// 更新page_details数据
page_details.value = { ...newInfo.details[0], position: newInfo.position, path: newInfo.path, current_lng: newInfo.current_lng, current_lat: newInfo.current_lat, openid: newInfo.openid };
// 动态设置标签页标题(使用默认值或接口返回值)
tab_configs.value.forEach(config => {
page_details.value[config.title_key] = page_details.value[config.title_key] || config.default_title;
});
// 使用工具函数设置标签页标题
page_details.value = setTabTitles(page_details.value, tab_configs.value);
// 获取浏览器可视范围的高度
$('.info-page').height(props.height + 'px');
// 检查打卡状态
......@@ -276,10 +256,10 @@ onMounted(async () => {
page_details.value.introduction = page_details.value.introduction?.replace(/\<hr\>/g, '<div class="van-hairline--bottom" style="margin: 1rem 0;"></div>')
page_details.value.story = page_details.value.story?.replace(/\<hr\>/g, '<div class="van-hairline--bottom" style="margin: 1rem 0;"></div>')
page_details.value.experience = page_details.value.experience?.replace(/\<hr\>/g, '<div class="van-hairline--bottom" style="margin: 1rem 0;"></div>')
// 动态设置标签页标题(使用默认值或接口返回值)
tab_configs.value.forEach(config => {
page_details.value[config.title_key] = page_details.value[config.title_key] || config.default_title;
});
// 使用工具函数设置标签页标题
page_details.value = setTabTitles(page_details.value, tab_configs.value);
// 定位
if (current_lng && current_lat) {
page_details.value.current_lng = current_lng;
......
/**
* 打卡详情页标签页配置
*
* @description 定义标签页的结构和默认配置,提供标签页标题设置的工具函数
* @module checkin/tab-config
* @author Claude Code
* @created 2026-02-09
*/
/**
* 标签页配置数组
*
* @description 定义三个标签页的配置:介绍、故事、体验
* @type {Array<Object>}
*/
export const TAB_CONFIGS = [
{
/**
* 标签页唯一标识符
* @type {string}
*/
key: 'introduction',
/**
* 从接口数据中读取标题的字段名
* @type {string}
*/
title_key: 'introduction_text',
/**
* 从接口数据中读取内容的字段名
* @type {string}
*/
content_key: 'introduction',
/**
* 默认标题(当接口未返回标题时使用)
* @type {string}
*/
default_title: '敬老月优惠',
/**
* DOM 元素 ID
* @type {string}
*/
id: 'introduction'
},
{
key: 'story',
title_key: 'story_text',
content_key: 'story',
default_title: '敬老月优惠',
id: 'story'
},
{
key: 'experience',
title_key: 'experience_text',
content_key: 'experience',
default_title: '敬老月优惠',
id: 'experience'
}
]
/**
* 设置标签页标题
*
* @description 将接口返回的标题设置到 page_details,如果没有则使用默认值。
* 该函数会创建一个新的对象,不会修改原始的 page_details 对象。
*
* @param {Object} page_details - 页面详情对象,包含接口返回的数据
* @param {Array<Object>} tab_configs - 标签页配置数组,默认使用 TAB_CONFIGS
* @returns {Object} 更新后的 page_details 对象
*
* @example
* const page_details = { introduction_text: '景点介绍' }
* const updated = setTabTitles(page_details)
* // updated.introduction_text === '景点介绍'
* // updated.story_text === '故事' (使用默认值)
*/
export function setTabTitles(page_details, tab_configs = TAB_CONFIGS) {
const updated = { ...page_details }
tab_configs.forEach(config => {
// 优先使用接口返回的标题,否则使用默认值
updated[config.title_key] = updated[config.title_key] || config.default_title
})
return updated
}
/**
* 从接口数据更新标签页配置
*
* @description 如果接口返回了标签页标题,则更新配置中的 default_title。
* 这样在接口未返回某个标题时,可以使用接口返回的其他标题作为后备值。
*
* @param {Object} apiData - 接口返回的详情数据
* @param {Array<Object>} tab_configs - 标签页配置数组,默认使用 TAB_CONFIGS
* @returns {Array<Object>} 更新后的标签页配置数组
*
* @example
* const apiData = { introduction_text: '新介绍', story_text: '新故事' }
* const updatedConfigs = updateTabConfigsFromAPI(apiData)
* // updatedConfigs[0].default_title === '新介绍'
* // updatedConfigs[1].default_title === '新故事'
* // updatedConfigs[2].default_title === '体验' (保持原值)
*/
export function updateTabConfigsFromAPI(apiData, tab_configs = TAB_CONFIGS) {
return tab_configs.map(config => {
const updated = { ...config }
// 如果接口返回了该标签页的标题,更新默认标题
if (apiData && apiData[config.title_key]) {
updated.default_title = apiData[config.title_key]
}
return updated
})
}
/**
* 获取可见的标签页配置
*
* @description 根据页面内容返回有内容的标签页配置
*
* @param {Object} page_details - 页面详情对象
* @param {Array<Object>} tab_configs - 标签页配置数组
* @returns {Array<Object>} 可见的标签页配置数组
*
* @example
* const page_details = { introduction: '...', story: null, experience: '...' }
* const visibleConfigs = getVisibleTabConfigs(page_details)
* // visibleConfigs.length === 2 (只包含 introduction 和 experience)
*/
export function getVisibleTabConfigs(page_details, tab_configs = TAB_CONFIGS) {
return tab_configs.filter(config => page_details[config.content_key])
}