feat: use community categories for topic navigation

This commit is contained in:
hajimi
2026-08-02 18:44:35 +08:00
parent 75b003619b
commit 3c2dddd6d1
3 changed files with 73 additions and 35 deletions
@@ -7,6 +7,7 @@ use app\common\model\community\CommunityPost;
use app\common\model\community\CommunityComment; use app\common\model\community\CommunityComment;
use app\common\model\community\CommunityLike; use app\common\model\community\CommunityLike;
use app\common\model\community\CommunityFollow; use app\common\model\community\CommunityFollow;
use app\common\model\community\CommunityCategory;
use app\common\model\community\CommunityTag; use app\common\model\community\CommunityTag;
use app\common\model\user\User; use app\common\model\user\User;
use app\common\model\ai\AiAnalysis; use app\common\model\ai\AiAnalysis;
@@ -24,7 +25,7 @@ class CommunityController extends BaseApiController
{ {
private const LIUHE_TAGS = ['旧澳六合', '新澳六合']; private const LIUHE_TAGS = ['旧澳六合', '新澳六合'];
public array $notNeedLogin = ['postLists', 'postDetail', 'commentLists', 'tagLists', 'userProfile', 'followList', 'fansList']; public array $notNeedLogin = ['postLists', 'postDetail', 'commentLists', 'tagLists', 'categoryLists', 'userProfile', 'followList', 'fansList'];
// 帖子列表 // 帖子列表
public function postLists() public function postLists()
@@ -423,6 +424,18 @@ class CommunityController extends BaseApiController
return $this->data($list); return $this->data($list);
} }
// 分类列表
public function categoryLists()
{
$list = CommunityCategory::where('status', 1)
->order('sort desc')
->order('id desc')
->field('id,name,icon,post_count')
->select()
->toArray();
return $this->data($list);
}
// 购买帖子 // 购买帖子
// confirm=0 探测是否已购买;confirm=1 确认扣分购买 // confirm=0 探测是否已购买;confirm=1 确认扣分购买
public function purchasePost() public function purchasePost()
+5
View File
@@ -57,6 +57,11 @@ export function getTagLists() {
return request.get({ url: '/community/tagLists' }) return request.get({ url: '/community/tagLists' })
} }
// 社区分类列表
export function getCommunityCategoryLists() {
return request.get({ url: '/community/categoryLists' })
}
// 社区用户统计(积分、关注等) // 社区用户统计(积分、关注等)
export function getCommunityUserStats() { export function getCommunityUserStats() {
return request.get({ url: '/community/userStats' }) return request.get({ url: '/community/userStats' })
+54 -34
View File
@@ -31,11 +31,12 @@
<view class="community-topics"> <view class="community-topics">
<scroll-view scroll-x class="community-topics__scroll" :show-scrollbar="false"> <scroll-view scroll-x class="community-topics__scroll" :show-scrollbar="false">
<view class="community-topics__list"> <view class="community-topics__list">
<view v-for="tag in displayTags" :key="tag.id" class="community-topic" <view v-for="category in displayCategories" :key="category.id" class="community-topic"
:class="{ 'community-topic--active': activeTagId === tag.id }" @tap="selectTag(tag)"> :class="{ 'community-topic--active': activeCategoryId === category.id }"
<image class="community-topic__icon-image" :src="tag.icon || DEFAULT_TOPIC_ICON" mode="aspectFill" @tap="selectCategory(category)">
@error="handleTopicIconError(tag)" /> <image class="community-topic__icon-image" :src="category.icon || DEFAULT_TOPIC_ICON"
<text>{{ tag.name }}</text> mode="aspectFill" @error="handleTopicIconError(category)" />
<text>{{ category.name }}</text>
</view> </view>
</view> </view>
</scroll-view> </scroll-view>
@@ -47,7 +48,7 @@
<text class="community-list__eyebrow">SPORTS COMMUNITY</text> <text class="community-list__eyebrow">SPORTS COMMUNITY</text>
<text class="community-list__title">{{ feedTitle }}</text> <text class="community-list__title">{{ feedTitle }}</text>
</view> </view>
<text v-if="activeTagName" class="community-list__selected"># {{ activeTagName }}</text> <text v-if="activeCategoryName" class="community-list__selected">{{ activeCategoryName }}</text>
</view> </view>
<view v-if="loading && postList.length === 0" class="community-loading"> <view v-if="loading && postList.length === 0" class="community-loading">
@@ -83,7 +84,13 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, onMounted, ref } from 'vue' import { computed, onMounted, ref } from 'vue'
import { onShow } from '@dcloudio/uni-app' import { onShow } from '@dcloudio/uni-app'
import { getCommunityPosts, getCommunityUserStats, getTagLists, purchasePost, toggleLike } from '@/api/community' import {
getCommunityCategoryLists,
getCommunityPosts,
getCommunityUserStats,
purchasePost,
toggleLike
} from '@/api/community'
import { useUserStore } from '@/stores/user' import { useUserStore } from '@/stores/user'
import { getToken } from '@/utils/auth' import { getToken } from '@/utils/auth'
import AiAssistantEntry from '@/components/ai-assistant-entry/ai-assistant-entry.vue' import AiAssistantEntry from '@/components/ai-assistant-entry/ai-assistant-entry.vue'
@@ -92,11 +99,12 @@ import PostCard from '@/packages_community/components/post-card.vue'
type FeedKey = 'recommend' | 'follow' | 'hot' | 'topic' type FeedKey = 'recommend' | 'follow' | 'hot' | 'topic'
const userStore = useUserStore() const userStore = useUserStore()
const COMMUNITY_ACTIVE_TAG_KEY = 'community_active_tag' const COMMUNITY_ACTIVE_CATEGORY_KEY = 'community_active_category'
const LEGACY_COMMUNITY_ACTIVE_TAG_KEY = 'community_active_tag'
const statusBarHeight = ref(0) const statusBarHeight = ref(0)
const activeFeed = ref<FeedKey>('recommend') const activeFeed = ref<FeedKey>('recommend')
const activeTagId = ref(0) const activeCategoryId = ref(0)
const tagList = ref<any[]>([]) const categoryList = ref<any[]>([])
const postList = ref<any[]>([]) const postList = ref<any[]>([])
const userPoints = ref<number | null>(null) const userPoints = ref<number | null>(null)
const loading = ref(false) const loading = ref(false)
@@ -118,22 +126,26 @@ const feedTabs: Array<{ key: FeedKey; label: string }> = [
const isLogin = computed(() => userStore.isLogin) const isLogin = computed(() => userStore.isLogin)
const userPointsText = computed(() => (isLogin.value ? String(userPoints.value ?? 0) : '登录')) const userPointsText = computed(() => (isLogin.value ? String(userPoints.value ?? 0) : '登录'))
const feedTitle = computed(() => ({ recommend: '精选讨论', follow: '关注动态', hot: '热门话题', topic: '话题广场' }[activeFeed.value])) const feedTitle = computed(() => ({ recommend: '精选讨论', follow: '关注动态', hot: '热门话题', topic: '话题广场' }[activeFeed.value]))
const activeTagName = computed(() => tagList.value.find((tag) => tag.id === activeTagId.value)?.name || '') const activeCategoryName = computed(() => categoryList.value.find(
(category) => category.id === activeCategoryId.value
)?.name || '')
const displayTags = computed(() => { const displayCategories = computed(() => {
return tagList.value.map((tag) => ({ return categoryList.value.map((category) => ({
...tag, ...category,
icon: typeof tag.icon === 'string' ? tag.icon.trim() : '' icon: typeof category.icon === 'string' ? category.icon.trim() : ''
})) }))
}) })
const handleTopicIconError = (tag: any) => { const handleTopicIconError = (category: any) => {
tag.icon = DEFAULT_TOPIC_ICON category.icon = DEFAULT_TOPIC_ICON
} }
const getSavedActiveTag = () => { const getSavedActiveCategory = () => {
const tagId = Number(uni.getStorageSync(COMMUNITY_ACTIVE_TAG_KEY)) const savedValue = uni.getStorageSync(COMMUNITY_ACTIVE_CATEGORY_KEY)
return Number.isFinite(tagId) && tagId > 0 ? tagId : 0 || uni.getStorageSync(LEGACY_COMMUNITY_ACTIVE_TAG_KEY)
const categoryId = Number(savedValue)
return Number.isFinite(categoryId) && categoryId > 0 ? categoryId : 0
} }
const goSearch = () => uni.navigateTo({ url: '/pages/search/search' }) const goSearch = () => uni.navigateTo({ url: '/pages/search/search' })
@@ -159,14 +171,22 @@ const loadUserPoints = async () => {
} }
} }
const fetchTags = async () => { const fetchCategories = async () => {
try { try {
tagList.value = (await getTagLists()) || [] categoryList.value = (await getCommunityCategoryLists()) || []
if (activeTagId.value && !tagList.value.some((tag) => tag.id === activeTagId.value)) { if (activeCategoryId.value && !categoryList.value.some(
activeTagId.value = 0 (category) => category.id === activeCategoryId.value
)) {
activeCategoryId.value = 0
uni.removeStorageSync(COMMUNITY_ACTIVE_CATEGORY_KEY)
uni.removeStorageSync(LEGACY_COMMUNITY_ACTIVE_TAG_KEY)
if (activeFeed.value === 'topic') fetchPosts(true)
} else if (activeCategoryId.value) {
uni.setStorageSync(COMMUNITY_ACTIVE_CATEGORY_KEY, activeCategoryId.value)
uni.removeStorageSync(LEGACY_COMMUNITY_ACTIVE_TAG_KEY)
} }
} catch (_error) { } catch (_error) {
tagList.value = [] categoryList.value = []
} }
} }
@@ -196,7 +216,7 @@ const fetchPosts = async (reset = false) => {
if (activeFeed.value === 'follow') params.follow = 1 if (activeFeed.value === 'follow') params.follow = 1
if (activeFeed.value === 'hot') params.is_hot = 1 if (activeFeed.value === 'hot') params.is_hot = 1
if (activeFeed.value === 'topic') params.is_topic = 1 if (activeFeed.value === 'topic') params.is_topic = 1
if (activeTagId.value > 0) params.tag_id = activeTagId.value if (activeCategoryId.value > 0) params.category_id = activeCategoryId.value
const response = await getCommunityPosts(params) const response = await getCommunityPosts(params)
const rawList = response?.lists || [] const rawList = response?.lists || []
if (requestId !== fetchRequestId.value) return if (requestId !== fetchRequestId.value) return
@@ -226,16 +246,16 @@ const switchFeed = (feed: FeedKey) => {
}) })
return return
} }
if (activeFeed.value === feed && (feed !== 'topic' || activeTagId.value === 0)) return if (activeFeed.value === feed && (feed !== 'topic' || activeCategoryId.value === 0)) return
activeFeed.value = feed activeFeed.value = feed
if (feed !== 'topic') activeTagId.value = 0 if (feed !== 'topic') activeCategoryId.value = 0
fetchPosts(true) fetchPosts(true)
} }
const selectTag = (tag: any) => { const selectCategory = (category: any) => {
activeFeed.value = 'topic' activeFeed.value = 'topic'
activeTagId.value = tag.id activeCategoryId.value = category.id
uni.setStorageSync(COMMUNITY_ACTIVE_TAG_KEY, tag.id) uni.setStorageSync(COMMUNITY_ACTIVE_CATEGORY_KEY, category.id)
fetchPosts(true) fetchPosts(true)
} }
@@ -328,9 +348,9 @@ onMounted(() => {
// #ifdef APP-PLUS || MP // #ifdef APP-PLUS || MP
statusBarHeight.value = systemInfo.statusBarHeight || 44 statusBarHeight.value = systemInfo.statusBarHeight || 44
// #endif // #endif
activeTagId.value = getSavedActiveTag() activeCategoryId.value = getSavedActiveCategory()
if (activeTagId.value) activeFeed.value = 'topic' if (activeCategoryId.value) activeFeed.value = 'topic'
fetchTags() fetchCategories()
fetchPosts(true) fetchPosts(true)
loadUserPoints() loadUserPoints()
}) })