feat(auth): implement OTP login flow with toast notifications
feat(content): enhance detail view with dynamic content and comments feat(order): add polling for payment status in the payment view feat(user): update dashboard to display wallet and recent orders feat(user): improve orders view with dynamic order fetching and status mapping feat(api): create API modules for auth, content, order, user, and common functionalities refactor(request): implement a centralized request utility for API calls
This commit is contained in:
@@ -45,37 +45,37 @@
|
||||
</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 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">
|
||||
<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="i in 12" :key="i" 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(`/contents/${i}`)">
|
||||
<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(`/contents/${item.id}`)">
|
||||
<!-- Cover -->
|
||||
<div class="aspect-video bg-slate-100 relative">
|
||||
<img :src="`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">
|
||||
<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> 12:40
|
||||
<i class="pi pi-play-circle"></i> {{ item.duration || '00:00' }}
|
||||
</div>
|
||||
<span v-if="i % 3 === 0" class="absolute top-2 right-2 px-1.5 py-0.5 bg-green-500 text-white text-xs font-bold rounded">限免</span>
|
||||
<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">[京剧]</span>
|
||||
<h3 class="font-bold text-slate-900 line-clamp-1 group-hover:text-primary-600 transition-colors">《锁麟囊》选段深度解析</h3>
|
||||
<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="https://api.dicebear.com/7.x/avataaars/svg?seed=Master1" class="w-5 h-5 rounded-full">
|
||||
<span>梅派传人小林</span>
|
||||
<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">1.2w 阅读</span>
|
||||
<span v-if="i % 3 === 0" class="text-sm font-bold text-green-600">免费</span>
|
||||
<span v-else class="text-sm font-bold text-red-600">¥ 9.90</span>
|
||||
<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>
|
||||
@@ -83,7 +83,7 @@
|
||||
|
||||
<!-- Pagination -->
|
||||
<div class="mt-12 flex justify-center">
|
||||
<button 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 @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>
|
||||
@@ -91,11 +91,50 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { ref, watch, onMounted } from 'vue';
|
||||
import { contentApi } from '../api/content';
|
||||
|
||||
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
|
||||
};
|
||||
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, sort], () => {
|
||||
page.value = 1;
|
||||
fetchContents();
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
fetchContents();
|
||||
});
|
||||
|
||||
const filters = {
|
||||
genres: ['全部', '京剧', '昆曲', '越剧', '黄梅戏', '豫剧', '评剧', '秦腔', '河北梆子'],
|
||||
|
||||
Reference in New Issue
Block a user