AIzhushou-screen/src/views/index/RightCenter.vue

395 lines
7.9 KiB
Vue
Raw Normal View History

2025-08-12 14:39:25 +08:00
<script setup lang="ts">
2025-08-15 15:56:13 +08:00
import { ref, onMounted } from "vue";
import { majorRanking, highSchoolRanking } from "@/api";
import top1Img from "@/assets/img/zheke/top1.png";
import top2Img from "@/assets/img/zheke/top2.png";
import top3Img from "@/assets/img/zheke/top3.png";
import top4Img from "@/assets/img/zheke/top4.png";
2025-08-12 14:39:25 +08:00
interface RankingItem {
2025-08-15 15:56:13 +08:00
rank: number;
name: string;
count: number;
}
interface MajorRankingItem {
2025-08-12 14:39:25 +08:00
rank: number;
major: string;
count: number;
}
2025-08-15 15:56:13 +08:00
interface SchoolRankingItem {
rank: number;
school: string;
count: number;
}
2025-08-12 14:39:25 +08:00
// 排行数据
const rankingData = ref<RankingItem[]>([]);
2025-08-15 15:56:13 +08:00
// 排行类型
const rankingTypes = [
{ label: "用户意向专业排行", value: "major" },
{ label: "用户生源高中排行", value: "school" },
];
const selectedRankingType = ref("major");
2025-08-12 14:39:25 +08:00
// 获取排行数据
const fetchRankingData = () => {
2025-08-15 15:56:13 +08:00
if (selectedRankingType.value === "major") {
majorRanking().then((res) => {
if (res.success) {
// 转换数据格式
rankingData.value = res.data.map((item: MajorRankingItem) => ({
rank: item.rank,
name: item.major,
count: item.count,
}));
}
});
} else {
highSchoolRanking().then((res) => {
if (res.success) {
// 转换数据格式
rankingData.value = res.data.map((item: SchoolRankingItem) => ({
rank: item.rank,
name: item.school,
count: item.count,
}));
}
});
}
};
// 处理排行类型变化
const handleRankingTypeChange = () => {
fetchRankingData();
};
// 计算最大用户数,用于进度条百分比计算
const maxCount = ref(0);
const getProgressWidth = (count: number) => {
if (maxCount.value === 0) return "0%";
return `${(count / maxCount.value) * 100}%`;
};
// 监听数据变化,计算最大值
const updateMaxCount = () => {
if (rankingData.value.length > 0) {
maxCount.value = Math.max(...rankingData.value.map((item) => item.count));
}
};
// 模拟数据
const initMockData = () => {
const mockData = [];
for (let i = 1; i <= 10; i++) {
mockData.push({
rank: i,
name:
selectedRankingType.value === "major" ? "视觉传达设计" : "视觉传达设计",
count: 3954,
});
}
rankingData.value = mockData;
updateMaxCount();
};
// 年份筛选选项
const yearOptions = ref([{ label: "年份筛选", value: "all" }]);
// 学院筛选
const selectedYear = ref("all");
// 处理年份筛选变化
const handleYearChange = () => {
console.log("年份筛选");
};
// 获取排名图片
const getRankImage = (rank: number) => {
switch (rank) {
case 1: return top1Img;
case 2: return top2Img;
case 3: return top3Img;
default: return top4Img;
}
2025-08-12 14:39:25 +08:00
};
onMounted(() => {
2025-08-15 15:56:13 +08:00
// 获取真实数据
2025-08-12 14:39:25 +08:00
fetchRankingData();
2025-08-15 15:56:13 +08:00
// 如果没有真实数据,使用模拟数据
setTimeout(() => {
if (rankingData.value.length === 0) {
initMockData();
} else {
updateMaxCount();
}
}, 500);
2025-08-12 14:39:25 +08:00
});
</script>
<template>
2025-08-15 15:56:13 +08:00
<div class="ranking-container">
<!-- 排行类型切换 -->
<div class="ranking-tabs">
<div
v-for="type in rankingTypes"
:key="type.value"
class="tab-item"
:class="{ active: selectedRankingType === type.value }"
@click="
selectedRankingType = type.value;
handleRankingTypeChange();
"
>
{{ type.label }}
</div>
</div>
<!-- 排行榜内容 -->
2025-08-12 14:39:25 +08:00
<div class="ranking-list">
2025-08-15 15:56:13 +08:00
<!-- 标题装饰 -->
<div class="ranking-title">
<img class="title-bg" src="@/assets/img/zheke/title-bg.png" alt="" />
<img
class="title-line"
src="@/assets/img/zheke/title-line.png"
alt=""
/>
<div class="title-inner">
{{
selectedRankingType === "major"
? "用户意向专业排行"
: "用户生源高中排行"
}}
</div>
<!-- 学院筛选 -->
<div class="filter-item">
<el-select
v-model="selectedYear"
size="small"
class="filter-select"
@change="handleYearChange"
>
<el-option
v-for="item in yearOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</div>
2025-08-12 14:39:25 +08:00
</div>
2025-08-15 15:56:13 +08:00
<div
v-for="(item, index) in rankingData"
:key="index"
class="ranking-item"
>
<div class="item-content">
<div
class="rank-badge"
:class="`topItem-${item.rank <= 3 ? item.rank : 'other'}`"
>
<img
:src="getRankImage(item.rank)"
:alt="`top${item.rank}`"
class="rank-bg"
/>
<span class="rank-text">TOP {{ item.rank }}</span>
2025-08-12 14:39:25 +08:00
</div>
2025-08-15 15:56:13 +08:00
<div class="item-name">{{ item.name }}</div>
<div class="item-count">{{ item.count.toLocaleString() }}</div>
</div>
<div class="progress-bar">
<div
class="progress-inner"
:style="{ width: getProgressWidth(item.count) }"
></div>
2025-08-12 14:39:25 +08:00
</div>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
2025-08-15 15:56:13 +08:00
.ranking-container {
2025-08-12 14:39:25 +08:00
width: 100%;
height: 100%;
2025-08-15 15:56:13 +08:00
display: flex;
flex-direction: column;
2025-08-12 14:39:25 +08:00
}
2025-08-15 15:56:13 +08:00
.ranking-title {
height: 33px;
line-height: 33px;
2025-08-12 14:39:25 +08:00
width: 100%;
2025-08-15 15:56:13 +08:00
color: #333;
text-align: left;
position: relative;
2025-08-12 14:39:25 +08:00
display: flex;
2025-08-15 15:56:13 +08:00
align-items: center;
padding-left: 41px;
background: transparent;
font-weight: bold;
font-size: 20px;
margin-bottom: 20px;
.title-bg {
height: 33px;
position: absolute;
left: 0;
top: 0;
}
.title-line {
height: 2px;
position: absolute;
left: 0;
bottom: 2px;
}
.title-inner {
font-weight: 600;
color: #393d44;
}
.filter-item {
position: absolute;
right: 0;
bottom: 5px;
font-weight: normal;
.filter-select {
width: 100px;
}
}
2025-08-12 14:39:25 +08:00
}
2025-08-15 15:56:13 +08:00
.ranking-tabs {
2025-08-12 14:39:25 +08:00
display: flex;
2025-08-15 15:56:13 +08:00
border-bottom: 1px solid #e6e9f0;
margin-bottom: 10px;
.tab-item {
padding: 10px 15px;
font-size: 14px;
color: #666;
cursor: pointer;
position: relative;
&.active {
color: #4b96ff;
font-weight: 500;
&::after {
content: "";
position: absolute;
bottom: -1px;
left: 0;
width: 100%;
height: 2px;
background-color: #4b96ff;
}
}
}
2025-08-12 14:39:25 +08:00
}
2025-08-15 15:56:13 +08:00
.ranking-list {
2025-08-12 14:39:25 +08:00
flex: 1;
overflow-y: auto;
2025-08-15 15:56:13 +08:00
padding: 5px 0;
/* 隐藏滚动条 */
&::-webkit-scrollbar {
display: none;
}
/* Firefox */
scrollbar-width: none;
/* IE */
-ms-overflow-style: none;
2025-08-12 14:39:25 +08:00
}
.ranking-item {
display: flex;
2025-08-15 15:56:13 +08:00
flex-direction: column;
margin-bottom: 23px;
// border-bottom: 1px solid #f0f2f5;
2025-08-12 14:39:25 +08:00
&:last-child {
border-bottom: none;
}
}
2025-08-15 15:56:13 +08:00
.rank-badge {
width: 60px;
height: 30px;
2025-08-12 14:39:25 +08:00
display: flex;
align-items: center;
justify-content: center;
2025-08-15 15:56:13 +08:00
font-size: 16px;
font-weight: bold;
position: relative;
2025-08-12 14:39:25 +08:00
2025-08-15 15:56:13 +08:00
.rank-bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: 2px;
object-fit: cover;
}
2025-08-12 14:39:25 +08:00
2025-08-15 15:56:13 +08:00
.rank-text {
position: relative; // 确保文字在图片上方
z-index: 1;
color: white;
2025-08-12 14:39:25 +08:00
}
2025-08-15 15:56:13 +08:00
&.topItem-other {
.rank-text {
color: #545966;
2025-08-12 14:39:25 +08:00
}
}
}
2025-08-15 15:56:13 +08:00
.item-content {
2025-08-12 14:39:25 +08:00
flex: 1;
2025-08-15 15:56:13 +08:00
display: flex;
align-items: center;
margin-bottom: 10px; // 添加固定的底部间距
}
.item-name {
font-size: 16px;
2025-08-12 14:39:25 +08:00
color: #333;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
2025-08-15 15:56:13 +08:00
margin-left: 27px;
2025-08-12 14:39:25 +08:00
}
.item-count {
2025-08-15 15:56:13 +08:00
font-size: 16px;
color: #2D2E30;
font-weight: 400;
margin-left: auto;
}
.progress-bar {
height: 6px;
background-color: #f0f2f5;
border-radius: 3px;
overflow: hidden;
.progress-inner {
height: 100%;
background-color: #4b96ff;
border-radius: 3px;
}
2025-08-12 14:39:25 +08:00
}
2025-08-15 15:56:13 +08:00
</style>