diff --git a/src/components/Study.vue b/src/components/Study.vue
index 6ca419d..a3bdfb3 100644
--- a/src/components/Study.vue
+++ b/src/components/Study.vue
@@ -19,8 +19,7 @@ import { getUrlTitle } from "@/utils/fetchTitle";
interface TaskItem {
title: string;
description: string;
- materialURL: string;
- lastLearningStatus: string;
+ materialUrl: string;
priority: number | string;
taskNum: string;
taskId: number;
@@ -52,18 +51,33 @@ const selectedTask = computed(() =>
tasks.value.find((item) => item.taskId === selectedTaskId.value) || null
);
-// 多 URL 支持
-const urlList = computed(() => {
- const raw = selectedTask.value?.materialURL || "";
- return raw.split(/\r?\n/).map((s) => s.trim()).filter((s) => s.startsWith("http"));
-});
-const urlTitles = ref([]);
-async function loadUrlTitles() {
- const urls = urlList.value;
- if (urls.length === 0) { urlTitles.value = []; return; }
- urlTitles.value = await fetchTitles(urls);
+const escapeHtml = (s: string) => s.replace(/&/g, "&").replace(//g, ">");
+const escapeAttr = (s: string) => s.replace(/"/g, """).replace(/&/g, "&");
+
+function renderMarkdown(raw: string): string {
+ if (!raw.trim()) return "";
+ let html = raw;
+ const links: string[] = [];
+ html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_m: string, text: string, url: string) => {
+ const tag = `${escapeHtml(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 = `${escapeHtml(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;
}
+const materialHtml = computed(() => renderMarkdown(selectedTask.value?.materialUrl || ""));
+
const formatEffectiveTime = (seconds: number): string => {
if (!seconds || seconds <= 0) return "0分钟";
const hours = Math.floor(seconds / 3600);
@@ -141,8 +155,7 @@ const fetchTasks = async () => {
tasks.value = (res?.data?.records || []).map((record: any) => ({
title: record.taskName,
description: record.taskDescription || "",
- materialURL: record.materialURL || record.materialUrl || "",
- lastLearningStatus: record.lastLearningStatus || "未开始",
+ materialUrl: record.materialUrl || "",
priority: Number.isFinite(record.taskPriority) ? Math.round(record.taskPriority) : "-",
taskNum: record.taskNum,
taskId: record.id,
@@ -324,7 +337,6 @@ onMounted(() => {
{{ item.priority }}
{{ item.title }}
- 状态:{{ item.lastLearningStatus }}
@@ -339,18 +351,12 @@ onMounted(() => {
任务描述
-
+
{{ selectedTask.description || '暂无描述' }}
学习材料
-
-
-
- {{ urlTitles[i] || url }}
-
-
-
+
暂未填写材料地址
@@ -578,11 +584,6 @@ onMounted(() => {
justify-content: center;
}
-.task-meta {
- margin: 4px 0 0;
- font-size: 12px;
- color: var(--text-secondary);
-}
.content-zone {
display: grid;
@@ -606,14 +607,33 @@ onMounted(() => {
font-size: 22px;
}
+.description-text {
+ margin: 0;
+ font-size: 14px;
+ line-height: 1.7;
+ color: var(--text-primary);
+ white-space: pre-wrap;
+ word-break: break-word;
+}
+
.detail-block {
- margin-top: 16px;
+ margin-top: 20px;
+ padding-top: 16px;
+ border-top: 1px solid var(--border-soft);
+}
+
+.detail-block:first-child {
+ margin-top: 0;
+ padding-top: 0;
+ border-top: none;
}
.label {
- margin: 0 0 8px;
+ margin: 0 0 10px;
color: var(--text-secondary);
- font-size: 13px;
+ font-size: 14px;
+ font-weight: 500;
+ letter-spacing: 0.5px;
}
.empty-text {
@@ -621,19 +641,29 @@ onMounted(() => {
color: var(--text-secondary);
}
-.material-list {
- display: flex;
- flex-direction: column;
- gap: 6px;
+.material-content {
+ font-size: 14px;
+ line-height: 1.8;
+ word-break: break-word;
+ color: var(--text-primary);
}
-.material-item {
- padding: 4px 0;
- word-break: break-all;
+.material-content :deep(a) {
+ color: var(--green-600);
+ text-decoration: none;
+ border-bottom: 1px dashed var(--green-400);
+ transition: color 0.15s, border-color 0.15s;
}
-.material-item .el-link {
- font-size: 13px;
+.material-content :deep(a:hover) {
+ color: var(--green-800);
+ border-bottom-style: solid;
+}
+
+.material-content :deep(a)::after {
+ content: " ↗";
+ font-size: 11px;
+ opacity: 0.5;
}
.stats-grid {
@@ -649,12 +679,12 @@ onMounted(() => {
}
.stat-label {
- font-size: 12px;
+ font-size: 13px;
color: var(--text-secondary);
}
.stat-value {
- font-size: 14px;
+ font-size: 15px;
font-weight: 600;
color: var(--text-primary);
}