feat(activity): connect dynamic category navigation to activity create and list views
这个提交包含在:
@@ -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;
|
||||
});
|
||||
|
||||
在新工单中引用
屏蔽一个用户