feat: improve tenant public page filters
This commit is contained in:
@@ -222,7 +222,7 @@
|
|||||||
**测试方案**
|
**测试方案**
|
||||||
- 对比查询次数/耗时(可选) + 数据正确性。
|
- 对比查询次数/耗时(可选) + 数据正确性。
|
||||||
|
|
||||||
### 14) 租户公开页完善(Portal)
|
### 14) 租户公开页完善(Portal)(已完成)
|
||||||
**需求目标**
|
**需求目标**
|
||||||
- 完善租户主页信息与内容聚合体验。
|
- 完善租户主页信息与内容聚合体验。
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { useToast } from "primevue/usetoast";
|
import { useToast } from "primevue/usetoast";
|
||||||
import { ref, onMounted } from "vue";
|
import { computed, onMounted, ref, watch } from "vue";
|
||||||
import { useRoute } from "vue-router";
|
import { useRoute } from "vue-router";
|
||||||
import { tenantApi } from "../../api/tenant";
|
import { tenantApi } from "../../api/tenant";
|
||||||
import { contentApi } from "../../api/content";
|
import { contentApi } from "../../api/content";
|
||||||
@@ -14,6 +14,7 @@ const isFollowing = ref(false);
|
|||||||
const tenant = ref({});
|
const tenant = ref({});
|
||||||
const contents = ref([]);
|
const contents = ref([]);
|
||||||
const featuredContent = ref(null);
|
const featuredContent = ref(null);
|
||||||
|
const topics = ref([]);
|
||||||
|
|
||||||
// New States
|
// New States
|
||||||
const loading = ref(true);
|
const loading = ref(true);
|
||||||
@@ -21,6 +22,8 @@ const followLoading = ref(false);
|
|||||||
const searchKeyword = ref("");
|
const searchKeyword = ref("");
|
||||||
const page = ref(1);
|
const page = ref(1);
|
||||||
const hasMore = ref(false);
|
const hasMore = ref(false);
|
||||||
|
const sortOption = ref("latest");
|
||||||
|
const selectedTopic = ref("");
|
||||||
const limit = 10;
|
const limit = 10;
|
||||||
|
|
||||||
const fetchData = async (isLoadMore = false) => {
|
const fetchData = async (isLoadMore = false) => {
|
||||||
@@ -29,18 +32,27 @@ const fetchData = async (isLoadMore = false) => {
|
|||||||
const id = route.params.id;
|
const id = route.params.id;
|
||||||
const query = {
|
const query = {
|
||||||
tenant_id: id,
|
tenant_id: id,
|
||||||
sort: "latest",
|
sort: sortOption.value,
|
||||||
page: page.value,
|
page: page.value,
|
||||||
limit: limit,
|
limit: limit,
|
||||||
keyword: searchKeyword.value,
|
keyword: searchKeyword.value,
|
||||||
};
|
};
|
||||||
|
if (selectedTopic.value) {
|
||||||
|
query.genre = selectedTopic.value;
|
||||||
|
}
|
||||||
|
|
||||||
const reqs = [contentApi.list(query)];
|
const reqs = [contentApi.list(query)];
|
||||||
|
|
||||||
// Only fetch tenant info & featured on first load
|
// Only fetch tenant info & featured on first load
|
||||||
if (!isLoadMore && page.value === 1) {
|
if (!isLoadMore && page.value === 1) {
|
||||||
reqs.push(tenantApi.get(id));
|
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);
|
const results = await Promise.all(reqs);
|
||||||
@@ -81,12 +93,25 @@ const fetchData = async (isLoadMore = false) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => fetchData());
|
onMounted(() => fetchData());
|
||||||
|
onMounted(async () => {
|
||||||
|
try {
|
||||||
|
topics.value = (await contentApi.listTopics()) || [];
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const handleSearch = () => {
|
const handleSearch = () => {
|
||||||
page.value = 1;
|
page.value = 1;
|
||||||
fetchData();
|
fetchData();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const applyFilters = () => {
|
||||||
|
page.value = 1;
|
||||||
|
contents.value = [];
|
||||||
|
fetchData();
|
||||||
|
};
|
||||||
|
|
||||||
const loadMore = () => {
|
const loadMore = () => {
|
||||||
page.value++;
|
page.value++;
|
||||||
fetchData(true);
|
fetchData(true);
|
||||||
@@ -126,6 +151,14 @@ const tabs = [
|
|||||||
{ label: "主页", value: "home" },
|
{ label: "主页", value: "home" },
|
||||||
{ label: "关于", value: "about" },
|
{ label: "关于", value: "about" },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const sortLabel = computed(() =>
|
||||||
|
sortOption.value === "hot" ? "最热" : "最新",
|
||||||
|
);
|
||||||
|
|
||||||
|
watch([sortOption, selectedTopic], () => {
|
||||||
|
applyFilters();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -300,7 +333,7 @@ const tabs = [
|
|||||||
></i>
|
></i>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="搜素频道内容"
|
placeholder="搜索频道内容"
|
||||||
v-model="searchKeyword"
|
v-model="searchKeyword"
|
||||||
@keyup.enter="handleSearch"
|
@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"
|
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 -->
|
<!-- Latest -->
|
||||||
<div>
|
<div>
|
||||||
<h3
|
<div class="flex flex-wrap items-center gap-4 mb-6">
|
||||||
class="text-xl font-bold text-slate-900 mb-6 pl-4 border-l-4 border-primary-600"
|
<h3
|
||||||
>
|
class="text-xl font-bold text-slate-900 pl-4 border-l-4 border-primary-600"
|
||||||
最新动态
|
>
|
||||||
</h3>
|
{{ 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 class="grid grid-cols-1 gap-6">
|
||||||
<div
|
<div
|
||||||
v-for="item in contents"
|
v-for="item in contents"
|
||||||
@@ -430,14 +515,33 @@ const tabs = [
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 4. About Tab -->
|
<!-- 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
|
<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 class="text-2xl font-bold text-slate-900 mb-4">认证信息</h2>
|
||||||
关于我们
|
<p class="text-slate-700 leading-relaxed">
|
||||||
</h2>
|
{{
|
||||||
<p>{{ tenant.description || "暂无详细介绍" }}</p>
|
tenant.certType === "personal"
|
||||||
|
? "个人认证"
|
||||||
|
: tenant.certType === "enterprise"
|
||||||
|
? "企业认证"
|
||||||
|
: tenant.certType
|
||||||
|
}}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user