Files
company-celebration/packages/client-mobile/src/views/VotingPage.vue
let5sne 296f6e09f8 fix: add MySQL database and fix deployment issues
## Changes

### Database Integration
- Add MySQL 8.0 service to docker-compose.yml
- Configure DATABASE_URL environment variable for server
- Add health check for MySQL service
- Create mysql_data volume for data persistence

### Dockerfile Improvements
- Generate Prisma Client in builder stage
- Copy Prisma Client from correct pnpm workspace location
- Ensure Prisma Client is available in production container

### Client-Mobile Fixes
- Remove deprecated StampDock.vue component
- Fix voting store API usage in VotingPage.vue
- Add type assertion for userTickets in connection.ts
- Add remark property to AwardConfig interface in voting.ts

## Testing
- All containers start successfully
- Database connection established
- Redis connection working
- 94 participants restored from Redis
- Vote data synced (20 votes)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-02 21:46:51 +08:00

101 lines
2.6 KiB
Vue

<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { useVotingStore } from '../stores/voting';
import { useConnectionStore } from '../stores/connection';
import VotingDock from '../components/VotingDock.vue';
import ProgramCard from '../components/ProgramCard.vue';
import ConnectionStatus from '../components/ConnectionStatus.vue';
const votingStore = useVotingStore();
const connectionStore = useConnectionStore();
// Mock programs data (replace with API call)
const programs = ref([
{ id: 'p1', name: '龙腾四海', team: '市场部' },
{ id: 'p2', name: '金马奔腾', team: '技术部' },
{ id: 'p3', name: '春风得意', team: '人力资源部' },
{ id: 'p4', name: '鸿运当头', team: '财务部' },
{ id: 'p5', name: '马到成功', team: '运营部' },
{ id: 'p6', name: '一马当先', team: '产品部' },
{ id: 'p7', name: '万马奔腾', team: '设计部' },
{ id: 'p8', name: '龙马精神', team: '销售部' },
]);
const isLoading = ref(false);
onMounted(async () => {
// Connect if not connected
if (!connectionStore.isConnected) {
connectionStore.connect();
}
});
</script>
<template>
<div class="voting-page safe-area-top">
<!-- Header -->
<header class="page-header">
<h1 class="page-title">节目投票</h1>
<p class="page-subtitle">
已使用 {{ votingStore.usedTicketCount }}/{{ votingStore.totalTicketCount }} 枚印章
</p>
<ConnectionStatus />
</header>
<!-- Program List (Postcards fade in with stagger) -->
<main class="program-list">
<ProgramCard
v-for="(program, index) in programs"
:key="program.id"
:program-id="program.id"
:program-name="program.name"
:team-name="program.team"
:index="index"
/>
</main>
<!-- Stamp Dock -->
<VotingDock />
</div>
</template>
<style lang="scss" scoped>
@use '../assets/styles/variables.scss' as *;
.voting-page {
min-height: 100vh;
background: $color-bg-primary;
padding-bottom: 120px; // Space for dock
}
.page-header {
background: $color-surface-glass;
backdrop-filter: $backdrop-blur;
-webkit-backdrop-filter: $backdrop-blur;
padding: $spacing-lg;
padding-top: calc(env(safe-area-inset-top) + #{$spacing-lg});
color: $color-text-inverse;
position: sticky;
top: 0;
z-index: $z-index-sticky;
}
.page-title {
font-size: $font-size-2xl;
font-weight: bold;
margin-bottom: $spacing-xs;
}
.page-subtitle {
font-size: $font-size-sm;
opacity: 0.9;
}
.program-list {
padding: $spacing-md;
display: flex;
flex-direction: column;
gap: $spacing-sm;
}
</style>