From 6accec1b2a09945aa6c35a7d67a0bf142fd1fd0b Mon Sep 17 00:00:00 2001 From: developer Date: Sun, 12 Jul 2026 13:49:00 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20StartTask=20=E9=A1=B5=E9=9D=A2=E5=AD=A6?= =?UTF-8?q?=E4=B9=A0=E6=9D=90=E6=96=99=E5=B1=95=E7=A4=BA=E4=B8=8E=E7=BC=96?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增学习材料卡片区域,支持 Markdown 渲染展示 - 支持弹窗编辑材料内容,保存到后端 task.materialUrl - taskInfo 新增 taskId 和 materialUrl 字段 - materialHtml computed 自动将链接渲染为可点击标签 - 卡片样式与页面风格统一(绿色主题) --- src/components/StartTask.vue | 130 +++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/src/components/StartTask.vue b/src/components/StartTask.vue index 3adbc0d..2ae328a 100644 --- a/src/components/StartTask.vue +++ b/src/components/StartTask.vue @@ -2,6 +2,7 @@ import { computed, onMounted, onUnmounted, ref, watch } from "vue"; import { useRoute } from "vue-router"; import { ElMessage, ElMessageBox } from "element-plus"; +import request from "@/utils/request"; import { useTimer } from "@/components/composables/useTimer"; import { continueSession, @@ -133,6 +134,8 @@ const taskInfo = ref({ sessionState: "--", taskName: "", taskNum, + taskId: 0, + materialUrl: "", startTime: "", endTime: "", lastStartTime: "", @@ -143,6 +146,65 @@ const taskInfo = ref({ systemMessage: "", }); +// 学习材料展示/编辑 +const editingMaterial = ref(false); +const editMaterialContent = ref(""); +const savingMaterial = ref(false); +const materialHtml = computed(() => { + const raw = taskInfo.value.materialUrl || ""; + if (!raw.trim()) return ""; + let html = raw; + const links: string[] = []; + html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_m: string, text: string, url: string) => { + const tag = `${text}`; + links.push(tag); + return `__LINK_${links.length - 1}__`; + }); + html = html.replace(/https?:\/\/[^\s)\u3001\uFF09\u300D\u300B<>]+/g, (url) => { + const clean = url.replace(/[.。,,;;!!??)】」』\]]+$/, ""); + const tag = `${clean}`; + links.push(tag); + return `__LINK_${links.length - 1}__`; + }); + html = html.replace(/\n/g, "
"); + for (let i = 0; i < links.length; i++) { + html = html.replace(`__LINK_${i}__`, links[i]); + } + return html; +}); + +function startEditMaterial() { + editMaterialContent.value = taskInfo.value.materialUrl; + editingMaterial.value = true; +} + +async function saveMaterial() { + savingMaterial.value = true; + try { + // 先拉取完整任务数据,只改 materialUrl,其他字段保持不变 + const res = await request.get(`/tasks/${taskInfo.value.taskId}`); + const current = res?.data || {}; + await request.put(`/tasks/${taskInfo.value.taskId}`, { + id: taskInfo.value.taskId, + taskName: current.taskName, + taskDescription: current.taskDescription || "", + materialUrl: editMaterialContent.value, + urgency: current.urgency ?? 0, + importance: current.importance ?? 0, + contentDifficulty: current.contentDifficulty ?? 0, + futureValue: current.futureValue ?? 0, + subjectivePriority: current.subjectivePriority ?? 0, + }); + taskInfo.value.materialUrl = editMaterialContent.value; + editingMaterial.value = false; + ElMessage.success("学习材料已更新"); + } catch (e: any) { + ElMessage.error(e?.message || "更新失败"); + } finally { + savingMaterial.value = false; + } +} + const { timerMinutes, timerSeconds, @@ -550,6 +612,30 @@ onUnmounted(() => { 状态:{{ statusText }} + +
+
+ 学习材料 + 编辑 +
+
+

暂未填写材料地址

+ + +

[链接文字](url) 或直接粘贴链接,保存时自动获取标题

+ +
+
+
本次预期 @@ -804,6 +890,50 @@ onUnmounted(() => { word-break: break-word; } +.material-session-card { + padding: 12px 16px; +} + +.material-session-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 6px; +} + +.material-session-label { + font-size: 12px; + font-weight: 700; + color: var(--green-700); + background: #e8f5e9; + padding: 2px 8px; + border-radius: 4px; +} + +.material-session-content { + font-size: 14px; + line-height: 1.7; + word-break: break-word; + color: var(--text-primary); +} + +.material-session-content :deep(a) { + color: var(--green-600); + text-decoration: none; + border-bottom: 1px dashed var(--green-400); +} + +.material-session-content :deep(a:hover) { + color: var(--green-800); + border-bottom-style: solid; +} + +.edit-hint { + margin: 6px 0 0; + font-size: 12px; + color: var(--text-secondary); +} + .dialog-hint { margin: 0 0 10px; font-size: 13px;