@deppon/deppon-prd-mcp 0.1.1 → 2.5.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,205 @@
1
+ import crypto from 'node:crypto';
2
+ const WIDGET_COLORS = {
3
+ ShellSidebar: 'light-blue',
4
+ PageHeader: 'grey',
5
+ FilterBar: 'yellow',
6
+ DataTable: 'light-green',
7
+ };
8
+ function shapeId() {
9
+ return `shape:${crypto.randomBytes(4).toString('hex')}`;
10
+ }
11
+ function pageId() {
12
+ return `page:${crypto.randomBytes(4).toString('hex')}`;
13
+ }
14
+ function indexAt(i) {
15
+ return `a${i + 1}`;
16
+ }
17
+ function geoShape(page, idx, x, y, w, h, text, color) {
18
+ return {
19
+ id: shapeId(),
20
+ typeName: 'shape',
21
+ type: 'geo',
22
+ x,
23
+ y,
24
+ rotation: 0,
25
+ isLocked: false,
26
+ opacity: 1,
27
+ parentId: page,
28
+ index: indexAt(idx),
29
+ meta: {},
30
+ props: {
31
+ w,
32
+ h,
33
+ geo: 'rectangle',
34
+ color,
35
+ fill: 'semi',
36
+ text,
37
+ dash: 'draw',
38
+ size: 'm',
39
+ },
40
+ };
41
+ }
42
+ function textShape(page, idx, x, y, text) {
43
+ return {
44
+ id: shapeId(),
45
+ typeName: 'shape',
46
+ type: 'text',
47
+ x,
48
+ y,
49
+ rotation: 0,
50
+ isLocked: false,
51
+ opacity: 1,
52
+ parentId: page,
53
+ index: indexAt(idx),
54
+ meta: {},
55
+ props: {
56
+ color: 'black',
57
+ size: 's',
58
+ w: 280,
59
+ text,
60
+ font: 'draw',
61
+ autoSize: true,
62
+ },
63
+ };
64
+ }
65
+ export function buildTldrawWireframeDocument(project, widgets) {
66
+ const pid = pageId();
67
+ const records = [
68
+ {
69
+ id: 'document:document',
70
+ typeName: 'document',
71
+ gridSize: 10,
72
+ name: `${project.pageName} · 线框`,
73
+ meta: { slug: project.slug, source: 'deppon-prd-mcp' },
74
+ },
75
+ {
76
+ id: pid,
77
+ typeName: 'page',
78
+ name: project.pageName,
79
+ index: 'a1',
80
+ meta: {},
81
+ },
82
+ {
83
+ id: `instance_page_state:${pid}`,
84
+ typeName: 'instance_page_state',
85
+ pageId: pid,
86
+ selectedShapeIds: [],
87
+ hintingShapeIds: [],
88
+ erasingShapeIds: [],
89
+ hoveredShapeId: null,
90
+ editingShapeId: null,
91
+ croppingShapeId: null,
92
+ focusedGroupId: null,
93
+ meta: {},
94
+ },
95
+ {
96
+ id: `camera:${pid}`,
97
+ typeName: 'camera',
98
+ x: -20,
99
+ y: -20,
100
+ z: 0.85,
101
+ meta: {},
102
+ },
103
+ ];
104
+ let shapeIdx = 0;
105
+ records.push(geoShape(pid, shapeIdx++, 0, 0, 1440, 900, `${project.pageName} · B端线框 1440×900`, 'black'));
106
+ for (const widget of widgets) {
107
+ const color = WIDGET_COLORS[widget.type] || 'violet';
108
+ const label = `${widget.type}${widget.props.menuPath || widget.props.title || widget.props.activeItem
109
+ ? `\n${String(widget.props.menuPath || widget.props.title || widget.props.activeItem)}`
110
+ : ''}`;
111
+ records.push(geoShape(pid, shapeIdx++, widget.x, widget.y, widget.w, widget.h, label.slice(0, 120), color));
112
+ }
113
+ if (project.filters.length > 0) {
114
+ const filterWidget = widgets.find(w => w.type === 'FilterBar');
115
+ if (filterWidget) {
116
+ const cols = Math.min(project.filters.length, 4);
117
+ const cellW = Math.floor((filterWidget.w - 32) / cols);
118
+ project.filters.forEach((f, i) => {
119
+ const col = i % cols;
120
+ const row = Math.floor(i / cols);
121
+ records.push(geoShape(pid, shapeIdx++, filterWidget.x + 16 + col * cellW, filterWidget.y + 48 + row * 44, cellW - 12, 36, f.name, 'light-violet'));
122
+ });
123
+ }
124
+ }
125
+ if (project.columns.length > 0) {
126
+ const table = widgets.find(w => w.type === 'DataTable');
127
+ if (table) {
128
+ const colW = Math.floor(table.w / (project.columns.length + 1));
129
+ project.columns.forEach((c, i) => {
130
+ records.push(textShape(pid, shapeIdx++, table.x + 8 + i * colW, table.y + 36, c.name));
131
+ });
132
+ }
133
+ }
134
+ records.push(textShape(pid, shapeIdx++, 240, 16, `PRD 线框 · ${project.pageName}\n${project.menuPath}\n打开:tldraw.com 或 VS Code tldraw 扩展`));
135
+ return {
136
+ document: records[0],
137
+ pageId: pid,
138
+ records,
139
+ };
140
+ }
141
+ export function buildTldrawFile(project, widgets) {
142
+ const { records } = buildTldrawWireframeDocument(project, widgets);
143
+ return {
144
+ tldrawFileFormatVersion: 1,
145
+ schema: {
146
+ schemaVersion: 2,
147
+ sequences: {
148
+ 'com.tldraw.store': 4,
149
+ 'com.tldraw.asset': 1,
150
+ 'com.tldraw.camera': 1,
151
+ 'com.tldraw.document': 2,
152
+ 'com.tldraw.instance': 25,
153
+ 'com.tldraw.instance_page_state': 5,
154
+ 'com.tldraw.page': 1,
155
+ 'com.tldraw.pointer': 1,
156
+ 'com.tldraw.shape': 4,
157
+ 'com.tldraw.shape.geo': 10,
158
+ 'com.tldraw.shape.text': 3,
159
+ 'com.tldraw.shape.note': 9,
160
+ 'com.tldraw.shape.arrow': 6,
161
+ },
162
+ },
163
+ records,
164
+ };
165
+ }
166
+ export function buildTldrawPrompt(project, handoff, tldrRelativePath) {
167
+ const widgetLines = handoff.widgets.map(w => `- ${w.type} (${w.x},${w.y}) ${w.w}×${w.h}`).join('\n');
168
+ return `# tldraw 线框任务:${project.pageName}
169
+
170
+ 基于 deppon-prd-mcp 已生成 PRD,在 tldraw 中绘制 **B 端后台线框**(低精度布局,用于评审与流程说明)。
171
+
172
+ ## 文件
173
+ - 相对路径(TLDRAW_DIR 下):\`${tldrRelativePath}\`
174
+ - 已预生成线框:\`${handoff.paths.tldrawWireframe || `<slug>/wireframe.tldr`}\`
175
+ - 可用 tldraw MCP 继续 \`tldraw_add_shape\` / \`tldraw_update_shape\` 微调
176
+
177
+ ## 页面背景
178
+ ${project.background}
179
+
180
+ ## 布局区块
181
+ ${widgetLines}
182
+
183
+ ## 筛选项
184
+ ${project.filters.map(f => `- ${f.name}(${f.type})`).join('\n') || '(无)'}
185
+
186
+ ## 表格列
187
+ ${project.columns.map(c => `- ${c.name}`).join('\n')}
188
+
189
+ ## 工具栏 / 行操作
190
+ - 工具栏:${project.toolbarButtons.join('、')}
191
+ - 行操作:${project.rowActions.join('、')}
192
+
193
+ ## MCP 操作建议
194
+ 1. \`tldraw_read\` 读取 \`${tldrRelativePath}\` 查看现有线框
195
+ 2. 按需 \`tldraw_add_shape\` 补充流程箭头(type: arrow)或备注(type: note)
196
+ 3. 高保真视觉稿请用 Penpot/Reframe;tldraw 专注 **线框与流程**
197
+
198
+ ## 打开方式
199
+ - [tldraw.com](https://www.tldraw.com/) → File → Open → 选择 wireframe.tldr
200
+ - VS Code 安装 tldraw 扩展后直接打开文件
201
+
202
+ ## 关联 PRD
203
+ ${handoff.paths.prd}
204
+ `;
205
+ }
@@ -56,6 +56,25 @@ export declare const PermissionRowSchema: z.ZodObject<{
56
56
  scope?: string | undefined;
57
57
  dataScope?: string | undefined;
58
58
  }>;
