release: freeze demo v1.1.0 2026-10-19
CI/CD Pipeline / Backend Build & Test (push) Canceled after 0s
CI/CD Pipeline / Admin Web Build (push) Canceled after 0s
CI/CD Pipeline / Mini Program Compile Check (push) Canceled after 0s
CI/CD Pipeline / Migration Validation (push) Canceled after 0s
CI/CD Pipeline / Test Shell Guard (Phase 23 Gate) (push) Canceled after 0s
CI/CD Pipeline / UI Contracts (source-structure gates) (push) Canceled after 0s
CI/CD Pipeline / E2E (live stack) (push) Canceled after 0s

This commit is contained in:
WorkBuddy
2026-10-19 20:36:22 +08:00
parent 8b505d9133
commit 69ebf33e18
157 changed files with 21111 additions and 72 deletions
@@ -0,0 +1,147 @@
<template>
<PageLayout title="活动报名">
<UiLoading v-if="loading" text="正在加载活动…" />
<UiErrorState v-else-if="error" @retry="load" />
<UiEmpty v-else-if="!activity" description="该活动不存在或已下线" />
<template v-else>
<div class="reg">
<div class="reg__card">
<div class="reg__title">{{ activity.title }}</div>
<div class="reg__meta">
<span>{{ formatDateTime(activity.startAt) || '时间待定' }}</span>
<span v-if="activity.locationName">{{ activity.locationName }}</span>
</div>
</div>
<div class="reg__card">
<div class="reg__label">备注 / 留言(选填)</div>
<van-field
v-model="remark"
type="textarea"
rows="3"
maxlength="200"
show-word-limit
placeholder="可填写方便工作人员联系的信息或特殊说明"
autosize
/>
</div>
<div class="reg__notice">
报名提交后需等待工作人员审核;不同活动类型的资格要求以活动页面和审核结果为准。
</div>
<van-button
class="reg__submit"
type="primary"
block
round
:loading="submitting"
@click="onSubmit"
>
提交报名
</van-button>
</div>
</template>
</PageLayout>
</template>
<script setup lang="ts">
import { computed, onMounted, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { activityService } from '@/api/services'
import { useAppStore } from '@/stores/app'
import { formatDateTime } from '@/utils/datetime'
import PageLayout from '@/layouts/PageLayout.vue'
import UiLoading from '@/components/UiLoading.vue'
import UiEmpty from '@/components/UiEmpty.vue'
import UiErrorState from '@/components/UiErrorState.vue'
const route = useRoute()
const router = useRouter()
const appStore = useAppStore()
const activityId = computed(() => String(route.query.id || ''))
const activity = ref<any>(null)
const loading = ref(true)
const error = ref(false)
const remark = ref('')
const submitting = ref(false)
async function load() {
loading.value = true
error.value = false
try {
if (!activityId.value) {
activity.value = null
return
}
activity.value = await activityService.getActivityDetail(activityId.value)
} catch (err: any) {
error.value = true
appStore.toastError(err?.message || '加载活动失败')
} finally {
loading.value = false
}
}
async function onSubmit() {
if (submitting.value || !activityId.value) return
submitting.value = true
try {
const payload = remark.value.trim() ? { remark: remark.value.trim() } : {}
await activityService.register(activityId.value, payload)
appStore.toastSuccess('报名已提交,请等待审核')
router.replace({ path: '/my/activities' })
} catch (err: any) {
appStore.toastError(err?.message || '报名失败')
} finally {
submitting.value = false
}
}
onMounted(load)
</script>
<style scoped>
.reg {
padding: 16px 16px 24px;
}
.reg__card {
background: var(--mini-surface);
border-radius: var(--mini-radius);
padding: 14px;
box-shadow: var(--mini-shadow);
margin-bottom: 12px;
}
.reg__title {
font-size: 16px;
font-weight: 600;
color: var(--mini-text-strong);
}
.reg__meta {
display: flex;
gap: 12px;
font-size: 12px;
color: var(--mini-text-secondary);
margin-top: 6px;
}
.reg__label {
font-size: 13px;
color: var(--mini-text-strong);
margin-bottom: 8px;
}
.reg__card :deep(.van-field) {
background: var(--mini-bg);
border-radius: 8px;
padding: 10px;
}
.reg__notice {
font-size: 12px;
color: var(--mini-text-muted);
line-height: 1.6;
margin: 4px 4px 16px;
}
.reg__submit {
margin-top: 8px;
}
</style>