Files
company-celebration/packages/client-mobile/src/views/HomeView.vue
let5sne 371f117064 fix: production deployment improvements and configuration fixes
## 主要改动

### 1. 修复二维码地址硬编码问题
- 将 MainDisplay.vue 中硬编码的移动端 URL 改为环境变量配置
- 添加 VITE_MOBILE_URL 环境变量支持
- 支持通过 .env 文件动态配置移动端地址

### 2. 修复音频文件路径问题
- 修正 display.ts 中音频文件路径,添加 /screen 前缀
- 修复 BGM、抽奖音效、胜利音效的加载路径

### 3. 修复 Docker 构建问题
- 添加中国 npm 镜像配置,解决构建超时问题
- 修复缺失的 tsconfig.base.json 文件拷贝
- 修复 Redis 环境变量配置(REDIS_HOST/REDIS_PORT)
- 添加 Lua 脚本文件拷贝到生产容器

### 4. 修复前端路由和资源加载
- 添加 Vite base path 配置 (/screen/)
- 修复 Vue Router base path 配置
- 修正 Caddyfile 路由顺序,确保 /screen 路径优先匹配

### 5. 修复 TypeScript 编译错误
- LuckyDrawView.vue: 添加 round 属性类型定义
- ProgramCard.vue: 添加非空断言处理

### 6. 修复 SCSS 变量问题
- 替换未定义的 SCSS 变量为硬编码颜色值
- 修复 VoteView、ConnectionStatus、HomeView、ScanLoginView 中的样式问题

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-26 16:59:02 +08:00

296 lines
6.4 KiB
Vue

<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { useRouter } from 'vue-router';
import { useConnectionStore } from '../stores/connection';
import { showToast } from 'vant';
const router = useRouter();
const connectionStore = useConnectionStore();
const userName = ref('');
const userDept = ref('技术部');
const isLoading = ref(false);
// Check if already logged in
onMounted(() => {
if (connectionStore.userId && connectionStore.userName && connectionStore.userName !== '访客') {
// Already logged in, redirect to vote page
router.replace('/vote');
}
});
async function handleEnter() {
if (!userName.value.trim()) {
showToast('请输入您的姓名');
return;
}
isLoading.value = true;
// Generate a simple user ID (in production, this would come from auth)
const odrawId = `user_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
connectionStore.setUser(odrawId, userName.value.trim(), userDept.value);
// Wait for connection
await new Promise((resolve) => setTimeout(resolve, 500));
isLoading.value = false;
router.push('/vote');
}
</script>
<template>
<div class="home-view safe-area-top safe-area-bottom">
<!-- Header decoration -->
<div class="header-decoration">
<div class="lantern left"></div>
<div class="lantern right"></div>
</div>
<!-- Main content -->
<div class="content">
<div class="logo-section">
<div class="year-badge">
<span class="year">2026</span>
<span class="zodiac">马年</span>
</div>
<h1 class="title gold-text">年会互动系统</h1>
<p class="subtitle">投票 · 抽奖 · 互动</p>
</div>
<div class="form-section">
<div class="input-wrapper guochao-border">
<van-field
v-model="userName"
placeholder="请输入您的姓名"
:border="false"
clearable
maxlength="20"
@keyup.enter="handleEnter"
/>
</div>
<div class="input-wrapper guochao-border">
<van-field
v-model="userDept"
placeholder="请输入您的部门"
:border="false"
clearable
maxlength="20"
@keyup.enter="handleEnter"
/>
</div>
<van-button
class="enter-btn"
type="primary"
block
round
:loading="isLoading"
loading-text="进入中..."
@click="handleEnter"
>
进入年会
</van-button>
</div>
<!-- Features -->
<div class="features">
<div class="feature-item">
<van-icon name="like-o" size="24" color="#c41230" />
<span>投票评选</span>
</div>
<div class="feature-item">
<van-icon name="gift-o" size="24" color="#d4a84b" />
<span>幸运抽奖</span>
</div>
<div class="feature-item">
<van-icon name="chart-trending-o" size="24" color="#c41230" />
<span>实时结果</span>
</div>
</div>
</div>
<!-- Footer -->
<div class="footer">
<p>© 2026 公司年会</p>
</div>
</div>
</template>
<style lang="scss" scoped>
@use '../assets/styles/variables.scss' as *;
.home-view {
min-height: 100vh;
display: flex;
flex-direction: column;
background: linear-gradient(180deg, #fff5f5 0%, #fef8f0 50%, #ffffff 100%);
position: relative;
overflow: hidden;
}
.header-decoration {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 120px;
pointer-events: none;
.lantern {
position: absolute;
top: -20px;
width: 40px;
height: 60px;
background: linear-gradient(180deg, $color-primary 0%, $color-primary-dark 100%);
border-radius: 50% 50% 45% 45%;
box-shadow: 0 4px 12px rgba($color-primary, 0.3);
&::before {
content: '';
position: absolute;
top: -8px;
left: 50%;
transform: translateX(-50%);
width: 16px;
height: 12px;
background: $color-gold;
border-radius: 2px 2px 0 0;
}
&::after {
content: '';
position: absolute;
bottom: -15px;
left: 50%;
transform: translateX(-50%);
width: 2px;
height: 15px;
background: $color-gold;
}
&.left {
left: 20px;
animation: swing 3s ease-in-out infinite;
}
&.right {
right: 20px;
animation: swing 3s ease-in-out infinite 0.5s;
}
}
}
@keyframes swing {
0%,
100% {
transform: rotate(-5deg);
}
50% {
transform: rotate(5deg);
}
}
.content {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
padding: $spacing-xl $spacing-lg;
padding-top: 100px;
}
.logo-section {
text-align: center;
margin-bottom: $spacing-xl;
.year-badge {
display: inline-flex;
flex-direction: column;
align-items: center;
background: linear-gradient(135deg, $color-primary 0%, $color-primary-dark 100%);
color: $color-text-inverse;
padding: $spacing-sm $spacing-lg;
border-radius: $radius-lg;
margin-bottom: $spacing-md;
box-shadow: $shadow-md;
.year {
font-size: $font-size-2xl;
font-weight: bold;
}
.zodiac {
font-size: $font-size-sm;
opacity: 0.9;
}
}
.title {
font-size: 32px;
font-weight: bold;
margin-bottom: $spacing-sm;
}
.subtitle {
color: $color-text-secondary;
font-size: $font-size-md;
}
}
.form-section {
margin-bottom: $spacing-xl;
.input-wrapper {
background: $color-bg-card;
margin-bottom: $spacing-md;
padding: $spacing-xs;
:deep(.van-field) {
background: transparent;
.van-field__control {
text-align: center;
font-size: $font-size-lg;
}
}
}
.enter-btn {
background: linear-gradient(135deg, $color-primary-light 0%, $color-primary 100%);
border: none;
height: 48px;
font-size: $font-size-lg;
font-weight: 500;
box-shadow: $shadow-md;
}
}
.features {
display: flex;
justify-content: space-around;
padding: $spacing-md 0;
.feature-item {
display: flex;
flex-direction: column;
align-items: center;
gap: $spacing-xs;
span {
font-size: $font-size-sm;
color: $color-text-secondary;
}
}
}
.footer {
text-align: center;
padding: $spacing-md;
color: #999;
font-size: $font-size-xs;
}
</style>