From 2315c0d08fe43e57bc41239a0705b574d5aa7dff Mon Sep 17 00:00:00 2001 From: developer Date: Sun, 12 Jul 2026 13:50:03 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20TaskForm=20=E5=BA=94=E7=94=A8=E5=9C=BA?= =?UTF-8?q?=E6=99=AF=E8=A1=A8=E5=8D=95=E5=BC=B9=E7=AA=97=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 应用场景表单从内联改为 el-dialog 弹窗 - 新增「添加应用场景」按钮,编辑也走弹窗 - 表单 label-position=top,布局更清晰 - 移除旧的 application-form 行内布局样式 - 新增 application-header 弹性布局 - 弹窗在保存/取消后自动关闭 --- src/components/TaskForm.vue | 113 ++++++++++++++++++++---------------- 1 file changed, 62 insertions(+), 51 deletions(-) diff --git a/src/components/TaskForm.vue b/src/components/TaskForm.vue index 541234d..203d3e6 100644 --- a/src/components/TaskForm.vue +++ b/src/components/TaskForm.vue @@ -39,8 +39,9 @@ const applicationStatusOptions: { label: string; value: TaskApplication["status" ]; const applications = ref([]); -const appUrlTitles = ref>({}); const applicationLoading = ref(false); +const appUrlTitles = ref>({}); +const applicationDialogVisible = ref(false); const applicationSaving = ref(false); const editingApplicationId = ref(null); const applicationForm = ref<{ @@ -55,6 +56,11 @@ const applicationForm = ref<{ status: "TODO", }); +const openApplicationDialog = () => { + resetApplicationForm(); + applicationDialogVisible.value = true; +}; + const resetApplicationForm = () => { editingApplicationId.value = null; applicationForm.value = { @@ -70,6 +76,7 @@ const loadApplications = async (targetTaskNum: string) => { try { const res = await getTaskApplications(targetTaskNum); applications.value = res?.data || []; + // 异步抓取标题 const map: Record = {}; await Promise.all( applications.value @@ -111,6 +118,7 @@ const saveApplication = async () => { await createTaskApplication(taskNum.value, payload); ElMessage.success("应用场景已添加"); } + applicationDialogVisible.value = false; resetApplicationForm(); await loadApplications(taskNum.value); } finally { @@ -126,6 +134,7 @@ const editApplication = (item: TaskApplication) => { resourceUrl: item.resourceUrl || "", status: item.status || "TODO", }; + applicationDialogVisible.value = true; }; const removeApplication = async (item: TaskApplication) => { @@ -145,6 +154,7 @@ const convertMaterialUrls = async () => { const bareUrls = new Set(); raw.replace(/https?:\/\/[^\s)\u3001\uFF09\u300D\u300B<>]+/g, (url) => { const clean = url.replace(/[.。,,;;!!??)】」』\]]+$/, ""); + // 跳过 [text](url) 内的 if (!raw.includes(`](${clean})`)) bareUrls.add(clean); return url; }); @@ -274,12 +284,14 @@ const escapeAttr = (s: string) => s.replace(/"/g, """).replace(/&/g, "& function renderMarkdown(raw: string, urlTitles: Record = {}): string { if (!raw.trim()) return ""; let html = raw; + // 1) [text](url) → <a> 占位,防止后续裸 URL 匹配进属性里 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}__`; }); + // 2) 裸 URL → <a> 占位 html = html.replace(/https?:\/\/[^\s)\u3001\uFF09\u300D\u300B<>]+/g, (url) => { const clean = url.replace(/[.。,,;;!!??)】」』\]]+$/, ""); const label = urlTitles[clean] || url; @@ -287,7 +299,9 @@ function renderMarkdown(raw: string, urlTitles: Record = {}): st links.push(tag); return `__LINK_${links.length - 1}__`; }); + // 3) 换行 html = html.replace(/\n/g, "
"); + // 4) 还原占位 for (let i = 0; i < links.length; i++) { html = html.replace(`__LINK_${i}__`, links[i]); } @@ -349,7 +363,12 @@ async function previewMaterial() { + v-model="materialUrl" + type="textarea" + :rows="6" + placeholder="支持 Markdown 格式 例如: 参考 [Vue.js 文档](https://vuejs.org/guide) 源码在 https://github.com/vuejs/core" + /> +
@@ -389,7 +408,10 @@ async function previewMaterial() {
-

应用场景

+
+

应用场景

+ 添加应用场景 +
@@ -409,33 +431,33 @@ async function previewMaterial() {

暂未设置应用场景

- -
- - - -
- - - - - {{ editingApplicationId ? "更新" : "添加" }} - - 取消 -
-
+ + + + + + + + + + + + + + + + + + + +
{{ buttonName }} 取消 @@ -502,7 +524,7 @@ async function previewMaterial() { } .material-preview :deep(a)::after { - content: " \u2197"; + content: " ↗"; font-size: 11px; opacity: 0.5; } @@ -517,6 +539,17 @@ async function previewMaterial() { grid-template-columns: 1fr; } +.application-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 12px; +} + +.application-header h3 { + margin: 0; +} + .application-list { display: flex; flex-direction: column; @@ -558,20 +591,6 @@ async function previewMaterial() { margin-top: 8px; } -.application-form { - display: flex; - flex-direction: column; - gap: 10px; -} - -.application-form-row { - display: grid; - grid-template-columns: minmax(160px, 220px) auto auto; - gap: 10px; - align-items: center; - justify-content: start; -} - .empty-text { margin: 0 0 12px; color: var(--text-secondary); @@ -603,13 +622,5 @@ async function previewMaterial() { flex-direction: column; } - .application-form-row { - grid-template-columns: 1fr; - } - - .application-form-row .el-button { - width: 100%; - margin: 0; - } }