chore: 纯网页版本

This commit is contained in:
2025-11-11 17:29:51 +08:00
parent 867beb5de7
commit 22016ac339
4 changed files with 1356 additions and 98 deletions

View File

@@ -25,12 +25,14 @@
/>
</div>
<button @click="login" class="login-btn">登录</button>
<button @click="goToHome" class="home-btn">返回首页</button>
<div v-if="loginError" class="error-message">{{ loginError }}</div>
</div>
</div>
<!-- 管理界面 --><div v-else class="management-container">
<!-- 顶部导航 --><div class="top-nav">
<!-- 顶部导航 -->
<div class="top-nav">
<h1 class="nav-title">📊 百人大战管理系统</h1>
<div class="nav-actions">
<span class="welcome-text">欢迎您{{ currentUser.username }}</span>
@@ -38,7 +40,8 @@
</div>
</div>
<!-- 功能选项卡 --><div class="tabs">
<!-- 功能选项卡 -->
<div class="tabs">
<button
v-for="tab in tabs"
:key="tab.key"
@@ -48,6 +51,204 @@
</div>
<!-- 内容区域 --><div class="tab-content">
<!-- 结束时间设置 --><div v-if="currentTab === 'endTime'" class="end-time-content">
<h2 class="config-title"> 百人大战结束时间设置</h2>
<div class="time-setting-section">
<div class="config-item">
<label class="checkbox-label">
<span>结束日期</span>
<input
type="date"
v-model="localBattleEndTime.date"
class="text-input"
>
</label>
</div>
<div class="config-item">
<label class="checkbox-label">
<span>结束时间</span>
<input
type="time"
step="1"
v-model="localBattleEndTime.time"
class="text-input"
>
</label>
</div>
<div class="config-item">
<span class="info-text">提示设置的结束时间将用于动态计算并显示"距离结束还有多少时间"</span>
</div>
</div>
</div>
<!-- 显示配置管理 --><div v-if="currentTab === 'config'" class="config-content">
<h2 class="config-title"> 显示配置管理</h2>
<!-- 个人排名显示配置 -->
<div class="config-section">
<h3>👤 个人排名显示选项</h3>
<div class="config-options">
<div class="config-item">
<label class="checkbox-label">
<input
type="checkbox"
v-model="localDisplayConfig.individual.showLevel"
>
<span>显示等级列</span>
</label>
</div>
<div class="config-item">
<label class="checkbox-label">
<input
type="checkbox"
v-model="localDisplayConfig.individual.showDepartment"
>
<span>显示部门列</span>
</label>
</div>
<div class="config-item">
<label class="checkbox-label">
<span>签单金额列显示名称</span>
<input type="text" v-model="localDisplayConfig.individual.scoreColumn.displayName"
placeholder="输入显示名称" class="text-input">
</label>
</div>
<div class="config-item">
<label class="checkbox-label">
<span>签单金额显示样式</span>
<select v-model="localDisplayConfig.individual.scoreColumn.displayStyle" class="select-input">
<option value="amount">金额¥符号</option>
<option value="number">普通数字</option>
</select>
</label>
</div>
<div class="column-widths-section">
<h4>列宽设置单位像素</h4>
<div class="column-width-item">
<label>排名列</label>
<input type="number" v-model.number="localDisplayConfig.individual.columnWidths.rank" min="40" max="200" class="width-input">
</div>
<div class="column-width-item">
<label>头像列</label>
<input type="number" v-model.number="localDisplayConfig.individual.columnWidths.avatar" min="40" max="200" class="width-input">
</div>
<div class="column-width-item">
<label>姓名列</label>
<select v-model.number="localDisplayConfig.individual.columnWidths.name" class="width-select">
<option :value="1">自动填充</option>
<option value="100">100px</option>
<option value="150">150px</option>
<option value="200">200px</option>
<option value="250">250px</option>
</select>
</div>
<div class="column-width-item">
<label>签单金额列</label>
<input type="number" v-model.number="localDisplayConfig.individual.columnWidths.score" min="60" max="200" class="width-input">
</div>
<div class="column-width-item" v-if="localDisplayConfig.individual.showLevel">
<label>等级列</label>
<input type="number" v-model.number="localDisplayConfig.individual.columnWidths.level" min="60" max="200" class="width-input">
</div>
<div class="column-width-item" v-if="localDisplayConfig.individual.showDepartment">
<label>部门列</label>
<select v-model.number="localDisplayConfig.individual.columnWidths.department" class="width-select">
<option :value="1">自动填充</option>
<option value="100">100px</option>
<option value="150">150px</option>
<option value="200">200px</option>
</select>
</div>
<div class="column-width-item">
<label>奖金列</label>
<input type="number" v-model.number="localDisplayConfig.individual.columnWidths.bonus" min="60" max="200" class="width-input">
</div>
</div>
</div>
</div>
<!-- 战队排名显示配置 -->
<div class="config-section">
<h3>👥 战队排名显示选项</h3>
<div class="config-options">
<div class="config-item">
<label class="checkbox-label">
<input
type="checkbox"
v-model="localDisplayConfig.team.showMemberCount"
>
<span>显示人数列</span>
</label>
</div>
<div class="config-item">
<label class="checkbox-label">
<input
type="checkbox"
v-model="localDisplayConfig.team.showLeader"
>
<span>显示队长列</span>
</label>
</div>
<div class="config-item">
<label class="checkbox-label">
<span>签单金额列显示名称</span>
<input type="text" v-model="localDisplayConfig.team.totalScoreColumn.displayName"
placeholder="输入显示名称" class="text-input">
</label>
</div>
<div class="config-item">
<label class="checkbox-label">
<span>签单金额显示样式</span>
<select v-model="localDisplayConfig.team.totalScoreColumn.displayStyle" class="select-input">
<option value="amount">金额¥符号</option>
<option value="number">普通数字</option>
</select>
</label>
</div>
<div class="column-widths-section">
<h4>列宽设置单位像素</h4>
<div class="column-width-item">
<label>排名列</label>
<input type="number" v-model.number="localDisplayConfig.team.columnWidths.rank" min="40" max="200" class="width-input">
</div>
<div class="column-width-item">
<label>战队名列</label>
<select v-model.number="localDisplayConfig.team.columnWidths.name" class="width-select">
<option :value="1">自动填充</option>
<option value="150">150px</option>
<option value="200">200px</option>
<option value="250">250px</option>
<option value="300">300px</option>
</select>
</div>
<div class="column-width-item">
<label>签单金额列</label>
<input type="number" v-model.number="localDisplayConfig.team.columnWidths.score" min="60" max="200" class="width-input">
</div>
<div class="column-width-item" v-if="localDisplayConfig.team.showMemberCount">
<label>人数列</label>
<input type="number" v-model.number="localDisplayConfig.team.columnWidths.memberCount" min="60" max="100" class="width-input">
</div>
<div class="column-width-item" v-if="localDisplayConfig.team.showLeader">
<label>队长列</label>
<select v-model.number="localDisplayConfig.team.columnWidths.leader" class="width-select">
<option :value="1">自动填充</option>
<option value="100">100px</option>
<option value="150">150px</option>
<option value="200">200px</option>
</select>
</div>
<div class="column-width-item">
<label>奖金列</label>
<input type="number" v-model.number="localDisplayConfig.team.columnWidths.bonus" min="60" max="200" class="width-input">
</div>
</div>
</div>
</div>
</div>
<!-- 个人排名管理 --><div v-if="currentTab === 'individual'" class="rank-content">
<div class="management-header">
<h2>👤 个人排名管理</h2>
@@ -57,9 +258,9 @@
<div class="table-header">
<span class="rank-col">排名</span>
<span class="name-col">姓名</span>
<span class="score-col">得分</span>
<span class="level-col">等级</span>
<span class="dept-col">部门</span>
<span class="score-col">{{ displayConfig.individual.scoreColumn.displayName }}</span>
<span v-if="displayConfig.individual.showLevel" class="level-col">等级</span>
<span v-if="displayConfig.individual.showDepartment" class="dept-col">部门</span>
<span class="bonus-col">奖金</span>
<span class="action-col">操作</span>
</div>
@@ -71,9 +272,9 @@
>
<span class="rank-col">{{ index + 1 }}</span>
<span class="name-col">{{ item.name }}</span>
<span class="score-col">{{ item.score }}</span>
<span class="level-col" :class="`level-${item.level}`">{{ item.level }}</span>
<span class="dept-col">{{ item.department }}</span>
<span class="score-col">{{ displayConfig.individual.scoreColumn.displayStyle === 'amount' ? '¥' + item.score : item.score }}</span>
<span v-if="displayConfig.individual.showLevel" class="level-col" :class="`level-${item.level}`">{{ item.level }}</span>
<span v-if="displayConfig.individual.showDepartment" class="dept-col">{{ item.department }}</span>
<span class="bonus-col">¥{{ item.bonus }}</span>
<span class="action-col">
<button @click="editIndividual(item)" class="edit-btn"> 编辑</button>
@@ -92,9 +293,9 @@
<div class="table-header">
<span class="rank-col">排名</span>
<span class="name-col">战队名称</span>
<span class="score-col">总分</span>
<span class="member-col">人数</span>
<span class="leader-col">队长</span>
<span class="score-col">{{ displayConfig.team.totalScoreColumn.displayName }}</span>
<span v-if="displayConfig.team.showMemberCount" class="member-col">人数</span>
<span v-if="displayConfig.team.showLeader" class="leader-col">队长</span>
<span class="bonus-col">奖金</span>
<span class="action-col">操作</span>
</div>
@@ -106,9 +307,9 @@
>
<span class="rank-col">{{ index + 1 }}</span>
<span class="name-col">{{ item.name }}</span>
<span class="score-col">{{ item.totalScore }}</span>
<span class="member-col">{{ item.memberCount }}</span>
<span class="leader-col">{{ item.leader }}</span>
<span class="score-col">{{ displayConfig.team.totalScoreColumn.displayStyle === 'amount' ? '¥' + item.totalScore : item.totalScore }}</span>
<span v-if="displayConfig.team.showMemberCount" class="member-col">{{ item.memberCount }}</span>
<span v-if="displayConfig.team.showLeader" class="leader-col">{{ item.leader }}</span>
<span class="bonus-col">¥{{ item.bonus }}</span>
<span class="action-col">
<button @click="editTeam(item)" class="edit-btn"> 编辑</button>
@@ -117,6 +318,259 @@
</div>
</div>
</div>
<!-- 战鼓配置 -->
<div v-if="currentTab === 'drum'" class="drum-config-content">
<h2 class="config-title">🥁 战鼓配置管理</h2>
<!-- 音效配置 -->
<div class="config-section">
<h3>🔊 音效配置</h3>
<div class="config-options">
<div class="config-item">
<label class="checkbox-label">
<input
type="checkbox"
v-model="localDrumConfig.sound.enabled"
>
<span>启用音效</span>
</label>
</div>
<div class="config-item">
<label class="checkbox-label">
<span>音量 (0-1)</span>
<input
type="number"
v-model.number="localDrumConfig.sound.volume"
min="0"
max="1"
step="0.1"
class="width-input"
>
</label>
</div>
<div class="config-item">
<label class="checkbox-label">
<span>起音时间 (s)</span>
<input
type="number"
v-model.number="localDrumConfig.sound.attackTime"
min="0.001"
max="0.5"
step="0.01"
class="width-input"
>
</label>
</div>
<div class="config-item">
<label class="checkbox-label">
<span>衰减时间 (s)</span>
<input
type="number"
v-model.number="localDrumConfig.sound.decayTime"
min="0.05"
max="1"
step="0.05"
class="width-input"
>
</label>
</div>
<h4 style="margin-top: 15px; color: #666;">🎵 第一个音调</h4>
<div class="config-item">
<label class="checkbox-label">
<span>音调类型</span>
<select v-model="localDrumConfig.sound.type1" class="select-input">
<option value="sine">正弦波</option>
<option value="square">方波</option>
<option value="triangle">三角波</option>
<option value="sawtooth">锯齿波</option>
</select>
</label>
</div>
<div class="config-item">
<label class="checkbox-label">
<span>频率 (Hz)</span>
<input
type="number"
v-model.number="localDrumConfig.sound.frequency1"
min="50"
max="500"
class="width-input"
>
</label>
</div>
<h4 style="margin-top: 15px; color: #666;">🎵 第二个音调</h4>
<div class="config-item">
<label class="checkbox-label">
<span>音调类型</span>
<select v-model="localDrumConfig.sound.type2" class="select-input">
<option value="sine">正弦波</option>
<option value="square">方波</option>
<option value="triangle">三角波</option>
<option value="sawtooth">锯齿波</option>
</select>
</label>
</div>
<div class="config-item">
<label class="checkbox-label">
<span>频率 (Hz)</span>
<input
type="number"
v-model.number="localDrumConfig.sound.frequency2"
min="50"
max="500"
class="width-input"
>
</label>
</div>
</div>
</div>
<!-- 动画配置 -->
<div class="config-section">
<h3>🎬 动画配置</h3>
<div class="config-options">
<div class="config-item">
<label class="checkbox-label">
<input
type="checkbox"
v-model="localDrumConfig.animation.enabled"
>
<span>启用动画</span>
</label>
</div>
<div class="config-item">
<label class="checkbox-label">
<span>节拍间隔 (ms)</span>
<input
type="number"
v-model.number="localDrumConfig.animation.beatInterval"
min="50"
max="1000"
class="width-input"
>
</label>
</div>
<div class="config-item">
<label class="checkbox-label">
<span>跳动缩放比例</span>
<input
type="number"
v-model.number="localDrumConfig.animation.beatScale"
min="1.0"
max="2.0"
step="0.1"
class="width-input"
>
</label>
</div>
<div class="config-item">
<label class="checkbox-label">
<span>跳动上下位移 (px)</span>
<input
type="number"
v-model.number="localDrumConfig.animation.beatTranslateY"
min="-50"
max="50"
class="width-input"
>
</label>
</div>
<div class="config-item">
<label class="checkbox-label">
<span>跳动旋转角度 (deg)</span>
<input
type="number"
v-model.number="localDrumConfig.animation.beatRotate"
min="0"
max="20"
class="width-input"
>
</label>
</div>
<div class="config-item">
<label class="checkbox-label">
<span>单次跳动持续时间 (ms)</span>
<input
type="number"
v-model.number="localDrumConfig.animation.beatDuration"
min="50"
max="500"
class="width-input"
>
</label>
</div>
</div>
</div>
<!-- 节拍模式配置 -->
<div class="config-section">
<h3>🎵 节拍模式配置</h3>
<div class="config-options">
<div class="config-item">
<label class="checkbox-label">
<span>每小节总拍数</span>
<input
type="number"
v-model.number="localDrumConfig.pattern.totalBeats"
min="1"
max="8"
class="width-input"
>
</label>
</div>
<div class="config-item">
<label class="checkbox-label">
<span>强拍位置 (1-4)</span>
<input
type="text"
v-model="localDrumConfig.pattern.strongBeatsStr"
placeholder="如: 1,4"
class="text-input"
@input="updateStrongBeats"
>
</label>
</div>
<div class="config-item">
<label class="checkbox-label">
<span>强拍音量倍数</span>
<input
type="number"
v-model.number="localDrumConfig.pattern.accentMultiplier"
min="1"
max="3"
step="0.1"
class="width-input"
>
</label>
</div>
<div class="config-item">
<label class="checkbox-label">
<span>强拍频率偏移 (%)</span>
<input
type="number"
v-model.number="localDrumConfig.pattern.accentFrequencyOffset"
min="-50"
max="50"
class="width-input"
>
</label>
</div>
<div class="config-item">
<label class="checkbox-label">
<span>强拍动画增强 (%)</span>
<input
type="number"
v-model.number="localDrumConfig.pattern.accentAnimation"
min="0"
max="100"
class="width-input"
>
</label>
</div>
</div>
</div>
</div>
</div>
<!-- 保存按钮 --><div class="save-section">
@@ -134,7 +588,7 @@
<input v-model="individualForm.name" type="text" required class="form-input">
</div>
<div class="form-group">
<label>得分:</label>
<label>签单金额:</label>
<input v-model.number="individualForm.score" type="number" required class="form-input">
</div>
<div class="form-group">
@@ -172,7 +626,7 @@
<input v-model="teamForm.name" type="text" required class="form-input">
</div>
<div class="form-group">
<label>总分:</label>
<label>签单金额:</label>
<input v-model.number="teamForm.totalScore" type="number" required class="form-input">
</div>
<div class="form-group">
@@ -205,11 +659,22 @@ import {
teamRankings,
systemUsers,
saveIndividualRankings,
saveTeamRankings
saveTeamRankings,
displayConfig,
saveDisplayConfig,
battleEndTime,
saveBattleEndTime,
drumConfig,
saveDrumConfig
} from '../data/mockData.js';
const router = useRouter();
// 返回首页
const goToHome = () => {
router.push('/');
};
// 登录相关
const isLoggedIn = ref(false);
const loginForm = reactive({
@@ -222,13 +687,41 @@ const currentUser = ref({});
// 标签页相关
const tabs = [
{ key: 'individual', label: '个人排名' },
{ key: 'team', label: '战队排名' }
{ key: 'team', label: '战队排名' },
{ key: 'config', label: '显示配置' },
{ key: 'endTime', label: '结束时间设置' },
{ key: 'drum', label: '战鼓配置' }
];
const currentTab = ref('individual');
// 本地数据副本
const localIndividualRankings = ref([...individualRankings]);
const localTeamRankings = ref([...teamRankings]);
const localDisplayConfig = ref({...displayConfig});
const localBattleEndTime = ref({...battleEndTime});
// 初始化本地战鼓配置副本
const localDrumConfig = ref({...drumConfig});
// 添加强拍位置的字符串表示,用于输入框
localDrumConfig.value.pattern.strongBeatsStr = localDrumConfig.value.pattern.strongBeats?.join(',') || '1,4';
// 更新强拍位置数组
const updateStrongBeats = () => {
try {
const beatsStr = localDrumConfig.value.pattern.strongBeatsStr;
if (!beatsStr) {
localDrumConfig.value.pattern.strongBeats = [];
return;
}
const beats = beatsStr.split(',')
.map(beat => parseInt(beat.trim()))
.filter(beat => !isNaN(beat) && beat > 0 && beat <= 8);
localDrumConfig.value.pattern.strongBeats = beats;
} catch (error) {
console.error('更新强拍位置失败:', error);
localDrumConfig.value.pattern.strongBeats = [1, 4];
localDrumConfig.value.pattern.strongBeatsStr = '1,4';
}
};
// 对话框状态
const showAddIndividual = ref(false);
@@ -298,6 +791,14 @@ const saveData = () => {
// 调用保存方法
saveIndividualRankings(localIndividualRankings.value);
saveTeamRankings(localTeamRankings.value);
saveDisplayConfig(localDisplayConfig.value);
saveBattleEndTime(localBattleEndTime.value);
// 保存战鼓配置前,确保强拍位置数组是最新的
updateStrongBeats();
// 移除临时的字符串表示,避免保存到配置中
const configToSave = {...localDrumConfig.value};
delete configToSave.pattern.strongBeatsStr;
saveDrumConfig(configToSave);
alert('数据保存成功!');
};
@@ -461,7 +962,7 @@ const deleteTeam = (id) => {
}
.login-btn {
width: 100%;
width: calc(50% - 5px);
padding: 12px;
background: linear-gradient(45deg, #667eea, #764ba2);
color: white;
@@ -470,12 +971,29 @@ const deleteTeam = (id) => {
font-size: 1.1rem;
cursor: pointer;
transition: transform 0.3s;
margin-right: 10px;
}
.login-btn:hover {
transform: translateY(-2px);
}
.home-btn {
width: calc(50% - 5px);
padding: 12px;
background: linear-gradient(45deg, #28a745, #20c997);
color: white;
border: none;
border-radius: 8px;
font-size: 1.1rem;
cursor: pointer;
transition: transform 0.3s;
}
.home-btn:hover {
transform: translateY(-2px);
}
.error-message {
color: #e74c3c;
text-align: center;
@@ -555,6 +1073,149 @@ const deleteTeam = (id) => {
padding: 30px;
}
/* 配置页面样式 */
.config-content {
background: #f8f9fa;
border-radius: 10px;
padding: 20px;
}
.config-title {
color: #667eea;
margin-bottom: 30px;
text-align: center;
}
.end-time-content {
padding: 20px;
background: rgba(255, 255, 255, 0.9);
border-radius: 15px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
margin-bottom: 20px;
}
.time-setting-section {
background: white;
padding: 20px;
border-radius: 10px;
border: 1px solid #ddd;
}
.info-text {
color: #666;
font-size: 0.9rem;
font-style: italic;
margin-top: 15px;
display: block;
}
.config-section {
background: white;
border-radius: 10px;
padding: 20px;
margin-bottom: 25px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.config-section h3 {
color: #495057;
margin-bottom: 15px;
border-bottom: 2px solid #667eea;
padding-bottom: 10px;
}
.config-options {
display: flex;
flex-direction: column;
gap: 15px;
}
.config-item {
display: flex;
align-items: center;
margin-bottom: 15px;
padding: 10px;
background: #f8f9fa;
border-radius: 8px;
}
.column-widths-section {
margin-top: 20px;
padding: 15px;
background: #e3f2fd;
border-radius: 10px;
border: 1px solid #bbdefb;
}
.column-widths-section h4 {
margin-top: 0;
margin-bottom: 15px;
color: #1976d2;
font-size: 1.1rem;
}
.column-width-item {
display: flex;
align-items: center;
margin-bottom: 10px;
padding: 8px;
background: white;
border-radius: 5px;
}
.column-width-item label {
width: 100px;
font-weight: 500;
color: #333;
}
.width-input {
width: 80px;
padding: 5px;
border: 1px solid #ddd;
border-radius: 4px;
}
.width-select {
width: 120px;
padding: 5px;
border: 1px solid #ddd;
border-radius: 4px;
}
.checkbox-label {
display: flex;
align-items: center;
gap: 10px;
cursor: pointer;
font-size: 1.1rem;
color: #495057;
}
.checkbox-label input[type="checkbox"] {
width: 18px;
height: 18px;
cursor: pointer;
}
.text-input,
.select-input {
padding: 5px 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
margin-left: 10px;
}
.text-input {
flex: 1;
max-width: 200px;
}
.select-input {
min-width: 150px;
}
/* 管理内容 */
.management-header {
display: flex;