257 lines
11 KiB
Vue
257 lines
11 KiB
Vue
<script setup>
|
||
import { ref, computed, onMounted } from 'vue'
|
||
import AOS from 'aos'
|
||
import 'aos/dist/aos.css'
|
||
|
||
onMounted(() => {
|
||
AOS.init({
|
||
duration: 1200,
|
||
offset: 120,
|
||
easing: 'ease-out-cubic',
|
||
once: false
|
||
})
|
||
})
|
||
|
||
// 模拟项目数据
|
||
const projects = ref(Array.from({ length: 12 }, (_, i) => ({
|
||
id: i + 1,
|
||
title: `项目 ${i + 1}`,
|
||
cover: 'https://pic.rmb.bdstatic.com/bjh/80852bfe7c321988191838517ba64e309354.jpeg@h_1280',
|
||
tags: ['Vue3', 'Node.js', 'TypeScript'].slice(0, Math.floor(Math.random() * 3) + 1),
|
||
category: ['Web应用', '移动端', '小程序', '企业系统'][i % 4],
|
||
author: '开发者' + (i + 1),
|
||
price: (Math.random() * 10000 + 5000).toFixed(2),
|
||
createdAt: new Date(Date.now() - Math.floor(Math.random() * 10000000000)).toISOString(),
|
||
description: '这是一个示例项目描述,包含了项目的主要功能和技术特点。实际项目中会有更详细的描述信息。'
|
||
})))
|
||
|
||
// 筛选排序逻辑
|
||
const selectedCategory = ref('all')
|
||
const sortBy = ref('date')
|
||
const searchQuery = ref('')
|
||
|
||
const categories = [
|
||
{ value: 'all', label: '全部分类' },
|
||
{ value: 'Web应用', label: 'Web应用' },
|
||
{ value: '移动端', label: '移动端' },
|
||
{ value: '小程序', label: '小程序' },
|
||
{ value: '企业系统', label: '企业系统' }
|
||
]
|
||
|
||
const sortOptions = [
|
||
{ value: 'date', label: '最新发布' },
|
||
{ value: 'price-high', label: '价格从高到低' },
|
||
{ value: 'price-low', label: '价格从低到高' }
|
||
]
|
||
|
||
const filteredProjects = computed(() => {
|
||
return projects.value
|
||
.filter(p =>
|
||
(selectedCategory.value === 'all' || p.category === selectedCategory.value) &&
|
||
(searchQuery.value === '' || p.title.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
|
||
p.description.toLowerCase().includes(searchQuery.value.toLowerCase()))
|
||
)
|
||
.sort((a, b) => {
|
||
if (sortBy.value === 'date') {
|
||
return new Date(b.createdAt) - new Date(a.createdAt)
|
||
} else if (sortBy.value === 'price-high') {
|
||
return parseFloat(b.price) - parseFloat(a.price)
|
||
} else if (sortBy.value === 'price-low') {
|
||
return parseFloat(a.price) - parseFloat(b.price)
|
||
}
|
||
return 0
|
||
})
|
||
})
|
||
|
||
const formatDate = (dateString) => {
|
||
const date = new Date(dateString)
|
||
return date.toLocaleDateString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit' })
|
||
}
|
||
|
||
const formatPrice = (price) => {
|
||
return '¥ ' + parseFloat(price).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<div class="bg-gradient-to-b from-gray-50 to-white dark:from-gray-900 dark:to-gray-800 min-h-screen py-12">
|
||
<div class="max-w-6xl mx-auto px-6">
|
||
<!-- 页面标题 -->
|
||
<div class="text-center mb-16" data-aos="fade-up">
|
||
<h1 class="text-4xl md:text-5xl font-bold mb-6 text-gray-900 dark:text-white">
|
||
<span class="relative">
|
||
项目展示
|
||
<span class="absolute -bottom-2 left-0 right-0 mx-auto w-1/3 h-1 bg-gradient-to-r from-rose-400 to-rose-600 rounded-full"></span>
|
||
</span>
|
||
</h1>
|
||
<p class="text-lg text-gray-600 dark:text-gray-300 max-w-3xl mx-auto">
|
||
探索我们的精选项目,从Web应用到移动端解决方案,满足您的各种数字化需求
|
||
</p>
|
||
</div>
|
||
|
||
<!-- 过滤排序栏 -->
|
||
<div class="mb-10" data-aos="fade-up">
|
||
<div class="bg-white dark:bg-gray-800 rounded-2xl shadow-lg p-6">
|
||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||
<!-- 搜索框 -->
|
||
<div class="relative">
|
||
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||
</svg>
|
||
</div>
|
||
<input
|
||
v-model="searchQuery"
|
||
type="text"
|
||
placeholder="搜索项目..."
|
||
class="pl-10 pr-4 py-2 w-full bg-gray-50 dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-xl focus:ring-rose-500 focus:border-rose-500 dark:text-white"
|
||
/>
|
||
</div>
|
||
|
||
<!-- 分类选择 -->
|
||
<div>
|
||
<select
|
||
v-model="selectedCategory"
|
||
class="w-full bg-gray-50 dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-xl px-4 py-2 focus:ring-rose-500 focus:border-rose-500 dark:text-white"
|
||
>
|
||
<option
|
||
v-for="category in categories"
|
||
:key="category.value"
|
||
:value="category.value"
|
||
>
|
||
{{ category.label }}
|
||
</option>
|
||
</select>
|
||
</div>
|
||
|
||
<!-- 排序选择 -->
|
||
<div>
|
||
<select
|
||
v-model="sortBy"
|
||
class="w-full bg-gray-50 dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-xl px-4 py-2 focus:ring-rose-500 focus:border-rose-500 dark:text-white"
|
||
>
|
||
<option
|
||
v-for="option in sortOptions"
|
||
:key="option.value"
|
||
:value="option.value"
|
||
>
|
||
{{ option.label }}
|
||
</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 项目卡片网格 -->
|
||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||
<div
|
||
v-for="project in filteredProjects"
|
||
:key="project.id"
|
||
class="group bg-white dark:bg-gray-800 rounded-3xl shadow-lg hover:shadow-xl transition-all duration-500 overflow-hidden transform hover:-translate-y-2"
|
||
data-aos="fade-up"
|
||
>
|
||
<div class="relative h-56 overflow-hidden">
|
||
<img
|
||
:src="project.cover"
|
||
class="w-full h-full object-cover transform transition-transform duration-700 group-hover:scale-110"
|
||
:alt="project.title"
|
||
/>
|
||
<div class="absolute inset-0 bg-gradient-to-t from-black/60 via-black/20 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
||
|
||
<div class="absolute top-4 right-4">
|
||
<span
|
||
class="px-3 py-1 bg-black/50 backdrop-blur-md text-white text-xs font-medium rounded-full"
|
||
>
|
||
{{ project.category }}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="p-6">
|
||
<div class="flex flex-wrap gap-2 mb-3">
|
||
<span
|
||
v-for="tag in project.tags"
|
||
:key="tag"
|
||
class="px-2 py-1 bg-rose-50 dark:bg-rose-900/20 text-rose-600 dark:text-rose-300 text-xs font-medium rounded-lg"
|
||
>
|
||
{{ tag }}
|
||
</span>
|
||
</div>
|
||
|
||
<h3 class="text-xl font-bold mb-2 text-gray-900 dark:text-white group-hover:text-rose-600 dark:group-hover:text-rose-400 transition-colors">
|
||
{{ project.title }}
|
||
</h3>
|
||
|
||
<p class="text-gray-600 dark:text-gray-300 text-sm mb-4 line-clamp-2">
|
||
{{ project.description }}
|
||
</p>
|
||
|
||
<div class="flex justify-between items-center">
|
||
<div>
|
||
<p class="text-lg font-bold text-rose-600 dark:text-rose-400">{{ formatPrice(project.price) }}</p>
|
||
<p class="text-xs text-gray-500 dark:text-gray-400">{{ formatDate(project.createdAt) }}</p>
|
||
</div>
|
||
|
||
<button class="w-10 h-10 rounded-full bg-rose-50 dark:bg-rose-900/20 text-rose-600 dark:text-rose-400 flex items-center justify-center hover:bg-rose-100 dark:hover:bg-rose-900/40 transition-colors">
|
||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3" />
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 分页 -->
|
||
<div class="mt-16 flex justify-center" data-aos="fade-up">
|
||
<nav class="flex items-center space-x-2">
|
||
<button class="w-10 h-10 rounded-full bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 flex items-center justify-center text-gray-500 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors">
|
||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
|
||
</svg>
|
||
</button>
|
||
|
||
<button class="w-10 h-10 rounded-full bg-rose-600 text-white flex items-center justify-center">
|
||
1
|
||
</button>
|
||
|
||
<button class="w-10 h-10 rounded-full bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 flex items-center justify-center text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors">
|
||
2
|
||
</button>
|
||
|
||
<button class="w-10 h-10 rounded-full bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 flex items-center justify-center text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors">
|
||
3
|
||
</button>
|
||
|
||
<span class="text-gray-500 dark:text-gray-400">...</span>
|
||
|
||
<button class="w-10 h-10 rounded-full bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 flex items-center justify-center text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors">
|
||
10
|
||
</button>
|
||
|
||
<button class="w-10 h-10 rounded-full bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 flex items-center justify-center text-gray-500 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors">
|
||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
||
</svg>
|
||
</button>
|
||
</nav>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
/* 添加轻奢风格的渐变背景和阴影效果 */
|
||
.bg-gradient-luxury {
|
||
background: linear-gradient(135deg, #f6f8fb 0%, #f0f4f8 100%);
|
||
}
|
||
|
||
/* 暗色模式下的轻奢风格 */
|
||
@media (prefers-color-scheme: dark) {
|
||
.bg-gradient-luxury {
|
||
background: linear-gradient(135deg, #1a1d23 0%, #111827 100%);
|
||
}
|
||
}
|
||
</style>
|