59
+ export declare const PrototypeLayoutSchema: z.ZodObject<{
60
+ blockOrder: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
61
+ filterOrder: z.ZodOptional<z.ZodArray<z.ZodNumber, "many">>;
62
+ columnOrder: z.ZodOptional<z.ZodArray<z.ZodNumber, "many">>;
63
+ toolbarOrder: z.ZodOptional<z.ZodArray<z.ZodNumber, "many">>;
64
+ hiddenBlocks: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
65
+ }, "strip", z.ZodTypeAny, {
66
+ blockOrder?: string[] | undefined;
67
+ filterOrder?: number[] | undefined;
68
+ columnOrder?: number[] | undefined;
69
+ toolbarOrder?: number[] | undefined;
70
+ hiddenBlocks?: string[] | undefined;
71
+ }, {
72
+ blockOrder?: string[] | undefined;
73
+ filterOrder?: number[] | undefined;
74
+ columnOrder?: number[] | undefined;
75
+ toolbarOrder?: number[] | undefined;
76
+ hiddenBlocks?: string[] | undefined;
77
+ }>;
59
78
  export declare const PrdProjectSchema: z.ZodObject<{
60
79
  slug: z.ZodString;
61
80
  pageName: z.ZodString;
@@ -124,6 +143,25 @@ export declare const PrdProjectSchema: z.ZodObject<{
124
143
  createButtonLabel: z.ZodOptional<z.ZodString>;
125
144
  enableCrud: z.ZodDefault<z.ZodBoolean>;
126
145
  mockRows: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodString>, "many">>;
146
+ layout: z.ZodOptional<z.ZodObject<{
147
+ blockOrder: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
148
+ filterOrder: z.ZodOptional<z.ZodArray<z.ZodNumber, "many">>;
149
+ columnOrder: z.ZodOptional<z.ZodArray<z.ZodNumber, "many">>;
150
+ toolbarOrder: z.ZodOptional<z.ZodArray<z.ZodNumber, "many">>;
151
+ hiddenBlocks: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
152
+ }, "strip", z.ZodTypeAny, {
153
+ blockOrder?: string[] | undefined;
154
+ filterOrder?: number[] | undefined;
155
+ columnOrder?: number[] | undefined;
156
+ toolbarOrder?: number[] | undefined;
157
+ hiddenBlocks?: string[] | undefined;
158
+ }, {
159
+ blockOrder?: string[] | undefined;
160
+ filterOrder?: number[] | undefined;
161
+ columnOrder?: number[] | undefined;
162
+ toolbarOrder?: number[] | undefined;
163
+ hiddenBlocks?: string[] | undefined;
164
+ }>>;
127
165
  }, "strip", z.ZodTypeAny, {
128
166
  slug: string;
129
167
  pageName: string;
@@ -158,6 +196,13 @@ export declare const PrdProjectSchema: z.ZodObject<{
158
196
  mockRows: Record<string, string>[];
159
197
  batchDate?: string | undefined;
160
198
  createButtonLabel?: string | undefined;
199
+ layout?: {
200
+ blockOrder?: string[] | undefined;
201
+ filterOrder?: number[] | undefined;
202
+ columnOrder?: number[] | undefined;
203
+ toolbarOrder?: number[] | undefined;
204
+ hiddenBlocks?: string[] | undefined;
205
+ } | undefined;
161
206
  }, {
162
207
  slug: string;
163
208
  pageName: string;
@@ -192,6 +237,13 @@ export declare const PrdProjectSchema: z.ZodObject<{
192
237
  createButtonLabel?: string | undefined;
193
238
  enableCrud?: boolean | undefined;
194
239
  mockRows?: Record<string, string>[] | undefined;
240
+ layout?: {
241
+ blockOrder?: string[] | undefined;
242
+ filterOrder?: number[] | undefined;
243
+ columnOrder?: number[] | undefined;
244
+ toolbarOrder?: number[] | undefined;
245
+ hiddenBlocks?: string[] | undefined;
246
+ } | undefined;
195
247
  }>;
196
248
  export type PrdProject = z.infer<typeof PrdProjectSchema>;
197
249
  export declare const PAGE_TYPE_LABELS: Record<PageType, string>;
@@ -28,11 +28,21 @@ export const PermissionRowSchema = z.object({
28
28
  scope: z.string().default(''),
29
29
  dataScope: z.string().default('全量'),
30
30
  });
31
+ export const PrototypeLayoutSchema = z.object({
32
+ blockOrder: z.array(z.string()).optional(),
33
+ filterOrder: z.array(z.number()).optional(),
34
+ columnOrder: z.array(z.number()).optional(),
35
+ toolbarOrder: z.array(z.number()).optional(),
36
+ hiddenBlocks: z.array(z.string()).optional(),
37
+ });
31
38
  export const PrdProjectSchema = z.object({
32
39
  slug: z.string().min(1),
33
40
  pageName: z.string().min(1),
34
41
  pageType: PageTypeSchema.default('list-crud'),
35
- batchDate: z.string().regex(/^\d{8}$/).optional(),
42
+ batchDate: z
43
+ .string()
44
+ .regex(/^\d{8}$/)
45
+ .optional(),
36
46
  useDpItTitle: z.boolean().default(true),
37
47
  background: z.string().min(1),
38
48
  features: z.array(FeatureRowSchema).default([]),
@@ -44,9 +54,8 @@ export const PrdProjectSchema = z.object({
44
54
  rowActions: z.array(z.string()).default(['查看', '编辑', '删除']),
45
55
  createButtonLabel: z.string().optional(),
46
56
  enableCrud: z.boolean().default(true),
47
- mockRows: z
48
- .array(z.record(z.string()))
49
- .default([]),
57
+ mockRows: z.array(z.record(z.string())).default([]),
58
+ layout: PrototypeLayoutSchema.optional(),
50
59
  });
51
60
  export const PAGE_TYPE_LABELS = {
52
61
  'list-crud': '后台列表 + CRUD',
@@ -4,7 +4,7 @@ import path from 'node:path';
4
4
  import { getHttpHost, getHttpPort, getPrototypesRoot, getWebRoot } from '../config.js';
5
5
  import { generatePrdMarkdown } from '../core/prd-generator.js';
6
6
  import { generatePrototypeHtml } from '../core/prototype-generator.js';
7
- import { applyOverrides, listProjects, loadProject, readProjectFiles, saveProject, } from '../core/project-store.js';
7
+ import { applyOverrides, listProjects, loadProject, readProjectFiles, saveDesignLayout, saveProject, } from '../core/project-store.js';
8
8
  import { createDefaultProject, PAGE_TYPE_LABELS, PrdProjectSchema } from '../core/types.js';
9
9
  let started = false;
10
10
  export async function startHttpServer() {
@@ -87,6 +87,17 @@ export async function startHttpServer() {
87
87
  res.status(400).json({ error: e instanceof Error ? e.message : String(e) });
88
88
  }
89
89
  });
90
+ app.post('/api/projects/:slug/design', (req, res) => {
91
+ try {
92
+ const updated = saveDesignLayout(req.params.slug, req.body || {});
93
+ if (!updated)
94
+ return res.status(404).json({ error: '项目不存在' });
95
+ res.json({ ok: true, project: updated, slug: updated.slug });
96
+ }
97
+ catch (e) {
98
+ res.status(400).json({ error: e instanceof Error ? e.message : String(e) });
99
+ }
100
+ });
90
101
  app.get('/api/projects/:slug/prototype', (req, res) => {
91
102
  const files = readProjectFiles(req.params.slug);
92
103
  if (!files)
@@ -4,8 +4,9 @@ import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprot
4
4
  import { getHttpBaseUrl } from '../config.js';
5
5
  import { generatePrdMarkdown } from '../core/prd-generator.js';
6
6
  import { generatePrototypeHtml } from '../core/prototype-generator.js';
7
- import { applyOverrides, listProjects, loadProject, readProjectFiles, saveProject, } from '../core/project-store.js';
8
- import { createDefaultProject, PAGE_TYPE_LABELS, PageTypeSchema, PrdProjectSchema, } from '../core/types.js';
7
+ import { applyOverrides, listProjects, loadProject, readProjectFiles, saveProject } from '../core/project-store.js';
8
+ import { createDefaultProject, PAGE_TYPE_LABELS, PageTypeSchema, PrdProjectSchema } from '../core/types.js';
9
+ import { saveCanvasHandoff } from '../core/canvas-handoff.js';
9
10
  import { startHttpServer } from '../http/server.js';
10
11
  export async function startMcpServer() {
11
12
  await startHttpServer();
@@ -79,6 +80,22 @@ export async function startMcpServer() {
79
80
  },
80
81
  },
81
82
  },
83
+ {
84
+ name: 'export_canvas_handoff',
85
+ description: 'PRD 项目导出 canvas-handoff.json + penpot/reframe/tldraw Prompt + wireframe.tldr,供外部 MCP 绘制原型(PRD 仍由 deppon-prd-mcp 管理)。',
86
+ inputSchema: {
87
+ type: 'object',
88
+ properties: {
89
+ slug: { type: 'string', description: '项目 slug' },
90
+ target: {
91
+ type: 'string',
92
+ enum: ['all', 'both', 'penpot', 'reframe', 'tldraw'],
93
+ description: 'handoff 目标,默认 all(含 tldraw 线框)',
94
+ },
95
+ },
96
+ required: ['slug'],
97
+ },
98
+ },
82
99
  ],
83
100
  }));
84
101
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
@@ -181,6 +198,46 @@ export async function startMcpServer() {
181
198
  ],
182
199
  };
183
200
  }
