hookehuyr

feat(打卡): 移除留言必填校验并支持富文本显示

- 移除打卡留言的必填校验,改为可选输入
- 修改界面文案"打卡内容"为"打卡留言"
- 为作业描述添加v-html支持以显示富文本内容
...@@ -264,10 +264,10 @@ export function useCheckin() { ...@@ -264,10 +264,10 @@ export function useCheckin() {
264 showToast('请先上传文件') 264 showToast('请先上传文件')
265 return 265 return
266 } 266 }
267 - if (message.value.trim() === '') { 267 + // if (message.value.trim() === '') {
268 - showToast('请输入打卡留言') 268 + // showToast('请输入打卡留言')
269 - return 269 + // return
270 - } 270 + // }
271 } 271 }
272 272
273 uploading.value = true 273 uploading.value = true
......
...@@ -6,8 +6,7 @@ ...@@ -6,8 +6,7 @@
6 <div class="section-wrapper"> 6 <div class="section-wrapper">
7 <div class="section-title">作业描述</div> 7 <div class="section-title">作业描述</div>
8 <div class="section-content"> 8 <div class="section-content">
9 - <div v-if="taskDetail.description" class="description-text"> 9 + <div v-if="taskDetail.description" class="description-text" v-html="taskDetail.description">
10 - {{ taskDetail.description }}
11 </div> 10 </div>
12 <div v-else class="no-description"> 11 <div v-else class="no-description">
13 暂无作业描述 12 暂无作业描述
...@@ -17,7 +16,7 @@ ...@@ -17,7 +16,7 @@
17 16
18 <!-- 打卡内容区域 --> 17 <!-- 打卡内容区域 -->
19 <div class="section-wrapper"> 18 <div class="section-wrapper">
20 - <div class="section-title">打卡内容</div> 19 + <div class="section-title">打卡留言</div>
21 <div class="section-content"> 20 <div class="section-content">
22 <!-- 文本输入区域 --> 21 <!-- 文本输入区域 -->
23 <div class="text-input-area"> 22 <div class="text-input-area">
...@@ -26,7 +25,7 @@ ...@@ -26,7 +25,7 @@
26 rows="6" 25 rows="6"
27 autosize 26 autosize
28 type="textarea" 27 type="textarea"
29 - :placeholder="activeType === 'text' ? '请输入打卡内容,至少需要10个字符' : '请输入打卡留言(可选)'" 28 + :placeholder="activeType === 'text' ? '请输入打卡留言,至少需要10个字符' : '请输入打卡留言(可选)'"
30 :maxlength="activeType === 'text' ? 500 : 200" 29 :maxlength="activeType === 'text' ? 500 : 200"
31 show-word-limit 30 show-word-limit
32 /> 31 />
...@@ -229,6 +228,79 @@ const getTaskDetail = async (month) => { ...@@ -229,6 +228,79 @@ const getTaskDetail = async (month) => {
229 const { code, data } = await getTaskDetailAPI({ i: route.query.id, month }) 228 const { code, data } = await getTaskDetailAPI({ i: route.query.id, month })
230 if (code) { 229 if (code) {
231 taskDetail.value = data 230 taskDetail.value = data
231 +
232 + // TODO: 临时 mock 富文本数据,后续替换为真实数据
233 + // if (!taskDetail.value.description) {
234 + // taskDetail.value.description = `
235 + // <div class="rich-content">
236 + // <h2 style="color: #2563eb; margin-bottom: 16px; font-size: 20px; font-weight: 600;">📚 本周学习任务</h2>
237 +
238 + // <p style="margin-bottom: 16px; line-height: 1.6; color: #374151;">
239 + // 同学们好!本周我们将学习<strong style="color: #dc2626;">Vue3 组合式 API</strong>的核心概念。请大家认真完成以下任务:
240 + // </p>
241 +
242 + // <div style="background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%); padding: 16px; border-radius: 12px; margin: 16px 0; border-left: 4px solid #f59e0b;">
243 + // <h3 style="color: #92400e; margin: 0 0 8px 0; font-size: 16px;">⚠️ 重要提醒</h3>
244 + // <p style="margin: 0; color: #78350f;">请在截止日期前提交作业,逾期将影响成绩评定。</p>
245 + // </div>
246 +
247 + // <h3 style="color: #059669; margin: 24px 0 12px 0; font-size: 18px;">📋 具体要求:</h3>
248 +
249 + // <ol style="margin: 16px 0; padding-left: 24px; color: #374151; line-height: 1.8;">
250 + // <li style="margin-bottom: 8px;">
251 + // <strong>理论学习:</strong>观看课程视频,理解 <code style="background: #f3f4f6; padding: 2px 6px; border-radius: 4px; color: #dc2626;">ref</code> 和 <code style="background: #f3f4f6; padding: 2px 6px; border-radius: 4px; color: #dc2626;">reactive</code> 的区别
252 + // </li>
253 + // <li style="margin-bottom: 8px;">
254 + // <strong>实践操作:</strong>完成课后练习,创建一个简单的计数器组件
255 + // </li>
256 + // <li style="margin-bottom: 8px;">
257 + // <strong>拓展思考:</strong>思考组合式 API 相比选项式 API 的优势
258 + // </li>
259 + // </ol>
260 +
261 + // <div style="text-align: center; margin: 24px 0;">
262 + // <img src="https://cdn.ipadbiz.cn/mlaj/images/vue3-composition-api.jpg"
263 + // alt="Vue3 组合式 API 示意图"
264 + // style="max-width: 100%; height: auto; border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.1);" />
265 + // <p style="margin-top: 8px; color: #6b7280; font-size: 14px; font-style: italic;">
266 + // Vue3 组合式 API 架构图
267 + // </p>
268 + // </div>
269 +
270 + // <div style="background: linear-gradient(135deg, #dbeafe 0%, #bfdbfe 100%); padding: 16px; border-radius: 12px; margin: 20px 0;">
271 + // <h4 style="color: #1e40af; margin: 0 0 12px 0; font-size: 16px;">💡 学习提示</h4>
272 + // <ul style="margin: 0; padding-left: 20px; color: #1e3a8a;">
273 + // <li style="margin-bottom: 6px;">可以参考官方文档进行学习</li>
274 + // <li style="margin-bottom: 6px;">建议先理解概念,再动手实践</li>
275 + // <li style="margin-bottom: 6px;">遇到问题可以在群里讨论</li>
276 + // </ul>
277 + // </div>
278 +
279 + // <blockquote style="border-left: 4px solid #10b981; background: #f0fdf4; padding: 16px; margin: 20px 0; border-radius: 0 8px 8px 0;">
280 + // <p style="margin: 0; color: #065f46; font-style: italic; line-height: 1.6;">
281 + // "学而时习之,不亦说乎?" —— 希望大家能够在实践中加深对知识的理解。
282 + // </p>
283 + // </blockquote>
284 +
285 + // <div style="text-align: center; margin: 24px 0;">
286 + // <img src="https://cdn.ipadbiz.cn/mlaj/images/coding-example.png"
287 + // alt="代码示例"
288 + // style="max-width: 100%; height: auto; border-radius: 8px; border: 2px solid #e5e7eb;" />
289 + // </div>
290 +
291 + // <div style="background: #fef2f2; border: 1px solid #fecaca; border-radius: 8px; padding: 16px; margin: 20px 0;">
292 + // <h4 style="color: #dc2626; margin: 0 0 8px 0; font-size: 16px;">📅 截止时间</h4>
293 + // <p style="margin: 0; color: #991b1b; font-weight: 500;">
294 + // 2024年12月31日 23:59
295 + // </p>
296 + // </div>
297 +
298 + // <p style="margin-top: 24px; color: #6b7280; text-align: center; font-size: 14px;">
299 + // 祝大家学习愉快!有问题随时联系老师 👨‍🏫
300 + // </p>
301 + // </div>
302 + // `
303 + // }
232 } 304 }
233 } 305 }
234 306
...@@ -297,6 +369,121 @@ onMounted(async () => { ...@@ -297,6 +369,121 @@ onMounted(async () => {
297 color: #666; 369 color: #666;
298 line-height: 1.6; 370 line-height: 1.6;
299 font-size: 0.95rem; 371 font-size: 0.95rem;
372 +
373 + // 富文本内容样式
374 + // :deep(.rich-content) {
375 + // h1, h2, h3, h4, h5, h6 {
376 + // margin: 16px 0 12px 0;
377 + // font-weight: 600;
378 + // line-height: 1.4;
379 + // }
380 +
381 + // h2 {
382 + // font-size: 20px;
383 + // color: #2563eb;
384 + // }
385 +
386 + // h3 {
387 + // font-size: 18px;
388 + // color: #059669;
389 + // }
390 +
391 + // h4 {
392 + // font-size: 16px;
393 + // }
394 +
395 + // p {
396 + // margin: 12px 0;
397 + // line-height: 1.6;
398 + // color: #374151;
399 + // }
400 +
401 + // strong {
402 + // font-weight: 600;
403 + // color: #dc2626;
404 + // }
405 +
406 + // code {
407 + // background: #f3f4f6;
408 + // padding: 2px 6px;
409 + // border-radius: 4px;
410 + // color: #dc2626;
411 + // font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
412 + // font-size: 0.9em;
413 + // }
414 +
415 + // ol, ul {
416 + // margin: 16px 0;
417 + // padding-left: 24px;
418 +
419 + // li {
420 + // margin-bottom: 8px;
421 + // line-height: 1.8;
422 + // }
423 + // }
424 +
425 + // blockquote {
426 + // border-left: 4px solid #10b981;
427 + // background: #f0fdf4;
428 + // padding: 16px;
429 + // margin: 20px 0;
430 + // border-radius: 0 8px 8px 0;
431 +
432 + // p {
433 + // margin: 0;
434 + // color: #065f46;
435 + // font-style: italic;
436 + // }
437 + // }
438 +
439 + // img {
440 + // max-width: 100%;
441 + // height: auto;
442 + // border-radius: 8px;
443 + // margin: 12px 0;
444 + // display: block;
445 + // }
446 +
447 + // // 特殊样式容器
448 + // .warning-box {
449 + // background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
450 + // padding: 16px;
451 + // border-radius: 12px;
452 + // margin: 16px 0;
453 + // border-left: 4px solid #f59e0b;
454 + // }
455 +
456 + // .info-box {
457 + // background: linear-gradient(135deg, #dbeafe 0%, #bfdbfe 100%);
458 + // padding: 16px;
459 + // border-radius: 12px;
460 + // margin: 20px 0;
461 + // }
462 +
463 + // .deadline-box {
464 + // background: #fef2f2;
465 + // border: 1px solid #fecaca;
466 + // border-radius: 8px;
467 + // padding: 16px;
468 + // margin: 20px 0;
469 + // }
470 +
471 + // // 图片容器居中
472 + // div[style*="text-align: center"] {
473 + // text-align: center;
474 +
475 + // img {
476 + // margin: 12px auto;
477 + // }
478 +
479 + // p {
480 + // color: #6b7280;
481 + // font-size: 14px;
482 + // font-style: italic;
483 + // margin-top: 8px;
484 + // }
485 + // }
486 + // }
300 } 487 }
301 488
302 .no-description { 489 .no-description {
......