import { useToast } from "primevue/usetoast";
import { computed, onMounted, ref, watch } from "vue";
-import { useRoute } from "vue-router";
-import { tenantApi } from "../../api/tenant";
+import { useRoute, useRouter } from "vue-router";
import { contentApi } from "../../api/content";
+import { tenantApi } from "../../api/tenant";
import { tenantPath } from "../../utils/tenant";
const route = useRoute();
+const router = useRouter();
const tenantRoute = (path) => tenantPath(path, route);
const toast = useToast();
+
+const tenantID = ref(0);
+
+const resolveTenantIDFromRoute = () => {
+ const raw = route.params.id;
+ const parsed = Number(raw);
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : 0;
+};
+
+const showFollowActions = computed(() => tenantID.value > 0);
const currentTab = ref("home");
const isFollowing = ref(false);
const tenant = ref({});
@@ -25,46 +36,57 @@ const hasMore = ref(false);
const sortOption = ref("latest");
const selectedTopic = ref("");
const limit = 10;
+const querySyncReady = ref(false);
+
+const normalizeGenreQuery = (value) => {
+ if (typeof value === "string") {
+ return value;
+ }
+ if (Array.isArray(value) && value.length > 0) {
+ return typeof value[0] === "string" ? value[0] : "";
+ }
+ return "";
+};
const fetchData = async (isLoadMore = false) => {
if (!isLoadMore) loading.value = true;
try {
- const id = route.params.id;
+ const effectiveTenantID = tenantID.value || resolveTenantIDFromRoute();
const query = {
- tenant_id: id,
sort: sortOption.value,
page: page.value,
limit: limit,
keyword: searchKeyword.value,
};
+ if (effectiveTenantID > 0) {
+ query.tenant_id = effectiveTenantID;
+ }
if (selectedTopic.value) {
query.genre = selectedTopic.value;
}
const reqs = [contentApi.list(query)];
- // Only fetch tenant info & featured on first load
+ // Only fetch featured on first load
if (!isLoadMore && page.value === 1) {
- reqs.push(tenantApi.get(id));
- reqs.push(
- contentApi.list({
- tenant_id: id,
- is_pinned: true,
- genre: selectedTopic.value || "",
- }),
- );
+ const pinnedQuery = {
+ is_pinned: true,
+ genre: selectedTopic.value || "",
+ };
+ if (effectiveTenantID > 0) {
+ pinnedQuery.tenant_id = effectiveTenantID;
+ }
+
+ reqs.push(contentApi.list(pinnedQuery));
}
const results = await Promise.all(reqs);
const c = results[0]; // Content List
if (!isLoadMore && page.value === 1) {
- const t = results[1];
- const f = results[2];
- tenant.value = t || {};
- isFollowing.value = t?.is_following || false;
+ const f = results[1];
- if (f && f.items && f.items.length > 0) {
+ if (f?.items?.length > 0) {
featuredContent.value = f.items[0];
} else {
featuredContent.value = null;
@@ -92,12 +114,34 @@ const fetchData = async (isLoadMore = false) => {
}
};
-onMounted(() => fetchData());
onMounted(async () => {
+ selectedTopic.value = normalizeGenreQuery(route.query.genre);
+ querySyncReady.value = true;
+
try {
- topics.value = (await contentApi.listTopics()) || [];
+ const topicsRes = await contentApi.listTopics();
+
+ topics.value = topicsRes || [];
+
+ tenantID.value = resolveTenantIDFromRoute();
+ tenant.value = {
+ id: tenantID.value,
+ name: route.params.tenantCode,
+ bio: "",
+ cover: "",
+ avatar: "",
+ stats: {
+ followers: 0,
+ contents: 0,
+ likes: 0,
+ },
+ };
+ isFollowing.value = false;
+
+ fetchData();
} catch (e) {
console.error(e);
+ fetchData();
}
});
@@ -119,13 +163,25 @@ const loadMore = () => {
const toggleFollow = async () => {
if (followLoading.value) return;
+
+ const effectiveTenantID = tenantID.value || resolveTenantIDFromRoute();
+ if (!effectiveTenantID) {
+ toast.add({
+ severity: "warn",
+ summary: "暂不可用",
+ detail: "当前频道暂不支持关注操作",
+ life: 2000,
+ });
+ return;
+ }
+
followLoading.value = true;
try {
if (isFollowing.value) {
- await tenantApi.unfollow(route.params.id);
+ await tenantApi.unfollow(effectiveTenantID);
isFollowing.value = false;
} else {
- await tenantApi.follow(route.params.id);
+ await tenantApi.follow(effectiveTenantID);
isFollowing.value = true;
toast.add({
severity: "success",
@@ -156,9 +212,43 @@ const sortLabel = computed(() =>
sortOption.value === "hot" ? "最热" : "最新",
);
-watch([sortOption, selectedTopic], () => {
- applyFilters();
+const currentTopicBreadcrumb = computed(() => {
+ const fromQuery = normalizeGenreQuery(route.query.genre);
+ if (fromQuery) return fromQuery;
+ if (selectedTopic.value) return selectedTopic.value;
+ return "全部专题";
});
+
+watch(
+ () => route.query.genre,
+ (newGenre) => {
+ const normalized = normalizeGenreQuery(newGenre);
+ if (normalized !== selectedTopic.value) {
+ selectedTopic.value = normalized;
+ }
+ },
+);
+
+watch(
+ [sortOption, selectedTopic],
+ ([nextSort, nextTopic], [prevSort, prevTopic]) => {
+ if (!querySyncReady.value) return;
+
+ if (nextTopic !== prevTopic) {
+ const nextQuery = { ...route.query };
+ if (nextTopic) {
+ nextQuery.genre = nextTopic;
+ } else {
+ delete nextQuery.genre;
+ }
+ router.replace({ query: nextQuery });
+ }
+
+ if (nextSort !== prevSort || nextTopic !== prevTopic) {
+ applyFilters();
+ }
+ },
+);
@@ -229,6 +319,7 @@ watch([sortOption, selectedTopic], () => {