StudentCard.vue 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. <template>
  2. <!--
  3. 学生卡片组件
  4. 功能:显示单个学生的信息和考勤状态
  5. 支持:
  6. 1. 学生基本信息显示(头像、姓名、编号)
  7. 2. 考勤状态可视化(出勤、旷课、迟到、早退、请假)
  8. 3. 点击切换考勤状态(仅在点名模式下)
  9. 4. 一寸证件照比例的头像显示
  10. -->
  11. <view
  12. class="student-card"
  13. @click="handleCardClick"
  14. >
  15. <!-- 学生头像 -->
  16. <view class="avatar-container">
  17. <image
  18. class="avatar"
  19. :src="student.avatar || '/static/images/default-avatar.png'"
  20. mode="aspectFill"
  21. @error="handleImageError"
  22. />
  23. </view>
  24. <!-- 学生信息 -->
  25. <view class="student-info">
  26. <view class="student-code">{{ student.code }}</view>
  27. <view class="student-name">{{ student.name }}</view>
  28. </view>
  29. <!-- 底部状态指示线 -->
  30. <view class="status-line-container">
  31. <!-- 默认灰色底线 -->
  32. <view class="status-line-base"></view>
  33. <!-- 迟到:左半边红线 -->
  34. <view v-if="student.status === 11" class="status-line-overlay status-late-overlay"></view>
  35. <!-- 旷课:整条红线 -->
  36. <view v-if="student.status === 1" class="status-line-overlay status-absent-overlay"></view>
  37. <!-- 早退:右半边红线 -->
  38. <view v-if="student.status === 21" class="status-line-overlay status-early-overlay"></view>
  39. </view>
  40. <!-- 临时:整个卡片可点击,用于测试 -->
  41. </view>
  42. </template>
  43. <script setup>
  44. /**
  45. * 学生卡片组件
  46. *
  47. * 主要功能:
  48. * 1. 显示学生基本信息(头像、姓名、编号)
  49. * 2. 考勤状态的可视化显示
  50. * 3. 考勤状态的点击切换功能
  51. * 4. 支持一寸证件照比例的头像显示
  52. *
  53. * 考勤状态说明(底部指示线):
  54. * - 81: 出勤(灰色底线)
  55. * - 1: 旷课(整条红线)
  56. * - 11: 迟到(左半边红线)
  57. * - 21: 早退(右半边红线)
  58. * - 0: 请假(灰色底线)
  59. */
  60. import { defineProps, defineEmits } from 'vue'
  61. const props = defineProps({
  62. student: {
  63. type: Object,
  64. required: true,
  65. default: () => ({
  66. id: '',
  67. name: '',
  68. code: '',
  69. avatar: '',
  70. row: 1,
  71. col: 1,
  72. status: 81 // 81-出勤, 1-旷课, 11-迟到, 21-早退, 0-请假
  73. })
  74. },
  75. // 是否在点名模式下(影响交互)
  76. attendanceMode: {
  77. type: Boolean,
  78. default: false
  79. }
  80. })
  81. const emit = defineEmits(['click', 'statusChange'])
  82. // 处理卡片点击
  83. const handleCardClick = () => {
  84. console.log('点击了学生卡片:', props.student.name, '当前模式:', props.attendanceMode)
  85. if (!props.attendanceMode) {
  86. console.log('不在点名模式,忽略点击')
  87. return // 只在点名模式下响应点击
  88. }
  89. console.log('开始状态切换,当前状态:', props.student.status)
  90. // 状态切换逻辑:81(出勤) → 1(旷课) → 11(迟到) → 21(早退) → 81(出勤)
  91. let newStatus
  92. switch (props.student.status) {
  93. case 81: // 出勤 → 旷课
  94. newStatus = 1
  95. break
  96. case 1: // 旷课 → 迟到
  97. newStatus = 11
  98. break
  99. case 11: // 迟到 → 早退
  100. newStatus = 21
  101. break
  102. case 21: // 早退 → 出勤
  103. newStatus = 81
  104. break
  105. default: // 其他状态 → 出勤
  106. newStatus = 81
  107. }
  108. emit('statusChange', {
  109. studentId: props.student.id,
  110. oldStatus: props.student.status,
  111. newStatus: newStatus
  112. })
  113. emit('click', props.student)
  114. }
  115. // 处理头像加载失败
  116. const handleImageError = () => {
  117. console.log('头像加载失败:', props.student.name)
  118. }
  119. </script>
  120. <style lang="scss" scoped>
  121. .student-card {
  122. width: 200rpx;
  123. background-color: #fff;
  124. border: 2rpx solid #E5E5E5;
  125. border-radius: 8rpx;
  126. display: flex;
  127. flex-direction: column;
  128. align-items: center;
  129. position: relative;
  130. overflow: hidden;
  131. transition: all 0.3s ease;
  132. padding: 20rpx 20rpx 0 20rpx; // 上右下左,底部不加padding因为有状态线
  133. box-sizing: border-box;
  134. // 头像容器 - 一寸证件照比例 (2.5:3.5 ≈ 5:7)
  135. .avatar-container {
  136. width: 100%; // 占满容器宽度
  137. height: 224rpx; // 按一寸证件照比例 (160 * 1.4 = 224)
  138. margin-top: 0;
  139. border-radius: 8rpx;
  140. overflow: hidden;
  141. .avatar {
  142. width: 100%;
  143. height: 100%;
  144. background-color: #F5F5F5;
  145. }
  146. }
  147. // 学生信息
  148. .student-info {
  149. width: 100%;
  150. display: flex;
  151. justify-content: space-between;
  152. align-items: center;
  153. margin-top: 20rpx;
  154. margin-bottom: 20rpx; // 添加底部边距,为状态线留出空间
  155. .student-code {
  156. font-size: 24rpx;
  157. color: #666;
  158. text-align: left;
  159. }
  160. .student-name {
  161. font-size: 28rpx;
  162. color: #333;
  163. font-weight: bold;
  164. text-align: right;
  165. overflow: hidden;
  166. text-overflow: ellipsis;
  167. white-space: nowrap;
  168. }
  169. }
  170. // 底部状态指示线样式
  171. .status-line-container {
  172. position: absolute;
  173. bottom: 0;
  174. left: 0;
  175. right: 0;
  176. height: 10rpx;
  177. pointer-events: none;
  178. }
  179. // 默认灰色底线
  180. .status-line-base {
  181. position: absolute;
  182. bottom: 0;
  183. left: 0;
  184. right: 0;
  185. height: 10rpx;
  186. background-color: #e0e0e0;
  187. border-radius: 8rpx;
  188. }
  189. // 状态指示线覆盖层
  190. .status-line-overlay {
  191. position: absolute;
  192. bottom: 0;
  193. height: 10rpx;
  194. background-color: #ff4444;
  195. border-radius: 8rpx;
  196. }
  197. // 迟到:左半边红线
  198. .status-late-overlay {
  199. left: 0;
  200. width: 50%;
  201. }
  202. // 旷课:整条红线
  203. .status-absent-overlay {
  204. left: 0;
  205. width: 100%;
  206. }
  207. // 早退:右半边红线
  208. .status-early-overlay {
  209. right: 0;
  210. width: 50%;
  211. }
  212. // 底部点击条
  213. .click-bar {
  214. position: absolute;
  215. bottom: 0;
  216. left: 0;
  217. right: 0;
  218. width: 100%;
  219. height: 40rpx; // 增加高度,更容易点击
  220. background-color: rgba(0, 0, 0, 0.05); // 淡灰色背景
  221. cursor: pointer;
  222. z-index: 10;
  223. display: flex;
  224. align-items: center;
  225. justify-content: center;
  226. .click-hint {
  227. font-size: 20rpx;
  228. color: #999;
  229. opacity: 0.7;
  230. }
  231. // 点击时的反馈效果
  232. &:active {
  233. background-color: rgba(0, 0, 0, 0.1);
  234. }
  235. }
  236. // 状态样式
  237. &.status-normal {
  238. opacity: 1;
  239. }
  240. &.status-late {
  241. opacity: 1;
  242. }
  243. &.status-early {
  244. opacity: 1;
  245. }
  246. &.status-absent {
  247. opacity: 1;
  248. }
  249. }
  250. </style>