hookehuyr

docs: 为多个组件添加JSDoc注释和中文注释

为TaskCascaderFilter、AddTargetDialog、CheckInList等组件添加详细的JSDoc注释
将LiveStreamCard组件的英文注释转换为中文
完善postCountModel组件的类型检查和空值处理
为CheckinDetailPage的关键方法添加详细说明
...@@ -67,7 +67,10 @@ const emit = defineEmits(['update:show', 'confirm']) ...@@ -67,7 +67,10 @@ const emit = defineEmits(['update:show', 'confirm'])
67 // 本地表单字段状态 67 // 本地表单字段状态
68 const localFields = ref([]) 68 const localFields = ref([])
69 69
70 -// 监听弹窗显示状态、字段配置和初始值的变化 70 +/**
71 + * 监听弹窗显示状态、字段配置和初始值的变化
72 + * @description 弹窗显示时,根据传入的 fields 配置初始化 localFields,并填充 initialValues 中的值
73 + */
71 watch([() => props.show, () => props.fields, () => props.initialValues], ([showVal, fieldsVal, initialValuesVal]) => { 74 watch([() => props.show, () => props.fields, () => props.initialValues], ([showVal, fieldsVal, initialValuesVal]) => {
72 if (showVal) { 75 if (showVal) {
73 // 初始化字段,添加 value 属性 76 // 初始化字段,添加 value 属性
......
...@@ -192,6 +192,11 @@ const actions = [ ...@@ -192,6 +192,11 @@ const actions = [
192 // 没有加删除功能, 那个接口也是不存在的 192 // 没有加删除功能, 那个接口也是不存在的
193 ] 193 ]
194 194
195 +/**
196 + * 开启长按计时器
197 + * @description 设置500ms定时器,触发后激活长按状态并显示操作菜单
198 + * @param {Object} item - 长按的目标对象
199 + */
195 const startLongPress = (item) => { 200 const startLongPress = (item) => {
196 isLongPress.value = false 201 isLongPress.value = false
197 longPressTimer.value = setTimeout(() => { 202 longPressTimer.value = setTimeout(() => {
...@@ -205,6 +210,10 @@ const startLongPress = (item) => { ...@@ -205,6 +210,10 @@ const startLongPress = (item) => {
205 }, 500) 210 }, 500)
206 } 211 }
207 212
213 +/**
214 + * 清除长按计时器
215 + * @description 取消未触发的长按定时器
216 + */
208 const clearLongPress = () => { 217 const clearLongPress = () => {
209 if (longPressTimer.value) { 218 if (longPressTimer.value) {
210 clearTimeout(longPressTimer.value) 219 clearTimeout(longPressTimer.value)
...@@ -212,7 +221,7 @@ const clearLongPress = () => { ...@@ -212,7 +221,7 @@ const clearLongPress = () => {
212 } 221 }
213 } 222 }
214 223
215 -// Touch events 224 +// 触摸事件处理
216 const onTouchStart = (item) => { 225 const onTouchStart = (item) => {
217 startLongPress(item) 226 startLongPress(item)
218 } 227 }
......
1 <!-- 1 <!--
2 * @Date: 2025-12-11 17:26:25 2 * @Date: 2025-12-11 17:26:25
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-12-18 20:57:49 4 + * @LastEditTime: 2026-01-22 17:00:53
5 * @FilePath: /mlaj/src/components/count/postCountModel.vue 5 * @FilePath: /mlaj/src/components/count/postCountModel.vue
6 * @Description: 发布作业统计模型(包括感恩次数和感恩对象) 6 * @Description: 发布作业统计模型(包括感恩次数和感恩对象)
7 --> 7 -->
8 <template> 8 <template>
9 - <div v-if="postData.gratitude_count" class="post-count-model"> 9 + <div v-if="postData && postData.gratitude_count" class="post-count-model">
10 <div class="flex justify-between items-center mb-2"> 10 <div class="flex justify-between items-center mb-2">
11 <div class="text-gray-500">感恩次数: </div> 11 <div class="text-gray-500">感恩次数: </div>
12 <div class="font-bold text-gray-600">{{ postData.gratitude_count }} </div> 12 <div class="font-bold text-gray-600">{{ postData.gratitude_count }} </div>
13 </div> 13 </div>
14 <div class="flex justify-between items-center"> 14 <div class="flex justify-between items-center">
15 <div class="text-gray-500">感恩对象: </div> 15 <div class="text-gray-500">感恩对象: </div>
16 - <div class="font-bold text-gray-600">{{ postData.gratitude_form_list?.map(item => item.name).join('、') }}</div> 16 + <div class="font-bold text-gray-600">
17 + {{ Array.isArray(postData.gratitude_form_list) ? postData.gratitude_form_list.map(item => item.name).join('、') : '' }}
18 + </div>
17 </div> 19 </div>
18 </div> 20 </div>
19 </template> 21 </template>
20 22
21 <script setup> 23 <script setup>
22 -import { ref } from 'vue' 24 +import { defineProps } from 'vue'
23 -import { useRoute, useRouter } from 'vue-router'
24 25
25 const props = defineProps({ 26 const props = defineProps({
26 - postData: { 27 + /**
27 - type: Object, 28 + * 帖子数据对象
28 - default: () => ({}) 29 + * @property {number} gratitude_count - 感恩次数
29 - } 30 + * @property {Array} gratitude_form_list - 感恩对象列表 [{name: string}]
31 + */
32 + postData: {
33 + type: Object,
34 + default: () => ({})
35 + }
30 }) 36 })
31 </script> 37 </script>
32 38
......
...@@ -175,6 +175,11 @@ const onChange = async ({ value, selectedOptions, tabIndex }) => { ...@@ -175,6 +175,11 @@ const onChange = async ({ value, selectedOptions, tabIndex }) => {
175 } 175 }
176 } 176 }
177 177
178 +/**
179 + * @description 完成选择
180 + * @param {Object} params
181 + * @param {Array} params.selectedOptions - 选中的选项数组
182 + */
178 const onFinish = ({ selectedOptions }) => { 183 const onFinish = ({ selectedOptions }) => {
179 show.value = false 184 show.value = false
180 185
......
...@@ -95,9 +95,25 @@ import dayjs from 'dayjs' ...@@ -95,9 +95,25 @@ import dayjs from 'dayjs'
95 * @property {boolean} [plain] - 是否普通模式,默认 `false`。 95 * @property {boolean} [plain] - 是否普通模式,默认 `false`。
96 */ 96 */
97 const props = defineProps({ 97 const props = defineProps({
98 + /**
99 + * 打卡项列表
100 + * @type {CheckInItem[]}
101 + */
98 items: { type: Array, default: () => [] }, 102 items: { type: Array, default: () => [] },
103 + /**
104 + * 是否紧凑模式
105 + * @default false
106 + */
99 dense: { type: Boolean, default: false }, 107 dense: { type: Boolean, default: false },
108 + /**
109 + * 是否启用滚动区域
110 + * @default false
111 + */
100 scroll: { type: Boolean, default: false }, 112 scroll: { type: Boolean, default: false },
113 + /**
114 + * 是否普通模式
115 + * @default false
116 + */
101 plain: { type: Boolean, default: false }, 117 plain: { type: Boolean, default: false },
102 }) 118 })
103 119
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
2 * @Author: hookehuyr hookehuyr@gmail.com 2 * @Author: hookehuyr hookehuyr@gmail.com
3 * @Date: 2025-11-07 11:00:00 3 * @Date: 2025-11-07 11:00:00
4 * @LastEditors: hookehuyr hookehuyr@gmail.com 4 * @LastEditors: hookehuyr hookehuyr@gmail.com
5 - * @LastEditTime: 2025-12-18 23:11:35 5 + * @LastEditTime: 2026-01-22 17:14:40
6 * @FilePath: /mlaj/src/components/ui/CourseGroupCascader.vue 6 * @FilePath: /mlaj/src/components/ui/CourseGroupCascader.vue
7 * @Description: 教师页面筛选组件(年级/班级/课程),内部管理v-model与options并对外emit change事件 7 * @Description: 教师页面筛选组件(年级/班级/课程),内部管理v-model与options并对外emit change事件
8 --> 8 -->
...@@ -26,14 +26,17 @@ const emit = defineEmits(['change']) ...@@ -26,14 +26,17 @@ const emit = defineEmits(['change'])
26 26
27 // 接收外部传入的默认值 27 // 接收外部传入的默认值
28 const props = defineProps({ 28 const props = defineProps({
29 + /** 默认选中的课程ID */
29 defaultCourseId: { 30 defaultCourseId: {
30 type: [Number, String], 31 type: [Number, String],
31 default: null 32 default: null
32 }, 33 },
34 + /** 默认选中的年级ID */
33 defaultMajorGroupId: { 35 defaultMajorGroupId: {
34 type: [Number, String], 36 type: [Number, String],
35 default: null 37 default: null
36 }, 38 },
39 + /** 默认选中的班级ID */
37 defaultMinorGroupId: { 40 defaultMinorGroupId: {
38 type: [Number, String], 41 type: [Number, String],
39 default: null 42 default: null
...@@ -50,7 +53,12 @@ const course_option = ref([]) ...@@ -50,7 +53,12 @@ const course_option = ref([])
50 const major_group_option = ref([]) 53 const major_group_option = ref([])
51 const minor_group_option = ref([]) 54 const minor_group_option = ref([])
52 55
53 -// 获取筛选选项列表 56 +/**
57 + * @description 获取筛选选项列表
58 + * 根据传入的 group_id (年级) 和 team_id (班级 - 这里参数名可能需确认 API 定义,但按原代码逻辑)
59 + * @param {number|null} group_id 课程ID? 原代码参数名有些混淆,根据 usage 应该是课程ID
60 + * @param {number|null} team_id 年级ID
61 + */
54 const getFilterList = async (group_id = null, team_id = null) => { 62 const getFilterList = async (group_id = null, team_id = null) => {
55 const { code, data } = await getTeacherGradeClassListAPI({ group_id, team_id }); 63 const { code, data } = await getTeacherGradeClassListAPI({ group_id, team_id });
56 if (code === 1) { 64 if (code === 1) {
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
7 --> 7 -->
8 <template> 8 <template>
9 <div class="relative"> 9 <div class="relative">
10 - <!-- Live indicator --> 10 + <!-- 直播状态指示器 -->
11 <div class="absolute top-2 left-2 bg-red-500/90 text-white text-xs px-2 py-1 rounded flex items-center z-10"> 11 <div class="absolute top-2 left-2 bg-red-500/90 text-white text-xs px-2 py-1 rounded flex items-center z-10">
12 <div class="w-2 h-2 bg-white rounded-full mr-1 animate-pulse"></div> 12 <div class="w-2 h-2 bg-white rounded-full mr-1 animate-pulse"></div>
13 直播中 13 直播中
...@@ -20,10 +20,10 @@ ...@@ -20,10 +20,10 @@
20 class="w-full h-28 object-cover" 20 class="w-full h-28 object-cover"
21 /> 21 />
22 22
23 - <!-- Gradient overlay --> 23 + <!-- 渐变遮罩 -->
24 <div class="absolute inset-0 bg-gradient-to-b from-transparent to-black/60"></div> 24 <div class="absolute inset-0 bg-gradient-to-b from-transparent to-black/60"></div>
25 25
26 - <!-- Stream info --> 26 + <!-- 直播信息 -->
27 <div class="absolute bottom-2 left-2 right-2"> 27 <div class="absolute bottom-2 left-2 right-2">
28 <h3 class="text-white text-sm font-medium">{{ stream.title }}{{ stream.subtitle }}</h3> 28 <h3 class="text-white text-sm font-medium">{{ stream.title }}{{ stream.subtitle }}</h3>
29 <div class="flex items-center mt-2"> 29 <div class="flex items-center mt-2">
...@@ -44,7 +44,14 @@ ...@@ -44,7 +44,14 @@
44 44
45 <script setup> 45 <script setup>
46 defineProps({ 46 defineProps({
47 - /** 直播流数据对象 */ 47 + /**
48 + * 直播流数据对象
49 + * @property {string} id - 直播ID
50 + * @property {string} title - 标题
51 + * @property {string} subtitle - 副标题
52 + * @property {string} imageUrl - 封面图片URL
53 + * @property {number} [viewers] - 观看人数
54 + */
48 stream: { 55 stream: {
49 type: Object, 56 type: Object,
50 required: true 57 required: true
......
...@@ -289,6 +289,7 @@ const personType = ref('') // 动态表单字段中的person_type ...@@ -289,6 +289,7 @@ const personType = ref('') // 动态表单字段中的person_type
289 289
290 /** 290 /**
291 * 更新动态表单字段 291 * 更新动态表单字段
292 + * @description 根据选中的作业选项更新动态表单字段配置
292 * @param {Object} option - 选中的作业选项 293 * @param {Object} option - 选中的作业选项
293 */ 294 */
294 const updateDynamicFormFields = (option) => { 295 const updateDynamicFormFields = (option) => {
...@@ -322,7 +323,12 @@ const updateDynamicFormFields = (option) => { ...@@ -322,7 +323,12 @@ const updateDynamicFormFields = (option) => {
322 } 323 }
323 } 324 }
324 325
325 -// 确认作业选择 326 +/**
327 + * 确认作业选择
328 + * @description 处理作业选择器的确认事件,更新相关状态
329 + * @param {Object} param0 - 选择器返回对象
330 + * @param {Array} param0.selectedOptions - 选中的选项数组
331 + */
326 const onConfirmTask = async ({ selectedOptions }) => { 332 const onConfirmTask = async ({ selectedOptions }) => {
327 const option = selectedOptions[0] 333 const option = selectedOptions[0]
328 selectedTaskText.value = option.text 334 selectedTaskText.value = option.text
...@@ -386,6 +392,10 @@ const toggleTarget = (item) => { ...@@ -386,6 +392,10 @@ const toggleTarget = (item) => {
386 } 392 }
387 } 393 }
388 394
395 +/**
396 + * 打开新增计数对象弹窗
397 + * @description 重置编辑状态并显示弹窗
398 + */
389 const openAddTargetDialog = () => { 399 const openAddTargetDialog = () => {
390 editingTarget.value = null; // 重置编辑对象 400 editingTarget.value = null; // 重置编辑对象
391 isConfirmMode.value = false; 401 isConfirmMode.value = false;
...@@ -393,8 +403,9 @@ const openAddTargetDialog = () => { ...@@ -393,8 +403,9 @@ const openAddTargetDialog = () => {
393 } 403 }
394 404
395 /** 405 /**
396 - * 确认添加/编辑对象 406 + * 确认添加/编辑计数对象
397 - * @param {Array} formFields - 表单字段数组 407 + * @description 处理弹窗确认事件,更新本地列表和选中状态
408 + * @param {Array} formFields - 表单字段数组,包含字段ID和值
398 */ 409 */
399 const confirmAddTarget = async (formFields) => { 410 const confirmAddTarget = async (formFields) => {
400 // 将表单字段数组转换为对象 411 // 将表单字段数组转换为对象
...@@ -445,7 +456,9 @@ const confirmAddTarget = async (formFields) => { ...@@ -445,7 +456,9 @@ const confirmAddTarget = async (formFields) => {
445 } 456 }
446 457
447 /** 458 /**
448 - * 处理对象编辑 459 + * 处理计数对象编辑
460 + * @description 打开弹窗并填充当前对象数据进行编辑
461 + * @param {Object} item - 待编辑的计数对象
449 */ 462 */
450 const handleTargetEdit = (item) => { 463 const handleTargetEdit = (item) => {
451 editingTarget.value = item 464 editingTarget.value = item
...@@ -454,7 +467,9 @@ const handleTargetEdit = (item) => { ...@@ -454,7 +467,9 @@ const handleTargetEdit = (item) => {
454 } 467 }
455 468
456 /** 469 /**
457 - * 处理对象删除 470 + * 处理计数对象删除
471 + * @description 从本地列表和选中列表中移除对象(暂未调用后端接口)
472 + * @param {Object} item - 待删除的计数对象
458 */ 473 */
459 const handleTargetDelete = async (item) => { 474 const handleTargetDelete = async (item) => {
460 // 屏蔽删除功能, 那个接口也是不存在的 475 // 屏蔽删除功能, 那个接口也是不存在的
...@@ -478,6 +493,8 @@ const handleTargetDelete = async (item) => { ...@@ -478,6 +493,8 @@ const handleTargetDelete = async (item) => {
478 493
479 /** 494 /**
480 * 是否禁用提交按钮 495 * 是否禁用提交按钮
496 + * @description 根据打卡类型(计数/普通)和必填项(文本/文件/选中对象)判断是否可提交
497 + * @returns {boolean}
481 */ 498 */
482 const isSubmitDisabled = computed(() => { 499 const isSubmitDisabled = computed(() => {
483 // 1. 校验作业选择 500 // 1. 校验作业选择
...@@ -504,6 +521,8 @@ const isSubmitDisabled = computed(() => { ...@@ -504,6 +521,8 @@ const isSubmitDisabled = computed(() => {
504 521
505 /** 522 /**
506 * 提交打卡 523 * 提交打卡
524 + * @description 校验表单数据(作业选择、计数对象、必填项等),构建提交数据,调用 useCheckin 的 onSubmit 方法
525 + * @returns {Promise<void>}
507 */ 526 */
508 const handleSubmit = async () => { 527 const handleSubmit = async () => {
509 // 计数打卡校验 528 // 计数打卡校验
......