@deepstorm/cli 0.1.0
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/README.md +72 -0
- package/dist/agents/reef-inspect-figma.md +77 -0
- package/dist/agents/reef-review-backend.md.tmpl +112 -0
- package/dist/agents/reef-review-frontend.md.tmpl +78 -0
- package/dist/agents/reef-review-infra.md +47 -0
- package/dist/agents/reef-review-security.md.tmpl +80 -0
- package/dist/agents/reef-scope-analysis.md +64 -0
- package/dist/build-registry.js +375 -0
- package/dist/cli.js +8581 -0
- package/dist/config-schema.json +133 -0
- package/dist/env-examples/context7.env-example +19 -0
- package/dist/env-examples/feishu-wiki.env-example +16 -0
- package/dist/env-examples/figma.env-example +16 -0
- package/dist/env-examples/github.env-example +20 -0
- package/dist/env-examples/jira.env-example +20 -0
- package/dist/hooks/mcp-hook.sh +77 -0
- package/dist/hooks/reef-auto-format.sh.tmpl +72 -0
- package/dist/hooks/reef-block-dangerous.sh +70 -0
- package/dist/hooks/reef-hooks.json +72 -0
- package/dist/hooks/reef-intent-detect.sh +129 -0
- package/dist/hooks/reef-protect-files.sh +55 -0
- package/dist/hooks/reef-run-tests.sh +84 -0
- package/dist/hooks/reef-scope-check.sh +386 -0
- package/dist/hooks/reef-scope-ci.sh +28 -0
- package/dist/hooks/reef-scope-gate.sh +115 -0
- package/dist/hooks/reef-scope-pre-commit.sh.tmpl +28 -0
- package/dist/hooks/reef-scope-setup.sh +204 -0
- package/dist/hooks/reef-scope-split.sh +203 -0
- package/dist/hooks/sweep-hooks.json +14 -0
- package/dist/hooks/sweep-mcp-hook.sh +77 -0
- package/dist/hooks/tide-hooks.json +14 -0
- package/dist/hooks/tide-session-preload.sh +17 -0
- package/dist/mcp/code-hosting/github.json +20 -0
- package/dist/mcp/design-tools/figma.json +19 -0
- package/dist/mcp/docs-reference/context7.json +28 -0
- package/dist/mcp/e2e-testing/playwright.json +13 -0
- package/dist/mcp/knowledge-base/feishu-wiki.json +19 -0
- package/dist/mcp/project-management/jira.json +27 -0
- package/dist/mcp-skills/deepflow-mcp-feishu-wiki-read/SKILL.md +65 -0
- package/dist/mcp-skills/deepflow-mcp-feishu-wiki-write/SKILL.md +63 -0
- package/dist/mcp-skills/deepflow-mcp-figma-read/SKILL.md +98 -0
- package/dist/mcp-skills/deepflow-mcp-github-read/SKILL.md +62 -0
- package/dist/mcp-skills/deepflow-mcp-github-write/SKILL.md +63 -0
- package/dist/mcp-skills/deepflow-mcp-jira-read/SKILL.md +80 -0
- package/dist/mcp-skills/deepflow-mcp-jira-write/SKILL.md +74 -0
- package/dist/mcp-skills/deepflow-mcp-playwright-read/SKILL.md +79 -0
- package/dist/mcp-skills/deepstorm-mcp-feishu-wiki-read/SKILL.md +65 -0
- package/dist/mcp-skills/deepstorm-mcp-feishu-wiki-write/SKILL.md +63 -0
- package/dist/mcp-skills/deepstorm-mcp-figma-read/SKILL.md +98 -0
- package/dist/mcp-skills/deepstorm-mcp-github-read/SKILL.md +62 -0
- package/dist/mcp-skills/deepstorm-mcp-github-write/SKILL.md +63 -0
- package/dist/mcp-skills/deepstorm-mcp-jira-read/SKILL.md +80 -0
- package/dist/mcp-skills/deepstorm-mcp-jira-write/SKILL.md +74 -0
- package/dist/mcp-skills/deepstorm-mcp-playwright-read/SKILL.md +79 -0
- package/dist/registry.json +818 -0
- package/dist/skills/atoll-ops/SKILL.md +46 -0
- package/dist/skills/reef-commit/SKILL.md +127 -0
- package/dist/skills/reef-gen-backend/SKILL.md.tmpl +87 -0
- package/dist/skills/reef-gen-backend/variants/java/steps.md +28 -0
- package/dist/skills/reef-gen-backend/variants/python/steps.md +70 -0
- package/dist/skills/reef-gen-frontend/SKILL.md.tmpl +83 -0
- package/dist/skills/reef-gen-frontend/variants/angular/steps.md +30 -0
- package/dist/skills/reef-harden/EXAMPLES.md +89 -0
- package/dist/skills/reef-harden/SKILL.md +136 -0
- package/dist/skills/reef-pr/SKILL.md +97 -0
- package/dist/skills/reef-review/SKILL.md.tmpl +107 -0
- package/dist/skills/reef-scope/SKILL.md +134 -0
- package/dist/skills/reef-start/SKILL.md.tmpl +562 -0
- package/dist/skills/reef-start/references/jira-start-subagent.md +60 -0
- package/dist/skills/reef-style-backend/SKILL.md.tmpl +134 -0
- package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/chat-client.md +96 -0
- package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/rag.md +94 -0
- package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/structured-output.md +62 -0
- package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/tool-calling.md +68 -0
- package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/quick-reference.md +220 -0
- package/dist/skills/reef-style-backend/fragments/java/api-spec/quick-reference.md +148 -0
- package/dist/skills/reef-style-backend/fragments/java/db-migration/liquibase/examples/database-migration.md +131 -0
- package/dist/skills/reef-style-backend/fragments/java/db-migration/liquibase/quick-reference.md +103 -0
- package/dist/skills/reef-style-backend/fragments/java/dependency-management/quick-reference.md +119 -0
- package/dist/skills/reef-style-backend/fragments/java/exception-handling/examples/error-code-enum.md +101 -0
- package/dist/skills/reef-style-backend/fragments/java/exception-handling/quick-reference.md +181 -0
- package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/controller.md +95 -0
- package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/dto-mapper.md +121 -0
- package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/infrastructure.md +179 -0
- package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/service-entity.md +202 -0
- package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/testing.md +107 -0
- package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/quick-reference.md +83 -0
- package/dist/skills/reef-style-backend/fragments/java/orm/hibernate/quick-reference.md +150 -0
- package/dist/skills/reef-style-backend/fragments/java/security-redlines/quick-reference.md +197 -0
- package/dist/skills/reef-style-backend/fragments/java/test/data-jpa-test/examples/user-repository-test.md +69 -0
- package/dist/skills/reef-style-backend/fragments/java/test/data-jpa-test/quick-reference.md +101 -0
- package/dist/skills/reef-style-backend/fragments/java/test/junit5/examples/user-service-test.md +61 -0
- package/dist/skills/reef-style-backend/fragments/java/test/junit5/quick-reference.md +100 -0
- package/dist/skills/reef-style-backend/fragments/java/test/spring-mvc-test/examples/user-controller-test.md +61 -0
- package/dist/skills/reef-style-backend/fragments/java/test/spring-mvc-test/quick-reference.md +85 -0
- package/dist/skills/reef-style-backend/fragments/java/test/spring-service-test/examples/user-service-integration-test.md +56 -0
- package/dist/skills/reef-style-backend/fragments/java/test/spring-service-test/quick-reference.md +83 -0
- package/dist/skills/reef-style-backend/fragments/python/alembic-migration/quick-reference.md +77 -0
- package/dist/skills/reef-style-backend/fragments/python/api-spec/quick-reference.md +164 -0
- package/dist/skills/reef-style-backend/fragments/python/dependency-management/quick-reference.md +139 -0
- package/dist/skills/reef-style-backend/fragments/python/exception-handling/quick-reference.md +177 -0
- package/dist/skills/reef-style-backend/fragments/python/fastapi-quick-reference/quick-reference.md +101 -0
- package/dist/skills/reef-style-backend/fragments/python/langchain/quick-reference.md +135 -0
- package/dist/skills/reef-style-backend/fragments/python/pytest-testing/quick-reference.md +111 -0
- package/dist/skills/reef-style-backend/fragments/python/ruff-mypy-toolchain/quick-reference.md +83 -0
- package/dist/skills/reef-style-backend/fragments/python/security-redlines/quick-reference.md +207 -0
- package/dist/skills/reef-style-backend/fragments/python/sqlalchemy-orm/quick-reference.md +91 -0
- package/dist/skills/reef-style-backend/variants/java/examples/code-wrapping.md +227 -0
- package/dist/skills/reef-style-backend/variants/java/examples/contributor-pattern.md +97 -0
- package/dist/skills/reef-style-backend/variants/java/quick-reference.md +117 -0
- package/dist/skills/reef-style-backend/variants/python/examples/crud-router.md +71 -0
- package/dist/skills/reef-style-backend/variants/python/examples/pydantic-schema.md +45 -0
- package/dist/skills/reef-style-backend/variants/python/examples/pytest-fixture.md +60 -0
- package/dist/skills/reef-style-backend/variants/python/examples/sqlalchemy-model.md +31 -0
- package/dist/skills/reef-style-backend/variants/python/quick-reference.md +111 -0
- package/dist/skills/reef-style-frontend/SKILL.md.tmpl +70 -0
- package/dist/skills/reef-style-frontend/fragments/css/tailwind/quick-reference.md +79 -0
- package/dist/skills/reef-style-frontend/fragments/test/vitest/examples/testing.md +150 -0
- package/dist/skills/reef-style-frontend/fragments/test/vitest/quick-reference.md +141 -0
- package/dist/skills/reef-style-frontend/fragments/ts-config/strict/quick-reference.md +112 -0
- package/dist/skills/reef-style-frontend/fragments/ui-lib/primeng/examples/ui-components.md +134 -0
- package/dist/skills/reef-style-frontend/fragments/ui-lib/primeng/quick-reference.md +66 -0
- package/dist/skills/reef-style-frontend/variants/angular/examples/code-wrapping.md +252 -0
- package/dist/skills/reef-style-frontend/variants/angular/examples/component-types-pipes.md +56 -0
- package/dist/skills/reef-style-frontend/variants/angular/examples/entity-types.md +100 -0
- package/dist/skills/reef-style-frontend/variants/angular/examples/forms-layer.md +119 -0
- package/dist/skills/reef-style-frontend/variants/angular/examples/service-routing.md +95 -0
- package/dist/skills/reef-style-frontend/variants/angular/quick-reference.md +100 -0
- package/dist/skills/reef-testcase/SKILL.md +138 -0
- package/dist/skills/reef-testcase/references/coverage-dimensions.md +99 -0
- package/dist/skills/reef-testcase/references/test-case-template.md +63 -0
- package/dist/skills/sweep-init/SKILL.md +286 -0
- package/dist/skills/sweep-init/scripts/flow-selector.mjs +305 -0
- package/dist/skills/sweep-plan/SKILL.md.tmpl +312 -0
- package/dist/skills/sweep-plan/references/test-flow-template.md +46 -0
- package/dist/skills/sweep-run/SKILL.md +437 -0
- package/dist/skills/sweep-run/scripts/env-manager.mjs +208 -0
- package/dist/skills/sweep-run/scripts/flow-parser.mjs +329 -0
- package/dist/skills/sweep-run/scripts/flow-selector.mjs +483 -0
- package/dist/skills/sweep-run/scripts/mcp-manager.mjs +208 -0
- package/dist/skills/sweep-run/scripts/spec-compiler.mjs +303 -0
- package/dist/skills/tide-discuss/SKILL.md.tmpl +449 -0
- package/dist/skills/tide-discuss/references/checklists.md +88 -0
- package/dist/skills/tide-discuss/references/data-format.md +237 -0
- package/dist/skills/tide-discuss/references/prd-template.md +134 -0
- package/dist/skills/tide-discuss/references/publish-flow.md +167 -0
- package/dist/skills/tide-discuss/references/role-prompts.md +105 -0
- package/package.json +38 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# TypeScript 严格模式规范
|
|
2
|
+
|
|
3
|
+
## 概述
|
|
4
|
+
|
|
5
|
+
启用 TypeScript 严格模式 (`strict: true`),提供更强的类型安全保障。
|
|
6
|
+
|
|
7
|
+
## 配置
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
// tsconfig.json
|
|
11
|
+
{
|
|
12
|
+
"compilerOptions": {
|
|
13
|
+
"strict": true,
|
|
14
|
+
"noUncheckedIndexedAccess": true,
|
|
15
|
+
"noImplicitOverride": true,
|
|
16
|
+
"exactOptionalPropertyTypes": false
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 核心规则
|
|
22
|
+
|
|
23
|
+
### strictNullChecks
|
|
24
|
+
|
|
25
|
+
`null` 和 `undefined` 不可赋值给其他类型:
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
// ❌ 错误
|
|
29
|
+
const name: string = null;
|
|
30
|
+
|
|
31
|
+
// ✅ 正确
|
|
32
|
+
const name: string | null = null;
|
|
33
|
+
const value = maybeUndefined(); // string | undefined
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### noUncheckedIndexedAccess
|
|
37
|
+
|
|
38
|
+
对象索引访问返回 `T | undefined`,不可直接使用:
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
interface Dict {
|
|
42
|
+
[key: string]: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const dict: Dict = { a: "1" };
|
|
46
|
+
|
|
47
|
+
// ❌ 错误 — dict["b"] 可能是 undefined
|
|
48
|
+
console.log(dict["b"].toString());
|
|
49
|
+
|
|
50
|
+
// ✅ 正确
|
|
51
|
+
const b = dict["b"];
|
|
52
|
+
if (b) {
|
|
53
|
+
console.log(b.toString());
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// 或用可选链
|
|
57
|
+
console.log(dict["b"]?.toString());
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### noImplicitOverride
|
|
61
|
+
|
|
62
|
+
重写父类方法必须显式标记 `override`:
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
class Base {
|
|
66
|
+
save(): void { /* ... */ }
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
class Derived extends Base {
|
|
70
|
+
// ❌ 错误 — 缺少 override
|
|
71
|
+
save(): void { /* ... */ }
|
|
72
|
+
|
|
73
|
+
// ✅ 正确
|
|
74
|
+
override save(): void { /* ... */ }
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## 常见场景
|
|
79
|
+
|
|
80
|
+
### 数组查找
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
const items: string[] = ["a", "b", "c"];
|
|
84
|
+
|
|
85
|
+
// ❌ 错误 — find 返回 T | undefined
|
|
86
|
+
const item = items.find(x => x === "d");
|
|
87
|
+
console.log(item.length);
|
|
88
|
+
|
|
89
|
+
// ✅ 正确
|
|
90
|
+
const item = items.find(x => x === "d");
|
|
91
|
+
if (item) {
|
|
92
|
+
console.log(item.length);
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### 对象属性访问
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
interface Config {
|
|
100
|
+
theme?: {
|
|
101
|
+
color?: string;
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const config: Config = {};
|
|
106
|
+
|
|
107
|
+
// ❌ 错误 — 深层可选链
|
|
108
|
+
const color: string = config.theme.color;
|
|
109
|
+
|
|
110
|
+
// ✅ 正确
|
|
111
|
+
const color = config.theme?.color;
|
|
112
|
+
```
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# 前端 UI 组件示例
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## 1. PrimeNG 使用原则
|
|
6
|
+
|
|
7
|
+
### Boolean shorthand
|
|
8
|
+
|
|
9
|
+
PrimeNG 的 boolean 属性传 `true` 时直接用属性名,不用 `[prop]="true"`:
|
|
10
|
+
|
|
11
|
+
```html
|
|
12
|
+
<!-- ✓ 正确 -->
|
|
13
|
+
<p-dialog [(visible)]="visible" modal (onHide)="onHide()">
|
|
14
|
+
<p-button severity="secondary" outlined loading disabled />
|
|
15
|
+
|
|
16
|
+
<!-- ✗ 错误 -->
|
|
17
|
+
<p-dialog [(visible)]="visible" [modal]="true">
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### CSS Layer 顺序
|
|
21
|
+
|
|
22
|
+
`styles.css` 中 CSS Layer 顺序:
|
|
23
|
+
|
|
24
|
+
```css
|
|
25
|
+
@layer theme, base, antd, primeui, primeicons, components, utilities;
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### providePrimeNG 全局配置
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
bootstrapApplication(App, {
|
|
32
|
+
providers: [
|
|
33
|
+
providePrimeNG({
|
|
34
|
+
overlayAppendTo: 'body',
|
|
35
|
+
zIndex: { modal: 1100, overlay: 1000, menu: 1000, tooltip: 1100 },
|
|
36
|
+
locale: zhHans(),
|
|
37
|
+
cssLayer: { name: 'primeui', order: 'theme, base, antd, primeui, primeicons, components, utilities' },
|
|
38
|
+
theme: { preset: Aura, options: { darkModeSelector: '.dark' } },
|
|
39
|
+
}),
|
|
40
|
+
],
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 2. PrimeNG Table 懒加载
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
@Component({
|
|
50
|
+
selector: 'app-user-list',
|
|
51
|
+
imports: [TableModule],
|
|
52
|
+
templateUrl: './user-list.component.html',
|
|
53
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
54
|
+
})
|
|
55
|
+
export class UserListComponent {
|
|
56
|
+
private readonly authService = inject(AuthService);
|
|
57
|
+
|
|
58
|
+
readonly users = signal<User[]>([]);
|
|
59
|
+
readonly totalUsers = signal(0);
|
|
60
|
+
|
|
61
|
+
loadUsers(event: TableLazyLoadEvent) {
|
|
62
|
+
this.authService.listUsers(toPageRequest(event))
|
|
63
|
+
.subscribe(({ items, totalItems }) => {
|
|
64
|
+
this.users.set(items);
|
|
65
|
+
this.totalUsers.set(totalItems);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## 3. 确认弹窗(删除操作)
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
@Component({
|
|
77
|
+
imports: [ConfirmDialog],
|
|
78
|
+
providers: [ConfirmationService],
|
|
79
|
+
template: `
|
|
80
|
+
<p-confirmdialog />
|
|
81
|
+
<p-button label="删除" (onClick)="onDelete(item.id)" />
|
|
82
|
+
`,
|
|
83
|
+
})
|
|
84
|
+
export class MyComponent {
|
|
85
|
+
private readonly confirmationService = inject(ConfirmationService);
|
|
86
|
+
|
|
87
|
+
onDelete(id: number) {
|
|
88
|
+
this.confirmationService.confirm({
|
|
89
|
+
accept: () => this.itemService.delete(id).subscribe(
|
|
90
|
+
() => this.messageService.add({ severity: 'success', summary: '删除成功' }),
|
|
91
|
+
),
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## 4. Drawer 侧边栏
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
@Component({
|
|
103
|
+
template: `
|
|
104
|
+
<p-drawer
|
|
105
|
+
styleClass="w-200"
|
|
106
|
+
[(visible)]="visible"
|
|
107
|
+
position="right"
|
|
108
|
+
(onHide)="onHide()"
|
|
109
|
+
>
|
|
110
|
+
<ng-template #header>
|
|
111
|
+
<div class="font-bold text-2xl">标题</div>
|
|
112
|
+
</ng-template>
|
|
113
|
+
<ng-template #footer>
|
|
114
|
+
<div class="flex justify-end items-center gap-2">
|
|
115
|
+
<p-button
|
|
116
|
+
severity="secondary"
|
|
117
|
+
outlined
|
|
118
|
+
label="取消"
|
|
119
|
+
(onClick)="onHide()"
|
|
120
|
+
/>
|
|
121
|
+
<p-button type="submit" label="确定" />
|
|
122
|
+
</div>
|
|
123
|
+
</ng-template>
|
|
124
|
+
</p-drawer>
|
|
125
|
+
`,
|
|
126
|
+
})
|
|
127
|
+
export class DrawerComponent {
|
|
128
|
+
readonly visible = signal(false);
|
|
129
|
+
|
|
130
|
+
onHide() {
|
|
131
|
+
this.visible.set(false);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# PrimeNG UI 规范
|
|
2
|
+
|
|
3
|
+
按需加载。仅当需要编写对应组件类型时阅读相关章节。
|
|
4
|
+
|
|
5
|
+
> **完整示例代码见 `examples/ui-components.md`**。
|
|
6
|
+
|
|
7
|
+
## 快速参考
|
|
8
|
+
|
|
9
|
+
### PrimeNG 组件
|
|
10
|
+
|
|
11
|
+
- 优先 PrimeNG 组件,不用原生 HTML(`<p-button>`、`<p-select>` 等)
|
|
12
|
+
- `styleClass` 代替 `class` 传递样式
|
|
13
|
+
- `pInvalid` 属性显示验证状态(不是 `invalid` 或 `class.ng-invalid`)
|
|
14
|
+
- 样式定制优先 Pass Through(`pt`),不支持时用 Design Tokens(`dt`)
|
|
15
|
+
- 禁止覆盖全局 CSS
|
|
16
|
+
|
|
17
|
+
### 数据表格(p-table)
|
|
18
|
+
|
|
19
|
+
- CRUD 列表页用 `<p-table>` 的 `lazy` 模式配合 `onLazyLoad` 事件实现服务端分页排序
|
|
20
|
+
- 设置 `[rows]` 和 `[totalRecords]`,首屏加载时手动触发一次 `lazy` 加载
|
|
21
|
+
- 空数据时设置 `emptyMessage="暂无数据"`(支持自定义 `ng-template pTemplate="emptymessage"`)
|
|
22
|
+
- 大数据量(>1000 行)考虑虚拟滚动:`[virtualScroll]="true"` + `[virtualScrollItemSize]="行高"`
|
|
23
|
+
|
|
24
|
+
### 下拉选择(p-select / p-dropdown)
|
|
25
|
+
|
|
26
|
+
- 必须设置 `optionLabel` 指定显示字段,`optionValue` 指定值字段(默认 `value`)
|
|
27
|
+
- 选项列表应从 Service 通过 httpResource 获取,不要硬编码在组件中
|
|
28
|
+
- 需要多选时用 `[multiple]="true"`
|
|
29
|
+
|
|
30
|
+
### 日期选择(p-datepicker)
|
|
31
|
+
|
|
32
|
+
- 设置 `dateFormat="yy-mm-dd"` 匹配后端日期格式
|
|
33
|
+
- 只选日期用 `[showTime]="false"`(默认),选日期时间用 `[showTime]="true"`
|
|
34
|
+
|
|
35
|
+
### 按钮(p-button)
|
|
36
|
+
|
|
37
|
+
- 写操作按钮设置 `[loading]="loading()"` 防止重复提交
|
|
38
|
+
- 纯图标按钮必须加 `[pTooltip]="提示"` 和 `aria-label`
|
|
39
|
+
- 危险操作(删除)用 `severity="danger"`
|
|
40
|
+
|
|
41
|
+
### 弹窗/对话框
|
|
42
|
+
|
|
43
|
+
- 关闭弹窗(`(onHide)`)后需重置表单状态:`form().reset()` + 错误提示清除
|
|
44
|
+
- 新增/编辑共用弹窗时,打开前根据 `editingId` 初始化表单值
|
|
45
|
+
- 提交完成后关闭弹窗并 reload 列表数据
|
|
46
|
+
|
|
47
|
+
### 确认操作
|
|
48
|
+
|
|
49
|
+
- 删除等危险操作使用确认弹窗:`p-confirmPopup` 或 ConfirmDialog
|
|
50
|
+
- 批量操作建议使用 `p-confirmDialog`
|
|
51
|
+
|
|
52
|
+
### 空状态与反馈
|
|
53
|
+
|
|
54
|
+
- 所有列表页必须有空状态提示
|
|
55
|
+
- 操作成功/失败后应有 Toast 反馈(通过 `MessageService`,拦截器已自动处理错误 toast)
|
|
56
|
+
- 长时间操作建议显示加载指示器(`p-progressSpinner` 或骨架屏 `p-skeleton`)
|
|
57
|
+
|
|
58
|
+
### 响应式
|
|
59
|
+
|
|
60
|
+
- 移动端适配:PrimeNG 响应式 `col-12 md:col-6 lg:col-4` 或 Tailwind `sm:`/`md:` 断点
|
|
61
|
+
- 表单在小屏上应单列显示
|
|
62
|
+
|
|
63
|
+
### 页面标题
|
|
64
|
+
|
|
65
|
+
- 每个功能页设置 `<title>页面名称 - 应用名</title>` 支持多 tab 导航
|
|
66
|
+
- 使用 `Title` service(`inject(Title)`)在 Component 中动态设置
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# 前端代码折行示例(90 列限制)
|
|
2
|
+
|
|
3
|
+
## 函数/方法签名
|
|
4
|
+
|
|
5
|
+
参数 ≤ 2 个且行总长 ≤ 90 则一行。超过时每行一个参数,2 格缩进:
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
private saveAllSuccessful(
|
|
9
|
+
appId: number,
|
|
10
|
+
form: Form,
|
|
11
|
+
answers: Map<number, Map<string, Answer>>,
|
|
12
|
+
): void { ... }
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## RxJS Pipe 链
|
|
16
|
+
|
|
17
|
+
每个算子一行,行首 `.`,2 格缩进:
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
this.service.getData(id).pipe(
|
|
21
|
+
switchMap(data => this.otherService.process(data)),
|
|
22
|
+
takeUntilDestroyed(this.destroyRef),
|
|
23
|
+
finalize(() => this.loading.set(false)),
|
|
24
|
+
).subscribe(result => this.data.set(result));
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Signal / computed / httpResource
|
|
28
|
+
|
|
29
|
+
简单用 `()` 直接返回,复杂用 `{}` 块。超过 90 列时换行:
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
readonly name = computed(() => this.user().username);
|
|
33
|
+
|
|
34
|
+
readonly columns = computed(() => {
|
|
35
|
+
if (this.visibleColumns().length === 0) { return [...]; }
|
|
36
|
+
return this.visibleColumns().map(mapColumn);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
private readonly formRevision = httpResource<FormRevision>(() => {
|
|
40
|
+
const id = this.response()?.formRevisionId;
|
|
41
|
+
return this.appId() !== null && id !== undefined
|
|
42
|
+
? `/api/v1/apps/${this.appId()}/forms/${this.formId()}/revisions/${id}`
|
|
43
|
+
: undefined;
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Signal Forms 校验链
|
|
48
|
+
|
|
49
|
+
每个 validator 一行,2 格缩进:
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
readonly form = form(signal(EMPTY_ITEM), (path) => {
|
|
53
|
+
required(path.name, { message: '名称不能为空' });
|
|
54
|
+
disabled(path.code, () => this.mode() === 'edit');
|
|
55
|
+
hidden(path.db, ({ valueOf }) => valueOf(path.type) === 'http');
|
|
56
|
+
validate(path.url, ({ value }) => {
|
|
57
|
+
return URL.parse(value()) === null
|
|
58
|
+
? { kind: 'url', message: 'URL 格式不正确' }
|
|
59
|
+
: null;
|
|
60
|
+
});
|
|
61
|
+
}, {
|
|
62
|
+
submission: { action: async () => {
|
|
63
|
+
await lastValueFrom(this.service.create(this.form().value()));
|
|
64
|
+
}},
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## 条件/三元表达式
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
if (
|
|
72
|
+
answer?.type === 'DateTimeAnswer'
|
|
73
|
+
&& answer.value !== null
|
|
74
|
+
) { ... }
|
|
75
|
+
|
|
76
|
+
const redirect = this.appId() !== null && this.formId() !== null
|
|
77
|
+
? `/api/v1/apps/${this.appId()}/forms/${this.formId()}/responses`
|
|
78
|
+
: undefined;
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## 模板 HTML 属性
|
|
82
|
+
|
|
83
|
+
整个标签(含所有属性)≤ 90 列则保持一行,超过 90 列才每行一个属性:
|
|
84
|
+
|
|
85
|
+
```angular2html
|
|
86
|
+
<!-- ≤ 90 列,一行 -->
|
|
87
|
+
<p-button type="submit" label="提交" (onClick)="form.submit()" />
|
|
88
|
+
|
|
89
|
+
<!-- > 90 列,每行一个属性 -->
|
|
90
|
+
<p-dialog
|
|
91
|
+
[(visible)]="visible"
|
|
92
|
+
[header]="isEditing ? '编辑' : '新增'"
|
|
93
|
+
modal
|
|
94
|
+
(onHide)="handleHide()"
|
|
95
|
+
>
|
|
96
|
+
|
|
97
|
+
<!-- 含复杂表达式属性,超长拆行 -->
|
|
98
|
+
<p-button
|
|
99
|
+
type="submit"
|
|
100
|
+
label="提交"
|
|
101
|
+
[disabled]="!form.form().valid"
|
|
102
|
+
(onClick)="form.submit()"
|
|
103
|
+
/>
|
|
104
|
+
|
|
105
|
+
<!-- 内容独占一行 -->
|
|
106
|
+
<span class="text-red-600 font-medium">
|
|
107
|
+
{{ error() }}
|
|
108
|
+
</span>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
HTML 注释和纯文字内容不折行,即使超过 90 列也保持一行。
|
|
112
|
+
|
|
113
|
+
## 长 class 三层规则(逐级尝试,选第一个满足 ≤ 90 列的)
|
|
114
|
+
|
|
115
|
+
```angular2html
|
|
116
|
+
<!-- 第 1 层:class="..." 一行能放下则一行 -->
|
|
117
|
+
<div class="flex flex-col items-center gap-2 p-5 bg-blue-50">
|
|
118
|
+
|
|
119
|
+
<!-- 第 2 层:一行放不下时,class=" 单独一行,所有 class 排在缩进行 -->
|
|
120
|
+
<div
|
|
121
|
+
class="
|
|
122
|
+
flex flex-col md:flex-row md:justify-between md:items-start md:w-4xl w-full gap-8
|
|
123
|
+
"
|
|
124
|
+
>
|
|
125
|
+
|
|
126
|
+
<!-- 第 3 层:仍超过 90 列时按功能分组排版 -->
|
|
127
|
+
<div
|
|
128
|
+
class="
|
|
129
|
+
flex flex-col items-center justify-between gap-3 p-4
|
|
130
|
+
bg-white border border-gray-200 rounded-lg shadow-sm
|
|
131
|
+
hover:shadow-md transition-shadow cursor-pointer
|
|
132
|
+
"
|
|
133
|
+
>
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## `@if` / `@for` / `@let` 结构指令
|
|
137
|
+
|
|
138
|
+
```angular2html
|
|
139
|
+
@if (data(); as items) {
|
|
140
|
+
<div class="text-gray-500">加载完成</div>
|
|
141
|
+
} @else {
|
|
142
|
+
<div class="text-gray-500">加载中...</div>
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
<div class="flex flex-col gap-2">
|
|
146
|
+
@for (item of items(); track item.id) {
|
|
147
|
+
<div class="border p-2">{{ item.name }}</div>
|
|
148
|
+
} @empty {
|
|
149
|
+
<div class="text-gray-400">暂无数据</div>
|
|
150
|
+
}
|
|
151
|
+
</div>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## `inject()` 声明
|
|
155
|
+
|
|
156
|
+
每个 `inject()` 独立一行。同类依赖相邻,不同类之间空行:
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
private readonly messageService = inject(MessageService);
|
|
160
|
+
private readonly confirmationService = inject(ConfirmationService);
|
|
161
|
+
|
|
162
|
+
private readonly formBuilder = inject(NonNullableFormBuilder);
|
|
163
|
+
|
|
164
|
+
private readonly authService = inject(AuthService);
|
|
165
|
+
private readonly dataSourceService = inject(DataSourceService);
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## 数组/对象字面量
|
|
169
|
+
|
|
170
|
+
`imports` 数组总是每项一行(项目约定),按字母序排列。其他数组/对象 ≤ 90 列一行,超过再拆行:
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
imports: [
|
|
174
|
+
Button, ConfirmDialog, Dialog, ErrorMessages,
|
|
175
|
+
FormField, FormRoot, InputText, TableModule,
|
|
176
|
+
],
|
|
177
|
+
|
|
178
|
+
private readonly columns = ['name', 'age', 'email'];
|
|
179
|
+
|
|
180
|
+
const longOption = {
|
|
181
|
+
label: '这是一个很长的文本标签',
|
|
182
|
+
value: 'long-value-key',
|
|
183
|
+
};
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## 泛型/类型标注
|
|
187
|
+
|
|
188
|
+
长泛型约束提取 type 别名:
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
type DialogState = 'a' | 'b' | 'c' | 'd' | 'e';
|
|
192
|
+
readonly state = computed<DialogState>(() => { ... });
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## `subscribe()` 回调
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
this.service.list(toPageRequest(event))
|
|
199
|
+
.subscribe(({ items, totalItems }) => {
|
|
200
|
+
this.users.set(items);
|
|
201
|
+
this.totalUsers.set(totalItems);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
this.service.delete(id).subscribe(() => {
|
|
205
|
+
this.messageService.add({ severity: 'success', summary: '删除成功' });
|
|
206
|
+
});
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## `.find()` / `.filter()` 回调
|
|
210
|
+
|
|
211
|
+
≤ 90 列则一行,超过时回调换行(2 格缩进),回调以 `,` 结尾,`)!` 单独一行:
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
const page = this.pages().find(p => p.id === id)!;
|
|
215
|
+
|
|
216
|
+
const selectedForm = this.formService.forms().find(
|
|
217
|
+
form => form.id === menuItem.formId!,
|
|
218
|
+
)!;
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## `messageService.add()` 等对象参数
|
|
222
|
+
|
|
223
|
+
对象参数 ≤ 90 列一行,超过拆行:
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
this.messageService.add({ severity: 'success', summary: '提交成功' });
|
|
227
|
+
|
|
228
|
+
this.messageService.add({
|
|
229
|
+
severity: 'warn',
|
|
230
|
+
summary: '当前应用已被禁用,暂无法进行业务操作',
|
|
231
|
+
});
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## 路由 `loadComponent`
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
loadComponent: () => import('./admin/user-list/user-list.component')
|
|
238
|
+
.then(m => m.UserListComponent),
|
|
239
|
+
|
|
240
|
+
loadComponent: () =>
|
|
241
|
+
import('./page-not-found/page-not-found.component').then(m => m.PageNotFoundComponent),
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## `async/await`
|
|
245
|
+
|
|
246
|
+
await 右侧超过 90 列时换行(2 格缩进):
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
const user = await lastValueFrom(
|
|
250
|
+
this.authService.createUser(this.form().value())
|
|
251
|
+
);
|
|
252
|
+
```
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# 前端组件模式、类型与 Pipe 示例
|
|
2
|
+
|
|
3
|
+
## 1. Discriminated Union(多态类型)
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
type FormControl = TextControl | NumberControl | ChoiceControl;
|
|
7
|
+
interface TextControl { type: 'TextControl'; label: string; maxLength?: number; }
|
|
8
|
+
interface NumberControl { type: 'NumberControl'; min?: number; max?: number; }
|
|
9
|
+
interface ChoiceControl { type: 'ChoiceControl'; options: Option[]; }
|
|
10
|
+
|
|
11
|
+
// Answer 多态(对应 Java 子类)
|
|
12
|
+
type Answer = UuidAnswer | TextAnswer | ChoiceAnswer | TableAnswer;
|
|
13
|
+
interface UuidAnswer { type: 'UuidAnswer'; value: string | null; }
|
|
14
|
+
interface TextAnswer { type: 'TextAnswer'; value: string | null; }
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## 2. 日期 Pipe
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
@Pipe({ name: 'localDateTime' })
|
|
21
|
+
export class LocalDateTimePipe implements PipeTransform {
|
|
22
|
+
transform(value: LocalDateTime, pattern = 'yyyy-MM-dd HH:mm:ss') {
|
|
23
|
+
return value.format(DateTimeFormatter.ofPattern(pattern));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 3. 组件 Host 动画
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
@Component({
|
|
32
|
+
host: {
|
|
33
|
+
class: 'text-red-500 text-sm',
|
|
34
|
+
'animate.enter': `
|
|
35
|
+
starting:opacity-0 starting:-translate-y-1
|
|
36
|
+
transition-all duration-250 ease-out
|
|
37
|
+
`,
|
|
38
|
+
'animate.leave': `
|
|
39
|
+
opacity-0 -translate-y-1
|
|
40
|
+
transition-all duration-100 ease-in
|
|
41
|
+
`,
|
|
42
|
+
},
|
|
43
|
+
})
|
|
44
|
+
export class ErrorMessages {
|
|
45
|
+
readonly errors = input.required<ValidationError[]>();
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## 4. input() / model() / output() 模式
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
readonly appId = input.required<number>();
|
|
53
|
+
readonly config = input<Config>(DEFAULT_CONFIG);
|
|
54
|
+
readonly visible = model(false); // 双向绑定父组件 [(visible)]
|
|
55
|
+
readonly delete = output<number>(); // 子传父事件
|
|
56
|
+
```
|