hookehuyr

refactor(studentPage): 重构学生详情页面布局和样式

- 使用van-config-provider包裹整个页面以支持主题变量
- 调整统计图表stroke-width为70以增强可视性
- 使用van-sticky优化标签页的粘性布局
- 添加返回顶部按钮并设置样式
- 统一调整时间显示字体大小
- 优化状态图标显示效果
...@@ -2,118 +2,110 @@ ...@@ -2,118 +2,110 @@
2 * @Author: hookehuyr hookehuyr@gmail.com 2 * @Author: hookehuyr hookehuyr@gmail.com
3 * @Date: 2025-06-19 17:12:19 3 * @Date: 2025-06-19 17:12:19
4 * @LastEditors: hookehuyr hookehuyr@gmail.com 4 * @LastEditors: hookehuyr hookehuyr@gmail.com
5 - * @LastEditTime: 2025-06-19 21:32:23 5 + * @LastEditTime: 2025-06-19 22:39:26
6 * @FilePath: /mlaj/src/views/teacher/studentPage.vue 6 * @FilePath: /mlaj/src/views/teacher/studentPage.vue
7 * @Description: 学生详情页面 7 * @Description: 学生详情页面
8 --> 8 -->
9 <template> 9 <template>
10 - <div class="bg-gradient-to-br from-green-50 via-green-100/30 to-blue-50/30 min-h-screen"> 10 + <van-config-provider :theme-vars="themeVars">
11 - <!-- 学生基本信息 --> 11 + <div class="bg-gradient-to-br from-green-50 via-green-100/30 to-blue-50/30 min-h-screen">
12 - <div class="bg-white p-4"> 12 + <!-- 学生基本信息 -->
13 - <div class="flex items-start mb-4"> 13 + <div class="bg-white p-4">
14 - <van-image round width="4rem" height="4rem" 14 + <div class="flex items-start mb-4">
15 - :src="studentInfo.avatar || 'https://cdn.ipadbiz.cn/mlaj/images/icon_1.jpeg'" fit="cover" class="mr-4" /> 15 + <van-image round width="4rem" height="4rem"
16 - <div class="flex-1"> 16 + :src="studentInfo.avatar || 'https://cdn.ipadbiz.cn/mlaj/images/icon_1.jpeg'" fit="cover" class="mr-4" />
17 - <div class="flex items-center mb-2"> 17 + <div class="flex-1">
18 - <h2 class="text-xl font-bold text-gray-800 mr-2">{{ studentInfo.name }}</h2> 18 + <div class="flex items-center mb-2">
19 - <van-icon v-if="studentInfo.gender === 'male'" name="friends-o" color="#3b82f6" size="16" /> 19 + <h2 class="text-xl font-bold text-gray-800 mr-2">{{ studentInfo.name }}</h2>
20 - <van-icon v-else name="like-o" color="#ec4899" size="16" /> 20 + <van-icon v-if="studentInfo.gender === 'male'" name="friends-o" color="#3b82f6" size="16" />
21 - </div> 21 + <van-icon v-else name="like-o" color="#ec4899" size="16" />
22 - <div class="flex items-center mb-2"> 22 + </div>
23 - <van-icon name="chat-o" size="16" color="#10b981" class="mr-1" /> 23 + <div class="flex items-center mb-2">
24 - <span class="text-sm text-gray-600 mr-4">{{ studentInfo.className }}</span> 24 + <van-icon name="chat-o" size="16" color="#10b981" class="mr-1" />
25 - <van-icon name="phone-o" size="16" color="#10b981" class="mr-1" /> 25 + <span class="text-sm text-gray-600 mr-4">{{ studentInfo.className }}</span>
26 - <span class="text-sm text-gray-600">{{ formatPhone(studentInfo.phone) }}</span> 26 + <van-icon name="phone-o" size="16" color="#10b981" class="mr-1" />
27 - </div> 27 + <span class="text-sm text-gray-600">{{ formatPhone(studentInfo.phone) }}</span>
28 - <!-- 标签 --> 28 + </div>
29 - <div class="flex flex-wrap gap-2"> 29 + <!-- 标签 -->
30 - <van-tag v-for="tag in studentInfo.tags" :key="tag" type="success" size="small"> 30 + <div class="flex flex-wrap gap-2">
31 - {{ tag }} 31 + <van-tag v-for="tag in studentInfo.tags" :key="tag" type="success" size="small">
32 - </van-tag> 32 + {{ tag }}
33 + </van-tag>
34 + </div>
33 </div> 35 </div>
34 </div> 36 </div>
35 </div> 37 </div>
36 - </div>
37 38
38 - <!-- 所学课程 --> 39 + <!-- 所学课程 -->
39 - <div class="bg-white mt-2 p-4"> 40 + <div class="bg-white mt-2 p-4">
40 - <div class="flex items-center mb-3"> 41 + <div class="flex items-center mb-3">
41 - <van-icon name="bookmark-o" size="16" color="#10b981" class="mr-2" /> 42 + <van-icon name="bookmark-o" size="16" color="#10b981" class="mr-2" />
42 - <span class="text-sm font-medium text-gray-700">所学课程</span> 43 + <span class="text-sm font-medium text-gray-700">所学课程</span>
43 - </div> 44 + </div>
44 - <div class="flex flex-wrap gap-2"> 45 + <div class="flex flex-wrap gap-2">
45 - <van-tag v-for="course in studentInfo.courses" :key="course" type="primary" size="small"> 46 + <van-tag v-for="course in studentInfo.courses" :key="course" type="primary" size="small">
46 - {{ course }} 47 + {{ course }}
47 - </van-tag> 48 + </van-tag>
49 + </div>
48 </div> 50 </div>
49 - </div>
50 51
51 - <!-- 统计数据 --> 52 + <!-- 统计数据 -->
52 - <div class="mt-2"> 53 + <div class="mt-2">
53 - <van-row> 54 + <van-row>
54 - <!-- 出勤率 --> 55 + <!-- 出勤率 -->
55 - <van-col span="8"> 56 + <van-col span="8">
56 - <div class="bg-white p-4 text-center"> 57 + <div class="bg-white p-4 text-center">
57 - <div class="relative w-16 h-16 mx-auto mb-2"> 58 + <div class="relative w-16 h-16 mx-auto mb-2">
58 - <van-circle v-model:current-rate="studentStats.attendanceRate" :rate="studentStats.attendanceRate" 59 + <van-circle v-model:current-rate="studentStats.attendanceRate" :rate="studentStats.attendanceRate"
59 - :speed="100" :text="studentStats.attendanceRate + '%'" stroke-width="50" color="#10b981" size="64" /> 60 + :speed="100" :text="studentStats.attendanceRate + '%'" stroke-width="70" color="#10b981" size="64" />
61 + </div>
62 + <div class="text-sm text-gray-600">出勤率</div>
60 </div> 63 </div>
61 - <div class="text-sm text-gray-600">出勤率</div> 64 + </van-col>
62 - </div> 65 + <!-- 作业完成率 -->
63 - </van-col> 66 + <van-col span="8">
64 - <!-- 作业完成率 --> 67 + <div class="bg-white p-4 text-center">
65 - <van-col span="8"> 68 + <div class="relative w-16 h-16 mx-auto mb-2">
66 - <div class="bg-white p-4 text-center"> 69 + <van-circle v-model:current-rate="studentStats.homeworkRate" :rate="studentStats.homeworkRate"
67 - <div class="relative w-16 h-16 mx-auto mb-2"> 70 + :speed="100" :text="studentStats.homeworkRate + '%'" stroke-width="70" color="#3b82f6" size="64" />
68 - <van-circle v-model:current-rate="studentStats.homeworkRate" :rate="studentStats.homeworkRate" 71 + </div>
69 - :speed="100" :text="studentStats.homeworkRate + '%'" stroke-width="50" color="#3b82f6" size="64" /> 72 + <div class="text-sm text-gray-600">作业完成率</div>
70 </div> 73 </div>
71 - <div class="text-sm text-gray-600">作业完成率</div> 74 + </van-col>
72 - </div> 75 + <!-- 测验成绩 -->
73 - </van-col> 76 + <van-col span="8">
74 - <!-- 测验成绩 --> 77 + <div class="bg-white p-4 text-center">
75 - <van-col span="8"> 78 + <div class="relative w-16 h-16 mx-auto mb-2">
76 - <div class="bg-white p-4 text-center"> 79 + <van-circle v-model:current-rate="studentStats.testScore" :rate="studentStats.testScore" :speed="100"
77 - <div class="relative w-16 h-16 mx-auto mb-2"> 80 + :text="studentStats.testScore + '%'" stroke-width="70" color="#f59e0b" size="64" />
78 - <van-circle v-model:current-rate="studentStats.testScore" :rate="studentStats.testScore" :speed="100" 81 + </div>
79 - :text="studentStats.testScore + '%'" stroke-width="50" color="#f59e0b" size="64" /> 82 + <div class="text-sm text-gray-600">测验成绩</div>
80 </div> 83 </div>
81 - <div class="text-sm text-gray-600">测验成绩</div> 84 + </van-col>
82 - </div> 85 + </van-row>
83 - </van-col> 86 + </div>
84 - </van-row>
85 - </div>
86 87
87 - <!-- 功能按钮 --> 88 + <!-- 功能按钮 -->
88 - <div class="mt-2 p-4"> 89 + <div class="mt-2 p-4">
89 - <!-- 状态筛选 --> 90 + <!-- 状态筛选 -->
90 - <div class="flex items-center justify-end mb-4"> 91 + <div class="flex items-center justify-end mb-4">
91 - <div @click="showStatusPopup = true" class="flex items-center text-sm text-gray-600 cursor-pointer"> 92 + <div @click="showStatusPopup = true" class="flex items-center text-sm text-gray-600 cursor-pointer">
92 - <span>{{ statusFilter }}</span> 93 + <span>{{ statusFilter }}</span>
93 - <van-icon name="arrow-down" size="14" class="ml-1" /> 94 + <van-icon name="arrow-down" size="14" class="ml-1" />
95 + </div>
94 </div> 96 </div>
95 </div> 97 </div>
96 - <!-- <div class="flex items-center justify-between mb-3"> 98 +
97 - <div @click="activeTab = 'homework'" 99 + <!-- 使用van-sticky包裹van-tabs实现粘性布局 -->
98 - :class="['flex-1 text-center py-2 text-sm font-medium cursor-pointer', activeTab === 'homework' ? 'text-green-600 border-b-2 border-green-600' : 'text-gray-500']"> 100 + <van-sticky :offset-top="0">
99 - 作业记录 101 + <div class="bg-white">
100 - </div> 102 + <van-tabs v-model:active="activeTab" color="#10b981" animated swipeable @change="handleTabChange">
101 - <div @click="activeTab = 'evaluation'" 103 + <van-tab title="作业记录" name="homework"></van-tab>
102 - :class="['flex-1 text-center py-2 text-sm font-medium cursor-pointer', activeTab === 'evaluation' ? 'text-green-600 border-b-2 border-green-600' : 'text-gray-500']"> 104 + <van-tab title="班主任点评" name="evaluation"></van-tab>
103 - 班主任点评 105 + <van-tab title="打卡统计" name="statistics"></van-tab>
104 - </div> 106 + </van-tabs>
105 - <div @click="activeTab = 'statistics'"
106 - :class="['flex-1 text-center py-2 text-sm font-medium cursor-pointer', activeTab === 'statistics' ? 'text-green-600 border-b-2 border-green-600' : 'text-gray-500']">
107 - 打卡统计
108 </div> 107 </div>
109 - </div> --> 108 + </van-sticky>
110 - <div class="px-4 py-3 bg-white" style="position: relative;">
111 - <van-tabs v-model:active="activeTab" color="#10b981" sticky animated swipeable @change="handleTabChange">
112 - <van-tab title="作业记录" name="homework"></van-tab>
113 - <van-tab title="班主任点评" name="evaluation"></van-tab>
114 - <van-tab title="打卡统计" name="statistics"></van-tab>
115 - </van-tabs>
116 - </div>
117 109
118 <!-- 记录列表 --> 110 <!-- 记录列表 -->
119 <van-list v-show="activeTab === 'statistics'" v-model:loading="recordLoading" :finished="recordFinished" 111 <van-list v-show="activeTab === 'statistics'" v-model:loading="recordLoading" :finished="recordFinished"
...@@ -123,12 +115,12 @@ ...@@ -123,12 +115,12 @@
123 <div class="flex items-center flex-1"> 115 <div class="flex items-center flex-1">
124 <div class="mr-4"> 116 <div class="mr-4">
125 <div style="display: flex; justify-content: center;"> 117 <div style="display: flex; justify-content: center;">
126 - <van-icon name="calendar-o" size="16" color="#6b7280" class="mb-1" /> &nbsp; 118 + <van-icon name="calendar-o" size="18" color="#6b7280" class="mb-1" /> &nbsp;
127 - <span class="text-xs text-gray-500">{{ record.date }}</span> 119 + <span class="text-sm text-gray-500">{{ record.date }}</span>
128 </div> 120 </div>
129 <div style="display: flex; "> 121 <div style="display: flex; ">
130 - <van-icon name="clock-o" size="16" color="#6b7280" class="mb-1" /> &nbsp; 122 + <van-icon name="clock-o" size="18" color="#6b7280" class="mb-1" /> &nbsp;
131 - <span class="text-xs text-gray-500">{{ record.time }}</span> 123 + <span class="text-sm text-gray-500">{{ record.time }}</span>
132 </div> 124 </div>
133 </div> 125 </div>
134 </div> 126 </div>
...@@ -137,7 +129,7 @@ ...@@ -137,7 +129,7 @@
137 <span v-else-if="record.status === '迟到'" class="text-orange-500 text-sm mr-2">{{ record.status }}</span> 129 <span v-else-if="record.status === '迟到'" class="text-orange-500 text-sm mr-2">{{ record.status }}</span>
138 <span v-else class="text-red-500 text-sm mr-2">{{ record.status }}</span> 130 <span v-else class="text-red-500 text-sm mr-2">{{ record.status }}</span>
139 131
140 - <van-icon v-if="record.status === '正常'" name="success" color="#10b981" size="16" /> 132 + <van-icon v-if="record.status === '正常'" name="passed" color="#10b981" size="16" />
141 <van-icon v-else-if="record.status === '迟到'" name="warning-o" color="#f59e0b" size="16" /> 133 <van-icon v-else-if="record.status === '迟到'" name="warning-o" color="#f59e0b" size="16" />
142 <van-icon v-else name="close" color="#ef4444" size="16" /> 134 <van-icon v-else name="close" color="#ef4444" size="16" />
143 </div> 135 </div>
...@@ -214,27 +206,29 @@ ...@@ -214,27 +206,29 @@
214 </div> 206 </div>
215 </div> 207 </div>
216 </van-list> 208 </van-list>
217 - </div> 209 + <van-back-top right="5vw" bottom="10vh" />
218 - 210 + <div style="height: 5rem;"></div>
219 - <!-- 状态筛选弹窗 --> 211 +
220 - <van-popup v-model:show="showStatusPopup" position="bottom" round> 212 + <!-- 状态筛选弹窗 -->
221 - <div class="p-4"> 213 + <van-popup v-model:show="showStatusPopup" position="bottom" round>
222 - <div class="text-center text-lg font-bold mb-4">选择状态</div> 214 + <div class="p-4">
223 - <van-cell-group> 215 + <div class="text-center text-lg font-bold mb-4">选择状态</div>
224 - <van-cell v-for="option in statusOptions" :key="option.value" :title="option.text" clickable 216 + <van-cell-group>
225 - @click="onStatusSelect(option)" :border="false" 217 + <van-cell v-for="option in statusOptions" :key="option.value" :title="option.text" clickable
226 - :class="{ 'text-green-600': statusFilter === option.value }"> 218 + @click="onStatusSelect(option)" :border="false"
227 - <template #right-icon> 219 + :class="{ 'text-green-600': statusFilter === option.value }">
228 - <van-icon v-if="statusFilter === option.value" name="success" color="#10b981" /> 220 + <template #right-icon>
229 - </template> 221 + <van-icon v-if="statusFilter === option.value" name="success" color="#10b981" />
230 - </van-cell> 222 + </template>
231 - </van-cell-group> 223 + </van-cell>
232 - <div class="mt-4"> 224 + </van-cell-group>
233 - <van-button block @click="showStatusPopup = false">取消</van-button> 225 + <div class="mt-4">
226 + <van-button block @click="showStatusPopup = false">取消</van-button>
227 + </div>
234 </div> 228 </div>
235 - </div> 229 + </van-popup>
236 - </van-popup> 230 + </div>
237 - </div> 231 + </van-config-provider>
238 </template> 232 </template>
239 233
240 <script setup> 234 <script setup>
...@@ -253,6 +247,10 @@ const router = useRouter() ...@@ -253,6 +247,10 @@ const router = useRouter()
253 const route = useRoute() 247 const route = useRoute()
254 useTitle(route.meta.title); 248 useTitle(route.meta.title);
255 249
250 +const themeVars = reactive({
251 + buttonNormalFontSize: '1rem',
252 +})
253 +
256 // 学生信息 254 // 学生信息
257 const studentInfo = ref({ 255 const studentInfo = ref({
258 id: 1, 256 id: 1,
...@@ -685,7 +683,11 @@ const formatData = (data) => { ...@@ -685,7 +683,11 @@ const formatData = (data) => {
685 } 683 }
686 </script> 684 </script>
687 685
688 -<style scoped lang="less"> 686 +<style lang="less">
687 +.van-back-top {
688 + background-color: #4caf50;
689 +}
690 +
689 /* 自定义样式 */ 691 /* 自定义样式 */
690 .van-circle { 692 .van-circle {
691 font-size: 12px; 693 font-size: 12px;
......