feat: 优化mark接口文档和工作空间页面字段

This commit is contained in:
xiongxiao
2026-03-19 02:02:03 +08:00
committed by cnb
parent 469d23b0b9
commit 330accb822
3 changed files with 317 additions and 45 deletions

View File

@@ -2,7 +2,7 @@ import { createQueryApi } from '@kevisual/query/api';
const api = { const api = {
"mark": { "mark": {
/** /**
* mark list. * 获取mark列表
* *
* @param data - Request parameters * @param data - Request parameters
* @param data.page - {number} 页码 * @param data.page - {number} 页码
@@ -14,7 +14,7 @@ const api = {
"list": { "list": {
"path": "mark", "path": "mark",
"key": "list", "key": "list",
"description": "mark list.", "description": "获取mark列表",
"metadata": { "metadata": {
"args": { "args": {
"page": { "page": {
@@ -58,12 +58,15 @@ const api = {
} }
}, },
/** /**
* 获取mark版本信息
*
* @param data - Request parameters * @param data - Request parameters
* @param data.id - {string} mark id * @param data.id - {string} mark id
*/ */
"getVersion": { "getVersion": {
"path": "mark", "path": "mark",
"key": "getVersion", "key": "getVersion",
"description": "获取mark版本信息",
"metadata": { "metadata": {
"args": { "args": {
"id": { "id": {
@@ -77,12 +80,15 @@ const api = {
} }
}, },
/** /**
* 获取mark详情
*
* @param data - Request parameters * @param data - Request parameters
* @param data.id - {string} mark id * @param data.id - {string} mark id
*/ */
"get": { "get": {
"path": "mark", "path": "mark",
"key": "get", "key": "get",
"description": "获取mark详情",
"metadata": { "metadata": {
"args": { "args": {
"id": { "id": {
@@ -96,18 +102,55 @@ const api = {
} }
}, },
/** /**
* 更新mark内容
*
* @param data - Request parameters * @param data - Request parameters
* @param data.id - {string} mark id * @param data.id - {string} mark id
* @param data.data - {object}
*/ */
"update": { "update": {
"path": "mark", "path": "mark",
"key": "update", "key": "update",
"description": "更新mark内容",
"metadata": { "metadata": {
"args": { "args": {
"id": { "id": {
"$schema": "https://json-schema.org/draft/2020-12/schema", "$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string", "type": "string",
"description": "mark id" "description": "mark id"
},
"data": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"title": {
"default": "",
"type": "string"
},
"tags": {
"default": []
},
"link": {
"default": "",
"type": "string"
},
"summary": {
"default": "",
"type": "string"
},
"description": {
"default": "",
"type": "string"
}
},
"required": [
"title",
"tags",
"link",
"summary",
"description"
],
"additionalProperties": false
} }
}, },
"url": "/api/router", "url": "/api/router",
@@ -115,6 +158,8 @@ const api = {
} }
}, },
/** /**
* 更新mark节点支持更新和删除操作
*
* @param data - Request parameters * @param data - Request parameters
* @param data.id - {string} mark id * @param data.id - {string} mark id
* @param data.operate - {"update" | "delete"} 节点操作类型update或delete * @param data.operate - {"update" | "delete"} 节点操作类型update或delete
@@ -123,6 +168,7 @@ const api = {
"updateNode": { "updateNode": {
"path": "mark", "path": "mark",
"key": "updateNode", "key": "updateNode",
"description": "更新mark节点支持更新和删除操作",
"metadata": { "metadata": {
"args": { "args": {
"id": { "id": {
@@ -166,6 +212,8 @@ const api = {
} }
}, },
/** /**
* 批量更新mark节点支持更新和删除操作
*
* @param data - Request parameters * @param data - Request parameters
* @param data.id - {string} mark id * @param data.id - {string} mark id
* @param data.nodeOperateList - {array} 要更新的节点列表 * @param data.nodeOperateList - {array} 要更新的节点列表
@@ -173,6 +221,7 @@ const api = {
"updateNodes": { "updateNodes": {
"path": "mark", "path": "mark",
"key": "updateNodes", "key": "updateNodes",
"description": "批量更新mark节点支持更新和删除操作",
"metadata": { "metadata": {
"args": { "args": {
"id": { "id": {
@@ -232,33 +281,77 @@ const api = {
} }
}, },
/** /**
* 创建mark * 创建一个新的mark.
* *
* @param data - Request parameters * @param data - Request parameters
* @param data.name - {string} mark名称 * @param data.title - {string} 标题
* @param data.markType - {string} mark类型,simple,wallnote,md,draw等 * @param data.tags - {unknown} 标签
* @param data.data - {object} mark数据 * @param data.link - {string} 链接
* @param data.summary - {string} 摘要
* @param data.description - {string} 描述
* @param data.markType - {string} mark类型
* @param data.config - {unknown} 配置
* @param data.data - {unknown} 数据
*/ */
"create": { "create": {
"path": "mark", "path": "mark",
"key": "create", "key": "create",
"description": "创建mark", "description": "创建一个新的mark.",
"metadata": { "metadata": {
"args": { "args": {
"name": { "title": {
"$schema": "https://json-schema.org/draft/2020-12/schema", "$schema": "https://json-schema.org/draft/2020-12/schema",
"description": "mark名称", "default": "",
"type": "string" "description": "标题",
"type": "string",
"optional": true
},
"tags": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "array",
"items": { "type": "string" },
"default": [],
"description": "标签",
"optional": true
},
"link": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"default": "",
"description": "链接",
"type": "string",
"optional": true
},
"summary": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"default": "",
"description": "摘要",
"type": "string",
"optional": true
},
"description": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"default": "",
"description": "描述",
"type": "string",
"optional": true
}, },
"markType": { "markType": {
"$schema": "https://json-schema.org/draft/2020-12/schema", "$schema": "https://json-schema.org/draft/2020-12/schema",
"default": "md",
"description": "mark类型", "description": "mark类型",
"type": "string" "type": "string",
"optional": true
},
"config": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"default": {},
"description": "配置",
"optional": true
}, },
"data": { "data": {
"$schema": "https://json-schema.org/draft/2020-12/schema", "$schema": "https://json-schema.org/draft/2020-12/schema",
"description": "mark数据", "default": {},
"type": "object", "description": "数据",
"optional": true "optional": true
} }
}, },
@@ -267,12 +360,12 @@ const api = {
} }
}, },
/** /**
* 获取菜单 * 获取mark菜单
*/ */
"getMenu": { "getMenu": {
"path": "mark", "path": "mark",
"key": "getMenu", "key": "getMenu",
"description": "获取菜单", "description": "获取mark菜单",
"metadata": { "metadata": {
"url": "/api/router", "url": "/api/router",
"source": "query-proxy-api" "source": "query-proxy-api"
@@ -281,4 +374,5 @@ const api = {
} }
} as const; } as const;
const queryApi = createQueryApi({ api }); const queryApi = createQueryApi({ api });
export { queryApi }; export { queryApi };

View File

@@ -88,7 +88,7 @@ export const App = () => {
boxShadow: '0 2px 4px rgba(0,0,0,0.1)' boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
}} }}
> >
<h3 style={{ margin: '0 0 8px 0', fontSize: '16px' }}>{item.name || '未命名'}</h3> <h3 style={{ margin: '0 0 8px 0', fontSize: '16px' }}>{item.title || '未命名'}</h3>
<p style={{ margin: '0 0 8px 0', color: '#666', fontSize: '14px' }}>ID: {item.id}</p> <p style={{ margin: '0 0 8px 0', color: '#666', fontSize: '14px' }}>ID: {item.id}</p>
<p style={{ margin: '0 0 8px 0', color: '#999', fontSize: '12px' }}> <p style={{ margin: '0 0 8px 0', color: '#999', fontSize: '12px' }}>
: {item.created_at ? new Date(item.created_at).toLocaleString() : '-'} : {item.created_at ? new Date(item.created_at).toLocaleString() : '-'}
@@ -156,22 +156,30 @@ export const App = () => {
function CreateDialog({ open, onClose, onSubmit }: { function CreateDialog({ open, onClose, onSubmit }: {
open: boolean; open: boolean;
onClose: () => void; onClose: () => void;
onSubmit: (data: { name: string, data?: any }) => Promise<void>; onSubmit: (data: { title: string, tags?: string[], link?: string, summary?: string, description?: string, data?: any }) => Promise<void>;
}) { }) {
const [name, setName] = useState(''); const [title, setTitle] = useState('');
const [tags, setTags] = useState('');
const [link, setLink] = useState('');
const [summary, setSummary] = useState('');
const [description, setDescription] = useState('');
const [data, setData] = useState(''); const [data, setData] = useState('');
const [submitting, setSubmitting] = useState(false); const [submitting, setSubmitting] = useState(false);
useEffect(() => { useEffect(() => {
if (open) { if (open) {
setName(''); setTitle('');
setTags('');
setLink('');
setSummary('');
setDescription('');
setData(''); setData('');
} }
}, [open]); }, [open]);
const handleSubmit = async () => { const handleSubmit = async () => {
if (!name.trim()) { if (!title.trim()) {
alert('请输入名称'); alert('请输入标题');
return; return;
} }
setSubmitting(true); setSubmitting(true);
@@ -186,7 +194,14 @@ function CreateDialog({ open, onClose, onSubmit }: {
return; return;
} }
} }
await onSubmit({ name: name.trim(), data: parsedData }); await onSubmit({
title: title.trim(),
tags: tags.split(',').map(t => t.trim()).filter(Boolean),
link: link.trim(),
summary: summary.trim(),
description: description.trim(),
data: parsedData
});
} finally { } finally {
setSubmitting(false); setSubmitting(false);
} }
@@ -211,17 +226,19 @@ function CreateDialog({ open, onClose, onSubmit }: {
background: '#fff', background: '#fff',
borderRadius: '8px', borderRadius: '8px',
padding: '24px', padding: '24px',
width: '480px', width: '520px',
maxWidth: '90%' maxWidth: '90%',
maxHeight: '90vh',
overflow: 'auto'
}} onClick={e => e.stopPropagation()}> }} onClick={e => e.stopPropagation()}>
<h2 style={{ margin: '0 0 20px 0' }}>Workspace</h2> <h2 style={{ margin: '0 0 20px 0' }}>Workspace</h2>
<div style={{ marginBottom: '16px' }}> <div style={{ marginBottom: '16px' }}>
<label style={{ display: 'block', marginBottom: '6px', fontWeight: 500 }}></label> <label style={{ display: 'block', marginBottom: '6px', fontWeight: 500 }}></label>
<input <input
type="text" type="text"
value={name} value={title}
onChange={e => setName(e.target.value)} onChange={e => setTitle(e.target.value)}
placeholder="请输入名称" placeholder="请输入标题"
style={{ style={{
width: '100%', width: '100%',
padding: '8px 12px', padding: '8px 12px',
@@ -231,6 +248,71 @@ function CreateDialog({ open, onClose, onSubmit }: {
}} }}
/> />
</div> </div>
<div style={{ marginBottom: '16px' }}>
<label style={{ display: 'block', marginBottom: '6px', fontWeight: 500 }}> ()</label>
<input
type="text"
value={tags}
onChange={e => setTags(e.target.value)}
placeholder="标签1, 标签2, 标签3"
style={{
width: '100%',
padding: '8px 12px',
border: '1px solid #ddd',
borderRadius: '4px',
boxSizing: 'border-box'
}}
/>
</div>
<div style={{ marginBottom: '16px' }}>
<label style={{ display: 'block', marginBottom: '6px', fontWeight: 500 }}></label>
<input
type="text"
value={link}
onChange={e => setLink(e.target.value)}
placeholder="https://..."
style={{
width: '100%',
padding: '8px 12px',
border: '1px solid #ddd',
borderRadius: '4px',
boxSizing: 'border-box'
}}
/>
</div>
<div style={{ marginBottom: '16px' }}>
<label style={{ display: 'block', marginBottom: '6px', fontWeight: 500 }}></label>
<input
type="text"
value={summary}
onChange={e => setSummary(e.target.value)}
placeholder="简要描述"
style={{
width: '100%',
padding: '8px 12px',
border: '1px solid #ddd',
borderRadius: '4px',
boxSizing: 'border-box'
}}
/>
</div>
<div style={{ marginBottom: '16px' }}>
<label style={{ display: 'block', marginBottom: '6px', fontWeight: 500 }}></label>
<textarea
value={description}
onChange={e => setDescription(e.target.value)}
placeholder="详细描述..."
rows={3}
style={{
width: '100%',
padding: '8px 12px',
border: '1px solid #ddd',
borderRadius: '4px',
boxSizing: 'border-box',
resize: 'vertical'
}}
/>
</div>
<div style={{ marginBottom: '20px' }}> <div style={{ marginBottom: '20px' }}>
<label style={{ display: 'block', marginBottom: '6px', fontWeight: 500 }}> (JSON)</label> <label style={{ display: 'block', marginBottom: '6px', fontWeight: 500 }}> (JSON)</label>
<textarea <textarea
@@ -289,22 +371,30 @@ function EditDialog({ open, item, onClose, onSubmit }: {
open: boolean; open: boolean;
item: any; item: any;
onClose: () => void; onClose: () => void;
onSubmit: (id: string, data: { name?: string, data?: any }) => Promise<void>; onSubmit: (id: string, data: { title?: string, tags?: string[], link?: string, summary?: string, description?: string, data?: any }) => Promise<void>;
}) { }) {
const [name, setName] = useState(''); const [title, setTitle] = useState('');
const [tags, setTags] = useState('');
const [link, setLink] = useState('');
const [summary, setSummary] = useState('');
const [description, setDescription] = useState('');
const [data, setData] = useState(''); const [data, setData] = useState('');
const [submitting, setSubmitting] = useState(false); const [submitting, setSubmitting] = useState(false);
useEffect(() => { useEffect(() => {
if (open && item) { if (open && item) {
setName(item.name || ''); setTitle(item.title || '');
setTags(item.tags ? item.tags.join(', ') : '');
setLink(item.link || '');
setSummary(item.summary || '');
setDescription(item.description || '');
setData(item.data ? JSON.stringify(item.data, null, 2) : ''); setData(item.data ? JSON.stringify(item.data, null, 2) : '');
} }
}, [open, item]); }, [open, item]);
const handleSubmit = async () => { const handleSubmit = async () => {
if (!name.trim()) { if (!title.trim()) {
alert('请输入名称'); alert('请输入标题');
return; return;
} }
if (!item?.id) return; if (!item?.id) return;
@@ -320,7 +410,14 @@ function EditDialog({ open, item, onClose, onSubmit }: {
return; return;
} }
} }
await onSubmit(item.id, { name: name.trim(), data: parsedData }); await onSubmit(item.id, {
title: title.trim(),
tags: tags.split(',').map(t => t.trim()).filter(Boolean),
link: link.trim(),
summary: summary.trim(),
description: description.trim(),
data: parsedData
});
} finally { } finally {
setSubmitting(false); setSubmitting(false);
} }
@@ -345,17 +442,19 @@ function EditDialog({ open, item, onClose, onSubmit }: {
background: '#fff', background: '#fff',
borderRadius: '8px', borderRadius: '8px',
padding: '24px', padding: '24px',
width: '480px', width: '520px',
maxWidth: '90%' maxWidth: '90%',
maxHeight: '90vh',
overflow: 'auto'
}} onClick={e => e.stopPropagation()}> }} onClick={e => e.stopPropagation()}>
<h2 style={{ margin: '0 0 20px 0' }}>Workspace</h2> <h2 style={{ margin: '0 0 20px 0' }}>Workspace</h2>
<div style={{ marginBottom: '16px' }}> <div style={{ marginBottom: '16px' }}>
<label style={{ display: 'block', marginBottom: '6px', fontWeight: 500 }}></label> <label style={{ display: 'block', marginBottom: '6px', fontWeight: 500 }}></label>
<input <input
type="text" type="text"
value={name} value={title}
onChange={e => setName(e.target.value)} onChange={e => setTitle(e.target.value)}
placeholder="请输入名称" placeholder="请输入标题"
style={{ style={{
width: '100%', width: '100%',
padding: '8px 12px', padding: '8px 12px',
@@ -365,6 +464,71 @@ function EditDialog({ open, item, onClose, onSubmit }: {
}} }}
/> />
</div> </div>
<div style={{ marginBottom: '16px' }}>
<label style={{ display: 'block', marginBottom: '6px', fontWeight: 500 }}> ()</label>
<input
type="text"
value={tags}
onChange={e => setTags(e.target.value)}
placeholder="标签1, 标签2, 标签3"
style={{
width: '100%',
padding: '8px 12px',
border: '1px solid #ddd',
borderRadius: '4px',
boxSizing: 'border-box'
}}
/>
</div>
<div style={{ marginBottom: '16px' }}>
<label style={{ display: 'block', marginBottom: '6px', fontWeight: 500 }}></label>
<input
type="text"
value={link}
onChange={e => setLink(e.target.value)}
placeholder="https://..."
style={{
width: '100%',
padding: '8px 12px',
border: '1px solid #ddd',
borderRadius: '4px',
boxSizing: 'border-box'
}}
/>
</div>
<div style={{ marginBottom: '16px' }}>
<label style={{ display: 'block', marginBottom: '6px', fontWeight: 500 }}></label>
<input
type="text"
value={summary}
onChange={e => setSummary(e.target.value)}
placeholder="简要描述"
style={{
width: '100%',
padding: '8px 12px',
border: '1px solid #ddd',
borderRadius: '4px',
boxSizing: 'border-box'
}}
/>
</div>
<div style={{ marginBottom: '16px' }}>
<label style={{ display: 'block', marginBottom: '6px', fontWeight: 500 }}></label>
<textarea
value={description}
onChange={e => setDescription(e.target.value)}
placeholder="详细描述..."
rows={3}
style={{
width: '100%',
padding: '8px 12px',
border: '1px solid #ddd',
borderRadius: '4px',
boxSizing: 'border-box',
resize: 'vertical'
}}
/>
</div>
<div style={{ marginBottom: '20px' }}> <div style={{ marginBottom: '20px' }}>
<label style={{ display: 'block', marginBottom: '6px', fontWeight: 500 }}> (JSON)</label> <label style={{ display: 'block', marginBottom: '6px', fontWeight: 500 }}> (JSON)</label>
<textarea <textarea

View File

@@ -7,7 +7,11 @@ import { toast } from 'sonner';
type WorkspaceItem = { type WorkspaceItem = {
id: string; id: string;
name: string; title: string;
tags?: string[];
link?: string;
summary?: string;
description?: string;
created_at: string; created_at: string;
updated_at: string; updated_at: string;
data?: any; data?: any;
@@ -28,8 +32,8 @@ type WorkspaceState = {
setEditingItem: (item: WorkspaceItem | null) => void; setEditingItem: (item: WorkspaceItem | null) => void;
// 数据操作 // 数据操作
getList: (params?: { search?: string, page?: number, pageSize?: number }) => Promise<void>; getList: (params?: { search?: string, page?: number, pageSize?: number }) => Promise<void>;
createItem: (data: { name: string, data?: any }) => Promise<void>; createItem: (data: { title: string, tags?: string[], link?: string, summary?: string, description?: string, data?: any }) => Promise<void>;
updateItem: (id: string, data: { name?: string, data?: any }) => Promise<void>; updateItem: (id: string, data: { title?: string, tags?: string[], link?: string, summary?: string, description?: string, data?: any }) => Promise<void>;
deleteItem: (id: string) => Promise<void>; deleteItem: (id: string) => Promise<void>;
getItem: (id: string) => Promise<WorkspaceItem | null>; getItem: (id: string) => Promise<WorkspaceItem | null>;
} }
@@ -76,8 +80,12 @@ export const useWorkspaceStore = create<WorkspaceState>((set, get) => ({
createItem: async (data) => { createItem: async (data) => {
try { try {
const res = await queryApi.mark.create({ const res = await queryApi.mark.create({
name: data.name, title: data.title,
markType: 'cnb', markType: 'cnb',
tags: data.tags || [],
link: data.link || '',
summary: data.summary || '',
description: data.description || '',
data: data.data || {} data: data.data || {}
}); });
if (res.code === 200) { if (res.code === 200) {
@@ -97,7 +105,13 @@ export const useWorkspaceStore = create<WorkspaceState>((set, get) => ({
try { try {
const res = await queryApi.mark.update({ const res = await queryApi.mark.update({
id, id,
...data data: {
title: data.title || '',
tags: data.tags || [],
link: data.link || '',
summary: data.summary || '',
description: data.description || ''
}
}); });
if (res.code === 200) { if (res.code === 200) {
toast.success('更新成功'); toast.success('更新成功');