docs: 为多个组件添加JSDoc注释和中文注释
为TaskCascaderFilter、AddTargetDialog、CheckInList等组件添加详细的JSDoc注释 将LiveStreamCard组件的英文注释转换为中文 完善postCountModel组件的类型检查和空值处理 为CheckinDetailPage的关键方法添加详细说明
Showing
8 changed files
with
91 additions
and
18 deletions
| ... | @@ -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({ |
| 27 | + /** | ||
| 28 | + * 帖子数据对象 | ||
| 29 | + * @property {number} gratitude_count - 感恩次数 | ||
| 30 | + * @property {Array} gratitude_form_list - 感恩对象列表 [{name: string}] | ||
| 31 | + */ | ||
| 26 | postData: { | 32 | postData: { |
| 27 | type: Object, | 33 | type: Object, |
| 28 | default: () => ({}) | 34 | default: () => ({}) | ... | ... |
| ... | @@ -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 | // 计数打卡校验 | ... | ... |
-
Please register or login to post a comment