fix:院校页面静态页

This commit is contained in:
zhouwentao 2026-02-01 14:34:27 +08:00
parent d5cffd2a76
commit ef087feb4d
7 changed files with 1583 additions and 240 deletions

5
src/components.d.ts vendored
View File

@ -8,14 +8,19 @@ export {}
/* prettier-ignore */ /* prettier-ignore */
declare module 'vue' { declare module 'vue' {
export interface GlobalComponents { export interface GlobalComponents {
AdmissionPrediction: typeof import('./components/school/AdmissionPrediction.vue')['default']
BackToTop: typeof import('./components/BackToTop.vue')['default'] BackToTop: typeof import('./components/BackToTop.vue')['default']
copy: typeof import('./components/ScoreForm copy.vue')['default'] copy: typeof import('./components/ScoreForm copy.vue')['default']
DictDemo: typeof import('./components/DictDemo.vue')['default'] DictDemo: typeof import('./components/DictDemo.vue')['default']
EnrollmentPlan: typeof import('./components/school/EnrollmentPlan.vue')['default']
FilterBar: typeof import('./components/FilterBar.vue')['default'] FilterBar: typeof import('./components/FilterBar.vue')['default']
HistoricalScores: typeof import('./components/school/HistoricalScores.vue')['default']
LoginForm: typeof import('./components/LoginForm.vue')['default'] LoginForm: typeof import('./components/LoginForm.vue')['default']
Majors: typeof import('./components/school/Majors.vue')['default']
README: typeof import('./components/README.md')['default'] README: typeof import('./components/README.md')['default']
RouterLink: typeof import('vue-router')['RouterLink'] RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView'] RouterView: typeof import('vue-router')['RouterView']
SchoolOverview: typeof import('./components/school/SchoolOverview.vue')['default']
ScoreDictForm: typeof import('./components/ScoreDictForm.vue')['default'] ScoreDictForm: typeof import('./components/ScoreDictForm.vue')['default']
ScoreForm: typeof import('./components/ScoreForm.vue')['default'] ScoreForm: typeof import('./components/ScoreForm.vue')['default']
TheCounter: typeof import('./components/TheCounter.vue')['default'] TheCounter: typeof import('./components/TheCounter.vue')['default']

View File

@ -0,0 +1,267 @@
<script setup lang="ts">
import { ref } from 'vue'
interface PredictionResult {
probability: number
riskLevel: 'low' | 'medium' | 'high'
suggestedScore: number
advice: string
}
const score = ref('')
const province = ref('山东')
const category = ref('理科')
const provinces = ['山东', '北京', '上海', '江苏', '浙江', '广东', '四川']
const categories = ['理科', '文科', '综合']
const isCalculating = ref(false)
const predictionResult = ref<PredictionResult | null>(null)
const calculatePrediction = () => {
if (!score.value) {
return
}
isCalculating.value = true
predictionResult.value = null
//
setTimeout(() => {
const scoreNum = Number.parseInt(score.value, 10)
let probability = 0
let riskLevel: 'low' | 'medium' | 'high' = 'high'
let suggestedScore = 0
let advice = ''
//
if (province.value === '山东' && category.value === '理科') {
if (scoreNum >= 620) {
probability = 95
riskLevel = 'low'
suggestedScore = 600
advice = '您的分数远超我校录取分数线,录取概率极高,建议报考我校王牌专业。'
}
else if (scoreNum >= 590) {
probability = 80
riskLevel = 'low'
suggestedScore = 580
advice = '您的分数高于我校录取分数线,录取概率较大,建议报考热门专业。'
}
else if (scoreNum >= 560) {
probability = 60
riskLevel = 'medium'
suggestedScore = 550
advice = '您的分数接近我校录取分数线,建议冲刺热门专业,稳妥选择普通专业。'
}
else if (scoreNum >= 540) {
probability = 30
riskLevel = 'high'
suggestedScore = 530
advice = '您的分数低于我校录取分数线,建议谨慎报考,可考虑其他院校。'
}
else {
probability = 10
riskLevel = 'high'
suggestedScore = 520
advice = '您的分数偏低,录取概率较低,建议考虑其他院校或专业。'
}
}
else {
//
if (scoreNum >= 600) {
probability = 90
riskLevel = 'low'
suggestedScore = 580
advice = '您的分数优势明显,录取概率很高。'
}
else if (scoreNum >= 570) {
probability = 70
riskLevel = 'medium'
suggestedScore = 550
advice = '您的分数有一定竞争力,建议合理填报志愿。'
}
else {
probability = 40
riskLevel = 'high'
suggestedScore = 530
advice = '您的分数竞争力一般,建议谨慎选择。'
}
}
predictionResult.value = {
probability,
riskLevel,
suggestedScore,
advice,
}
isCalculating.value = false
}, 1000)
}
const getRiskLevelText = (level: string) => {
const map: Record<string, string> = {
low: '低风险',
medium: '中等风险',
high: '高风险',
}
return map[level] || level
}
const getRiskLevelColor = (level: string) => {
const map: Record<string, string> = {
low: 'text-green-600 dark:text-green-400',
medium: 'text-yellow-600 dark:text-yellow-400',
high: 'text-red-600 dark:text-red-400',
}
return map[level] || ''
}
const getRiskLevelBg = (level: string) => {
const map: Record<string, string> = {
low: 'bg-green-100 dark:bg-green-900/30',
medium: 'bg-yellow-100 dark:bg-yellow-900/30',
high: 'bg-red-100 dark:bg-red-900/30',
}
return map[level] || ''
}
</script>
<template>
<div class="rounded-lg bg-white p-5 shadow-sm dark:bg-slate-800">
<div class="mb-4 flex items-center justify-between">
<h2 class="border-l-4 border-orange-500 pl-3 text-lg text-gray-900 font-bold dark:text-white">
录取预测
</h2>
</div>
<!-- 输入区域 -->
<div class="mb-5 space-y-4 rounded-lg border border-gray-200 p-4 dark:border-slate-700">
<div class="grid grid-cols-1 gap-4 md:grid-cols-3">
<!-- 分数输入 -->
<div>
<label class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300">
高考分数
</label>
<input
v-model="score"
type="number"
placeholder="请输入分数"
class="w-full rounded border border-gray-200 bg-white px-4 py-2 text-sm dark:border-slate-600 dark:bg-slate-700 dark:text-white"
>
</div>
<!-- 省份选择 -->
<div>
<label class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300">
报考省份
</label>
<select
v-model="province"
class="w-full rounded border border-gray-200 bg-white px-4 py-2 text-sm dark:border-slate-600 dark:bg-slate-700 dark:text-white"
>
<option v-for="prov in provinces" :key="prov" :value="prov">
{{ prov }}
</option>
</select>
</div>
<!-- 科类选择 -->
<div>
<label class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300">
科类
</label>
<select
v-model="category"
class="w-full rounded border border-gray-200 bg-white px-4 py-2 text-sm dark:border-slate-600 dark:bg-slate-700 dark:text-white"
>
<option v-for="cat in categories" :key="cat" :value="cat">
{{ cat }}
</option>
</select>
</div>
</div>
<!-- 计算按钮 -->
<button
:disabled="!score || isCalculating"
class="w-full rounded bg-orange-500 px-4 py-2.5 text-sm font-medium text-white transition-colors hover:bg-orange-600 disabled:cursor-not-allowed disabled:bg-gray-300 dark:disabled:bg-slate-600"
@click="calculatePrediction"
>
<span v-if="isCalculating">计算中...</span>
<span v-else>开始预测</span>
</button>
</div>
<!-- 预测结果 -->
<div v-if="predictionResult" class="space-y-4">
<!-- 概率展示 -->
<div class="rounded-lg border border-gray-200 p-5 dark:border-slate-700">
<h3 class="mb-4 text-base font-bold text-gray-900 dark:text-white">
预测结果
</h3>
<div class="mb-4 flex items-center gap-4">
<div class="flex-1">
<div class="mb-2 flex items-center justify-between text-sm">
<span class="text-gray-600 dark:text-gray-400">录取概率</span>
<span class="text-2xl font-bold text-orange-600 dark:text-orange-400">
{{ predictionResult.probability }}%
</span>
</div>
<div class="h-3 w-full overflow-hidden rounded-full bg-gray-200 dark:bg-slate-700">
<div
class="h-full rounded-full bg-gradient-to-r from-orange-400 to-orange-600 transition-all duration-1000"
:style="{ width: `${predictionResult.probability}%` }"
/>
</div>
</div>
</div>
<!-- 风险等级 -->
<div class="flex items-center justify-between rounded-lg p-3" :class="getRiskLevelBg(predictionResult.riskLevel)">
<span class="text-sm text-gray-600 dark:text-gray-400">风险等级</span>
<span class="text-base font-bold" :class="getRiskLevelColor(predictionResult.riskLevel)">
{{ getRiskLevelText(predictionResult.riskLevel) }}
</span>
</div>
</div>
<!-- 建议分数 -->
<div class="rounded-lg bg-blue-50 p-4 dark:bg-slate-700/30">
<div class="mb-2 flex items-center justify-between">
<span class="text-sm text-gray-600 dark:text-gray-400">建议分数线</span>
<span class="text-xl font-bold text-blue-600 dark:text-blue-400">
{{ predictionResult.suggestedScore }}
</span>
</div>
<p class="text-xs text-gray-500 dark:text-gray-400">
基于历史数据分析的建议录取分数线仅供参考
</p>
</div>
<!-- 报考建议 -->
<div class="rounded-lg border border-orange-200 bg-orange-50 p-4 dark:border-orange-800 dark:bg-orange-900/20">
<h4 class="mb-2 flex items-center gap-2 text-sm font-bold text-orange-800 dark:text-orange-300">
💡 报考建议
</h4>
<p class="text-sm text-gray-700 leading-relaxed dark:text-gray-300">
{{ predictionResult.advice }}
</p>
</div>
</div>
<!-- 说明 -->
<div class="mt-4 rounded-lg bg-gray-50 p-4 text-xs text-gray-600 dark:bg-slate-700/30 dark:text-gray-300">
<p class="mb-1 font-bold">
免责声明
</p>
<ul class="list-inside list-disc space-y-1">
<li>本预测结果基于历史数据模型计算仅供参考不代表实际录取结果</li>
<li>实际录取分数受当年试题难度报考人数招生计划等多种因素影响</li>
<li>建议结合多种因素综合考虑理性填报志愿</li>
<li>最终录取结果以各省教育考试院公布为准</li>
</ul>
</div>
</div>
</template>

View File

@ -0,0 +1,189 @@
<script setup lang="ts">
import { ref } from 'vue'
interface PlanData {
year: number
province: string
category: string
totalQuota: number
majors: {
name: string
quota: number
category: string
}[]
}
const selectedYear = ref(2024)
const selectedProvince = ref('山东')
const selectedCategory = ref('理科')
const years = [2024, 2023, 2022]
const provinces = ['山东', '北京', '上海', '江苏', '浙江']
const categories = ['理科', '文科', '综合']
const planData: PlanData[] = [
{
year: 2024,
province: '山东',
category: '理科',
totalQuota: 800,
majors: [
{ name: '石油工程', quota: 120, category: '本科' },
{ name: '地质学', quota: 80, category: '本科' },
{ name: '化学工程与工艺', quota: 100, category: '本科' },
{ name: '机械设计制造及其自动化', quota: 90, category: '本科' },
{ name: '计算机科学与技术', quota: 70, category: '本科' },
{ name: '土木工程', quota: 85, category: '本科' },
{ name: '油气储运工程', quota: 75, category: '本科' },
{ name: '应用化学', quota: 60, category: '本科' },
{ name: '勘查技术与工程', quota: 65, category: '本科' },
{ name: '安全工程', quota: 55, category: '本科' },
],
},
{
year: 2024,
province: '山东',
category: '文科',
totalQuota: 200,
majors: [
{ name: '英语', quota: 50, category: '本科' },
{ name: '工商管理', quota: 60, category: '本科' },
{ name: '会计学', quota: 55, category: '本科' },
{ name: '市场营销', quota: 35, category: '本科' },
],
},
{
year: 2023,
province: '山东',
category: '理科',
totalQuota: 750,
majors: [
{ name: '石油工程', quota: 115, category: '本科' },
{ name: '地质学', quota: 75, category: '本科' },
{ name: '化学工程与工艺', quota: 95, category: '本科' },
{ name: '机械设计制造及其自动化', quota: 85, category: '本科' },
{ name: '计算机科学与技术', quota: 65, category: '本科' },
],
},
]
const filteredPlan = computed(() => {
return planData.find(
item =>
item.year === selectedYear.value &&
item.province === selectedProvince.value &&
item.category === selectedCategory.value,
)
})
</script>
<template>
<div class="rounded-lg bg-white p-5 shadow-sm dark:bg-slate-800">
<div class="mb-4 flex items-center justify-between">
<h2 class="border-l-4 border-orange-500 pl-3 text-lg text-gray-900 font-bold dark:text-white">
招生计划
</h2>
</div>
<!-- 筛选条件 -->
<div class="mb-5 flex flex-wrap gap-3">
<select
v-model="selectedYear"
class="rounded border border-gray-200 bg-white px-3 py-2 text-sm dark:border-slate-600 dark:bg-slate-700 dark:text-white"
>
<option v-for="year in years" :key="year" :value="year">
{{ year }}
</option>
</select>
<select
v-model="selectedProvince"
class="rounded border border-gray-200 bg-white px-3 py-2 text-sm dark:border-slate-600 dark:bg-slate-700 dark:text-white"
>
<option v-for="province in provinces" :key="province" :value="province">
{{ province }}
</option>
</select>
<select
v-model="selectedCategory"
class="rounded border border-gray-200 bg-white px-3 py-2 text-sm dark:border-slate-600 dark:bg-slate-700 dark:text-white"
>
<option v-for="category in categories" :key="category" :value="category">
{{ category }}
</option>
</select>
</div>
<!-- 总计划数 -->
<div v-if="filteredPlan" class="mb-5 rounded-lg bg-blue-50 p-4 dark:bg-slate-700/30">
<div class="flex items-center justify-between">
<span class="text-sm text-gray-600 dark:text-gray-300">
{{ selectedYear }} {{ selectedProvince }} {{ selectedCategory }}总计划
</span>
<span class="text-2xl font-bold text-blue-600 dark:text-blue-400">
{{ filteredPlan.totalQuota }}
</span>
</div>
</div>
<!-- 专业列表 -->
<div v-if="filteredPlan && filteredPlan.majors.length > 0" class="overflow-x-auto">
<table class="w-full text-left text-sm">
<thead class="bg-gray-50 text-xs text-gray-500 dark:bg-slate-700 dark:text-gray-400">
<tr>
<th class="w-10 px-4 py-3 text-center">
序号
</th>
<th class="px-4 py-3">
专业名称
</th>
<th class="w-24 px-4 py-3 text-center">
学历层次
</th>
<th class="w-24 px-4 py-3 text-center">
计划数
</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100 dark:divide-slate-700">
<tr
v-for="(major, idx) in filteredPlan.majors"
:key="idx"
class="hover:bg-gray-50 dark:hover:bg-slate-700/50"
>
<td class="px-4 py-3 text-center text-gray-600 dark:text-gray-400">
{{ idx + 1 }}
</td>
<td class="px-4 py-3 text-gray-900 font-medium dark:text-white">
{{ major.name }}
</td>
<td class="px-4 py-3 text-center">
<span class="rounded bg-gray-100 px-2 py-1 text-xs dark:bg-slate-600 dark:text-gray-300">
{{ major.category }}
</span>
</td>
<td class="px-4 py-3 text-center text-orange-600 font-medium dark:text-orange-400">
{{ major.quota }}
</td>
</tr>
</tbody>
</table>
</div>
<!-- 数据为空提示 -->
<div v-else class="py-8 text-center text-gray-500 dark:text-gray-400">
暂无数据
</div>
<!-- 说明 -->
<div class="mt-4 rounded-lg bg-orange-50 p-4 text-xs text-gray-600 dark:bg-slate-700/30 dark:text-gray-300">
<p class="mb-1 font-bold">
💡 数据说明
</p>
<ul class="list-inside list-disc space-y-1">
<li>招生计划数以各省教育考试院公布为准</li>
<li>实际录取人数可能根据生源情况调整</li>
<li>建议关注官方渠道获取最新招生信息</li>
</ul>
</div>
</div>
</template>

View File

@ -0,0 +1,154 @@
<script setup lang="ts">
import { ref } from 'vue'
interface ScoreData {
year: number
province: string
category: string
minScore: number
avgScore: number
maxScore: number
rank: number
}
const selectedYear = ref(2024)
const selectedProvince = ref('山东')
const selectedCategory = ref('理科')
const years = [2024, 2023, 2022, 2021, 2020]
const provinces = ['山东', '北京', '上海', '江苏', '浙江', '广东', '四川']
const categories = ['理科', '文科', '综合']
const scoreData: ScoreData[] = [
{ year: 2024, province: '山东', category: '理科', minScore: 580, avgScore: 600, maxScore: 620, rank: 15000 },
{ year: 2024, province: '山东', category: '文科', minScore: 560, avgScore: 580, maxScore: 600, rank: 8000 },
{ year: 2023, province: '山东', category: '理科', minScore: 575, avgScore: 595, maxScore: 615, rank: 16000 },
{ year: 2023, province: '山东', category: '文科', minScore: 555, avgScore: 575, maxScore: 595, rank: 8500 },
{ year: 2022, province: '山东', category: '理科', minScore: 570, avgScore: 590, maxScore: 610, rank: 17000 },
{ year: 2022, province: '山东', category: '文科', minScore: 550, avgScore: 570, maxScore: 590, rank: 9000 },
]
const filteredScores = computed(() => {
return scoreData.filter(
item =>
item.year === selectedYear.value &&
item.province === selectedProvince.value &&
item.category === selectedCategory.value,
)
})
</script>
<template>
<div class="rounded-lg bg-white p-5 shadow-sm dark:bg-slate-800">
<div class="mb-4 flex items-center justify-between">
<h2 class="border-l-4 border-orange-500 pl-3 text-lg text-gray-900 font-bold dark:text-white">
历年分数
</h2>
</div>
<!-- 筛选条件 -->
<div class="mb-5 flex flex-wrap gap-3">
<select
v-model="selectedYear"
class="rounded border border-gray-200 bg-white px-3 py-2 text-sm dark:border-slate-600 dark:bg-slate-700 dark:text-white"
>
<option v-for="year in years" :key="year" :value="year">
{{ year }}
</option>
</select>
<select
v-model="selectedProvince"
class="rounded border border-gray-200 bg-white px-3 py-2 text-sm dark:border-slate-600 dark:bg-slate-700 dark:text-white"
>
<option v-for="province in provinces" :key="province" :value="province">
{{ province }}
</option>
</select>
<select
v-model="selectedCategory"
class="rounded border border-gray-200 bg-white px-3 py-2 text-sm dark:border-slate-600 dark:bg-slate-700 dark:text-white"
>
<option v-for="category in categories" :key="category" :value="category">
{{ category }}
</option>
</select>
</div>
<!-- 分数表格 -->
<div class="overflow-x-auto">
<table class="w-full text-left text-sm">
<thead class="bg-gray-50 text-xs text-gray-500 dark:bg-slate-700 dark:text-gray-400">
<tr>
<th class="px-4 py-3">
年份
</th>
<th class="px-4 py-3">
省份
</th>
<th class="px-4 py-3">
科类
</th>
<th class="px-4 py-3">
最低分
</th>
<th class="px-4 py-3">
平均分
</th>
<th class="px-4 py-3">
最高分
</th>
<th class="px-4 py-3">
省排位
</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100 dark:divide-slate-700">
<tr
v-for="(item, idx) in filteredScores"
:key="idx"
class="hover:bg-gray-50 dark:hover:bg-slate-700/50"
>
<td class="px-4 py-3 text-gray-900 dark:text-white">
{{ item.year }}
</td>
<td class="px-4 py-3 text-gray-600 dark:text-gray-300">
{{ item.province }}
</td>
<td class="px-4 py-3 text-gray-600 dark:text-gray-300">
{{ item.category }}
</td>
<td class="px-4 py-3 text-orange-600 font-medium dark:text-orange-400">
{{ item.minScore }}
</td>
<td class="px-4 py-3 text-gray-900 dark:text-white">
{{ item.avgScore }}
</td>
<td class="px-4 py-3 text-gray-900 dark:text-white">
{{ item.maxScore }}
</td>
<td class="px-4 py-3 text-gray-600 dark:text-gray-300">
{{ item.rank.toLocaleString() }}
</td>
</tr>
</tbody>
</table>
</div>
<!-- 数据为空提示 -->
<div v-if="filteredScores.length === 0" class="py-8 text-center text-gray-500 dark:text-gray-400">
暂无数据
</div>
<!-- 图表说明 -->
<div class="mt-4 rounded-lg bg-orange-50 p-4 text-xs text-gray-600 dark:bg-slate-700/30 dark:text-gray-300">
<p class="mb-1 font-bold">
💡 数据说明
</p>
<ul class="list-inside list-disc space-y-1">
<li>数据来源于官方招生办仅供参考</li>
<li>点击年份可查看不同年份的录取数据</li>
<li>建议结合省排位进行志愿填报</li>
</ul>
</div>
</div>
</template>

View File

@ -0,0 +1,352 @@
<script setup lang="ts">
import { ref } from 'vue'
interface MajorInfo {
college: string
majors: {
name: string
degree: string
duration: number
category: string
description: string
tags: string[]
}[]
}
const searchKeyword = ref('')
const selectedCollege = ref('全部')
const selectedCategory = ref('全部')
const colleges = ['全部', '地球科学与技术学院', '石油工程学院', '化学化工学院', '机电工程学院', '储运与建筑工程学院']
const categories = ['全部', '工学', '理学', '管理学', '文学']
const majorsData: MajorInfo[] = [
{
college: '地球科学与技术学院',
majors: [
{
name: '勘查技术与工程',
degree: '工学学士',
duration: 4,
category: '工学',
description: '培养具备地质学、地球物理学等方面的基本理论和基本知识,能在资源勘查、工程勘察等部门从事勘查、设计、施工、管理等方面工作的高级工程技术人才。',
tags: ['国家级特色专业', '双一流学科'],
},
{
name: '地质学',
degree: '理学学士',
duration: 4,
category: '理学',
description: '培养具备地质学基本理论、基本知识,掌握地质调查、矿产资源勘查、地质工程设计等基本技能的专门人才。',
tags: ['省级重点学科'],
},
{
name: '资源勘查工程',
degree: '工学学士',
duration: 4,
category: '工学',
description: '培养具备扎实的地质学基础,掌握矿产资源勘查与评价的基本方法和技能,能在资源勘查、开发等领域从事技术和管理工作的复合型人才。',
tags: ['国家特色专业'],
},
{
name: '地球物理学',
degree: '理学学士',
duration: 4,
category: '理学',
description: '培养具备地球物理学的基础理论和基本技能,能在地球物理勘查、地震监测、工程物探等领域从事技术工作的专门人才。',
tags: ['省级特色专业'],
},
],
},
{
college: '石油工程学院',
majors: [
{
name: '海洋油气工程',
degree: '工学学士',
duration: 4,
category: '工学',
description: '培养具备海洋油气工程领域的专业知识,能在海洋油气田开发、海洋工程设计等方面工作的高级工程技术人才。',
tags: ['新兴专业', '国家战略专业'],
},
{
name: '石油工程',
degree: '工学学士',
duration: 4,
category: '工学',
description: '培养具备石油工程领域的基本理论和专业知识,能在油气田开发、钻井工程、采油工程等领域从事技术和管理工作的复合型人才。',
tags: ['国家级特色专业', '双一流学科'],
},
{
name: '碳储科学与工程',
degree: '工学学士',
duration: 4,
category: '工学',
description: '培养具备碳捕集、利用与封存(CCUS)领域的专业知识,能在碳减排、碳交易、碳中和等领域工作的创新型工程技术人才。',
tags: ['新兴专业', '绿色低碳'],
},
],
},
{
college: '化学化工学院',
majors: [
{
name: '化学',
degree: '理学学士',
duration: 4,
category: '理学',
description: '培养具备化学的基本理论、基本知识和基本技能,能在化学及相关领域从事科研、教学、技术等工作的高级专门人才。',
tags: ['省级重点学科'],
},
{
name: '化学工程与工艺',
degree: '工学学士',
duration: 4,
category: '工学',
description: '培养具备化学工程与工艺方面的基本理论和基本知识,能在化工、炼油、冶金、能源等部门从事工程设计、技术开发、生产管理等工作的高级工程技术人才。',
tags: ['国家级特色专业'],
},
{
name: '应用化学',
degree: '工学学士',
duration: 4,
category: '工学',
description: '培养具备应用化学方面的基本理论和基本知识,能在化工、材料、医药、环保等领域从事技术开发、生产管理等工作的高级工程技术人才。',
tags: ['省级特色专业'],
},
{
name: '环境工程',
degree: '工学学士',
duration: 4,
category: '工学',
description: '培养具备环境工程领域的基本理论和专业知识,能在环境污染防治、环境监测、环境规划等领域从事技术和管理工作的复合型人才。',
tags: ['绿色低碳', '新兴专业'],
},
{
name: '能源化学工程',
degree: '工学学士',
duration: 4,
category: '工学',
description: '培养具备能源化学工程领域的专业知识,能在新能源、清洁能源、能源转化等领域从事技术开发和工程设计工作的高级工程技术人才。',
tags: ['新兴专业', '国家战略专业'],
},
],
},
{
college: '机电工程学院',
majors: [
{
name: '安全工程',
degree: '工学学士',
duration: 4,
category: '工学',
description: '培养具备安全工程领域的专业知识,能在安全监察、安全管理、安全评价、安全设计等领域从事技术和管理工作的复合型人才。',
tags: ['国家级特色专业'],
},
{
name: '工业设计',
degree: '工学学士',
duration: 4,
category: '工学',
description: '培养具备工业设计的基本理论和基本技能,能在产品造型设计、视觉传达设计、环境设计等领域从事设计工作的复合型人才。',
tags: ['省级特色专业'],
},
{
name: '智能制造工程',
degree: '工学学士',
duration: 4,
category: '工学',
description: '培养具备智能制造领域的基本理论和专业知识,能在智能制造系统设计、智能装备开发、工业互联网等领域从事技术工作的复合型人才。',
tags: ['新兴专业', '国家战略专业'],
},
{
name: '机械设计制造及其自动化',
degree: '工学学士',
duration: 4,
category: '工学',
description: '培养具备机械设计制造及其自动化方面的基本理论和基本知识,能在机械制造、自动化控制等领域从事设计、制造、运行管理等工作的高级工程技术人才。',
tags: ['国家级特色专业'],
},
],
},
{
college: '储运与建筑工程学院',
majors: [
{
name: '土木工程',
degree: '工学学士',
duration: 4,
category: '工学',
description: '培养具备土木工程领域的基本理论和专业知识,能在房屋建筑、地下建筑、桥梁隧道等领域从事设计、施工、管理等工作的高级工程技术人才。',
tags: ['省级特色专业'],
},
{
name: '工程力学',
degree: '工学学士',
duration: 4,
category: '工学',
description: '培养具备工程力学的基本理论和基本技能,能在工程结构分析、强度计算、优化设计等领域从事技术工作的专门人才。',
tags: ['省级重点学科'],
},
{
name: '建筑环境与能源应用工程',
degree: '工学学士',
duration: 4,
category: '工学',
description: '培养具备建筑环境与能源应用工程领域的专业知识,能在暖通空调、燃气供应、建筑节能等领域从事设计、施工、管理等工作的高级工程技术人才。',
tags: ['省级特色专业', '绿色低碳'],
},
{
name: '油气储运工程',
degree: '工学学士',
duration: 4,
category: '工学',
description: '培养具备油气储运工程方面的基本理论和基本知识,能在油气储存、运输、配送等领域从事设计、施工、管理等工作的高级工程技术人才。',
tags: ['国家级特色专业', '双一流学科'],
},
],
},
]
const filteredMajors = computed(() => {
let result = majorsData
//
if (selectedCollege.value !== '全部') {
result = result.filter(item => item.college === selectedCollege.value)
}
//
let allMajors: { college: string, major: any }[] = []
result.forEach(item => {
item.majors.forEach(major => {
allMajors.push({ college: item.college, major })
})
})
//
if (selectedCategory.value !== '全部') {
allMajors = allMajors.filter(item => item.major.category === selectedCategory.value)
}
//
if (searchKeyword.value) {
const keyword = searchKeyword.value.toLowerCase()
allMajors = allMajors.filter(item =>
item.major.name.toLowerCase().includes(keyword) ||
item.major.description.toLowerCase().includes(keyword),
)
}
return allMajors
})
</script>
<template>
<div class="rounded-lg bg-white p-5 shadow-sm dark:bg-slate-800">
<div class="mb-4 flex items-center justify-between">
<h2 class="border-l-4 border-orange-500 pl-3 text-lg text-gray-900 font-bold dark:text-white">
开设专业
</h2>
</div>
<!-- 搜索和筛选 -->
<div class="mb-5 space-y-3">
<!-- 搜索框 -->
<div class="relative">
<input
v-model="searchKeyword"
type="text"
placeholder="搜索专业名称或描述..."
class="w-full rounded border border-gray-200 bg-white px-4 py-2 pl-10 text-sm dark:border-slate-600 dark:bg-slate-700 dark:text-white"
>
<span class="absolute left-3 top-2.5 text-gray-400">🔍</span>
</div>
<!-- 筛选条件 -->
<div class="flex flex-wrap gap-3">
<select
v-model="selectedCollege"
class="rounded border border-gray-200 bg-white px-3 py-2 text-sm dark:border-slate-600 dark:bg-slate-700 dark:text-white"
>
<option v-for="college in colleges" :key="college" :value="college">
{{ college }}
</option>
</select>
<select
v-model="selectedCategory"
class="rounded border border-gray-200 bg-white px-3 py-2 text-sm dark:border-slate-600 dark:bg-slate-700 dark:text-white"
>
<option v-for="category in categories" :key="category" :value="category">
{{ category }}
</option>
</select>
<div class="ml-auto text-sm text-gray-500 dark:text-gray-400">
共找到 <span class="font-bold text-orange-600 dark:text-orange-400">{{ filteredMajors.length }}</span> 个专业
</div>
</div>
</div>
<!-- 专业列表 -->
<div v-if="filteredMajors.length > 0" class="space-y-4">
<div
v-for="(item, idx) in filteredMajors"
:key="idx"
class="overflow-hidden rounded-lg border border-gray-200 dark:border-slate-700"
>
<div class="bg-gray-50 px-4 py-2 text-sm font-medium text-gray-700 dark:bg-slate-700 dark:text-gray-200">
{{ item.college }}
</div>
<div class="p-4">
<div class="mb-2 flex items-start justify-between">
<h3 class="text-base font-bold text-gray-900 dark:text-white">
{{ item.major.name }}
</h3>
<span class="flex-shrink-0 rounded bg-blue-100 px-2 py-1 text-xs text-blue-700 dark:bg-blue-900/30 dark:text-blue-300">
{{ item.major.degree }}
</span>
</div>
<div class="mb-3 flex gap-2 text-sm text-gray-500 dark:text-gray-400">
<span>学制{{ item.major.duration }}</span>
<span>类别{{ item.major.category }}</span>
</div>
<p class="mb-3 text-sm text-gray-600 leading-relaxed dark:text-gray-300">
{{ item.major.description }}
</p>
<div class="flex flex-wrap gap-2">
<span
v-for="(tag, tagIdx) in item.major.tags"
:key="tagIdx"
class="rounded bg-orange-100 px-2 py-0.5 text-xs text-orange-700 dark:bg-orange-900/30 dark:text-orange-300"
>
{{ tag }}
</span>
</div>
</div>
</div>
</div>
<!-- 数据为空提示 -->
<div v-else class="py-12 text-center text-gray-500 dark:text-gray-400">
<div class="mb-2 text-4xl">
📚
</div>
<p>未找到符合条件的专业</p>
<p class="text-xs">
请尝试调整筛选条件或搜索关键词
</p>
</div>
<!-- 说明 -->
<div class="mt-4 rounded-lg bg-orange-50 p-4 text-xs text-gray-600 dark:bg-slate-700/30 dark:text-gray-300">
<p class="mb-1 font-bold">
💡 专业说明
</p>
<ul class="list-inside list-disc space-y-1">
<li>专业设置以教育部最新公布为准</li>
<li>点击专业标签可查看详细信息</li>
<li>建议结合个人兴趣和就业前景选择专业</li>
</ul>
</div>
</div>
</template>

View File

@ -0,0 +1,488 @@
<script setup lang="ts">
import { ref, computed } from 'vue'
interface Ranking {
label: string
value: number
trend?: 'up' | 'down' | 'flat'
}
interface Major {
college: string
majors: string[]
}
interface CampusFacility {
name: string
icon: string
images: string[]
info: string
}
const activeLocationTab = ref('唐岛湾校区')
const selectedCampus = ref('唐岛湾校区')
const showDetailModal = ref(false)
const campusOptions = ['唐岛湾校区', '东营科教园区', '古镇口校区']
const rankings: Ranking[] = [
{ label: '软科综合', value: 67 },
{ label: '校友会综合', value: 80 },
{ label: 'US世界', value: 500 },
{ label: '人气值排名', value: 70 },
]
const departments: Major[] = [
{ college: '地球科学与技术学院', majors: ['勘查技术与工程', '地质学', '资源勘查工程', '地球物理学'] },
{ college: '石油工程学院', majors: ['海洋油气工程', '石油工程', '碳储科学与工程'] },
{ college: '化学化工学院', majors: ['化学', '化学工程与工艺', '应用化学', '环境工程', '能源化学工程'] },
{ college: '机电工程学院', majors: ['安全工程', '工业设计', '智能制造工程', '机械设计制造及其自动化'] },
{ college: '储运与建筑工程学院', majors: ['土木工程', '工程力学', '建筑环境与能源应用工程', '油气储运工程'] },
]
const campusFacilities = ref<CampusFacility[]>([
{
name: '唐岛湾校区',
icon: '🏫',
images: [
'https://images.unsplash.com/photo-1562774053-701939374585?w=400&h=300&fit=crop',
'https://images.unsplash.com/photo-1606761568499-6d2451b23c66?w=400&h=300&fit=crop',
],
info: '位于青岛市黄岛区占地面积3000亩拥有现代化的教学设施和优美的校园环境。',
},
{
name: '东营科教园区',
icon: '🏛️',
images: [
'https://images.unsplash.com/photo-1541339907198-e08756dedf3f?w=400&h=300&fit=crop',
'https://images.unsplash.com/photo-1591380542610-f6263b6884b3?w=400&h=300&fit=crop',
],
info: '位于东营市,是学校的发源地,历史悠久,承载着学校的传统与精神。',
},
{
name: '古镇口校区',
icon: '🏢',
images: [
'https://images.unsplash.com/photo-1580582932707-520aed937b7b?w=400&h=300&fit=crop',
'https://images.unsplash.com/photo-1541339907198-e08756dedf3f?w=400&h=300&fit=crop',
],
info: '位于青岛西海岸新区,重点发展海洋科学与工程技术等新兴学科。',
},
])
const sceneryImages = [
'https://images.unsplash.com/photo-1562774053-701939374585?w=400&h=300&fit=crop',
'https://images.unsplash.com/photo-1606761568499-6d2451b23c66?w=400&h=300&fit=crop',
'https://images.unsplash.com/photo-1591380542610-f6263b6884b3?w=400&h=300&fit=crop',
'https://images.unsplash.com/photo-1580582932707-520aed937b7b?w=400&h=300&fit=crop',
]
const locationInfo = computed(() => {
return activeLocationTab.value === '唐岛湾校区'
? { address: '山东省青岛市黄岛区长江西路66号', details: '附近3km内分布着 58个餐饮场所...' }
: { address: '山东省东营市...', details: '老校区历史悠久...' }
})
const currentCampusFacilities = computed(() => {
return campusFacilities.value.find(c => c.name === selectedCampus.value)
})
</script>
<template>
<div class="space-y-5">
<!-- 1. 基本信息模块 -->
<section class="rounded-lg bg-white p-5 shadow-sm dark:bg-slate-800">
<div class="mb-3 flex items-center justify-between">
<h2 class="border-l-4 border-orange-500 pl-3 text-lg text-gray-900 font-bold dark:text-white">
基本信息
</h2>
<button
class="text-xs text-gray-500 hover:text-orange-500"
@click="showDetailModal = true"
>
[详情]
</button>
</div>
<p class="mb-5 text-sm text-gray-600 leading-relaxed dark:text-gray-300">
中国石油大学华东是教育部直属全国重点大学是国家"211工程"重点建设和开展"985工程优势学科创新平台"建设并建有研究生院的高校之一学校是教育部和五大能源企业集团公司教育部和山东省人民政府共建的高校...
</p>
<!-- 排名与数据 -->
<div class="mb-5 rounded-lg bg-orange-50 p-5 dark:bg-slate-700/50">
<div class="grid grid-cols-2 gap-4 text-center md:grid-cols-4">
<div v-for="(rank, idx) in rankings" :key="idx" class="border-r border-orange-200 last:border-0 dark:border-slate-600">
<div class="text-xl text-orange-600 font-bold dark:text-orange-400">
{{ rank.value }}
</div>
<div class="mt-1 text-xs text-gray-500 dark:text-gray-400">
{{ rank.label }}
</div>
</div>
</div>
</div>
<!-- 地理位置 & 地图 -->
<div class="overflow-hidden border border-gray-200 rounded-lg dark:border-slate-700">
<div class="flex border-b border-gray-200 bg-gray-50 dark:border-slate-700 dark:bg-slate-700">
<button
v-for="tab in ['唐岛湾校区', '东营科教园区', '古镇口校区']" :key="tab"
class="px-4 py-2 text-sm transition-colors"
:class="activeLocationTab === tab ? 'bg-white dark:bg-slate-800 text-orange-500 border-t-2 border-orange-500' : 'text-gray-600 dark:text-gray-400'"
@click="activeLocationTab = tab"
>
{{ tab }}
</button>
</div>
<div class="h-64 flex flex-col md:flex-row">
<!-- 模拟地图 -->
<div class="group relative w-full flex items-center justify-center bg-blue-50 md:w-1/2 dark:bg-slate-900">
<span class="text-4xl text-blue-300">MAP</span>
<div class="absolute inset-0 bg-black/5 transition dark:bg-white/5 group-hover:bg-transparent" />
<div class="absolute bottom-2 right-2 rounded bg-white px-2 py-1 text-xs shadow dark:bg-slate-700">
地图详情 >
</div>
</div>
<!-- 地址详情 -->
<div class="w-full flex flex-col justify-center p-4 md:w-1/2">
<h4 class="mb-2 flex items-center gap-2 text-gray-800 font-bold dark:text-white">
📍 {{ activeLocationTab }}
</h4>
<p class="mb-3 text-sm text-orange-500">
{{ locationInfo.address }}
</p>
<p class="text-xs text-gray-500 leading-relaxed dark:text-gray-400">
{{ locationInfo.details }}
</p>
<div class="mt-4 flex gap-4 text-center">
<div>
<div class="text-sm font-bold dark:text-white">
58
</div><div class="text-xs text-gray-400">
餐饮场所
</div>
</div>
<div>
<div class="text-sm font-bold dark:text-white">
27
</div><div class="text-xs text-gray-400">
酒店
</div>
</div>
<div>
<div class="text-sm font-bold dark:text-white">
7
</div><div class="text-xs text-gray-400">
购物中心
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- 2. 院系设置模块 -->
<section class="rounded-lg bg-white p-5 shadow-sm dark:bg-slate-800">
<div class="mb-3 flex items-center justify-between">
<h2 class="border-l-4 border-orange-500 pl-3 text-lg text-gray-900 font-bold dark:text-white">
院系设置
</h2>
</div>
<div class="overflow-x-auto">
<table class="w-full text-left text-sm">
<thead class="bg-gray-50 text-xs text-gray-500 dark:bg-slate-700 dark:text-gray-400">
<tr>
<th class="w-1/4 px-4 py-2.5">
学院
</th>
<th class="px-4 py-2.5">
专业
</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100 dark:divide-slate-700">
<tr v-for="(dept, idx) in departments" :key="idx" class="hover:bg-gray-50 dark:hover:bg-slate-700/50">
<td class="px-4 py-2.5 text-gray-900 font-medium dark:text-white">
{{ dept.college }}
</td>
<td class="px-4 py-2.5 text-gray-600 dark:text-gray-300">
<div class="flex flex-wrap gap-2">
<span v-for="major in dept.majors" :key="major" class="rounded bg-gray-100 px-2 py-0.5 text-xs dark:bg-slate-600">
{{ major }} <span class="ml-1 text-gray-300">本科</span>
</span>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- 分页模拟 -->
<div class="mt-4 flex justify-center gap-2 text-xs text-gray-500">
<button class="px-2 py-1 hover:text-orange-500">
首页
</button>
<button class="px-2 py-1 hover:text-orange-500">
上一页
</button>
<button class="h-6 w-6 flex items-center justify-center rounded-full bg-orange-500 text-white">
1
</button>
<button class="h-6 w-6 flex items-center justify-center rounded-full hover:bg-gray-100 dark:hover:bg-slate-700">
2
</button>
<button class="px-2 py-1 hover:text-orange-500">
下一页
</button>
</div>
</section>
<!-- 3. 校园风光 -->
<section class="rounded-lg bg-white p-5 shadow-sm dark:bg-slate-800">
<div class="mb-3 flex items-center justify-between">
<h2 class="border-l-4 border-orange-500 pl-3 text-lg text-gray-900 font-bold dark:text-white">
校园风光
</h2>
<a href="#" class="text-xs text-orange-500">更多 ></a>
</div>
<div class="grid grid-cols-2 gap-3 md:grid-cols-4">
<div v-for="(img, i) in sceneryImages" :key="i" class="group relative h-40 cursor-pointer overflow-hidden rounded-lg">
<img :src="img" class="h-full w-full transform object-cover transition group-hover:scale-110" alt="scenery">
<div class="absolute inset-0 bg-black/10 transition group-hover:bg-black/0" />
</div>
</div>
</section>
<!-- 4. 校园配置 & 奖学金 (两列并排) -->
<div class="grid grid-cols-1 gap-5 md:grid-cols-2">
<!-- 校园配置 -->
<section class="rounded-lg bg-white p-5 shadow-sm dark:bg-slate-800">
<div class="mb-3 flex items-center justify-between">
<h2 class="border-l-4 border-orange-500 pl-3 text-lg text-gray-900 font-bold dark:text-white">
校园配置
</h2>
<!-- 地区下拉切换 -->
<select
v-model="selectedCampus"
class="rounded border border-gray-200 bg-white px-3 py-1.5 text-xs text-gray-600 dark:border-slate-600 dark:bg-slate-700 dark:text-gray-300"
>
<option v-for="campus in campusOptions" :key="campus" :value="campus">
{{ campus }}
</option>
</select>
</div>
<!-- 校区信息 -->
<div v-if="currentCampusFacilities" class="mb-4 rounded-lg bg-gray-50 p-3 dark:bg-slate-700/30">
<p class="text-sm text-gray-600 dark:text-gray-300">
{{ currentCampusFacilities.info }}
</p>
</div>
<!-- 基础设施 -->
<div class="mb-4 grid grid-cols-3 gap-3">
<div class="flex flex-col items-center rounded bg-gray-50 p-2 dark:bg-slate-700/30">
<span class="mb-1 text-2xl">🛏</span>
<span class="text-xs text-gray-600 dark:text-gray-300">4/</span>
</div>
<div class="flex flex-col items-center rounded bg-gray-50 p-2 dark:bg-slate-700/30">
<span class="mb-1 text-2xl">🍚</span>
<span class="text-xs text-gray-600 dark:text-gray-300">5个食堂</span>
</div>
<div class="flex flex-col items-center rounded bg-gray-50 p-2 dark:bg-slate-700/30">
<span class="mb-1 text-2xl">🪑</span>
<span class="text-xs text-gray-600 dark:text-gray-300">上床下桌</span>
</div>
<div class="flex flex-col items-center rounded bg-gray-50 p-2 dark:bg-slate-700/30">
<span class="mb-1 text-2xl">🚿</span>
<span class="text-xs text-gray-600 dark:text-gray-300">独立卫浴</span>
</div>
<div class="flex flex-col items-center rounded bg-gray-50 p-2 dark:bg-slate-700/30">
<span class="mb-1 text-2xl"></span>
<span class="text-xs text-gray-600 dark:text-gray-300">有空调</span>
</div>
<div class="flex flex-col items-center rounded bg-gray-50 p-2 dark:bg-slate-700/30">
<span class="mb-1 text-2xl">🏊</span>
<span class="text-xs text-gray-600 dark:text-gray-300">有游泳馆</span>
</div>
</div>
<!-- 校区图片 -->
<div v-if="currentCampusFacilities" class="flex gap-3 overflow-x-auto pb-2">
<div
v-for="(img, idx) in currentCampusFacilities.images"
:key="idx"
class="h-28 w-28 flex-shrink-0 overflow-hidden rounded-lg bg-gray-200 dark:bg-slate-700"
>
<img :src="img" class="h-full w-full object-cover" alt="校园图片">
</div>
</div>
</section>
<!-- 奖学金设置 -->
<section class="rounded-lg bg-white p-5 shadow-sm dark:bg-slate-800">
<div class="mb-3 flex items-center justify-between">
<h2 class="border-l-4 border-orange-500 pl-3 text-lg text-gray-900 font-bold dark:text-white">
奖学金设置
</h2>
<span class="text-xs text-gray-400">更新: 2024-06-18</span>
</div>
<div class="custom-scrollbar h-48 overflow-y-auto pr-2 text-sm text-gray-600 space-y-4 dark:text-gray-300">
<div>
<h4 class="mb-1 text-gray-800 font-bold dark:text-gray-200">
奖学金设置
</h4>
<p class="text-xs leading-relaxed">
目前学校已经建立起以各类奖学金助学金助学贷款勤工助学困难补助为主体的多元化资助体系
</p>
</div>
<div>
<h4 class="mb-1 text-gray-800 font-bold dark:text-gray-200">
困难资助办法
</h4>
<p class="text-xs leading-relaxed">
家庭经济特别困难的新生如暂时筹集不齐学费和住宿费可在报到当天通过学校开设的"绿色通道"办理入学手续
</p>
</div>
</div>
</section>
</div>
<!-- 详情模态框 -->
<Teleport to="body">
<Transition name="modal">
<div
v-if="showDetailModal"
class="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4"
@click.self="showDetailModal = false"
>
<div class="max-h-[80vh] w-full max-w-3xl overflow-y-auto rounded-lg bg-white p-6 shadow-xl dark:bg-slate-800">
<div class="mb-4 flex items-center justify-between">
<h3 class="text-xl font-bold text-gray-900 dark:text-white">
学校详细信息
</h3>
<button
class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-200"
@click="showDetailModal = false"
>
</button>
</div>
<div class="space-y-4 text-sm text-gray-600 dark:text-gray-300">
<div>
<h4 class="mb-2 font-bold text-gray-900 dark:text-white">
学校简介
</h4>
<p class="leading-relaxed">
中国石油大学华东是教育部直属全国重点大学是国家"211工程"重点建设和开展"985工程优势学科创新平台"建设并建有研究生院的高校之一学校是教育部和五大能源企业集团公司教育部和山东省人民政府共建的高校是石油石化行业高层次人才培养的重要基地被誉为"石油科技人才的摇篮"
</p>
</div>
<div>
<h4 class="mb-2 font-bold text-gray-900 dark:text-white">
历史沿革
</h4>
<p class="leading-relaxed">
学校始建于1953年历经北京石油学院华东石油学院石油大学华东三个发展阶段2000经教育部批准石油大学华东开始独立办学2005学校更名为中国石油大学华东2017学校进入国家"双一流"建设高校行列
</p>
</div>
<div>
<h4 class="mb-2 font-bold text-gray-900 dark:text-white">
学科建设
</h4>
<ul class="list-inside list-disc space-y-1">
<li>博士学位授权一级学科16</li>
<li>硕士学位授权一级学科33</li>
<li>国家重点学科5</li>
<li>国家重点培育学科2</li>
<li>博士后流动站11</li>
</ul>
</div>
<div>
<h4 class="mb-2 font-bold text-gray-900 dark:text-white">
师资力量
</h4>
<p class="leading-relaxed">
学校现有教职工3100余人其中专任教师2000余人教授副教授1100余人拥有两院院士8人含双聘国家杰出青年科学基金获得者"长江学者奖励计划"特聘教授国家"万人计划"领军人才等国家级人才70余人
</p>
</div>
<div>
<h4 class="mb-2 font-bold text-gray-900 dark:text-white">
办学条件
</h4>
<ul class="list-inside list-disc space-y-1">
<li>占地面积5000余亩</li>
<li>建筑面积140余万平方米</li>
<li>图书馆藏书320余万册</li>
<li>教学科研仪器设备总值15亿元</li>
</ul>
</div>
<div>
<h4 class="mb-2 font-bold text-gray-900 dark:text-white">
就业情况
</h4>
<p class="leading-relaxed">
学校毕业生就业率始终保持90%以上石油石化相关专业毕业生就业率达到98%以上毕业生主要就业于中石油中石化中海油等国有大型企业以及各类民营企业和科研院所
</p>
</div>
</div>
<div class="mt-6 flex justify-end">
<button
class="rounded bg-orange-500 px-6 py-2 text-sm font-medium text-white transition-colors hover:bg-orange-600"
@click="showDetailModal = false"
>
关闭
</button>
</div>
</div>
</div>
</Transition>
</Teleport>
</div>
</template>
<style scoped>
/* 模态框动画 */
.modal-enter-active,
.modal-leave-active {
transition: opacity 0.3s ease;
}
.modal-enter-from,
.modal-leave-to {
opacity: 0;
}
.modal-enter-active .bg-white,
.modal-leave-active .bg-white {
transition: transform 0.3s ease;
}
.modal-enter-from .bg-white,
.modal-leave-to .bg-white {
transform: scale(0.9);
}
/* 自定义滚动条样式 */
.custom-scrollbar::-webkit-scrollbar {
width: 4px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: transparent;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: #cbd5e1;
border-radius: 2px;
}
.dark .custom-scrollbar::-webkit-scrollbar-thumb {
background: #475569;
}
</style>

View File

@ -1,13 +1,14 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref } from 'vue' import { computed, ref, onMounted, onUnmounted } from 'vue'
//
import SchoolOverview from '~/components/school/SchoolOverview.vue'
import HistoricalScores from '~/components/school/HistoricalScores.vue'
import EnrollmentPlan from '~/components/school/EnrollmentPlan.vue'
import Majors from '~/components/school/Majors.vue'
import AdmissionPrediction from '~/components/school/AdmissionPrediction.vue'
// --- --- // --- ---
// interface StatItem {
// label: string;
// value: string | number;
// icon?: string;
// }
interface Ranking { interface Ranking {
label: string label: string
value: number value: number
@ -24,6 +25,17 @@ const route = useRoute('/school/[schoolCode]')
const schoolCode = ref('') const schoolCode = ref('')
const activeLocationTab = ref('唐岛湾校区') const activeLocationTab = ref('唐岛湾校区')
const activeNavTab = ref('学校概况')
//
const currentSlide = ref(0)
const carouselInterval = ref<number | null>(null)
const bannerImages = [
'https://images.unsplash.com/photo-1541339907198-e08756dedf3f?w=1200&h=400&fit=crop',
'https://images.unsplash.com/photo-1562774053-701939374585?w=1200&h=400&fit=crop',
'https://images.unsplash.com/photo-1606761568499-6d2451b23c66?w=1200&h=400&fit=crop',
'https://images.unsplash.com/photo-1591380542610-f6263b6884b3?w=1200&h=400&fit=crop',
]
// //
// const stats: StatItem[] = [ // const stats: StatItem[] = [
@ -61,13 +73,12 @@ const facilities = [
{ name: '有游泳馆', icon: '🏊' }, { name: '有游泳馆', icon: '🏊' },
] ]
// (使) //
const bgHeader = 'https://via.placeholder.com/1200x300/e6f2ff/0056b3?text=University+Header'
const sceneryImages = [ const sceneryImages = [
'https://via.placeholder.com/300x200/ffedcc/e67e22?text=Scenery+1', 'https://images.unsplash.com/photo-1564981797816-1043664bf78d?w=400&h=300&fit=crop',
'https://via.placeholder.com/300x200/d4edda/155724?text=Scenery+2', 'https://images.unsplash.com/photo-1523050854058-8df90110c9f1?w=400&h=300&fit=crop',
'https://via.placeholder.com/300x200/f8d7da/721c24?text=Scenery+3', 'https://images.unsplash.com/photo-1541829070764-84a7d30dd3f3?w=400&h=300&fit=crop',
'https://via.placeholder.com/300x200/cce5ff/004085?text=Scenery+4', 'https://images.unsplash.com/photo-1607237138185-eedd9c632b0b?w=400&h=300&fit=crop',
] ]
// //
@ -77,6 +88,38 @@ const locationInfo = computed(() => {
: { address: '山东省东营市...', details: '老校区历史悠久...' } : { address: '山东省东营市...', details: '老校区历史悠久...' }
}) })
//
const nextSlide = () => {
currentSlide.value = (currentSlide.value + 1) % bannerImages.length
}
const prevSlide = () => {
currentSlide.value = (currentSlide.value - 1 + bannerImages.length) % bannerImages.length
}
const goToSlide = (index: number) => {
currentSlide.value = index
}
const startCarousel = () => {
carouselInterval.value = window.setInterval(nextSlide, 4000)
}
const stopCarousel = () => {
if (carouselInterval.value) {
clearInterval(carouselInterval.value)
carouselInterval.value = null
}
}
onMounted(() => {
startCarousel()
})
onUnmounted(() => {
stopCarousel()
})
watchEffect(() => { watchEffect(() => {
schoolCode.value = route.params.schoolCode schoolCode.value = route.params.schoolCode
}) })
@ -113,21 +156,78 @@ useHead({
<!-- 导航菜单 --> <!-- 导航菜单 -->
<nav class="scrollbar-hide flex overflow-x-auto pb-2 text-sm text-gray-500 font-medium space-x-8 sm:pb-0 dark:text-gray-400"> <nav class="scrollbar-hide flex overflow-x-auto pb-2 text-sm text-gray-500 font-medium space-x-8 sm:pb-0 dark:text-gray-400">
<a href="#" class="whitespace-nowrap border-b-2 border-orange-500 pb-3 text-orange-600 dark:text-orange-400">学校概况</a> <button
<a href="#" class="whitespace-nowrap pb-3 hover:text-gray-700 dark:hover:text-gray-200">历年分数</a> class="whitespace-nowrap border-b-2 pb-3 transition-colors"
<a href="#" class="whitespace-nowrap pb-3 hover:text-gray-700 dark:hover:text-gray-200">招生计划</a> :class="activeNavTab === '学校概况' ? 'border-orange-500 text-orange-600 dark:text-orange-400' : 'border-transparent hover:text-gray-700 dark:hover:text-gray-200'"
<a href="#" class="whitespace-nowrap pb-3 hover:text-gray-700 dark:hover:text-gray-200">开设专业</a> @click="activeNavTab = '学校概况'"
<a href="#" class="whitespace-nowrap pb-3 hover:text-gray-700 dark:hover:text-gray-200">录取预测</a> >
学校概况
</button>
<button
class="whitespace-nowrap border-b-2 pb-3 transition-colors"
:class="activeNavTab === '历年分数' ? 'border-orange-500 text-orange-600 dark:text-orange-400' : 'border-transparent hover:text-gray-700 dark:hover:text-gray-200'"
@click="activeNavTab = '历年分数'"
>
历年分数
</button>
<button
class="whitespace-nowrap border-b-2 pb-3 transition-colors"
:class="activeNavTab === '招生计划' ? 'border-orange-500 text-orange-600 dark:text-orange-400' : 'border-transparent hover:text-gray-700 dark:hover:text-gray-200'"
@click="activeNavTab = '招生计划'"
>
招生计划
</button>
<button
class="whitespace-nowrap border-b-2 pb-3 transition-colors"
:class="activeNavTab === '开设专业' ? 'border-orange-500 text-orange-600 dark:text-orange-400' : 'border-transparent hover:text-gray-700 dark:hover:text-gray-200'"
@click="activeNavTab = '开设专业'"
>
开设专业
</button>
<button
class="whitespace-nowrap border-b-2 pb-3 transition-colors"
:class="activeNavTab === '录取预测' ? 'border-orange-500 text-orange-600 dark:text-orange-400' : 'border-transparent hover:text-gray-700 dark:hover:text-gray-200'"
@click="activeNavTab = '录取预测'"
>
录取预测
</button>
</nav> </nav>
</div> </div>
</header> </header>
<!-- 主要内容区域 --> <!-- 主要内容区域 -->
<main class="mx-auto max-w-7xl px-4 py-6 lg:px-8 sm:px-6"> <!-- mx-auto max-w-7xl px-4 py-6 lg:px-8 sm:px-6 -->
<main class="mx-auto max-w-7xl">
<!-- 顶部 Banner 信息卡片 --> <!-- 顶部 Banner 信息卡片 -->
<div class="mb-6 overflow-hidden rounded-lg bg-white shadow dark:bg-slate-800"> <div class="mb-6 overflow-hidden rounded-lg bg-white shadow dark:bg-slate-800">
<!-- Banner 图片 --> <!-- 轮播图 -->
<div class="relative h-48 bg-cover bg-center md:h-64" :style="{ backgroundImage: `url(${bgHeader})` }"> <div class="relative h-48 bg-cover bg-center md:h-64" :style="{ backgroundImage: `url(${bannerImages[currentSlide]})` }">
<!-- 左右切换按钮 -->
<button
class="absolute left-2 top-1/2 -translate-y-1/2 rounded-full bg-black/30 p-2 text-white backdrop-blur-sm transition hover:bg-black/50"
@click="prevSlide"
>
</button>
<button
class="absolute right-2 top-1/2 -translate-y-1/2 rounded-full bg-black/30 p-2 text-white backdrop-blur-sm transition hover:bg-black/50"
@click="nextSlide"
>
</button>
<!-- 轮播指示器 -->
<div class="absolute bottom-4 left-1/2 flex -translate-x-1/2 gap-2">
<button
v-for="(img, index) in bannerImages"
:key="index"
class="h-2 w-2 rounded-full transition-all"
:class="currentSlide === index ? 'w-8 bg-white' : 'bg-white/50'"
@click="goToSlide(index)"
/>
</div>
<!-- 右上角按钮 -->
<div class="absolute bottom-4 right-4 flex gap-2"> <div class="absolute bottom-4 right-4 flex gap-2">
<button class="rounded bg-black/50 px-3 py-1 text-sm text-white backdrop-blur-sm hover:bg-black/70"> <button class="rounded bg-black/50 px-3 py-1 text-sm text-white backdrop-blur-sm hover:bg-black/70">
📺 视频 📺 视频
@ -212,225 +312,13 @@ useHead({
<div class="grid grid-cols-1 gap-6 lg:grid-cols-4"> <div class="grid grid-cols-1 gap-6 lg:grid-cols-4">
<!-- 左侧/主体内容 (占3份宽度) --> <!-- 左侧/主体内容 (占3份宽度) -->
<div class="lg:col-span-3 space-y-6"> <div class="lg:col-span-3 space-y-6">
<!-- 1. 基本信息模块 --> <!-- 子页面组件 -->
<section class="rounded-lg bg-white p-6 shadow-sm dark:bg-slate-800"> <SchoolOverview v-show="activeNavTab === '学校概况'" />
<div class="mb-4 flex items-center justify-between"> <HistoricalScores v-show="activeNavTab === '历年分数'" />
<h2 class="border-l-4 border-orange-500 pl-3 text-lg text-gray-900 font-bold dark:text-white"> <EnrollmentPlan v-show="activeNavTab === '招生计划'" />
基本信息 <Majors v-show="activeNavTab === '开设专业'" />
</h2> <AdmissionPrediction v-show="activeNavTab === '录取预测'" />
<a href="#" class="text-xs text-gray-500 hover:text-orange-500">[详情]</a>
</div>
<p class="mb-6 text-sm text-gray-600 leading-relaxed dark:text-gray-300">
中国石油大学华东是教育部直属全国重点大学是国家211工程重点建设和开展985工程优势学科创新平台建设并建有研究生院的高校之一学校是教育部和五大能源企业集团公司教育部和山东省人民政府共建的高校...
</p>
<!-- 排名与数据 -->
<div class="mb-6 rounded-lg bg-orange-50 p-4 dark:bg-slate-700/50">
<div class="grid grid-cols-2 gap-4 text-center md:grid-cols-4">
<div v-for="(rank, idx) in rankings" :key="idx" class="border-r border-orange-200 last:border-0 dark:border-slate-600">
<div class="text-xl text-orange-600 font-bold dark:text-orange-400">
{{ rank.value }}
</div>
<div class="mt-1 text-xs text-gray-500 dark:text-gray-400">
{{ rank.label }}
</div>
</div>
</div>
</div>
<!-- 地理位置 & 地图 -->
<div class="overflow-hidden border border-gray-200 rounded-lg dark:border-slate-700">
<div class="flex border-b border-gray-200 bg-gray-50 dark:border-slate-700 dark:bg-slate-700">
<button
v-for="tab in ['唐岛湾校区', '东营科教园区', '古镇口校区']" :key="tab"
class="px-4 py-2 text-sm transition-colors"
:class="activeLocationTab === tab ? 'bg-white dark:bg-slate-800 text-orange-500 border-t-2 border-orange-500' : 'text-gray-600 dark:text-gray-400'"
@click="activeLocationTab = tab"
>
{{ tab }}
</button>
</div>
<div class="h-64 flex flex-col md:flex-row">
<!-- 模拟地图 -->
<div class="group relative w-full flex items-center justify-center bg-blue-50 md:w-1/2 dark:bg-slate-900">
<span class="text-4xl text-blue-300">MAP</span>
<div class="absolute inset-0 bg-black/5 transition dark:bg-white/5 group-hover:bg-transparent" />
<div class="absolute bottom-2 right-2 rounded bg-white px-2 py-1 text-xs shadow dark:bg-slate-700">
地图详情 >
</div>
</div>
<!-- 地址详情 -->
<div class="w-full flex flex-col justify-center p-4 md:w-1/2">
<h4 class="mb-2 flex items-center gap-2 text-gray-800 font-bold dark:text-white">
📍 {{ activeLocationTab }}
</h4>
<p class="mb-3 text-sm text-orange-500">
{{ locationInfo.address }}
</p>
<p class="text-xs text-gray-500 leading-relaxed dark:text-gray-400">
{{ locationInfo.details }}
</p>
<div class="mt-4 flex gap-4 text-center">
<div>
<div class="text-sm font-bold dark:text-white">
58
</div><div class="text-xs text-gray-400">
餐饮场所
</div>
</div>
<div>
<div class="text-sm font-bold dark:text-white">
27
</div><div class="text-xs text-gray-400">
酒店
</div>
</div>
<div>
<div class="text-sm font-bold dark:text-white">
7
</div><div class="text-xs text-gray-400">
购物中心
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- 2. 院系设置模块 -->
<section class="rounded-lg bg-white p-6 shadow-sm dark:bg-slate-800">
<div class="mb-4 flex items-center justify-between">
<h2 class="border-l-4 border-orange-500 pl-3 text-lg text-gray-900 font-bold dark:text-white">
院系设置
</h2>
</div>
<div class="overflow-x-auto">
<table class="w-full text-left text-sm">
<thead class="bg-gray-50 text-xs text-gray-500 dark:bg-slate-700 dark:text-gray-400">
<tr>
<th class="w-1/4 px-4 py-3">
学院
</th>
<th class="px-4 py-3">
专业
</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100 dark:divide-slate-700">
<tr v-for="(dept, idx) in departments" :key="idx" class="hover:bg-gray-50 dark:hover:bg-slate-700/50">
<td class="px-4 py-3 text-gray-900 font-medium dark:text-white">
{{ dept.college }}
</td>
<td class="px-4 py-3 text-gray-600 dark:text-gray-300">
<div class="flex flex-wrap gap-2">
<span v-for="major in dept.majors" :key="major" class="rounded bg-gray-100 px-2 py-0.5 text-xs dark:bg-slate-600">
{{ major }} <span class="ml-1 text-gray-300">本科</span>
</span>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- 分页模拟 -->
<div class="mt-4 flex justify-center gap-2 text-xs text-gray-500">
<button class="px-2 py-1 hover:text-orange-500">
首页
</button>
<button class="px-2 py-1 hover:text-orange-500">
上一页
</button>
<button class="h-6 w-6 flex items-center justify-center rounded-full bg-orange-500 text-white">
1
</button>
<button class="h-6 w-6 flex items-center justify-center rounded-full hover:bg-gray-100 dark:hover:bg-slate-700">
2
</button>
<button class="px-2 py-1 hover:text-orange-500">
下一页
</button>
</div>
</section>
<!-- 3. 校园风光 -->
<section class="rounded-lg bg-white p-6 shadow-sm dark:bg-slate-800">
<div class="mb-4 flex items-center justify-between">
<h2 class="border-l-4 border-orange-500 pl-3 text-lg text-gray-900 font-bold dark:text-white">
校园风光
</h2>
<a href="#" class="text-xs text-orange-500">更多 ></a>
</div>
<div class="grid grid-cols-2 gap-4 md:grid-cols-4">
<div v-for="(img, i) in sceneryImages" :key="i" class="group relative h-32 cursor-pointer overflow-hidden rounded-lg">
<img :src="img" class="h-full w-full transform object-cover transition group-hover:scale-110" alt="scenery">
<div class="absolute inset-0 bg-black/10 transition group-hover:bg-black/0" />
</div>
</div>
</section>
<!-- 4. 校园配置 & 奖学金 (两列并排) -->
<div class="grid grid-cols-1 gap-6 md:grid-cols-2">
<!-- 校园配置 -->
<section class="rounded-lg bg-white p-6 shadow-sm dark:bg-slate-800">
<div class="mb-4 flex items-center justify-between">
<h2 class="border-l-4 border-orange-500 pl-3 text-lg text-gray-900 font-bold dark:text-white">
校园配置
</h2>
<!-- 简单下拉模拟 -->
<span class="cursor-pointer rounded bg-gray-100 px-2 py-1 text-xs text-gray-500 dark:bg-slate-700">唐岛湾校区 </span>
</div>
<div class="grid grid-cols-3 mb-4 gap-4">
<div v-for="item in facilities" :key="item.name" class="flex flex-col items-center rounded bg-gray-50 p-2 dark:bg-slate-700/30">
<span class="mb-1 text-2xl">{{ item.icon }}</span>
<span class="text-xs text-gray-600 dark:text-gray-300">{{ item.name }}</span>
</div>
</div>
<div class="flex gap-2 overflow-x-auto pb-2">
<div class="h-24 w-24 flex flex-shrink-0 items-center justify-center rounded bg-gray-200 text-xs text-gray-400 dark:bg-slate-700">
食堂图片
</div>
<div class="h-24 w-24 flex flex-shrink-0 items-center justify-center rounded bg-gray-200 text-xs text-gray-400 dark:bg-slate-700">
宿舍图片
</div>
</div>
</section>
<!-- 奖学金设置 -->
<section class="rounded-lg bg-white p-6 shadow-sm dark:bg-slate-800">
<div class="mb-4 flex items-center justify-between">
<h2 class="border-l-4 border-orange-500 pl-3 text-lg text-gray-900 font-bold dark:text-white">
奖学金设置
</h2>
<span class="text-xs text-gray-400">更新: 2024-06-18</span>
</div>
<div class="custom-scrollbar h-48 overflow-y-auto pr-2 text-sm text-gray-600 space-y-4 dark:text-gray-300">
<div>
<h4 class="mb-1 text-gray-800 font-bold dark:text-gray-200">
奖学金设置
</h4>
<p class="text-xs leading-relaxed">
目前学校已经建立起以各类奖学金助学金助学贷款勤工助学困难补助为主体的多元化资助体系
</p>
</div>
<div>
<h4 class="mb-1 text-gray-800 font-bold dark:text-gray-200">
困难资助办法
</h4>
<p class="text-xs leading-relaxed">
家庭经济特别困难的新生如暂时筹集不齐学费和住宿费可在报到当天通过学校开设的绿色通道办理入学手续
</p>
</div>
</div>
</section>
</div>
</div> </div>
<!-- 右侧侧边栏 (保留男女比例其他已移除) --> <!-- 右侧侧边栏 (保留男女比例其他已移除) -->
<div class="hidden lg:col-span-1 lg:block space-y-6"> <div class="hidden lg:col-span-1 lg:block space-y-6">
<!-- 男女比例 --> <!-- 男女比例 -->