201
+ if (name === 'export_canvas_handoff') {
202
+ const slug = String(input.slug || '');
203
+ const target = String(input.target || 'all');
204
+ const project = loadProject(slug);
205
+ if (!project)
206
+ throw new Error(`项目不存在: ${slug}`);
207
+ const result = saveCanvasHandoff(project);
208
+ const referenceUrl = `${getHttpBaseUrl()}/projects/${slug}/prototype.html`;
209
+ const tldrawWireframeUrl = `${getHttpBaseUrl()}/projects/${slug}/wireframe.tldr`;
210
+ return {
211
+ content: [
212
+ {
213
+ type: 'text',
214
+ text: JSON.stringify({
215
+ ok: true,
216
+ slug,
217
+ target,
218
+ handoffPath: result.handoffPath,
219
+ penpotPromptPath: result.penpotPromptPath,
220
+ reframePromptPath: result.reframePromptPath,
221
+ tldrawPromptPath: result.tldrawPromptPath,
222
+ tldrawWireframePath: result.tldrawWireframePath,
223
+ tldrawWireframeUrl,
224
+ referencePrototypeUrl: referenceUrl,
225
+ widgetCount: result.handoff.widgets.length,
226
+ penpotPromptPreview: result.handoff.penpot.prompt.slice(0, 400),
227
+ reframePromptPreview: result.handoff.reframe.prompt.slice(0, 400),
228
+ tldrawPromptPreview: result.handoff.tldraw.prompt.slice(0, 400),
229
+ nextSteps: result.nextSteps,
230
+ cursorWorkflow: [
231
+ '1. deppon-prd: generate_prd → export_canvas_handoff',
232
+ '2. tldraw: 打开 wireframe.tldr,Agent 读 tldraw-prompt.md 用 tldraw MCP 补充线框',
233
+ '3. penpot: yarn penpot:mcp,Agent 读 penpot-prompt.md',
234
+ '4. reframe: yarn reframe:platform,Agent 读 reframe-prompt.md',
235
+ ],
236
+ }, null, 2),
237
+ },
238
+ ],
239
+ };
240
+ }
184
241
  throw new Error(`Unknown tool: ${name}`);
