OL-LearnPlatform/src/views/ActivityRegistration.vue
2025-08-04 02:13:12 +08:00

586 lines
13 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="activity-registration-page">
<!-- 面包屑导航 -->
<div class="breadcrumb-container">
<div class="container">
<nav class="breadcrumb">
<router-link to="/" class="breadcrumb-item">首页</router-link>
<span class="breadcrumb-separator">></span>
<router-link to="/activities" class="breadcrumb-item">活动</router-link>
<span class="breadcrumb-separator">></span>
<span class="breadcrumb-current">全国青少年人工智能创新实践活动</span>
</nav>
</div>
</div>
<!-- 活动横幅区域 -->
<div class="hero-banner">
<div class="banner-image-container">
<img
src="/images/activity/活动报名-切图.png"
alt="活动横幅"
class="banner-image"
/>
</div>
</div>
<!-- 报名表单区域 -->
<div class="registration-form-container">
<div class="container">
<div class="form-section">
<div class="form-header">
<h3 class="form-title">📋 填写信息</h3>
</div>
<form class="registration-form" @submit.prevent="handleSubmit">
<!-- 姓名 -->
<div class="form-group">
<label class="form-label required">*姓名</label>
<input
type="text"
v-model="formData.name"
class="form-input"
placeholder="请输入姓名"
required
/>
</div>
<!-- 邮箱 -->
<div class="form-group">
<label class="form-label required">*邮箱:</label>
<input
type="email"
v-model="formData.email"
class="form-input"
placeholder="请输入邮箱"
required
/>
</div>
<!-- 手机 -->
<div class="form-group">
<label class="form-label required">*手机:</label>
<input
type="tel"
v-model="formData.phone"
class="form-input"
placeholder="请输入手机号"
required
/>
</div>
<!-- 手机号(验证码) -->
<div class="form-group">
<label class="form-label required">*手机号:</label>
<div class="input-group">
<input
type="tel"
v-model="formData.phone"
class="form-input"
placeholder="请输入手机号"
required
/>
<button
type="button"
class="verify-btn"
@click="sendVerificationCode"
:disabled="countdown > 0"
>
{{ countdown > 0 ? `${countdown}s后重试` : '获取验证码' }}
</button>
</div>
</div>
<!-- 验证码 -->
<div class="form-group">
<label class="form-label required">*验证码:</label>
<input
type="text"
v-model="formData.verificationCode"
class="form-input"
placeholder="请输入验证码"
required
/>
</div>
<!-- 班级 -->
<div class="form-group">
<label class="form-label">班级:</label>
<input
type="text"
v-model="formData.className"
class="form-input"
placeholder="请输入班级"
/>
</div>
<!-- 选择参与组别 -->
<div class="form-group">
<label class="form-label required">*选择参与组别:</label>
<select v-model="formData.group" class="form-select" required>
<option value="">请选择组别</option>
<option value="ai-art">AI艺术创作营</option>
<option value="ai-interaction">AI交互设计营</option>
<option value="ai-practice">人工智能实践营</option>
<option value="ai-algorithm">AI算法挑战营</option>
<option value="ai-certificate">AI创新数字素养证书</option>
</select>
</div>
<!-- 附件上传 -->
<div class="form-group">
<label class="form-label">附件:</label>
<div class="file-upload-area">
<input
type="file"
ref="fileInput"
@change="handleFileUpload"
multiple
class="file-input"
accept=".pdf,.doc,.docx,.jpg,.jpeg,.png"
/>
<div class="upload-placeholder" @click="triggerFileUpload">
<div class="upload-icon">📎</div>
<p class="upload-text">点击上传文件或拖拽文件到此处</p>
<p class="upload-hint">支持 PDF、Word、图片格式</p>
</div>
<div v-if="uploadedFiles.length > 0" class="uploaded-files">
<div v-for="(file, index) in uploadedFiles" :key="index" class="file-item">
<span class="file-name">{{ file.name }}</span>
<button type="button" @click="removeFile(index)" class="remove-btn">×</button>
</div>
</div>
</div>
</div>
<!-- 提交按钮 -->
<div class="form-actions">
<button type="submit" class="submit-btn" :disabled="isSubmitting">
{{ isSubmitting ? '提交中...' : '提交报名信息' }}
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
import { useRouter } from 'vue-router'
const router = useRouter()
// 表单数据
const formData = reactive({
name: '',
email: '',
phone: '',
verificationCode: '',
className: '',
group: 'ai-art' // 默认选择AI艺术创作营
})
// 文件上传
const fileInput = ref<HTMLInputElement>()
const uploadedFiles = ref<File[]>([])
// 验证码倒计时
const countdown = ref(0)
const isSubmitting = ref(false)
// 发送验证码
const sendVerificationCode = async () => {
if (!formData.phone) {
alert('请先输入手机号')
return
}
// 模拟发送验证码
countdown.value = 60
const timer = setInterval(() => {
countdown.value--
if (countdown.value <= 0) {
clearInterval(timer)
}
}, 1000)
alert('验证码已发送到您的手机')
}
// 触发文件上传
const triggerFileUpload = () => {
fileInput.value?.click()
}
// 处理文件上传
const handleFileUpload = (event: Event) => {
const target = event.target as HTMLInputElement
if (target.files) {
const newFiles = Array.from(target.files)
uploadedFiles.value.push(...newFiles)
}
}
// 移除文件
const removeFile = (index: number) => {
uploadedFiles.value.splice(index, 1)
}
// 提交表单
const handleSubmit = async () => {
isSubmitting.value = true
try {
// 模拟提交
await new Promise(resolve => setTimeout(resolve, 2000))
alert('报名成功!我们会尽快与您联系。')
router.push('/activity/1') // 返回活动详情页
} catch (error) {
alert('报名失败,请稍后重试')
} finally {
isSubmitting.value = false
}
}
</script>
<style scoped>
.activity-registration-page {
min-height: 100vh;
background-color: #f5f5f5;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
/* 面包屑导航 */
.breadcrumb-container {
background: white;
padding: 10px 0;
border-bottom: 1px solid #e0e0e0;
}
.breadcrumb {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
}
.breadcrumb-item {
color: #1890ff;
text-decoration: none;
}
.breadcrumb-separator {
color: #999;
}
.breadcrumb-current {
color: #333;
}
/* 活动横幅区域 */
.hero-banner {
width: 100%;
position: relative;
overflow: hidden;
}
.banner-image-container {
width: 100%;
height: 400px;
position: relative;
display: flex;
align-items: center;
justify-content: center;
}
.banner-image {
width: 100%;
height: 100%;
object-fit: cover;
object-position: center;
}
.activity-description {
font-size: 14px;
line-height: 1.6;
opacity: 0.8;
}
.activity-description p {
margin-bottom: 5px;
}
.banner-illustration {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
}
.illustration-placeholder {
width: 300px;
height: 200px;
position: relative;
}
.ai-characters {
width: 100%;
height: 100%;
position: relative;
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 200"><defs><linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" style="stop-color:rgba(255,255,255,0.2)"/><stop offset="100%" style="stop-color:rgba(255,255,255,0.1)"/></linearGradient></defs><circle cx="80" cy="80" r="30" fill="rgba(255,255,255,0.3)"/><circle cx="220" cy="120" r="25" fill="rgba(255,255,255,0.2)"/><rect x="120" y="60" width="60" height="80" rx="10" fill="url(%23grad1)"/></svg>') center/contain no-repeat;
}
/* 报名表单区域 */
.registration-form-container {
padding: 40px 0;
background: #f5f5f5;
}
.form-section {
max-width: 800px;
margin: 0 auto;
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
overflow: hidden;
}
.form-header {
background: #f8f9fa;
padding: 20px 30px;
border-bottom: 1px solid #e9ecef;
}
.form-title {
font-size: 18px;
font-weight: 600;
color: #333;
margin: 0;
}
.registration-form {
padding: 30px;
}
.form-group {
margin-bottom: 24px;
}
.form-label {
display: block;
font-size: 14px;
color: #333;
margin-bottom: 8px;
font-weight: 500;
}
.form-label.required {
color: #333;
}
.form-label.required::before {
content: '*';
color: #ff4d4f;
margin-right: 4px;
}
.form-input {
width: 100%;
height: 40px;
padding: 0 12px;
border: 1px solid #d9d9d9;
border-radius: 4px;
font-size: 14px;
transition: border-color 0.3s;
box-sizing: border-box;
}
.form-input:focus {
border-color: #1890ff;
outline: none;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}
.input-group {
display: flex;
gap: 10px;
}
.input-group .form-input {
flex: 1;
}
.verify-btn {
background: #1890ff;
color: white;
border: none;
padding: 0 16px;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
white-space: nowrap;
transition: background-color 0.3s;
}
.verify-btn:hover:not(:disabled) {
background: #40a9ff;
}
.verify-btn:disabled {
background: #d9d9d9;
cursor: not-allowed;
}
.form-select {
width: 100%;
height: 40px;
padding: 0 12px;
border: 1px solid #d9d9d9;
border-radius: 4px;
font-size: 14px;
background: white;
cursor: pointer;
box-sizing: border-box;
}
.form-select:focus {
border-color: #1890ff;
outline: none;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}
/* 文件上传区域 */
.file-upload-area {
border: 2px dashed #d9d9d9;
border-radius: 4px;
padding: 20px;
text-align: center;
transition: border-color 0.3s;
cursor: pointer;
}
.file-upload-area:hover {
border-color: #1890ff;
}
.file-input {
display: none;
}
.upload-placeholder {
color: #666;
}
.upload-icon {
font-size: 24px;
margin-bottom: 10px;
}
.upload-text {
font-size: 14px;
margin-bottom: 5px;
}
.upload-hint {
font-size: 12px;
color: #999;
}
.uploaded-files {
margin-top: 15px;
text-align: left;
}
.file-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 12px;
background: #f5f5f5;
border-radius: 4px;
margin-bottom: 8px;
}
.file-name {
font-size: 14px;
color: #333;
}
.remove-btn {
background: #ff4d4f;
color: white;
border: none;
width: 20px;
height: 20px;
border-radius: 50%;
cursor: pointer;
font-size: 12px;
display: flex;
align-items: center;
justify-content: center;
}
/* 提交按钮 */
.form-actions {
text-align: center;
margin-top: 30px;
}
.submit-btn {
background: #1890ff;
color: white;
border: none;
padding: 12px 40px;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
transition: background-color 0.3s;
min-width: 160px;
}
.submit-btn:hover:not(:disabled) {
background: #40a9ff;
}
.submit-btn:disabled {
background: #d9d9d9;
cursor: not-allowed;
}
/* 响应式设计 */
@media (max-width: 768px) {
.banner-content {
flex-direction: column;
text-align: center;
}
.banner-illustration {
margin-top: 20px;
}
.main-title {
font-size: 28px;
}
.sub-title {
font-size: 18px;
}
.registration-form {
padding: 20px;
}
}
</style>