fix(checkin): allow task description rich text to wrap
Override inline rich-text no-wrap styles so long assignment descriptions render fully in the checkin detail page. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Showing
1 changed file
with
282 additions
and
197 deletions
| ... | @@ -13,11 +13,8 @@ | ... | @@ -13,11 +13,8 @@ |
| 13 | <div class="section-wrapper"> | 13 | <div class="section-wrapper"> |
| 14 | <div class="section-title">作业描述</div> | 14 | <div class="section-title">作业描述</div> |
| 15 | <div class="section-content"> | 15 | <div class="section-content"> |
| 16 | - <div v-if="displayTaskNote" class="description-text" v-html="displayTaskNote"> | 16 | + <div v-if="displayTaskNote" class="description-text" v-html="displayTaskNote"></div> |
| 17 | - </div> | 17 | + <div v-else class="no-description">暂无作业描述</div> |
| 18 | - <div v-else class="no-description"> | ||
| 19 | - 暂无作业描述 | ||
| 20 | - </div> | ||
| 21 | </div> | 18 | </div> |
| 22 | </div> | 19 | </div> |
| 23 | 20 | ||
| ... | @@ -28,18 +25,31 @@ | ... | @@ -28,18 +25,31 @@ |
| 28 | <!-- 作业选择区域 --> | 25 | <!-- 作业选择区域 --> |
| 29 | <div class="mb-4"> | 26 | <div class="mb-4"> |
| 30 | <!-- 编辑模式下直接显示文本 --> | 27 | <!-- 编辑模式下直接显示文本 --> |
| 31 | - <div v-if="isEditMode" class="bg-gray-50 rounded-lg p-3 border border-gray-100 flex items-center justify-between"> | 28 | + <div |
| 32 | - <span class="text-gray-700 font-medium">当前作业</span> | 29 | + v-if="isEditMode" |
| 33 | - <span class="text-gray-900 font-bold">{{ selectedTaskText }}</span> | 30 | + class="flex items-center justify-between rounded-lg border border-gray-100 bg-gray-50 p-3" |
| 31 | + > | ||
| 32 | + <span class="font-medium text-gray-700">当前作业</span> | ||
| 33 | + <span class="font-bold text-gray-900">{{ selectedTaskText }}</span> | ||
| 34 | </div> | 34 | </div> |
| 35 | 35 | ||
| 36 | <!-- 非编辑模式下显示选择框 --> | 36 | <!-- 非编辑模式下显示选择框 --> |
| 37 | <template v-else> | 37 | <template v-else> |
| 38 | - <van-field v-model="selectedTaskText" is-link readonly label="选择作业" placeholder="请选择本次打卡的作业" | 38 | + <van-field |
| 39 | - @click="showTaskPicker = true" class="rounded-lg border border-gray-100" /> | 39 | + v-model="selectedTaskText" |
| 40 | + is-link | ||
| 41 | + readonly | ||
| 42 | + label="选择作业" | ||
| 43 | + placeholder="请选择本次打卡的作业" | ||
| 44 | + @click="showTaskPicker = true" | ||
| 45 | + class="rounded-lg border border-gray-100" | ||
| 46 | + /> | ||
| 40 | <van-popup v-model:show="showTaskPicker" round position="bottom"> | 47 | <van-popup v-model:show="showTaskPicker" round position="bottom"> |
| 41 | - <van-picker :columns="taskOptions" @cancel="showTaskPicker = false" | 48 | + <van-picker |
| 42 | - @confirm="onConfirmTask" /> | 49 | + :columns="taskOptions" |
| 50 | + @cancel="showTaskPicker = false" | ||
| 51 | + @confirm="onConfirmTask" | ||
| 52 | + /> | ||
| 43 | </van-popup> | 53 | </van-popup> |
| 44 | </template> | 54 | </template> |
| 45 | </div> | 55 | </div> |
| ... | @@ -56,16 +66,30 @@ | ... | @@ -56,16 +66,30 @@ |
| 56 | /> | 66 | /> |
| 57 | 67 | ||
| 58 | <!-- 计数次数 --> | 68 | <!-- 计数次数 --> |
| 59 | - <div v-if="taskType === 'count'" | 69 | + <div |
| 60 | - class="mb-4 flex items-center justify-between bg-gray-50 p-3 rounded-lg"> | 70 | + v-if="taskType === 'count'" |
| 71 | + class="mb-4 flex items-center justify-between rounded-lg bg-gray-50 p-3" | ||
| 72 | + > | ||
| 61 | <div class="text-sm font-bold text-gray-700">{{ dynamicFieldText }}次数</div> | 73 | <div class="text-sm font-bold text-gray-700">{{ dynamicFieldText }}次数</div> |
| 62 | - <van-stepper v-model="countValue" min="1" integer input-width="80px" button-size="28px" /> | 74 | + <van-stepper |
| 75 | + v-model="countValue" | ||
| 76 | + min="1" | ||
| 77 | + integer | ||
| 78 | + input-width="80px" | ||
| 79 | + button-size="28px" | ||
| 80 | + /> | ||
| 63 | </div> | 81 | </div> |
| 64 | 82 | ||
| 65 | <!-- 新增计数对象弹框 --> | 83 | <!-- 新增计数对象弹框 --> |
| 66 | <AddTargetDialog | 84 | <AddTargetDialog |
| 67 | v-model:show="showAddTargetDialog" | 85 | v-model:show="showAddTargetDialog" |
| 68 | - :title="editingTarget ? (isConfirmMode ? `确认${dynamicFieldText}项` : `编辑${dynamicFieldText}项`) : `添加${dynamicFieldText}项`" | 86 | + :title=" |
| 87 | + editingTarget | ||
| 88 | + ? isConfirmMode | ||
| 89 | + ? `确认${dynamicFieldText}项` | ||
| 90 | + : `编辑${dynamicFieldText}项` | ||
| 91 | + : `添加${dynamicFieldText}项` | ||
| 92 | + " | ||
| 69 | :fields="dynamicFormFields" | 93 | :fields="dynamicFormFields" |
| 70 | :initial-values="editingTarget" | 94 | :initial-values="editingTarget" |
| 71 | @confirm="confirmAddTarget" | 95 | @confirm="confirmAddTarget" |
| ... | @@ -73,23 +97,47 @@ | ... | @@ -73,23 +97,47 @@ |
| 73 | 97 | ||
| 74 | <!-- 文本输入区域 --> | 98 | <!-- 文本输入区域 --> |
| 75 | <div class="text-input-area"> | 99 | <div class="text-input-area"> |
| 76 | - <van-field v-model="message" rows="6" autosize type="textarea" | 100 | + <van-field |
| 77 | - :placeholder="taskType === 'count' ? '请输入留言(可选)' : (activeType === 'text' ? '请输入留言,至少需要10个字符' : '请输入留言(可选)')" /> | 101 | + v-model="message" |
| 102 | + rows="6" | ||
| 103 | + autosize | ||
| 104 | + type="textarea" | ||
| 105 | + :placeholder=" | ||
| 106 | + taskType === 'count' | ||
| 107 | + ? '请输入留言(可选)' | ||
| 108 | + : activeType === 'text' | ||
| 109 | + ? '请输入留言,至少需要10个字符' | ||
| 110 | + : '请输入留言(可选)' | ||
| 111 | + " | ||
| 112 | + /> | ||
| 78 | </div> | 113 | </div> |
| 79 | 114 | ||
| 80 | <!-- 类型选项卡 --> | 115 | <!-- 类型选项卡 --> |
| 81 | <div class="checkin-tabs" v-if="selectedTaskValue.length > 0"> | 116 | <div class="checkin-tabs" v-if="selectedTaskValue.length > 0"> |
| 82 | <div class="tabs-header"> | 117 | <div class="tabs-header"> |
| 83 | - <div class="tab-title">{{ taskType === 'count' ? '附件类型(可选)' : '附件类型' }}</div> | 118 | + <div class="tab-title"> |
| 119 | + {{ taskType === 'count' ? '附件类型(可选)' : '附件类型' }} | ||
| 120 | + </div> | ||
| 84 | <div class="tabs-nav"> | 121 | <div class="tabs-nav"> |
| 85 | - <div v-for="option in attachmentTypeOptions" :key="option.key" | 122 | + <div |
| 86 | - @click="switchType(option.key)" :class="['tab-item', 'relative', { | 123 | + v-for="option in attachmentTypeOptions" |
| 87 | - active: activeType === option.key | 124 | + :key="option.key" |
| 88 | - }]"> | 125 | + @click="switchType(option.key)" |
| 126 | + :class="[ | ||
| 127 | + 'tab-item', | ||
| 128 | + 'relative', | ||
| 129 | + { | ||
| 130 | + active: activeType === option.key, | ||
| 131 | + }, | ||
| 132 | + ]" | ||
| 133 | + > | ||
| 89 | <van-icon :name="getIconName(option.key)" size="1.2rem" /> | 134 | <van-icon :name="getIconName(option.key)" size="1.2rem" /> |
| 90 | <span class="tab-text">{{ option.value }}</span> | 135 | <span class="tab-text">{{ option.value }}</span> |
| 91 | <!-- <div v-if="multiAttachmentEnabled && getTypeCount(option.key) > 0" class="absolute -top-2 -right-2 bg-red-500 text-white text-[10px] rounded-full min-w-[16px] h-[16px] flex items-center justify-center px-1"> --> | 136 | <!-- <div v-if="multiAttachmentEnabled && getTypeCount(option.key) > 0" class="absolute -top-2 -right-2 bg-red-500 text-white text-[10px] rounded-full min-w-[16px] h-[16px] flex items-center justify-center px-1"> --> |
| 92 | - <div v-if="getTypeCount(option.key) > 0" class="absolute -top-2 -right-2 bg-red-500 text-white text-[10px] rounded-full min-w-[16px] h-[16px] flex items-center justify-center px-1"> | 137 | + <div |
| 138 | + v-if="getTypeCount(option.key) > 0" | ||
| 139 | + class="absolute -right-2 -top-2 flex h-[16px] min-w-[16px] items-center justify-center rounded-full bg-red-500 px-1 text-[10px] text-white" | ||
| 140 | + > | ||
| 93 | {{ getTypeCount(option.key) }} | 141 | {{ getTypeCount(option.key) }} |
| 94 | </div> | 142 | </div> |
| 95 | </div> | 143 | </div> |
| ... | @@ -98,10 +146,20 @@ | ... | @@ -98,10 +146,20 @@ |
| 98 | 146 | ||
| 99 | <!-- 文件上传区域 --> | 147 | <!-- 文件上传区域 --> |
| 100 | <div v-if="activeType !== '' && activeType !== 'text'" class="upload-area"> | 148 | <div v-if="activeType !== '' && activeType !== 'text'" class="upload-area"> |
| 101 | - <van-uploader v-model="displayFileList" :max-count="maxCount" :max-size="maxFileSizeBytes" | 149 | + <van-uploader |
| 102 | - :before-read="beforeReadGuard" :after-read="afterRead" @delete="onDelete" | 150 | + v-model="displayFileList" |
| 103 | - @click-preview="onClickPreview" multiple :accept="getAcceptType()" result-type="file" | 151 | + :max-count="maxCount" |
| 104 | - :deletable="true" upload-icon="plus" /> | 152 | + :max-size="maxFileSizeBytes" |
| 153 | + :before-read="beforeReadGuard" | ||
| 154 | + :after-read="afterRead" | ||
| 155 | + @delete="onDelete" | ||
| 156 | + @click-preview="onClickPreview" | ||
| 157 | + multiple | ||
| 158 | + :accept="getAcceptType()" | ||
| 159 | + result-type="file" | ||
| 160 | + :deletable="true" | ||
| 161 | + upload-icon="plus" | ||
| 162 | + /> | ||
| 105 | 163 | ||
| 106 | <!-- 文件列表显示 --> | 164 | <!-- 文件列表显示 --> |
| 107 | <!-- <div v-if="fileList.length > 0" class="file-list"> | 165 | <!-- <div v-if="fileList.length > 0" class="file-list"> |
| ... | @@ -116,7 +174,9 @@ | ... | @@ -116,7 +174,9 @@ |
| 116 | </div> --> | 174 | </div> --> |
| 117 | 175 | ||
| 118 | <div class="upload-tips"> | 176 | <div class="upload-tips"> |
| 119 | - <div class="tip-text">最多上传{{ maxCount }}个文件,每个不超过{{ maxFileSizeMb }}MB</div> | 177 | + <div class="tip-text"> |
| 178 | + 最多上传{{ maxCount }}个文件,每个不超过{{ maxFileSizeMb }}MB | ||
| 179 | + </div> | ||
| 120 | <div class="tip-text">{{ getUploadTips() }}</div> | 180 | <div class="tip-text">{{ getUploadTips() }}</div> |
| 121 | </div> | 181 | </div> |
| 122 | </div> | 182 | </div> |
| ... | @@ -126,7 +186,14 @@ | ... | @@ -126,7 +186,14 @@ |
| 126 | 186 | ||
| 127 | <!-- 提交按钮 --> | 187 | <!-- 提交按钮 --> |
| 128 | <div v-if="!taskDetail.is_finish || isEditMode" class="submit-area"> | 188 | <div v-if="!taskDetail.is_finish || isEditMode" class="submit-area"> |
| 129 | - <van-button type="primary" block size="large" :loading="uploading" :disabled="isSubmitDisabled" @click="handleSubmit"> | 189 | + <van-button |
| 190 | + type="primary" | ||
| 191 | + block | ||
| 192 | + size="large" | ||
| 193 | + :loading="uploading" | ||
| 194 | + :disabled="isSubmitDisabled" | ||
| 195 | + @click="handleSubmit" | ||
| 196 | + > | ||
| 130 | {{ isEditMode ? '保存修改' : '提交' }} | 197 | {{ isEditMode ? '保存修改' : '提交' }} |
| 131 | </van-button> | 198 | </van-button> |
| 132 | </div> | 199 | </div> |
| ... | @@ -140,50 +207,89 @@ | ... | @@ -140,50 +207,89 @@ |
| 140 | </van-overlay> | 207 | </van-overlay> |
| 141 | 208 | ||
| 142 | <!-- 音频播放器弹窗 --> | 209 | <!-- 音频播放器弹窗 --> |
| 143 | - <van-popup v-model:show="audioShow" position="bottom" round closeable :style="{ height: '60%', width: '100%' }"> | 210 | + <van-popup |
| 211 | + v-model:show="audioShow" | ||
| 212 | + position="bottom" | ||
| 213 | + round | ||
| 214 | + closeable | ||
| 215 | + :style="{ height: '60%', width: '100%' }" | ||
| 216 | + > | ||
| 144 | <div class="p-4"> | 217 | <div class="p-4"> |
| 145 | - <h3 class="text-lg font-medium mb-4 text-center">{{ audioTitle }}</h3> | 218 | + <h3 class="mb-4 text-center text-lg font-medium">{{ audioTitle }}</h3> |
| 146 | - <AudioPlayer v-if="audioShow && audioUrl" :songs="[{ title: audioTitle, url: audioUrl }]" | 219 | + <AudioPlayer |
| 147 | - class="w-full" /> | 220 | + v-if="audioShow && audioUrl" |
| 221 | + :songs="[{ title: audioTitle, url: audioUrl }]" | ||
| 222 | + class="w-full" | ||
| 223 | + /> | ||
| 148 | </div> | 224 | </div> |
| 149 | </van-popup> | 225 | </van-popup> |
| 150 | 226 | ||
| 151 | <!-- 视频播放器弹窗 --> | 227 | <!-- 视频播放器弹窗 --> |
| 152 | - <van-popup v-model:show="videoShow" position="center" round closeable | 228 | + <van-popup |
| 153 | - :style="{ width: '95%', maxHeight: '80vh' }" @close="stopVideoPlay"> | 229 | + v-model:show="videoShow" |
| 230 | + position="center" | ||
| 231 | + round | ||
| 232 | + closeable | ||
| 233 | + :style="{ width: '95%', maxHeight: '80vh' }" | ||
| 234 | + @close="stopVideoPlay" | ||
| 235 | + > | ||
| 154 | <div class="p-4"> | 236 | <div class="p-4"> |
| 155 | - <h3 class="text-lg font-medium mb-4 text-center">视频预览</h3> | 237 | + <h3 class="mb-4 text-center text-lg font-medium">视频预览</h3> |
| 156 | - <div class="relative w-full bg-black rounded-lg overflow-hidden" style="aspect-ratio: 16/9;"> | 238 | + <div class="relative w-full overflow-hidden rounded-lg bg-black" style="aspect-ratio: 16/9"> |
| 157 | <!-- 视频封面 --> | 239 | <!-- 视频封面 --> |
| 158 | - <div v-show="!isVideoPlaying" | 240 | + <div |
| 159 | - class="absolute inset-0 flex items-center justify-center cursor-pointer" | 241 | + v-show="!isVideoPlaying" |
| 160 | - @click="startVideoPlay"> | 242 | + class="absolute inset-0 flex cursor-pointer items-center justify-center" |
| 161 | - <img :src="videoCover || 'https://cdn.ipadbiz.cn/mlaj/images/cover_video_2.png'" | 243 | + @click="startVideoPlay" |
| 162 | - :alt="videoTitle" class="w-full h-full object-cover" /> | 244 | + > |
| 245 | + <img | ||
| 246 | + :src="videoCover || 'https://cdn.ipadbiz.cn/mlaj/images/cover_video_2.png'" | ||
| 247 | + :alt="videoTitle" | ||
| 248 | + class="h-full w-full object-cover" | ||
| 249 | + /> | ||
| 163 | <div class="absolute inset-0 flex items-center justify-center bg-black/20"> | 250 | <div class="absolute inset-0 flex items-center justify-center bg-black/20"> |
| 164 | <div | 251 | <div |
| 165 | - class="w-16 h-16 rounded-full bg-black/50 flex items-center justify-center hover:bg-black/70 transition-colors"> | 252 | + class="flex h-16 w-16 items-center justify-center rounded-full bg-black/50 transition-colors hover:bg-black/70" |
| 253 | + > | ||
| 166 | <van-icon name="play-circle-o" class="text-white" size="40" /> | 254 | <van-icon name="play-circle-o" class="text-white" size="40" /> |
| 167 | </div> | 255 | </div> |
| 168 | </div> | 256 | </div> |
| 169 | </div> | 257 | </div> |
| 170 | <!-- 视频播放器 --> | 258 | <!-- 视频播放器 --> |
| 171 | - <VideoPlayer v-if="isVideoPlaying" ref="videoPlayerRef" :video-url="videoUrl" | 259 | + <VideoPlayer |
| 172 | - :video-id="videoTitle" :use-native-on-ios="false" :autoplay="false" class="w-full h-full" @play="handleVideoPlay" | 260 | + v-if="isVideoPlaying" |
| 173 | - @pause="handleVideoPause" /> | 261 | + ref="videoPlayerRef" |
| 262 | + :video-url="videoUrl" | ||
| 263 | + :video-id="videoTitle" | ||
| 264 | + :use-native-on-ios="false" | ||
| 265 | + :autoplay="false" | ||
| 266 | + class="h-full w-full" | ||
| 267 | + @play="handleVideoPlay" | ||
| 268 | + @pause="handleVideoPause" | ||
| 269 | + /> | ||
| 174 | </div> | 270 | </div> |
| 175 | </div> | 271 | </div> |
| 176 | </van-popup> | 272 | </van-popup> |
| 177 | 273 | ||
| 178 | <!-- 图片预览弹窗 --> | 274 | <!-- 图片预览弹窗 --> |
| 179 | - <van-image-preview v-model:show="imageShow" :images="imageList" :start-position="imageIndex" :show-index="true" /> | 275 | + <van-image-preview |
| 276 | + v-model:show="imageShow" | ||
| 277 | + :images="imageList" | ||
| 278 | + :start-position="imageIndex" | ||
| 279 | + :show-index="true" | ||
| 280 | + /> | ||
| 180 | </div> | 281 | </div> |
| 181 | </template> | 282 | </template> |
| 182 | 283 | ||
| 183 | <script setup> | 284 | <script setup> |
| 184 | import { ref, computed, onMounted, nextTick, reactive, watch, onBeforeUnmount } from 'vue' | 285 | import { ref, computed, onMounted, nextTick, reactive, watch, onBeforeUnmount } from 'vue' |
| 185 | import { useRoute, useRouter, onBeforeRouteLeave } from 'vue-router' | 286 | import { useRoute, useRouter, onBeforeRouteLeave } from 'vue-router' |
| 186 | -import { getTaskDetailAPI, getUploadTaskInfoAPI, getSubtaskListAPI, reuseGratitudeFormAPI } from "@/api/checkin" | 287 | +import { |
| 288 | + getTaskDetailAPI, | ||
| 289 | + getUploadTaskInfoAPI, | ||
| 290 | + getSubtaskListAPI, | ||
| 291 | + reuseGratitudeFormAPI, | ||
| 292 | +} from '@/api/checkin' | ||
| 187 | import { useTitle } from '@vueuse/core' | 293 | import { useTitle } from '@vueuse/core' |
| 188 | import { useCheckin } from '@/composables/useCheckin' | 294 | import { useCheckin } from '@/composables/useCheckin' |
| 189 | import { useCheckinDraft } from '@/composables/useCheckinDraft' | 295 | import { useCheckinDraft } from '@/composables/useCheckinDraft' |
| ... | @@ -226,7 +332,7 @@ const { | ... | @@ -226,7 +332,7 @@ const { |
| 226 | switchType, | 332 | switchType, |
| 227 | initEditData, | 333 | initEditData, |
| 228 | gratitudeCount, | 334 | gratitudeCount, |
| 229 | - gratitudeFormList | 335 | + gratitudeFormList, |
| 230 | } = useCheckin() | 336 | } = useCheckin() |
| 231 | 337 | ||
| 232 | // 使用草稿缓存composable | 338 | // 使用草稿缓存composable |
| ... | @@ -236,19 +342,19 @@ const { | ... | @@ -236,19 +342,19 @@ const { |
| 236 | save_draft: saveDraft, | 342 | save_draft: saveDraft, |
| 237 | read_draft: readDraft, | 343 | read_draft: readDraft, |
| 238 | clear_draft: clearDraft, | 344 | clear_draft: clearDraft, |
| 239 | - cleanup_expired: cleanupExpiredDrafts | 345 | + cleanup_expired: cleanupExpiredDrafts, |
| 240 | } = useCheckinDraft() | 346 | } = useCheckinDraft() |
| 241 | 347 | ||
| 242 | // 草稿Key | 348 | // 草稿Key |
| 243 | -const draftKey = computed(() => { | 349 | +const draftKey = computed(() => |
| 244 | - return buildDraftKey({ | 350 | + buildDraftKey({ |
| 245 | user_id: currentUser.value?.id, | 351 | user_id: currentUser.value?.id, |
| 246 | task_id: route.query.task_id, | 352 | task_id: route.query.task_id, |
| 247 | date: route.query.date, | 353 | date: route.query.date, |
| 248 | task_type: route.query.task_type, | 354 | task_type: route.query.task_type, |
| 249 | - status: route.query.status || 'create' | 355 | + status: route.query.status || 'create', |
| 250 | }) | 356 | }) |
| 251 | -}) | 357 | +) |
| 252 | 358 | ||
| 253 | // 动态字段文字 | 359 | // 动态字段文字 |
| 254 | const dynamicFieldText = ref('感恩') | 360 | const dynamicFieldText = ref('感恩') |
| ... | @@ -307,17 +413,21 @@ const autoSaveDraft = debounce(() => { | ... | @@ -307,17 +413,21 @@ const autoSaveDraft = debounce(() => { |
| 307 | file_list: fileList.value, // save_draft内部会过滤done状态 | 413 | file_list: fileList.value, // save_draft内部会过滤done状态 |
| 308 | count: { | 414 | count: { |
| 309 | gratitude_count: countValue.value, | 415 | gratitude_count: countValue.value, |
| 310 | - gratitude_form_list: selectedTargets.value | 416 | + gratitude_form_list: selectedTargets.value, |
| 311 | - } | 417 | + }, |
| 312 | } | 418 | } |
| 313 | 419 | ||
| 314 | saveDraft(draftKey.value, payload) | 420 | saveDraft(draftKey.value, payload) |
| 315 | }, 500) | 421 | }, 500) |
| 316 | 422 | ||
| 317 | // 监听数据变化触发自动保存 | 423 | // 监听数据变化触发自动保存 |
| 318 | -watch([message, fileList, selectedTaskValue, countValue, selectedTargets], () => { | 424 | +watch( |
| 425 | + [message, fileList, selectedTaskValue, countValue, selectedTargets], | ||
| 426 | + () => { | ||
| 319 | autoSaveDraft() | 427 | autoSaveDraft() |
| 320 | -}, { deep: true }) | 428 | + }, |
| 429 | + { deep: true } | ||
| 430 | +) | ||
| 321 | 431 | ||
| 322 | // 页面离开前强制保存一次 | 432 | // 页面离开前强制保存一次 |
| 323 | onBeforeRouteLeave(() => { | 433 | onBeforeRouteLeave(() => { |
| ... | @@ -351,13 +461,16 @@ const checkAndRestoreDraft = async () => { | ... | @@ -351,13 +461,16 @@ const checkAndRestoreDraft = async () => { |
| 351 | 461 | ||
| 352 | // 校验草稿中的作业是否仍然有效 | 462 | // 校验草稿中的作业是否仍然有效 |
| 353 | // 如果草稿中包含具体的作业ID,必须确保该作业在当前可用的作业列表(taskOptions)中存在 | 463 | // 如果草稿中包含具体的作业ID,必须确保该作业在当前可用的作业列表(taskOptions)中存在 |
| 354 | - const draftSubtaskId = payload.subtask_id || (payload.selected_task_value && payload.selected_task_value[0]) | 464 | + const draftSubtaskId = |
| 465 | + payload.subtask_id || (payload.selected_task_value && payload.selected_task_value[0]) | ||
| 355 | if (draftSubtaskId) { | 466 | if (draftSubtaskId) { |
| 356 | // taskOptions 已经在 onMounted 中加载完毕 | 467 | // taskOptions 已经在 onMounted 中加载完毕 |
| 357 | - const isValidTask = taskOptions.value.some(option => option.value == draftSubtaskId) | 468 | + const isValidTask = taskOptions.value.some( |
| 469 | + option => String(option.value) === String(draftSubtaskId) | ||
| 470 | + ) | ||
| 358 | 471 | ||
| 359 | if (!isValidTask) { | 472 | if (!isValidTask) { |
| 360 | - console.log('[草稿清理] 作业已失效,中断恢复流程', draftSubtaskId) | 473 | + console.warn('[草稿清理] 作业已失效,中断恢复流程', draftSubtaskId) |
| 361 | try { | 474 | try { |
| 362 | await showDialog({ | 475 | await showDialog({ |
| 363 | title: '草稿已失效', | 476 | title: '草稿已失效', |
| ... | @@ -375,7 +488,8 @@ const checkAndRestoreDraft = async () => { | ... | @@ -375,7 +488,8 @@ const checkAndRestoreDraft = async () => { |
| 375 | } | 488 | } |
| 376 | 489 | ||
| 377 | // 检查是否有实质内容 | 490 | // 检查是否有实质内容 |
| 378 | - const hasContent = (payload.message && payload.message.trim()) || | 491 | + const hasContent = |
| 492 | + (payload.message && payload.message.trim()) || | ||
| 379 | (payload.file_list && payload.file_list.length > 0) | 493 | (payload.file_list && payload.file_list.length > 0) |
| 380 | 494 | ||
| 381 | if (!hasContent) return | 495 | if (!hasContent) return |
| ... | @@ -385,11 +499,9 @@ const checkAndRestoreDraft = async () => { | ... | @@ -385,11 +499,9 @@ const checkAndRestoreDraft = async () => { |
| 385 | title: '发现未提交的草稿', | 499 | title: '发现未提交的草稿', |
| 386 | message: '上次编辑的内容未提交,是否恢复?', | 500 | message: '上次编辑的内容未提交,是否恢复?', |
| 387 | confirmButtonText: '恢复', | 501 | confirmButtonText: '恢复', |
| 388 | - cancelButtonText: '丢弃' | 502 | + cancelButtonText: '丢弃', |
| 389 | }) | 503 | }) |
| 390 | - | ||
| 391 | // 确认恢复 | 504 | // 确认恢复 |
| 392 | - console.log('[草稿恢复] 开始恢复数据', payload) | ||
| 393 | 505 | ||
| 394 | if (payload.message) message.value = payload.message | 506 | if (payload.message) message.value = payload.message |
| 395 | if (payload.active_type) activeType.value = payload.active_type | 507 | if (payload.active_type) activeType.value = payload.active_type |
| ... | @@ -400,7 +512,7 @@ const checkAndRestoreDraft = async () => { | ... | @@ -400,7 +512,7 @@ const checkAndRestoreDraft = async () => { |
| 400 | fileList.value = payload.file_list.map(f => ({ | 512 | fileList.value = payload.file_list.map(f => ({ |
| 401 | ...f, | 513 | ...f, |
| 402 | status: 'done', | 514 | status: 'done', |
| 403 | - message: '已上传' | 515 | + message: '已上传', |
| 404 | })) | 516 | })) |
| 405 | } | 517 | } |
| 406 | 518 | ||
| ... | @@ -440,7 +552,11 @@ const checkAndRestoreDraft = async () => { | ... | @@ -440,7 +552,11 @@ const checkAndRestoreDraft = async () => { |
| 440 | 552 | ||
| 441 | // 恢复感恩列表(计数对象) | 553 | // 恢复感恩列表(计数对象) |
| 442 | // 必须在恢复 selectedTaskValue 之后执行,因为 fetchTargetList 依赖 subtask_id | 554 | // 必须在恢复 selectedTaskValue 之后执行,因为 fetchTargetList 依赖 subtask_id |
| 443 | - if (payload.count?.gratitude_form_list && Array.isArray(payload.count.gratitude_form_list) && payload.count.gratitude_form_list.length > 0) { | 555 | + if ( |
| 556 | + payload.count?.gratitude_form_list && | ||
| 557 | + Array.isArray(payload.count.gratitude_form_list) && | ||
| 558 | + payload.count.gratitude_form_list.length > 0 | ||
| 559 | + ) { | ||
| 444 | const savedList = payload.count.gratitude_form_list | 560 | const savedList = payload.count.gratitude_form_list |
| 445 | 561 | ||
| 446 | // 如果有作业ID,先获取基础列表 | 562 | // 如果有作业ID,先获取基础列表 |
| ... | @@ -457,8 +573,9 @@ const checkAndRestoreDraft = async () => { | ... | @@ -457,8 +573,9 @@ const checkAndRestoreDraft = async () => { |
| 457 | 573 | ||
| 458 | savedList.forEach(savedItem => { | 574 | savedList.forEach(savedItem => { |
| 459 | // 尝试在 targetList 中找到对应项(获取最新状态/引用) | 575 | // 尝试在 targetList 中找到对应项(获取最新状态/引用) |
| 460 | - const existingItem = targetList.value.find(t => | 576 | + const existingItem = targetList.value.find( |
| 461 | - (savedItem.id && t.id && t.id == savedItem.id) || | 577 | + t => |
| 578 | + (savedItem.id && t.id && String(t.id) === String(savedItem.id)) || | ||
| 462 | (!savedItem.id && savedItem.name === t.name) | 579 | (!savedItem.id && savedItem.name === t.name) |
| 463 | ) | 580 | ) |
| 464 | 581 | ||
| ... | @@ -469,7 +586,7 @@ const checkAndRestoreDraft = async () => { | ... | @@ -469,7 +586,7 @@ const checkAndRestoreDraft = async () => { |
| 469 | // 如果 targetList 里没有(可能是新增的,或者 targetList 变了),则直接使用草稿项 | 586 | // 如果 targetList 里没有(可能是新增的,或者 targetList 变了),则直接使用草稿项 |
| 470 | restoredTargets.push({ | 587 | restoredTargets.push({ |
| 471 | ...savedItem, | 588 | ...savedItem, |
| 472 | - has_confirmed: true | 589 | + has_confirmed: true, |
| 473 | }) | 590 | }) |
| 474 | // 同时也加到 targetList 里显示出来(如果是新增的) | 591 | // 同时也加到 targetList 里显示出来(如果是新增的) |
| 475 | targetList.value.push(restoredTargets[restoredTargets.length - 1]) | 592 | targetList.value.push(restoredTargets[restoredTargets.length - 1]) |
| ... | @@ -484,7 +601,6 @@ const checkAndRestoreDraft = async () => { | ... | @@ -484,7 +601,6 @@ const checkAndRestoreDraft = async () => { |
| 484 | } | 601 | } |
| 485 | 602 | ||
| 486 | showToast('已恢复草稿') | 603 | showToast('已恢复草稿') |
| 487 | - | ||
| 488 | } catch (e) { | 604 | } catch (e) { |
| 489 | // 取消恢复,清除草稿 | 605 | // 取消恢复,清除草稿 |
| 490 | if (e !== 'cancel') console.error(e) | 606 | if (e !== 'cancel') console.error(e) |
| ... | @@ -493,8 +609,7 @@ const checkAndRestoreDraft = async () => { | ... | @@ -493,8 +609,7 @@ const checkAndRestoreDraft = async () => { |
| 493 | } | 609 | } |
| 494 | } | 610 | } |
| 495 | 611 | ||
| 496 | - | 612 | +const beforeReadGuard = file => { |
| 497 | -const beforeReadGuard = (file) => { | ||
| 498 | const files = Array.isArray(file) ? file : [file] | 613 | const files = Array.isArray(file) ? file : [file] |
| 499 | if (activeType.value === 'video') { | 614 | if (activeType.value === 'video') { |
| 500 | const hasMov = files.some(item => { | 615 | const hasMov = files.some(item => { |
| ... | @@ -506,7 +621,8 @@ const beforeReadGuard = (file) => { | ... | @@ -506,7 +621,8 @@ const beforeReadGuard = (file) => { |
| 506 | if (hasMov) { | 621 | if (hasMov) { |
| 507 | showDialog({ | 622 | showDialog({ |
| 508 | title: '不支持 MOV 格式', | 623 | title: '不支持 MOV 格式', |
| 509 | - message: 'MOV(QuickTime)在非苹果系统/部分播放器兼容性较差,可能出现无法打开、黑屏、无声等问题。\n\n请将视频导出/转换为 MP4(更通用)后再上传。', | 624 | + message: |
| 625 | + 'MOV(QuickTime)在非苹果系统/部分播放器兼容性较差,可能出现无法打开、黑屏、无声等问题。\n\n请将视频导出/转换为 MP4(更通用)后再上传。', | ||
| 510 | confirmButtonText: '我知道了', | 626 | confirmButtonText: '我知道了', |
| 511 | }) | 627 | }) |
| 512 | return false | 628 | return false |
| ... | @@ -530,7 +646,8 @@ const beforeReadGuard = (file) => { | ... | @@ -530,7 +646,8 @@ const beforeReadGuard = (file) => { |
| 530 | if (unsupportedFiles.length > 0) { | 646 | if (unsupportedFiles.length > 0) { |
| 531 | showDialog({ | 647 | showDialog({ |
| 532 | title: '不支持的音频格式', | 648 | title: '不支持的音频格式', |
| 533 | - message: '当前音频播放基于系统浏览器能力,不同机型/系统对音频格式支持差异较大(例如 .wma 等常见无法播放)。\n\n为避免上传后无法播放,请使用 .mp3 或 .m4a(推荐)重新导出/转换后再上传。', | 649 | + message: |
| 650 | + '当前音频播放基于系统浏览器能力,不同机型/系统对音频格式支持差异较大(例如 .wma 等常见无法播放)。\n\n为避免上传后无法播放,请使用 .mp3 或 .m4a(推荐)重新导出/转换后再上传。', | ||
| 534 | confirmButtonText: '我知道了', | 651 | confirmButtonText: '我知道了', |
| 535 | }) | 652 | }) |
| 536 | return false | 653 | return false |
| ... | @@ -547,8 +664,8 @@ const beforeReadGuard = (file) => { | ... | @@ -547,8 +664,8 @@ const beforeReadGuard = (file) => { |
| 547 | * @param {string} type - 文件类型 | 664 | * @param {string} type - 文件类型 |
| 548 | * @returns {number} 文件数量 | 665 | * @returns {number} 文件数量 |
| 549 | */ | 666 | */ |
| 550 | -const getTypeCount = (type) => { | 667 | +const getTypeCount = type => |
| 551 | - return fileList.value.filter(item => { | 668 | + fileList.value.filter(item => { |
| 552 | if (item.file_type) { | 669 | if (item.file_type) { |
| 553 | return item.file_type === type | 670 | return item.file_type === type |
| 554 | } | 671 | } |
| ... | @@ -560,7 +677,6 @@ const getTypeCount = (type) => { | ... | @@ -560,7 +677,6 @@ const getTypeCount = (type) => { |
| 560 | } | 677 | } |
| 561 | return false | 678 | return false |
| 562 | }).length | 679 | }).length |
| 563 | -} | ||
| 564 | 680 | ||
| 565 | /** | 681 | /** |
| 566 | * 当前显示的(经过类型过滤的)文件列表 | 682 | * 当前显示的(经过类型过滤的)文件列表 |
| ... | @@ -569,8 +685,8 @@ const getTypeCount = (type) => { | ... | @@ -569,8 +685,8 @@ const getTypeCount = (type) => { |
| 569 | * 2. setter: 处理 van-uploader 的更新(添加/删除),同步回 fileList | 685 | * 2. setter: 处理 van-uploader 的更新(添加/删除),同步回 fileList |
| 570 | */ | 686 | */ |
| 571 | const displayFileList = computed({ | 687 | const displayFileList = computed({ |
| 572 | - get: () => { | 688 | + get: () => |
| 573 | - return fileList.value.filter(item => { | 689 | + fileList.value.filter(item => { |
| 574 | if (item.file_type) { | 690 | if (item.file_type) { |
| 575 | return item.file_type === activeType.value | 691 | return item.file_type === activeType.value |
| 576 | } | 692 | } |
| ... | @@ -580,9 +696,8 @@ const displayFileList = computed({ | ... | @@ -580,9 +696,8 @@ const displayFileList = computed({ |
| 580 | if (activeType.value === 'audio') return item.file.type.startsWith('audio/') | 696 | if (activeType.value === 'audio') return item.file.type.startsWith('audio/') |
| 581 | } | 697 | } |
| 582 | return false | 698 | return false |
| 583 | - }) | 699 | + }), |
| 584 | - }, | 700 | + set: val => { |
| 585 | - set: (val) => { | ||
| 586 | // 找出不属于当前视图的其他文件(保留它们) | 701 | // 找出不属于当前视图的其他文件(保留它们) |
| 587 | const otherFiles = fileList.value.filter(item => { | 702 | const otherFiles = fileList.value.filter(item => { |
| 588 | if (item.file_type) { | 703 | if (item.file_type) { |
| ... | @@ -600,13 +715,9 @@ const displayFileList = computed({ | ... | @@ -600,13 +715,9 @@ const displayFileList = computed({ |
| 600 | 715 | ||
| 601 | // 合并其他文件和当前视图的新文件列表 | 716 | // 合并其他文件和当前视图的新文件列表 |
| 602 | fileList.value = [...otherFiles, ...val] | 717 | fileList.value = [...otherFiles, ...val] |
| 603 | - } | 718 | + }, |
| 604 | }) | 719 | }) |
| 605 | 720 | ||
| 606 | - | ||
| 607 | - | ||
| 608 | - | ||
| 609 | - | ||
| 610 | const maxFileSizeBytes = computed(() => { | 721 | const maxFileSizeBytes = computed(() => { |
| 611 | const size = Number(maxFileSizeMb.value || 0) | 722 | const size = Number(maxFileSizeMb.value || 0) |
| 612 | if (!Number.isFinite(size) || size <= 0) return 20 * 1024 * 1024 | 723 | if (!Number.isFinite(size) || size <= 0) return 20 * 1024 * 1024 |
| ... | @@ -626,9 +737,7 @@ const displayTaskNote = computed(() => { | ... | @@ -626,9 +737,7 @@ const displayTaskNote = computed(() => { |
| 626 | // 打卡类型 | 737 | // 打卡类型 |
| 627 | const taskType = computed(() => route.query.task_type) | 738 | const taskType = computed(() => route.query.task_type) |
| 628 | 739 | ||
| 629 | - | 740 | +const fetchTargetList = async subtask_id => { |
| 630 | - | ||
| 631 | -const fetchTargetList = async (subtask_id) => { | ||
| 632 | const { code, data } = await reuseGratitudeFormAPI({ subtask_id }) | 741 | const { code, data } = await reuseGratitudeFormAPI({ subtask_id }) |
| 633 | if (code === 1) { | 742 | if (code === 1) { |
| 634 | targetList.value = data.gratitude_form_list || [] | 743 | targetList.value = data.gratitude_form_list || [] |
| ... | @@ -640,8 +749,9 @@ const fetchTargetList = async (subtask_id) => { | ... | @@ -640,8 +749,9 @@ const fetchTargetList = async (subtask_id) => { |
| 640 | const validTargets = [] | 749 | const validTargets = [] |
| 641 | 750 | ||
| 642 | lastUsedTargetList.value.forEach(lastItem => { | 751 | lastUsedTargetList.value.forEach(lastItem => { |
| 643 | - const targetItem = targetList.value.find(t => | 752 | + const targetItem = targetList.value.find( |
| 644 | - (lastItem.id && t.id && t.id == lastItem.id) || | 753 | + t => |
| 754 | + (lastItem.id && t.id && String(t.id) === String(lastItem.id)) || | ||
| 645 | (!lastItem.id && lastItem.name === t.name) | 755 | (!lastItem.id && lastItem.name === t.name) |
| 646 | ) | 756 | ) |
| 647 | 757 | ||
| ... | @@ -654,8 +764,9 @@ const fetchTargetList = async (subtask_id) => { | ... | @@ -654,8 +764,9 @@ const fetchTargetList = async (subtask_id) => { |
| 654 | 764 | ||
| 655 | // 将这些项加入 selectedTargets(去重) | 765 | // 将这些项加入 selectedTargets(去重) |
| 656 | validTargets.forEach(item => { | 766 | validTargets.forEach(item => { |
| 657 | - const exists = selectedTargets.value.some(t => | 767 | + const exists = selectedTargets.value.some( |
| 658 | - (item.id && t.id && t.id == item.id) || | 768 | + t => |
| 769 | + (item.id && t.id && String(t.id) === String(item.id)) || | ||
| 659 | (!item.id && t.name === item.name) | 770 | (!item.id && t.name === item.name) |
| 660 | ) | 771 | ) |
| 661 | if (!exists) { | 772 | if (!exists) { |
| ... | @@ -666,24 +777,20 @@ const fetchTargetList = async (subtask_id) => { | ... | @@ -666,24 +777,20 @@ const fetchTargetList = async (subtask_id) => { |
| 666 | } | 777 | } |
| 667 | } | 778 | } |
| 668 | 779 | ||
| 669 | - | ||
| 670 | - | ||
| 671 | /** | 780 | /** |
| 672 | * 更新动态表单字段 | 781 | * 更新动态表单字段 |
| 673 | * @description 根据选中的作业选项更新动态表单字段配置 | 782 | * @description 根据选中的作业选项更新动态表单字段配置 |
| 674 | * @param {Object} option - 选中的作业选项 | 783 | * @param {Object} option - 选中的作业选项 |
| 675 | */ | 784 | */ |
| 676 | -const updateDynamicFormFields = (option) => { | 785 | +const updateDynamicFormFields = option => { |
| 677 | if (option.field_list && Array.isArray(option.field_list)) { | 786 | if (option.field_list && Array.isArray(option.field_list)) { |
| 678 | // 处理动态表单字段 | 787 | // 处理动态表单字段 |
| 679 | - dynamicFormFields.value = option.field_list.map(field => { | 788 | + dynamicFormFields.value = option.field_list.map(field => ({ |
| 680 | - return { | ||
| 681 | id: field.field || field.field_name || field.name || field.id, // 兼容多种字段名 | 789 | id: field.field || field.field_name || field.name || field.id, // 兼容多种字段名 |
| 682 | label: field.label || '未命名', | 790 | label: field.label || '未命名', |
| 683 | type: field.type || 'text', // 默认类型,如果后端有类型字段可替换 | 791 | type: field.type || 'text', // 默认类型,如果后端有类型字段可替换 |
| 684 | - required: true // 默认必填,如果后端有必填字段可替换 | 792 | + required: true, // 默认必填,如果后端有必填字段可替换 |
| 685 | - } | 793 | + })) |
| 686 | - }) | ||
| 687 | // 确保如果有city字段,类型为textarea | 794 | // 确保如果有city字段,类型为textarea |
| 688 | const cityField = dynamicFormFields.value.find(f => f.id === 'city') | 795 | const cityField = dynamicFormFields.value.find(f => f.id === 'city') |
| 689 | if (cityField) { | 796 | if (cityField) { |
| ... | @@ -745,11 +852,11 @@ const onConfirmTask = async ({ selectedOptions }) => { | ... | @@ -745,11 +852,11 @@ const onConfirmTask = async ({ selectedOptions }) => { |
| 745 | // } | 852 | // } |
| 746 | // }) | 853 | // }) |
| 747 | 854 | ||
| 748 | - | 855 | +const toggleTarget = item => { |
| 749 | - | ||
| 750 | -const toggleTarget = (item) => { | ||
| 751 | // 优先使用id匹配,如果id不存在,则使用name匹配 | 856 | // 优先使用id匹配,如果id不存在,则使用name匹配 |
| 752 | - const index = selectedTargets.value.findIndex(t => (item.id ? t.id === item.id : t.name === item.name)) | 857 | + const index = selectedTargets.value.findIndex(t => |
| 858 | + item.id ? t.id === item.id : t.name === item.name | ||
| 859 | + ) | ||
| 753 | if (index > -1) { | 860 | if (index > -1) { |
| 754 | // 取消选中 | 861 | // 取消选中 |
| 755 | selectedTargets.value.splice(index, 1) | 862 | selectedTargets.value.splice(index, 1) |
| ... | @@ -771,9 +878,9 @@ const toggleTarget = (item) => { | ... | @@ -771,9 +878,9 @@ const toggleTarget = (item) => { |
| 771 | * @description 重置编辑状态并显示弹窗 | 878 | * @description 重置编辑状态并显示弹窗 |
| 772 | */ | 879 | */ |
| 773 | const openAddTargetDialog = () => { | 880 | const openAddTargetDialog = () => { |
| 774 | - editingTarget.value = null; // 重置编辑对象 | 881 | + editingTarget.value = null // 重置编辑对象 |
| 775 | - isConfirmMode.value = false; | 882 | + isConfirmMode.value = false |
| 776 | - showAddTargetDialog.value = true; | 883 | + showAddTargetDialog.value = true |
| 777 | } | 884 | } |
| 778 | 885 | ||
| 779 | /** | 886 | /** |
| ... | @@ -781,7 +888,7 @@ const openAddTargetDialog = () => { | ... | @@ -781,7 +888,7 @@ const openAddTargetDialog = () => { |
| 781 | * @description 处理弹窗确认事件,更新本地列表和选中状态 | 888 | * @description 处理弹窗确认事件,更新本地列表和选中状态 |
| 782 | * @param {Array} formFields - 表单字段数组,包含字段ID和值 | 889 | * @param {Array} formFields - 表单字段数组,包含字段ID和值 |
| 783 | */ | 890 | */ |
| 784 | -const confirmAddTarget = async (formFields) => { | 891 | +const confirmAddTarget = formFields => { |
| 785 | // 将表单字段数组转换为对象 | 892 | // 将表单字段数组转换为对象 |
| 786 | const formData = formFields.reduce((acc, field) => { | 893 | const formData = formFields.reduce((acc, field) => { |
| 787 | if (field.id) { | 894 | if (field.id) { |
| ... | @@ -802,8 +909,9 @@ const confirmAddTarget = async (formFields) => { | ... | @@ -802,8 +909,9 @@ const confirmAddTarget = async (formFields) => { |
| 802 | } | 909 | } |
| 803 | 910 | ||
| 804 | // 检查是否在选中列表中 | 911 | // 检查是否在选中列表中 |
| 805 | - const selectedIndex = selectedTargets.value.findIndex(t => | 912 | + const selectedIndex = selectedTargets.value.findIndex( |
| 806 | - (editingTarget.value.id && t.id && t.id == editingTarget.value.id) || | 913 | + t => |
| 914 | + (editingTarget.value.id && t.id && String(t.id) === String(editingTarget.value.id)) || | ||
| 807 | (!editingTarget.value.id && t.name === editingTarget.value.name) | 915 | (!editingTarget.value.id && t.name === editingTarget.value.name) |
| 808 | ) | 916 | ) |
| 809 | 917 | ||
| ... | @@ -818,7 +926,7 @@ const confirmAddTarget = async (formFields) => { | ... | @@ -818,7 +926,7 @@ const confirmAddTarget = async (formFields) => { |
| 818 | // 新增成功,更新本地列表 | 926 | // 新增成功,更新本地列表 |
| 819 | const newTarget = { | 927 | const newTarget = { |
| 820 | ...formData, | 928 | ...formData, |
| 821 | - has_confirmed: true // 新增的对象默认已确认 | 929 | + has_confirmed: true, // 新增的对象默认已确认 |
| 822 | } | 930 | } |
| 823 | targetList.value.push(newTarget) | 931 | targetList.value.push(newTarget) |
| 824 | // 默认勾选新增的对象 | 932 | // 默认勾选新增的对象 |
| ... | @@ -826,7 +934,7 @@ const confirmAddTarget = async (formFields) => { | ... | @@ -826,7 +934,7 @@ const confirmAddTarget = async (formFields) => { |
| 826 | showToast('新增成功') | 934 | showToast('新增成功') |
| 827 | } | 935 | } |
| 828 | 936 | ||
| 829 | - showAddTargetDialog.value = false; | 937 | + showAddTargetDialog.value = false |
| 830 | } | 938 | } |
| 831 | 939 | ||
| 832 | /** | 940 | /** |
| ... | @@ -834,7 +942,7 @@ const confirmAddTarget = async (formFields) => { | ... | @@ -834,7 +942,7 @@ const confirmAddTarget = async (formFields) => { |
| 834 | * @description 打开弹窗并填充当前对象数据进行编辑 | 942 | * @description 打开弹窗并填充当前对象数据进行编辑 |
| 835 | * @param {Object} item - 待编辑的计数对象 | 943 | * @param {Object} item - 待编辑的计数对象 |
| 836 | */ | 944 | */ |
| 837 | -const handleTargetEdit = (item) => { | 945 | +const handleTargetEdit = item => { |
| 838 | editingTarget.value = item | 946 | editingTarget.value = item |
| 839 | isConfirmMode.value = false // 明确设置为非确认模式 | 947 | isConfirmMode.value = false // 明确设置为非确认模式 |
| 840 | showAddTargetDialog.value = true | 948 | showAddTargetDialog.value = true |
| ... | @@ -845,7 +953,7 @@ const handleTargetEdit = (item) => { | ... | @@ -845,7 +953,7 @@ const handleTargetEdit = (item) => { |
| 845 | * @description 从本地列表和选中列表中移除对象(暂未调用后端接口) | 953 | * @description 从本地列表和选中列表中移除对象(暂未调用后端接口) |
| 846 | * @param {Object} item - 待删除的计数对象 | 954 | * @param {Object} item - 待删除的计数对象 |
| 847 | */ | 955 | */ |
| 848 | -const handleTargetDelete = async (item) => { | 956 | +const handleTargetDelete = async item => { |
| 849 | // 屏蔽删除功能, 那个接口也是不存在的 | 957 | // 屏蔽删除功能, 那个接口也是不存在的 |
| 850 | // const { code } = await gratitudeDeleteAPI({ id: item.id }) | 958 | // const { code } = await gratitudeDeleteAPI({ id: item.id }) |
| 851 | // if (code === 1) { | 959 | // if (code === 1) { |
| ... | @@ -854,13 +962,11 @@ const handleTargetDelete = async (item) => { | ... | @@ -854,13 +962,11 @@ const handleTargetDelete = async (item) => { |
| 854 | // if (targetIndex > -1) { | 962 | // if (targetIndex > -1) { |
| 855 | // targetList.value.splice(targetIndex, 1) | 963 | // targetList.value.splice(targetIndex, 1) |
| 856 | // } | 964 | // } |
| 857 | - | ||
| 858 | // // 从选中列表中也删除 | 965 | // // 从选中列表中也删除 |
| 859 | // const selectedIndex = selectedTargets.value.findIndex(t => t.id === item.id) | 966 | // const selectedIndex = selectedTargets.value.findIndex(t => t.id === item.id) |
| 860 | // if (selectedIndex > -1) { | 967 | // if (selectedIndex > -1) { |
| 861 | // selectedTargets.value.splice(selectedIndex, 1) | 968 | // selectedTargets.value.splice(selectedIndex, 1) |
| 862 | // } | 969 | // } |
| 863 | - | ||
| 864 | // showToast('删除成功') | 970 | // showToast('删除成功') |
| 865 | // } | 971 | // } |
| 866 | } | 972 | } |
| ... | @@ -887,10 +993,9 @@ const isSubmitDisabled = computed(() => { | ... | @@ -887,10 +993,9 @@ const isSubmitDisabled = computed(() => { |
| 887 | if (activeType.value === 'text') { | 993 | if (activeType.value === 'text') { |
| 888 | // 文本打卡:必须填写内容且长度不少于10个字符 | 994 | // 文本打卡:必须填写内容且长度不少于10个字符 |
| 889 | return !message.value.trim() || message.value.trim().length < 10 | 995 | return !message.value.trim() || message.value.trim().length < 10 |
| 890 | - } else { | 996 | + } |
| 891 | // 其他类型:必须有文件 (如果是混合模式,只要有文件就行) | 997 | // 其他类型:必须有文件 (如果是混合模式,只要有文件就行) |
| 892 | return fileList.value.length === 0 | 998 | return fileList.value.length === 0 |
| 893 | - } | ||
| 894 | }) | 999 | }) |
| 895 | 1000 | ||
| 896 | /** | 1001 | /** |
| ... | @@ -902,7 +1007,8 @@ const handleSubmit = async () => { | ... | @@ -902,7 +1007,8 @@ const handleSubmit = async () => { |
| 902 | // 计数打卡校验 | 1007 | // 计数打卡校验 |
| 903 | if (taskType.value === 'count') { | 1008 | if (taskType.value === 'count') { |
| 904 | if (selectedTaskValue.value.length === 0) { | 1009 | if (selectedTaskValue.value.length === 0) { |
| 905 | - const taskText = taskOptions.value.find(t => t.value === selectedTaskValue.value[0])?.text || '作业' | 1010 | + const taskText = |
| 1011 | + taskOptions.value.find(t => t.value === selectedTaskValue.value[0])?.text || '作业' | ||
| 906 | showToast(`请选择${taskText}`) | 1012 | showToast(`请选择${taskText}`) |
| 907 | return | 1013 | return |
| 908 | } | 1014 | } |
| ... | @@ -914,7 +1020,7 @@ const handleSubmit = async () => { | ... | @@ -914,7 +1020,7 @@ const handleSubmit = async () => { |
| 914 | } | 1020 | } |
| 915 | 1021 | ||
| 916 | const extraData = { | 1022 | const extraData = { |
| 917 | - subtask_id: selectedTaskValue.value.length > 0 ? selectedTaskValue.value[0] : '' | 1023 | + subtask_id: selectedTaskValue.value.length > 0 ? selectedTaskValue.value[0] : '', |
| 918 | } | 1024 | } |
| 919 | 1025 | ||
| 920 | // 如果是计数打卡,添加选中的计数对象列表, 并添加次数 | 1026 | // 如果是计数打卡,添加选中的计数对象列表, 并添加次数 |
| ... | @@ -933,13 +1039,9 @@ const handleSubmit = async () => { | ... | @@ -933,13 +1039,9 @@ const handleSubmit = async () => { |
| 933 | await onSubmit(extraData, onSuccess) | 1039 | await onSubmit(extraData, onSuccess) |
| 934 | } | 1040 | } |
| 935 | 1041 | ||
| 936 | - | ||
| 937 | - | ||
| 938 | // 是否为编辑模式 | 1042 | // 是否为编辑模式 |
| 939 | const isEditMode = computed(() => route.query.status === 'edit') | 1043 | const isEditMode = computed(() => route.query.status === 'edit') |
| 940 | 1044 | ||
| 941 | - | ||
| 942 | - | ||
| 943 | /** | 1045 | /** |
| 944 | * 返回上一页 | 1046 | * 返回上一页 |
| 945 | */ | 1047 | */ |
| ... | @@ -952,12 +1054,12 @@ const onClickLeft = () => { | ... | @@ -952,12 +1054,12 @@ const onClickLeft = () => { |
| 952 | * @param {string} type - 打卡类型 | 1054 | * @param {string} type - 打卡类型 |
| 953 | * @returns {string} 图标名称 | 1055 | * @returns {string} 图标名称 |
| 954 | */ | 1056 | */ |
| 955 | -const getIconName = (type) => { | 1057 | +const getIconName = type => { |
| 956 | const iconMap = { | 1058 | const iconMap = { |
| 957 | - 'text': 'edit', | 1059 | + text: 'edit', |
| 958 | - 'image': 'photo', | 1060 | + image: 'photo', |
| 959 | - 'video': 'video', | 1061 | + video: 'video', |
| 960 | - 'audio': 'music' | 1062 | + audio: 'music', |
| 961 | } | 1063 | } |
| 962 | return iconMap[type] || 'edit' | 1064 | return iconMap[type] || 'edit' |
| 963 | } | 1065 | } |
| ... | @@ -968,9 +1070,9 @@ const getIconName = (type) => { | ... | @@ -968,9 +1070,9 @@ const getIconName = (type) => { |
| 968 | */ | 1070 | */ |
| 969 | const getFileIcon = () => { | 1071 | const getFileIcon = () => { |
| 970 | const iconMap = { | 1072 | const iconMap = { |
| 971 | - 'image': 'photo', | 1073 | + image: 'photo', |
| 972 | - 'video': 'video', | 1074 | + video: 'video', |
| 973 | - 'audio': 'music' | 1075 | + audio: 'music', |
| 974 | } | 1076 | } |
| 975 | return iconMap[activeType.value] || 'description' | 1077 | return iconMap[activeType.value] || 'description' |
| 976 | } | 1078 | } |
| ... | @@ -981,9 +1083,9 @@ const getFileIcon = () => { | ... | @@ -981,9 +1083,9 @@ const getFileIcon = () => { |
| 981 | */ | 1083 | */ |
| 982 | const getAcceptType = () => { | 1084 | const getAcceptType = () => { |
| 983 | const acceptMap = { | 1085 | const acceptMap = { |
| 984 | - 'image': 'image/*', | 1086 | + image: 'image/*', |
| 985 | - 'video': '.mp4,video/mp4', | 1087 | + video: '.mp4,video/mp4', |
| 986 | - 'audio': '.mp3,.m4a,.aac,.wav' | 1088 | + audio: '.mp3,.m4a,.aac,.wav', |
| 987 | } | 1089 | } |
| 988 | return acceptMap[activeType.value] || '*' | 1090 | return acceptMap[activeType.value] || '*' |
| 989 | } | 1091 | } |
| ... | @@ -994,9 +1096,9 @@ const getAcceptType = () => { | ... | @@ -994,9 +1096,9 @@ const getAcceptType = () => { |
| 994 | */ | 1096 | */ |
| 995 | const getUploadTips = () => { | 1097 | const getUploadTips = () => { |
| 996 | const tipsMap = { | 1098 | const tipsMap = { |
| 997 | - 'image': '支持格式:.jpg/.jpeg/.png', | 1099 | + image: '支持格式:.jpg/.jpeg/.png', |
| 998 | - 'video': '支持格式:.mp4(不支持 .mov)', | 1100 | + video: '支持格式:.mp4(不支持 .mov)', |
| 999 | - 'audio': '支持格式:.mp3/.m4a/.aac/.wav(不支持 .wma)' | 1101 | + audio: '支持格式:.mp3/.m4a/.aac/.wav(不支持 .wma)', |
| 1000 | } | 1102 | } |
| 1001 | return tipsMap[activeType.value] || '' | 1103 | return tipsMap[activeType.value] || '' |
| 1002 | } | 1104 | } |
| ... | @@ -1005,7 +1107,7 @@ const getUploadTips = () => { | ... | @@ -1005,7 +1107,7 @@ const getUploadTips = () => { |
| 1005 | * 获取任务详情 | 1107 | * 获取任务详情 |
| 1006 | * @param {string} month - 月份 | 1108 | * @param {string} month - 月份 |
| 1007 | */ | 1109 | */ |
| 1008 | -const getTaskDetail = async (month) => { | 1110 | +const getTaskDetail = async month => { |
| 1009 | const { code, data } = await getTaskDetailAPI({ i: route.query.task_id, month }) | 1111 | const { code, data } = await getTaskDetailAPI({ i: route.query.task_id, month }) |
| 1010 | if (code === 1) { | 1112 | if (code === 1) { |
| 1011 | taskDetail.value = data | 1113 | taskDetail.value = data |
| ... | @@ -1016,7 +1118,7 @@ const getTaskDetail = async (month) => { | ... | @@ -1016,7 +1118,7 @@ const getTaskDetail = async (month) => { |
| 1016 | * 更新附件类型选项 | 1118 | * 更新附件类型选项 |
| 1017 | * @param {Array|Object} attachmentType - 附件类型数据 | 1119 | * @param {Array|Object} attachmentType - 附件类型数据 |
| 1018 | */ | 1120 | */ |
| 1019 | -const updateAttachmentTypeOptions = (attachmentType) => { | 1121 | +const updateAttachmentTypeOptions = attachmentType => { |
| 1020 | const { options, upload_size_limit_mb_map } = normalizeAttachmentTypeConfig(attachmentType) | 1122 | const { options, upload_size_limit_mb_map } = normalizeAttachmentTypeConfig(attachmentType) |
| 1021 | attachmentTypeOptions.value = options | 1123 | attachmentTypeOptions.value = options |
| 1022 | 1124 | ||
| ... | @@ -1027,7 +1129,9 @@ const updateAttachmentTypeOptions = (attachmentType) => { | ... | @@ -1027,7 +1129,9 @@ const updateAttachmentTypeOptions = (attachmentType) => { |
| 1027 | 1129 | ||
| 1028 | // 如果是计数打卡(count),过滤掉文本(text)类型 | 1130 | // 如果是计数打卡(count),过滤掉文本(text)类型 |
| 1029 | if (taskType.value === 'count') { | 1131 | if (taskType.value === 'count') { |
| 1030 | - attachmentTypeOptions.value = attachmentTypeOptions.value.filter(option => option.key !== 'text') | 1132 | + attachmentTypeOptions.value = attachmentTypeOptions.value.filter( |
| 1133 | + option => option.key !== 'text' | ||
| 1134 | + ) | ||
| 1031 | } | 1135 | } |
| 1032 | 1136 | ||
| 1033 | // 设置默认选中类型(非计数打卡模式下) | 1137 | // 设置默认选中类型(非计数打卡模式下) |
| ... | @@ -1044,82 +1148,60 @@ const updateAttachmentTypeOptions = (attachmentType) => { | ... | @@ -1044,82 +1148,60 @@ const updateAttachmentTypeOptions = (attachmentType) => { |
| 1044 | * @param {Object} file - 文件对象 | 1148 | * @param {Object} file - 文件对象 |
| 1045 | * @param {Object} detail - 详细信息 | 1149 | * @param {Object} detail - 详细信息 |
| 1046 | */ | 1150 | */ |
| 1047 | -const onClickPreview = (file, detail) => { | 1151 | +const onClickPreview = file => { |
| 1048 | - console.log('onClickPreview - file:', file) | ||
| 1049 | - console.log('onClickPreview - detail:', detail) | ||
| 1050 | - console.log('file对象的所有属性:', Object.keys(file)) | ||
| 1051 | - | ||
| 1052 | const fileName = file.name || file.file?.name || '' | 1152 | const fileName = file.name || file.file?.name || '' |
| 1053 | - | ||
| 1054 | - // 尝试多种方式获取文件URL | ||
| 1055 | let fileUrl = '' | 1153 | let fileUrl = '' |
| 1056 | 1154 | ||
| 1057 | - // 方式1: 直接从file对象获取 | ||
| 1058 | if (file.url) { | 1155 | if (file.url) { |
| 1059 | fileUrl = file.url | 1156 | fileUrl = file.url |
| 1060 | - console.log('从file.url获取URL:', fileUrl) | 1157 | + } else if (file.content) { |
| 1061 | - } | ||
| 1062 | - // 方式2: 从file.content获取 | ||
| 1063 | - else if (file.content) { | ||
| 1064 | fileUrl = file.content | 1158 | fileUrl = file.content |
| 1065 | - console.log('从file.content获取URL:', fileUrl) | 1159 | + } else if (file.objectURL) { |
| 1066 | - } | ||
| 1067 | - // 方式3: 从file.objectURL获取 | ||
| 1068 | - else if (file.objectURL) { | ||
| 1069 | fileUrl = file.objectURL | 1160 | fileUrl = file.objectURL |
| 1070 | - console.log('从file.objectURL获取URL:', fileUrl) | 1161 | + } else if (file.file) { |
| 1071 | - } | ||
| 1072 | - // 方式4: 从file.file获取 | ||
| 1073 | - else if (file.file) { | ||
| 1074 | if (file.file.url) { | 1162 | if (file.file.url) { |
| 1075 | fileUrl = file.file.url | 1163 | fileUrl = file.file.url |
| 1076 | - console.log('从file.file.url获取URL:', fileUrl) | ||
| 1077 | } else { | 1164 | } else { |
| 1078 | - // 创建临时URL | ||
| 1079 | try { | 1165 | try { |
| 1080 | fileUrl = URL.createObjectURL(file.file) | 1166 | fileUrl = URL.createObjectURL(file.file) |
| 1081 | - console.log('通过URL.createObjectURL创建URL:', fileUrl) | ||
| 1082 | } catch (error) { | 1167 | } catch (error) { |
| 1083 | console.error('创建ObjectURL失败:', error) | 1168 | console.error('创建ObjectURL失败:', error) |
| 1084 | } | 1169 | } |
| 1085 | } | 1170 | } |
| 1086 | - } | 1171 | + } else { |
| 1087 | - // 方式5: 检查是否有其他可能的URL字段 | ||
| 1088 | - else { | ||
| 1089 | const possibleUrlFields = ['src', 'path', 'value', 'href', 'link'] | 1172 | const possibleUrlFields = ['src', 'path', 'value', 'href', 'link'] |
| 1090 | for (const field of possibleUrlFields) { | 1173 | for (const field of possibleUrlFields) { |
| 1091 | if (file[field]) { | 1174 | if (file[field]) { |
| 1092 | fileUrl = file[field] | 1175 | fileUrl = file[field] |
| 1093 | - console.log(`从file.${field}获取URL:`, fileUrl) | ||
| 1094 | break | 1176 | break |
| 1095 | } | 1177 | } |
| 1096 | } | 1178 | } |
| 1097 | } | 1179 | } |
| 1098 | 1180 | ||
| 1099 | - console.log('最终提取的文件名:', fileName) | ||
| 1100 | - console.log('最终提取的文件URL:', fileUrl) | ||
| 1101 | - | ||
| 1102 | if (!fileUrl) { | 1181 | if (!fileUrl) { |
| 1103 | console.warn('文件URL不存在,文件对象完整结构:', JSON.stringify(file, null, 2)) | 1182 | console.warn('文件URL不存在,文件对象完整结构:', JSON.stringify(file, null, 2)) |
| 1104 | showToast('无法获取文件URL,请检查文件是否上传成功') | 1183 | showToast('无法获取文件URL,请检查文件是否上传成功') |
| 1105 | return | 1184 | return |
| 1106 | } | 1185 | } |
| 1107 | 1186 | ||
| 1108 | - // 根据打卡类型或文件扩展名判断文件类型 | 1187 | + let finalFileType = file.file_type |
| 1109 | - const finalFileType = file.file_type || (isAudioFile(fileName) ? 'audio' : (isVideoFile(fileName) ? 'video' : 'image')) | 1188 | + if (!finalFileType) { |
| 1189 | + if (isAudioFile(fileName)) { | ||
| 1190 | + finalFileType = 'audio' | ||
| 1191 | + } else if (isVideoFile(fileName)) { | ||
| 1192 | + finalFileType = 'video' | ||
| 1193 | + } else { | ||
| 1194 | + finalFileType = 'image' | ||
| 1195 | + } | ||
| 1196 | + } | ||
| 1110 | 1197 | ||
| 1111 | if (finalFileType === 'audio') { | 1198 | if (finalFileType === 'audio') { |
| 1112 | - console.log('准备播放音频:', fileName, fileUrl) | ||
| 1113 | showAudio(fileName, fileUrl) | 1199 | showAudio(fileName, fileUrl) |
| 1114 | } else if (finalFileType === 'video') { | 1200 | } else if (finalFileType === 'video') { |
| 1115 | - console.log('准备播放视频:', fileName, fileUrl) | ||
| 1116 | showVideo(fileName, fileUrl) | 1201 | showVideo(fileName, fileUrl) |
| 1117 | } else if (finalFileType === 'image') { | 1202 | } else if (finalFileType === 'image') { |
| 1118 | - console.log('图片预览由van-uploader组件处理,跳过文件列表点击预览') | ||
| 1119 | - // 图片预览由van-uploader的@click-preview事件处理,避免重复弹出 | ||
| 1120 | return | 1203 | return |
| 1121 | } else { | 1204 | } else { |
| 1122 | - console.log('该文件类型不支持预览,文件名:', fileName, '类型:', finalFileType) | ||
| 1123 | showToast('该文件类型不支持预览') | 1205 | showToast('该文件类型不支持预览') |
| 1124 | } | 1206 | } |
| 1125 | } | 1207 | } |
| ... | @@ -1214,7 +1296,7 @@ const onClickPreview = (file, detail) => { | ... | @@ -1214,7 +1296,7 @@ const onClickPreview = (file, detail) => { |
| 1214 | * @param {string} fileName - 文件名 | 1296 | * @param {string} fileName - 文件名 |
| 1215 | * @returns {boolean} | 1297 | * @returns {boolean} |
| 1216 | */ | 1298 | */ |
| 1217 | -const isAudioFile = (fileName) => { | 1299 | +const isAudioFile = fileName => { |
| 1218 | const audioExtensions = ['.mp3', '.wav', '.ogg', '.aac', '.m4a', '.flac', '.wma'] | 1300 | const audioExtensions = ['.mp3', '.wav', '.ogg', '.aac', '.m4a', '.flac', '.wma'] |
| 1219 | return audioExtensions.some(ext => fileName.toLowerCase().includes(ext)) | 1301 | return audioExtensions.some(ext => fileName.toLowerCase().includes(ext)) |
| 1220 | } | 1302 | } |
| ... | @@ -1224,7 +1306,7 @@ const isAudioFile = (fileName) => { | ... | @@ -1224,7 +1306,7 @@ const isAudioFile = (fileName) => { |
| 1224 | * @param {string} fileName - 文件名 | 1306 | * @param {string} fileName - 文件名 |
| 1225 | * @returns {boolean} | 1307 | * @returns {boolean} |
| 1226 | */ | 1308 | */ |
| 1227 | -const isVideoFile = (fileName) => { | 1309 | +const isVideoFile = fileName => { |
| 1228 | const videoExtensions = ['.mp4', '.avi', '.mov', '.wmv', '.flv', '.webm', '.mkv'] | 1310 | const videoExtensions = ['.mp4', '.avi', '.mov', '.wmv', '.flv', '.webm', '.mkv'] |
| 1229 | return videoExtensions.some(ext => fileName.toLowerCase().includes(ext)) | 1311 | return videoExtensions.some(ext => fileName.toLowerCase().includes(ext)) |
| 1230 | } | 1312 | } |
| ... | @@ -1234,7 +1316,7 @@ const isVideoFile = (fileName) => { | ... | @@ -1234,7 +1316,7 @@ const isVideoFile = (fileName) => { |
| 1234 | * @param {string} fileName - 文件名 | 1316 | * @param {string} fileName - 文件名 |
| 1235 | * @returns {boolean} | 1317 | * @returns {boolean} |
| 1236 | */ | 1318 | */ |
| 1237 | -const isImageFile = (fileName) => { | 1319 | +const isImageFile = fileName => { |
| 1238 | const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp', '.svg'] | 1320 | const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp', '.svg'] |
| 1239 | return imageExtensions.some(ext => fileName.toLowerCase().includes(ext)) | 1321 | return imageExtensions.some(ext => fileName.toLowerCase().includes(ext)) |
| 1240 | } | 1322 | } |
| ... | @@ -1320,11 +1402,11 @@ const stopVideoPlay = () => { | ... | @@ -1320,11 +1402,11 @@ const stopVideoPlay = () => { |
| 1320 | */ | 1402 | */ |
| 1321 | onMounted(async () => { | 1403 | onMounted(async () => { |
| 1322 | // 获取任务详情 | 1404 | // 获取任务详情 |
| 1323 | - const current_date = route.query.date; | 1405 | + const current_date = route.query.date |
| 1324 | if (current_date) { | 1406 | if (current_date) { |
| 1325 | - getTaskDetail(dayjs(current_date).format('YYYY-MM')); | 1407 | + getTaskDetail(dayjs(current_date).format('YYYY-MM')) |
| 1326 | } else { | 1408 | } else { |
| 1327 | - getTaskDetail(dayjs().format('YYYY-MM')); | 1409 | + getTaskDetail(dayjs().format('YYYY-MM')) |
| 1328 | } | 1410 | } |
| 1329 | 1411 | ||
| 1330 | // 初始化选中的子任务ID | 1412 | // 初始化选中的子任务ID |
| ... | @@ -1333,15 +1415,16 @@ onMounted(async () => { | ... | @@ -1333,15 +1415,16 @@ onMounted(async () => { |
| 1333 | // 获取小作业列表 | 1415 | // 获取小作业列表 |
| 1334 | const subtask_list = await getSubtaskListAPI({ task_id: route.query.task_id, date: current_date }) | 1416 | const subtask_list = await getSubtaskListAPI({ task_id: route.query.task_id, date: current_date }) |
| 1335 | if (subtask_list.code === 1) { | 1417 | if (subtask_list.code === 1) { |
| 1336 | - taskOptions.value = [...subtask_list.data.map(item => ({ | 1418 | + taskOptions.value = [ |
| 1337 | - text: item.is_makeup ? '补卡:' + item.title : item.title, | 1419 | + ...subtask_list.data.map(item => ({ |
| 1420 | + text: item.is_makeup ? `补卡:${item.title}` : item.title, | ||
| 1338 | value: item.id, | 1421 | value: item.id, |
| 1339 | note: item.note, // 作业描述 | 1422 | note: item.note, // 作业描述 |
| 1340 | is_makeup: item.is_makeup, // 是否为补录 | 1423 | is_makeup: item.is_makeup, // 是否为补录 |
| 1341 | field_list: item.field_list, // 动态字段列表 | 1424 | field_list: item.field_list, // 动态字段列表 |
| 1342 | person_type: item.person_type, // 打卡对象类型 | 1425 | person_type: item.person_type, // 打卡对象类型 |
| 1343 | attachment_type: item.attachment_type, // 附件类型 | 1426 | attachment_type: item.attachment_type, // 附件类型 |
| 1344 | - })) | 1427 | + })), |
| 1345 | ] | 1428 | ] |
| 1346 | } | 1429 | } |
| 1347 | 1430 | ||
| ... | @@ -1370,7 +1453,7 @@ onMounted(async () => { | ... | @@ -1370,7 +1453,7 @@ onMounted(async () => { |
| 1370 | 1453 | ||
| 1371 | // 初始化编辑数据 | 1454 | // 初始化编辑数据 |
| 1372 | await initEditData(taskOptions.value, { | 1455 | await initEditData(taskOptions.value, { |
| 1373 | - onTaskFound: (option) => { | 1456 | + onTaskFound: option => { |
| 1374 | updateDynamicFormFields(option) | 1457 | updateDynamicFormFields(option) |
| 1375 | // 更新附件类型选项 | 1458 | // 更新附件类型选项 |
| 1376 | if (option.attachment_type) { | 1459 | if (option.attachment_type) { |
| ... | @@ -1379,7 +1462,7 @@ onMounted(async () => { | ... | @@ -1379,7 +1462,7 @@ onMounted(async () => { |
| 1379 | updateAttachmentTypeOptions(taskDetail.value.attachment_type) | 1462 | updateAttachmentTypeOptions(taskDetail.value.attachment_type) |
| 1380 | } | 1463 | } |
| 1381 | }, | 1464 | }, |
| 1382 | - ensureTargetList: async (id) => { | 1465 | + ensureTargetList: async id => { |
| 1383 | if (targetList.value.length === 0) { | 1466 | if (targetList.value.length === 0) { |
| 1384 | await fetchTargetList(id) | 1467 | await fetchTargetList(id) |
| 1385 | } | 1468 | } |
| ... | @@ -1390,9 +1473,9 @@ onMounted(async () => { | ... | @@ -1390,9 +1473,9 @@ onMounted(async () => { |
| 1390 | // selectedTargets.value = list | 1473 | // selectedTargets.value = list |
| 1391 | // } | 1474 | // } |
| 1392 | // }, | 1475 | // }, |
| 1393 | - setCount: (val) => { | 1476 | + setCount: val => { |
| 1394 | countValue.value = val | 1477 | countValue.value = val |
| 1395 | - } | 1478 | + }, |
| 1396 | }) | 1479 | }) |
| 1397 | 1480 | ||
| 1398 | // 尝试恢复草稿 (非编辑模式) | 1481 | // 尝试恢复草稿 (非编辑模式) |
| ... | @@ -1439,14 +1522,16 @@ onMounted(async () => { | ... | @@ -1439,14 +1522,16 @@ onMounted(async () => { |
| 1439 | line-height: 1.6; | 1522 | line-height: 1.6; |
| 1440 | font-size: 0.95rem; | 1523 | font-size: 0.95rem; |
| 1441 | word-break: break-word; | 1524 | word-break: break-word; |
| 1442 | - overflow-wrap: break-word; | 1525 | + overflow-wrap: anywhere; |
| 1443 | width: 100%; | 1526 | width: 100%; |
| 1444 | box-sizing: border-box; | 1527 | box-sizing: border-box; |
| 1445 | - overflow: hidden; | ||
| 1446 | 1528 | ||
| 1447 | :deep(*) { | 1529 | :deep(*) { |
| 1448 | max-width: 100% !important; | 1530 | max-width: 100% !important; |
| 1449 | box-sizing: border-box; | 1531 | box-sizing: border-box; |
| 1532 | + white-space: normal !important; | ||
| 1533 | + overflow-wrap: anywhere; | ||
| 1534 | + word-break: break-word; | ||
| 1450 | } | 1535 | } |
| 1451 | 1536 | ||
| 1452 | :deep(img) { | 1537 | :deep(img) { | ... | ... |
-
Please register or login to post a comment