185
242
  }
186
243
  catch (error) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deppon/deppon-prd-mcp",
3
- "version": "0.1.1",
4
- "mcpName": "io.github.wangyang1103/deppon-prd-mcp",
3
+ "version": "2.5.10",
4
+ "mcpName": "io.github.chriswong1103/deppon-prd-mcp",
5
5
  "description": "私有化 PRD 生成 + 交互原型 MCP 服务(基于 deppon-prd-generator skill)",
6
6
  "license": "MIT",
7
7
  "type": "module",
@@ -30,7 +30,10 @@
30
30
  "dev:web": "npm run build && node dist/cli.js --web-only",
31
31
  "prepack": "npm run build",
32
32
  "publish:auto": "npm publish",
33
- "publish:registry": "mcp-publisher publish"
33
+ "publish:registry": "mcp-publisher publish",
34
+ "penpot:mcp": "bash scripts/start-penpot-mcp.sh",
35
+ "reframe:setup": "bash scripts/setup-reframe.sh",
36
+ "reframe:platform": "cd \"$HOME/.deppon/reframe\" && npm start"
34
37
  },
35
38
  "dependencies": {
36
39
  "@modelcontextprotocol/sdk": "^1.12.1",
@@ -2,14 +2,14 @@
2
2
 
3
3
  本目录存放**已完成的对照示例**(非业务仓库的 `src/prototypes/` 交付物),供生成 PRD / 可选 `prototype.html` 时对齐**章节结构、表格粒度、标注与 `prd.md` 联动(`fetch` + `marked`)**。各子目录内 `prd.md` 文首含指向**同目录** `prototype.html` 的 Markdown 相对链接(与 `SKILL.md`「PRD 与原型互链」一致);正文与原型 UI 默认**简体中文**。
4
4
 
5
- | 子目录 | 对应模板文件 | 页面类型 / 场景 |
6
- | ------ | ------------ | ---------------- |
7
- | `list-crud/` | `template/backend-list-prd-template.md` | 后台列表 **+ 完整 CRUD**(筛选/分页/详情/表单/删除) |
8
- | `backend-list/` | `template/backend-list-prd-template.md` | 后台 **纯列表**(筛选 + 表 + 分页,无弹层 CRUD) |
9
- | `form-edit/` | `template/backend-form-edit-prd-template.md` | 后台 **整页表单编辑**(字段/联动/保存取消) |
10
- | `form-preview/` | `template/backend-form-preview-prd-template.md` | 后台 **只读详情 / 预览** |
11
- | `data-dashboard/` | `template/data-prd-template.md` | **数据看板**(筛选 + 卡 + 图 + 表 + 指标总览) |
12
- | `user-frontend/` | `template/user-frontend-prd-template.md` | **用户前台 / C 端**活动页 |
5
+ | 子目录 | 对应模板文件 | 页面类型 / 场景 |
6
+ | ----------------------- | ----------------------------------------------- | --------------------------------------------------------------- |
7
+ | `list-crud/` | `template/backend-list-prd-template.md` | 后台列表 **+ 完整 CRUD**(筛选/分页/详情/表单/删除) |
8
+ | `backend-list/` | `template/backend-list-prd-template.md` | 后台 **纯列表**(筛选 + 表 + 分页,无弹层 CRUD) |
9
+ | `form-edit/` | `template/backend-form-edit-prd-template.md` | 后台 **整页表单编辑**(字段/联动/保存取消) |
10
+ | `form-preview/` | `template/backend-form-preview-prd-template.md` | 后台 **只读详情 / 预览** |
11
+ | `data-dashboard/` | `template/data-prd-template.md` | **数据看板**(筛选 + 卡 + 图 + 表 + 指标总览) |
12
+ | `user-frontend/` | `template/user-frontend-prd-template.md` | **用户前台 / C 端**活动页 |
13
13
  | `app-shell-navigation/` | `template/app-shell-navigation-prd-template.md` | **管理台壳层 + 多页文档中心**(侧栏/顶栏/栅格 + `pages/` 互跳) |
14
14
 
15
15
  **打开原型**:各 `prototype.html` 须通过**本地 HTTP**访问同目录,以便 `fetch('prd.md')`;勿依赖 `file://`。**`app-shell-navigation/`** 还须从该子目录打开,保证 iframe 内 `pages/*.html` 相对路径可用。
@@ -4,37 +4,37 @@
4
4
 
5
5
  ## 1. 整体结构(左右 + 顶)
6
6
 
7
- | 区域 | 位置 | 说明 |
8
- | ---- | ---- | ---- |
9
- | 侧栏 | 左侧固定 | 主导航;展开宽 **180px**,收起 **56px** |
10
- | 顶栏 | 主区上方 | 高 **56px**;Logo/标题左,工具右 |
11
- | 主工作区 | 顶栏下方、侧栏右侧 | 内嵌 **iframe** 或路由 `router-view` |
7
+ | 区域 | 位置 | 说明 |
8
+ | -------- | ------------------ | --------------------------------------- |
9
+ | 侧栏 | 左侧固定 | 主导航;展开宽 **180px**,收起 **56px** |
10
+ | 顶栏 | 主区上方 | 高 **56px**;Logo/标题左,工具右 |
11
+ | 主工作区 | 顶栏下方、侧栏右侧 | 内嵌 **iframe** 或路由 `router-view` |
12
12
 
13
13
  ## 2. 间距(与视觉稿一致时可照抄)
14
14
 
15
- | 名称 | 数值 | 用途 |
16
- | ---- | ---- | ---- |
17
- | 侧栏—主区 | **16px** | 水平间隙 |
18
- | 顶栏—内容 | **16px** | 顶栏底到主区顶 |
19
- | 主区内块间距 | **16px** | 卡片/区块之间 |
20
- | 页面底边距 | **24px** | 主区 `padding-bottom` |
15
+ | 名称 | 数值 | 用途 |
16
+ | ------------ | -------- | --------------------- |
17
+ | 侧栏—主区 | **16px** | 水平间隙 |
18
+ | 顶栏—内容 | **16px** | 顶栏底到主区顶 |
19
+ | 主区内块间距 | **16px** | 卡片/区块之间 |
20
+ | 页面底边距 | **24px** | 主区 `padding-bottom` |
21
21
 
22
22
  ## 3. 24 栅格(主区内逻辑分栏)
23
23
 
24
- | 区块 | 建议占位 | 说明 |
25
- | ---- | -------- | ---- |
26
- | 顶通栏 | **24 / 24** | 全宽 KPI / 筛选横条 |
27
- | 下区左 | **16 / 24**(约 2/3) | 列表、大图表 |
28
- | 下区右 | **8 / 24**(约 1/3) | 辅信息、快捷入口 |
24
+ | 区块 | 建议占位 | 说明 |
25
+ | ------ | --------------------- | ------------------- |
26
+ | 顶通栏 | **24 / 24** | 全宽 KPI / 筛选横条 |
27
+ | 下区左 | **16 / 24**(约 2/3) | 列表、大图表 |
28
+ | 下区右 | **8 / 24**(约 1/3) | 辅信息、快捷入口 |
29
29
 
30
30
  ## 4. 侧栏导航层级(交互摘要)
31
31
 
32
- | 层级 | 行高参考 | 交互 |
33
- | ---- | -------- | ---- |
32
+ | 层级 | 行高参考 | 交互 |
33
+ | ---- | -------- | ----------------------------------- |
34
34
  | 一级 | **52px** | 图标 + 文案 + chevron;点击展开二级 |
35
- | 二级 | 略缩进 | 无图标或弱图标 |
36
- | 三级 | 飞层卡片 | 悬停二级项右侧浮出;点击进子页 |
37
- | 收起 | 宽 56px | 仅图标 + Tooltip |
35
+ | 二级 | 略缩进 | 无图标或弱图标 |
36
+ | 三级 | 飞层卡片 | 悬停二级项右侧浮出;点击进子页 |
37
+ | 收起 | 宽 56px | 仅图标 + Tooltip |
38
38
 
39
39
  ## 5. 多页文档中心文件约定
40
40