2647 lines
80 KiB
Vue
2647 lines
80 KiB
Vue
<template>
|
||
<div>
|
||
<!-- 第一部分:百日大战主题 - 使用banner0.png图片 -->
|
||
<section class="theme-section card-game">
|
||
<div class="theme-container">
|
||
<img src="/banner0.png" alt="百日大战主题" class="banner-image bai-day-battle">
|
||
</div>
|
||
</section>
|
||
|
||
<!-- 第二部分:战鼓动画(浮动并支持拖放) -->
|
||
<section v-if="localDisplayConfig.showDrum" class="drums-section card-game" @mousedown="startDrag"
|
||
@click="handleDrumClick" :style="{ left: drumsPosition.x + 'px', top: drumsPosition.y + 'px' }">
|
||
<div class="drums-container">
|
||
<!-- 战鼓动画在上面 -->
|
||
<div class="drums-animation">
|
||
<div class="drum glow-border" :class="{ beating: isBeating }">🥁</div>
|
||
<div class="drum" :class="{ beating: isBeating }">🥁</div>
|
||
<div class="trophy" style="font-size: 2.5rem; filter: drop-shadow(0 0 10px var(--gold-primary));">🏆</div>
|
||
<div class="drum" :class="{ beating: isBeating }">🥁</div>
|
||
<div class="drum" :class="{ beating: isBeating }">🥁</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- 任务设置模块 -->
|
||
<section class="task-settings-section card-game">
|
||
<div class="task-title-container">
|
||
<h1 class="game-title" v-if="taskSettings?.mainTitle">任务: <span class="game-title-highlight">{{
|
||
taskSettings.mainTitle }} </span> </h1>
|
||
<p class="game-subtitle" v-if="taskSettings?.subtitle">{{ taskSettings.subtitle }}</p>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- 第三部分:奖金设置(图片形式) -->
|
||
<section v-if="localDisplayConfig.showBonusModule" class="bonus-section card-game"
|
||
@mousedown="startBonusDrag"
|
||
@touchstart="startBonusTouch"
|
||
@click.stop>
|
||
<div class="bonus-awards-container">
|
||
<div><img src="/award1.png" alt="一等奖" class="award-image"></div>
|
||
<div><img src="/award2.png" alt="二等奖" class="award-image"></div>
|
||
<div><img src="/award3.png" alt="三等奖" class="award-image"></div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- 第四部分:总战绩 -->
|
||
<section class="total-score-section card-game">
|
||
<div class="total-score-container">
|
||
<div class="game-subtitle total-score-total-title">
|
||
<img src="/completed_performance.png"
|
||
alt="总战绩"
|
||
style="width: 600px; display: block; margin: 0 auto; height: auto;"
|
||
class="total-score-total-image">
|
||
<img src="/completed_performance.png"
|
||
alt="总战绩"
|
||
style="width: 880px; display: block; margin: 0 auto; height: auto;"
|
||
class="total-score-total-image">
|
||
</div>
|
||
<div class="total-score-content">
|
||
<div class="total-score-item">
|
||
<span class="score-value total-score-total-value">{{ localDisplayConfig.team?.totalScoreColumn?.displayStyle === 'amount' ? '¥' : '' }}{{ totalTeamScore }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- 第五部分:排名明细 -->
|
||
<section class="rankings-section card-game">
|
||
<div class="rankings-container">
|
||
|
||
<!-- 战区排名 -->
|
||
<div class="team-rankings">
|
||
<!-- 战区排名宣传语 -->
|
||
<div class="team-rankings-propagation">
|
||
<img src="/team-propagation.png" alt="虎狼之师" class="team-rankings-propagation-image"
|
||
style="width: 500px; height: auto">
|
||
</div>
|
||
<!-- 战区冠军 -->
|
||
<div class="team-champion">
|
||
<div class="team-logo" :class="{ 'photo-container': localDisplayConfig.championLogos?.teamChampionType === 'photo' }">
|
||
<img v-if="localDisplayConfig.championLogos?.teamChampion"
|
||
:src="localDisplayConfig.championLogos.teamChampion" alt="战区冠军"
|
||
class="champion-logo"
|
||
:style="localDisplayConfig.championLogos?.teamChampionType === 'photo'
|
||
? {
|
||
width: localDisplayConfig.championLogos?.teamChampionPhotoWidth + 'px',
|
||
height: localDisplayConfig.championLogos?.teamChampionPhotoHeight + 'px',
|
||
objectFit: 'cover'
|
||
}
|
||
: {
|
||
width: localDisplayConfig.championLogos?.teamChampionSize + 'px',
|
||
height: localDisplayConfig.championLogos?.teamChampionSize + 'px'
|
||
}
|
||
">
|
||
<img v-else src="/crown.png" alt="战区冠军" class="champion-logo" />
|
||
</div>
|
||
<div class="champion-name">
|
||
{{ teamRankings[0]?.name || '暂无冠军' }}
|
||
</div>
|
||
</div>
|
||
<div class="team-rankings-container">
|
||
<h2 class="game-subtitle">战区排名</h2>
|
||
<div class="rank-table">
|
||
<div class="table-header" :style="{ 'grid-template-columns': teamGridTemplate }">
|
||
<span class="rank-col" :style="{ textAlign: localDisplayConfig.team?.columnAlignments?.rank || 'left' }">排名</span>
|
||
<span class="name-col" :style="{ textAlign: localDisplayConfig.team?.columnAlignments?.name || 'left' }">战区名称</span>
|
||
<span class="score-col" :style="{ textAlign: localDisplayConfig.team?.columnAlignments?.score || 'left' }">{{ localDisplayConfig.team?.totalScoreColumn?.displayName || '业绩' }}</span>
|
||
<span v-if="localDisplayConfig.team?.showMemberCount" class="member-col" :style="{ textAlign: localDisplayConfig.team?.columnAlignments?.memberCount || 'left' }">人数</span>
|
||
<span v-if="localDisplayConfig.team?.showLeader" class="leader-col" :style="{ textAlign: localDisplayConfig.team?.columnAlignments?.leader || 'left' }">将军</span>
|
||
<span v-if="localDisplayConfig.team?.showBonus" class="bonus-col" :style="{ textAlign: localDisplayConfig.team?.columnAlignments?.bonus || 'left' }">奖金</span>
|
||
</div>
|
||
<div v-for="(item, index) in filteredTeamRankings" :key="item.id" class="table-row"
|
||
:style="{ 'grid-template-columns': teamGridTemplate }" :class="{
|
||
'top-three': index < 3,
|
||
'highlight': index === 0
|
||
}">
|
||
<span class="rank-col" :style="{ textAlign: localDisplayConfig.team?.columnAlignments?.rank || 'left' }">{{ index + 1 }}</span>
|
||
<span class="name-col" :style="{ textAlign: localDisplayConfig.team?.columnAlignments?.name || 'left' }">{{ item.name }}</span>
|
||
<span class="score-col" :style="{ textAlign: localDisplayConfig.team?.columnAlignments?.score || 'left' }">{{ localDisplayConfig.team?.totalScoreColumn?.displayStyle === 'amount' ? '¥' : '' }}{{ formatNumber(item.totalScore) }}</span>
|
||
<span v-if="localDisplayConfig.team?.showMemberCount" class="member-col" :style="{ textAlign: localDisplayConfig.team?.columnAlignments?.memberCount || 'left' }">{{ item.memberCount }}人</span>
|
||
<span v-if="localDisplayConfig.team?.showLeader" class="leader-col" :style="{ textAlign: localDisplayConfig.team?.columnAlignments?.leader || 'left' }">{{ item.leader }}</span>
|
||
<span v-if="localDisplayConfig.team?.showBonus" class="bonus-col" :style="{ textAlign: localDisplayConfig.team?.columnAlignments?.bonus || 'left' }">¥{{ item.bonus }}</span>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="individual-rankings">
|
||
<!-- 英雄排名宣传语 -->
|
||
<div class="individual-rankings-propagation">
|
||
<img src="/individual-propagation.png" alt="虎狼之师" class="individual-rankings-propagation-image"
|
||
style="width: 500px; height: auto">
|
||
</div>
|
||
<!-- 英雄冠军 -->
|
||
<div class="individual-champion">
|
||
<div class="champion-content">
|
||
<div class="individual-avatar champion-container">
|
||
<div class="crown-animation" :class="{'crown-animation-run': localDisplayConfig.crown?.animationEnabled}" v-if="filteredIndividualRankings.length > 0" :style="{
|
||
fontSize: (localDisplayConfig.crown?.size || localDisplayConfig.championLogos?.individualChampionSize * 0.8) + 'px'
|
||
}">
|
||
👑
|
||
</div>
|
||
<img v-if="filteredIndividualRankings[0]?.avatar && filteredIndividualRankings[0].avatar.startsWith('/')"
|
||
:src="filteredIndividualRankings[0].avatar" alt="冠军头像" class="avatar-image avatar-image-champion"
|
||
:style="{ width: localDisplayConfig.championLogos?.individualChampionSize + 'px', height: localDisplayConfig.championLogos?.individualChampionSize + 'px' }">
|
||
<img v-else-if="localDisplayConfig.championLogos?.individualChampion"
|
||
:src="localDisplayConfig.championLogos.individualChampion" alt="英雄冠军" class="champion-logo"
|
||
:style="{ width: localDisplayConfig.championLogos?.individualChampionSize + 'px', height: localDisplayConfig.championLogos?.individualChampionSize + 'px' }">
|
||
<div v-else class="champion-log">
|
||
<span
|
||
:style="{ fontSize: localDisplayConfig.championLogos?.individualChampionSize ? (localDisplayConfig.championLogos.individualChampionSize * 0.8) + 'px' : '2rem' }">
|
||
{{ filteredIndividualRankings[0]?.avatar || '👤' }}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
<div class="champion-name">
|
||
{{ filteredIndividualRankings[0]?.name || '暂无冠军' }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- 英雄排名 -->
|
||
<div class="individual-rankings-container">
|
||
<h2 class="game-subtitle">英雄排名</h2>
|
||
<div class="rank-table">
|
||
<div class="table-header" :style="{ 'grid-template-columns': individualGridTemplate }">
|
||
<span class="rank-col" :style="{ textAlign: localDisplayConfig.individual?.columnAlignments?.rank || 'left' }">排名</span>
|
||
<span v-if="localDisplayConfig.individual?.showAvatar" class="avatar-col">头像</span>
|
||
<span class="name-col" :style="{ textAlign: localDisplayConfig.individual?.columnAlignments?.name || 'left' }">姓名</span>
|
||
<span v-if="localDisplayConfig.individual?.showTeam" class="team-col" :style="{ textAlign: localDisplayConfig.individual?.columnAlignments?.team || 'left' }">{{
|
||
localDisplayConfig.individual?.teamColumn?.displayName || '战区' }}</span>
|
||
<span class="score-col" :style="{ textAlign: localDisplayConfig.individual?.columnAlignments?.score || 'left' }">{{ localDisplayConfig.individual?.scoreColumn?.displayName || '业绩' }}</span>
|
||
<span v-if="localDisplayConfig.individual?.showLevel" class="level-col" :style="{ textAlign: localDisplayConfig.individual?.columnAlignments?.level || 'left' }">等级</span>
|
||
<span v-if="localDisplayConfig.individual?.showDepartment" class="dept-col" :style="{ textAlign: localDisplayConfig.individual?.columnAlignments?.department || 'left' }">部门</span>
|
||
<span v-if="localDisplayConfig.individual?.showBonus" class="bonus-col" :style="{ textAlign: localDisplayConfig.individual?.columnAlignments?.bonus || 'left' }">奖金</span>
|
||
</div>
|
||
<div v-for="(item, index) in filteredIndividualRankings" :key="item.id" class="table-row"
|
||
:style="{ 'grid-template-columns': individualGridTemplate }" :class="{
|
||
'top-three': index < 3,
|
||
'highlight': index === 0
|
||
}">
|
||
<span class="rank-col" :style="{ textAlign: localDisplayConfig.individual?.columnAlignments?.rank || 'left' }">{{ index + 1 }}</span>
|
||
<span v-if="localDisplayConfig.individual?.showAvatar" class="avatar-col">
|
||
<img v-if="item.avatar && item.avatar.startsWith('/')" :src="item.avatar" alt="头像"
|
||
class="avatar-image">
|
||
<span v-else>{{ item.avatar || '👤' }}</span>
|
||
</span>
|
||
<span class="name-col" :style="{ textAlign: localDisplayConfig.individual?.columnAlignments?.name || 'left' }">{{ item.name || '-' }}</span>
|
||
<span v-if="localDisplayConfig.individual?.showTeam" class="team-col" :style="{ textAlign: localDisplayConfig.individual?.columnAlignments?.team || 'left' }">{{ item.team || '-' }}</span>
|
||
<span class="score-col" :style="{ textAlign: localDisplayConfig.individual?.columnAlignments?.score || 'left' }">{{ localDisplayConfig.individual?.scoreColumn?.displayStyle === 'amount' ? '¥' : '' }}{{ formatNumber(item.score) }}</span>
|
||
<span v-if="localDisplayConfig.individual?.showLevel" class="level-col" :style="{ textAlign: localDisplayConfig.individual?.columnAlignments?.level || 'left' }"
|
||
:class="`level-${item.level}`">{{ item.level }}</span>
|
||
<span v-if="localDisplayConfig.individual?.showDepartment" class="dept-col" :style="{ textAlign: localDisplayConfig.individual?.columnAlignments?.department || 'left' }">{{ item.department }}</span>
|
||
<span v-if="localDisplayConfig.individual?.showBonus" class="bonus-col" :style="{ textAlign: localDisplayConfig.individual?.columnAlignments?.bonus || 'left' }">¥{{ item.bonus }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- 浮动倒计时组件 -->
|
||
<div class="timer-float glow-border" :style="{ right: '20px', top: '10px' }">
|
||
<span class="label">距离结束还有:</span>
|
||
<div class="countdown" style="display: flex; gap: 15px;">
|
||
<span class="time-item timer-number">{{ days }}天</span>
|
||
<span class="time-item timer-number">{{ hours }}时</span>
|
||
<span class="time-item timer-number">{{ minutes }}分</span>
|
||
<span class="time-item timer-number">{{ seconds }}秒</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 浮动管理员入口 -->
|
||
<div class="admin-entry-float">
|
||
<button @click="goToAdmin" class="btn-game-secondary">
|
||
🔐 管理员入口
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, onBeforeMount, onMounted, onUnmounted, watch, computed, reactive, proxyRefs } from 'vue';
|
||
import { useRouter } from 'vue-router';
|
||
import {
|
||
individualRankings as importedIndividualRankings,
|
||
teamRankings as importedTeamRankings,
|
||
bonusRules,
|
||
displayConfig,
|
||
battleEndTime,
|
||
drumConfig,
|
||
initializeData
|
||
} from '../data/mockData.js';
|
||
import { readConfig } from '../services/configService.js';
|
||
|
||
// 创建默认显示配置的函数
|
||
function createDefaultDisplayConfig() {
|
||
return {
|
||
championLogos: {
|
||
teamChampion: '',
|
||
teamChampionSize: 60,
|
||
teamChampionType: 'avatar', // 'avatar' 或 'photo'
|
||
teamChampionPhotoWidth: 120,
|
||
teamChampionPhotoHeight: 80,
|
||
individualChampion: '',
|
||
individualChampionSize: 60
|
||
},
|
||
crownPosition: {
|
||
top: -100, // 皇冠位置的top值,默认-100px
|
||
left: null, // 皇冠位置的left值,默认null(使用居中)
|
||
right: null, // 皇冠位置的right值,默认null
|
||
bottom: null // 皇冠位置的bottom值,默认null
|
||
},
|
||
subtitleImage: {
|
||
src: '/completed_performance.png',
|
||
width: 200,
|
||
height: 60,
|
||
alt: '总战绩'
|
||
},
|
||
individual: {
|
||
scoreColumn: {
|
||
displayName: '业绩',
|
||
displayStyle: 'number'
|
||
},
|
||
teamColumn: {
|
||
displayName: '战区'
|
||
},
|
||
showLevel: true,
|
||
showDepartment: true,
|
||
showBonus: false,
|
||
showTeam: true, // 默认显示战区列
|
||
showAvatar: false, // 默认不显示头像列
|
||
defaultDisplayRows: 0, // 默认显示所有行
|
||
filterZeroScore: false, // 默认不过滤业绩为0的记录
|
||
columnWidths: {
|
||
team: 120 // 默认战区列宽
|
||
},
|
||
columnAlignments: {
|
||
rank: 'left',
|
||
name: 'left',
|
||
score: 'left',
|
||
level: 'left',
|
||
department: 'left',
|
||
team: 'left',
|
||
bonus: 'left'
|
||
}
|
||
},
|
||
team: {
|
||
totalScoreColumn: {
|
||
displayName: '战绩',
|
||
displayStyle: 'number'
|
||
},
|
||
showMemberCount: true,
|
||
showLeader: true,
|
||
showBonus: false,
|
||
defaultDisplayRows: 0, // 默认显示所有行
|
||
filterZeroScore: false, // 默认不过滤业绩为0的记录
|
||
columnWidths: {},
|
||
columnAlignments: {
|
||
rank: 'left',
|
||
name: 'left',
|
||
score: 'left',
|
||
memberCount: 'left',
|
||
leader: 'left',
|
||
bonus: 'left'
|
||
}
|
||
}
|
||
};
|
||
}
|
||
|
||
// 创建默认奖金规则的函数
|
||
function createDefaultBonusRules() {
|
||
return [
|
||
{ rank: '1-3', description: '前三名', individualBonus: '¥10000, ¥8000, ¥5000', teamBonus: '¥50000, ¥30000, ¥20000' },
|
||
{ rank: '4-10', description: '四至十名', individualBonus: '¥3000/人', teamBonus: '¥10000/队' },
|
||
{ rank: '11-20', description: '十一至二十名', individualBonus: '¥1000/人', teamBonus: '¥5000/队' }
|
||
];
|
||
}
|
||
|
||
// 初始化默认显示配置
|
||
const defaultDisplayConfig = createDefaultDisplayConfig();
|
||
|
||
// 深度合并配置,确保所有必要属性都存在
|
||
function mergeConfig(config1, config2) {
|
||
if (!config2) return config1;
|
||
const merged = { ...config1 };
|
||
for (const key in config2) {
|
||
if (config2.hasOwnProperty(key)) {
|
||
if (typeof config2[key] === 'object' && config2[key] !== null) {
|
||
merged[key] = mergeConfig(merged[key] || {}, config2[key]);
|
||
} else {
|
||
// 确保所有非对象类型的属性也被正确复制
|
||
merged[key] = config2[key];
|
||
}
|
||
}
|
||
}
|
||
return merged;
|
||
}
|
||
|
||
// 响应式数据 - 使用ref包装导入的数据,确保响应式更新
|
||
const individualRankings = ref(importedIndividualRankings || []);
|
||
const teamRankings = ref(importedTeamRankings || []);
|
||
|
||
// 计算属性 - 过滤后的个人排名数据
|
||
const filteredIndividualRankings = computed(() => {
|
||
let filtered = individualRankings.value;
|
||
// 如果配置了过滤业绩为0的记录
|
||
if (localDisplayConfig.value?.individual?.filterZeroScore) {
|
||
filtered = filtered.filter(item => item.score > 0);
|
||
}
|
||
return filtered;
|
||
});
|
||
|
||
// 计算属性 - 过滤后的战区排名数据
|
||
const filteredTeamRankings = computed(() => {
|
||
let filtered = teamRankings.value;
|
||
// 如果配置了过滤业绩为0的记录
|
||
if (localDisplayConfig.value?.team?.filterZeroScore) {
|
||
filtered = filtered.filter(item => item.totalScore > 0);
|
||
}
|
||
return filtered;
|
||
});
|
||
|
||
// 格式化数字为中国人习惯的金额表达方式(使用万分位分隔符)
|
||
function formatNumber(num) {
|
||
if (num === null || num === undefined || isNaN(num)) return '0';
|
||
const numStr = num.toString();
|
||
// 先处理小数部分
|
||
const parts = numStr.split('.');
|
||
let integerPart = parts[0];
|
||
const decimalPart = parts.length > 1 ? '.' + parts[1] : '';
|
||
|
||
// 对于整数部分,从右往左每四位添加一个逗号(万分位分隔)
|
||
const formattedInteger = integerPart.replace(/\B(?=(\d{4})+(?!\d))/g, ',');
|
||
|
||
return formattedInteger + decimalPart;
|
||
}
|
||
|
||
// 计算属性 - 所有战区业绩总和
|
||
const totalTeamScore = computed(() => {
|
||
const total = teamRankings.value.reduce((total, team) => {
|
||
return total + (team.totalScore || 0);
|
||
}, 0);
|
||
return formatNumber(total);
|
||
});
|
||
// 确保即使displayConfig存在,也要和默认配置合并,保证结构完整性
|
||
const localDisplayConfig = ref(() => {
|
||
if (displayConfig) {
|
||
const configCopy = JSON.parse(JSON.stringify(displayConfig));
|
||
const merged = mergeConfig(defaultDisplayConfig, configCopy);
|
||
|
||
// 确保所有必要属性都存在
|
||
if (!merged.individual) merged.individual = { ...defaultDisplayConfig.individual };
|
||
if (!merged.team) merged.team = { ...defaultDisplayConfig.team };
|
||
|
||
// 确保columnAlignments属性存在
|
||
if (!merged.individual.columnAlignments) {
|
||
merged.individual.columnAlignments = { ...defaultDisplayConfig.individual.columnAlignments };
|
||
}
|
||
if (!merged.team.columnAlignments) {
|
||
merged.team.columnAlignments = { ...defaultDisplayConfig.team.columnAlignments };
|
||
}
|
||
|
||
// 确保defaultDisplayRows属性存在
|
||
if (merged.individual.defaultDisplayRows === undefined) {
|
||
merged.individual.defaultDisplayRows = defaultDisplayConfig.individual.defaultDisplayRows;
|
||
}
|
||
if (merged.team.defaultDisplayRows === undefined) {
|
||
merged.team.defaultDisplayRows = defaultDisplayConfig.team.defaultDisplayRows;
|
||
}
|
||
|
||
return merged;
|
||
}
|
||
return defaultDisplayConfig;
|
||
});
|
||
const taskSettings = ref({
|
||
mainTitle: '3000万',
|
||
subtitle: '时间: 2025-11-12 - 2026-02-08'
|
||
});
|
||
|
||
// 加载任务设置和初始化所有数据
|
||
onBeforeMount(async () => {
|
||
try {
|
||
// 首先初始化所有数据,确保从服务器获取最新配置
|
||
await initializeData();
|
||
|
||
// 然后重新获取最新的数据
|
||
const config = await readConfig();
|
||
if (config) {
|
||
// 更新任务设置
|
||
if (config.taskSettings) {
|
||
taskSettings.value = config.taskSettings;
|
||
}
|
||
|
||
// 直接从config中更新排名数据,确保使用最新的服务器数据
|
||
if (config.individualRankings && config.individualRankings.length > 0) {
|
||
individualRankings.value = config.individualRankings;
|
||
}
|
||
|
||
if (config.teamRankings && config.teamRankings.length > 0) {
|
||
teamRankings.value = config.teamRankings;
|
||
}
|
||
|
||
// 更新显示配置
|
||
if (config.displayConfig) {
|
||
const configCopy = JSON.parse(JSON.stringify(config.displayConfig));
|
||
localDisplayConfig.value = mergeConfig(defaultDisplayConfig, configCopy);
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('加载数据失败:', error);
|
||
}
|
||
});
|
||
|
||
// 定义英雄排名表中每行默认高度
|
||
const tableIndividualTopThreeHeight = 3 * 47.5; // 前三名高度px
|
||
const tableIndividualRowHeight = 34.5; // 其他行高度px
|
||
const tableIndividualReserveHeight = 10; // 保留空间高度px
|
||
|
||
// 更新CSS变量,将默认显示行数传递给样式
|
||
watch(
|
||
() => localDisplayConfig.value?.individual?.defaultDisplayRows,
|
||
(newValue) => {
|
||
if (newValue && newValue > 0) {
|
||
const otherRowsHeight = (newValue - 3) * tableIndividualRowHeight;
|
||
const actualHeight = tableIndividualTopThreeHeight + otherRowsHeight + tableIndividualReserveHeight;
|
||
document.documentElement.style.setProperty('--individual-default-height', `${actualHeight}px`);
|
||
document.documentElement.style.setProperty('--individual-overflow-y', 'auto');
|
||
document.documentElement.style.setProperty('--individual-overflow-x', 'auto');
|
||
document.documentElement.style.setProperty('--individual-scroll-lock', '');
|
||
} else {
|
||
// 根据实际数据条数计算高度
|
||
const otherRowsHeight = (filteredIndividualRankings.value.length - 3) * tableIndividualRowHeight;
|
||
const actualHeight = tableIndividualTopThreeHeight + otherRowsHeight + tableIndividualReserveHeight;
|
||
document.documentElement.style.setProperty('--individual-default-height', `${actualHeight}px`);
|
||
document.documentElement.style.setProperty('--individual-overflow-y', 'hidden');
|
||
document.documentElement.style.setProperty('--individual-overflow-x', 'hidden');
|
||
document.documentElement.style.setProperty('--individual-scroll-lock', 'lock');
|
||
}
|
||
},
|
||
{ immediate: true }
|
||
);
|
||
|
||
// 当英雄排名数据变化时,重新计算高度(如果当前是显示所有行模式)
|
||
watch(
|
||
() => filteredIndividualRankings.value.length,
|
||
() => {
|
||
console.log('filteredIndividualRankings.value.length', filteredIndividualRankings.value.length);
|
||
const displayRows = localDisplayConfig.value?.individual?.defaultDisplayRows;
|
||
if (!displayRows || displayRows === 0) {
|
||
// 根据实际数据条数计算高度
|
||
const otherRowsHeight = (filteredIndividualRankings.value.length - 3) * tableIndividualRowHeight;
|
||
const actualHeight = tableIndividualTopThreeHeight + otherRowsHeight + tableIndividualReserveHeight;
|
||
document.documentElement.style.setProperty('--individual-default-height', `${actualHeight}px`);
|
||
document.documentElement.style.setProperty('--individual-overflow-y', 'hidden');
|
||
document.documentElement.style.setProperty('--individual-overflow-x', 'hidden');
|
||
document.documentElement.style.setProperty('--individual-scroll-lock', 'lock');
|
||
}
|
||
}
|
||
);
|
||
|
||
// 定义战区排名表中每行默认高度
|
||
const tableTeamTopThreeHeight = 3 * 48; // 前三名高度px
|
||
const tableTeamRowHeight = 48; // 其他行高度px
|
||
const tableTeamReserveHeight = 20; // 保留空间高度px
|
||
|
||
// 添加监听以同步战区排名默认显示行数配置到CSS变量
|
||
watch(
|
||
() => localDisplayConfig.value?.team?.defaultDisplayRows,
|
||
(newValue) => {
|
||
if (newValue && newValue > 0) {
|
||
const otherRowsHeight = (newValue - 3) * tableTeamRowHeight;
|
||
const actualHeight = tableTeamTopThreeHeight + otherRowsHeight + tableTeamReserveHeight;
|
||
document.documentElement.style.setProperty('--team-default-height', `${actualHeight}px`);
|
||
document.documentElement.style.setProperty('--team-overflow-y', 'auto');
|
||
document.documentElement.style.setProperty('--team-overflow-x', 'auto');
|
||
document.documentElement.style.setProperty('--team-scroll-lock', '');
|
||
} else {
|
||
// 根据实际数据条数计算高度
|
||
const otherRowsHeight = (teamRankings.value.length - 3) * tableTeamRowHeight;
|
||
const actualHeight = tableTeamTopThreeHeight + otherRowsHeight + tableTeamReserveHeight;
|
||
document.documentElement.style.setProperty('--team-default-height', `${actualHeight}px`);
|
||
document.documentElement.style.setProperty('--team-overflow-y', 'hidden');
|
||
document.documentElement.style.setProperty('--team-overflow-x', 'hidden');
|
||
document.documentElement.style.setProperty('--team-scroll-lock', 'lock');
|
||
}
|
||
},
|
||
{ immediate: true }
|
||
);
|
||
|
||
// 当战区数据变化时,重新计算高度(如果当前是显示所有行模式)
|
||
watch(
|
||
() => teamRankings.value.length,
|
||
() => {
|
||
const displayRows = localDisplayConfig.value?.team?.defaultDisplayRows;
|
||
if (!displayRows || displayRows === 0) {
|
||
// 根据实际数据条数计算高度
|
||
const otherRowsHeight = (teamRankings.value.length - 3) * tableTeamRowHeight;
|
||
const actualHeight = tableTeamTopThreeHeight + otherRowsHeight + tableTeamReserveHeight;
|
||
document.documentElement.style.setProperty('--team-default-height', `${actualHeight}px`);
|
||
document.documentElement.style.setProperty('--team-overflow-y', 'hidden');
|
||
document.documentElement.style.setProperty('--team-overflow-x', 'hidden');
|
||
document.documentElement.style.setProperty('--team-scroll-lock', 'lock');
|
||
}
|
||
}
|
||
);
|
||
|
||
// 监听冠军字体大小配置变化,更新CSS变量
|
||
watch(
|
||
() => localDisplayConfig.value?.championLogos,
|
||
(newConfig) => {
|
||
if (newConfig) {
|
||
// 设置战区冠军字体大小
|
||
if (newConfig.teamChampionFontSize) {
|
||
document.documentElement.style.setProperty('--team-champion-font-size', newConfig.teamChampionFontSize + 'rem');
|
||
}
|
||
// 设置英雄冠军字体大小
|
||
if (newConfig.individualChampionFontSize) {
|
||
document.documentElement.style.setProperty('--individual-champion-font-size', newConfig.individualChampionFontSize + 'rem');
|
||
}
|
||
}
|
||
},
|
||
{ immediate: true, deep: true }
|
||
);
|
||
|
||
// 监听皇冠位置配置变化,更新皇冠位置
|
||
watch(
|
||
() => localDisplayConfig.value?.crownPosition,
|
||
(newConfig) => {
|
||
if (newConfig) {
|
||
// 更新top值
|
||
if (newConfig.top !== null && newConfig.top !== undefined) {
|
||
document.documentElement.style.setProperty('--crown-top', newConfig.top + 'px');
|
||
} else {
|
||
document.documentElement.style.removeProperty('--crown-top');
|
||
}
|
||
|
||
// 更新left值
|
||
if (newConfig.left !== null && newConfig.left !== undefined) {
|
||
document.documentElement.style.setProperty('--crown-left', newConfig.left + 'px');
|
||
} else {
|
||
document.documentElement.style.removeProperty('--crown-left');
|
||
}
|
||
|
||
// 更新right值
|
||
if (newConfig.right !== null && newConfig.right !== undefined) {
|
||
document.documentElement.style.setProperty('--crown-right', newConfig.right + 'px');
|
||
} else {
|
||
document.documentElement.style.removeProperty('--crown-right');
|
||
}
|
||
|
||
// 更新bottom值
|
||
if (newConfig.bottom !== null && newConfig.bottom !== undefined) {
|
||
document.documentElement.style.setProperty('--crown-bottom', newConfig.bottom + 'px');
|
||
} else {
|
||
document.documentElement.style.removeProperty('--crown-bottom');
|
||
}
|
||
}
|
||
},
|
||
{ deep: true, immediate: true }
|
||
);
|
||
|
||
// 确保奖金规则有默认值
|
||
const displayBonusRules = computed(() => {
|
||
return Array.isArray(bonusRules) && bonusRules.length > 0
|
||
? bonusRules
|
||
: createDefaultBonusRules();
|
||
});
|
||
|
||
const router = useRouter();
|
||
|
||
// 计算英雄排名表格的列布局
|
||
const individualGridTemplate = computed(() => {
|
||
try {
|
||
const config = localDisplayConfig.value?.individual;
|
||
if (!config) return '60px 1fr 120px 80px 80px';
|
||
|
||
// 确保showTeam默认为true
|
||
if (config.showTeam === undefined) {
|
||
config.showTeam = true;
|
||
}
|
||
|
||
const widths = config.columnWidths || {};
|
||
const cols = [];
|
||
|
||
cols.push((widths.rank || 60) + 'px'); // 排名
|
||
|
||
// 头像列 - 根据配置决定是否显示
|
||
if (config.showAvatar) {
|
||
cols.push((widths.avatar || 60) + 'px'); // 头像
|
||
}
|
||
|
||
cols.push((widths.name === 1 || widths.name === '1') ? '1fr' : (widths.name || 120) + 'px'); // 姓名
|
||
|
||
// 战区列(在姓名列后面)
|
||
if (config.showTeam) {
|
||
cols.push((widths.team === 1 || widths.team === '1') ? '1fr' : (widths.team || 120) + 'px'); // 战区
|
||
}
|
||
|
||
cols.push((widths.score || 80) + 'px'); // 分数
|
||
|
||
if (config.showLevel) {
|
||
cols.push((widths.level || 80) + 'px'); // 等级
|
||
}
|
||
|
||
if (config.showDepartment) {
|
||
cols.push((widths.department === 1 || widths.department === '1') ? '1fr' : (widths.department || 100) + 'px'); // 部门
|
||
}
|
||
|
||
if (config.showBonus) {
|
||
cols.push((widths.bonus || 80) + 'px'); // 奖金
|
||
}
|
||
|
||
return cols.join(' ');
|
||
} catch (error) {
|
||
console.error('计算英雄排名表格布局出错:', error);
|
||
return '60px 1fr 120px 80px 80px'; // 兜底布局(不包含头像列)
|
||
}
|
||
});
|
||
|
||
// 计算战区排名表格的列布局
|
||
const teamGridTemplate = computed(() => {
|
||
try {
|
||
const config = localDisplayConfig.value?.team;
|
||
if (!config) return '60px 1fr 80px 80px';
|
||
|
||
const widths = config.columnWidths || {};
|
||
const cols = [];
|
||
|
||
cols.push((widths.rank || 60) + 'px'); // 排名
|
||
cols.push((widths.name === 1 || widths.name === '1') ? '1fr' : (widths.name || 150) + 'px'); // 战区名
|
||
cols.push((widths.score || 80) + 'px'); // 分数
|
||
|
||
if (config.showMemberCount) {
|
||
cols.push((widths.memberCount || 60) + 'px'); // 人数
|
||
}
|
||
|
||
if (config.showLeader) {
|
||
cols.push((widths.leader === 1 || widths.leader === '1') ? '1fr' : (widths.leader || 120) + 'px'); // 队长
|
||
}
|
||
|
||
if (config.showBonus) {
|
||
cols.push((widths.bonus || 80) + 'px'); // 奖金
|
||
}
|
||
|
||
return cols.join(' ');
|
||
} catch (error) {
|
||
console.error('计算战区排名表格布局出错:', error);
|
||
return '60px 1fr 80px 80px'; // 兜底布局
|
||
}
|
||
});
|
||
|
||
// 倒计时状态
|
||
const days = ref(0);
|
||
const hours = ref(0);
|
||
const minutes = ref(0);
|
||
const seconds = ref(0);
|
||
|
||
// 战鼓动画状态
|
||
const isBeating = ref(false);
|
||
let beatInterval = null;
|
||
let countdownInterval = null;
|
||
|
||
// 音频上下文和战鼓音效
|
||
let audioContext = null;
|
||
const isPlayingSound = ref(false);
|
||
|
||
// 战鼓位置状态
|
||
const drumsPosition = ref({ x: 20, y: 20 });
|
||
// 倒计时位置状态已移除,直接在模板中使用固定位置
|
||
// 奖金设置模块位置状态 - 使用reactive存储实际定位值
|
||
const bonusPosition = reactive({ x: 'auto', y: 'auto' });
|
||
|
||
let isDragging = false;
|
||
let isBonusDragging = false;
|
||
let dragOffset = { x: 0, y: 0 };
|
||
let bonusDragOffset = { x: 0, y: 0 };
|
||
|
||
// 节流函数
|
||
function throttle(func, limit) {
|
||
let inThrottle;
|
||
return function () {
|
||
const args = arguments;
|
||
const context = this;
|
||
if (!inThrottle) {
|
||
func.apply(context, args);
|
||
inThrottle = true;
|
||
setTimeout(() => inThrottle = false, limit);
|
||
}
|
||
};
|
||
}
|
||
|
||
// 开始拖动战鼓
|
||
const startDrag = (e) => {
|
||
isDragging = true;
|
||
dragOffset.x = e.clientX - drumsPosition.value.x;
|
||
dragOffset.y = e.clientY - drumsPosition.value.y;
|
||
e.preventDefault();
|
||
};
|
||
|
||
// 开始拖动奖金模块(鼠标事件)
|
||
const startBonusDrag = (e) => {
|
||
e.stopPropagation(); // 阻止事件冒泡
|
||
isBonusDragging = true;
|
||
// 计算鼠标相对于元素左上角的偏移量
|
||
const rect = e.currentTarget.getBoundingClientRect();
|
||
bonusDragOffset.x = e.clientX - rect.left;
|
||
bonusDragOffset.y = e.clientY - rect.top;
|
||
// 提高拖动时的性能,临时禁用动画效果
|
||
e.currentTarget.style.transition = 'none';
|
||
e.preventDefault();
|
||
};
|
||
|
||
// 开始拖动奖金模块(触摸事件)
|
||
const startBonusTouch = (e) => {
|
||
e.stopPropagation(); // 阻止事件冒泡
|
||
e.preventDefault(); // 阻止默认行为,如滚动
|
||
|
||
const touch = e.touches[0];
|
||
isBonusDragging = true;
|
||
|
||
// 计算触摸点相对于元素左上角的偏移量
|
||
const rect = e.currentTarget.getBoundingClientRect();
|
||
bonusDragOffset.x = touch.clientX - rect.left;
|
||
bonusDragOffset.y = touch.clientY - rect.top;
|
||
|
||
// 提高拖动时的性能,临时禁用动画效果
|
||
e.currentTarget.style.transition = 'none';
|
||
};
|
||
|
||
// 结束拖动(鼠标和触摸事件)
|
||
const endDrag = (e) => {
|
||
isDragging = false;
|
||
|
||
if (isBonusDragging) {
|
||
// 恢复动画效果
|
||
const bonusElement = document.querySelector('.bonus-section');
|
||
if (bonusElement) {
|
||
bonusElement.style.transition = '';
|
||
}
|
||
}
|
||
isBonusDragging = false;
|
||
};
|
||
|
||
// 结束触摸拖动
|
||
const endTouch = (e) => {
|
||
if (isBonusDragging) {
|
||
// 恢复动画效果
|
||
const bonusElement = document.querySelector('.bonus-section');
|
||
if (bonusElement) {
|
||
bonusElement.style.transition = '';
|
||
}
|
||
}
|
||
isBonusDragging = false;
|
||
};
|
||
|
||
// 优化的拖动函数 - 使用节流减少更新频率(鼠标事件)
|
||
const drag = throttle((e) => {
|
||
if (isDragging) {
|
||
drumsPosition.value.x = e.clientX - dragOffset.x;
|
||
drumsPosition.value.y = e.clientY - dragOffset.y;
|
||
}
|
||
if (isBonusDragging) {
|
||
// 计算新的位置
|
||
const newX = e.clientX - bonusDragOffset.x;
|
||
const newY = e.clientY - bonusDragOffset.y;
|
||
|
||
// 确保元素不会被拖出视口
|
||
const windowWidth = window.innerWidth;
|
||
const windowHeight = window.innerHeight;
|
||
const element = document.querySelector('.bonus-section');
|
||
|
||
if (element) {
|
||
const elementWidth = element.offsetWidth;
|
||
const elementHeight = element.offsetHeight;
|
||
|
||
// 限制在视口内,添加一些边距
|
||
const limitedX = Math.max(10, Math.min(newX, windowWidth - elementWidth - 10));
|
||
const limitedY = Math.max(10, Math.min(newY, windowHeight - elementHeight - 10));
|
||
|
||
// 使用优化的更新函数更新奖金模块位置
|
||
requestAnimationFrame(() => {
|
||
bonusPosition.x = limitedX;
|
||
bonusPosition.y = limitedY;
|
||
// 直接设置元素样式确保实时更新
|
||
element.style.left = `${limitedX}px`;
|
||
element.style.top = `${limitedY}px`;
|
||
element.style.right = 'auto';
|
||
element.style.bottom = 'auto';
|
||
});
|
||
}
|
||
}
|
||
}, 10); // 10ms节流,约100FPS
|
||
|
||
// 优化的触摸拖动函数 - 使用节流减少更新频率
|
||
const touchMove = throttle((e) => {
|
||
if (isBonusDragging) {
|
||
e.preventDefault(); // 阻止页面滚动
|
||
|
||
const touch = e.touches[0];
|
||
// 计算新的位置
|
||
const newX = touch.clientX - bonusDragOffset.x;
|
||
const newY = touch.clientY - bonusDragOffset.y;
|
||
|
||
// 确保元素不会被拖出视口
|
||
const windowWidth = window.innerWidth;
|
||
const windowHeight = window.innerHeight;
|
||
const element = document.querySelector('.bonus-section');
|
||
|
||
if (element) {
|
||
const elementWidth = element.offsetWidth;
|
||
const elementHeight = element.offsetHeight;
|
||
|
||
// 限制在视口内,添加一些边距
|
||
const limitedX = Math.max(10, Math.min(newX, windowWidth - elementWidth - 10));
|
||
const limitedY = Math.max(10, Math.min(newY, windowHeight - elementHeight - 10));
|
||
|
||
// 使用优化的更新函数更新奖金模块位置
|
||
requestAnimationFrame(() => {
|
||
bonusPosition.x = limitedX;
|
||
bonusPosition.y = limitedY;
|
||
// 直接设置元素样式确保实时更新
|
||
element.style.left = `${limitedX}px`;
|
||
element.style.top = `${limitedY}px`;
|
||
element.style.right = 'auto';
|
||
element.style.bottom = 'auto';
|
||
});
|
||
}
|
||
}
|
||
}, 10); // 10ms节流,约100FPS
|
||
|
||
// 计算倒计时
|
||
const calculateCountdown = () => {
|
||
// 使用配置的结束时间
|
||
const endDateStr = `${battleEndTime.date}T${battleEndTime.time}`;
|
||
const endDate = new Date(endDateStr).getTime();
|
||
const now = new Date().getTime();
|
||
const distance = endDate - now;
|
||
|
||
if (distance > 0) {
|
||
days.value = Math.floor(distance / (1000 * 60 * 60 * 24));
|
||
hours.value = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
|
||
minutes.value = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
|
||
seconds.value = Math.floor((distance % (1000 * 60)) / 1000);
|
||
}
|
||
};
|
||
|
||
// 初始化音频上下文
|
||
const initAudioContext = () => {
|
||
if (!audioContext) {
|
||
audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
||
}
|
||
};
|
||
|
||
// 音频缓存,用于存储加载的MP3文件
|
||
const audioBufferCache = ref({});
|
||
|
||
// 加载MP3文件到音频缓冲区
|
||
const loadAudioFile = async (filePath) => {
|
||
try {
|
||
// 检查是否已缓存
|
||
if (audioBufferCache.value[filePath]) {
|
||
return audioBufferCache.value[filePath];
|
||
}
|
||
|
||
// 加载音频文件
|
||
const response = await fetch(filePath);
|
||
const arrayBuffer = await response.arrayBuffer();
|
||
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
|
||
|
||
// 缓存音频缓冲区
|
||
audioBufferCache.value[filePath] = audioBuffer;
|
||
return audioBuffer;
|
||
} catch (error) {
|
||
console.error('加载音频文件失败:', error);
|
||
return null;
|
||
}
|
||
};
|
||
|
||
// 播放战鼓音效
|
||
const playDrumSound = async (isStrongBeat = false) => {
|
||
// 检查是否启用声音播放
|
||
if (!audioContext || isPlayingSound.value || drumConfig?.sound?.enabled === false) return;
|
||
|
||
isPlayingSound.value = true;
|
||
|
||
try {
|
||
// 使用配置的音效参数
|
||
const soundConfig = drumConfig?.sound || {};
|
||
const patternConfig = drumConfig?.pattern || {};
|
||
|
||
// 检查是否配置了MP3文件路径
|
||
if (soundConfig.soundSrc && soundConfig.soundSrc.trim() !== '') {
|
||
// 使用MP3文件播放
|
||
const audioBuffer = await loadAudioFile(soundConfig.soundSrc);
|
||
|
||
if (audioBuffer) {
|
||
// 创建音频源节点
|
||
const source = audioContext.createBufferSource();
|
||
const gainNode = audioContext.createGain();
|
||
|
||
// 连接节点
|
||
source.buffer = audioBuffer;
|
||
source.connect(gainNode);
|
||
gainNode.connect(audioContext.destination);
|
||
|
||
// 设置音量,支持强拍音量增强
|
||
const baseVolume = soundConfig.volume || 1.0;
|
||
const accentMultiplier = patternConfig.accentMultiplier || 1.2;
|
||
const volume = isStrongBeat ? baseVolume * accentMultiplier : baseVolume;
|
||
gainNode.gain.value = volume;
|
||
|
||
// 播放声音
|
||
source.start(0);
|
||
|
||
// 设置完成后重置播放状态
|
||
setTimeout(() => {
|
||
isPlayingSound.value = false;
|
||
}, audioBuffer.duration * 1000 + 100);
|
||
|
||
return;
|
||
}
|
||
}
|
||
|
||
// 如果没有配置MP3文件或加载失败,回退到合成音效
|
||
// 创建振荡器节点
|
||
const oscillator = audioContext.createOscillator();
|
||
const gainNode = audioContext.createGain();
|
||
|
||
// 连接节点
|
||
oscillator.connect(gainNode);
|
||
gainNode.connect(audioContext.destination);
|
||
|
||
// 设置战鼓音效参数,支持强拍
|
||
const baseVolume = soundConfig.volume || 1.0;
|
||
// 使用pattern配置中的强拍音量倍数
|
||
const accentMultiplier = patternConfig.accentMultiplier || 1.2;
|
||
const volume = isStrongBeat ? baseVolume * accentMultiplier : baseVolume;
|
||
|
||
// 使用soundConfig中的type1
|
||
oscillator.type = soundConfig.type1 || 'sine';
|
||
|
||
// 基础频率,支持强拍频率偏移
|
||
const baseFrequency = soundConfig.frequency1 || 150;
|
||
const frequencyOffset = isStrongBeat ? (patternConfig.accentFrequencyOffset || 0) / 100 : 0;
|
||
const actualFrequency = baseFrequency * (1 + frequencyOffset);
|
||
|
||
oscillator.frequency.setValueAtTime(actualFrequency, audioContext.currentTime);
|
||
// 频率渐变
|
||
oscillator.frequency.exponentialRampToValueAtTime(actualFrequency * 0.5, audioContext.currentTime + 0.1);
|
||
|
||
// 设置音量包络
|
||
gainNode.gain.setValueAtTime(0, audioContext.currentTime);
|
||
gainNode.gain.linearRampToValueAtTime(volume, audioContext.currentTime + (soundConfig.attackTime || 0.01));
|
||
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + (soundConfig.decayTime || 0.3));
|
||
|
||
// 播放声音
|
||
oscillator.start();
|
||
oscillator.stop(audioContext.currentTime + (soundConfig.decayTime || 0.3));
|
||
|
||
// 双音调效果 - 始终使用
|
||
const oscillator2 = audioContext.createOscillator();
|
||
const gainNode2 = audioContext.createGain();
|
||
|
||
oscillator2.connect(gainNode2);
|
||
gainNode2.connect(audioContext.destination);
|
||
|
||
oscillator2.type = soundConfig.type2 || 'triangle';
|
||
oscillator2.frequency.setValueAtTime(soundConfig.frequency2 || 100, audioContext.currentTime);
|
||
|
||
gainNode2.gain.setValueAtTime(0, audioContext.currentTime);
|
||
gainNode2.gain.linearRampToValueAtTime(volume * 0.8, audioContext.currentTime + (soundConfig.attackTime || 0.01));
|
||
gainNode2.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + (soundConfig.decayTime || 0.3) + 0.2);
|
||
|
||
oscillator2.start();
|
||
oscillator2.stop(audioContext.currentTime + (soundConfig.decayTime || 0.3) + 0.2);
|
||
|
||
// 设置完成后重置播放状态
|
||
setTimeout(() => {
|
||
isPlayingSound.value = false;
|
||
}, (soundConfig.decayTime || 0.3) * 1000 + 150);
|
||
} catch (error) {
|
||
console.error('播放战鼓音效出错:', error);
|
||
isPlayingSound.value = false;
|
||
}
|
||
};
|
||
|
||
// 修改相关函数以支持异步
|
||
const handleDrumClick = async () => {
|
||
// 如果音频上下文未初始化,初始化它
|
||
if (!audioContext) {
|
||
initAudioContext();
|
||
}
|
||
|
||
// 如果音频上下文被暂停,恢复它
|
||
if (audioContext.state === 'suspended') {
|
||
audioContext.resume();
|
||
}
|
||
|
||
// 触发战鼓动画和音效,使用配置的点击效果
|
||
const animationConfig = drumConfig?.animation || {};
|
||
isBeating.value = true;
|
||
await playDrumSound(true); // 点击总是强拍
|
||
|
||
setTimeout(() => {
|
||
isBeating.value = false;
|
||
}, animationConfig.clickBeatDuration || 250);
|
||
};
|
||
|
||
// 战鼓动画效果
|
||
const startDrumAnimation = () => {
|
||
// 检查是否显示战鼓
|
||
if (drumConfig?.showDrum === false) return;
|
||
|
||
// 使用配置的动画和节拍参数
|
||
const animationConfig = drumConfig?.animation || {};
|
||
const patternConfig = drumConfig?.pattern || {};
|
||
|
||
// 检查是否启用动画
|
||
if (animationConfig.enabled === false) return;
|
||
|
||
let beatCount = 0;
|
||
// 使用配置的节拍间隔
|
||
const interval = animationConfig.beatInterval || 200;
|
||
|
||
beatInterval = setInterval(() => {
|
||
beatCount++;
|
||
// 使用配置的节拍模式和总拍数
|
||
const totalBeats = patternConfig.totalBeats || 4;
|
||
const currentBeat = ((beatCount - 1) % totalBeats) + 1;
|
||
|
||
// 根据节拍模式确定是否是强拍
|
||
const strongBeats = patternConfig.strongBeats || [1, 4];
|
||
const isStrongBeat = strongBeats.includes(currentBeat);
|
||
|
||
// 执行动画和音效
|
||
isBeating.value = true;
|
||
|
||
// 根据是否是强拍播放音效
|
||
playDrumSound(isStrongBeat);
|
||
|
||
// 设置CSS变量,支持强拍动画增强
|
||
const drums = document.querySelectorAll('.drum');
|
||
drums.forEach(drum => {
|
||
// 使用配置的动画参数
|
||
drum.style.setProperty('--drum-scale', isStrongBeat ?
|
||
(animationConfig.beatScale || 1.3) * (1 + (patternConfig.accentAnimation || 0) / 100) :
|
||
(animationConfig.beatScale || 1.3));
|
||
drum.style.setProperty('--drum-translate-y', isStrongBeat ?
|
||
`${(animationConfig.beatTranslateY || -15) * (1 + (patternConfig.accentAnimation || 0) / 100)}px` :
|
||
`${animationConfig.beatTranslateY || -15}px`);
|
||
drum.style.setProperty('--drum-rotate', `${animationConfig.beatRotate || 5}deg`);
|
||
drum.style.setProperty('--drum-brightness', isStrongBeat ? '1.4' : '1.3');
|
||
drum.style.setProperty('--drum-saturation', isStrongBeat ? '1.3' : '1.2');
|
||
});
|
||
|
||
// 根据节拍类型设置持续时间
|
||
const beatDuration = isStrongBeat
|
||
? (animationConfig.beatDuration || 150)
|
||
: (animationConfig.beatDuration || 100);
|
||
|
||
setTimeout(() => {
|
||
isBeating.value = false;
|
||
}, beatDuration);
|
||
}, interval);
|
||
};
|
||
|
||
// 已移至文件中异步版本的handleDrumClick函数
|
||
|
||
// 跳转到管理员页面
|
||
const goToAdmin = () => {
|
||
router.push('/admin');
|
||
};
|
||
|
||
// 监听窗口点击事件,用于用户交互后初始化音频上下文
|
||
document.addEventListener('click', initAudioContext, { once: true });
|
||
document.addEventListener('touchstart', initAudioContext, { once: true });
|
||
|
||
const handleResize = () => {
|
||
// 计算并设置排名明细区域的最小高度,使其底部与视口对齐
|
||
const rankingsSection = document.querySelector('.rankings-section');
|
||
if (rankingsSection) {
|
||
const windowHeight = window.innerHeight;
|
||
const rankingsTop = rankingsSection.offsetTop;
|
||
const rankingsMinHeight = windowHeight - rankingsTop - 20; // 减去一些边距
|
||
if (rankingsMinHeight > 0) {
|
||
rankingsSection.style.minHeight = rankingsMinHeight + 'px';
|
||
}
|
||
}
|
||
};
|
||
|
||
onMounted(async () => {
|
||
try {
|
||
// 异步初始化数据
|
||
await initializeData();
|
||
|
||
// 更新本地显示配置,确保columnAlignments属性存在
|
||
if (displayConfig) {
|
||
const configCopy = JSON.parse(JSON.stringify(displayConfig));
|
||
const defaultConfig = createDefaultDisplayConfig();
|
||
const merged = mergeConfig(defaultConfig, configCopy);
|
||
|
||
// 确保columnAlignments属性存在
|
||
if (!merged.individual.columnAlignments) {
|
||
merged.individual.columnAlignments = { ...defaultConfig.individual.columnAlignments };
|
||
}
|
||
if (!merged.team.columnAlignments) {
|
||
merged.team.columnAlignments = { ...defaultConfig.team.columnAlignments };
|
||
}
|
||
|
||
localDisplayConfig.value = merged;
|
||
} else {
|
||
localDisplayConfig.value = createDefaultDisplayConfig();
|
||
}
|
||
} catch (error) {
|
||
console.error('初始化数据失败:', error);
|
||
// 使用默认配置
|
||
localDisplayConfig.value = createDefaultDisplayConfig();
|
||
}
|
||
|
||
calculateCountdown();
|
||
countdownInterval = setInterval(calculateCountdown, 10); // 改为10ms更新一次以显示毫秒
|
||
startDrumAnimation();
|
||
// 监听窗口大小变化,确保排名明细与底部对齐
|
||
window.addEventListener('resize', handleResize);
|
||
handleResize(); // 初始调整
|
||
|
||
// 添加拖放相关的事件监听
|
||
document.addEventListener('mousemove', drag);
|
||
document.addEventListener('mouseup', endDrag);
|
||
|
||
// 添加触摸事件监听
|
||
document.addEventListener('touchmove', touchMove, { passive: false });
|
||
document.addEventListener('touchend', endTouch);
|
||
document.addEventListener('touchcancel', endTouch);
|
||
});
|
||
|
||
// 监听结束时间变化(在真实环境中,可能需要通过props或store来监听)
|
||
const handleBattleEndTimeChange = () => {
|
||
calculateCountdown();
|
||
};
|
||
|
||
// 监听显示配置变化(在真实环境中,可能需要通过props或store来监听)
|
||
const handleDisplayConfigChange = () => {
|
||
// 更新本地配置
|
||
localDisplayConfig.value = { ...displayConfig };
|
||
};
|
||
|
||
// 这里模拟监听配置变化
|
||
// 在实际项目中,可能需要通过WebSocket或轮询来更新配置
|
||
|
||
onUnmounted(() => {
|
||
if (countdownInterval) clearInterval(countdownInterval);
|
||
if (beatInterval) clearInterval(beatInterval);
|
||
window.removeEventListener('resize', handleResize);
|
||
|
||
// 移除拖放相关的事件监听
|
||
document.removeEventListener('mousemove', drag);
|
||
document.removeEventListener('mouseup', endDrag);
|
||
|
||
// 移除触摸事件监听
|
||
document.removeEventListener('touchmove', touchMove);
|
||
document.removeEventListener('touchend', endTouch);
|
||
document.removeEventListener('touchcancel', endTouch);
|
||
|
||
// 清理音频资源
|
||
if (audioContext) {
|
||
audioContext.close();
|
||
audioContext = null;
|
||
}
|
||
});
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
@keyframes crownFloat {
|
||
0% {
|
||
transform: translateY(0px) rotate(0deg);
|
||
}
|
||
50% {
|
||
transform: translateY(-5px) rotate(2deg);
|
||
}
|
||
100% {
|
||
transform: translateY(0px) rotate(0deg);
|
||
}
|
||
}
|
||
.avatar-image-champion {
|
||
object-fit: cover;
|
||
border-radius: 50%;
|
||
}
|
||
|
||
/* 冠军模块样式 */
|
||
.champion-section {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
padding: 20px;
|
||
gap: 20px;
|
||
}
|
||
|
||
.team-champion,
|
||
.individual-champion {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
text-align: center;
|
||
padding: 15px;
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.team-logo {
|
||
font-size: 0;
|
||
margin-bottom: 0;
|
||
display: inline-block;
|
||
color: gold;
|
||
}
|
||
|
||
.individual-avatar {
|
||
font-size: 2rem;
|
||
margin-bottom: 5px;
|
||
display: inline-block;
|
||
}
|
||
|
||
/* 冠军头像容器 */
|
||
.champion-container {
|
||
position: relative;
|
||
display: inline-block;
|
||
}
|
||
|
||
/* 皇冠动画效果 */
|
||
.crown-animation {
|
||
position: absolute;
|
||
left: var(--crown-left, 50%);
|
||
right: var(--crown-right, auto);
|
||
top: var(--crown-top, -100px);
|
||
bottom: var(--crown-bottom, auto);
|
||
transform: var(--crown-left, translateX(-50%));
|
||
z-index: 10;
|
||
text-shadow: 0 0 10px var(--gold-primary), 0 0 20px var(--gold-secondary);
|
||
}
|
||
|
||
.crown-animation-run {
|
||
animation: crownFloat 2s ease-in-out infinite, crownGlow 3s ease-in-out infinite;
|
||
}
|
||
|
||
/* 皇冠浮动动画 */
|
||
@keyframes crownFloat {
|
||
0% {
|
||
transform: translateX(-50%) translateY(0) rotate(-10deg);
|
||
}
|
||
50% {
|
||
transform: translateX(-50%) translateY(-5px) rotate(10deg);
|
||
}
|
||
100% {
|
||
transform: translateX(-50%) translateY(0) rotate(-10deg);
|
||
}
|
||
}
|
||
|
||
/* 皇冠发光效果动画 */
|
||
@keyframes crownGlow {
|
||
0%, 100% {
|
||
text-shadow: 0 0 10px var(--gold-primary), 0 0 20px var(--gold-secondary);
|
||
}
|
||
50% {
|
||
text-shadow: 0 0 15px var(--gold-primary), 0 0 30px var(--gold-secondary), 0 0 40px var(--gold-tertiary);
|
||
}
|
||
}
|
||
|
||
|
||
@keyframes crownGlow {
|
||
0%, 100% {
|
||
text-shadow: 0 0 10px var(--gold-primary), 0 0 20px var(--gold-secondary);
|
||
}
|
||
50% {
|
||
text-shadow: 0 0 15px var(--gold-primary), 0 0 30px var(--gold-secondary), 0 0 40px rgba(255, 215, 0, 0.5);
|
||
}
|
||
}
|
||
|
||
.champion-logo {
|
||
object-fit: cover;
|
||
max-width: 120px;
|
||
border-radius: 50%;
|
||
}
|
||
|
||
.photo-container .champion-logo {
|
||
max-width: unset !important;
|
||
border-radius: 0 !important;
|
||
}
|
||
|
||
|
||
.avatar-image {
|
||
width: 30px;
|
||
height: 30px;
|
||
object-fit: cover;
|
||
vertical-align: middle;
|
||
border-radius: 50%;
|
||
}
|
||
|
||
.champion-title {
|
||
font-size: 0.9rem;
|
||
color: #999;
|
||
margin-bottom: 3px;
|
||
}
|
||
|
||
.champion-name {
|
||
font-size: var(--individual-champion-font-size, 1.8rem);
|
||
font-weight: bold;
|
||
color: gold;
|
||
}
|
||
|
||
/* 战区冠军名称特殊样式 */
|
||
.team-champion .champion-name {
|
||
font-size: var(--team-champion-font-size, 1.8rem);
|
||
}
|
||
|
||
/* 基础样式 */
|
||
:root {
|
||
--gold-primary: #ffd700;
|
||
--gold-secondary: #ffed4a;
|
||
--purple-primary: #6c5ce7;
|
||
--purple-secondary: #a29bfe;
|
||
}
|
||
|
||
|
||
/* 主题容器样式 - 添加居中对齐 */
|
||
.theme-container {
|
||
text-align: center;
|
||
}
|
||
|
||
.bai-day-battle {
|
||
width: 1200px;
|
||
}
|
||
|
||
/* Banner图片样式 - 居中显示 */
|
||
.banner-image {
|
||
max-width: 90%;
|
||
height: auto;
|
||
display: inline-block;
|
||
border-radius: 8px;
|
||
}
|
||
|
||
.bonus-section {
|
||
position: fixed;
|
||
cursor: move;
|
||
transition: transform 0.2s ease;
|
||
z-index: 9999; /* 设置最高层级,确保在最上层显示 */
|
||
}
|
||
|
||
/* 奖金图片容器样式 - Flex布局居中且保持一行 */
|
||
.bonus-awards-container {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
gap: 5px;
|
||
flex-wrap: nowrap;
|
||
}
|
||
|
||
/* 奖金图片样式 */
|
||
.award-image {
|
||
width: auto;
|
||
height: 120px;
|
||
/* border-radius: 8px; */
|
||
/* box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); */
|
||
/* transition: transform 0.2s ease, box-shadow 0.2s ease; */
|
||
}
|
||
|
||
.award-image:hover {
|
||
transform: translateY(-5px);
|
||
/* box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15); */
|
||
}
|
||
|
||
|
||
/* 响应式设计 */
|
||
@media (max-width: 768px) {
|
||
|
||
.total-score-total-image {
|
||
width: 280px !important; /* 调整总战绩图片大小,覆盖内联样式 */
|
||
width: 340px !important; /* 调整总战绩图片大小,覆盖内联样式 */
|
||
}
|
||
|
||
/* 移动端总战绩金额字体调小 */
|
||
.total-score-total-value {
|
||
font-size: 5rem !important; /* 调小总战绩金额字体 */
|
||
margin-top: -40px !important; /* 调整与图片的间距 */
|
||
}
|
||
|
||
.team-rankings-propagation-image {
|
||
width: 200px !important; /* 覆盖内联样式 */
|
||
}
|
||
|
||
.individual-rankings-propagation-image {
|
||
width: 200px !important; /* 覆盖内联样式 */
|
||
}
|
||
|
||
/* 战区排名宣传语容器调整 */
|
||
.team-rankings-propagation {
|
||
position: relative;
|
||
z-index: 5; /* 提高z-index避免被其他元素遮挡 */
|
||
margin-bottom: 70px; /* 进一步增大战区冠军的间距 */
|
||
}
|
||
|
||
/* 英雄排名宣传语容器调整 */
|
||
.individual-rankings-propagation {
|
||
position: relative;
|
||
z-index: 5; /* 提高z-index避免被其他元素遮挡 */
|
||
margin-bottom: 10px; /* 减小英雄榜虎狼之师图片与个人英雄图片的距离 */
|
||
}
|
||
|
||
/* 移动端战区冠军布局调整,与个人英雄冠军保持一致 */
|
||
.team-champion {
|
||
margin-bottom: 30px; /* 增加与下方战区排名标题的间距 */
|
||
padding: 10px; /* 调整内边距 */
|
||
min-height: 120px; /* 确保与英雄冠军有相同的视觉权重 */
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
/* 移动端英雄冠军布局调整 */
|
||
.individual-champion {
|
||
margin-bottom: -10px; /* 恢复为修改之前的间距 */
|
||
padding: 10px; /* 与战区冠军保持一致的内边距 */
|
||
min-height: 120px; /* 确保与战区冠军有相同的视觉权重 */
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
/* 确保两个排名区域在移动端有相同的起始位置 */
|
||
.team-rankings,
|
||
.individual-rankings {
|
||
position: relative;
|
||
z-index: 2; /* 统一的层级 */
|
||
padding-top: 10px; /* 统一的顶部内边距 */
|
||
}
|
||
|
||
/* 战区和英雄排名容器在移动端的统一样式 */
|
||
.team-rankings-container,
|
||
.individual-rankings-container {
|
||
margin-top: 20px; /* 与上方冠军图片的统一间距 */
|
||
}
|
||
|
||
/* 隐藏倒计时模块 */
|
||
.timer-float {
|
||
display: none !important;
|
||
}
|
||
|
||
/* 隐藏管理员入口 */
|
||
.admin-entry-float {
|
||
display: none !important;
|
||
}
|
||
|
||
/* 奖金模块垂直居中显示,两侧20px间距 */
|
||
.bonus-section {
|
||
/* 背景全部取消设置 **/
|
||
background: none !important;
|
||
|
||
position: static !important;
|
||
margin: 20px auto;
|
||
width: calc(100% - 40px);
|
||
max-width: none;
|
||
left: 0 !important;
|
||
right: 0 !important;
|
||
top: 0 !important;
|
||
bottom: 0 !important;
|
||
}
|
||
|
||
/* 保留响应式中的padding设置,但移除其他可能冲突的位置样式 */
|
||
.bonus-section {
|
||
padding: 8px; /* 减小padding */
|
||
}
|
||
|
||
.bonus-awards-container {
|
||
gap: 10px;
|
||
scale: 0.8;
|
||
}
|
||
|
||
/* 确保图片容器有足够的显示空间 */
|
||
.team-rankings, .individual-rankings {
|
||
position: relative;
|
||
z-index: 2; /* 确保排名区域在奖金模块之上 */
|
||
}
|
||
|
||
.timer-float {
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
.timer-float .countdown {
|
||
gap: 8px;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 480px) {
|
||
.banner-image {
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.award-image {
|
||
max-width: 100%;
|
||
}
|
||
|
||
.timer-float {
|
||
font-size: 0.8rem;
|
||
}
|
||
}
|
||
|
||
/* 浮动倒计时样式 - 科技感 */
|
||
.timer-float {
|
||
position: absolute;
|
||
background: linear-gradient(135deg, rgba(23, 25, 35, 0.95), rgba(23, 25, 35, 0.85));
|
||
padding: 15px 20px;
|
||
border-radius: 12px;
|
||
z-index: 950;
|
||
cursor: move;
|
||
backdrop-filter: blur(10px);
|
||
border: 1px solid #3a3f51;
|
||
box-shadow:
|
||
0 0 15px rgba(0, 255, 255, 0.3),
|
||
inset 0 0 10px rgba(0, 255, 255, 0.1);
|
||
animation: borderPulse 3s infinite alternate;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.timer-float:hover {
|
||
transform: scale(1.05);
|
||
box-shadow:
|
||
0 0 25px rgba(0, 255, 255, 0.5),
|
||
inset 0 0 15px rgba(0, 255, 255, 0.2);
|
||
}
|
||
|
||
@keyframes borderPulse {
|
||
0% {
|
||
box-shadow:
|
||
0 0 10px rgba(0, 255, 255, 0.2),
|
||
inset 0 0 5px rgba(0, 255, 255, 0.1);
|
||
}
|
||
|
||
100% {
|
||
box-shadow:
|
||
0 0 20px rgba(0, 255, 255, 0.5),
|
||
inset 0 0 15px rgba(0, 255, 255, 0.3);
|
||
}
|
||
}
|
||
|
||
.card-game {
|
||
/* 半透明背景,增强层次感 */
|
||
background: transparent;
|
||
/* padding: 20px;
|
||
margin-bottom: 30px; */
|
||
/* 强化阴影效果 */
|
||
border: 0;
|
||
}
|
||
|
||
|
||
|
||
/* 主题部分 */
|
||
.theme-section {
|
||
background: transparent;
|
||
text-align: center;
|
||
}
|
||
|
||
.main-title {
|
||
font-size: 2.5rem;
|
||
color: var(--gold-primary);
|
||
margin-bottom: 8px;
|
||
text-shadow: 0 4px 8px rgba(0, 0, 0, 0.5);
|
||
font-weight: bold;
|
||
letter-spacing: 2px;
|
||
}
|
||
|
||
.subtitle {
|
||
font-size: 1.2rem;
|
||
color: #fff;
|
||
margin-bottom: 20px;
|
||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
|
||
}
|
||
|
||
.countdown {
|
||
font-size: 1.2rem;
|
||
margin-top: 20px;
|
||
}
|
||
|
||
.timer {
|
||
display: inline-block;
|
||
background: linear-gradient(45deg, #6c5ce7, #a29bfe);
|
||
color: white;
|
||
padding: 12px 25px;
|
||
border-radius: 50px;
|
||
font-weight: bold;
|
||
margin-bottom: 14px;
|
||
/* 下移4px */
|
||
}
|
||
|
||
/* 科技感倒计时标签 */
|
||
.timer-float .label {
|
||
color: #00e5ff;
|
||
font-family: 'Courier New', monospace;
|
||
font-weight: bold;
|
||
text-transform: uppercase;
|
||
letter-spacing: 1px;
|
||
text-shadow: 0 0 5px rgba(0, 229, 255, 0.7);
|
||
display: block;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
/* 科技感时间项 */
|
||
.time-item {
|
||
background: linear-gradient(135deg, #1a1d2e, #2d3748);
|
||
padding: 8px 12px;
|
||
border-radius: 8px;
|
||
margin: 0 5px;
|
||
font-size: 1.3rem;
|
||
font-family: 'Courier New', monospace;
|
||
font-weight: bold;
|
||
color: #00e5ff;
|
||
text-shadow: 0 0 10px rgba(0, 229, 255, 0.8);
|
||
border: 1px solid #00e5ff;
|
||
box-shadow:
|
||
0 0 10px rgba(0, 229, 255, 0.3),
|
||
inset 0 0 5px rgba(0, 229, 255, 0.2);
|
||
transition: all 0.2s ease;
|
||
min-width: 60px;
|
||
text-align: center;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.time-item::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: -100%;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: linear-gradient(90deg, transparent, rgba(0, 229, 255, 0.2), transparent);
|
||
animation: shine 2s infinite;
|
||
}
|
||
|
||
.time-item:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow:
|
||
0 0 15px rgba(0, 229, 255, 0.5),
|
||
inset 0 0 10px rgba(0, 229, 255, 0.3);
|
||
}
|
||
|
||
.time-item.milliseconds {
|
||
font-size: 1rem;
|
||
color: #ff3d00;
|
||
border-color: #ff3d00;
|
||
text-shadow: 0 0 10px rgba(255, 61, 0, 0.8);
|
||
animation: pulse 1s infinite alternate;
|
||
}
|
||
|
||
.time-item.milliseconds::before {
|
||
background: linear-gradient(90deg, transparent, rgba(255, 61, 0, 0.2), transparent);
|
||
}
|
||
|
||
@keyframes shine {
|
||
100% {
|
||
left: 100%;
|
||
}
|
||
}
|
||
|
||
@keyframes pulse {
|
||
0% {
|
||
box-shadow:
|
||
0 0 10px rgba(255, 61, 0, 0.3),
|
||
inset 0 0 5px rgba(255, 61, 0, 0.2);
|
||
}
|
||
|
||
100% {
|
||
box-shadow:
|
||
0 0 15px rgba(255, 61, 0, 0.6),
|
||
inset 0 0 10px rgba(255, 61, 0, 0.4);
|
||
}
|
||
}
|
||
|
||
/* 毫秒闪烁动画增强紧张感 */
|
||
@keyframes blink {
|
||
|
||
0%,
|
||
50% {
|
||
opacity: 1;
|
||
transform: scale(1);
|
||
}
|
||
|
||
51%,
|
||
100% {
|
||
opacity: 0.7;
|
||
transform: scale(0.95);
|
||
}
|
||
}
|
||
|
||
/* 战鼓部分 - 浮动并支持拖放 */
|
||
.drums-section {
|
||
position: fixed;
|
||
left: 20px;
|
||
top: 20px;
|
||
padding: 20px;
|
||
background: rgba(255, 255, 255, 0.95);
|
||
border-radius: 20px;
|
||
cursor: move;
|
||
z-index: 1000;
|
||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
||
transition: box-shadow 0.3s ease;
|
||
}
|
||
|
||
.drums-section:hover {
|
||
box-shadow: 0 6px 30px rgba(0, 0, 0, 0.25);
|
||
}
|
||
|
||
.drums-section:active {
|
||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
|
||
}
|
||
|
||
.drums-container {
|
||
display: flex;
|
||
justify-content: center;
|
||
}
|
||
|
||
.drums-animation {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 20px;
|
||
font-size: 3rem;
|
||
}
|
||
|
||
.drum {
|
||
transition: transform 0.1s ease, filter 0.1s ease;
|
||
animation: idlePulse 2s infinite alternate;
|
||
}
|
||
|
||
.drum.beating {
|
||
/* 使用CSS变量方便动态调整 */
|
||
--drum-scale: 1.3;
|
||
--drum-translate-y: -15px;
|
||
--drum-rotate: 5deg;
|
||
--drum-brightness: 1.3;
|
||
--drum-saturation: 1.2;
|
||
|
||
transform: scale(var(--drum-scale)) translateY(var(--drum-translate-y)) rotate(var(--drum-rotate));
|
||
filter: brightness(var(--drum-brightness)) saturate(var(--drum-saturation));
|
||
animation: drumBeat 0.1s ease-in-out;
|
||
}
|
||
|
||
/* 战鼓闲置时的轻微脉动动画 */
|
||
@keyframes idlePulse {
|
||
0% {
|
||
transform: scale(1);
|
||
}
|
||
|
||
100% {
|
||
transform: scale(1.05);
|
||
}
|
||
}
|
||
|
||
/* 增强跳动效果的关键帧动画 */
|
||
@keyframes drumBeat {
|
||
0% {
|
||
transform: scale(1);
|
||
}
|
||
|
||
50% {
|
||
transform: scale(var(--drum-scale, 1.3)) translateY(var(--drum-translate-y, -15px)) rotate(var(--drum-rotate, 5deg));
|
||
}
|
||
|
||
100% {
|
||
transform: scale(1);
|
||
}
|
||
}
|
||
|
||
.trophy {
|
||
animation: bounce 1s infinite alternate;
|
||
}
|
||
|
||
@keyframes bounce {
|
||
from {
|
||
transform: translateY(0);
|
||
}
|
||
|
||
to {
|
||
transform: translateY(-10px);
|
||
}
|
||
}
|
||
|
||
/* 按钮样式 */
|
||
.btn-game-secondary {
|
||
background: linear-gradient(45deg, #6c5ce7, #a29bfe);
|
||
color: white;
|
||
border: none;
|
||
padding: 12px 20px;
|
||
border-radius: 30px;
|
||
font-size: 1rem;
|
||
cursor: pointer;
|
||
box-shadow: 0 4px 20px rgba(108, 92, 231, 0.4);
|
||
transition: all 0.3s ease;
|
||
border: 2px solid rgba(255, 255, 255, 0.3);
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.btn-game-secondary:hover {
|
||
transform: scale(1.1) translateY(-2px);
|
||
box-shadow: 0 6px 30px rgba(108, 92, 231, 0.6);
|
||
background: linear-gradient(45deg, #7f7fd5, #86a8e7);
|
||
}
|
||
|
||
/* 排名部分 */
|
||
.rankings-section {
|
||
margin: 0 20px 30px 20px;
|
||
}
|
||
|
||
.rankings-container {
|
||
display: flex;
|
||
gap: 15px;
|
||
flex-wrap: wrap;
|
||
justify-content: center;
|
||
background: none;
|
||
border: none;
|
||
}
|
||
|
||
.individual-rankings,
|
||
.team-rankings {
|
||
flex: 1;
|
||
min-width: 350px;
|
||
max-width: 550px;
|
||
}
|
||
|
||
/* 为指定的容器添加背景和边框 */
|
||
.team-rankings-container,
|
||
.individual-rankings-container {
|
||
background: rgba(255, 255, 255, 0.95);
|
||
border-radius: 15px;
|
||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||
backdrop-filter: blur(10px);
|
||
border: 2px solid rgba(255, 215, 0, 0.5);
|
||
padding: 15px;
|
||
}
|
||
|
||
.section-title {
|
||
color: #d63031;
|
||
text-align: center;
|
||
margin-bottom: 15px;
|
||
font-size: 1.6rem;
|
||
}
|
||
|
||
.task-settings-section {
|
||
margin-top: -220px;
|
||
}
|
||
|
||
.task-title-container {
|
||
padding: 30px 40px;
|
||
text-align: center;
|
||
}
|
||
|
||
.task-title-container .game-title {
|
||
color: #ffffff;
|
||
font-size: 2.5rem;
|
||
font-weight: bold;
|
||
margin: 0 0 15px 0;
|
||
animation: glow-pulse 2s infinite alternate;
|
||
}
|
||
|
||
.task-title-container .game-title .game-title-highlight {
|
||
font-size: 3.5rem;
|
||
}
|
||
|
||
.task-title-container .game-subtitle {
|
||
color: #ffffff;
|
||
font-size: 1.5rem;
|
||
margin: 0;
|
||
}
|
||
|
||
@keyframes glow-pulse {
|
||
0% {
|
||
text-shadow: 3px 3px 0 rgba(0, 0, 0, 0.2);
|
||
}
|
||
|
||
100% {
|
||
text-shadow: 3px 3px 0 rgba(0, 0, 0, 0.2), 0 0 20px rgba(255, 255, 255, 0.8);
|
||
}
|
||
}
|
||
|
||
.rank-table {
|
||
border-radius: 10px;
|
||
overflow: hidden;
|
||
overflow-y: auto;
|
||
position: relative;
|
||
margin-top: 0.8rem;
|
||
max-height: 420px;
|
||
max-height: 420px;
|
||
// 只对左侧战区的.rank-table生效(通过父容器限定)
|
||
.team-rankings-container & {
|
||
-ms-overflow-style: none; /* IE/Edge 隐藏 */
|
||
scrollbar-width: none; /* Firefox 隐藏 */
|
||
}
|
||
// Chrome/Safari 隐藏左侧滚动条(伪元素必须单独写)
|
||
.team-rankings-container &::-webkit-scrollbar {
|
||
display: none !important;
|
||
width: 0 !important;
|
||
}
|
||
|
||
// ========== 新增:右侧英雄排名滚动条保留原样 ==========
|
||
.individual-rankings-container & {
|
||
-ms-overflow-style: auto; /* 恢复IE/Edge默认 */
|
||
scrollbar-width: auto; /* 恢复Firefox默认 */
|
||
}
|
||
// Chrome/Safari 恢复右侧滚动条
|
||
.individual-rankings-container &::-webkit-scrollbar {
|
||
display: block !important;
|
||
width: 6px !important; // 恢复默认滚动条宽度
|
||
}
|
||
|
||
}
|
||
|
||
.table-header {
|
||
background: linear-gradient(45deg, #6c5ce7, #a29bfe);
|
||
color: white;
|
||
display: grid;
|
||
padding: 12px 10px;
|
||
font-weight: bold;
|
||
position: sticky;
|
||
top: 0;
|
||
z-index: 10;
|
||
/* 增强标题可读性 */
|
||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||
}
|
||
|
||
.team-rankings .table-header {
|
||
position: sticky;
|
||
top: 0;
|
||
z-index: 10;
|
||
}
|
||
|
||
.table-row {
|
||
display: grid;
|
||
padding: 12px 10px;
|
||
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
|
||
transition: all 0.3s ease;
|
||
/* 浅色背景增强可读性 */
|
||
background: rgba(255, 255, 255, 0.9);
|
||
color: #333;
|
||
}
|
||
|
||
.team-rankings .table-row {
|
||
padding: 12px 10px;
|
||
}
|
||
|
||
.table-row:hover {
|
||
background-color: rgba(255, 255, 255, 0.95);
|
||
transform: translateY(-1px);
|
||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
||
}
|
||
|
||
.table-row.top-three {
|
||
background-color: rgba(255, 243, 205, 0.95);
|
||
font-size: 1.1rem;
|
||
font-weight: bold;
|
||
color: #000;
|
||
transform: scale(1.02);
|
||
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.2);
|
||
transition: all 0.3s ease;
|
||
z-index: 2;
|
||
position: relative;
|
||
}
|
||
|
||
.table-row.top-three:hover {
|
||
transform: scale(1.03);
|
||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.15);
|
||
}
|
||
|
||
/* 前三名特殊背景色,增强亮度和对比度 - 确保英雄排名和战区排名样式一致 */
|
||
.team-rankings-container .table-row:nth-child(1),
|
||
.individual-rankings-container .table-row:nth-child(1) {
|
||
background: linear-gradient(135deg, #ffd700, #403f3c);
|
||
color: #333;
|
||
box-shadow: 0 6px 20px rgba(255, 215, 0, 0.4);
|
||
}
|
||
|
||
.team-rankings-container .table-row:nth-child(2),
|
||
.individual-rankings-container .table-row:nth-child(2) {
|
||
background: linear-gradient(135deg, #c0c0c0, #e0e0e0);
|
||
color: #333;
|
||
box-shadow: 0 6px 20px rgba(192, 192, 192, 0.4);
|
||
}
|
||
|
||
.team-rankings-container .table-row:nth-child(3),
|
||
.individual-rankings-container .table-row:nth-child(3) {
|
||
background: linear-gradient(135deg, #cd7f32, #d7ccc8);
|
||
color: #333;
|
||
box-shadow: 0 6px 20px rgba(205, 127, 50, 0.4);
|
||
}
|
||
|
||
.table-row.highlight {
|
||
background: linear-gradient(135deg, #ffd32a, #ff7979);
|
||
}
|
||
|
||
.level-SSS {
|
||
color: #ffd700;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.level-SS {
|
||
color: #c0c0c0;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.level-S {
|
||
color: #cd7f32;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.level-A {
|
||
color: #4caf50;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.level-B {
|
||
color: #2196f3;
|
||
font-weight: bold;
|
||
}
|
||
|
||
/* 浮动管理员入口 */
|
||
.admin-entry-float {
|
||
position: fixed;
|
||
bottom: 20px;
|
||
left: 20px;
|
||
z-index: 1000;
|
||
}
|
||
|
||
/* 管理员按钮使用通用按钮样式 */
|
||
.admin-entry-float .btn-game-secondary {
|
||
font-size: 0.8rem;
|
||
padding: 8px 16px;
|
||
min-width: 80px;
|
||
border-radius: 6px;
|
||
}
|
||
|
||
/* 针对高度大于1080分辨率的精确优化 */
|
||
|
||
|
||
/* 移动端背景图片设置 - 全局样式 */
|
||
/* 总战绩部分样式 */
|
||
.total-score-section {
|
||
margin: 20px auto;
|
||
max-width: 1200px;
|
||
padding: 20px;
|
||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||
animation: fadeIn 0.5s ease-in-out;
|
||
}
|
||
|
||
.total-score-container {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 15px;
|
||
}
|
||
|
||
/* 移除空的total-score-content规则集以避免SCSS警告 */
|
||
|
||
|
||
.total-score-item {
|
||
display: flex;
|
||
justify-content: center; /* 水平居中 */
|
||
align-items: center; /* 垂直居中 */
|
||
padding: 10px;
|
||
min-height: 60px; /* 确保有足够的高度来展示垂直居中效果 */
|
||
}
|
||
|
||
.score-value {
|
||
font-size: calc(var(--individual-champion-font-size, 5rem) * 2);
|
||
font-size: calc(var(--individual-champion-font-size, 6rem) * 2);
|
||
font-weight: bold;
|
||
color: #fff2c4;
|
||
text-shadow: 0 0 10px rgba(0, 0, 0, .8), 0 0 20px rgb(16 16 16 / 30%), 1px 1px 2px rgba(0, 0, 0, .8);
|
||
}
|
||
|
||
.total-score-total-value {
|
||
margin-top: -40px;
|
||
}
|
||
|
||
/* 战区冠军分数特殊样式 */
|
||
.team-champion .score-value {
|
||
font-size: calc(var(--team-champion-font-size, 1.8rem) * 4);
|
||
text-shadow:
|
||
0 0 15px rgba(255, 215, 0, 0.7),
|
||
0 0 30px rgba(255, 215, 0, 0.4),
|
||
2px 2px 3px rgba(0, 0, 0, 0.9);
|
||
}
|
||
|
||
/* 照片容器样式 */
|
||
.team-logo.photo-container {
|
||
overflow: hidden;
|
||
margin-top: -97px;
|
||
}
|
||
|
||
.individual-champion {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-bottom: 20px;
|
||
gap: 30px;
|
||
}
|
||
|
||
.team-rankings-propagation {
|
||
align-items: center;
|
||
display: flex;
|
||
justify-content: center;
|
||
}
|
||
|
||
.individual-rankings-propagation {
|
||
align-items: center;
|
||
display: flex;
|
||
justify-content: center;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
|
||
.total-score-total-title {
|
||
align-items: center;
|
||
display: flex;
|
||
justify-content: center;
|
||
}
|
||
|
||
/* 战区排名容器设置 - 根据配置决定显示行数和滚动行为 */
|
||
.team-rankings-container .rank-table {
|
||
position: relative;
|
||
min-height: auto;
|
||
/* 移动端强制显示所有行,覆盖JS动态设置的CSS变量 */
|
||
height: auto !important;
|
||
max-height: none !important;
|
||
overflow-y: visible !important;
|
||
overflow-x: auto !important;
|
||
/* 强制覆盖CSS变量 */
|
||
--team-default-height: auto !important;
|
||
--team-overflow-y: visible !important;
|
||
--team-overflow-x: auto !important;
|
||
}
|
||
|
||
/* 移动端战区排名显示所有内容,不受滚动锁定影响 */
|
||
:root[style*="--team-scroll-lock: lock"] .team-rankings-container .rank-table {
|
||
overflow-y: visible !important;
|
||
overflow-x: auto !important;
|
||
height: auto !important;
|
||
min-width: auto !important;
|
||
width: 100% !important;
|
||
}
|
||
|
||
/* 确保表格内容在移动端正确显示 */
|
||
:root[style*="--team-scroll-lock: lock"] .team-rankings-container .rank-table .table-header,
|
||
:root[style*="--team-scroll-lock: lock"] .team-rankings-container .rank-table .table-row {
|
||
min-width: auto !important;
|
||
width: 100% !important;
|
||
overflow-x: visible !important;
|
||
white-space: nowrap !important;
|
||
}
|
||
|
||
.individual-rankings-propagation {
|
||
margin-top: -40px;
|
||
padding-bottom: 10px;
|
||
margin-bottom: 20px; /* 恢复为修改之前的间距 */
|
||
}
|
||
|
||
.individual-rankings {
|
||
margin-top: 60px; /* 增加排名列表的顶部间距 */
|
||
}
|
||
|
||
/* 移动端战区排名容器位置调整 */
|
||
.team-rankings {
|
||
margin-top: 0; /* 确保与英雄排名区域在统一水平线 */
|
||
}
|
||
|
||
/* 英雄排名容器设置 - 根据配置决定显示行数和滚动行为 */
|
||
.individual-rankings-container .rank-table {
|
||
position: relative;
|
||
min-height: auto;
|
||
/* 移动端强制显示所有行,覆盖JS动态设置的CSS变量 */
|
||
height: auto !important;
|
||
max-height: none !important;
|
||
overflow-y: visible !important;
|
||
overflow-x: auto !important;
|
||
/* 隐藏滚动条 */
|
||
-ms-overflow-style: none;
|
||
scrollbar-width: none;
|
||
/* 强制覆盖CSS变量 */
|
||
--individual-default-height: auto !important;
|
||
--individual-overflow-y: visible !important;
|
||
--individual-overflow-x: auto !important;
|
||
}
|
||
|
||
/* 移动端英雄排名显示所有内容,不受滚动锁定影响 */
|
||
:root[style*="--individual-scroll-lock: lock"] .individual-rankings-container .rank-table {
|
||
overflow-y: visible !important;
|
||
overflow-x: auto !important;
|
||
height: auto !important;
|
||
min-width: auto !important;
|
||
width: 100% !important;
|
||
}
|
||
|
||
/* 确保表格内容在移动端正确显示 */
|
||
:root[style*="--individual-scroll-lock: lock"] .individual-rankings-container .rank-table .table-header,
|
||
:root[style*="--individual-scroll-lock: lock"] .individual-rankings-container .rank-table .table-row {
|
||
min-width: auto !important;
|
||
width: 100% !important;
|
||
overflow-x: visible !important;
|
||
white-space: nowrap !important;
|
||
}
|
||
|
||
/* 移动端非前三名字体放大 */
|
||
.team-rankings-container .table-row:not(:nth-child(1)):not(:nth-child(2)):not(:nth-child(3)),
|
||
.individual-rankings-container .table-row:not(:nth-child(1)):not(:nth-child(2)):not(:nth-child(3)) {
|
||
font-size: 1.1rem;
|
||
}
|
||
|
||
/* 移动端名次列水平垂直居中 */
|
||
.team-rankings-container .table-header > :first-child,
|
||
.individual-rankings-container .table-header > :first-child,
|
||
.team-rankings-container .table-row > :first-child,
|
||
.individual-rankings-container .table-row > :first-child {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
text-align: center;
|
||
}
|
||
|
||
/* 确保前三名特殊样式在移动端正确显示 */
|
||
.team-rankings-container .table-row.top-three,
|
||
.individual-rankings-container .table-row.top-three {
|
||
transform: scale(1);
|
||
box-shadow: none;
|
||
position: static;
|
||
}
|
||
|
||
.team-rankings-container .table-row:nth-child(1),
|
||
.team-rankings-container .table-row:nth-child(2),
|
||
.team-rankings-container .table-row:nth-child(3),
|
||
.individual-rankings-container .table-row:nth-child(1),
|
||
.individual-rankings-container .table-row:nth-child(2),
|
||
.individual-rankings-container .table-row:nth-child(3) {
|
||
box-shadow: none;
|
||
z-index: 1;
|
||
}
|
||
|
||
/* 当设置为禁止滚动时,确保战区排名容器不允许滚动 */
|
||
:root[style*="--team-scroll-lock: lock"] .team-rankings-container {
|
||
overflow: visible !important;
|
||
height: auto !important;
|
||
}
|
||
|
||
:root[style*="--team-scroll-lock: lock"] .team-rankings-container .rank-table {
|
||
overflow: hidden !important;
|
||
display: block;
|
||
min-width: auto !important;
|
||
width: 100% !important;
|
||
}
|
||
|
||
/* 确保表格头部和行在禁止滚动模式下能够完整显示内容 */
|
||
:root[style*="--team-scroll-lock: lock"] .team-rankings-container .table-header,
|
||
:root[style*="--team-scroll-lock: lock"] .team-rankings-container .table-row {
|
||
min-width: auto !important;
|
||
width: 100% !important;
|
||
overflow-x: visible !important;
|
||
white-space: normal !important;
|
||
}
|
||
|
||
/* 英雄排名列表隐藏水平滚动条 */
|
||
.individual-rankings-container .rank-table::-webkit-scrollbar {
|
||
width: 6px; /* 保留垂直滚动条 */
|
||
height: 0; /* 隐藏水平滚动条 */
|
||
}
|
||
|
||
/* 移除原有卡片背景,让内容在背景图上显示 */
|
||
.card-game {
|
||
background: transparent;
|
||
box-shadow: none;
|
||
}
|
||
|
||
/* 主题部分调整 */
|
||
.theme-section {
|
||
width: 100%;
|
||
margin: 0;
|
||
padding: 0;
|
||
}
|
||
|
||
.banner-image {
|
||
width: 100%;
|
||
height: auto;
|
||
}
|
||
|
||
/* 战鼓部分调整 */
|
||
.drums-section {
|
||
transform: scale(0.8);
|
||
/* 缩小战鼓元素 */
|
||
}
|
||
|
||
/* 2. 倒计时模块调整 - 移至冠军战区上方,缩小时间显示为一行 */
|
||
.timer-float {
|
||
position: relative;
|
||
right: auto;
|
||
top: auto;
|
||
margin: 10px auto;
|
||
width: 95%;
|
||
padding: 8px;
|
||
border-radius: 8px;
|
||
background: rgba(0, 0, 0, 0.8);
|
||
color: white;
|
||
text-align: center;
|
||
}
|
||
|
||
.timer-float .label {
|
||
font-size: 0.9rem;
|
||
display: block;
|
||
margin-bottom: 5px;
|
||
}
|
||
|
||
.countdown {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
gap: 8px;
|
||
flex-wrap: nowrap;
|
||
}
|
||
|
||
.time-item {
|
||
font-size: 0.85rem;
|
||
padding: 4px 6px;
|
||
background: rgba(255, 215, 0, 0.2);
|
||
border-radius: 4px;
|
||
border: 1px solid rgba(255, 215, 0, 0.5);
|
||
}
|
||
|
||
/* 任务设置调整 */
|
||
.task-settings-section {
|
||
margin-top: -100px;
|
||
/* 调整负边距 */
|
||
}
|
||
|
||
.task-title-container {
|
||
padding: 15px;
|
||
}
|
||
|
||
.task-title-container .game-title {
|
||
font-size: 1.6rem;
|
||
}
|
||
|
||
.task-title-container .game-title-highlight {
|
||
font-size: 2rem;
|
||
}
|
||
|
||
.task-title-container .game-subtitle {
|
||
font-size: 1rem;
|
||
}
|
||
|
||
/* 排名部分调整 */
|
||
.rankings-section {
|
||
margin: 0 10px 20px 10px;
|
||
}
|
||
|
||
.rankings-container {
|
||
flex-direction: column;
|
||
gap: 10px;
|
||
}
|
||
|
||
.individual-rankings,
|
||
.team-rankings {
|
||
min-width: auto;
|
||
width: 100%;
|
||
/* 调整顺序,确保倒计时在冠军上方 */
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
/* 将倒计时移动到冠军上方的位置 */
|
||
.team-rankings {
|
||
order: -1;
|
||
}
|
||
|
||
.team-rankings-container,
|
||
.individual-rankings-container {
|
||
padding: 10px;
|
||
border-radius: 10px;
|
||
background: rgba(255, 255, 255, 0.95);
|
||
}
|
||
|
||
/* 当设置为禁止滚动时,确保战区排名容器不允许滚动 */
|
||
:root[style*="--team-scroll-lock: lock"] .team-rankings-container {
|
||
overflow: visible !important;
|
||
height: auto !important;
|
||
}
|
||
|
||
:root[style*="--team-scroll-lock: lock"] .team-rankings-container .rank-table {
|
||
overflow: hidden !important;
|
||
white-space: nowrap;
|
||
display: block;
|
||
}
|
||
|
||
/* 表格调整 */
|
||
.rank-table {
|
||
min-height: 250px;
|
||
/* 移除最大高度限制,允许显示所有行 */
|
||
overflow-x: hidden;
|
||
/* 禁止横向滚动 */
|
||
overflow-y: auto;
|
||
/* 允许纵向滚动 */
|
||
/* 隐藏移动端滚动条 */
|
||
-ms-overflow-style: none;
|
||
scrollbar-width: none;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
/* 隐藏WebKit浏览器移动端滚动条 */
|
||
.rank-table::-webkit-scrollbar {
|
||
display: none;
|
||
}
|
||
|
||
/* 英雄排名列表设置 - 根据配置决定显示行数和滚动行为 */
|
||
.individual-rankings-container .rank-table {
|
||
overflow-x: hidden !important;
|
||
/* 禁止横向滚动 */
|
||
height: var(--individual-default-height, auto) !important;
|
||
overflow-y: var(--individual-overflow-y, auto) !important;
|
||
/* 确保英雄排名列表隐藏滚动条 */
|
||
-ms-overflow-style: none;
|
||
scrollbar-width: none;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
/* 当设置了滚动锁定时,禁止所有滚动 */
|
||
:root[style*="--individual-scroll-lock: lock"] .individual-rankings-container {
|
||
overflow: visible !important;
|
||
height: auto !important;
|
||
}
|
||
|
||
:root[style*="--individual-scroll-lock: lock"] .individual-rankings-container .rank-table {
|
||
overflow: hidden !important;
|
||
white-space: nowrap;
|
||
display: block;
|
||
}
|
||
|
||
.individual-rankings-container .rank-table::-webkit-scrollbar {
|
||
display: none;
|
||
}
|
||
|
||
.table-header {
|
||
font-size: 1rem;
|
||
padding: 8px 6px;
|
||
min-width: 500px;
|
||
/* 固定表头,防止滚动时移动 */
|
||
position: sticky;
|
||
top: 0;
|
||
z-index: 10;
|
||
background-color: rgba(255, 255, 255, 0.95);
|
||
}
|
||
|
||
.table-row {
|
||
font-size: 0.9rem;
|
||
padding: 8px 6px;
|
||
min-width: 500px;
|
||
/* 确保行内容不会被截断 */
|
||
overflow: visible;
|
||
}
|
||
|
||
/* 冠军部分调整 */
|
||
.team-champion,
|
||
.individual-champion {
|
||
margin: 5px 0;
|
||
}
|
||
|
||
/* 管理员入口调整 */
|
||
.admin-entry-float {
|
||
position: relative;
|
||
top: auto;
|
||
right: auto;
|
||
margin: 10px;
|
||
text-align: center;
|
||
}
|
||
|
||
.btn-game-secondary {
|
||
padding: 10px 15px;
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
/* 3. 奖金模块调整 - 移至底部显示 */
|
||
.bonus-section {
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
width: 100%;
|
||
margin: 0;
|
||
padding: 10px;
|
||
z-index: 1000;
|
||
transform: translateY(0);
|
||
transition: transform 0.3s ease;
|
||
box-shadow: none;
|
||
}
|
||
|
||
.bonus-awards-container {
|
||
display: flex;
|
||
justify-content: space-around;
|
||
align-items: center;
|
||
}
|
||
|
||
.award-image {
|
||
height: 120px;
|
||
width: auto;
|
||
}
|
||
|
||
/* 确保重要内容优先显示 */
|
||
.rank-col,
|
||
.name-col,
|
||
.score-col {
|
||
font-weight: bold;
|
||
}
|
||
|
||
/* 为底部奖金模块留出空间 */
|
||
.admin-entry-float {
|
||
margin-bottom: 100px;
|
||
}
|
||
|
||
/* 针对极小屏幕的特殊处理 */
|
||
@media (max-width: 480px) {
|
||
.table-header {
|
||
font-size: 0.9rem;
|
||
padding: 8px 6px;
|
||
min-width: 450px;
|
||
}
|
||
|
||
.table-row {
|
||
min-width: 450px;
|
||
font-size: 0.75rem;
|
||
padding: 6px 4px;
|
||
}
|
||
|
||
.game-title {
|
||
font-size: 1.4rem;
|
||
}
|
||
|
||
.game-title-highlight {
|
||
font-size: 1.7rem;
|
||
}
|
||
|
||
.award-image {
|
||
height: 120px;
|
||
}
|
||
|
||
.countdown {
|
||
gap: 6px;
|
||
}
|
||
|
||
.time-item {
|
||
font-size: 0.8rem;
|
||
padding: 3px 5px;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 触摸设备优化 */
|
||
@media (hover: none) and (pointer: coarse) {
|
||
|
||
/* 增大点击区域 */
|
||
.btn-game-secondary {
|
||
padding: 12px 24px;
|
||
font-size: 1rem;
|
||
}
|
||
|
||
/* 移除hover效果,添加触摸反馈 */
|
||
.table-row:active {
|
||
background-color: rgba(255, 255, 255, 0.95);
|
||
transform: scale(1.01);
|
||
}
|
||
|
||
.btn-game-secondary:active {
|
||
transform: scale(0.98);
|
||
}
|
||
}
|
||
|
||
/* 响应式设计,1200px以上显示 */
|
||
@media (min-width: 1920px) {
|
||
.bonus-section {
|
||
top: 360px;
|
||
left: 320px;
|
||
}
|
||
}
|
||
|
||
|
||
@media (min-width: 2000px) {
|
||
.bonus-section {
|
||
position: fixed;
|
||
top: 360px;
|
||
left: 720px;
|
||
z-index: 9999; /* 设置最高层级,确保在最上层显示 */
|
||
cursor: move;
|
||
transition: transform 0.2s ease;
|
||
}
|
||
}
|
||
.team-rankings-container .rank-table {
|
||
-ms-overflow-style: none !important;
|
||
scrollbar-width: none !important;
|
||
}
|
||
.team-rankings-container .rank-table::-webkit-scrollbar {
|
||
display: none !important;
|
||
width: 0 !important;
|
||
}
|
||
</style> |