vitesse-yitisheng-web/src/pages/universities.vue

515 lines
16 KiB
Vue

<script setup lang="ts">
import type { FilterState } from '~/components/FilterBar.vue'
defineOptions({
name: 'UniversitiesPage',
})
useHead({
title: '找大学',
})
const currentFilters = ref<FilterState | null>(null)
const currentKeyword = ref('')
function handleDataChange(data: { keyword: string, filters: FilterState }) {
console.warn('发起请求:', data.keyword, data.filters)
currentKeyword.value = data.keyword
currentFilters.value = data.filters
}
// Define reactive data for filters
const selectedRegions = ref<string[]>([])
const selectedTags = ref<string[]>([])
const selectedSchoolTypes = ref<string[]>([])
const selectedCategories = ref<string[]>([])
const universityName = ref('')
// Sample university data
const universities = ref([
{
id: 1,
name: '中国石油大学(华东)',
logo: 'http://img1.youzy.cn/content/media/thumbs/p00026840.jpeg',
location: '山东青岛',
tags: ['STEM', 'Public', 'Ministry-affiliated'],
rankTags: ['211', 'Double First-Class'],
campus: 'East China',
},
{
id: 2,
name: '清华大学',
logo: 'http://img1.youzy.cn/content/media/thumbs/p00026840.jpeg',
location: '北京',
tags: ['Comprehensive', 'Public', 'Ministry-affiliated'],
rankTags: ['985', '211', 'Double First-Class'],
campus: '',
},
{
id: 3,
name: '北京大学',
logo: 'http://img1.youzy.cn/content/media/thumbs/p00026840.jpeg',
location: '北京',
tags: ['Comprehensive', 'Public', 'Ministry-affiliated'],
rankTags: ['985', '211', 'Double First-Class'],
campus: '',
},
{
id: 4,
name: '上海交通大学',
logo: 'http://img1.youzy.cn/content/media/thumbs/p00026840.jpeg',
location: '上海',
tags: ['Comprehensive', 'Public', 'Ministry-affiliated'],
rankTags: ['985', '211', 'Double First-Class'],
campus: '',
},
{
id: 5,
name: '复旦大学',
logo: 'http://img1.youzy.cn/content/media/thumbs/p00026840.jpeg',
location: '上海',
tags: ['Comprehensive', 'Public', 'Ministry-affiliated'],
rankTags: ['985', '211', 'Double First-Class'],
campus: '',
},
{
id: 6,
name: '浙江大学',
logo: 'http://img1.youzy.cn/content/media/thumbs/p00026840.jpeg',
location: '浙江杭州',
tags: ['Comprehensive', 'Public', 'Ministry-affiliated'],
rankTags: ['985', '211', 'Double First-Class'],
campus: '',
},
{
id: 7,
name: '南京大学',
logo: 'http://img1.youzy.cn/content/media/thumbs/p00026840.jpeg',
location: '江苏南京',
tags: ['Comprehensive', 'Public', 'Ministry-affiliated'],
rankTags: ['985', '211', 'Double First-Class'],
campus: '',
},
{
id: 8,
name: '中山大学',
logo: 'http://img1.youzy.cn/content/media/thumbs/p00026840.jpeg',
location: '广东广州',
tags: ['Comprehensive', 'Public', 'Ministry-affiliated'],
rankTags: ['985', '211', 'Double First-Class'],
campus: '',
},
{
id: 9,
name: '华中科技大学',
logo: 'http://img1.youzy.cn/content/media/thumbs/p00026840.jpeg',
location: '湖北武汉',
tags: ['STEM', 'Public', 'Ministry-affiliated'],
rankTags: ['985', '211', 'Double First-Class'],
campus: '',
},
{
id: 10,
name: '西安交通大学',
logo: 'http://img1.youzy.cn/content/media/thumbs/p00026840.jpeg',
location: '陕西西安',
tags: ['Comprehensive', 'Public', 'Ministry-affiliated'],
rankTags: ['985', '211', 'Double First-Class'],
campus: '',
},
{
id: 11,
name: '哈尔滨工业大学',
logo: 'http://img1.youzy.cn/content/media/thumbs/p00026840.jpeg',
location: '黑龙江哈尔滨',
tags: ['STEM', 'Public', 'Ministry-affiliated'],
rankTags: ['985', '211', 'Double First-Class'],
campus: '',
},
{
id: 12,
name: '北京理工大学',
logo: 'http://img1.youzy.cn/content/media/thumbs/p00026840.jpeg',
location: '北京',
tags: ['STEM', 'Public', 'Ministry-affiliated'],
rankTags: ['985', '211', 'Double First-Class'],
campus: '',
},
{
id: 13,
name: '电子科技大学',
logo: 'http://img1.youzy.cn/content/media/thumbs/p00026840.jpeg',
location: '四川成都',
tags: ['STEM', 'Public', 'Ministry-affiliated'],
rankTags: ['985', '211', 'Double First-Class'],
campus: '',
},
{
id: 14,
name: '南开大学',
logo: 'http://img1.youzy.cn/content/media/thumbs/p00026840.jpeg',
location: '天津',
tags: ['Comprehensive', 'Public', 'Ministry-affiliated'],
rankTags: ['985', '211', 'Double First-Class'],
campus: '',
},
{
id: 15,
name: '天津大学',
logo: 'http://img1.youzy.cn/content/media/thumbs/p00026840.jpeg',
location: '天津',
tags: ['STEM', 'Public', 'Ministry-affiliated'],
rankTags: ['985', '211', 'Double First-Class'],
campus: '',
},
])
// New filter data for the updated UI
const selectedProvinces = ref<string[]>([])
// Updated filter function to include province filtering
const filteredUniversities = computed(() => {
return universities.value.filter((university) => {
// Filter by name
if (
universityName.value
&& !university.name
.toLowerCase()
.includes(universityName.value.toLowerCase())
) {
return false
}
// Filter by province if not '不限'
if (
selectedProvinces.value.length > 0
&& !selectedProvinces.value.includes('不限')
) {
// Extract province from location (first character or first two characters)
const universityProvince = extractProvinceFromLocation(
university.location,
)
if (!selectedProvinces.value.includes(universityProvince)) {
return false
}
}
// Filter by region (simplified - in real app, would match location to regions)
if (selectedRegions.value.length > 0) {
// For demo, we'll skip region filtering
}
// Filter by tags
if (
selectedTags.value.length > 0
&& !selectedTags.value.some(tag => university.rankTags.includes(tag))
) {
return false
}
// Filter by school type (simplified - in real app, would match school type)
if (selectedSchoolTypes.value.length > 0) {
// For demo, we'll skip school type filtering
}
// Filter by category (simplified - in real app, would match category)
if (selectedCategories.value.length > 0) {
// For demo, we'll skip category filtering
}
return true
})
})
// Helper function to extract province from location
function extractProvinceFromLocation(location: string): string {
// Common provinces that are 2 characters
const twoCharProvinces = [
'北京',
'上海',
'天津',
'重庆',
'香港',
'澳门',
'台湾',
]
for (const province of twoCharProvinces) {
if (location.startsWith(province)) {
return province
}
}
// For other provinces, take the first character
if (location.length > 0) {
return location.charAt(0)
}
return ''
}
// Pagination data
const currentPage = ref(1)
const itemsPerPage = 5
const totalPages = computed(() =>
Math.ceil(universities.value.length / itemsPerPage),
)
// Add helper function for pagination
function getPageNumber(index: number) {
if (totalPages.value <= 5) {
return index
}
const currentPageValue = currentPage.value
const totalPagesValue = totalPages.value
if (currentPageValue <= 3) {
// First few pages: show 1, 2, 3, 4, 5
return index
}
else if (currentPageValue >= totalPagesValue - 2) {
// Last few pages: show last 5 pages
return totalPagesValue - 5 + index
}
else {
// Middle pages: show current page in center
return currentPageValue - 3 + index
}
}
// Get universities for current page
const paginatedUniversities = computed(() => {
const startIndex = (currentPage.value - 1) * itemsPerPage
return filteredUniversities.value.slice(
startIndex,
startIndex + itemsPerPage,
)
})
// Search function
</script>
<template>
<div class="mx-auto max-w-7xl px-4 lg:px-8 sm:px-6">
<!-- Top Section: Filters -->
<div class="mb-8 mt-8 rounded-lg bg-white p-6 shadow">
<div class="grid grid-cols-1 select-none gap-6 lg:grid-cols-6">
<!-- Top Toolbar -->
<div class="lg:col-span-4">
<FilterBar @change="handleDataChange" />
<div class="mb-4 mt-4 flex items-center justify-between">
<h2 class="text-lg font-semibold">
大学: {{ filteredUniversities.length }}
</h2>
</div>
<div class="space-y-4">
<div
v-for="university in paginatedUniversities"
:key="university.id"
class="flex justify-between border border-gray-200 rounded-lg p-4 transition-shadow hover:shadow-md"
>
<!-- Left: Logo and Name -->
<div class="mr-4 flex items-center">
<img
:src="university.logo"
:alt="university.name"
class="mr-3 h-12 w-12 object-contain"
>
<div class="flex flex-col space-y-2">
<!-- name location -->
<div class="w-full flex items-center">
<h3 class="text-gray-900 font-bold">
{{ university.name }}
</h3>
<div class="ml-5 flex items-center text-gray-600">
<svg
xmlns="http://www.w3.org/2000/svg"
class="mr-1 h-4 w-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"
/>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"
/>
</svg>
<span>{{ university.location }}</span>
</div>
</div>
<div class="w-full flex flex-wrap gap-2">
<span
v-for="tag in university.tags"
:key="tag"
class="rounded bg-gray-100 px-2 py-1 text-xs text-gray-700"
>
{{ tag }}
</span>
</div>
<!-- Middle: Location and Tags -->
<div class="w-full flex flex-wrap gap-2">
<span
v-for="rankTag in university.rankTags"
:key="rankTag"
class="rounded bg-blue-100 px-2 py-1 text-xs text-blue-700"
>
{{ rankTag }}
</span>
</div>
</div>
</div>
<!-- Right: Action Button -->
<div class="flex-3">
<button
class="flex items-center rounded-md bg-orange-100 px-3 py-2 text-orange-700 transition-colors hover:bg-orange-200"
>
<span>录取概率</span>
<svg
xmlns="http://www.w3.org/2000/svg"
class="ml-1 h-4 w-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"
/>
</svg>
</button>
</div>
</div>
</div>
<!-- Pagination -->
<div class="mt-8 flex items-center justify-center space-x-2">
<button
:disabled="currentPage === 1"
class="border border-gray-300 rounded-md bg-white px-4 py-2 text-sm text-gray-700 font-medium hover:bg-gray-50 disabled:opacity-50"
@click="currentPage = 1"
>
首页
</button>
<button
:disabled="currentPage === 1"
class="border border-gray-300 rounded-md bg-white px-4 py-2 text-sm text-gray-700 font-medium hover:bg-gray-50 disabled:opacity-50"
@click="currentPage = currentPage - 1"
>
上一页
</button>
<!-- Page numbers -->
<div class="flex space-x-1">
<button
v-for="page in Math.min(5, totalPages)"
:key="page"
:class="[
currentPage === getPageNumber(page)
? 'bg-blue-500 text-white'
: 'bg-white text-gray-700 hover:bg-gray-50',
]"
class="border border-gray-300 rounded-md px-3 px-3 py-2 py-2 text-sm text-sm font-medium"
@click="currentPage = getPageNumber(page)"
>
{{ getPageNumber(page) }}
</button>
</div>
<button
:disabled="currentPage === totalPages"
class="border border-gray-300 rounded-md bg-white px-4 py-2 text-sm text-gray-700 font-medium hover:bg-gray-50 disabled:opacity-50"
@click="currentPage = currentPage + 1"
>
下一页
</button>
<button
:disabled="currentPage === totalPages"
class="border border-gray-300 rounded-md bg-white px-4 py-2 text-sm text-gray-700 font-medium hover:bg-gray-50 disabled:opacity-50"
@click="currentPage = totalPages"
>
末页
</button>
</div>
</div>
<div class="lg:col-span-2">
<div class="h-full flex items-center justify-center border border-gray-200 rounded-lg bg-gray-50 p-6">
<p class="text-gray-500">
预留空间
</p>
</div>
</div>
</div>
<!-- <div class="flex space-x-4">
<div class="mt-8 p-4 border border-dashed border-gray-300 rounded">
<p>当前筛选条件: {{ JSON.stringify(currentFilters) }}</p>
<p>当前搜索词: {{ currentKeyword }}</p>
</div>
</div> -->
</div>
<!-- Bottom Section: Reserved Space -->
<div class="mb-8 border border-gray-200 rounded-lg bg-gray-50 p-6">
<p class="text-center text-gray-500">
预留空间
</p>
</div>
</div>
</template>
<style scoped>
/* Additional styling for the university cards */
.university-card {
transition: box-shadow 0.2s ease;
}
.university-card:hover {
box-shadow:
0 4px 6px -1px rgba(0, 0, 0, 0.1),
0 2px 4px -1px rgba(0, 0, 0, 0.06);
}
/* Pagination styling */
.pagination-btn {
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 0.375rem;
border: 1px solid #d1d5db;
background-color: #ffffff;
font-size: 0.875rem;
font-weight: 500;
color: #374151;
padding: 0.5rem 0.75rem;
transition: all 0.2s ease;
}
.pagination-btn:hover {
background-color: #f3f4f6;
}
.pagination-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.current-page {
background-color: #f97316;
color: white;
border-color: #f97316;
}
</style>