246 lines
7.6 KiB
Vue
246 lines
7.6 KiB
Vue
<script setup>
|
|
import { ref, watch, onMounted } from "vue";
|
|
import { useRoute } from "vue-router";
|
|
import { contentApi } from "../api/content";
|
|
import { tenantPath } from "../utils/tenant";
|
|
|
|
const route = useRoute();
|
|
const tenantRoute = (path) => tenantPath(path, route);
|
|
const selectedGenre = ref("全部");
|
|
const selectedPrice = ref("all");
|
|
const sort = ref("latest");
|
|
const keyword = ref("");
|
|
const contents = ref([]);
|
|
const page = ref(1);
|
|
|
|
const fetchContents = async (append = false) => {
|
|
const params = {
|
|
page: page.value,
|
|
limit: 12,
|
|
sort: sort.value,
|
|
price_type: selectedPrice.value,
|
|
};
|
|
if (selectedGenre.value !== "全部") params.genre = selectedGenre.value;
|
|
if (keyword.value) params.keyword = keyword.value;
|
|
|
|
try {
|
|
const res = await contentApi.list(params);
|
|
if (append) {
|
|
contents.value = [...contents.value, ...(res.items || [])];
|
|
} else {
|
|
contents.value = res.items || [];
|
|
}
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
};
|
|
|
|
const loadMore = () => {
|
|
page.value++;
|
|
fetchContents(true);
|
|
};
|
|
|
|
watch([selectedGenre, selectedPrice, sort], () => {
|
|
page.value = 1;
|
|
fetchContents();
|
|
});
|
|
|
|
onMounted(() => {
|
|
fetchContents();
|
|
});
|
|
|
|
const filters = {
|
|
genres: [
|
|
"全部",
|
|
"京剧",
|
|
"昆曲",
|
|
"越剧",
|
|
"黄梅戏",
|
|
"豫剧",
|
|
"评剧",
|
|
"秦腔",
|
|
"河北梆子",
|
|
],
|
|
prices: [
|
|
{ label: "全部", value: "all" },
|
|
{ label: "免费", value: "free" },
|
|
{ label: "付费", value: "paid" },
|
|
{ label: "会员专享", value: "member" },
|
|
],
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div class="mx-auto max-w-screen-xl py-8 min-h-screen">
|
|
<!-- Filter Section -->
|
|
<div class="bg-white rounded-xl shadow-sm border border-slate-100 p-6 mb-8">
|
|
<div class="space-y-6">
|
|
<!-- Genre -->
|
|
<div class="flex items-start gap-4">
|
|
<span
|
|
class="text-sm font-bold text-slate-500 mt-1.5 w-12 flex-shrink-0"
|
|
>曲种:</span
|
|
>
|
|
<div class="flex flex-wrap gap-2">
|
|
<button
|
|
v-for="g in filters.genres"
|
|
:key="g"
|
|
@click="selectedGenre = g"
|
|
class="px-3 py-1.5 rounded-lg text-sm font-medium transition-all"
|
|
:class="
|
|
selectedGenre === g
|
|
? 'bg-slate-900 text-white shadow-sm'
|
|
: 'text-slate-600 hover:bg-slate-100'
|
|
"
|
|
>
|
|
{{ g }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Price -->
|
|
<div class="flex items-start gap-4">
|
|
<span
|
|
class="text-sm font-bold text-slate-500 mt-1.5 w-12 flex-shrink-0"
|
|
>价格:</span
|
|
>
|
|
<div class="flex flex-wrap gap-2">
|
|
<button
|
|
v-for="p in filters.prices"
|
|
:key="p.value"
|
|
@click="selectedPrice = p.value"
|
|
class="px-3 py-1.5 rounded-lg text-sm font-medium transition-all"
|
|
:class="
|
|
selectedPrice === p.value
|
|
? 'bg-slate-900 text-white shadow-sm'
|
|
: 'text-slate-600 hover:bg-slate-100'
|
|
"
|
|
>
|
|
{{ p.label }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Sort & Search -->
|
|
<div
|
|
class="border-t border-slate-100 mt-6 pt-4 flex flex-col sm:flex-row justify-between items-center gap-4"
|
|
>
|
|
<div class="flex gap-4 text-sm font-medium text-slate-500">
|
|
<button
|
|
@click="sort = 'latest'"
|
|
:class="{ 'text-primary-600 font-bold': sort === 'latest' }"
|
|
class="hover:text-slate-900 transition-colors cursor-pointer"
|
|
>
|
|
最新发布
|
|
</button>
|
|
<button
|
|
@click="sort = 'hot'"
|
|
:class="{ 'text-primary-600 font-bold': sort === 'hot' }"
|
|
class="hover:text-slate-900 transition-colors cursor-pointer"
|
|
>
|
|
最多播放
|
|
</button>
|
|
<button
|
|
@click="sort = 'price_asc'"
|
|
:class="{ 'text-primary-600 font-bold': sort === 'price_asc' }"
|
|
class="hover:text-slate-900 transition-colors cursor-pointer"
|
|
>
|
|
价格最低
|
|
</button>
|
|
</div>
|
|
<div class="relative w-full sm:w-64">
|
|
<i
|
|
class="pi pi-search absolute left-3 top-1/2 -translate-y-1/2 text-slate-400"
|
|
></i>
|
|
<input
|
|
v-model="keyword"
|
|
@keyup.enter="fetchContents"
|
|
type="text"
|
|
placeholder="在结果中搜索..."
|
|
class="w-full h-9 pl-9 pr-4 rounded-lg bg-slate-50 border border-slate-200 text-sm focus:bg-white focus:border-primary-500 outline-none transition-all"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Content Grid -->
|
|
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
|
|
<div
|
|
v-for="item in contents"
|
|
:key="item.id"
|
|
class="bg-white rounded-xl border border-slate-100 overflow-hidden hover:shadow-lg transition-all group cursor-pointer active:scale-[0.99]"
|
|
@click="$router.push(tenantRoute(`/contents/${item.id}`))"
|
|
>
|
|
<!-- Cover -->
|
|
<div class="aspect-video bg-slate-100 relative">
|
|
<img
|
|
:src="
|
|
item.cover ||
|
|
`https://images.unsplash.com/photo-1514306191717-452ec28c7f31?ixlib=rb-1.2.1&auto=format&fit=crop&w=400&q=60`
|
|
"
|
|
class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105"
|
|
/>
|
|
<div
|
|
class="absolute bottom-2 left-2 px-1.5 py-0.5 bg-black/60 text-white text-xs rounded flex items-center gap-1"
|
|
>
|
|
<i class="pi pi-play-circle"></i> {{ item.duration || "00:00" }}
|
|
</div>
|
|
<span
|
|
v-if="item.price === 0"
|
|
class="absolute top-2 right-2 px-1.5 py-0.5 bg-green-500 text-white text-xs font-bold rounded"
|
|
>免费</span
|
|
>
|
|
</div>
|
|
|
|
<!-- Info -->
|
|
<div class="p-4">
|
|
<div class="flex items-center gap-2 mb-2">
|
|
<span
|
|
class="text-xs text-slate-500 border border-slate-200 px-1 rounded"
|
|
>[{{ item.genre || "未知" }}]</span
|
|
>
|
|
<h3
|
|
class="font-bold text-slate-900 line-clamp-1 group-hover:text-primary-600 transition-colors"
|
|
>
|
|
{{ item.title }}
|
|
</h3>
|
|
</div>
|
|
<div class="flex items-center gap-2 text-xs text-slate-500 mb-3">
|
|
<img
|
|
:src="
|
|
item.author_avatar ||
|
|
'https://api.dicebear.com/7.x/avataaars/svg?seed=' +
|
|
item.author_id
|
|
"
|
|
class="w-5 h-5 rounded-full"
|
|
/>
|
|
<span>{{ item.author_name || "Unknown" }}</span>
|
|
</div>
|
|
<div class="flex items-center justify-between">
|
|
<span class="text-xs text-slate-400">{{ item.views }} 阅读</span>
|
|
<span
|
|
v-if="item.price === 0"
|
|
class="text-sm font-bold text-green-600"
|
|
>免费</span
|
|
>
|
|
<span v-else class="text-sm font-bold text-red-600"
|
|
>¥ {{ item.price }}</span
|
|
>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Pagination -->
|
|
<div class="mt-12 flex justify-center">
|
|
<button
|
|
@click="loadMore"
|
|
class="px-8 py-3 bg-white border border-slate-200 rounded-full text-slate-600 hover:bg-slate-50 hover:text-primary-600 font-medium transition-all shadow-sm cursor-pointer active:scale-95"
|
|
>
|
|
加载更多
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</template>
|