Files
company-celebration/packages/client-screen/src/views/VoteResultsView.vue
empty 30cd29d45d feat: add Admin Control Panel, voting status check, and router security
Admin Control Panel:
- Add full AdminControl.vue with 3 sections (Voting, Lottery, Global)
- Add AdminLogin.vue with access code gate (20268888)
- Add admin.ts store with state persistence
- Add admin.types.ts with state machine types
- Add router guards for /admin/director-console

Voting System Fixes:
- Add voting status check before accepting votes (VOTING_CLOSED error)
- Fix client to display server error messages
- Fix button disabled logic to prevent ambiguity in paused state
- Auto-generate userId on connect to fix UNAUTHORIZED error

Big Screen Enhancements:
- Add LiveVotingView.vue with particle system
- Add LotteryMachine.ts with 3-stage animation (Galaxy/Storm/Reveal)
- Add useSocketClient.ts composable
- Fix MainDisplay.vue SCSS syntax error
- Add admin state sync listener in display store

Server Updates:
- Add admin.service.ts for state management
- Add isVotingOpen() and getVotingStatus() methods
- Add admin socket event handlers

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 15:34:37 +08:00

202 lines
4.1 KiB
Vue

<script setup lang="ts">
import { useRouter } from 'vue-router';
const router = useRouter();
function goBack() {
router.push('/');
}
// Mock vote results
const categories = [
{
name: '最佳员工',
results: [
{ name: '张三', votes: 45, percentage: 30 },
{ name: '李四', votes: 38, percentage: 25 },
{ name: '王五', votes: 32, percentage: 21 },
],
},
{
name: '最佳团队',
results: [
{ name: '技术一组', votes: 52, percentage: 35 },
{ name: '产品组', votes: 41, percentage: 27 },
{ name: '设计组', votes: 35, percentage: 23 },
],
},
];
</script>
<template>
<div class="vote-results-view">
<!-- Header -->
<header class="header">
<button class="back-btn" @click="goBack"> 返回</button>
<h1 class="title gold-text">投票结果</h1>
<div class="placeholder"></div>
</header>
<!-- Results grid -->
<main class="results-grid">
<div v-for="category in categories" :key="category.name" class="category-card">
<h2 class="category-name">{{ category.name }}</h2>
<div class="results-list">
<div
v-for="(result, index) in category.results"
:key="result.name"
class="result-item"
:class="{ winner: index === 0 }"
>
<span class="rank">{{ index + 1 }}</span>
<span class="name">{{ result.name }}</span>
<div class="bar-container">
<div class="bar" :style="{ width: result.percentage + '%' }"></div>
</div>
<span class="votes">{{ result.votes }}</span>
</div>
</div>
</div>
</main>
</div>
</template>
<style lang="scss" scoped>
@use '../assets/styles/variables.scss' as *;
.vote-results-view {
width: 100%;
height: 100%;
background: $color-bg-gradient;
display: flex;
flex-direction: column;
overflow: auto;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30px 50px;
.back-btn {
background: none;
border: 1px solid $color-gold;
color: $color-gold;
padding: 10px 20px;
border-radius: 8px;
cursor: pointer;
font-size: 16px;
transition: all $transition-fast;
&:hover {
background: rgba($color-gold, 0.1);
}
}
.title {
font-size: 48px;
font-weight: bold;
}
.placeholder {
width: 100px;
}
}
.results-grid {
flex: 1;
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 40px;
padding: 40px 50px;
}
.category-card {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba($color-gold, 0.3);
border-radius: 16px;
padding: 30px;
.category-name {
font-size: 28px;
color: $color-gold;
margin-bottom: 24px;
text-align: center;
}
.results-list {
display: flex;
flex-direction: column;
gap: 16px;
}
.result-item {
display: flex;
align-items: center;
gap: 16px;
padding: 16px;
background: rgba(255, 255, 255, 0.03);
border-radius: 8px;
&.winner {
background: rgba($color-gold, 0.1);
border: 1px solid $color-gold;
.rank {
background: $color-gold;
color: #000;
}
.name {
color: $color-gold;
}
.bar {
background: linear-gradient(90deg, $color-gold-dark, $color-gold);
}
}
.rank {
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.1);
border-radius: 50%;
font-size: 16px;
font-weight: bold;
}
.name {
width: 120px;
font-size: 20px;
color: $color-text-light;
}
.bar-container {
flex: 1;
height: 24px;
background: rgba(255, 255, 255, 0.1);
border-radius: 12px;
overflow: hidden;
.bar {
height: 100%;
background: linear-gradient(90deg, $color-primary-dark, $color-primary);
border-radius: 12px;
transition: width 1s ease;
}
}
.votes {
width: 60px;
text-align: right;
font-size: 18px;
color: $color-text-muted;
}
}
}
</style>