From 7c84ff3b8deb0217f8f7047b99309c75cb53d56b Mon Sep 17 00:00:00 2001 From: Steven Date: Fri, 30 Oct 2026 16:18:32 +0800 Subject: [PATCH] =?UTF-8?q?feat(knowledge):=20=E7=9F=A5=E8=AF=86=E8=83=BD?= =?UTF-8?q?=E6=8C=82=E9=99=84=E4=BB=B6=EF=BC=8C=E6=96=B0=E5=BB=BA=E6=97=B6?= =?UTF-8?q?=E5=B0=B1=E8=83=BD=E4=BC=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 知识是备忘录,备忘录不能只有字:手册、日志、导出表格都得能挂上去。 附件本来就落在 item 上(`ALLOWED_KINDS` 早含 knowledge),缺的是"新建时 还没有条目 id"这一段。 - 懒建草稿:`KnowledgeComposer` 第一次传附件(或往正文贴图)时先 `POST /knowledge` 存一条草稿拿到 id,`draftingRef` 去重防止贴图与上传同时打出两条草稿; 之后的保存走 `PATCH /knowledge/{id}`。没传过附件就取消,库里不留空条目。 - `RichTextEditor` 增 `ensureItemId`:没有 itemId 时先把草稿存下来再传图, 于是"新建时不能贴图"这条限制也没了;工具栏上传按钮按是否有落点显隐。 - 新建面板加了附件区:有草稿 id 就是正常的 `AttachmentPanel`(上传/下载/删), 没有就先给一个「添加附件」按钮,点一下建草稿。 - 安卓知识详情加附件区:列表(文件名 + 大小)、`OpenDocument` 上传任意文件、 点「打开」用浏览器看签名直链。编辑器原本只能插图,现在手册/日志也能传。 - 文档:API.md 说明附件三类事项共用同一套接口,以及懒建草稿的先后顺序; ROADMAP 加 3.13。 验证:后端 pytest 94 passed、ruff 全绿;Web `tsc --noEmit` 通过、`vite build` 通过; `knowledge-notebook.mjs` 15 项全绿(新增「新建面板里能上传附件」「详情抽屉里能看到 刚传的附件」「附件挂在知识上,详情回读得到」)。安卓端仍是 JDK 25 + Gradle 8.7 不兼容,本次未编译验证。 --- 2.Design/API.md | 4 + 2.Design/ROADMAP-M0-M1.md | 3 +- .../screens/knowledge-composer-attachment.png | Bin 0 -> 73737 bytes .../java/com/dadao/task/ui/KnowledgeScreen.kt | 106 ++++++++++++++++++ web/e2e/knowledge-notebook.mjs | 36 +++++- web/src/components/RichTextEditor.tsx | 33 ++++-- web/src/pages/Knowledge.tsx | 82 +++++++++++++- 7 files changed, 250 insertions(+), 14 deletions(-) create mode 100644 2.Design/screens/knowledge-composer-attachment.png diff --git a/2.Design/API.md b/2.Design/API.md index 64cfa02..caac97b 100644 --- a/2.Design/API.md +++ b/2.Design/API.md @@ -517,8 +517,12 @@ Query: 附件对象的字段:`id / item_id / filename / content_type / size / sha256 / uploader_id / created_at / raw_url / can_delete`。 `raw_url` 是**后端签发的短期直链**(默认 30 分钟),客户端直接用它渲染,不需要自己拼令牌。 +附件可挂在 `kind` 为 `task / ticket / knowledge` 的事项上(`ALLOWED_KINDS`),知识与任务、工单共用同一套接口; 事项与知识的列表接口会附带 `attachment_count`,详情接口附带 `attachments` 全量。 +新建知识时条目还不存在,前端用「懒建草稿」:点上传(或往正文里贴图)时先 `POST /knowledge` 存一条草稿拿到 `item_id`, +再上传到 `/items/{item_id}/attachments`,之后的保存一律走 `PATCH /knowledge/{item_id}`。没传过附件就取消的人,库里不会留下空条目。 + ### 6.2 图片直链:短期签名,而不是把令牌写进正文 `` 发不出 `Authorization` 头。两种解法:把图片读成 base64 内联(正文膨胀几十倍,破坏检索), diff --git a/2.Design/ROADMAP-M0-M1.md b/2.Design/ROADMAP-M0-M1.md index f191820..7fa956c 100644 --- a/2.Design/ROADMAP-M0-M1.md +++ b/2.Design/ROADMAP-M0-M1.md @@ -292,7 +292,8 @@ | 3.9 | 安卓知识列表 | 1.0 | [x] 只读列表 + 详情动作(编辑正文走安卓编辑器) | | 3.10 | 文档与 e2e | 1.0 | [x] `knowledge.mjs` 7 项 + `retrieval.mjs` 6 项 | | 3.11 | 知识分类:工作区三级小树(`knowledge_category` + 迁移 0007 + RLS),知识可归一类,列表按分类子树 / 未分类 / 标签筛选 | 1.0 | [x] 增删改移 + 重名 `7008`、超三级 `7007`、删分类要 `force` | -| 3.12 | 备忘录形态:新建知识一次写全(标题/类型/分类/标签/正文),不再是"填个标题就完成";抽屉加宽 + 可整屏 | 1.0 | [x] Web `KnowledgeComposer` + 安卓新建后直进编辑器;正文就是详情同款富文本编辑器(Vditor / Compose 编辑器),未保存前不能贴图;`Sheet` 增 `size="md/lg/xl"` 与展开按钮;截图 `2.Design/screens/knowledge-composer.png`、`knowledge-categories.png` | +| 3.12 | 备忘录形态:新建知识一次写全(标题/类型/分类/标签/正文),不再是"填个标题就完成";抽屉加宽 + 可整屏 | 1.0 | [x] Web `KnowledgeComposer` + 安卓新建后直进编辑器;正文就是详情同款富文本编辑器(Vditor / Compose 编辑器);`Sheet` 增 `size="md/lg/xl"` 与展开按钮;截图 `2.Design/screens/knowledge-composer.png`、`knowledge-categories.png` | +| 3.13 | 知识能挂附件:新建面板里直接上传(懒建草稿拿到条目 id 再传),详情里能看、能下载/打开 | 1.0 | [x] Web `AttachmentPanel` + `RichTextEditor.ensureItemId`(贴图/传附件前自动存草稿);安卓详情页附件列表 + `OpenDocument` 上传 + 浏览器打开;附件仍落在 `item` 上(`ALLOWED_KINDS` 含 knowledge) | ### M3 完成定义(DOD) - [x] 一件事办完 → 自动出现一份草稿 → 核对 → 发布,全程不用手动复制粘贴 diff --git a/2.Design/screens/knowledge-composer-attachment.png b/2.Design/screens/knowledge-composer-attachment.png new file mode 100644 index 0000000000000000000000000000000000000000..4799af5e1c42fed6e8c8e510690b59d940677677 GIT binary patch literal 73737 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 Y5dHsFj<)LcV<2~Uy85}Sb4q9e0Lmn+%>V!Z literal 0 HcmV?d00001 diff --git a/android/app/src/main/java/com/dadao/task/ui/KnowledgeScreen.kt b/android/app/src/main/java/com/dadao/task/ui/KnowledgeScreen.kt index 05bc40e..8a07aca 100644 --- a/android/app/src/main/java/com/dadao/task/ui/KnowledgeScreen.kt +++ b/android/app/src/main/java/com/dadao/task/ui/KnowledgeScreen.kt @@ -1,6 +1,12 @@ package com.dadao.task.ui +import android.content.Intent +import android.net.Uri +import android.provider.OpenableColumns +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -34,16 +40,20 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties import com.dadao.task.data.Api import com.dadao.task.data.ApiException +import com.dadao.task.data.Attachment import com.dadao.task.data.KnowledgeCategory import com.dadao.task.data.KnowledgeDoc import com.dadao.task.data.Workspace +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext private val knowledgeTabs = listOf( "draft" to "待核对", @@ -459,12 +469,47 @@ private fun KnowledgeDetailDialog( onEdit: (EditRequest) -> Unit, ) { val scope = rememberCoroutineScope() + val context = LocalContext.current var action by remember { mutableStateOf(doc.status) } var version by remember { mutableStateOf(doc.version) } var categoryId by remember { mutableStateOf(doc.category?.id) } var tags by remember { mutableStateOf(doc.tags.joinToString(" ") { it.name }) } var savingMeta by remember { mutableStateOf(false) } var metaNotice by remember { mutableStateOf(null) } + /** + * 附件:备忘录不只文字,手册、日志、表格都要能挂上来。 + * 附件必须落在条目上,所以这里只在已有的知识详情里传(新建时由正文里的编辑器负责)。 + */ + var attachments by remember(doc.id) { mutableStateOf(doc.attachments) } + var uploading by remember { mutableStateOf(false) } + + LaunchedEffect(doc.id) { + runCatching { api.attachments(workspace.id, doc.id) }.onSuccess { attachments = it } + } + + val picker = rememberLauncherForActivityResult(ActivityResultContracts.OpenDocument()) { uri -> + if (uri == null) return@rememberLauncherForActivityResult + scope.launch { + uploading = true + try { + val bytes = withContext(Dispatchers.IO) { + context.contentResolver.openInputStream(uri)?.use { it.readBytes() } + } + if (bytes == null) { + onFail(IllegalStateException("读不到这个文件,换个来源试试")) + return@launch + } + val name = attachmentName(context, uri) ?: "file-${System.currentTimeMillis()}" + val mime = context.contentResolver.getType(uri) ?: "application/octet-stream" + val created = api.uploadAttachment(workspace.id, doc.id, name, mime, bytes) + attachments = attachments + created + } catch (e: Exception) { + onFail(e) + } finally { + uploading = false + } + } + } val actions = when (action) { "draft" -> listOf("submit_review" to "提交审议", "publish" to "发布", "archive" to "归档") "in_review" -> listOf("withdraw" to "撤回审议", "publish" to "发布", "archive" to "归档") @@ -498,6 +543,50 @@ private fun KnowledgeDetailDialog( color = InkSoft, ) + // 附件:手册、日志、导出表格都能挂在这条知识下,点一下用浏览器打开 + Spacer(Modifier.height(14.dp)) + SectionLabel("附件") + Row(verticalAlignment = Alignment.CenterVertically) { + Text( + if (attachments.isEmpty()) "还没有附件" else "${attachments.size} 个", + style = MaterialTheme.typography.labelSmall, + color = InkMuted, + ) + Spacer(Modifier.width(10.dp)) + TextButton( + enabled = !uploading, + onClick = { picker.launch(arrayOf("*/*")) }, + ) { Text(if (uploading) "上传中…" else "上传附件") } + } + attachments.forEach { row -> + Row( + Modifier.fillMaxWidth().padding(vertical = 2.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + row.filename, + style = MaterialTheme.typography.bodySmall, + color = Ink, + modifier = Modifier.weight(1f), + ) + Text( + humanSize(row.size), + style = MaterialTheme.typography.labelSmall, + color = InkMuted, + ) + Spacer(Modifier.width(4.dp)) + TextButton( + onClick = { + // raw_url 是带短时签名的相对路径,拼上当前后端地址才能在浏览器里打开 + val url = row.rawUrl?.let { api.currentOrigin() + it } ?: return@TextButton + runCatching { + context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url))) + }.onFailure { onFail(it) } + }, + ) { Text("打开") } + } + } + // 已发布的内容不能直接改,分类/标签也跟着锁住,避免"发布后又悄悄换归属" if (action != "published") { Spacer(Modifier.height(14.dp)) @@ -613,3 +702,20 @@ private fun KnowledgeDetailDialog( containerColor = Paper, ) } + +/** 附件列表里显示的大小:人与手机都不需要精确到字节。 */ +private fun humanSize(bytes: Long): String = when { + bytes < 1024 -> "$bytes B" + bytes < 1024 * 1024 -> "${bytes / 1024} KB" + else -> String.format("%.1f MB", bytes / 1024.0 / 1024.0) +} + +/** 从 content:// 里问出文件名:微信、文件管理器给的 uri 形态各不相同,能问就问。 */ +private fun attachmentName(context: android.content.Context, uri: Uri): String? = + runCatching { + context.contentResolver.query(uri, arrayOf(OpenableColumns.DISPLAY_NAME), null, null, null) + ?.use { cursor -> + if (cursor.moveToFirst()) cursor.getString(0) else null + } + }.getOrNull() ?: uri.lastPathSegment?.substringAfterLast('/') + diff --git a/web/e2e/knowledge-notebook.mjs b/web/e2e/knowledge-notebook.mjs index c22075a..93b1baa 100644 --- a/web/e2e/knowledge-notebook.mjs +++ b/web/e2e/knowledge-notebook.mjs @@ -5,6 +5,7 @@ * 前面造数据走接口,界面部分点真实按钮。 */ import { chromium } from "playwright-core"; +import { mkdirSync, writeFileSync } from "node:fs"; const WEB = process.env.WEB ?? "http://127.0.0.1:5180"; const API = process.env.API ?? "http://127.0.0.1:8100/api/v1"; @@ -98,6 +99,28 @@ try { (await composerBody.getAttribute("aria-label")) === "正文", ); await page.screenshot({ path: "e2e/artifacts/knowledge-composer-editor.png" }); + + // ②½ 附件:新建面板里就能传——点「添加附件」先把这条存成草稿,再上传 + mkdirSync("e2e/artifacts", { recursive: true }); + const samplePath = "e2e/artifacts/附件样例.txt"; + writeFileSync(samplePath, "命令行回滚手册:kubectl rollout undo deployment/api\n"); + await page.getByRole("button", { name: "添加附件", exact: true }).click(); + // 面板出现后再走真实的「上传」按钮 + filechooser:页面上不止一个 file input(编辑器也有) + await page.getByRole("button", { name: "上传", exact: true }).waitFor({ timeout: 15_000 }); + const [chooser] = await Promise.all([ + page.waitForEvent("filechooser", { timeout: 15_000 }), + page.getByRole("button", { name: "上传", exact: true }).click(), + ]); + const [uploadResp] = await Promise.all([ + page.waitForResponse( + (r) => r.url().includes("/attachments") && r.request().method() === "POST", + { timeout: 20_000 }, + ), + chooser.setFiles(samplePath), + ]); + await page.getByText("附件样例.txt", { exact: false }).first().waitFor({ timeout: 10_000 }); + check("新建面板里能上传附件(先懒建草稿)", uploadResp.ok(), `HTTP ${uploadResp.status()}`); + await page.screenshot({ path: "e2e/artifacts/knowledge-composer-attachment.png" }); await page.getByRole("button", { name: "保存并继续写", exact: true }).click(); // 保存后直接打开详情:标题、分类路径、标签都在 @@ -107,6 +130,7 @@ try { "新建面板能把分类与标签一起存下来", drawerText.includes("研发 / 部署") && drawerText.includes("发布"), ); + check("详情抽屉里能看到刚传的附件", drawerText.includes("附件样例.txt")); await page.screenshot({ path: "e2e/artifacts/knowledge-composer-saved.png", fullPage: false }); // ③ 列表卡片上能看到分类路径与标签(不用点开) @@ -188,7 +212,17 @@ try { doc?.category?.full_path === "研发 / 部署", `tags=${JSON.stringify((doc?.tags ?? []).map((t) => t.name))} category=${doc?.category?.full_path}`, ); - check("正文按 Markdown 存下来了", String(doc?.body_md ?? "").includes("停掉灰度流量")); + check( + "正文按 Markdown 存下来了", + String(doc?.body_md ?? "").includes("停掉灰度流量"), + ); + // 列表只带 attachment_count(省一次 N+1),文件清单要问详情 + const detail = await call("GET", `/workspaces/${ws.id}/knowledge/${doc.id}`, undefined, token); + check( + "附件挂在这条知识上(详情回读得到)", + (detail?.attachments ?? []).some((a) => a.filename === "附件样例.txt"), + `attachments=${JSON.stringify((detail?.attachments ?? []).map((a) => a.filename))}`, + ); // ⑥ 分类重名与三级上限:接口层面确认约束还在 const dup = await fetch( diff --git a/web/src/components/RichTextEditor.tsx b/web/src/components/RichTextEditor.tsx index ae9d00e..3f52b91 100644 --- a/web/src/components/RichTextEditor.tsx +++ b/web/src/components/RichTextEditor.tsx @@ -20,6 +20,11 @@ interface Props { wsId: string; /** 新建场景还没有条目 id:正文照样能写,只是暂时不能贴图(贴图前必须先存草稿) */ itemId?: string; + /** + * 新建场景的兜底:要贴图时先老老实实把草稿存下来,拿到条目 id 再传。 + * 返回 null 表示没存成(由调用方自己提示),编辑器不会继续上传。 + */ + ensureItemId?: () => Promise; placeholder?: string; disabled?: boolean; /** 编辑器初始高度(px) */ @@ -46,6 +51,7 @@ export function RichTextEditor({ onChange, wsId, itemId, + ensureItemId, placeholder = "写点什么…支持粘贴/拖入图片,也可以用工具栏排版", disabled = false, minHeight = 240, @@ -62,6 +68,7 @@ export function RichTextEditor({ const onErrorRef = useRef(onError); const resourcesRef = useRef(resources); const onUploadedRef = useRef(onUploaded); + const ensureItemIdRef = useRef(ensureItemId); const [ready, setReady] = useState(false); const [uploading, setUploading] = useState(false); @@ -70,6 +77,7 @@ export function RichTextEditor({ onErrorRef.current = onError; resourcesRef.current = resources; onUploadedRef.current = onUploaded; + ensureItemIdRef.current = ensureItemId; const disabledRef = useRef(disabled); disabledRef.current = disabled; @@ -113,8 +121,8 @@ export function RichTextEditor({ "line", "code", "inline-code", - // 没有条目 id 就没有上传落点,索性不摆这个按钮,免得点了才报错 - ...(itemId ? ["upload"] : []), + // 没有落点就不摆这个按钮,免得点了才报错;新建时靠 ensureItemId 先存草稿 + ...(itemId || ensureItemId ? ["upload"] : []), "table", "|", "undo", @@ -131,9 +139,18 @@ export function RichTextEditor({ accept: "image/*", multiple: true, handler: async (files: File[]) => { - if (!itemId) { - onErrorRef.current?.("先保存这条内容,再回来贴图或传附件"); - return null; + // 新建时还没有条目 id:先让调用方把草稿存下来,拿到落点再传 + let targetId = itemId; + if (!targetId) { + const ensured = await ensureItemIdRef.current?.(); + if (!ensured) { + // 存草稿失败的原因由 ensureItemId 自己报,这里不叠一层噪音 + if (!ensureItemIdRef.current) { + onErrorRef.current?.("先保存这条内容,再回来贴图或传附件"); + } + return null; + } + targetId = ensured; } const images = files.filter((file) => file.type.startsWith("image/")); if (images.length === 0) { @@ -145,7 +162,7 @@ export function RichTextEditor({ try { for (const file of images) { const created = await uploadFile( - `/workspaces/${wsId}/items/${itemId}/attachments`, + `/workspaces/${wsId}/items/${targetId}/attachments`, file, ); // 自己插入,不用 Vditor 代劳:它对 handler 的返回值只会 @@ -219,7 +236,9 @@ export function RichTextEditor({ ? "正在上传图片…" : itemId ? "支持直接粘贴或拖入图片;内容按 Markdown 保存,模型和导出都读得懂" - : "内容按 Markdown 保存;存下来之后就能直接贴图、传附件"} + : ensureItemId + ? "支持直接粘贴或拖入图片:会先自动存成草稿,再传上去" + : "内容按 Markdown 保存;存下来之后就能直接贴图、传附件"}

); diff --git a/web/src/pages/Knowledge.tsx b/web/src/pages/Knowledge.tsx index 85dae90..e6733e7 100644 --- a/web/src/pages/Knowledge.tsx +++ b/web/src/pages/Knowledge.tsx @@ -3,7 +3,7 @@ import { useParams } from "react-router-dom"; import { AppShell } from "../components/AppShell"; import { AttachmentPanel } from "../components/AttachmentPanel"; import { RichTextEditor } from "../components/RichTextEditor"; -import { Link2, Plus, Search, Sparkle } from "../components/icons"; +import { Link2, Paperclip, Plus, Search, Sparkle } from "../components/icons"; import { Button, EmptyState, @@ -566,6 +566,16 @@ function KnowledgeComposer({ const [newCat, setNewCat] = useState(""); const [busy, setBusy] = useState(false); const [error, setError] = useState(null); + const [notice, setNotice] = useState(null); + /** + * 附件与图片都要落在具体条目上,可新建时条目还不存在。 + * 解法是懒建草稿:第一次传附件/贴图时才 POST 一条草稿,之后一律 PATCH。 + * 没传过附件的人,取消后什么都不会留在库里。 + */ + const [draftId, setDraftId] = useState(null); + const [draftVersion, setDraftVersion] = useState(0); + const [attachments, setAttachments] = useState([]); + const draftingRef = useRef | null>(null); const parentDepth = categories.find((c) => c.id === categoryId)?.depth ?? 0; const canAddCategory = parentDepth < 3 && Boolean(newCat.trim()); @@ -581,17 +591,50 @@ function KnowledgeComposer({ } } + /** 拿条目 id:已有就直接返回,没有就现存一条草稿;并发调用共用同一个请求。 */ + async function ensureDraft(): Promise { + if (draftId) return draftId; + if (draftingRef.current) return draftingRef.current; + const pending = (async () => { + try { + const doc = await api.post(`/workspaces/${wsId}/knowledge`, { + title: title.trim() || "未命名知识", + body_md: body, + doc_type: docType, + tag_names: tags.split(/[\s,,]+/).filter(Boolean), + category_id: categoryId || null, + }); + setDraftId(doc.id); + setDraftVersion(doc.version); + setAttachments(doc.attachments ?? []); + setTitle(doc.title); + setNotice("已存成草稿,现在可以继续写、贴图、传附件"); + return doc.id; + } catch (err) { + setError(err instanceof ApiError ? err.message : "保存草稿失败"); + return null; + } finally { + draftingRef.current = null; + } + })(); + draftingRef.current = pending; + return pending; + } + async function save() { if (!canSave) return; + const itemId = await ensureDraft(); + if (!itemId) return; setBusy(true); setError(null); try { - const doc = await api.post(`/workspaces/${wsId}/knowledge`, { + const doc = await api.patch(`/workspaces/${wsId}/knowledge/${itemId}`, { + version: draftVersion, title: title.trim(), body_md: body, doc_type: docType, tag_names: tags.split(/[\s,,]+/).filter(Boolean), - category_id: categoryId || null, + ...(categoryId ? { category_id: categoryId } : { clear_category: true }), }); onCreated(doc); } catch (err) { @@ -607,7 +650,7 @@ function KnowledgeComposer({ onClose={onClose} size="xl" title="记一条知识" - subtitle="标题、正文、标签、分类一次填完;保存后可以继续贴图、传附件。" + subtitle="标题、正文、标签、分类一次填完;附件和贴图会先把这条存成草稿。" footer={
- 先存成草稿,核对发布后才进团队检索 + {draftId + ? "已经是草稿了:直接关掉会留在「待核对」里" + : "先存成草稿,核对发布后才进团队检索"}
} @@ -717,12 +762,39 @@ function KnowledgeComposer({ value={body} onChange={setBody} minHeight={320} + ensureItemId={ensureDraft} onError={setError} placeholder={"写下要点、步骤、结论…\n\n标题、类型、分类、标签都在上面,正文就是这张备忘的主体。"} /> +
+ {draftId ? ( + + ) : ( +
+ + + 附件 + + + + 附件要挂在条目上:点一下先把这条存成草稿,然后就能上传、下载 + +
+ )} +
+ + {notice &&

{notice}

} {error && {error}}