mirror of
https://gitee.com/ccnetcore/Yi
synced 2026-04-28 12:13:26 +08:00
1323 lines
32 KiB
Vue
1323 lines
32 KiB
Vue
<script setup lang="ts">
|
||
import { Trophy, User, Cpu, CirclePlus, CircleCheck } from '@element-plus/icons-vue';
|
||
import { ref, computed, onMounted } from 'vue';
|
||
import { ElMessage } from 'element-plus';
|
||
import { getRankingList } from '@/api/ranking';
|
||
import { RankingTypeEnum, type RankingItemDto } from '@/api/ranking/types';
|
||
|
||
// 加载状态
|
||
const loading = ref(false);
|
||
|
||
// 模型排行数据
|
||
const modelRankingList = ref<RankingItemDto[]>([]);
|
||
|
||
// 工具排行数据
|
||
const toolRankingList = ref<RankingItemDto[]>([]);
|
||
|
||
// 获取排行榜数据
|
||
async function fetchRankingData() {
|
||
loading.value = true;
|
||
try {
|
||
const [modelRes, toolRes] = await Promise.all([
|
||
getRankingList({ type: RankingTypeEnum.Model }),
|
||
getRankingList({ type: RankingTypeEnum.Tool }),
|
||
]);
|
||
|
||
if (modelRes.code === 200) {
|
||
modelRankingList.value = modelRes.data;
|
||
}
|
||
if (toolRes.code === 200) {
|
||
toolRankingList.value = toolRes.data;
|
||
}
|
||
}
|
||
catch (error) {
|
||
console.error('获取排行榜数据失败:', error);
|
||
ElMessage.error('获取排行榜数据失败');
|
||
}
|
||
finally {
|
||
loading.value = false;
|
||
}
|
||
}
|
||
|
||
// 获取排名样式
|
||
function getRankClass(rank: number) {
|
||
if (rank === 1) return 'rank-1';
|
||
if (rank === 2) return 'rank-2';
|
||
if (rank === 3) return 'rank-3';
|
||
return 'rank-normal';
|
||
}
|
||
|
||
// 更新时间
|
||
const updateTime = computed(() => {
|
||
const now = new Date();
|
||
return now.toLocaleString('zh-CN', {
|
||
year: 'numeric',
|
||
month: '2-digit',
|
||
day: '2-digit',
|
||
hour: '2-digit',
|
||
minute: '2-digit',
|
||
});
|
||
});
|
||
|
||
// AI编程能力计算器弹窗
|
||
const calculatorVisible = ref(false);
|
||
const calcStep = ref<1 | 2 | 3>(1);
|
||
const selectedModel = ref<RankingItemDto | null>(null);
|
||
const selectedTool = ref<RankingItemDto | null>(null);
|
||
|
||
// 计算AI编程能力分数
|
||
const calculatedScore = computed(() => {
|
||
if (!selectedModel.value || !selectedTool.value) return null;
|
||
const score = selectedModel.value.score * 0.65 + selectedTool.value.score * 0.35;
|
||
return score.toFixed(1);
|
||
});
|
||
|
||
// 根据得分获取描述
|
||
const scoreDescription = computed(() => {
|
||
const score = parseFloat(calculatedScore.value || '0');
|
||
if (score === 100) return '天才大脑';
|
||
if (score >= 95) return '博士大脑';
|
||
if (score >= 90) return '程序员大脑';
|
||
if (score >= 80) return '实习生大脑';
|
||
if (score >= 70) return '猴脑';
|
||
if (score >= 60) return '猪脑';
|
||
return '傻逼';
|
||
});
|
||
|
||
// 打开计算器
|
||
function openCalculator() {
|
||
calculatorVisible.value = true;
|
||
calcStep.value = 1;
|
||
selectedModel.value = null;
|
||
selectedTool.value = null;
|
||
}
|
||
|
||
// 关闭计算器
|
||
function closeCalculator() {
|
||
calculatorVisible.value = false;
|
||
}
|
||
|
||
// 选择模型
|
||
function selectModel(model: RankingItemDto) {
|
||
selectedModel.value = model;
|
||
if (calcStep.value === 1) {
|
||
calcStep.value = 2;
|
||
}
|
||
}
|
||
|
||
// 选择工具
|
||
function selectTool(tool: RankingItemDto) {
|
||
selectedTool.value = tool;
|
||
if (calcStep.value === 2) {
|
||
calcStep.value = 3;
|
||
}
|
||
}
|
||
|
||
// 重新开始
|
||
function restartCalculator() {
|
||
calcStep.value = 1;
|
||
selectedModel.value = null;
|
||
selectedTool.value = null;
|
||
}
|
||
|
||
// 页面加载时获取数据
|
||
onMounted(() => {
|
||
fetchRankingData();
|
||
});
|
||
</script>
|
||
|
||
<template>
|
||
<div class="ranking-container">
|
||
<!-- 页面标题 -->
|
||
<div class="ranking-header content-wrapper">
|
||
<div class="header-content">
|
||
<div class="title-section">
|
||
<div class="title-icon">
|
||
<el-icon size="40"><Trophy /></el-icon>
|
||
</div>
|
||
<div class="title-text">
|
||
<h1 class="main-title">意心Ai全球大模型实时排行榜(编程)</h1>
|
||
<p class="subtitle">基于综合性能、用户活跃度、代码质量等多维度评估</p>
|
||
</div>
|
||
</div>
|
||
<el-button type="primary" class="calculator-btn" @click="openCalculator">
|
||
<el-icon><CirclePlus /></el-icon>
|
||
点击测测当前你的Ai编程能力
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 主内容区 -->
|
||
<div class="ranking-content content-wrapper">
|
||
<!-- 左侧:模型排行 -->
|
||
<div class="ranking-section">
|
||
<div class="section-header">
|
||
<div class="section-title">
|
||
<el-icon size="24"><Cpu /></el-icon>
|
||
<span>模型排行(编程)</span>
|
||
</div>
|
||
<el-tag type="primary" effect="plain">Top {{ modelRankingList.length }}</el-tag>
|
||
</div>
|
||
|
||
<!-- 加载状态 -->
|
||
<div v-if="loading" class="loading-wrapper">
|
||
<el-skeleton :rows="8" animated />
|
||
</div>
|
||
|
||
<!-- 排行列表 -->
|
||
<div v-else class="ranking-list">
|
||
<div
|
||
v-for="(item, index) in modelRankingList"
|
||
:key="item.id"
|
||
class="ranking-item"
|
||
:class="getRankClass(index + 1)"
|
||
>
|
||
<div class="rank-number">
|
||
<span v-if="index + 1 <= 3" class="rank-medal">
|
||
<el-icon size="20"><Trophy /></el-icon>
|
||
</span>
|
||
<span v-else>{{ index + 1 }}</span>
|
||
</div>
|
||
<div class="rank-logo">
|
||
<el-image
|
||
v-if="item.logoUrl"
|
||
:src="item.logoUrl"
|
||
:alt="item.name"
|
||
class="logo-image"
|
||
fit="cover"
|
||
/>
|
||
<div v-else class="logo-placeholder">
|
||
<el-icon><Cpu /></el-icon>
|
||
</div>
|
||
</div>
|
||
<div class="rank-middle">
|
||
<div class="rank-info">
|
||
<div class="rank-name">{{ item.name }}</div>
|
||
<div class="rank-meta">
|
||
<span class="provider">{{ item.provider }}</span>
|
||
<span v-if="item.description" class="divider">|</span>
|
||
<span class="description">{{ item.description }}</span>
|
||
</div>
|
||
</div>
|
||
<div class="rank-progress">
|
||
<div class="progress-bar">
|
||
<div
|
||
class="progress-fill"
|
||
:style="{ width: `${item.score}%` }"
|
||
/>
|
||
</div>
|
||
<div class="progress-score">
|
||
<span class="score-value">{{ item.score }}</span>
|
||
<span class="score-label">分</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 右侧:工具排行 -->
|
||
<div class="ranking-section">
|
||
<div class="section-header">
|
||
<div class="section-title">
|
||
<el-icon size="24"><User /></el-icon>
|
||
<span>工具排行(编程)</span>
|
||
</div>
|
||
<el-tag type="success" effect="plain">Top {{ toolRankingList.length }}</el-tag>
|
||
</div>
|
||
|
||
<!-- 加载状态 -->
|
||
<div v-if="loading" class="loading-wrapper">
|
||
<el-skeleton :rows="8" animated />
|
||
</div>
|
||
|
||
<!-- 排行列表 -->
|
||
<div v-else class="ranking-list">
|
||
<div
|
||
v-for="(item, index) in toolRankingList"
|
||
:key="item.id"
|
||
class="ranking-item"
|
||
:class="getRankClass(index + 1)"
|
||
>
|
||
<div class="rank-number">
|
||
<span v-if="index + 1 <= 3" class="rank-medal">
|
||
<el-icon size="20"><Trophy /></el-icon>
|
||
</span>
|
||
<span v-else>{{ index + 1 }}</span>
|
||
</div>
|
||
<div class="rank-logo">
|
||
<el-image
|
||
v-if="item.logoUrl"
|
||
:src="item.logoUrl"
|
||
:alt="item.name"
|
||
class="logo-image"
|
||
fit="cover"
|
||
/>
|
||
<div v-else class="logo-placeholder">
|
||
<el-icon><User /></el-icon>
|
||
</div>
|
||
</div>
|
||
<div class="rank-middle">
|
||
<div class="rank-info">
|
||
<div class="rank-name">{{ item.name }}</div>
|
||
<div class="rank-meta">
|
||
<span class="provider">{{ item.provider }}</span>
|
||
<span v-if="item.description" class="divider">|</span>
|
||
<span class="description">{{ item.description }}</span>
|
||
</div>
|
||
</div>
|
||
<div class="rank-progress">
|
||
<div class="progress-bar">
|
||
<div
|
||
class="progress-fill"
|
||
:style="{ width: `${item.score}%` }"
|
||
/>
|
||
</div>
|
||
<div class="progress-score">
|
||
<span class="score-value">{{ item.score }}</span>
|
||
<span class="score-label">分</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- AI编程能力计算器弹窗 -->
|
||
<el-dialog
|
||
v-model="calculatorVisible"
|
||
title="AI编程能力计算器"
|
||
width="800px"
|
||
:close-on-click-modal="false"
|
||
class="calculator-dialog"
|
||
>
|
||
<!-- 步骤1:选择模型 -->
|
||
<div v-if="calcStep === 1" class="calc-step">
|
||
<div class="step-header">
|
||
<div class="step-number">1</div>
|
||
<div class="step-title">请选择你正在使用的大模型</div>
|
||
</div>
|
||
<div class="step-list">
|
||
<div
|
||
v-for="(item, index) in modelRankingList"
|
||
:key="item.id"
|
||
class="step-item"
|
||
:class="{ active: selectedModel?.id === item.id }"
|
||
@click="selectModel(item)"
|
||
>
|
||
<div class="step-item-rank" :class="getRankClass(index + 1)">{{ index + 1 }}</div>
|
||
<div class="step-item-logo">
|
||
<el-image
|
||
v-if="item.logoUrl"
|
||
:src="item.logoUrl"
|
||
:alt="item.name"
|
||
class="logo-image"
|
||
fit="cover"
|
||
/>
|
||
<div v-else class="logo-placeholder">
|
||
<el-icon><Cpu /></el-icon>
|
||
</div>
|
||
</div>
|
||
<div class="step-item-info">
|
||
<div class="step-item-name">{{ item.name }}</div>
|
||
<div class="step-item-meta">
|
||
<span class="provider">{{ item.provider }}</span>
|
||
<span v-if="item.description" class="divider">|</span>
|
||
<span class="desc">{{ item.description }}</span>
|
||
</div>
|
||
</div>
|
||
<div class="step-item-score">{{ item.score }}分</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 步骤2:选择工具 -->
|
||
<div v-if="calcStep === 2" class="calc-step">
|
||
<div class="step-header">
|
||
<div class="step-number">2</div>
|
||
<div class="step-title">请选择你正在使用的编程工具</div>
|
||
<div v-if="selectedModel" class="step-selected">
|
||
已选模型:<span>{{ selectedModel.name }}</span>
|
||
</div>
|
||
</div>
|
||
<div class="step-list">
|
||
<div
|
||
v-for="(item, index) in toolRankingList"
|
||
:key="item.id"
|
||
class="step-item"
|
||
:class="{ active: selectedTool?.id === item.id }"
|
||
@click="selectTool(item)"
|
||
>
|
||
<div class="step-item-rank" :class="getRankClass(index + 1)">{{ index + 1 }}</div>
|
||
<div class="step-item-logo">
|
||
<el-image
|
||
v-if="item.logoUrl"
|
||
:src="item.logoUrl"
|
||
:alt="item.name"
|
||
class="logo-image"
|
||
fit="cover"
|
||
/>
|
||
<div v-else class="logo-placeholder">
|
||
<el-icon><User /></el-icon>
|
||
</div>
|
||
</div>
|
||
<div class="step-item-info">
|
||
<div class="step-item-name">{{ item.name }}</div>
|
||
<div class="step-item-meta">
|
||
<span class="provider">{{ item.provider }}</span>
|
||
<span v-if="item.description" class="divider">|</span>
|
||
<span class="desc">{{ item.description }}</span>
|
||
</div>
|
||
</div>
|
||
<div class="step-item-score">{{ item.score }}分</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 步骤3:计算结果 -->
|
||
<div v-if="calcStep === 3" class="calc-step result-step">
|
||
<div class="result-header">
|
||
<el-icon size="48" color="#67c23a"><CircleCheck /></el-icon>
|
||
<h3>计算完成</h3>
|
||
</div>
|
||
<div class="result-content">
|
||
<div class="result-combination">
|
||
<div class="result-item">
|
||
<div class="result-label">大模型</div>
|
||
<div class="result-value">{{ selectedModel?.name }}</div>
|
||
<div class="result-score">{{ selectedModel?.score }}分 × 65%</div>
|
||
</div>
|
||
<div class="result-plus">+</div>
|
||
<div class="result-item">
|
||
<div class="result-label">编程工具</div>
|
||
<div class="result-value">{{ selectedTool?.name }}</div>
|
||
<div class="result-score">{{ selectedTool?.score }}分 × 35%</div>
|
||
</div>
|
||
</div>
|
||
<div class="result-divider" />
|
||
<div class="result-final">
|
||
<div class="result-final-label">AI编程能力综合评分</div>
|
||
<div class="result-final-score">{{ calculatedScore }}<span>分</span></div>
|
||
<div class="result-congrats">恭喜你,陪伴你的AI是一个<span class="brain-type">{{ scoreDescription }}</span></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<template #footer>
|
||
<div class="dialog-footer">
|
||
<el-button v-if="calcStep > 1" @click="calcStep--">上一步</el-button>
|
||
<el-button v-if="calcStep === 3" type="primary" @click="restartCalculator">重新计算</el-button>
|
||
<el-button @click="closeCalculator">关闭</el-button>
|
||
</div>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<!-- 底部说明 -->
|
||
<div class="ranking-footer content-wrapper">
|
||
<div class="footer-content">
|
||
<el-alert
|
||
title="排行榜说明"
|
||
type="info"
|
||
:closable="false"
|
||
show-icon
|
||
>
|
||
<template #default>
|
||
<div class="footer-desc">
|
||
<p>• 模型排行综合评估:主要全网编程能力指数、意心Ai平台实测指数、代码生成能力、推理速度、准确率、多语言支持等维度</p>
|
||
<p>• 工具排行综合评估:主要全网编程能力指数、意心Ai平台实测指数、用户活跃度、功能完整性、易用性、社区反馈等维度</p>
|
||
<p>• 数据实时更新,以上排行仅意心Ai平台模型综合编程能力对比</p>
|
||
</div>
|
||
</template>
|
||
</el-alert>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped lang="scss">
|
||
.ranking-container {
|
||
width: 100%;
|
||
height: 100%;
|
||
overflow-y: auto;
|
||
background: linear-gradient(135deg, #f5f7fa 0%, #e4e7ed 100%);
|
||
padding: 24px;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
// 内容包装器 - 限制最大宽度并居中
|
||
.content-wrapper {
|
||
max-width: 1400px;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
// 页面头部
|
||
.ranking-header {
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
border-radius: 16px;
|
||
padding: 32px 40px;
|
||
margin-bottom: 24px;
|
||
box-shadow: 0 8px 32px rgba(102, 126, 234, 0.3);
|
||
|
||
.header-content {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
gap: 16px;
|
||
}
|
||
|
||
.title-section {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 20px;
|
||
|
||
.title-icon {
|
||
width: 70px;
|
||
height: 70px;
|
||
background: rgba(255, 255, 255, 0.2);
|
||
backdrop-filter: blur(10px);
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: white;
|
||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
|
||
}
|
||
|
||
.title-text {
|
||
.main-title {
|
||
font-size: 32px;
|
||
font-weight: 800;
|
||
color: white;
|
||
margin: 0 0 8px 0;
|
||
letter-spacing: -0.5px;
|
||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.subtitle {
|
||
font-size: 15px;
|
||
color: rgba(255, 255, 255, 0.9);
|
||
margin: 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
.update-time {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
color: rgba(255, 255, 255, 0.9);
|
||
font-size: 14px;
|
||
background: rgba(255, 255, 255, 0.15);
|
||
padding: 10px 18px;
|
||
border-radius: 30px;
|
||
backdrop-filter: blur(10px);
|
||
}
|
||
|
||
.calculator-btn {
|
||
background: rgba(255, 255, 255, 0.2);
|
||
backdrop-filter: blur(10px);
|
||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||
color: white;
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
padding: 12px 24px;
|
||
height: auto;
|
||
border-radius: 30px;
|
||
transition: all 0.3s;
|
||
position: relative;
|
||
overflow: hidden;
|
||
animation: btnPulse 2s ease-in-out infinite;
|
||
|
||
// 光效扫过动画
|
||
&::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: -100%;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: linear-gradient(
|
||
90deg,
|
||
transparent,
|
||
rgba(255, 255, 255, 0.4),
|
||
transparent
|
||
);
|
||
animation: lightSweep 3s ease-in-out infinite;
|
||
}
|
||
|
||
// 发光光环
|
||
&::after {
|
||
content: '';
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
width: 100%;
|
||
height: 100%;
|
||
border-radius: 30px;
|
||
border: 2px solid rgba(255, 255, 255, 0.6);
|
||
opacity: 0;
|
||
animation: glowRing 2s ease-out infinite;
|
||
}
|
||
|
||
&:hover {
|
||
background: rgba(255, 255, 255, 0.3);
|
||
transform: translateY(-2px) scale(1.05);
|
||
box-shadow:
|
||
0 4px 20px rgba(0, 0, 0, 0.2),
|
||
0 0 30px rgba(255, 255, 255, 0.3);
|
||
animation: none;
|
||
|
||
&::before {
|
||
animation: none;
|
||
}
|
||
|
||
&::after {
|
||
animation: none;
|
||
opacity: 0;
|
||
}
|
||
}
|
||
|
||
.el-icon {
|
||
margin-right: 6px;
|
||
animation: iconBounce 1s ease-in-out infinite;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 按钮脉冲动画
|
||
@keyframes btnPulse {
|
||
0%, 100% {
|
||
transform: scale(1);
|
||
box-shadow:
|
||
0 0 0 0 rgba(255, 255, 255, 0.4),
|
||
0 4px 15px rgba(0, 0, 0, 0.1);
|
||
}
|
||
50% {
|
||
transform: scale(1.03);
|
||
box-shadow:
|
||
0 0 0 8px rgba(255, 255, 255, 0),
|
||
0 6px 20px rgba(0, 0, 0, 0.15);
|
||
}
|
||
}
|
||
|
||
// 光效扫过动画
|
||
@keyframes lightSweep {
|
||
0% {
|
||
left: -100%;
|
||
}
|
||
50%, 100% {
|
||
left: 100%;
|
||
}
|
||
}
|
||
|
||
// 发光光环动画
|
||
@keyframes glowRing {
|
||
0% {
|
||
transform: translate(-50%, -50%) scale(1);
|
||
opacity: 0.6;
|
||
}
|
||
100% {
|
||
transform: translate(-50%, -50%) scale(1.5);
|
||
opacity: 0;
|
||
}
|
||
}
|
||
|
||
// 图标跳动动画
|
||
@keyframes iconBounce {
|
||
0%, 100% {
|
||
transform: translateY(0);
|
||
}
|
||
50% {
|
||
transform: translateY(-3px);
|
||
}
|
||
}
|
||
|
||
// 主内容区
|
||
.ranking-content {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 20px;
|
||
margin-bottom: 24px;
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
|
||
@media (max-width: 1024px) {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
}
|
||
|
||
// 排行区域
|
||
.ranking-section {
|
||
background: white;
|
||
border-radius: 16px;
|
||
padding: 20px;
|
||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.06);
|
||
transition: transform 0.3s, box-shadow 0.3s;
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
|
||
&:hover {
|
||
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.section-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 20px;
|
||
padding-bottom: 16px;
|
||
border-bottom: 2px solid #f0f0f0;
|
||
|
||
.section-title {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
font-size: 20px;
|
||
font-weight: 700;
|
||
color: #303133;
|
||
|
||
.el-icon {
|
||
color: #667eea;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
.loading-wrapper {
|
||
padding: 40px 20px;
|
||
}
|
||
|
||
.ranking-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
}
|
||
|
||
.ranking-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
padding: 14px;
|
||
border-radius: 12px;
|
||
background: #f8f9fa;
|
||
transition: all 0.3s;
|
||
border: 2px solid transparent;
|
||
|
||
&:hover {
|
||
background: white;
|
||
border-color: #667eea;
|
||
transform: translateX(4px);
|
||
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.15);
|
||
}
|
||
|
||
// 前三名特殊样式
|
||
&.rank-1 {
|
||
background: linear-gradient(135deg, #fff8e1 0%, #ffecb3 100%);
|
||
border-color: #ffc107;
|
||
|
||
.rank-number {
|
||
background: linear-gradient(135deg, #ffc107, #ff8f00);
|
||
color: white;
|
||
}
|
||
|
||
.progress-fill {
|
||
background: linear-gradient(90deg, #ffc107 0%, #ff8f00 100%);
|
||
}
|
||
|
||
.progress-score .score-value {
|
||
color: #ff8f00;
|
||
}
|
||
}
|
||
|
||
&.rank-2 {
|
||
background: linear-gradient(135deg, #f5f5f5 0%, #e0e0e0 100%);
|
||
border-color: #9e9e9e;
|
||
|
||
.rank-number {
|
||
background: linear-gradient(135deg, #9e9e9e, #616161);
|
||
color: white;
|
||
}
|
||
|
||
.progress-fill {
|
||
background: linear-gradient(90deg, #9e9e9e 0%, #616161 100%);
|
||
}
|
||
|
||
.progress-score .score-value {
|
||
color: #616161;
|
||
}
|
||
}
|
||
|
||
&.rank-3 {
|
||
background: linear-gradient(135deg, #fff3e0 0%, #ffe0b2 100%);
|
||
border-color: #ff9800;
|
||
|
||
.rank-number {
|
||
background: linear-gradient(135deg, #ff9800, #f57c00);
|
||
color: white;
|
||
}
|
||
|
||
.progress-fill {
|
||
background: linear-gradient(90deg, #ff9800 0%, #f57c00 100%);
|
||
}
|
||
|
||
.progress-score .score-value {
|
||
color: #f57c00;
|
||
}
|
||
}
|
||
|
||
.rank-number {
|
||
width: 40px;
|
||
height: 40px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 16px;
|
||
font-weight: 700;
|
||
color: #606266;
|
||
background: white;
|
||
border-radius: 50%;
|
||
flex-shrink: 0;
|
||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.06);
|
||
|
||
.rank-medal {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
}
|
||
|
||
.rank-logo {
|
||
width: 40px;
|
||
height: 40px;
|
||
flex-shrink: 0;
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
background: white;
|
||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.06);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
|
||
.logo-image {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.logo-placeholder {
|
||
width: 100%;
|
||
height: 100%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
color: white;
|
||
font-size: 18px;
|
||
}
|
||
}
|
||
|
||
.rank-middle {
|
||
flex: 1;
|
||
min-width: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
}
|
||
|
||
.rank-info {
|
||
.rank-name {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: #303133;
|
||
margin-bottom: 4px;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
.rank-meta {
|
||
font-size: 12px;
|
||
color: #909399;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
min-width: 0;
|
||
|
||
.provider {
|
||
color: #667eea;
|
||
font-weight: 600;
|
||
background: rgba(102, 126, 234, 0.1);
|
||
padding: 1px 6px;
|
||
border-radius: 4px;
|
||
font-size: 11px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.divider {
|
||
color: #dcdfe6;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.description {
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
min-width: 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
.rank-progress {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
|
||
.progress-bar {
|
||
flex: 1;
|
||
height: 8px;
|
||
background: #e4e7ed;
|
||
border-radius: 4px;
|
||
overflow: hidden;
|
||
|
||
.progress-fill {
|
||
height: 100%;
|
||
background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
|
||
border-radius: 4px;
|
||
transition: width 0.5s ease;
|
||
}
|
||
}
|
||
|
||
.progress-score {
|
||
display: flex;
|
||
align-items: baseline;
|
||
gap: 2px;
|
||
flex-shrink: 0;
|
||
min-width: 60px;
|
||
justify-content: flex-end;
|
||
|
||
.score-value {
|
||
font-size: 18px;
|
||
font-weight: 700;
|
||
color: #667eea;
|
||
}
|
||
|
||
.score-label {
|
||
font-size: 12px;
|
||
color: #909399;
|
||
}
|
||
}
|
||
}
|
||
|
||
.rank-score {
|
||
display: none;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 计算器弹窗样式
|
||
.calculator-dialog {
|
||
.calc-step {
|
||
.step-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
margin-bottom: 20px;
|
||
padding-bottom: 16px;
|
||
border-bottom: 2px solid #f0f0f0;
|
||
|
||
.step-number {
|
||
width: 36px;
|
||
height: 36px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
color: white;
|
||
font-size: 18px;
|
||
font-weight: 700;
|
||
border-radius: 50%;
|
||
}
|
||
|
||
.step-title {
|
||
font-size: 18px;
|
||
font-weight: 700;
|
||
color: #303133;
|
||
}
|
||
|
||
.step-selected {
|
||
margin-left: auto;
|
||
font-size: 13px;
|
||
color: #909399;
|
||
background: #f5f7fa;
|
||
padding: 6px 12px;
|
||
border-radius: 6px;
|
||
|
||
span {
|
||
color: #667eea;
|
||
font-weight: 600;
|
||
}
|
||
}
|
||
}
|
||
|
||
.step-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 10px;
|
||
max-height: 400px;
|
||
overflow-y: auto;
|
||
padding-right: 8px;
|
||
|
||
.step-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
padding: 12px;
|
||
background: #f8f9fa;
|
||
border-radius: 10px;
|
||
cursor: pointer;
|
||
transition: all 0.3s;
|
||
border: 2px solid transparent;
|
||
|
||
&:hover {
|
||
background: white;
|
||
border-color: #667eea;
|
||
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.15);
|
||
transform: translateX(4px);
|
||
}
|
||
|
||
&.active {
|
||
background: white;
|
||
border-color: #667eea;
|
||
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.2);
|
||
|
||
.step-item-rank {
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
color: white;
|
||
}
|
||
}
|
||
|
||
.step-item-rank {
|
||
width: 28px;
|
||
height: 28px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 13px;
|
||
font-weight: 700;
|
||
color: #606266;
|
||
background: white;
|
||
border-radius: 6px;
|
||
flex-shrink: 0;
|
||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.06);
|
||
|
||
&.rank-1 {
|
||
background: linear-gradient(135deg, #ffc107, #ff8f00);
|
||
color: white;
|
||
}
|
||
|
||
&.rank-2 {
|
||
background: linear-gradient(135deg, #9e9e9e, #616161);
|
||
color: white;
|
||
}
|
||
|
||
&.rank-3 {
|
||
background: linear-gradient(135deg, #ff9800, #f57c00);
|
||
color: white;
|
||
}
|
||
}
|
||
|
||
.step-item-logo {
|
||
width: 36px;
|
||
height: 36px;
|
||
flex-shrink: 0;
|
||
border-radius: 6px;
|
||
overflow: hidden;
|
||
background: white;
|
||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.06);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
|
||
.logo-image {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.logo-placeholder {
|
||
width: 100%;
|
||
height: 100%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
color: white;
|
||
font-size: 16px;
|
||
}
|
||
}
|
||
|
||
.step-item-info {
|
||
flex: 1;
|
||
min-width: 0;
|
||
|
||
.step-item-name {
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
color: #303133;
|
||
margin-bottom: 2px;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
.step-item-meta {
|
||
font-size: 12px;
|
||
color: #909399;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
min-width: 0;
|
||
|
||
.provider {
|
||
color: #667eea;
|
||
font-weight: 600;
|
||
background: rgba(102, 126, 234, 0.1);
|
||
padding: 1px 6px;
|
||
border-radius: 4px;
|
||
font-size: 11px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.divider {
|
||
color: #dcdfe6;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.desc {
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
min-width: 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
.step-item-score {
|
||
font-size: 15px;
|
||
font-weight: 700;
|
||
color: #667eea;
|
||
flex-shrink: 0;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.result-step {
|
||
text-align: center;
|
||
padding: 20px;
|
||
|
||
.result-header {
|
||
margin-bottom: 30px;
|
||
|
||
h3 {
|
||
font-size: 24px;
|
||
font-weight: 700;
|
||
color: #303133;
|
||
margin: 16px 0 0 0;
|
||
}
|
||
}
|
||
|
||
.result-content {
|
||
background: #f8f9fa;
|
||
border-radius: 16px;
|
||
padding: 30px;
|
||
|
||
.result-combination {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 20px;
|
||
margin-bottom: 24px;
|
||
|
||
.result-item {
|
||
flex: 1;
|
||
max-width: 200px;
|
||
background: white;
|
||
padding: 20px;
|
||
border-radius: 12px;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||
|
||
.result-label {
|
||
font-size: 13px;
|
||
color: #909399;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.result-value {
|
||
font-size: 16px;
|
||
font-weight: 700;
|
||
color: #303133;
|
||
margin-bottom: 8px;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
.result-score {
|
||
font-size: 14px;
|
||
color: #667eea;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
|
||
.result-plus {
|
||
font-size: 24px;
|
||
font-weight: 700;
|
||
color: #909399;
|
||
}
|
||
}
|
||
|
||
.result-divider {
|
||
height: 1px;
|
||
background: #e4e7ed;
|
||
margin: 24px 0;
|
||
}
|
||
|
||
.result-final {
|
||
.result-final-label {
|
||
font-size: 14px;
|
||
color: #909399;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.result-final-score {
|
||
font-size: 56px;
|
||
font-weight: 800;
|
||
color: #667eea;
|
||
line-height: 1;
|
||
|
||
span {
|
||
font-size: 20px;
|
||
font-weight: 500;
|
||
margin-left: 4px;
|
||
}
|
||
}
|
||
|
||
.result-congrats {
|
||
margin-top: 20px;
|
||
font-size: 16px;
|
||
color: #606266;
|
||
|
||
.brain-type {
|
||
font-size: 24px;
|
||
font-weight: 700;
|
||
color: #ff6b6b;
|
||
margin-left: 4px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.dialog-footer {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
gap: 12px;
|
||
}
|
||
}
|
||
|
||
// 底部说明
|
||
.ranking-footer {
|
||
.footer-content {
|
||
max-width: 100%;
|
||
}
|
||
|
||
.footer-desc {
|
||
p {
|
||
margin: 4px 0;
|
||
font-size: 13px;
|
||
color: #606266;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 响应式
|
||
@media (max-width: 768px) {
|
||
.ranking-container {
|
||
padding: 16px;
|
||
}
|
||
|
||
.ranking-header {
|
||
padding: 24px 20px;
|
||
|
||
.title-section {
|
||
.title-icon {
|
||
width: 50px;
|
||
height: 50px;
|
||
|
||
.el-icon {
|
||
font-size: 24px;
|
||
}
|
||
}
|
||
|
||
.title-text {
|
||
.main-title {
|
||
font-size: 22px;
|
||
}
|
||
|
||
.subtitle {
|
||
font-size: 13px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.ranking-section {
|
||
padding: 16px;
|
||
|
||
.section-title {
|
||
font-size: 18px;
|
||
}
|
||
|
||
.ranking-item {
|
||
padding: 10px;
|
||
gap: 10px;
|
||
|
||
.rank-number {
|
||
width: 34px;
|
||
height: 34px;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.rank-logo {
|
||
width: 36px;
|
||
height: 36px;
|
||
}
|
||
|
||
.rank-info {
|
||
.rank-name {
|
||
font-size: 14px;
|
||
}
|
||
|
||
.rank-meta {
|
||
font-size: 11px;
|
||
gap: 4px;
|
||
|
||
.provider {
|
||
font-size: 10px;
|
||
padding: 1px 4px;
|
||
}
|
||
}
|
||
}
|
||
|
||
.rank-score {
|
||
.score-value {
|
||
font-size: 16px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style>
|