feat(activity): connect dynamic category navigation to activity create and list views

这个提交包含在:
xcbyy
2026-10-10 09:57:04 +08:00
父节点 e36e80c399
当前提交 82ba59f373
共有 2 个文件被更改,包括 86 次插入 和 35 次删除

查看文件

@@ -288,10 +288,10 @@
import { ref, computed, onMounted, onUnmounted } from 'vue';
import { onShow } from '@dcloudio/uni-app';
import { ICONS } from '@/common/icons.js';
import { createActivity, getSafetyPlaces } from '@/common/api.js';
import { createActivity, getSafetyPlaces, getNavCategories } from '@/common/api.js';
import { getCurrentThemeKey, applyThemeToNativeUI } from '@/common/theme.js';
const games = ['美食陪吃', '同城陪唱', '下午茶陪喝', '同城微醺', '同城桌游', '热门电竞'];
const games = ref(['美食陪吃', '同城陪唱', '下午茶陪喝', '同城微醺', '同城桌游', '热门电竞']);
const currentThemeKey = ref(getCurrentThemeKey());
const onThemeChanged = (key) => {
@@ -351,14 +351,27 @@ const recommendPublicZones = ref([
const loadLivePlaces = async () => {
try {
const list = await getSafetyPlaces();
if (Array.isArray(list) && list.length > 0) {
recommendPublicZones.value = list
const [placesList, catList] = await Promise.allSettled([
getSafetyPlaces(),
getNavCategories()
]);
if (placesList.status === 'fulfilled' && Array.isArray(placesList.value) && placesList.value.length > 0) {
recommendPublicZones.value = placesList.value
.filter((p) => p.status === '正常营业')
.map((p) => `${p.area} · ${p.name}`);
}
if (catList.status === 'fulfilled' && Array.isArray(catList.value) && catList.value.length > 0) {
const activeCats = catList.value
.filter((c) => c.status === 1 && c.name && c.name !== '全部活动')
.map((c) => c.name);
if (activeCats.length > 0) {
games.value = activeCats;
}
}
} catch (e) {
console.warn('[ActivityCreate] 加载合规场所异常降级:', e);
console.warn('[ActivityCreate] 加载合规场所与品类异常降级:', e);
}
};

查看文件

@@ -61,7 +61,7 @@
</view>
</view>
<!-- 核心品类快捷筛选胶囊栏 (陪吃·陪唱·陪喝) -->
<!-- 核心品类快捷筛选胶囊栏 (动态根据后台金刚区配置渲染) -->
<view class="category-capsule-bar">
<view
class="capsule-item btn-active"
@@ -71,28 +71,14 @@
<text class="capsule-text">全部品类</text>
</view>
<view
v-for="cat in navCapsules"
:key="cat.code"
class="capsule-item btn-active"
:class="{ active: currentCategory === 'MEAL' }"
@click="switchCategory('MEAL')"
:class="{ active: currentCategory === cat.code || currentCategory === cat.catKey }"
@click="switchCategory(cat.code)"
>
<image class="capsule-svg" :src="ICONS.utensils" mode="aspectFit" />
<text class="capsule-text">美食陪吃</text>
</view>
<view
class="capsule-item btn-active"
:class="{ active: currentCategory === 'SING' }"
@click="switchCategory('SING')"
>
<image class="capsule-svg" :src="ICONS.micVocal" mode="aspectFit" />
<text class="capsule-text">同城陪唱</text>
</view>
<view
class="capsule-item btn-active"
:class="{ active: currentCategory === 'DRINK' }"
@click="switchCategory('DRINK')"
>
<image class="capsule-svg" :src="ICONS.coffee" mode="aspectFit" />
<text class="capsule-text">下午茶陪喝</text>
<image class="capsule-svg" :src="cat.iconSvg" mode="aspectFit" v-if="cat.iconSvg" />
<text class="capsule-text">{{ cat.name }}</text>
</view>
</view>
@@ -190,7 +176,7 @@ import { ref, computed, onMounted, onUnmounted } from 'vue';
import { onShow, onLoad } from '@dcloudio/uni-app';
import CustomTabBar from '@/components/CustomTabBar.vue';
import SkeletonScreen from '@/components/SkeletonScreen.vue';
import { getActivities } from '@/common/api.js';
import { getActivities, getNavCategories } from '@/common/api.js';
import { ICONS } from '@/common/icons.js';
import { getCurrentThemeKey, applyThemeToNativeUI } from '@/common/theme.js';
import { getNavBarInfo } from '@/common/nav.js';
@@ -201,6 +187,7 @@ const isLoading = ref(true);
const activities = ref([]);
const currentTab = ref('ALL');
const currentCategory = ref('ALL');
const dynamicCategories = ref([]);
const currentThemeKey = ref(getCurrentThemeKey());
const searchKeyword = ref('');
const navInfo = ref(getNavBarInfo());
@@ -238,6 +225,43 @@ const fetchLiveActivities = async () => {
}
};
const fetchLiveCategories = async () => {
try {
const res = await getNavCategories();
if (Array.isArray(res) && res.length > 0) {
dynamicCategories.value = res.filter((c) => c.status === 1 && c.code !== 'more');
}
} catch (e) {
console.warn('[ActivityList] 获取品类导航异常降级:', e);
}
};
const navCapsules = computed(() => {
if (dynamicCategories.value && dynamicCategories.value.length > 0) {
return dynamicCategories.value.map((c) => {
let icon = ICONS.gameSwords;
if (c.code === 'meal') icon = ICONS.utensils;
else if (c.code === 'sing') icon = ICONS.micVocal;
else if (c.code === 'drink' || c.code === 'bar') icon = ICONS.coffee;
else if (c.code === 'esports') icon = ICONS.gamepad;
return {
code: c.code.toUpperCase(),
catKey: c.cat,
name: c.name,
iconSvg: icon
};
});
}
// 默认兜底品类
return [
{ code: 'MEAL', catKey: 'offline_meal', name: '美食陪吃', iconSvg: ICONS.utensils },
{ code: 'SING', catKey: 'offline_sing', name: '同城陪唱', iconSvg: ICONS.micVocal },
{ code: 'DRINK', catKey: 'offline_drink', name: '下午茶陪喝', iconSvg: ICONS.coffee }
];
});
onLoad((options) => {
if (options && options.category) {
currentCategory.value = options.category;
@@ -258,6 +282,7 @@ onMounted(() => {
applyThemeToNativeUI(currentThemeKey.value);
uni.$on('theme-change', onThemeChanged);
loadInitialActivities();
fetchLiveCategories();
});
onShow(() => {
@@ -271,6 +296,7 @@ onShow(() => {
uni.removeStorageSync('ACTIVITY_ACTIVE_TAB');
}
fetchLiveActivities();
fetchLiveCategories();
});
onUnmounted(() => {
@@ -314,14 +340,26 @@ const filteredActivities = computed(() => {
matchTab = a.payType === currentTab.value;
}
// 3. 品类筛选 (美食陪吃 / 同城陪唱 / 下午茶陪喝/微醺)
// 3. 品类筛选 (支持动态匹配后台下发品类)
let matchCat = true;
if (currentCategory.value === 'MEAL') {
matchCat = a.game === '美食陪吃';
} else if (currentCategory.value === 'SING') {
matchCat = a.game === '同城陪唱';
} else if (currentCategory.value === 'DRINK') {
matchCat = a.game === '下午茶陪喝' || a.game === '同城微醺';
if (currentCategory.value && currentCategory.value !== 'ALL') {
const targetCatCode = currentCategory.value.toUpperCase();
if (targetCatCode === 'MEAL') {
matchCat = a.game === '美食陪吃' || (a.gameCategory || '').includes('MEAL');
} else if (targetCatCode === 'SING') {
matchCat = a.game === '同城陪唱' || (a.gameCategory || '').includes('SING');
} else if (targetCatCode === 'DRINK' || targetCatCode === 'BAR') {
matchCat = a.game === '下午茶陪喝' || a.game === '同城微醺' || (a.gameCategory || '').includes('DRINK');
} else if (targetCatCode === 'ESPORTS') {
matchCat = a.game.includes('电竞') || a.game.includes('游戏') || (a.gameCategory || '').includes('ESPORTS');
} else {
const found = navCapsules.value.find((c) => c.code === targetCatCode);
if (found) {
matchCat = a.game.includes(found.name) || found.name.includes(a.game);
} else {
matchCat = a.game.includes(currentCategory.value);
}
}
}
return matchKeyword && matchTab && matchCat;
});