feat: improve tenant public page filters
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user