feat(打卡): 重构计数打卡功能,支持表单数据复用
- 修改计数对象为列表项,增加确认模式 - 重构动态表单字段处理逻辑 - 更新API接口参数为gratitude_form_list - 优化计数打卡的提交逻辑
Showing
6 changed files
with
145 additions
and
93 deletions
| 1 | /* | 1 | /* |
| 2 | * @Date: 2025-06-06 09:26:16 | 2 | * @Date: 2025-06-06 09:26:16 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-12-16 11:52:53 | 4 | + * @LastEditTime: 2025-12-16 17:54:43 |
| 5 | * @FilePath: /mlaj/src/api/checkin.js | 5 | * @FilePath: /mlaj/src/api/checkin.js |
| 6 | * @Description: 签到模块相关接口 | 6 | * @Description: 签到模块相关接口 |
| 7 | */ | 7 | */ |
| ... | @@ -22,6 +22,7 @@ const Api = { | ... | @@ -22,6 +22,7 @@ const Api = { |
| 22 | CHECKIN_TEACHER_LIST: '/srv/?a=checkin&t=teacher_list', | 22 | CHECKIN_TEACHER_LIST: '/srv/?a=checkin&t=teacher_list', |
| 23 | CHECKIN_TEACHER_REVIEW: '/srv/?a=checkin&t=teacher_review', | 23 | CHECKIN_TEACHER_REVIEW: '/srv/?a=checkin&t=teacher_review', |
| 24 | CHECKIN_TEACHER_CHECKED_DATES: '/srv/?a=checkin&t=teacher_checked_dates', | 24 | CHECKIN_TEACHER_CHECKED_DATES: '/srv/?a=checkin&t=teacher_checked_dates', |
| 25 | + CHECKIN_TEACHER_REUSE_GRATITUDE_FORM: '/srv/?a=checkin&t=reuse_gratitude_form', | ||
| 25 | } | 26 | } |
| 26 | 27 | ||
| 27 | /** | 28 | /** |
| ... | @@ -75,7 +76,7 @@ export const checkinTaskAPI = (params) => fn(fetch.post(Api.TASK_CHECKIN, param | ... | @@ -75,7 +76,7 @@ export const checkinTaskAPI = (params) => fn(fetch.post(Api.TASK_CHECKIN, param |
| 75 | * @param file_type 上传附件的类型 image=上传图片,video=视频,audio=音频 | 76 | * @param file_type 上传附件的类型 image=上传图片,video=视频,audio=音频 |
| 76 | * @param makeup_time 补卡时间 | 77 | * @param makeup_time 补卡时间 |
| 77 | * @param gratitude_count 感恩次数 | 78 | * @param gratitude_count 感恩次数 |
| 78 | - * @param gratitude_people_ids 感恩对象ID数组 [id1,id2,id3] | 79 | + * @param gratitude_form_list 感恩表单数据 [{id,name,city,unit,其他信息字段}] |
| 79 | * @returns | 80 | * @returns |
| 80 | */ | 81 | */ |
| 81 | export const addUploadTaskAPI = (params) => fn(fetch.post(Api.TASK_UPLOAD_ADD, params)) | 82 | export const addUploadTaskAPI = (params) => fn(fetch.post(Api.TASK_UPLOAD_ADD, params)) |
| ... | @@ -106,7 +107,7 @@ export const getUploadTaskListAPI = (params) => fn(fetch.post(Api.TASK_UPLOAD_L | ... | @@ -106,7 +107,7 @@ export const getUploadTaskListAPI = (params) => fn(fetch.post(Api.TASK_UPLOAD_L |
| 106 | * avatar 打卡人头像, created_time 打卡时间, created_time_desc 打卡时间描述, note 打卡内容, files[{meta_id,name,value,extension}] 附件列表, | 107 | * avatar 打卡人头像, created_time 打卡时间, created_time_desc 打卡时间描述, note 打卡内容, files[{meta_id,name,value,extension}] 附件列表, |
| 107 | * file_type 上传附件的类型 image=上传图片,video=视频,audio=音频, like_count 点赞数, is_my 是不是我的打卡, is_like 我是否已经点赞, is_makeup 是否补卡 | 108 | * file_type 上传附件的类型 image=上传图片,video=视频,audio=音频, like_count 点赞数, is_my 是不是我的打卡, is_like 我是否已经点赞, is_makeup 是否补卡 |
| 108 | * gratitude_count 感恩次数 | 109 | * gratitude_count 感恩次数 |
| 109 | - * gratitude_people 感恩对象列表 [{id,name,city,unit}] | 110 | + * gratitude_form_list 感恩表单数据 [{id,name,city,unit,其他信息字段}] |
| 110 | * } | 111 | * } |
| 111 | */ | 112 | */ |
| 112 | export const getUploadTaskInfoAPI = (params) => fn(fetch.get(Api.TASK_UPLOAD_INFO, params)) | 113 | export const getUploadTaskInfoAPI = (params) => fn(fetch.get(Api.TASK_UPLOAD_INFO, params)) |
| ... | @@ -118,7 +119,7 @@ export const getUploadTaskInfoAPI = (params) => fn(fetch.get(Api.TASK_UPLOAD_IN | ... | @@ -118,7 +119,7 @@ export const getUploadTaskInfoAPI = (params) => fn(fetch.get(Api.TASK_UPLOAD_IN |
| 118 | * @param meta_id[] 附件ID列表 | 119 | * @param meta_id[] 附件ID列表 |
| 119 | * @param file_type 上传附件的类型 image=上传图片,video=视频,audio=音频 | 120 | * @param file_type 上传附件的类型 image=上传图片,video=视频,audio=音频 |
| 120 | * @param gratitude_count 感恩次数 | 121 | * @param gratitude_count 感恩次数 |
| 121 | - * @param gratitude_people_ids 感恩对象ID数组 [id1,id2,id3] | 122 | + * @param gratitude_form_list 感恩表单数据 [{id,name,city,unit,其他信息字段}] |
| 122 | * @returns | 123 | * @returns |
| 123 | */ | 124 | */ |
| 124 | export const editUploadTaskInfoAPI = (params) => fn(fetch.get(Api.TASK_UPLOAD_EDIT, params)) | 125 | export const editUploadTaskInfoAPI = (params) => fn(fetch.get(Api.TASK_UPLOAD_EDIT, params)) |
| ... | @@ -183,3 +184,10 @@ export const checkinTaskReviewAPI = (params) => fn(fetch.post(Api.CHECKIN_TEACH | ... | @@ -183,3 +184,10 @@ export const checkinTaskReviewAPI = (params) => fn(fetch.post(Api.CHECKIN_TEACH |
| 183 | * @returns data: { my_checkin_dates 已打卡日期列表 } | 184 | * @returns data: { my_checkin_dates 已打卡日期列表 } |
| 184 | */ | 185 | */ |
| 185 | export const getCheckinTeacherCheckedDatesAPI = (params) => fn(fetch.get(Api.CHECKIN_TEACHER_CHECKED_DATES, params)) | 186 | export const getCheckinTeacherCheckedDatesAPI = (params) => fn(fetch.get(Api.CHECKIN_TEACHER_CHECKED_DATES, params)) |
| 187 | + | ||
| 188 | +/** | ||
| 189 | + * @description: 复用感恩表单数据 | ||
| 190 | + * @param subtask_id 小作业ID | ||
| 191 | + * @returns | ||
| 192 | + */ | ||
| 193 | +export const reuseGratitudeFormAPI = (params) => fn(fetch.post(Api.CHECKIN_TEACHER_REUSE_GRATITUDE_FORM, params)) | ... | ... |
| 1 | /* | 1 | /* |
| 2 | * @Date: 2025-06-06 09:26:16 | 2 | * @Date: 2025-06-06 09:26:16 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-12-16 13:59:55 | 4 | + * @LastEditTime: 2025-12-16 17:52:19 |
| 5 | * @FilePath: /mlaj/src/api/gratitude.js | 5 | * @FilePath: /mlaj/src/api/gratitude.js |
| 6 | * @Description: 计数模块相关接口 | 6 | * @Description: 计数模块相关接口 |
| 7 | */ | 7 | */ | ... | ... |
| ... | @@ -12,7 +12,7 @@ | ... | @@ -12,7 +12,7 @@ |
| 12 | <div v-for="field in localFields" :key="field.id"> | 12 | <div v-for="field in localFields" :key="field.id"> |
| 13 | <van-field | 13 | <van-field |
| 14 | v-model="field.value" | 14 | v-model="field.value" |
| 15 | - label-width="4rem" | 15 | + label-width="5rem" |
| 16 | :label="field.label" | 16 | :label="field.label" |
| 17 | :placeholder="'请输入' + field.label" | 17 | :placeholder="'请输入' + field.label" |
| 18 | :type="field.type === 'textarea' ? 'textarea' : 'text'" | 18 | :type="field.type === 'textarea' ? 'textarea' : 'text'" |
| ... | @@ -43,7 +43,7 @@ const props = defineProps({ | ... | @@ -43,7 +43,7 @@ const props = defineProps({ |
| 43 | */ | 43 | */ |
| 44 | title: { | 44 | title: { |
| 45 | type: String, | 45 | type: String, |
| 46 | - default: '添加对象' | 46 | + default: '添加列表项' |
| 47 | }, | 47 | }, |
| 48 | /** | 48 | /** |
| 49 | * 表单字段配置 | 49 | * 表单字段配置 |
| ... | @@ -101,14 +101,8 @@ const onBeforeClose = (action) => { | ... | @@ -101,14 +101,8 @@ const onBeforeClose = (action) => { |
| 101 | } | 101 | } |
| 102 | } | 102 | } |
| 103 | 103 | ||
| 104 | - // 收集表单数据 | 104 | + // 触发确认事件,传递表单数据 (保持与 fields 结构一致) |
| 105 | - const formData = localFields.value.reduce((acc, field) => { | 105 | + emit('confirm', localFields.value) |
| 106 | - acc[field.id] = field.value | ||
| 107 | - return acc | ||
| 108 | - }, {}) | ||
| 109 | - | ||
| 110 | - // 触发确认事件,传递表单数据 | ||
| 111 | - emit('confirm', formData) | ||
| 112 | return true // 允许关闭 | 106 | return true // 允许关闭 |
| 113 | } | 107 | } |
| 114 | return true // 取消时允许关闭 | 108 | return true // 取消时允许关闭 | ... | ... |
| 1 | <!-- | 1 | <!-- |
| 2 | * @Date: 2025-12-16 11:44:27 | 2 | * @Date: 2025-12-16 11:44:27 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-12-16 14:17:38 | 4 | + * @LastEditTime: 2025-12-16 17:59:29 |
| 5 | * @FilePath: /mlaj/src/components/count/CheckinTargetList.vue | 5 | * @FilePath: /mlaj/src/components/count/CheckinTargetList.vue |
| 6 | * @Description: 打卡动态对象列表组件 | 6 | * @Description: 打卡动态对象列表组件 |
| 7 | --> | 7 | --> |
| ... | @@ -9,7 +9,7 @@ | ... | @@ -9,7 +9,7 @@ |
| 9 | <div class="mb-4"> | 9 | <div class="mb-4"> |
| 10 | <div class="flex justify-between items-center mb-2 mx-2"> | 10 | <div class="flex justify-between items-center mb-2 mx-2"> |
| 11 | <div class="flex items-center gap-2"> | 11 | <div class="flex items-center gap-2"> |
| 12 | - <div class="text-sm font-bold text-gray-700">{{ dynamicFieldText }}对象</div> | 12 | + <div class="text-sm font-bold text-gray-700">{{ dynamicFieldText }}列表</div> |
| 13 | <div class="text-xs text-gray-400 font-normal scale-90 origin-left">(长按可编辑/删除)</div> | 13 | <div class="text-xs text-gray-400 font-normal scale-90 origin-left">(长按可编辑/删除)</div> |
| 14 | </div> | 14 | </div> |
| 15 | <van-button size="small" type="primary" plain icon="plus" @click="onAdd" class="!h-7">添加</van-button> | 15 | <van-button size="small" type="primary" plain icon="plus" @click="onAdd" class="!h-7">添加</van-button> |
| ... | @@ -49,7 +49,7 @@ | ... | @@ -49,7 +49,7 @@ |
| 49 | </div> | 49 | </div> |
| 50 | </template> | 50 | </template> |
| 51 | <div v-else class="w-full text-center py-4 text-gray-400 text-sm"> | 51 | <div v-else class="w-full text-center py-4 text-gray-400 text-sm"> |
| 52 | - 暂无{{ dynamicFieldText }}对象,请点击上方添加按钮 | 52 | + 暂无{{ dynamicFieldText }}列表,请点击上方添加按钮 |
| 53 | </div> | 53 | </div> |
| 54 | </div> | 54 | </div> |
| 55 | </div> | 55 | </div> | ... | ... |
| ... | @@ -307,13 +307,20 @@ export function useCheckin() { | ... | @@ -307,13 +307,20 @@ export function useCheckin() { |
| 307 | let result | 307 | let result |
| 308 | if (route.query.status === 'edit') { | 308 | if (route.query.status === 'edit') { |
| 309 | // 编辑打卡 | 309 | // 编辑打卡 |
| 310 | - result = await editUploadTaskInfoAPI({ | 310 | + const editData = { |
| 311 | i: route.query.post_id, | 311 | i: route.query.post_id, |
| 312 | subtask_id: submitData.subtask_id || route.query.subtask_id, | 312 | subtask_id: submitData.subtask_id || route.query.subtask_id, |
| 313 | note: submitData.note, | 313 | note: submitData.note, |
| 314 | meta_id: submitData.meta_id, | 314 | meta_id: submitData.meta_id, |
| 315 | file_type: submitData.file_type, | 315 | file_type: submitData.file_type, |
| 316 | - }) | 316 | + } |
| 317 | + | ||
| 318 | + // 如果有计数对象列表,也需要传递 | ||
| 319 | + if (submitData.gratitude_form_list) { | ||
| 320 | + editData.gratitude_form_list = submitData.gratitude_form_list | ||
| 321 | + } | ||
| 322 | + | ||
| 323 | + result = await editUploadTaskInfoAPI(editData) | ||
| 317 | } else { | 324 | } else { |
| 318 | // 新增打卡 | 325 | // 新增打卡 |
| 319 | result = await addUploadTaskAPI(submitData) | 326 | result = await addUploadTaskAPI(submitData) | ... | ... |
| ... | @@ -58,7 +58,7 @@ | ... | @@ -58,7 +58,7 @@ |
| 58 | <!-- 新增计数对象弹框 --> | 58 | <!-- 新增计数对象弹框 --> |
| 59 | <AddTargetDialog | 59 | <AddTargetDialog |
| 60 | v-model:show="showAddTargetDialog" | 60 | v-model:show="showAddTargetDialog" |
| 61 | - :title="editingTarget ? `编辑${dynamicFieldText}对象` : `添加${dynamicFieldText}对象`" | 61 | + :title="editingTarget ? (isConfirmMode ? `确认${dynamicFieldText}项` : `编辑${dynamicFieldText}项`) : `添加${dynamicFieldText}项`" |
| 62 | :fields="dynamicFormFields" | 62 | :fields="dynamicFormFields" |
| 63 | :initial-values="editingTarget" | 63 | :initial-values="editingTarget" |
| 64 | @confirm="confirmAddTarget" | 64 | @confirm="confirmAddTarget" |
| ... | @@ -174,9 +174,8 @@ | ... | @@ -174,9 +174,8 @@ |
| 174 | <script setup> | 174 | <script setup> |
| 175 | import { ref, computed, onMounted, nextTick, reactive, watch } from 'vue' | 175 | import { ref, computed, onMounted, nextTick, reactive, watch } from 'vue' |
| 176 | import { useRoute, useRouter } from 'vue-router' | 176 | import { useRoute, useRouter } from 'vue-router' |
| 177 | -import { getTaskDetailAPI, getUploadTaskInfoAPI, getSubtaskListAPI } from "@/api/checkin" | 177 | +import { getTaskDetailAPI, getUploadTaskInfoAPI, getSubtaskListAPI, reuseGratitudeFormAPI } from "@/api/checkin" |
| 178 | import { getTeacherFindSettingsAPI } from '@/api/teacher' | 178 | import { getTeacherFindSettingsAPI } from '@/api/teacher' |
| 179 | -import { getGratitudeListAPI, gratitudeAddAPI, gratitudeEditAPI, gratitudeDelAPI } from '@/api/gratitude' | ||
| 180 | import { useTitle } from '@vueuse/core' | 179 | import { useTitle } from '@vueuse/core' |
| 181 | import { useCheckin } from '@/composables/useCheckin' | 180 | import { useCheckin } from '@/composables/useCheckin' |
| 182 | import AudioPlayer from '@/components/ui/AudioPlayer.vue' | 181 | import AudioPlayer from '@/components/ui/AudioPlayer.vue' |
| ... | @@ -225,34 +224,48 @@ const taskType = computed(() => route.query.task_type) | ... | @@ -225,34 +224,48 @@ const taskType = computed(() => route.query.task_type) |
| 225 | const showTaskPicker = ref(false) | 224 | const showTaskPicker = ref(false) |
| 226 | const taskOptions = ref([]) | 225 | const taskOptions = ref([]) |
| 227 | 226 | ||
| 228 | -const fetchTargetList = async (person_type) => { | 227 | +const fetchTargetList = async (subtask_id) => { |
| 229 | - const { code, data } = await getGratitudeListAPI({ person_type }) | 228 | + const { code, data } = await reuseGratitudeFormAPI({ subtask_id }) |
| 230 | if (code) { | 229 | if (code) { |
| 231 | targetList.value = data.gratitude_people || [] | 230 | targetList.value = data.gratitude_people || [] |
| 232 | } | 231 | } |
| 232 | + | ||
| 233 | + targetList.value = [{ | ||
| 234 | + id: 1, | ||
| 235 | + name: '张三', | ||
| 236 | + city: '北京', | ||
| 237 | + unit: '公司', | ||
| 238 | + }, { | ||
| 239 | + id: 2, | ||
| 240 | + name: '李四', | ||
| 241 | + city: '上海', | ||
| 242 | + unit: '公司', | ||
| 243 | + }] | ||
| 233 | } | 244 | } |
| 234 | 245 | ||
| 235 | // 动态表单字段 (默认值,实际会根据选择的作业动态更新) | 246 | // 动态表单字段 (默认值,实际会根据选择的作业动态更新) |
| 236 | const dynamicFormFields = ref([]) | 247 | const dynamicFormFields = ref([]) |
| 237 | const personType = ref('') // 动态表单字段中的person_type | 248 | const personType = ref('') // 动态表单字段中的person_type |
| 238 | 249 | ||
| 239 | -// 确认作业选择 | 250 | +/** |
| 240 | -const onConfirmTask = ({ selectedOptions }) => { | 251 | + * 更新动态表单字段 |
| 241 | - const option = selectedOptions[0] | 252 | + * @param {Object} option - 选中的作业选项 |
| 242 | - selectedTaskText.value = option.text | 253 | + */ |
| 243 | - selectedTaskValue.value = [option.value] | 254 | +const updateDynamicFormFields = (option) => { |
| 244 | - isMakeup.value = !!option.is_makeup | ||
| 245 | - showTaskPicker.value = false | ||
| 246 | - personType.value = option.person_type | ||
| 247 | - | ||
| 248 | - // 动态表单字段映射 | ||
| 249 | if (option.field_list && Array.isArray(option.field_list)) { | 255 | if (option.field_list && Array.isArray(option.field_list)) { |
| 250 | - dynamicFormFields.value = option.field_list.map(field => ({ | 256 | + dynamicFormFields.value = option.field_list.map(field => { |
| 251 | - id: field.field_name, | 257 | + // 尝试多种方式获取ID |
| 252 | - label: field.label, | 258 | + const id = field.field_name || field.id || field.name || field.key || field.field |
| 253 | - type: 'text', // 默认类型,如果后端有类型字段可替换 | 259 | + if (!id) { |
| 254 | - required: true // 默认必填,如果后端有必填字段可替换 | 260 | + console.warn('动态表单字段缺少ID:', field) |
| 255 | - })) | 261 | + } |
| 262 | + return { | ||
| 263 | + id: id, | ||
| 264 | + label: field.label || '未命名', | ||
| 265 | + type: 'text', // 默认类型,如果后端有类型字段可替换 | ||
| 266 | + required: true // 默认必填,如果后端有必填字段可替换 | ||
| 267 | + } | ||
| 268 | + }) | ||
| 256 | // 确保如果有city字段,类型为textarea | 269 | // 确保如果有city字段,类型为textarea |
| 257 | const cityField = dynamicFormFields.value.find(f => f.id === 'city') | 270 | const cityField = dynamicFormFields.value.find(f => f.id === 'city') |
| 258 | if (cityField) { | 271 | if (cityField) { |
| ... | @@ -271,10 +284,23 @@ const onConfirmTask = ({ selectedOptions }) => { | ... | @@ -271,10 +284,23 @@ const onConfirmTask = ({ selectedOptions }) => { |
| 271 | { id: 'unit', label: '单位', type: 'textarea', required: true }, | 284 | { id: 'unit', label: '单位', type: 'textarea', required: true }, |
| 272 | ] | 285 | ] |
| 273 | } | 286 | } |
| 287 | +} | ||
| 288 | + | ||
| 289 | +// 确认作业选择 | ||
| 290 | +const onConfirmTask = ({ selectedOptions }) => { | ||
| 291 | + const option = selectedOptions[0] | ||
| 292 | + selectedTaskText.value = option.text | ||
| 293 | + selectedTaskValue.value = [option.value] | ||
| 294 | + isMakeup.value = !!option.is_makeup | ||
| 295 | + showTaskPicker.value = false | ||
| 296 | + personType.value = option.person_type | ||
| 297 | + | ||
| 298 | + // 更新动态表单字段 | ||
| 299 | + updateDynamicFormFields(option) | ||
| 274 | 300 | ||
| 275 | // 如果是计数打卡,根据选中的作业ID查询计数对象 | 301 | // 如果是计数打卡,根据选中的作业ID查询计数对象 |
| 276 | if (taskType.value === 'count') { | 302 | if (taskType.value === 'count') { |
| 277 | - fetchTargetList(personType.value) | 303 | + fetchTargetList(selectedTaskValue.value[0]) |
| 278 | } | 304 | } |
| 279 | } | 305 | } |
| 280 | 306 | ||
| ... | @@ -293,62 +319,76 @@ const selectedTargets = ref([]) | ... | @@ -293,62 +319,76 @@ const selectedTargets = ref([]) |
| 293 | const targetList = ref([]) | 319 | const targetList = ref([]) |
| 294 | const showAddTargetDialog = ref(false) | 320 | const showAddTargetDialog = ref(false) |
| 295 | const editingTarget = ref(null) | 321 | const editingTarget = ref(null) |
| 322 | +const isConfirmMode = ref(false) // 是否为确认模式(首次点击选中) | ||
| 296 | 323 | ||
| 297 | const toggleTarget = (item) => { | 324 | const toggleTarget = (item) => { |
| 298 | const index = selectedTargets.value.findIndex(t => t.name === item.name) | 325 | const index = selectedTargets.value.findIndex(t => t.name === item.name) |
| 299 | if (index > -1) { | 326 | if (index > -1) { |
| 327 | + // 取消选中 | ||
| 300 | selectedTargets.value.splice(index, 1) | 328 | selectedTargets.value.splice(index, 1) |
| 301 | } else { | 329 | } else { |
| 302 | - selectedTargets.value.push(item) | 330 | + // 选中逻辑:如果是第一次选中(未确认过),则弹出确认框 |
| 331 | + if (!item.has_confirmed) { | ||
| 332 | + editingTarget.value = item | ||
| 333 | + isConfirmMode.value = true | ||
| 334 | + showAddTargetDialog.value = true | ||
| 335 | + } else { | ||
| 336 | + // 已确认过,直接选中 | ||
| 337 | + selectedTargets.value.push(item) | ||
| 338 | + } | ||
| 303 | } | 339 | } |
| 304 | } | 340 | } |
| 305 | 341 | ||
| 306 | const openAddTargetDialog = () => { | 342 | const openAddTargetDialog = () => { |
| 307 | editingTarget.value = null; // 重置编辑对象 | 343 | editingTarget.value = null; // 重置编辑对象 |
| 344 | + isConfirmMode.value = false; | ||
| 308 | showAddTargetDialog.value = true; | 345 | showAddTargetDialog.value = true; |
| 309 | } | 346 | } |
| 310 | 347 | ||
| 311 | /** | 348 | /** |
| 312 | * 确认添加/编辑对象 | 349 | * 确认添加/编辑对象 |
| 313 | - * @param {Object} formData - 表单数据 | 350 | + * @param {Array} formFields - 表单字段数组 |
| 314 | */ | 351 | */ |
| 315 | -const confirmAddTarget = async (formData) => { | 352 | +const confirmAddTarget = async (formFields) => { |
| 316 | - console.log(`${editingTarget.value ? '编辑' : '新增'}${dynamicFieldText.value}对象信息:`, formData) | 353 | + // 将表单字段数组转换为对象 |
| 354 | + const formData = formFields.reduce((acc, field) => { | ||
| 355 | + if (field.id) { | ||
| 356 | + acc[field.id] = field.value | ||
| 357 | + } | ||
| 358 | + return acc | ||
| 359 | + }, {}) | ||
| 317 | 360 | ||
| 318 | if (editingTarget.value) { | 361 | if (editingTarget.value) { |
| 319 | - // 编辑模式 | 362 | + // 编辑模式或确认模式 |
| 320 | const index = targetList.value.findIndex(t => t === editingTarget.value) | 363 | const index = targetList.value.findIndex(t => t === editingTarget.value) |
| 321 | if (index > -1) { | 364 | if (index > -1) { |
| 322 | - const { code } = await gratitudeEditAPI({ ...editingTarget.value }) | 365 | + // 更新对象 |
| 323 | - if (code) { | 366 | + targetList.value[index] = { ...targetList.value[index], ...formData } |
| 324 | - // 更新对象 | 367 | + if (isConfirmMode.value) { |
| 325 | - targetList.value[index] = { ...targetList.value[index], ...formData } | 368 | + targetList.value[index].has_confirmed = true // 标记为已确认 |
| 326 | - | ||
| 327 | - // 如果在选中列表中,也需要更新 | ||
| 328 | - const selectedIndex = selectedTargets.value.findIndex(t => t.id === editingTarget.value.id) | ||
| 329 | - if (selectedIndex > -1) { | ||
| 330 | - selectedTargets.value[selectedIndex] = { ...selectedTargets.value[selectedIndex], ...formData } | ||
| 331 | - } | ||
| 332 | } | 369 | } |
| 333 | - } | 370 | + |
| 334 | - showToast('修改成功') | 371 | + // 如果在选中列表中,也需要更新 |
| 335 | - } else { | 372 | + const selectedIndex = selectedTargets.value.findIndex(t => t.id === editingTarget.value.id) |
| 336 | - // 新增模式 | 373 | + if (selectedIndex > -1) { |
| 337 | - try { | 374 | + selectedTargets.value[selectedIndex] = { ...selectedTargets.value[selectedIndex], ...formData } |
| 338 | - const res = await gratitudeAddAPI({ | 375 | + } |
| 339 | - ...formData, | 376 | + |
| 340 | - person_type: personType.value | 377 | + // 如果是确认模式,确认后自动加入选中列表 |
| 341 | - }) | 378 | + if (isConfirmMode.value && selectedIndex === -1) { |
| 342 | - if (res.code) { | 379 | + selectedTargets.value.push(targetList.value[index]) |
| 343 | - // 新增成功,更新本地列表 | ||
| 344 | - targetList.value.push({ | ||
| 345 | - ...formData, | ||
| 346 | - }) | ||
| 347 | - showToast('新增成功') | ||
| 348 | } | 380 | } |
| 349 | - } catch (error) { | 381 | + |
| 350 | - showToast(`新增失败:${error.message || '未知错误'}`) | 382 | + showToast(isConfirmMode.value ? '确认成功' : '修改成功') |
| 351 | } | 383 | } |
| 384 | + } else { | ||
| 385 | + // 新增成功,更新本地列表 | ||
| 386 | + targetList.value.push({ | ||
| 387 | + ...formData, | ||
| 388 | + }) | ||
| 389 | + console.warn(formData); | ||
| 390 | + console.warn(targetList.value); | ||
| 391 | + showToast('新增成功') | ||
| 352 | } | 392 | } |
| 353 | 393 | ||
| 354 | showAddTargetDialog.value = false; | 394 | showAddTargetDialog.value = false; |
| ... | @@ -359,6 +399,7 @@ const confirmAddTarget = async (formData) => { | ... | @@ -359,6 +399,7 @@ const confirmAddTarget = async (formData) => { |
| 359 | */ | 399 | */ |
| 360 | const handleTargetEdit = (item) => { | 400 | const handleTargetEdit = (item) => { |
| 361 | editingTarget.value = item | 401 | editingTarget.value = item |
| 402 | + isConfirmMode.value = false // 明确设置为非确认模式 | ||
| 362 | showAddTargetDialog.value = true | 403 | showAddTargetDialog.value = true |
| 363 | } | 404 | } |
| 364 | 405 | ||
| ... | @@ -410,31 +451,31 @@ const isSubmitDisabled = computed(() => { | ... | @@ -410,31 +451,31 @@ const isSubmitDisabled = computed(() => { |
| 410 | } | 451 | } |
| 411 | }) | 452 | }) |
| 412 | 453 | ||
| 454 | +/** | ||
| 455 | + * 提交打卡 | ||
| 456 | + */ | ||
| 413 | const handleSubmit = async () => { | 457 | const handleSubmit = async () => { |
| 414 | - // 1. 校验作业选择 | 458 | + // 计数打卡校验 |
| 415 | - if (!selectedTaskValue.value || selectedTaskValue.value.length === 0) { | ||
| 416 | - showToast('请选择作业') | ||
| 417 | - return | ||
| 418 | - } | ||
| 419 | - | ||
| 420 | - const extraData = { | ||
| 421 | - subtask_id: selectedTaskValue.value[0] // 小作业ID | ||
| 422 | - } | ||
| 423 | - | ||
| 424 | - // 2. 计数打卡特定校验 | ||
| 425 | if (taskType.value === 'count') { | 459 | if (taskType.value === 'count') { |
| 426 | - if (selectedTargets.value.length === 0) { | 460 | + if (selectedTaskValue.value.length === 0) { |
| 427 | - showToast(`请选择${dynamicFieldText.value}对象`) | 461 | + const taskText = taskOptions.value.find(t => t.value === selectedTaskValue.value[0])?.text || '作业' |
| 462 | + showToast(`请选择${taskText}`) | ||
| 428 | return | 463 | return |
| 429 | } | 464 | } |
| 430 | - if (!countValue.value || countValue.value <= 0) { | 465 | + if (selectedTargets.value.length === 0) { |
| 431 | - showToast(`${dynamicFieldText.value}次数必须大于0`) | 466 | + const targetText = dynamicFieldText.value || '对象' |
| 467 | + showToast(`请选择${targetText}`) | ||
| 432 | return | 468 | return |
| 433 | } | 469 | } |
| 470 | + } | ||
| 471 | + | ||
| 472 | + const extraData = { | ||
| 473 | + subtask_id: selectedTaskValue.value.length > 0 ? selectedTaskValue.value[0] : '' | ||
| 474 | + } | ||
| 434 | 475 | ||
| 435 | - // 传递额外数据 | 476 | + // 如果是计数打卡,添加选中的计数对象列表 |
| 436 | - extraData.targets = selectedTargets.value.map(t => t.id) | 477 | + if (taskType.value === 'count') { |
| 437 | - extraData.count = countValue.value | 478 | + extraData.gratitude_form_list = selectedTargets.value |
| 438 | } | 479 | } |
| 439 | 480 | ||
| 440 | await onSubmit(extraData) | 481 | await onSubmit(extraData) |
| ... | @@ -883,11 +924,13 @@ onMounted(async () => { | ... | @@ -883,11 +924,13 @@ onMounted(async () => { |
| 883 | selectedTaskText.value = option.text | 924 | selectedTaskText.value = option.text |
| 884 | isMakeup.value = !!option.is_makeup | 925 | isMakeup.value = !!option.is_makeup |
| 885 | personType.value = option.person_type | 926 | personType.value = option.person_type |
| 927 | + // 初始化动态表单字段 | ||
| 928 | + updateDynamicFormFields(option) | ||
| 886 | } | 929 | } |
| 887 | 930 | ||
| 888 | // 如果是计数打卡,根据选中的作业ID查询计数对象 | 931 | // 如果是计数打卡,根据选中的作业ID查询计数对象 |
| 889 | if (taskType.value === 'count') { | 932 | if (taskType.value === 'count') { |
| 890 | - fetchTargetList(personType.value) | 933 | + fetchTargetList(selectedTaskValue.value[0]) |
| 891 | } | 934 | } |
| 892 | } | 935 | } |
| 893 | 936 | ... | ... |
-
Please register or login to post a comment