draftgo-cli 3.0.29 → 3.0.35
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.
- package/LICENSE +21 -0
- package/README.md +41 -139
- package/package.json +10 -2
- package/resources/skill/SKILL.md +61 -184
- package/resources/skill/init/SKILL.md +18 -66
- package/resources/skill/manifest.json +34 -0
- package/resources/skill/pull/SKILL.md +18 -52
- package/resources/skill/push/SKILL.md +30 -282
- package/resources/skill/references/aihub.md +86 -0
- package/resources/skill/{quickref → references}/api-endpoints.md +39 -13
- package/resources/skill/references/api.json +20248 -0
- package/resources/skill/{quickref → references}/app-api.md +40 -0
- package/resources/skill/{core → references}/architecture.md +2 -2
- package/resources/skill/references/chat-sdk.md +201 -0
- package/resources/skill/references/custom-services.md +308 -0
- package/resources/skill/{specs → references}/data.md +5 -5
- package/resources/skill/{rules → references}/frontend.md +138 -32
- package/resources/skill/{core → references}/modules.md +7 -5
- package/resources/skill/references/parallel.md +48 -0
- package/resources/skill/{specs → references}/runtime.md +1 -1
- package/resources/skill/scripts/draftgo_push.py +80 -12
- package/resources/skill/story/SKILL.md +11 -16
- package/src/cli.js +13 -7
- package/src/commandRegistry.js +34 -0
- package/src/commands/api.js +153 -8
- package/src/commands/help.js +24 -29
- package/src/commands/init.js +17 -18
- package/src/commands/local.js +9 -3
- package/src/commands/sync.js +1 -1
- package/src/commands/update.js +40 -12
- package/src/index.js +13 -57
- package/src/localdev/compose.js +44 -200
- package/src/localdev/index.js +116 -216
- package/src/localdev/mysqlClient.js +12 -9
- package/src/localdev/services.js +163 -0
- package/src/projectConfig.js +1 -1
- package/src/projectMap.js +17 -80
- package/src/skill.js +1 -1
- package/src/updateCheck.js +2 -12
- package/resources/skill/practices/anti-patterns.md +0 -80
- package/resources/skill/practices/best-practices.md +0 -60
- package/resources/skill/practices/dev-declaration.md +0 -114
- package/resources/skill/quickref/api.json +0 -17784
- package/resources/skill/rules/dev-workflow.md +0 -749
- package/resources/skill/rules/parallel.md +0 -263
- package/resources/skill/scripts/__pycache__/draftgo_pull.cpython-312.pyc +0 -0
- package/resources/skill/scripts/__pycache__/draftgo_push.cpython-312.pyc +0 -0
- package/resources/skill/specs/custom-services.md +0 -199
- package/src/commands/doctor.js +0 -54
- package/src/commands/new.js +0 -186
- package/src/commands/projectScript.js +0 -37
- package/src/commands/upgrade.js +0 -52
- /package/resources/skill/{specs → references}/db-relations.md +0 -0
- /package/resources/skill/{rules → references}/debugging-syntax.md +0 -0
- /package/resources/skill/{specs → references}/security.md +0 -0
- /package/resources/skill/{specs → references}/ui-protocol.md +0 -0
package/src/projectMap.js
CHANGED
|
@@ -485,85 +485,34 @@ function analyzeProject(projectDir) {
|
|
|
485
485
|
warnings.push(message);
|
|
486
486
|
warningDetails.push({ code, confidence, message });
|
|
487
487
|
};
|
|
488
|
-
const routeSeen = new Map();
|
|
489
|
-
const signatureSeen = new Map();
|
|
490
|
-
|
|
491
|
-
if (!exists(path.join(projectDir, '.draftgo'))) {
|
|
492
|
-
errors.push('未找到 .draftgo/,请先运行 draftgo init 或 draftgo connect。');
|
|
493
|
-
}
|
|
494
|
-
|
|
495
488
|
for (const page of map.pages) {
|
|
496
489
|
const label = `${page.title || '未命名页面'}${page.id != null ? `#${page.id}` : ''}`;
|
|
497
|
-
if (
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
errors.push(`route 重复:${page.route}(${routeSeen.get(page.route)} 与 ${label})。`);
|
|
504
|
-
} else {
|
|
505
|
-
routeSeen.set(page.route, label);
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
if (page.route !== '/' && !isProbablySystemPage(page)) {
|
|
509
|
-
const refs = map.routeRefs[page.route] || [];
|
|
510
|
-
const own = pageKey(page);
|
|
511
|
-
const externalRefs = refs.filter((s) => s !== own);
|
|
512
|
-
if (externalRefs.length === 0) {
|
|
513
|
-
addWarning('DG-ROUTE-001', 'medium', `${label} (${page.route}) 未在导航、首页或其他页面入口中发现绑定引用。`);
|
|
514
|
-
}
|
|
490
|
+
if (page.route && page.route !== '/' && !isProbablySystemPage(page)) {
|
|
491
|
+
const refs = map.routeRefs[page.route] || [];
|
|
492
|
+
const own = pageKey(page);
|
|
493
|
+
const externalRefs = refs.filter((source) => source !== own);
|
|
494
|
+
if (externalRefs.length === 0) {
|
|
495
|
+
addWarning('DG-ROUTE-001', 'medium', `${label} (${page.route}) 未在导航、首页或其他页面入口中发现绑定引用。`);
|
|
515
496
|
}
|
|
516
497
|
}
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
}
|
|
523
|
-
if (/\b(onclick|addEventListener)\b[\s\S]{0,120}\b(toast|alert)\b/i.test(html) && !/\b(App\.(post|put|delete|get)|fetch\s*\()/i.test(html)) {
|
|
524
|
-
addWarning('DG-CLOSURE-001', 'medium', `${label} 疑似只有反馈提示、缺少真实数据读写或 API 调用。`);
|
|
525
|
-
}
|
|
526
|
-
reportPaginationHack(label, html, addWarning);
|
|
527
|
-
reportColorThemeRisk(label, html, addWarning);
|
|
528
|
-
const signature = normalizeHtmlSkeleton(html);
|
|
529
|
-
if (signature) {
|
|
530
|
-
if (!signatureSeen.has(signature)) signatureSeen.set(signature, []);
|
|
531
|
-
signatureSeen.get(signature).push({ label, area: page.area });
|
|
532
|
-
}
|
|
533
|
-
const parseErrors = htmlParseErrors(html);
|
|
534
|
-
for (const parseError of parseErrors.slice(0, 5)) {
|
|
535
|
-
const at = parseError.line ? `(行 ${parseError.line}${parseError.column ? `:${parseError.column}` : ''})` : '';
|
|
536
|
-
addWarning('DG-HTML-001', 'high', `${label} HTML 解析提醒:${parseError.code}${at}。`);
|
|
537
|
-
}
|
|
498
|
+
if (!page.html_file) continue;
|
|
499
|
+
const html = readTextSafe(relToAbs(projectDir, page.html_file));
|
|
500
|
+
const parseErrors = htmlParseErrors(html);
|
|
501
|
+
for (const parseError of parseErrors.slice(0, 5)) {
|
|
502
|
+
const at = parseError.line ? `(行 ${parseError.line}${parseError.column ? `:${parseError.column}` : ''})` : '';
|
|
503
|
+
addWarning('DG-HTML-001', 'high', `${label} HTML 解析提醒:${parseError.code}${at}。`);
|
|
538
504
|
}
|
|
539
505
|
}
|
|
540
506
|
|
|
541
|
-
const adminCount = map.pageGroups.admin.length;
|
|
542
|
-
const maintainableBusinessPages = map.pageGroups.business.filter(needsAdminCompanion);
|
|
543
|
-
if (maintainableBusinessPages.length > 0 && adminCount === 0) {
|
|
544
|
-
addWarning(
|
|
545
|
-
'DG-ADMIN-001',
|
|
546
|
-
'medium',
|
|
547
|
-
`检测到 ${maintainableBusinessPages.length} 个具有可维护内容信号的业务页面,但未发现管理端页面;若内容需要运营维护,建议补管理端页面或明确说明不需要。`,
|
|
548
|
-
);
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
for (const labels of signatureSeen.values()) {
|
|
552
|
-
const nonSystem = labels.filter((item) => item.area !== 'system');
|
|
553
|
-
if (nonSystem.length < 3) continue;
|
|
554
|
-
addWarning(
|
|
555
|
-
'DG-DUPLICATE-001',
|
|
556
|
-
'low',
|
|
557
|
-
`发现 ${nonSystem.length} 个结构几乎一致的页面,疑似工作台副本:${nonSystem.map((item) => item.label).join('、')}。建议复刻内置导航/侧栏后按业务差异拆分,不要整页复制。`,
|
|
558
|
-
);
|
|
559
|
-
break;
|
|
560
|
-
}
|
|
561
|
-
|
|
562
507
|
for (const nav of map.navigations) {
|
|
563
508
|
if (!nav.html_file) continue;
|
|
564
509
|
const navLabel = `导航${nav.name || nav.code || nav.id || ''}`;
|
|
565
510
|
const html = readTextSafe(relToAbs(projectDir, nav.html_file));
|
|
566
|
-
|
|
511
|
+
const parseErrors = htmlParseErrors(html);
|
|
512
|
+
for (const parseError of parseErrors.slice(0, 5)) {
|
|
513
|
+
const at = parseError.line ? `(行 ${parseError.line}${parseError.column ? `:${parseError.column}` : ''})` : '';
|
|
514
|
+
addWarning('DG-HTML-001', 'high', `${navLabel} HTML 解析提醒:${parseError.code}${at}。`);
|
|
515
|
+
}
|
|
567
516
|
}
|
|
568
517
|
|
|
569
518
|
const orphanGroups = [
|
|
@@ -579,18 +528,6 @@ function analyzeProject(projectDir) {
|
|
|
579
528
|
}
|
|
580
529
|
}
|
|
581
530
|
|
|
582
|
-
for (const script of map.custom_scripts) {
|
|
583
|
-
if (String(script.mode || '').toLowerCase() !== 'route' || !script.code_file) continue;
|
|
584
|
-
const code = readTextSafe(relToAbs(projectDir, script.code_file));
|
|
585
|
-
if (/\bdef\s+handle\s*\(/.test(code) && !/@route\s*\(/.test(code)) {
|
|
586
|
-
errors.push(`自定义脚本${script.name || script.slug || script.id}#${script.id} 是 route 模式但只有 handle(),缺少 @route("METHOD /path") 注册,HTTP 端点不会命中。`);
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
if (map.pages.length === 0 && exists(path.join(projectDir, '.draftgo'))) {
|
|
591
|
-
addWarning('DG-CACHE-001', 'low', '未发现 pages/index.json 页面缓存;开发前建议先拉取页面。');
|
|
592
|
-
}
|
|
593
|
-
|
|
594
531
|
return { map, errors, warnings, warningDetails };
|
|
595
532
|
}
|
|
596
533
|
|
package/src/skill.js
CHANGED
|
@@ -22,7 +22,7 @@ const {
|
|
|
22
22
|
|
|
23
23
|
const SKILL_SOURCE_DIR = path.join(RESOURCES_DIR, 'skill');
|
|
24
24
|
const SHARED_RESOURCE_DIR = path.join('.draftgo', 'skill-shared');
|
|
25
|
-
const SHARED_FILES = new Set(['
|
|
25
|
+
const SHARED_FILES = new Set(['references/api.json']);
|
|
26
26
|
|
|
27
27
|
function getPackageVersion() {
|
|
28
28
|
try {
|
package/src/updateCheck.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
// Fetch the latest draftgo-cli version from the npm registry.
|
|
4
|
-
// Results are cached for 24 hours so
|
|
4
|
+
// Results are cached for 24 hours so update stays fast and deterministic.
|
|
5
5
|
|
|
6
6
|
const https = require('https');
|
|
7
7
|
const fs = require('fs');
|
|
8
8
|
const os = require('os');
|
|
9
9
|
const path = require('path');
|
|
10
|
-
const { getPackageVersion } = require('./skill');
|
|
11
10
|
|
|
12
11
|
const REGISTRY_URL = 'https://registry.npmjs.org/draftgo-cli/latest';
|
|
13
12
|
const CACHE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
@@ -78,13 +77,4 @@ function cmpSemver(a, b) {
|
|
|
78
77
|
return 0;
|
|
79
78
|
}
|
|
80
79
|
|
|
81
|
-
|
|
82
|
-
async function getFreshness() {
|
|
83
|
-
if (process.env.DRAFTGO_NO_UPDATE_CHECK === '1') return null;
|
|
84
|
-
const current = getPackageVersion();
|
|
85
|
-
const latest = await fetchLatestVersion();
|
|
86
|
-
if (!latest) return null;
|
|
87
|
-
return { current, latest, outdated: cmpSemver(latest, current) > 0 };
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
module.exports = { fetchLatestVersion, cmpSemver, getFreshness, readCache, CACHE_TTL_MS };
|
|
80
|
+
module.exports = { fetchLatestVersion, cmpSemver, readCache, CACHE_TTL_MS };
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
read_when: 开发前检查思路 · Code Review 时
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
# 反模式清单
|
|
6
|
-
|
|
7
|
-
## 功能完成度反模式
|
|
8
|
-
|
|
9
|
-
| 反模式 | 问题 | 正确做法 |
|
|
10
|
-
|---|---|---|
|
|
11
|
-
| 静态卡片充当功能 | 按钮点击无效,数据硬编码 | 默认按真实落地闭环实现;用户说"先看效果"才允许 mock |
|
|
12
|
-
| 只建页面不绑入口 | 页面存在但无法访问 | 创建页面后必须绑定至少一个入口 |
|
|
13
|
-
| 只做前台不做后台 | 展示数据没有维护入口 | 展示可维护内容时主动判断是否需要管理侧页面 |
|
|
14
|
-
| 业务页批量复制工作台模板 | 页面之间只有标题不同,体验同质化 | 按业务差异拆分列表/详情/编辑/管理结构,不要整页复制 |
|
|
15
|
-
| 新导航直接改坏内置导航 | 容易丢失原有路由和配置能力 | 需要新导航时先复制一份内置导航再改 |
|
|
16
|
-
| 新系统不做范围清点就开工 | 页面、功能、数据和管理端边界混乱 | 通常先在 Task 写轻量 PRD,清点页面/功能/数据/入口后再开发 |
|
|
17
|
-
| 伪功能写入 lessons 前不说明 | 用户不知道功能不完整 | 无法实现真实闭环时停止开发,向用户说明阻塞点 |
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## 架构理解反模式
|
|
22
|
-
|
|
23
|
-
| 反模式 | 问题 |
|
|
24
|
-
|---|---|
|
|
25
|
-
| 在数据库页面写 TSX / React 组件 | 数据库页面不进入 Vite 编译链 |
|
|
26
|
-
| 在业务页面渲染系统 Header / 导航 | Header 属于壳层,页面渲染会重复 |
|
|
27
|
-
| 无视现有导航直接重做顶部栏/后台侧栏 | 容易丢失现有路由、权限入口和配置能力 |
|
|
28
|
-
| 直接修改系统内置页面承接业务需求 | 容易破坏管理员能力和平台升级兼容性 |
|
|
29
|
-
| 把普通用户也暴露到管理后台入口 | 容易污染前台语境 | 普通用户不显示管理后台,管理员额外显示 |
|
|
30
|
-
| 用 `/admin/db` 承接全部运营功能 | 业务域能力和底层数据能力混在一起 | 业务后台按域拆分,`/admin/db` 只保留通用数据能力 |
|
|
31
|
-
| 修改壳层 `.tsx` 文件来改页面内容 | 页面内容在数据库里,改壳层源码不对 |
|
|
32
|
-
|
|
33
|
-
---
|
|
34
|
-
|
|
35
|
-
## API 调用反模式
|
|
36
|
-
|
|
37
|
-
| 反模式 | 问题 | 正确做法 |
|
|
38
|
-
|---|---|---|
|
|
39
|
-
| 不检查 `res.code` 直接用 `res.data` | 报错时会访问 null | 先 `if (res.code !== 200)` 处理错误 |
|
|
40
|
-
| 大数据表格不传分页参数 | 后端会全量返回,首屏可能慢 | 表格/后台列表显式传 `page` + `page_size` |
|
|
41
|
-
| `GET /db-meta/{id}` 用 id 查 type | 返回"元数据不存在" | 用 type 查:`GET /db-meta/{type}` |
|
|
42
|
-
| 动态 DB POST 时字段平铺到 body 顶层 | 字段被忽略 | 业务字段必须放在 `data: {}` 里 |
|
|
43
|
-
| 在页面里写死 API Key / Bearer | 密钥暴露在前端 | 在 Go 自定义服务中使用 `ctx.HTTP`,页面调用其 route 端点 |
|
|
44
|
-
|
|
45
|
-
---
|
|
46
|
-
|
|
47
|
-
## 路由 & DOM 反模式
|
|
48
|
-
|
|
49
|
-
| 反模式 | 问题 | 正确做法 |
|
|
50
|
-
|---|---|---|
|
|
51
|
-
| `window.location.search` 读参数 | iframe 里取不到壳层 URL | `window.__DG_ROUTE_CONTEXT__.query` |
|
|
52
|
-
| `window.location.href = '/path'` 跳转 | 只跳 iframe 自身,壳层不感知 | `window.parent.location.href = '/path'` |
|
|
53
|
-
| `window.alert/confirm/prompt` | 阻塞主线程,iframe 行为不可预期 | `App.showModal` / `App.confirm` |
|
|
54
|
-
|
|
55
|
-
---
|
|
56
|
-
|
|
57
|
-
## 开发流程反模式
|
|
58
|
-
|
|
59
|
-
| 反模式 | 问题 |
|
|
60
|
-
|---|---|
|
|
61
|
-
| 高风险任务没有 Story 就动手 | 方向错了才发现,返工代价大 |
|
|
62
|
-
| 小修也走完整 Story 门 | 拖慢速度,过度流程化 |
|
|
63
|
-
| 发现与 Story 冲突时静默执行 | 覆盖已确认的需求 |
|
|
64
|
-
| 页面改了但没更新导航入口 | 用户找不到新功能 |
|
|
65
|
-
| 开发前完全不读对应规则文档 | 容易误改系统页、误用导航、误写平台语法 |
|
|
66
|
-
| 每次开发都从头读全部规则 | 按任务规模渐进读取,小修只读最小必要规则 |
|
|
67
|
-
|
|
68
|
-
---
|
|
69
|
-
|
|
70
|
-
## 性能 & 体验反模式
|
|
71
|
-
|
|
72
|
-
| 反模式 | 问题 |
|
|
73
|
-
|---|---|
|
|
74
|
-
| 请求完成前页面空白 | 先渲染骨架屏,再异步填数据 |
|
|
75
|
-
| 操作型页面用文档流堆叠 | 分页/操作栏随少量数据上浮 |
|
|
76
|
-
| 只按当前主题硬编码颜色 | 亮暗切换后颜色异常,应优先用系统 token,自主配色也要同时覆盖浅色与深色 |
|
|
77
|
-
| 弹窗内容区自带双滚动条 | 体验粗糙、难以操作 | 弹窗/抽屉优先隐藏外层滚动条,只让内容区滚动 |
|
|
78
|
-
| 下拉选择器直接用原生 select / 系统默认下拉样式 | 视觉和交互过于默认,难以和页面风格一致 | 优先做自定义弹层菜单 |
|
|
79
|
-
| 前端完全没有动效层次 | 页面显得僵硬 | 在关键转场、状态变化和层级切换处合理使用 GSAP |
|
|
80
|
-
| 引入境外 CDN | 国内访问慢/超时 |
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
read_when: 开发过程中做决策时 · Code Review 时
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
# 最佳实践
|
|
6
|
-
|
|
7
|
-
## 服务设计
|
|
8
|
-
|
|
9
|
-
- **优先用平台能力**:动态 DB → 自定义服务,按复杂度递进,不要绕过平台直接硬编码
|
|
10
|
-
- **db_meta 先于代码**:操作动态 DB 前先读 `.draftgo/db_meta/index.json`,不要硬编码 type 值
|
|
11
|
-
- **权限跟数据一起设计**:创建 db_meta 时同时设计 `permission` 字段,不要事后补
|
|
12
|
-
- **schema_validation 开启**:动态 DB 数据写入时建议开启 `schema_validation: 1`,及早发现数据问题
|
|
13
|
-
- **关联一致性放在 schema**:A 被 B 引用时,优先在 B 的真实引用字段配置 `ref.onDelete`,不要让前端连续请求多个删除接口来模拟级联
|
|
14
|
-
|
|
15
|
-
## 页面开发
|
|
16
|
-
|
|
17
|
-
- **先分配空间,再放组件**:操作型页面用工作台布局(flex column + 主体 flex:1 min-height:0),不用文档流堆叠
|
|
18
|
-
- **四态必须都有**:加载态(dg-skeleton)/ 空态(明确文案 + 操作入口)/ 错误态(Toast + 重试)/ 成功态
|
|
19
|
-
- **初始渲染不能空白**:先展示骨架屏,再异步填数据;不能因为网络延迟导致整页白屏
|
|
20
|
-
- **新页面必须绑定入口**:导航栏 / 首页模块 / 后台菜单 / 相关页面按钮至少一个;只建文件不绑入口不算完成
|
|
21
|
-
- **导航先复刻再改**:顶部导航和管理端侧边栏通常先基于现有内置导航资源复制/改造,再补业务路由和视觉优化
|
|
22
|
-
- **导航状态要完整**:导航通常同时考虑未登录、已登录、管理员可见性,以及收起/展开状态;普通用户不显示管理后台入口
|
|
23
|
-
- **业务先判断管理端**:案例、新闻、产品、订单、预约、资料等内容优先判断前台展示、管理端维护和同一份真实数据
|
|
24
|
-
- **业务后台按域拆分**:运营日常使用的后台能力优先做业务域管理页并注册到后台侧边栏;`/admin/db` 保留给底层通用数据能力
|
|
25
|
-
- **首页先定定位**:首页通常先按官网介绍、品牌门面和入口聚合设计,除非用户明确要“进入即使用”
|
|
26
|
-
- **先补关键角色路径**:优先明确前台用户和管理员/运营从哪里进入、在哪里操作、怎么回到结果页;简单改动不必机械画全量地图
|
|
27
|
-
- **新系统先落轻量 PRD**:新系统/新板块/新模块先在 `.draftgo/Task/` 写清页面清单、功能清单、数据、入口和验收证据,再开始实现
|
|
28
|
-
- **功能先对齐意图**:新功能先确认“解决谁的什么问题”,再谈页面数、数据源和后台;不要直接跳到实现细节
|
|
29
|
-
- **体验优先本地化**:弹窗/抽屉尽量去滚动条,涉及下拉选项样式时优先做自定义弹层菜单,避免系统默认下拉样式;动效可合理使用本地 GSAP,但不要抢可用性
|
|
30
|
-
- **FilterPill + Popover 筛选胶囊**:列表页筛选推荐这个组合——胶囊按钮触发浮层,浮层内放筛选表单/选项;不阻塞操作,视觉轻量,适合多条件筛选;参考 admin-pages.html 的 `.filter-popover-wrapper` + `.filter-popover` 实现(胶囊按钮 + 独立浮层)
|
|
31
|
-
- **FilterPill + Popove 展开式筛选行**:另一种常见模式——工具栏右侧放"展开筛选"按钮,点击后筛选条件直接展开在工具栏下方;适合筛选项少且固定场景;参考 admin-notices.html 的 `filterOpen && h('div', { className: 'pg-filter-row' }, ...)` 实现(Antd Select 下拉,展开行内横排)
|
|
32
|
-
|
|
33
|
-
## API 调用
|
|
34
|
-
|
|
35
|
-
- **统一信封消费**:始终检查 `res.code !== 200`,再从 `res.data` 取载荷;不要假设 200
|
|
36
|
-
- **按场景选择分页**:列表请求不传 `page` / `page_size` 会全量返回且无上限;页面表格、管理后台和大数据量场景应显式传 `page` + `page_size`
|
|
37
|
-
- **第三方调用走自定义服务**:不要在页面里写 API Key / Bearer;在 Go 服务端用 `ctx.HTTP` 请求并通过 route 暴露受控端点
|
|
38
|
-
|
|
39
|
-
## 资源路径
|
|
40
|
-
|
|
41
|
-
- **静态资源用本地路径**:`/assets/tailwindcss.js`、`/assets/fontawesome/css/all.min.css`、`/assets/icons/{name}.svg`、`/assets/vendor/html2canvas/html2canvas.min.js`
|
|
42
|
-
- **图标用内置库**:先查 `/assets/icons/manifest.json`,已有的直接用,不要引外部图标
|
|
43
|
-
|
|
44
|
-
## 主题适配
|
|
45
|
-
|
|
46
|
-
- **优先系统主题配色**:默认使用 `var(--dg-accent)`、`var(--dg-bg-surface)` 等语义 token,继承当前浅色 / 深色主题
|
|
47
|
-
- **自主配色要双模式**:页面不搭配、品牌要求、用户明确指定或图表需要时可自定义,但必须提供浅色与深色两套变量或覆盖
|
|
48
|
-
- **品牌色 / 图表色例外**:仍要确保两种模式下对比度达 WCAG AA,避免亮暗切换后颜色失效
|
|
49
|
-
|
|
50
|
-
## 推送收尾
|
|
51
|
-
|
|
52
|
-
- **修改页面同时推导航**:如果页面新增了导航入口,导航 HTML 也要一起 push
|
|
53
|
-
- **changelog 按影响写**:影响可见功能、跨资源、已发布时写 `.draftgo/changelog.md`,纯小修可跳过
|
|
54
|
-
- **用 draftgo check 验收**:涉及路由、入口绑定的改动,用 `draftgo check` 确认没有悬空入口
|
|
55
|
-
|
|
56
|
-
## 规则分层
|
|
57
|
-
|
|
58
|
-
- **高频高代价规则**:优先内联到必经路径,不要只放在按需读文件里
|
|
59
|
-
- **低频明确触发规则**:保留外链,但必须写出触发条件
|
|
60
|
-
- **低频低代价规则**:保持外链即可
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
read_when: 开始任何标准功能/高风险开发任务之前 · 用户意图模糊需要澄清时
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
# 开发声明协议
|
|
6
|
-
|
|
7
|
-
开发声明是在正式动代码前,AI 向用户同步「我理解的任务边界」,确保方向对齐、避免返工。
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## 触发条件
|
|
12
|
-
|
|
13
|
-
满足以下任一条件时,输出开发声明:
|
|
14
|
-
|
|
15
|
-
- 标准功能或高风险任务(按 `dev-workflow.md` 分级)
|
|
16
|
-
- 涉及 2 个以上资源(页面 + 导航、多页面联动、DB + 页面等)
|
|
17
|
-
- 新系统 / 新板块 / 新模块 / 完整业务能力,需要先产出轻量 PRD 与页面/功能清单
|
|
18
|
-
- 用户描述包含模糊词(「做一个...」「帮我整一个...」「类似...的功能」)
|
|
19
|
-
- 实现路径有分叉点(多种技术方案可选)
|
|
20
|
-
- 用户意图与现有 Story 存在潜在冲突
|
|
21
|
-
|
|
22
|
-
**不触发**:小修、单文件纯改、用户已提供完整规格的任务。
|
|
23
|
-
|
|
24
|
-
---
|
|
25
|
-
|
|
26
|
-
## 信息充分性判断
|
|
27
|
-
|
|
28
|
-
输出声明前,检查是否掌握以下信息:
|
|
29
|
-
|
|
30
|
-
| 必须已知 | 可推断 | 需要追问 |
|
|
31
|
-
|---|---|---|
|
|
32
|
-
| 功能目标(做什么) | 技术实现路径 | 数据来源(DB类型/字段) |
|
|
33
|
-
| 目标页面/路由 | 权限配置 | 是否需要管理侧页面 |
|
|
34
|
-
| 核心交互流程 | UI 风格偏好 | 与已有功能的关系 |
|
|
35
|
-
| 入口位置/导航归属 | 角色路径地图 | 是否必须保留系统内置入口 |
|
|
36
|
-
| 导航状态 | 登录态 / 管理员态 / 收起展开 | 是否需要区分未登录 / 已登录 / 管理员 |
|
|
37
|
-
|
|
38
|
-
信息充分 → 直接输出声明
|
|
39
|
-
关键信息缺失 → 先追问(最多 3 个问题),再输出声明
|
|
40
|
-
|
|
41
|
-
---
|
|
42
|
-
|
|
43
|
-
## 追问格式(适配选择式交互)
|
|
44
|
-
|
|
45
|
-
追问必须带 AI 的推测和选项,不能空问:
|
|
46
|
-
|
|
47
|
-
```
|
|
48
|
-
我准备这样理解这个任务,请确认或调整:
|
|
49
|
-
|
|
50
|
-
0. **功能意图**(我的推测:先对齐这个功能要解决谁的什么问题)
|
|
51
|
-
A. 解决明确业务问题,给明确角色使用(推荐)
|
|
52
|
-
B. 内部/运营工具,不直接面向前台用户
|
|
53
|
-
C. 先做 MVP,方向后续再收敛
|
|
54
|
-
|
|
55
|
-
1. **数据来源**(我的推测:用动态 DB 新建 `order` 类型)
|
|
56
|
-
A. 用动态 DB 新建(推荐)
|
|
57
|
-
B. 复用已有 DB 类型:___
|
|
58
|
-
C. 用系统配置 KV 存储
|
|
59
|
-
|
|
60
|
-
2. **管理侧**(我的推测:通常需要一个后台管理页)
|
|
61
|
-
A. 需要,用户提交 + 管理员审核
|
|
62
|
-
B. 不需要,纯用户自助
|
|
63
|
-
C. 后续再做
|
|
64
|
-
|
|
65
|
-
3. **导航状态**(我的推测:通常需要区分登录态和管理员态)
|
|
66
|
-
A. 需要,未登录 / 已登录 / 管理员都要考虑
|
|
67
|
-
B. 只做已登录态
|
|
68
|
-
C. 仅单一静态入口
|
|
69
|
-
|
|
70
|
-
4. **入口位置**(我的推测:通常加到顶部导航)
|
|
71
|
-
A. 顶部导航
|
|
72
|
-
B. 首页模块入口
|
|
73
|
-
C. 仅通过链接/按钮访问,不加导航
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
---
|
|
77
|
-
|
|
78
|
-
## 开发声明输出格式
|
|
79
|
-
|
|
80
|
-
```markdown
|
|
81
|
-
## 开发声明
|
|
82
|
-
|
|
83
|
-
**任务**:[一句话描述目标]
|
|
84
|
-
|
|
85
|
-
**我将要做的事**:
|
|
86
|
-
- [ ] [资源 1]:[具体内容]
|
|
87
|
-
- [ ] [资源 2]:[具体内容]
|
|
88
|
-
- [ ] [入口绑定]:[绑定位置]
|
|
89
|
-
- [ ] [PRD/Task]:在 `.draftgo/Task/` 记录页面清单、功能清单、数据、角色路径、导航状态和管理闭环
|
|
90
|
-
|
|
91
|
-
**意图**:[解决什么问题,给谁用]
|
|
92
|
-
|
|
93
|
-
**数据方案**:[DB类型 / 字段设计 / 权限配置]
|
|
94
|
-
|
|
95
|
-
**角色路径 / 管理闭环**:[前台用户怎么进入,管理员/运营怎么维护,是否保留现有系统入口]
|
|
96
|
-
|
|
97
|
-
**不做的事**:[明确排除的内容,避免范围蔓延]
|
|
98
|
-
|
|
99
|
-
**风险点**:[如有,说明;无则省略]
|
|
100
|
-
|
|
101
|
-
确认后开始。
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
新系统 / 新板块场景下,这份声明后续应沉淀到 `.draftgo/Task/YYYY-MM-DD-<topic>.md`,并扩展出轻量 PRD。PRD 不追求冗长,但必须清点页面、功能、数据、入口、管理端、导航状态和验收证据。
|
|
105
|
-
|
|
106
|
-
---
|
|
107
|
-
|
|
108
|
-
## 关键原则
|
|
109
|
-
|
|
110
|
-
1. **声明是同步,不是审批**——输出后等用户一句确认(「可以」「没问题」「开始」均算确认),不必等用户逐条review。
|
|
111
|
-
2. **推测要带自信**——「我推测」不是「我不确定」,是「我基于上下文的最优判断」;若用户不纠正则视为接受。
|
|
112
|
-
3. **一次声明,不反复打断**——声明输出后开始执行,中途不再停下来重复确认细节,除非遇到破坏性操作。
|
|
113
|
-
4. **小修不触发**——小修直接定位改,拖慢节奏的声明反而是反模式。
|
|
114
|
-
5. **声明后发现冲突**——若执行中发现与 Story 或现有资源冲突,停止并显式提示,不静默执行。
|