tmp-upload #4

Merged
admin merged 33 commits from tmp-upload into main 2025-11-29 02:30:38 +00:00
2 changed files with 501 additions and 539 deletions
Showing only changes of commit 91e8af324d - Show all commits

View File

@@ -6,23 +6,11 @@
<h2 class="game-title">🔐 管理员登录</h2> <h2 class="game-title">🔐 管理员登录</h2>
<div class="form-group"> <div class="form-group">
<label for="username" class="text-gold">用户名:</label> <label for="username" class="text-gold">用户名:</label>
<input <input id="username" v-model="loginForm.username" type="text" placeholder="请输入用户名" class="form-input" />
id="username"
v-model="loginForm.username"
type="text"
placeholder="请输入用户名"
class="form-input"
/>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="password" class="text-gold">密码:</label> <label for="password" class="text-gold">密码:</label>
<input <input id="password" v-model="loginForm.password" type="password" placeholder="请输入密码" class="form-input" />
id="password"
v-model="loginForm.password"
type="password"
placeholder="请输入密码"
class="form-input"
/>
</div> </div>
<div style="display: flex; gap: 15px; margin-top: 25px;"> <div style="display: flex; gap: 15px; margin-top: 25px;">
<button @click="login" type="submit" class="btn-game" style="flex: 1; padding: 12px;">登录</button> <button @click="login" type="submit" class="btn-game" style="flex: 1; padding: 12px;">登录</button>
@@ -32,50 +20,40 @@
</div> </div>
</div> </div>
<!-- 管理界面 --><div v-else class="management-container"> <!-- 管理界面 -->
<div v-else class="management-container">
<!-- 顶部导航 --> <!-- 顶部导航 -->
<div class="top-nav"> <div class="top-nav">
<h1 class="game-title">📊 百日大战管理系统</h1> <h1 class="game-title">📊 百日大战管理系统</h1>
<div class="nav-actions"> <div class="nav-actions">
<span class="welcome-text">欢迎您{{ currentUser.username }}</span> <span class="welcome-text">欢迎您{{ currentUser.username }}</span>
<button @click="handleRefreshData" class="btn-game" style="margin-right: 10px;">刷新数据</button> <button @click="handleRefreshData" class="btn-game" style="margin-right: 10px;">刷新数据</button>
<button @click="logout" class="btn-game-secondary">退出登录</button> <button @click="logout" class="btn-game-secondary">退出登录</button>
</div> </div>
</div> </div>
<!-- 功能选项卡 --> <!-- 功能选项卡 -->
<div class="tabs"> <div class="tabs">
<button <button v-for="tab in tabs" :key="tab.key" @click="currentTab = tab.key"
v-for="tab in tabs" :class="['btn-game', { active: currentTab === tab.key }]">{{ tab.label }}</button>
:key="tab.key"
@click="currentTab = tab.key"
:class="['btn-game', { active: currentTab === tab.key }]"
>{{ tab.label }}</button>
</div> </div>
<!-- 内容区域 --><div class="tab-content" style="max-height: calc(100vh - 200px); overflow-y: auto;"> <!-- 内容区域 -->
<!-- 结束时间设置 --><div v-if="currentTab === 'endTime'" class="end-time-content"> <div class="tab-content" style="max-height: calc(100vh - 200px); overflow-y: auto;">
<!-- 结束时间设置 -->
<div v-if="currentTab === 'endTime'" class="end-time-content">
<h2 class="game-subtitle"> 百日大战结束时间设置</h2> <h2 class="game-subtitle"> 百日大战结束时间设置</h2>
<div class="time-setting-section"> <div class="time-setting-section">
<div class="config-item"> <div class="config-item">
<label class="checkbox-label text-gold"> <label class="checkbox-label text-gold">
<span>结束日期</span> <span>结束日期</span>
<input <input type="date" v-model="localBattleEndTime.date" class="text-input">
type="date"
v-model="localBattleEndTime.date"
class="text-input"
>
</label> </label>
</div> </div>
<div class="config-item"> <div class="config-item">
<label class="checkbox-label"> <label class="checkbox-label">
<span>结束时间</span> <span>结束时间</span>
<input <input type="time" step="1" v-model="localBattleEndTime.time" class="text-input">
type="time"
step="1"
v-model="localBattleEndTime.time"
class="text-input"
>
</label> </label>
</div> </div>
<div class="config-item"> <div class="config-item">
@@ -84,7 +62,8 @@
</div> </div>
</div> </div>
<!-- 显示配置管理 --><div v-if="currentTab === 'config'" class="config-content"> <!-- 显示配置管理 -->
<div v-if="currentTab === 'config'" class="config-content">
<h2 class="game-subtitle"> 显示配置管理</h2> <h2 class="game-subtitle"> 显示配置管理</h2>
<!-- 个人排名显示配置 --> <!-- 个人排名显示配置 -->
@@ -93,27 +72,33 @@
<div class="config-options"> <div class="config-options">
<div class="config-item"> <div class="config-item">
<label class="checkbox-label text-gold"> <label class="checkbox-label text-gold">
<input <input type="checkbox" v-model="localDisplayConfig.individual.showLevel">
type="checkbox"
v-model="localDisplayConfig.individual.showLevel"
>
<span class="text-gold">显示等级列</span> <span class="text-gold">显示等级列</span>
</label> </label>
</div> </div>
<div class="config-item"> <div class="config-item">
<label class="checkbox-label"> <label class="checkbox-label">
<input <input type="checkbox" v-model="localDisplayConfig.individual.showDepartment">
type="checkbox"
v-model="localDisplayConfig.individual.showDepartment"
>
<span class="text-gold">显示部门列</span> <span class="text-gold">显示部门列</span>
</label> </label>
</div> </div>
<div class="config-item">
<label class="checkbox-label">
<input type="checkbox" v-model="localDisplayConfig.individual.showAvatar">
<span class="text-gold">显示头像列</span>
</label>
</div>
<div class="config-item">
<label class="checkbox-label">
<span class="text-gold">移动端最大显示行数</span>
<input type="number" v-model.number="localDisplayConfig.individual.maxDisplayRows" min="1" max="50" class="number-input">
</label>
</div>
<div class="config-item"> <div class="config-item">
<label class="checkbox-label"> <label class="checkbox-label">
<span class="text-gold">业绩列显示名称</span> <span class="text-gold">业绩列显示名称</span>
<input type="text" v-model="localDisplayConfig.individual.scoreColumn.displayName" <input type="text" v-model="localDisplayConfig.individual.scoreColumn.displayName"
placeholder="输入显示名称" class="text-input"> placeholder="输入显示名称" class="text-input">
</label> </label>
</div> </div>
<div class="config-item"> <div class="config-item">
@@ -130,11 +115,13 @@
<h4 class="text-gold">列宽设置单位像素</h4> <h4 class="text-gold">列宽设置单位像素</h4>
<div class="column-width-item"> <div class="column-width-item">
<label class="text-gold">排名列</label> <label class="text-gold">排名列</label>
<input type="number" v-model.number="localDisplayConfig.individual.columnWidths.rank" min="40" max="200" class="width-input"> <input type="number" v-model.number="localDisplayConfig.individual.columnWidths.rank" min="40"
max="200" class="width-input">
</div> </div>
<div class="column-width-item"> <div class="column-width-item">
<label class="text-gold">头像列</label> <label class="text-gold">头像列</label>
<input type="number" v-model.number="localDisplayConfig.individual.columnWidths.avatar" min="40" max="200" class="width-input"> <input type="number" v-model.number="localDisplayConfig.individual.columnWidths.avatar" min="40"
max="200" class="width-input">
</div> </div>
<div class="column-width-item"> <div class="column-width-item">
<label class="text-gold">姓名列</label> <label class="text-gold">姓名列</label>
@@ -148,11 +135,13 @@
</div> </div>
<div class="column-width-item"> <div class="column-width-item">
<label class="text-gold">业绩列</label> <label class="text-gold">业绩列</label>
<input type="number" v-model.number="localDisplayConfig.individual.columnWidths.score" min="60" max="200" class="width-input"> <input type="number" v-model.number="localDisplayConfig.individual.columnWidths.score" min="60"
max="200" class="width-input">
</div> </div>
<div class="column-width-item" v-if="localDisplayConfig.individual.showLevel"> <div class="column-width-item" v-if="localDisplayConfig.individual.showLevel">
<label class="text-gold">等级列</label> <label class="text-gold">等级列</label>
<input type="number" v-model.number="localDisplayConfig.individual.columnWidths.level" min="60" max="200" class="width-input"> <input type="number" v-model.number="localDisplayConfig.individual.columnWidths.level" min="60"
max="200" class="width-input">
</div> </div>
<div class="column-width-item" v-if="localDisplayConfig.individual.showDepartment"> <div class="column-width-item" v-if="localDisplayConfig.individual.showDepartment">
<label class="text-gold">部门列</label> <label class="text-gold">部门列</label>
@@ -163,9 +152,15 @@
<option value="200">200px</option> <option value="200">200px</option>
</select> </select>
</div> </div>
<div class="column-width-item" v-if="localDisplayConfig.individual.showAvatar">
<label class="text-gold">头像列</label>
<input type="number" v-model.number="localDisplayConfig.individual.columnWidths.avatar" min="40"
max="200" class="width-input">
</div>
<div class="column-width-item"> <div class="column-width-item">
<label class="text-gold">奖金列</label> <label class="text-gold">奖金列</label>
<input type="number" v-model.number="localDisplayConfig.individual.columnWidths.bonus" min="60" max="200" class="width-input"> <input type="number" v-model.number="localDisplayConfig.individual.columnWidths.bonus" min="60"
max="200" class="width-input">
</div> </div>
</div> </div>
</div> </div>
@@ -177,27 +172,21 @@
<div class="config-options"> <div class="config-options">
<div class="config-item"> <div class="config-item">
<label class="checkbox-label"> <label class="checkbox-label">
<input <input type="checkbox" v-model="localDisplayConfig.team.showMemberCount">
type="checkbox"
v-model="localDisplayConfig.team.showMemberCount"
>
<span class="text-gold">显示人数列</span> <span class="text-gold">显示人数列</span>
</label> </label>
</div> </div>
<div class="config-item"> <div class="config-item">
<label class="checkbox-label"> <label class="checkbox-label">
<input <input type="checkbox" v-model="localDisplayConfig.team.showLeader">
type="checkbox"
v-model="localDisplayConfig.team.showLeader"
>
<span class="text-gold">显示队长列</span> <span class="text-gold">显示队长列</span>
</label> </label>
</div> </div>
<div class="config-item"> <div class="config-item">
<label class="checkbox-label"> <label class="checkbox-label">
<span class="text-gold">业绩列显示名称</span> <span class="text-gold">业绩列显示名称</span>
<input type="text" v-model="localDisplayConfig.team.totalScoreColumn.displayName" <input type="text" v-model="localDisplayConfig.team.totalScoreColumn.displayName" placeholder="输入显示名称"
placeholder="输入显示名称" class="text-input"> class="text-input">
</label> </label>
</div> </div>
<div class="config-item"> <div class="config-item">
@@ -214,7 +203,8 @@
<h4 class="text-gold">列宽设置单位像素</h4> <h4 class="text-gold">列宽设置单位像素</h4>
<div class="column-width-item"> <div class="column-width-item">
<label class="text-gold">排名列</label> <label class="text-gold">排名列</label>
<input type="number" v-model.number="localDisplayConfig.team.columnWidths.rank" min="40" max="200" class="width-input"> <input type="number" v-model.number="localDisplayConfig.team.columnWidths.rank" min="40" max="200"
class="width-input">
</div> </div>
<div class="column-width-item"> <div class="column-width-item">
<label class="text-gold">战区名列</label> <label class="text-gold">战区名列</label>
@@ -228,11 +218,13 @@
</div> </div>
<div class="column-width-item"> <div class="column-width-item">
<label class="text-gold">业绩列</label> <label class="text-gold">业绩列</label>
<input type="number" v-model.number="localDisplayConfig.team.columnWidths.score" min="60" max="200" class="width-input"> <input type="number" v-model.number="localDisplayConfig.team.columnWidths.score" min="60" max="200"
class="width-input">
</div> </div>
<div class="column-width-item" v-if="localDisplayConfig.team.showMemberCount"> <div class="column-width-item" v-if="localDisplayConfig.team.showMemberCount">
<label class="text-gold">人数列</label> <label class="text-gold">人数列</label>
<input type="number" v-model.number="localDisplayConfig.team.columnWidths.memberCount" min="60" max="100" class="width-input"> <input type="number" v-model.number="localDisplayConfig.team.columnWidths.memberCount" min="60"
max="100" class="width-input">
</div> </div>
<div class="column-width-item" v-if="localDisplayConfig.team.showLeader"> <div class="column-width-item" v-if="localDisplayConfig.team.showLeader">
<label class="text-gold">队长列</label> <label class="text-gold">队长列</label>
@@ -245,14 +237,16 @@
</div> </div>
<div class="column-width-item"> <div class="column-width-item">
<label class="text-gold">奖金列</label> <label class="text-gold">奖金列</label>
<input type="number" v-model.number="localDisplayConfig.team.columnWidths.bonus" min="60" max="200" class="width-input"> <input type="number" v-model.number="localDisplayConfig.team.columnWidths.bonus" min="60" max="200"
class="width-input">
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<!-- 个人排名管理 --><div v-if="currentTab === 'individual'" class="rank-content"> <!-- 个人排名管理 -->
<div v-if="currentTab === 'individual'" class="rank-content">
<div class="management-header"> <div class="management-header">
<h2 class="game-subtitle">👤 个人排名管理</h2> <h2 class="game-subtitle">👤 个人排名管理</h2>
<button @click="showAddIndividual = true" class="btn-game"> 添加人员</button> <button @click="showAddIndividual = true" class="btn-game"> 添加人员</button>
@@ -268,20 +262,18 @@
<span class="bonus-col">奖金</span> <span class="bonus-col">奖金</span>
<span class="action-col">操作</span> <span class="action-col">操作</span>
</div> </div>
<div <div v-for="(item, index) in localIndividualRankings" :key="item.id" class="table-row"
v-for="(item, index) in localIndividualRankings" :class="{ 'highlight': index === 0 }">
:key="item.id"
class="table-row"
:class="{ 'highlight': index === 0 }"
>
<span class="rank-col">{{ index + 1 }}</span> <span class="rank-col">{{ index + 1 }}</span>
<span class="avatar-col"> <span class="avatar-col">
<img v-if="item.avatar && item.avatar.startsWith('/')" :src="item.avatar" alt="头像" class="table-avatar"> <img v-if="item.avatar && item.avatar.startsWith('/')" :src="item.avatar" alt="头像" class="table-avatar">
<span v-else>{{ item.avatar || '👤' }}</span> <span v-else>{{ item.avatar || '👤' }}</span>
</span> </span>
<span class="name-col">{{ item.name }}</span> <span class="name-col">{{ item.name }}</span>
<span class="score-col">{{ localDisplayConfig.individual.scoreColumn.displayStyle === 'amount' ? '¥' + item.score : item.score }}</span> <span class="score-col">{{ localDisplayConfig.individual.scoreColumn.displayStyle === 'amount' ? '¥' +
<span v-if="localDisplayConfig.individual.showLevel" class="level-col" :class="`level-${item.level}`">{{ item.level }}</span> item.score : item.score }}</span>
<span v-if="localDisplayConfig.individual.showLevel" class="level-col" :class="`level-${item.level}`">{{
item.level }}</span>
<span v-if="localDisplayConfig.individual.showDepartment" class="dept-col">{{ item.department }}</span> <span v-if="localDisplayConfig.individual.showDepartment" class="dept-col">{{ item.department }}</span>
<span class="bonus-col">¥{{ item.bonus }}</span> <span class="bonus-col">¥{{ item.bonus }}</span>
<span class="action-col"> <span class="action-col">
@@ -292,7 +284,8 @@
</div> </div>
</div> </div>
<!-- 战区排名管理 --><div v-if="currentTab === 'team'" class="rank-content"> <!-- 战区排名管理 -->
<div v-if="currentTab === 'team'" class="rank-content">
<div class="management-header"> <div class="management-header">
<h2 class="game-subtitle">👥 战区排名管理</h2> <h2 class="game-subtitle">👥 战区排名管理</h2>
<button @click="showAddTeam = true" class="btn-game"> 添加战区</button> <button @click="showAddTeam = true" class="btn-game"> 添加战区</button>
@@ -307,21 +300,18 @@
<span class="bonus-col">奖金</span> <span class="bonus-col">奖金</span>
<span class="action-col">操作</span> <span class="action-col">操作</span>
</div> </div>
<div <div v-for="(item, index) in localTeamRankings" :key="item.id" class="table-row"
v-for="(item, index) in localTeamRankings" :class="{ 'highlight': index === 0 }">
:key="item.id"
class="table-row"
:class="{ 'highlight': index === 0 }"
>
<span class="rank-col">{{ index + 1 }}</span> <span class="rank-col">{{ index + 1 }}</span>
<span class="name-col">{{ item.name }}</span> <span class="name-col">{{ item.name }}</span>
<span class="score-col">{{ localDisplayConfig.team.totalScoreColumn.displayStyle === 'amount' ? '¥' + item.totalScore : item.totalScore }}</span> <span class="score-col">{{ localDisplayConfig.team.totalScoreColumn.displayStyle === 'amount' ? '¥' +
item.totalScore : item.totalScore }}</span>
<span v-if="localDisplayConfig.team.showMemberCount" class="member-col">{{ item.memberCount }}</span> <span v-if="localDisplayConfig.team.showMemberCount" class="member-col">{{ item.memberCount }}</span>
<span v-if="localDisplayConfig.team.showLeader" class="leader-col">{{ item.leader }}</span> <span v-if="localDisplayConfig.team.showLeader" class="leader-col">{{ item.leader }}</span>
<span class="bonus-col">¥{{ item.bonus }}</span> <span class="bonus-col">¥{{ item.bonus }}</span>
<span class="action-col"> <span class="action-col">
<button @click="editTeam(item)" class="btn-game-secondary btn-edit"></button> <button @click="editTeam(item)" class="btn-game-secondary btn-edit"></button>
<button @click="deleteTeam(item.id)" class="btn-game-secondary btn-delete">🗑</button> <button @click="deleteTeam(item.id)" class="btn-game-secondary btn-delete">🗑</button>
</span> </span>
</div> </div>
</div> </div>
@@ -333,11 +323,7 @@
<div class="config-section"> <div class="config-section">
<h3 class="text-gold">🏆 奖金规则配置</h3> <h3 class="text-gold">🏆 奖金规则配置</h3>
<div class="bonus-rules-list"> <div class="bonus-rules-list">
<div <div v-for="(rule, index) in localBonusRules" :key="index" class="bonus-rule-item">
v-for="(rule, index) in localBonusRules"
:key="index"
class="bonus-rule-item"
>
<div class="rule-form"> <div class="rule-form">
<div class="form-group"> <div class="form-group">
<label class="text-gold">名次范围:</label> <label class="text-gold">名次范围:</label>
@@ -349,11 +335,13 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="text-gold">个人奖励:</label> <label class="text-gold">个人奖励:</label>
<input v-model="rule.individualBonus" type="text" class="form-input" placeholder="如: ¥10000, ¥8000, ¥5000"> <input v-model="rule.individualBonus" type="text" class="form-input"
placeholder="如: ¥10000, ¥8000, ¥5000">
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="text-gold">团队奖励:</label> <label class="text-gold">团队奖励:</label>
<input v-model="rule.teamBonus" type="text" class="form-input" placeholder="如: ¥50000, ¥30000, ¥20000"> <input v-model="rule.teamBonus" type="text" class="form-input"
placeholder="如: ¥50000, ¥30000, ¥20000">
</div> </div>
<div class="form-actions"> <div class="form-actions">
<button @click="deleteBonusRule(index)" class="btn-game-secondary">🗑 删除</button> <button @click="deleteBonusRule(index)" class="btn-game-secondary">🗑 删除</button>
@@ -374,28 +362,23 @@
<div class="logo-upload-section"> <div class="logo-upload-section">
<div class="logo-preview"> <div class="logo-preview">
<div v-if="championLogos.teamChampion" class="logo-image-container"> <div v-if="championLogos.teamChampion" class="logo-image-container">
<img :src="championLogos.teamChampion" alt="战区冠军Logo" class="logo-preview-image"> <img :src="championLogos.teamChampion" alt="战区冠军Logo" class="logo-preview-image">
</div> </div>
<div v-else class="logo-placeholder"> <div v-else class="logo-placeholder">
<span>未上传Logo</span> <span>未上传Logo</span>
</div> </div>
</div> </div>
<div class="upload-controls"> <div class="upload-controls">
<input type="file" accept="image/*" @change="(e) => handleChampionLogoUpload(e, 'teamChampion')" class="logo-input"> <input type="file" accept="image/*" @change="(e) => handleChampionLogoUpload(e, 'teamChampion')"
class="logo-input">
<button v-if="championLogos.teamChampion" @click="clearChampionLogo('teamChampion')" class="btn-clear"> <button v-if="championLogos.teamChampion" @click="clearChampionLogo('teamChampion')" class="btn-clear">
清除Logo 清除Logo
</button> </button>
</div> </div>
<div class="size-control"> <div class="size-control">
<label class="text-gold">显示大小</label> <label class="text-gold">显示大小</label>
<input <input type="number" v-model.number="championLogos.teamChampionSize" min="30" max="200"
type="number" class="width-input" placeholder="输入大小(像素)">
v-model.number="championLogos.teamChampionSize"
min="30"
max="200"
class="width-input"
placeholder="输入大小(像素)"
>
<span class="size-unit">px</span> <span class="size-unit">px</span>
</div> </div>
<p class="upload-hint">支持JPGPNGGIF格式建议尺寸200x200像素文件大小不超过5MB</p> <p class="upload-hint">支持JPGPNGGIF格式建议尺寸200x200像素文件大小不超过5MB</p>
@@ -407,28 +390,24 @@
<div class="logo-upload-section"> <div class="logo-upload-section">
<div class="logo-preview"> <div class="logo-preview">
<div v-if="championLogos.individualChampion" class="logo-image-container"> <div v-if="championLogos.individualChampion" class="logo-image-container">
<img :src="championLogos.individualChampion" alt="英雄冠军Logo" class="logo-preview-image"> <img :src="championLogos.individualChampion" alt="英雄冠军Logo" class="logo-preview-image">
</div> </div>
<div v-else class="logo-placeholder"> <div v-else class="logo-placeholder">
<span>未上传Logo</span> <span>未上传Logo</span>
</div> </div>
</div> </div>
<div class="upload-controls"> <div class="upload-controls">
<input type="file" accept="image/*" @change="(e) => handleChampionLogoUpload(e, 'individualChampion')" class="logo-input"> <input type="file" accept="image/*" @change="(e) => handleChampionLogoUpload(e, 'individualChampion')"
<button v-if="championLogos.individualChampion" @click="clearChampionLogo('individualChampion')" class="btn-clear"> class="logo-input">
<button v-if="championLogos.individualChampion" @click="clearChampionLogo('individualChampion')"
class="btn-clear">
清除Logo 清除Logo
</button> </button>
</div> </div>
<div class="size-control"> <div class="size-control">
<label class="text-gold">显示大小</label> <label class="text-gold">显示大小</label>
<input <input type="number" v-model.number="championLogos.individualChampionSize" min="30" max="200"
type="number" class="width-input" placeholder="输入大小(像素)">
v-model.number="championLogos.individualChampionSize"
min="30"
max="200"
class="width-input"
placeholder="输入大小(像素)"
>
<span class="size-unit">px</span> <span class="size-unit">px</span>
</div> </div>
<p class="upload-hint">支持JPGPNGGIF格式建议尺寸200x200像素文件大小不超过5MB</p> <p class="upload-hint">支持JPGPNGGIF格式建议尺寸200x200像素文件大小不超过5MB</p>
@@ -446,50 +425,29 @@
<div class="config-options"> <div class="config-options">
<div class="config-item"> <div class="config-item">
<label class="checkbox-label"> <label class="checkbox-label">
<input <input type="checkbox" v-model="localDrumConfig.sound.enabled">
type="checkbox"
v-model="localDrumConfig.sound.enabled"
>
<span>启用音效</span> <span>启用音效</span>
</label> </label>
</div> </div>
<div class="config-item"> <div class="config-item">
<label class="checkbox-label"> <label class="checkbox-label">
<span>音量 (0-1)</span> <span>音量 (0-1)</span>
<input <input type="number" v-model.number="localDrumConfig.sound.volume" min="0" max="1" step="0.1"
type="number" class="width-input">
v-model.number="localDrumConfig.sound.volume"
min="0"
max="1"
step="0.1"
class="width-input"
>
</label> </label>
</div> </div>
<div class="config-item"> <div class="config-item">
<label class="checkbox-label"> <label class="checkbox-label">
<span>起音时间 (s)</span> <span>起音时间 (s)</span>
<input <input type="number" v-model.number="localDrumConfig.sound.attackTime" min="0.001" max="0.5"
type="number" step="0.01" class="width-input">
v-model.number="localDrumConfig.sound.attackTime"
min="0.001"
max="0.5"
step="0.01"
class="width-input"
>
</label> </label>
</div> </div>
<div class="config-item"> <div class="config-item">
<label class="checkbox-label"> <label class="checkbox-label">
<span>衰减时间 (s)</span> <span>衰减时间 (s)</span>
<input <input type="number" v-model.number="localDrumConfig.sound.decayTime" min="0.05" max="1" step="0.05"
type="number" class="width-input">
v-model.number="localDrumConfig.sound.decayTime"
min="0.05"
max="1"
step="0.05"
class="width-input"
>
</label> </label>
</div> </div>
<h4 style="margin-top: 15px; color: #666;">🎵 第一个音调</h4> <h4 style="margin-top: 15px; color: #666;">🎵 第一个音调</h4>
@@ -507,13 +465,8 @@
<div class="config-item"> <div class="config-item">
<label class="checkbox-label"> <label class="checkbox-label">
<span>频率 (Hz)</span> <span>频率 (Hz)</span>
<input <input type="number" v-model.number="localDrumConfig.sound.frequency1" min="50" max="500"
type="number" class="width-input">
v-model.number="localDrumConfig.sound.frequency1"
min="50"
max="500"
class="width-input"
>
</label> </label>
</div> </div>
<h4 style="margin-top: 15px; color: #666;">🎵 第二个音调</h4> <h4 style="margin-top: 15px; color: #666;">🎵 第二个音调</h4>
@@ -531,13 +484,8 @@
<div class="config-item"> <div class="config-item">
<label class="checkbox-label"> <label class="checkbox-label">
<span>频率 (Hz)</span> <span>频率 (Hz)</span>
<input <input type="number" v-model.number="localDrumConfig.sound.frequency2" min="50" max="500"
type="number" class="width-input">
v-model.number="localDrumConfig.sound.frequency2"
min="50"
max="500"
class="width-input"
>
</label> </label>
</div> </div>
</div> </div>
@@ -549,72 +497,43 @@
<div class="config-options"> <div class="config-options">
<div class="config-item"> <div class="config-item">
<label class="checkbox-label"> <label class="checkbox-label">
<input <input type="checkbox" v-model="localDrumConfig.animation.enabled">
type="checkbox"
v-model="localDrumConfig.animation.enabled"
>
<span>启用动画</span> <span>启用动画</span>
</label> </label>
</div> </div>
<div class="config-item"> <div class="config-item">
<label class="checkbox-label"> <label class="checkbox-label">
<span>节拍间隔 (ms)</span> <span>节拍间隔 (ms)</span>
<input <input type="number" v-model.number="localDrumConfig.animation.beatInterval" min="50" max="1000"
type="number" class="width-input">
v-model.number="localDrumConfig.animation.beatInterval"
min="50"
max="1000"
class="width-input"
>
</label> </label>
</div> </div>
<div class="config-item"> <div class="config-item">
<label class="checkbox-label"> <label class="checkbox-label">
<span>跳动缩放比例</span> <span>跳动缩放比例</span>
<input <input type="number" v-model.number="localDrumConfig.animation.beatScale" min="1.0" max="2.0"
type="number" step="0.1" class="width-input">
v-model.number="localDrumConfig.animation.beatScale"
min="1.0"
max="2.0"
step="0.1"
class="width-input"
>
</label> </label>
</div> </div>
<div class="config-item"> <div class="config-item">
<label class="checkbox-label"> <label class="checkbox-label">
<span>跳动上下位移 (px)</span> <span>跳动上下位移 (px)</span>
<input <input type="number" v-model.number="localDrumConfig.animation.beatTranslateY" min="-50" max="50"
type="number" class="width-input">
v-model.number="localDrumConfig.animation.beatTranslateY"
min="-50"
max="50"
class="width-input"
>
</label> </label>
</div> </div>
<div class="config-item"> <div class="config-item">
<label class="checkbox-label"> <label class="checkbox-label">
<span>跳动旋转角度 (deg)</span> <span>跳动旋转角度 (deg)</span>
<input <input type="number" v-model.number="localDrumConfig.animation.beatRotate" min="0" max="20"
type="number" class="width-input">
v-model.number="localDrumConfig.animation.beatRotate"
min="0"
max="20"
class="width-input"
>
</label> </label>
</div> </div>
<div class="config-item"> <div class="config-item">
<label class="checkbox-label"> <label class="checkbox-label">
<span>单次跳动持续时间 (ms)</span> <span>单次跳动持续时间 (ms)</span>
<input <input type="number" v-model.number="localDrumConfig.animation.beatDuration" min="50" max="500"
type="number" class="width-input">
v-model.number="localDrumConfig.animation.beatDuration"
min="50"
max="500"
class="width-input"
>
</label> </label>
</div> </div>
</div> </div>
@@ -627,62 +546,36 @@
<div class="config-item"> <div class="config-item">
<label class="checkbox-label"> <label class="checkbox-label">
<span>每小节总拍数</span> <span>每小节总拍数</span>
<input <input type="number" v-model.number="localDrumConfig.pattern.totalBeats" min="1" max="8"
type="number" class="width-input">
v-model.number="localDrumConfig.pattern.totalBeats"
min="1"
max="8"
class="width-input"
>
</label> </label>
</div> </div>
<div class="config-item"> <div class="config-item">
<label class="checkbox-label"> <label class="checkbox-label">
<span>强拍位置 (1-4)</span> <span>强拍位置 (1-4)</span>
<input <input type="text" v-model="localDrumConfig.pattern.strongBeatsStr" placeholder="如: 1,4"
type="text" class="text-input" @input="updateStrongBeats">
v-model="localDrumConfig.pattern.strongBeatsStr"
placeholder="如: 1,4"
class="text-input"
@input="updateStrongBeats"
>
</label> </label>
</div> </div>
<div class="config-item"> <div class="config-item">
<label class="checkbox-label"> <label class="checkbox-label">
<span>强拍音量倍数</span> <span>强拍音量倍数</span>
<input <input type="number" v-model.number="localDrumConfig.pattern.accentMultiplier" min="1" max="3"
type="number" step="0.1" class="width-input">
v-model.number="localDrumConfig.pattern.accentMultiplier"
min="1"
max="3"
step="0.1"
class="width-input"
>
</label> </label>
</div> </div>
<div class="config-item"> <div class="config-item">
<label class="checkbox-label"> <label class="checkbox-label">
<span>强拍频率偏移 (%)</span> <span>强拍频率偏移 (%)</span>
<input <input type="number" v-model.number="localDrumConfig.pattern.accentFrequencyOffset" min="-50" max="50"
type="number" class="width-input">
v-model.number="localDrumConfig.pattern.accentFrequencyOffset"
min="-50"
max="50"
class="width-input"
>
</label> </label>
</div> </div>
<div class="config-item"> <div class="config-item">
<label class="checkbox-label"> <label class="checkbox-label">
<span>强拍动画增强 (%)</span> <span>强拍动画增强 (%)</span>
<input <input type="number" v-model.number="localDrumConfig.pattern.accentAnimation" min="0" max="100"
type="number" class="width-input">
v-model.number="localDrumConfig.pattern.accentAnimation"
min="0"
max="100"
class="width-input"
>
</label> </label>
</div> </div>
</div> </div>
@@ -690,13 +583,15 @@
</div> </div>
</div> </div>
<!-- 保存按钮 --><div class="save-section"> <!-- 保存按钮 -->
<div class="save-section">
<button @click="saveData" class="save-btn">💾 保存所有数据</button> <button @click="saveData" class="save-btn">💾 保存所有数据</button>
<button @click="goBack" class="back-btn">🏠 返回首页</button> <button @click="goBack" class="back-btn">🏠 返回首页</button>
</div> </div>
</div> </div>
<!-- 添加/编辑个人对话框 --><div v-if="showAddIndividual || showEditIndividual" class="modal-overlay"> <!-- 添加/编辑个人对话框 -->
<div v-if="showAddIndividual || showEditIndividual" class="modal-overlay">
<div class="modal"> <div class="modal">
<h3 class="modal-title">{{ editingIndividual ? '编辑人员' : '添加人员' }}</h3> <h3 class="modal-title">{{ editingIndividual ? '编辑人员' : '添加人员' }}</h3>
<form @submit.prevent="saveIndividual"> <form @submit.prevent="saveIndividual">
@@ -722,14 +617,16 @@
<label>头像:</label> <label>头像:</label>
<div class="avatar-upload"> <div class="avatar-upload">
<div v-if="individualForm.avatar && individualForm.avatar.startsWith('/')" class="avatar-preview"> <div v-if="individualForm.avatar && individualForm.avatar.startsWith('/')" class="avatar-preview">
<img :src="individualForm.avatar" alt="头像预览" style="width: 80px; height: 80px; object-fit: cover; border-radius: 50%;"> <img :src="individualForm.avatar" alt="头像预览"
style="width: 80px; height: 80px; object-fit: cover; border-radius: 50%;">
</div> </div>
<div v-else class="avatar-emoji-preview"> <div v-else class="avatar-emoji-preview">
{{ individualForm.avatar }} {{ individualForm.avatar }}
</div> </div>
<input type="file" accept="image/*" @change="handleAvatarUpload" class="avatar-input"> <input type="file" accept="image/*" @change="handleAvatarUpload" class="avatar-input">
<label for="emoji-select">或选择表情:</label> <label for="emoji-select">或选择表情:</label>
<select v-model="individualForm.avatar" id="emoji-select" class="form-input" v-if="!individualForm.avatar.startsWith('/')"> <select v-model="individualForm.avatar" id="emoji-select" class="form-input"
v-if="!individualForm.avatar.startsWith('/')">
<option value="👑">👑 皇冠</option> <option value="👑">👑 皇冠</option>
<option value="🥇">🥇 金牌</option> <option value="🥇">🥇 金牌</option>
<option value="🥈">🥈 银牌</option> <option value="🥈">🥈 银牌</option>
@@ -767,7 +664,8 @@
</div> </div>
</div> </div>
<!-- 添加/编辑战区对话框 --><div v-if="showAddTeam || showEditTeam" class="modal-overlay"> <!-- 添加/编辑战区对话框 -->
<div v-if="showAddTeam || showEditTeam" class="modal-overlay">
<div class="modal"> <div class="modal">
<h3 class="modal-title">{{ editingTeam ? '编辑战区' : '添加战区' }}</h3> <h3 class="modal-title">{{ editingTeam ? '编辑战区' : '添加战区' }}</h3>
<form @submit.prevent="saveTeam"> <form @submit.prevent="saveTeam">
@@ -865,13 +763,13 @@ onMounted(async () => {
localIndividualRankings.value = [...individualRankings]; localIndividualRankings.value = [...individualRankings];
localTeamRankings.value = [...teamRankings]; localTeamRankings.value = [...teamRankings];
localBonusRules.value = [...bonusRules]; localBonusRules.value = [...bonusRules];
localDisplayConfig.value = {...displayConfig}; localDisplayConfig.value = { ...displayConfig };
localBattleEndTime.value = {...battleEndTime}; localBattleEndTime.value = { ...battleEndTime };
localDrumConfig.value = {...drumConfig}; localDrumConfig.value = { ...drumConfig };
// 初始化冠军Logo配置 // 初始化冠军Logo配置
if (displayConfig.championLogos) { if (displayConfig.championLogos) {
championLogos.value = {...displayConfig.championLogos}; championLogos.value = { ...displayConfig.championLogos };
} }
// 重新处理强拍位置 // 重新处理强拍位置
@@ -944,9 +842,9 @@ const handleRefreshData = () => {
localIndividualRankings.value = [...individualRankings]; localIndividualRankings.value = [...individualRankings];
localTeamRankings.value = [...teamRankings]; localTeamRankings.value = [...teamRankings];
localBonusRules.value = [...bonusRules]; localBonusRules.value = [...bonusRules];
localDisplayConfig.value = {...displayConfig}; localDisplayConfig.value = { ...displayConfig };
localBattleEndTime.value = {...battleEndTime}; localBattleEndTime.value = { ...battleEndTime };
localDrumConfig.value = {...drumConfig}; localDrumConfig.value = { ...drumConfig };
// 重新处理强拍位置 // 重新处理强拍位置
if (localDrumConfig.value.pattern && localDrumConfig.value.pattern.strongBeats) { if (localDrumConfig.value.pattern && localDrumConfig.value.pattern.strongBeats) {
@@ -970,10 +868,10 @@ const currentTab = ref('individual');
const localIndividualRankings = ref([...individualRankings]); const localIndividualRankings = ref([...individualRankings]);
const localTeamRankings = ref([...teamRankings]); const localTeamRankings = ref([...teamRankings]);
const localBonusRules = ref([...bonusRules]); const localBonusRules = ref([...bonusRules]);
const localDisplayConfig = ref({...displayConfig}); const localDisplayConfig = ref({ ...displayConfig });
const localBattleEndTime = ref({...battleEndTime}); const localBattleEndTime = ref({ ...battleEndTime });
// 初始化本地战鼓配置副本 // 初始化本地战鼓配置副本
const localDrumConfig = ref({...drumConfig}); const localDrumConfig = ref({ ...drumConfig });
// 添加强拍位置的字符串表示,用于输入框 // 添加强拍位置的字符串表示,用于输入框
if (localDrumConfig.value.pattern && localDrumConfig.value.pattern.strongBeats) { if (localDrumConfig.value.pattern && localDrumConfig.value.pattern.strongBeats) {
localDrumConfig.value.pattern.strongBeatsStr = localDrumConfig.value.pattern.strongBeats.join(',') || '1,4'; localDrumConfig.value.pattern.strongBeatsStr = localDrumConfig.value.pattern.strongBeats.join(',') || '1,4';
@@ -991,9 +889,9 @@ onMounted(async () => {
localIndividualRankings.value = [...individualRankings]; localIndividualRankings.value = [...individualRankings];
localTeamRankings.value = [...teamRankings]; localTeamRankings.value = [...teamRankings];
localBonusRules.value = [...bonusRules]; localBonusRules.value = [...bonusRules];
localDisplayConfig.value = {...displayConfig}; localDisplayConfig.value = { ...displayConfig };
localBattleEndTime.value = {...battleEndTime}; localBattleEndTime.value = { ...battleEndTime };
localDrumConfig.value = {...drumConfig}; localDrumConfig.value = { ...drumConfig };
// 重新处理强拍位置 // 重新处理强拍位置
if (localDrumConfig.value.pattern && localDrumConfig.value.pattern.strongBeats) { if (localDrumConfig.value.pattern && localDrumConfig.value.pattern.strongBeats) {
@@ -1137,7 +1035,7 @@ const saveData = async () => {
// 保存战鼓配置前,确保强拍位置数组是最新的 // 保存战鼓配置前,确保强拍位置数组是最新的
updateStrongBeats(); updateStrongBeats();
// 移除临时的字符串表示,避免保存到配置中 // 移除临时的字符串表示,避免保存到配置中
const configToSave = {...localDrumConfig.value}; const configToSave = { ...localDrumConfig.value };
delete configToSave.pattern.strongBeatsStr; delete configToSave.pattern.strongBeatsStr;
// 导入必要的配置服务函数 // 导入必要的配置服务函数
@@ -1293,8 +1191,8 @@ const deleteBonusRule = (index) => {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.btn-edit,
.btn-edit, .btn-delete { .btn-delete {
padding: 0; padding: 0;
cursor: pointer; cursor: pointer;
transition: background-color 0.3s; transition: background-color 0.3s;
@@ -1350,17 +1248,20 @@ const deleteBonusRule = (index) => {
border-radius: 8px; border-radius: 8px;
font-size: 1rem; font-size: 1rem;
transition: border-color 0.3s, box-shadow 0.3s; transition: border-color 0.3s, box-shadow 0.3s;
min-height: 44px; /* 增加最小高度,符合可访问性标准 */ min-height: 44px;
/* 增加最小高度,符合可访问性标准 */
} }
.form-input:focus { .form-input:focus {
outline: none; outline: none;
border-color: #667eea; border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); /* 增加聚焦效果 */ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
/* 增加聚焦效果 */
} }
.form-input:hover { .form-input:hover {
border-color: #99a5f0; /* 悬停效果 */ border-color: #99a5f0;
/* 悬停效果 */
} }
.login-btn { .login-btn {
@@ -1741,7 +1642,8 @@ const deleteBonusRule = (index) => {
gap: 10px; gap: 10px;
} }
.avatar-preview, .avatar-emoji-preview { .avatar-preview,
.avatar-emoji-preview {
width: 80px; width: 80px;
height: 80px; height: 80px;
display: flex; display: flex;

View File

@@ -8,36 +8,31 @@
</section> </section>
<!-- 第二部分战鼓动画浮动并支持拖放 --> <!-- 第二部分战鼓动画浮动并支持拖放 -->
<section <section v-if="localDisplayConfig.showDrum" class="drums-section card-game" @mousedown="startDrag"
v-if="localDisplayConfig.showDrum" @click="handleDrumClick" :style="{ left: drumsPosition.x + 'px', top: drumsPosition.y + 'px' }">
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-container">
<!-- 战鼓动画在上面 --> <!-- 战鼓动画在上面 -->
<div class="drums-animation"> <div class="drums-animation">
<div class="drum glow-border" :class="{ beating: isBeating }">🥁</div> <div class="drum glow-border" :class="{ beating: isBeating }">🥁</div>
<div class="drum" :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="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 class="drum" :class="{ beating: isBeating }">🥁</div> <div class="drum" :class="{ beating: isBeating }">🥁</div>
</div> </div>
</div> </div>
</section> </section>
<!-- 任务设置模块 --> <!-- 任务设置模块 -->
<section class="task-settings-section card-game"> <section class="task-settings-section card-game">
<div class="task-title-container"> <div class="task-title-container">
<h1 class="game-title" v-if="taskSettings?.mainTitle">任务: <span class="game-title-highlight">{{ taskSettings.mainTitle }} </span> </h1> <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> <p class="game-subtitle" v-if="taskSettings?.subtitle">{{ taskSettings.subtitle }}</p>
</div> </div>
</section> </section>
<!-- 第三部分奖金设置图片形式 --> <!-- 第三部分奖金设置图片形式 -->
<section v-if="localDisplayConfig.showBonusModule" class="bonus-section card-game" <section v-if="localDisplayConfig.showBonusModule" class="bonus-section card-game" @mousedown="startBonusDrag"
@mousedown="startBonusDrag"
@click.stop> @click.stop>
<div class="bonus-awards-container"> <div class="bonus-awards-container">
<div><img src="/award1.png" alt="一等奖" class="award-image"></div> <div><img src="/award1.png" alt="一等奖" class="award-image"></div>
@@ -56,11 +51,10 @@
<div class="team-champion"> <div class="team-champion">
<div class="team-logo"> <div class="team-logo">
<img v-if="localDisplayConfig.championLogos?.teamChampion" <img v-if="localDisplayConfig.championLogos?.teamChampion"
:src="localDisplayConfig.championLogos.teamChampion" :src="localDisplayConfig.championLogos.teamChampion" alt="战区冠军" class="champion-logo"
alt="战区冠军" :style="{ width: localDisplayConfig.championLogos?.teamChampionSize + 'px', height: localDisplayConfig.championLogos?.teamChampionSize + 'px' }">
class="champion-logo" <span v-else
:style="{ width: localDisplayConfig.championLogos?.teamChampionSize + 'px', height: localDisplayConfig.championLogos?.teamChampionSize + 'px' }"> :style="{ fontSize: localDisplayConfig.championLogos?.teamChampionSize ? (localDisplayConfig.championLogos.teamChampionSize * 0.8) + 'px' : '2rem' }">🏅</span>
<span v-else :style="{ fontSize: localDisplayConfig.championLogos?.teamChampionSize ? (localDisplayConfig.championLogos.teamChampionSize * 0.8) + 'px' : '2rem' }">🏅</span>
</div> </div>
<div class="champion-name"> <div class="champion-name">
{{ teamRankings[0]?.name || '暂无冠军' }} {{ teamRankings[0]?.name || '暂无冠军' }}
@@ -77,19 +71,15 @@
<span v-if="localDisplayConfig.team?.showLeader" class="leader-col">队长</span> <span v-if="localDisplayConfig.team?.showLeader" class="leader-col">队长</span>
<span v-if="localDisplayConfig.team?.showBonus" class="bonus-col">奖金</span> <span v-if="localDisplayConfig.team?.showBonus" class="bonus-col">奖金</span>
</div> </div>
<div <div v-for="(item, index) in teamRankings" :key="item.id" class="table-row"
v-for="(item, index) in teamRankings" :style="{ 'grid-template-columns': teamGridTemplate }" :class="{
:key="item.id"
class="table-row"
:style="{ 'grid-template-columns': teamGridTemplate }"
:class="{
'top-three': index < 3, 'top-three': index < 3,
'highlight': index === 0 'highlight': index === 0
}" }">
>
<span class="rank-col">{{ index + 1 }}</span> <span class="rank-col">{{ index + 1 }}</span>
<span class="name-col">{{ item.name }}</span> <span class="name-col">{{ item.name }}</span>
<span class="score-col">{{ localDisplayConfig.team?.totalScoreColumn?.displayStyle === 'amount' ? '¥' + item.totalScore : item.totalScore }}</span> <span class="score-col">{{ localDisplayConfig.team?.totalScoreColumn?.displayStyle === 'amount' ? '¥' +
item.totalScore : item.totalScore }}</span>
<span v-if="localDisplayConfig.team?.showMemberCount" class="member-col">{{ item.memberCount }}</span> <span v-if="localDisplayConfig.team?.showMemberCount" class="member-col">{{ item.memberCount }}</span>
<span v-if="localDisplayConfig.team?.showLeader" class="leader-col">{{ item.leader }}</span> <span v-if="localDisplayConfig.team?.showLeader" class="leader-col">{{ item.leader }}</span>
<span v-if="localDisplayConfig.team?.showBonus" class="bonus-col">¥{{ item.bonus }}</span> <span v-if="localDisplayConfig.team?.showBonus" class="bonus-col">¥{{ item.bonus }}</span>
@@ -104,16 +94,13 @@
<div class="individual-champion"> <div class="individual-champion">
<div class="individual-avatar"> <div class="individual-avatar">
<img v-if="individualRankings[0]?.avatar && individualRankings[0].avatar.startsWith('/')" <img v-if="individualRankings[0]?.avatar && individualRankings[0].avatar.startsWith('/')"
:src="individualRankings[0].avatar" :src="individualRankings[0].avatar" alt="冠军头像" class="avatar-image avatar-image-champion"
alt="冠军头像" :style="{ width: localDisplayConfig.championLogos?.individualChampionSize + 'px', height: localDisplayConfig.championLogos?.individualChampionSize + 'px' }">
class="avatar-image avatar-image-champion"
:style="{ width: localDisplayConfig.championLogos?.individualChampionSize + 'px', height: localDisplayConfig.championLogos?.individualChampionSize + 'px' }">
<img v-else-if="localDisplayConfig.championLogos?.individualChampion" <img v-else-if="localDisplayConfig.championLogos?.individualChampion"
:src="localDisplayConfig.championLogos.individualChampion" :src="localDisplayConfig.championLogos.individualChampion" alt="英雄冠军" class="champion-logo"
alt="英雄冠军" :style="{ width: localDisplayConfig.championLogos?.individualChampionSize + 'px', height: localDisplayConfig.championLogos?.individualChampionSize + 'px' }">
class="champion-logo" <span v-else
:style="{ width: localDisplayConfig.championLogos?.individualChampionSize + 'px', height: localDisplayConfig.championLogos?.individualChampionSize + 'px' }"> :style="{ fontSize: localDisplayConfig.championLogos?.individualChampionSize ? (localDisplayConfig.championLogos.individualChampionSize * 0.8) + 'px' : '2rem' }">
<span v-else :style="{ fontSize: localDisplayConfig.championLogos?.individualChampionSize ? (localDisplayConfig.championLogos.individualChampionSize * 0.8) + 'px' : '2rem' }">
{{ individualRankings[0]?.avatar || '👤' }} {{ individualRankings[0]?.avatar || '👤' }}
</span> </span>
</div> </div>
@@ -127,46 +114,43 @@
<div class="rank-table"> <div class="rank-table">
<div class="table-header" :style="{ 'grid-template-columns': individualGridTemplate }"> <div class="table-header" :style="{ 'grid-template-columns': individualGridTemplate }">
<span class="rank-col">排名</span> <span class="rank-col">排名</span>
<span class="avatar-col">头像</span> <span v-if="localDisplayConfig.individual?.showAvatar" class="avatar-col">头像</span>
<span class="name-col">姓名</span> <span class="name-col">姓名</span>
<span v-if="localDisplayConfig.individual?.showTeam" class="team-col">{{ localDisplayConfig.individual?.teamColumn?.displayName || '战区' }}</span> <span v-if="localDisplayConfig.individual?.showTeam" class="team-col">{{
<span class="score-col">{{ localDisplayConfig.individual?.scoreColumn?.displayName || '业绩' }}</span> localDisplayConfig.individual?.teamColumn?.displayName || '战区' }}</span>
<span v-if="localDisplayConfig.individual?.showLevel" class="level-col">等级</span> <span class="score-col">{{ localDisplayConfig.individual?.scoreColumn?.displayName || '业绩' }}</span>
<span v-if="localDisplayConfig.individual?.showDepartment" class="dept-col">部门</span> <span v-if="localDisplayConfig.individual?.showLevel" class="level-col">等级</span>
<span v-if="localDisplayConfig.individual?.showBonus" class="bonus-col">奖金</span> <span v-if="localDisplayConfig.individual?.showDepartment" class="dept-col">部门</span>
</div> <span v-if="localDisplayConfig.individual?.showBonus" class="bonus-col">奖金</span>
<div </div>
v-for="(item, index) in individualRankings" <div v-for="(item, index) in individualRankings.slice(0, localDisplayConfig.individual.maxDisplayRows)" :key="item.id" class="table-row"
:key="item.id" :style="{ 'grid-template-columns': individualGridTemplate }" :class="{
class="table-row"
:style="{ 'grid-template-columns': individualGridTemplate }"
:class="{
'top-three': index < 3, 'top-three': index < 3,
'highlight': index === 0 'highlight': index === 0
}" }">
> <span class="rank-col">{{ index + 1 }}</span>
<span class="rank-col">{{ index + 1 }}</span> <span v-if="localDisplayConfig.individual?.showAvatar" class="avatar-col">
<span class="avatar-col"> <img v-if="item.avatar && item.avatar.startsWith('/')" :src="item.avatar" alt="头像"
<img v-if="item.avatar && item.avatar.startsWith('/')" :src="item.avatar" alt="头像" class="avatar-image"> class="avatar-image">
<span v-else>{{ item.avatar }}</span> <span v-else>{{ item.avatar || '👤' }}</span>
</span> </span>
<span class="name-col">{{ item.name }}</span> <span class="name-col">{{ item.name }}</span>
<span v-if="localDisplayConfig.individual?.showTeam" class="team-col">{{ item.team || '-' }}</span> <span v-if="localDisplayConfig.individual?.showTeam" class="team-col">{{ item.team || '-' }}</span>
<span class="score-col">{{ localDisplayConfig.individual?.scoreColumn?.displayStyle === 'amount' ? '¥' + item.score : item.score }}</span> <span class="score-col">{{ localDisplayConfig.individual?.scoreColumn?.displayStyle === 'amount' ? '¥' +
<span v-if="localDisplayConfig.individual?.showLevel" class="level-col" :class="`level-${item.level}`">{{ item.level }}</span> item.score : item.score }}</span>
<span v-if="localDisplayConfig.individual?.showDepartment" class="dept-col">{{ item.department }}</span> <span v-if="localDisplayConfig.individual?.showLevel" class="level-col"
<span v-if="localDisplayConfig.individual?.showBonus" class="bonus-col">¥{{ item.bonus }}</span> :class="`level-${item.level}`">{{ item.level }}</span>
<span v-if="localDisplayConfig.individual?.showDepartment" class="dept-col">{{ item.department }}</span>
<span v-if="localDisplayConfig.individual?.showBonus" class="bonus-col">¥{{ item.bonus }}</span>
</div>
</div> </div>
</div> </div>
</div>
</div> </div>
</div> </div>
</section> </section>
<!-- 浮动倒计时组件 --> <!-- 浮动倒计时组件 -->
<div class="timer-float glow-border" <div class="timer-float glow-border" :style="{ right: '20px', top: '10px' }">
:style="{ right: '20px', top: '10px' }"
>
<span class="label">距离结束还有</span> <span class="label">距离结束还有</span>
<div class="countdown" style="display: flex; gap: 15px;"> <div class="countdown" style="display: flex; gap: 15px;">
<span class="time-item timer-number">{{ days }}</span> <span class="time-item timer-number">{{ days }}</span>
@@ -202,6 +186,12 @@ import { readConfig } from '../services/configService.js';
// 创建默认显示配置的函数 // 创建默认显示配置的函数
function createDefaultDisplayConfig() { function createDefaultDisplayConfig() {
return { return {
championLogos: {
teamChampion: '',
teamChampionSize: 60,
individualChampion: '',
individualChampionSize: 60
},
individual: { individual: {
scoreColumn: { scoreColumn: {
displayName: '战绩', displayName: '战绩',
@@ -215,6 +205,8 @@ function createDefaultDisplayConfig() {
showDepartment: true, showDepartment: true,
showBonus: false, showBonus: false,
showTeam: true, // 默认显示战区列 showTeam: true, // 默认显示战区列
showAvatar: false, // 默认不显示头像列
maxDisplayRows: 10, // 默认最多显示10行
columnWidths: { columnWidths: {
team: 120 // 默认战区列宽 team: 120 // 默认战区列宽
} }
@@ -324,7 +316,7 @@ const router = useRouter();
const individualGridTemplate = computed(() => { const individualGridTemplate = computed(() => {
try { try {
const config = localDisplayConfig.value?.individual; const config = localDisplayConfig.value?.individual;
if (!config) return '60px 60px 1fr 120px 80px 80px'; if (!config) return '60px 1fr 120px 80px 80px';
// 确保showTeam默认为true // 确保showTeam默认为true
if (config.showTeam === undefined) { if (config.showTeam === undefined) {
@@ -335,7 +327,12 @@ const individualGridTemplate = computed(() => {
const cols = []; const cols = [];
cols.push((widths.rank || 60) + 'px'); // 排名 cols.push((widths.rank || 60) + 'px'); // 排名
cols.push((widths.avatar || 60) + 'px'); // 头像
// 头像列 - 根据配置决定是否显示
if (config.showAvatar) {
cols.push((widths.avatar || 60) + 'px'); // 头像
}
cols.push((widths.name === 1 || widths.name === '1') ? '1fr' : (widths.name || 120) + 'px'); // 姓名 cols.push((widths.name === 1 || widths.name === '1') ? '1fr' : (widths.name || 120) + 'px'); // 姓名
// 战区列(在姓名列后面) // 战区列(在姓名列后面)
@@ -360,7 +357,7 @@ const individualGridTemplate = computed(() => {
return cols.join(' '); return cols.join(' ');
} catch (error) { } catch (error) {
console.error('计算英雄排名表格布局出错:', error); console.error('计算英雄排名表格布局出错:', error);
return '60px 60px 1fr 120px 80px 80px'; // 兜底布局(包含战区列) return '60px 1fr 120px 80px 80px'; // 兜底布局(包含头像列)
} }
}); });
@@ -425,7 +422,7 @@ let bonusDragOffset = { x: 0, y: 0 };
// 节流函数 // 节流函数
function throttle(func, limit) { function throttle(func, limit) {
let inThrottle; let inThrottle;
return function() { return function () {
const args = arguments; const args = arguments;
const context = this; const context = this;
if (!inThrottle) { if (!inThrottle) {
@@ -801,7 +798,7 @@ const handleBattleEndTimeChange = () => {
// 监听显示配置变化在真实环境中可能需要通过props或store来监听 // 监听显示配置变化在真实环境中可能需要通过props或store来监听
const handleDisplayConfigChange = () => { const handleDisplayConfigChange = () => {
// 更新本地配置 // 更新本地配置
localDisplayConfig.value = {...displayConfig}; localDisplayConfig.value = { ...displayConfig };
}; };
// 这里模拟监听配置变化 // 这里模拟监听配置变化
@@ -825,7 +822,6 @@ onUnmounted(() => {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.avatar-image-champion { .avatar-image-champion {
object-fit: contain; object-fit: contain;
} }
@@ -884,6 +880,7 @@ onUnmounted(() => {
font-weight: bold; font-weight: bold;
color: gold; color: gold;
} }
/* 基础样式 */ /* 基础样式 */
:root { :root {
--gold-primary: #ffd700; --gold-primary: #ffd700;
@@ -942,6 +939,7 @@ onUnmounted(() => {
/* 响应式设计 */ /* 响应式设计 */
@media (max-width: 768px) { @media (max-width: 768px) {
/* 隐藏倒计时模块 */ /* 隐藏倒计时模块 */
.timer-float { .timer-float {
display: none !important; display: none !important;
@@ -966,6 +964,7 @@ onUnmounted(() => {
top: 0 !important; top: 0 !important;
bottom: 0 !important; bottom: 0 !important;
} }
/* 保留响应式中的padding设置但移除其他可能冲突的位置样式 */ /* 保留响应式中的padding设置但移除其他可能冲突的位置样式 */
.bonus-section { .bonus-section {
padding: 10px; padding: 10px;
@@ -1028,6 +1027,7 @@ onUnmounted(() => {
0 0 10px rgba(0, 255, 255, 0.2), 0 0 10px rgba(0, 255, 255, 0.2),
inset 0 0 5px rgba(0, 255, 255, 0.1); inset 0 0 5px rgba(0, 255, 255, 0.1);
} }
100% { 100% {
box-shadow: box-shadow:
0 0 20px rgba(0, 255, 255, 0.5), 0 0 20px rgba(0, 255, 255, 0.5),
@@ -1096,7 +1096,8 @@ onUnmounted(() => {
padding: 12px 25px; padding: 12px 25px;
border-radius: 50px; border-radius: 50px;
font-weight: bold; font-weight: bold;
margin-bottom: 14px; /* 下移4px */ margin-bottom: 14px;
/* 下移4px */
} }
/* 科技感倒计时标签 */ /* 科技感倒计时标签 */
@@ -1175,6 +1176,7 @@ onUnmounted(() => {
0 0 10px rgba(255, 61, 0, 0.3), 0 0 10px rgba(255, 61, 0, 0.3),
inset 0 0 5px rgba(255, 61, 0, 0.2); inset 0 0 5px rgba(255, 61, 0, 0.2);
} }
100% { 100% {
box-shadow: box-shadow:
0 0 15px rgba(255, 61, 0, 0.6), 0 0 15px rgba(255, 61, 0, 0.6),
@@ -1184,8 +1186,18 @@ onUnmounted(() => {
/* 毫秒闪烁动画增强紧张感 */ /* 毫秒闪烁动画增强紧张感 */
@keyframes blink { @keyframes blink {
0%, 50% { opacity: 1; transform: scale(1); }
51%, 100% { opacity: 0.7; transform: scale(0.95); } 0%,
50% {
opacity: 1;
transform: scale(1);
}
51%,
100% {
opacity: 0.7;
transform: scale(0.95);
}
} }
/* 战鼓部分 - 浮动并支持拖放 */ /* 战鼓部分 - 浮动并支持拖放 */
@@ -1242,15 +1254,28 @@ onUnmounted(() => {
/* 战鼓闲置时的轻微脉动动画 */ /* 战鼓闲置时的轻微脉动动画 */
@keyframes idlePulse { @keyframes idlePulse {
0% { transform: scale(1); } 0% {
100% { transform: scale(1.05); } transform: scale(1);
}
100% {
transform: scale(1.05);
}
} }
/* 增强跳动效果的关键帧动画 */ /* 增强跳动效果的关键帧动画 */
@keyframes drumBeat { @keyframes drumBeat {
0% { transform: scale(1); } 0% {
50% { transform: scale(var(--drum-scale, 1.3)) translateY(var(--drum-translate-y, -15px)) rotate(var(--drum-rotate, 5deg)); } transform: scale(1);
100% { 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 { .trophy {
@@ -1258,8 +1283,13 @@ onUnmounted(() => {
} }
@keyframes bounce { @keyframes bounce {
from { transform: translateY(0); } from {
to { transform: translateY(-10px); } transform: translateY(0);
}
to {
transform: translateY(-10px);
}
} }
/* 按钮样式 */ /* 按钮样式 */
@@ -1355,6 +1385,7 @@ onUnmounted(() => {
0% { 0% {
text-shadow: 3px 3px 0 rgba(0, 0, 0, 0.2); text-shadow: 3px 3px 0 rgba(0, 0, 0, 0.2);
} }
100% { 100% {
text-shadow: 3px 3px 0 rgba(0, 0, 0, 0.2), 0 0 20px rgba(255, 255, 255, 0.8); text-shadow: 3px 3px 0 rgba(0, 0, 0, 0.2), 0 0 20px rgba(255, 255, 255, 0.8);
} }
@@ -1425,20 +1456,23 @@ onUnmounted(() => {
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.15); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.15);
} }
/* 前三名特殊背景色,增强亮度和对比度 */ /* 前三名特殊背景色,增强亮度和对比度 - 确保英雄排名和战区排名样式一致 */
.table-row:nth-child(1) { .team-rankings-container .table-row:nth-child(1),
.individual-rankings-container .table-row:nth-child(1) {
background: linear-gradient(135deg, #ffd700, #ffed4a); background: linear-gradient(135deg, #ffd700, #ffed4a);
color: #333; color: #333;
box-shadow: 0 6px 20px rgba(255, 215, 0, 0.4); box-shadow: 0 6px 20px rgba(255, 215, 0, 0.4);
} }
.table-row:nth-child(2) { .team-rankings-container .table-row:nth-child(2),
.individual-rankings-container .table-row:nth-child(2) {
background: linear-gradient(135deg, #c0c0c0, #e0e0e0); background: linear-gradient(135deg, #c0c0c0, #e0e0e0);
color: #333; color: #333;
box-shadow: 0 6px 20px rgba(192, 192, 192, 0.4); box-shadow: 0 6px 20px rgba(192, 192, 192, 0.4);
} }
.table-row:nth-child(3) { .team-rankings-container .table-row:nth-child(3),
.individual-rankings-container .table-row:nth-child(3) {
background: linear-gradient(135deg, #cd7f32, #d7ccc8); background: linear-gradient(135deg, #cd7f32, #d7ccc8);
color: #333; color: #333;
box-shadow: 0 6px 20px rgba(205, 127, 50, 0.4); box-shadow: 0 6px 20px rgba(205, 127, 50, 0.4);
@@ -1474,12 +1508,12 @@ onUnmounted(() => {
} }
/* 浮动管理员入口 */ /* 浮动管理员入口 */
.admin-entry-float { .admin-entry-float {
position: fixed; position: fixed;
bottom: 20px; bottom: 20px;
left: 20px; left: 20px;
z-index: 1000; z-index: 1000;
} }
/* 管理员按钮使用通用按钮样式 */ /* 管理员按钮使用通用按钮样式 */
.admin-entry-float .btn-game-secondary { .admin-entry-float .btn-game-secondary {
@@ -1491,6 +1525,7 @@ onUnmounted(() => {
/* 针对高度大于1080分辨率的精确优化 */ /* 针对高度大于1080分辨率的精确优化 */
@media (min-height: 1080px) { @media (min-height: 1080px) {
/* 优化表格布局 - 设置高度确保只显示5行 */ /* 优化表格布局 - 设置高度确保只显示5行 */
.rank-table { .rank-table {
min-height: 280px !important; min-height: 280px !important;
@@ -1514,7 +1549,29 @@ onUnmounted(() => {
} }
/* 移动端背景图片设置 - 全局样式 */ /* 移动端背景图片设置 - 全局样式 */
@media (max-width: 768px) { @media (max-width: 768px) {
/* 战区排名容器设置 - 禁止滚动 */
.team-rankings-container .rank-table {
overflow: hidden;
position: relative;
min-height: auto;
}
/* 确保前三名特殊样式在移动端正确显示 */
.team-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) {
box-shadow: none;
z-index: 1;
}
/* 1. 背景图片全屏显示并固定 */ /* 1. 背景图片全屏显示并固定 */
.battle-ranking { .battle-ranking {
padding: 0 !important; padding: 0 !important;
@@ -1546,7 +1603,8 @@ onUnmounted(() => {
/* 战鼓部分调整 */ /* 战鼓部分调整 */
.drums-section { .drums-section {
transform: scale(0.8); /* 缩小战鼓元素 */ transform: scale(0.8);
/* 缩小战鼓元素 */
} }
/* 2. 倒计时模块调整 - 移至冠军战区上方,缩小时间显示为一行 */ /* 2. 倒计时模块调整 - 移至冠军战区上方,缩小时间显示为一行 */
@@ -1587,7 +1645,8 @@ onUnmounted(() => {
/* 任务设置调整 */ /* 任务设置调整 */
.task-settings-section { .task-settings-section {
margin-top: -100px; /* 调整负边距 */ margin-top: -100px;
/* 调整负边距 */
} }
.task-title-container { .task-title-container {
@@ -1640,8 +1699,9 @@ onUnmounted(() => {
/* 表格调整 */ /* 表格调整 */
.rank-table { .rank-table {
min-height: 250px; min-height: 250px;
max-height: 250px; /* 移除最大高度限制,允许显示所有行 */
overflow-x: auto; /* 允许横向滚动 */ overflow-x: auto;
/* 允许横向滚动 */
} }
.table-header { .table-header {
@@ -1754,6 +1814,7 @@ onUnmounted(() => {
/* 触摸设备优化 */ /* 触摸设备优化 */
@media (hover: none) and (pointer: coarse) { @media (hover: none) and (pointer: coarse) {
/* 增大点击区域 */ /* 增大点击区域 */
.btn-game-secondary { .btn-game-secondary {
padding: 12px 24px; padding: 12px 24px;
@@ -1790,5 +1851,4 @@ onUnmounted(() => {
transition: transform 0.2s ease; transition: transform 0.2s ease;
} }
} }
</style> </style>