feat: improve tenant public page filters

This commit is contained in:
2026-01-17 20:50:38 +08:00
parent 7fca7a40e7
commit 1419bd9bd0
2 changed files with 120 additions and 16 deletions

View File

@@ -1,6 +1,6 @@
<script setup>
import { useToast } from "primevue/usetoast";
import { ref, onMounted } from "vue";
import { computed, onMounted, ref, watch } from "vue";
import { useRoute } from "vue-router";
import { tenantApi } from "../../api/tenant";
import { contentApi } from "../../api/content";
@@ -14,6 +14,7 @@ const isFollowing = ref(false);
const tenant = ref({});
const contents = ref([]);
const featuredContent = ref(null);
const topics = ref([]);
// New States
const loading = ref(true);
@@ -21,6 +22,8 @@ const followLoading = ref(false);
const searchKeyword = ref("");
const page = ref(1);
const hasMore = ref(false);
const sortOption = ref("latest");
const selectedTopic = ref("");
const limit = 10;
const fetchData = async (isLoadMore = false) => {
@@ -29,18 +32,27 @@ const fetchData = async (isLoadMore = false) => {
const id = route.params.id;
const query = {
tenant_id: id,
sort: "latest",
sort: sortOption.value,
page: page.value,
limit: limit,
keyword: searchKeyword.value,
};
if (selectedTopic.value) {
query.genre = selectedTopic.value;
}
const reqs = [contentApi.list(query)];
// Only fetch tenant info & featured on first load
if (!isLoadMore && page.value === 1) {
reqs.push(tenantApi.get(id));
reqs.push(contentApi.list({ tenant_id: id, is_pinned: true }));
reqs.push(
contentApi.list({
tenant_id: id,
is_pinned: true,
genre: selectedTopic.value || "",
}),
);
}
const results = await Promise.all(reqs);
@@ -81,12 +93,25 @@ const fetchData = async (isLoadMore = false) => {
};
onMounted(() => fetchData());
onMounted(async () => {
try {
topics.value = (await contentApi.listTopics()) || [];
} catch (e) {
console.error(e);
}
});
const handleSearch = () => {
page.value = 1;
fetchData();
};
const applyFilters = () => {
page.value = 1;
contents.value = [];
fetchData();
};
const loadMore = () => {
page.value++;
fetchData(true);
@@ -126,6 +151,14 @@ const tabs = [
{ label: "主页", value: "home" },
{ label: "关于", value: "about" },
];
const sortLabel = computed(() =>
sortOption.value === "hot" ? "最热" : "最新",
);
watch([sortOption, selectedTopic], () => {
applyFilters();
});
</script>
<template>
@@ -300,7 +333,7 @@ const tabs = [
></i>
<input
type="text"
placeholder="搜频道内容"
placeholder="搜频道内容"
v-model="searchKeyword"
@keyup.enter="handleSearch"
class="h-9 pl-9 pr-4 rounded-full bg-slate-100 border-none text-sm focus:bg-white focus:ring-2 focus:ring-primary-100 transition-all w-48 focus:w-64"
@@ -360,11 +393,63 @@ const tabs = [
<!-- Latest -->
<div>
<h3
class="text-xl font-bold text-slate-900 mb-6 pl-4 border-l-4 border-primary-600"
>
最新动态
</h3>
<div class="flex flex-wrap items-center gap-4 mb-6">
<h3
class="text-xl font-bold text-slate-900 pl-4 border-l-4 border-primary-600"
>
{{ sortLabel }}动态
</h3>
<div class="flex items-center gap-2">
<button
class="px-4 py-2 rounded-full text-sm font-semibold transition-colors"
:class="
sortOption === 'latest'
? 'bg-slate-900 text-white'
: 'bg-slate-100 text-slate-600 hover:bg-slate-200'
"
@click="sortOption = 'latest'"
>
最新
</button>
<button
class="px-4 py-2 rounded-full text-sm font-semibold transition-colors"
:class="
sortOption === 'hot'
? 'bg-slate-900 text-white'
: 'bg-slate-100 text-slate-600 hover:bg-slate-200'
"
@click="sortOption = 'hot'"
>
最热
</button>
</div>
<div class="flex items-center gap-2 flex-wrap">
<button
class="px-3 py-1 rounded-full text-xs font-semibold transition-colors"
:class="
selectedTopic === ''
? 'bg-primary-600 text-white'
: 'bg-slate-100 text-slate-600 hover:bg-slate-200'
"
@click="selectedTopic = ''"
>
全部专题
</button>
<button
v-for="topic in topics"
:key="topic.title"
class="px-3 py-1 rounded-full text-xs font-semibold transition-colors"
:class="
selectedTopic === topic.title
? 'bg-primary-600 text-white'
: 'bg-slate-100 text-slate-600 hover:bg-slate-200'
"
@click="selectedTopic = topic.title"
>
{{ topic.title }} ({{ topic.count }})
</button>
</div>
</div>
<div class="grid grid-cols-1 gap-6">
<div
v-for="item in contents"
@@ -430,14 +515,33 @@ const tabs = [
</div>
<!-- 4. About Tab -->
<div v-if="currentTab === 'about'" class="max-w-3xl mx-auto">
<div v-if="currentTab === 'about'" class="max-w-3xl mx-auto space-y-8">
<div class="bg-white rounded-2xl border border-slate-100 shadow-sm p-8">
<h2 class="text-2xl font-bold text-slate-900 mb-4">简介</h2>
<p class="text-slate-700 leading-relaxed">
{{ tenant.bio || "暂无简介" }}
</p>
</div>
<div class="bg-white rounded-2xl border border-slate-100 shadow-sm p-8">
<h2 class="text-2xl font-bold text-slate-900 mb-4">详细介绍</h2>
<p class="text-slate-700 leading-relaxed whitespace-pre-wrap">
{{ tenant.description || "暂无详细介绍" }}
</p>
</div>
<div
class="prose prose-slate prose-lg text-slate-700 whitespace-pre-wrap"
v-if="tenant.certType"
class="bg-white rounded-2xl border border-slate-100 shadow-sm p-8"
>
<h2 class="font-bold text-2xl mb-6 pb-4 border-b border-slate-100">
关于我们
</h2>
<p>{{ tenant.description || "暂无详细介绍" }}</p>
<h2 class="text-2xl font-bold text-slate-900 mb-4">认证信息</h2>
<p class="text-slate-700 leading-relaxed">
{{
tenant.certType === "personal"
? "个人认证"
: tenant.certType === "enterprise"
? "企业认证"
: tenant.certType
}}
</p>
</div>
</div>
</div>