hookehuyr

feat(router): 添加加入作业页面路由和视图组件

新增加入作业功能页面路由配置和对应的Vue组件,包含课程信息展示、作业详情和加入按钮功能
1 /* 1 /*
2 * @Date: 2025-03-21 13:28:30 2 * @Date: 2025-03-21 13:28:30
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-09-30 16:50:44 4 + * @LastEditTime: 2025-11-17 13:54:31
5 * @FilePath: /mlaj/src/router/checkin.js 5 * @FilePath: /mlaj/src/router/checkin.js
6 * @Description: 文件描述 6 * @Description: 文件描述
7 */ 7 */
...@@ -96,4 +96,13 @@ export default [ ...@@ -96,4 +96,13 @@ export default [
96 requiresAuth: true 96 requiresAuth: true
97 } 97 }
98 }, 98 },
99 + {
100 + path: '/checkin/join',
101 + name: 'JoinCheckIn',
102 + component: () => import('@root/src/views/checkin/JoinCheckInPage.vue'),
103 + meta: {
104 + title: '加入作业',
105 + requiresAuth: true
106 + }
107 + },
99 ] 108 ]
......
1 +<!--
2 + * @Date: 2025-11-17 13:42:00
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2025-11-17 14:22:00
5 + * @FilePath: /mlaj/src/views/checkin/JoinCheckInPage.vue
6 + * @Description: 文件描述
7 +-->
8 +<template>
9 + <AppLayout :hasTitle="false">
10 + <div class="join-check-in-page">
11 + <!-- 头部课程信息 -->
12 + <div class="joinHeader bg-white rounded-lg shadow px-4 py-3">
13 + <div class="flex flex-col">
14 + <van-image
15 + height="8rem"
16 + fit="cover"
17 + :src="mock_task_info.course_cover_url"
18 + class="rounded-lg"
19 + />
20 + <div class="mt-3 text-center">
21 + <div class="courseTitle text-base font-semibold text-gray-800">{{ mock_task_info.course_title }}</div>
22 + <div class="teacher flex items-center mt-1 justify-center">
23 + <van-image
24 + round
25 + width="1.4rem"
26 + height="1.4rem"
27 + fit="cover"
28 + :src="mock_task_info.teacher_avatar"
29 + />
30 + <span class="ml-2 text-sm text-gray-700">{{ mock_task_info.teacher_name }}</span>
31 + </div>
32 + <div class="schedule text-xs text-gray-500 mt-1">{{ mock_task_info.schedule_desc }}</div>
33 + </div>
34 + </div>
35 + </div>
36 +
37 + <!-- 作业信息 -->
38 + <div class="infoCard bg-white rounded-lg shadow mt-4">
39 + <div class="cardHeader px-4 py-3 border-b border-gray-100">
40 + <div class="cardTitle text-sm font-semibold text-gray-800">作业信息</div>
41 + </div>
42 + <div class="cardBody px-4 py-3 space-y-2">
43 + <div class="infoItem flex justify-between text-sm">
44 + <div class="label text-gray-500">作业名称</div>
45 + <div class="value text-gray-800">{{ mock_task_info.task_title }}</div>
46 + </div>
47 + <div class="infoItem flex justify-between text-sm">
48 + <div class="label text-gray-500">所属班级</div>
49 + <div class="value text-gray-800">{{ mock_task_info.grade_name }} · {{ mock_task_info.class_name }}</div>
50 + </div>
51 + <div class="infoItem flex justify-between text-sm">
52 + <div class="label text-gray-500">目标次数</div>
53 + <div class="value text-gray-800">{{ mock_task_info.target_number }} 次</div>
54 + </div>
55 + <div class="infoItem flex justify-between text-sm">
56 + <div class="label text-gray-500">起止时间</div>
57 + <div class="value text-gray-800">{{ mock_task_info.period_desc }}</div>
58 + </div>
59 + <div class="infoItem flex justify-between text-sm">
60 + <div class="label text-gray-500">报名截止</div>
61 + <div class="value text-gray-800">{{ mock_task_info.join_deadline_desc }}</div>
62 + </div>
63 + </div>
64 + </div>
65 +
66 + <!-- 作业说明 -->
67 + <div class="infoCard bg-white rounded-lg shadow mt-4">
68 + <div class="cardHeader px-4 py-3 border-b border-gray-100">
69 + <div class="cardTitle text-sm font-semibold text-gray-800">作业说明</div>
70 + </div>
71 + <div class="cardBody px-4 py-3">
72 + <div class="text-sm text-gray-700 leading-6 whitespace-pre-line">{{ mock_task_info.task_desc }}</div>
73 + </div>
74 + </div>
75 +
76 + <!-- 加入按钮 -->
77 + <div class="joinAction p-4 bg-gradient-to-t from-white/95 to-white/20">
78 + <van-button type="primary" round block class="h-11 text-base font-semibold" @click="on_join_click">
79 + 加入该作业
80 + </van-button>
81 + </div>
82 + </div>
83 + </AppLayout>
84 +</template>
85 +
86 +<script setup>
87 +import { ref, onMounted } from 'vue'
88 +import { useRoute, useRouter } from 'vue-router'
89 +import { useTitle } from "@vueuse/core";
90 +import { showConfirmDialog } from 'vant'
91 +import AppLayout from '@/components/layout/AppLayout.vue'
92 +
93 +const $route = useRoute();
94 +const $router = useRouter();
95 +useTitle($route.meta.title);
96 +
97 +// Mock数据:课程与作业信息
98 +const mock_task_info = ref({
99 + course_title: '英语口语提升营',
100 + course_cover_url: 'https://cdn.ipadbiz.cn/mlaj/images/cover_video_2.png?imageMogr2/thumbnail/200x/strip/quality/70',
101 + teacher_name: '王老师',
102 + teacher_avatar: 'https://cdn.ipadbiz.cn/mlaj/images/icon_1.jpeg?imageMogr2/thumbnail/200x/strip/quality/70',
103 + schedule_desc: '每周二、周四晚 19:00-20:00',
104 + grade_name: '五年级',
105 + class_name: '二班',
106 + task_title: '本周作业:日常口语练习',
107 + task_desc: '请完成3次日常口语练习打卡,每次不少于60秒,可上传音频或视频。',
108 + target_number: 3,
109 + period_desc: '2025-11-01 至 2025-11-30',
110 + join_deadline_desc: '2025-11-20 23:59',
111 +})
112 +
113 +// 目标打卡页作业ID(无真实数据前默认写死)
114 +const target_checkin_id = ref('833668')
115 +
116 +/**
117 + * 载入页面Mock数据
118 + * @returns {void}
119 + * 注释:此处预留真实接口接入位置,当前为静态Mock。
120 + */
121 +const load_mock_data = () => {
122 + // 预留:后续可根据 $route.query.id 拉取真实数据并映射
123 +}
124 +
125 +/**
126 + * 点击加入作业的处理
127 + * @returns {Promise<void>}
128 + * 注释:弹出确认框,确认后跳转到打卡页。
129 + */
130 +const on_join_click = async () => {
131 + try {
132 + await showConfirmDialog({
133 + title: '确认加入',
134 + message: '是否确认加入该作业?加入后可前往打卡页进行每日打卡。',
135 + confirmButtonColor: '#4caf50',
136 + })
137 + // 确认后跳转到打卡页
138 + $router.push({
139 + path: '/checkin/index',
140 + query: {
141 + id: target_checkin_id.value,
142 + }
143 + })
144 + } catch (err) {
145 + // 用户取消
146 + }
147 +}
148 +
149 +onMounted(() => {
150 + load_mock_data()
151 +})
152 +</script>
153 +
154 +<style lang="less" scoped>
155 +.join-check-in-page {
156 + min-height: 100vh;
157 + background: linear-gradient(to bottom right, #f0fdf4, #f0fdfa, #eff6ff);
158 + padding: 1rem;
159 + padding-bottom: 6rem;
160 +
161 + .joinHeader {
162 + .courseTitle {
163 + line-height: 1.4;
164 + }
165 + .teacher {
166 + .name {
167 + color: #4caf50;
168 + }
169 + }
170 + }
171 +
172 + .infoCard {
173 + .cardHeader {
174 + .cardTitle {
175 + color: #4caf50;
176 + }
177 + }
178 + .cardBody {
179 + .infoItem {
180 + .label {
181 + color: #6b7280;
182 + }
183 + .value {
184 + color: #111827;
185 + }
186 + }
187 + }
188 + }
189 +
190 + .joinAction {
191 + box-shadow: 0 -6px 12px rgba(0,0,0,0.06);
192 + }
193 +}
194 +</style>