OL-LearnPlatform-Frontend/src/views/ActivityRegistration.vue
2025-08-12 16:58:22 +08:00

633 lines
14 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 class="hero-content">
<h2>与AI共创未来</h2>
<p class="hero-content-description">2025年全国青少年人工智能创新实践活动</p>
<div>
<p>主办单位中国科协青少年科技中心中国青少年科技教育工作者协会上海人工智能创新中心上海市科协</p>
<p>支持单位中国公众科学素质促进联合体</p>
<p>协办单位各省级青少年科技教育活动部门单位上海市科协科学教育中心</p>
</div>
</div>
</div>
<!-- 报名表单区域 -->
<div class="registration-form-container">
<div class="container">
<div class="form-section">
<div class="form-header">
<h3 class="form-title">
<img src="/images/activity/fill.png" alt="填写信息" class="form-title-icon" />
填写信息
</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="text" v-model="formData.gender" class="form-input" placeholder="请输入性别" required />
</div>
<!-- 年龄 -->
<div class="form-group">
<label class="form-label required">年龄:</label>
<input type="number" v-model="formData.age" class="form-input" placeholder="请输入年龄" min="0" max="120"
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="email" v-model="formData.email" 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 annex">附件:</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">
<img src="/images/activity/upload.png" alt="上传" class="upload-icon-img" />
</div>
<p class="upload-text">将文件拖到此处,或<span>点击上传</span></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: '',
gender: '',
age: '',
phone: '',
verificationCode: '',
email: '',
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: #fff;
}
.container {
width: 1420px;
margin: 0 auto;
padding: 0;
}
/* 面包屑导航 */
.breadcrumb-container {
background: white;
padding: 10px 0;
}
.breadcrumb {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
}
.breadcrumb-item {
color: #333;
text-decoration: none;
}
.breadcrumb-separator {
color: #999;
}
.breadcrumb-current {
color: #333;
}
/* 活动横幅区域 */
.hero-banner {
margin: auto;
width: 1420px;
position: relative;
overflow: hidden;
}
.hero-content {
position: absolute;
top: 50%;
transform: translateY(-50%);
left: 100px;
}
.hero-content h2 {
font-family: AlimamaShuHeiTi, AlimamaShuHeiTi, '黑体';
font-weight: bold;
font-size: 48px;
color: #FFFFFF;
line-height: 57px;
text-align: justify;
font-style: normal;
}
.hero-content .hero-content-description {
margin-top: 12px;
margin-bottom: 40px;
font-family: PingFangSC, PingFang SC;
font-weight: 500;
font-size: 32px;
color: #FFFFFF;
line-height: 45px;
text-align: justify;
font-style: normal;
}
.hero-content div p {
margin-bottom: 2px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 14px;
color: #FFFFFF;
line-height: 20px;
text-align: justify;
font-style: normal;
text-transform: none;
}
.banner-image-container {
margin: auto;
width: 1420px;
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;
}
.form-section {
width: 100%;
margin: 0 auto;
background: white;
border-radius: 8px;
overflow: hidden;
}
.form-header {}
.form-title {
font-size: 16px;
font-weight: 600;
color: #333;
margin: 0;
display: flex;
align-items: center;
gap: 8px;
}
.form-title-icon {
width: 24px;
height: 24px;
object-fit: contain;
}
.registration-form {
padding: 30px 220px;
}
.form-group {
display: flex;
margin-bottom: 24px;
gap: 10px;
}
.form-label {
width: 180px;
font-size: 16px;
color: #333;
font-weight: 500;
display: flex;
align-items: center;
justify-content: flex-end;
}
.annex {
align-items: start;
}
.form-label.required {
color: #333;
}
.form-label.required::before {
content: '*';
color: #ff4d4f;
margin-right: 4px;
}
.form-input {
width: 100%;
height: 56px;
padding: 0 12px;
border: 1px solid #D8D8D8;
font-size: 16px;
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;
width: 100%;
position: relative;
}
.input-group .form-input {
flex: 1;
}
.verify-btn {
position: absolute;
right: 0;
top: 25%;
transform: translateY(-25%);
background: none;
color: #0389D1;
border: none;
padding: 0 16px;
cursor: pointer;
font-size: 16px;
white-space: nowrap;
transition: background-color 0.3s;
height: 56px;
align-self: flex-start;
}
.verify-btn:hover:not(:disabled) {
background: #40a9ff;
color: white;
}
.verify-btn:disabled {
background: #d9d9d9;
cursor: not-allowed;
}
.form-select {
width: 100%;
height: 56px;
padding: 0 12px;
border: 1px solid #D8D8D8;
font-size: 16px;
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 {
width: 100%;
height: 210px;
padding: 40px;
text-align: center;
transition: border-color 0.3s;
cursor: pointer;
border: 2px dashed #D8D8D8;
}
.file-upload-area:hover {
border-color: #1890ff;
}
.file-input {
display: none;
}
.upload-placeholder {
color: #666;
}
.upload-icon {
margin-bottom: 10px;
display: flex;
justify-content: center;
}
.upload-icon-img {
width: 128px;
height: 77px;
object-fit: contain;
}
.upload-text {
font-size: 16px;
margin-bottom: 5px;
}
.upload-text span {
color: #0088D1;
}
.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;
}
.inline-group {
display: flex;
gap: 20px;
}
.inline-item {
flex: 1;
}
/* 提交按钮 */
.form-actions {
text-align: center;
margin-top: 30px;
}
.submit-btn {
width: 320px;
height: 48px;
background: #0389D1;
color: white;
border: none;
border-radius: 30px;
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>