@fui-org/fui-cli 2.1.4 → 2.2.1
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 +16 -7
- package/dist/fui-xg17j3f9.js +396 -0
- package/dist/fui.js +1 -1
- package/package.json +1 -1
- package/skills/fui/SKILL.md +152 -39
- package/skills/{fui-skill → fui}/assets/projectdefaultstyle.css +489 -489
- package/skills/{fui-skill → fui}/examples/component.vue +162 -162
- package/skills/{fui-skill → fui}/references/INDEX.md +4 -5
- package/skills/{fui-skill → fui}/references/advanced-techniques.md +170 -168
- package/skills/{fui-skill → fui}/references/component-design.md +2 -2
- package/skills/{fui-skill → fui}/references/component-quickref.md +78 -78
- package/skills/{fui-skill → fui}/references/component-table.md +287 -287
- package/skills/{fui-skill → fui}/references/components-dialog.md +191 -191
- package/skills/{fui-skill → fui}/references/components-display.md +141 -141
- package/skills/{fui-skill → fui}/references/components-echart.md +316 -316
- package/skills/{fui-skill → fui}/references/components-input.md +335 -335
- package/skills/{fui-skill → fui}/references/controls-patterns.md +2 -2
- package/skills/{fui-skill → fui}/references/controls-styling-vocabulary.md +173 -173
- package/skills/{fui-skill → fui}/references/db-workflow.md +9 -9
- package/skills/{fui-skill → fui}/references/default-function.md +456 -456
- package/skills/{fui-skill → fui}/references/design-modes.md +57 -57
- package/skills/{fui-skill → fui}/references/echart-templates.md +489 -489
- package/skills/{fui-skill → fui}/references/fastproject.md +99 -99
- package/skills/{fui-skill → fui}/references/fullstack-workflow.md +314 -313
- package/skills/{fui-skill → fui}/references/module-data-patterns.md +117 -117
- package/skills/{fui-skill → fui}/references/module-json-anatomy.md +132 -132
- package/skills/{fui-skill → fui}/references/module-structure.md +144 -144
- package/skills/{fui-skill → fui}/references/new-session.md +3 -20
- package/skills/{fui-skill → fui}/references/pdfmake.md +60 -60
- package/skills/{fui-skill → fui}/references/permission-system.md +1 -1
- package/skills/{fui-skill → fui}/references/platform-architecture.md +269 -269
- package/skills/{fui-skill → fui}/references/project-config.md +303 -303
- package/skills/{fui-skill → fui}/references/project-provisioning.md +4 -4
- package/skills/{fui-skill → fui}/references/script-map.md +262 -262
- package/skills/{fui-skill → fui}/references/sql-clr-functions.md +225 -225
- package/skills/{fui-skill → fui}/references/system-design.md +3 -3
- package/skills/{fui-skill → fui}/references/tapi-permission-patterns.md +156 -156
- package/skills/{fui-skill → fui}/references/tapi-reference.md +474 -474
- package/skills/fui/references/tools-registry.md +214 -0
- package/skills/{fui-skill → fui}/references/ui-crosswindow-patterns.md +321 -321
- package/skills/{fui-skill → fui}/references/ui-dialog-patterns.md +255 -255
- package/skills/{fui-skill → fui}/references/ui-layout-patterns.md +176 -176
- package/skills/{fui-skill → fui}/references/ui-patterns.md +303 -303
- package/skills/{fui-skill → fui}/references/ui-screenshot-review.md +95 -95
- package/skills/{fui-skill → fui}/references/ui-table-cell-patterns.md +318 -318
- package/skills/{fui-skill → fui}/references/verification.md +18 -18
- package/skills/{fui-skill → fui}/references/watcher-patterns.md +163 -163
- package/skills/{fui-skill → fui}/references/websocket-realtime.md +271 -271
- package/skills/{fui-skill → fui}/scripts/component-3.0.js +2715 -2715
- package/skills/{fui-skill → fui}/scripts/component.js +3147 -3147
- package/skills/{fui-skill → fui}/scripts/componentTable-3.0.js +1038 -1038
- package/skills/{fui-skill → fui}/scripts/componentTable.js +891 -891
- package/skills/{fui-skill → fui}/scripts/defaultfunction-3.0.js +866 -866
- package/skills/{fui-skill → fui}/scripts/defaultfunction.js +1051 -1051
- package/skills/{fui-skill → fui}/scripts/fastproject-3.0.js +870 -870
- package/skills/{fui-skill → fui}/scripts/fastproject.js +828 -828
- package/dist/fui-h4z7zhk2.js +0 -381
- package/skills/fui-skill/SKILL.md +0 -139
- package/skills/fui-skill/references/tools-registry.md +0 -84
- /package/skills/{fui-skill → fui}/assets/projectdefaultstyle-3.0.css +0 -0
- /package/skills/{fui-skill → fui}/design-md/airbnb/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/airbnb/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/airtable/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/airtable/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/apple/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/apple/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/asu/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/asu/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/binance/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/binance/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/bmw/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/bmw/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/bmw-m/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/bmw-m/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/bugatti/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/bugatti/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/cal/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/cal/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/claude/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/claude/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/clay/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/clay/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/clickhouse/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/clickhouse/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/cohere/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/cohere/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/coinbase/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/coinbase/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/composio/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/composio/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/cursor/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/cursor/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/elevenlabs/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/elevenlabs/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/expo/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/expo/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/ferrari/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/ferrari/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/figma/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/figma/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/framer/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/framer/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/fui/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/hashicorp/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/hashicorp/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/ibm/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/ibm/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/intercom/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/intercom/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/kraken/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/kraken/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/lamborghini/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/lamborghini/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/linear.app/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/linear.app/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/lovable/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/lovable/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mastercard/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mastercard/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/meta/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/meta/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/minimax/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/minimax/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mintlify/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mintlify/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/miro/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/miro/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mistral.ai/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mistral.ai/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mongodb/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mongodb/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/nike/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/nike/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/notion/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/notion/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/nvidia/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/nvidia/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/ollama/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/ollama/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/opencode.ai/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/opencode.ai/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/pinterest/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/pinterest/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/playstation/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/playstation/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/posthog/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/posthog/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/raycast/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/raycast/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/renault/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/renault/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/replicate/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/replicate/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/resend/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/resend/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/revolut/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/revolut/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/runwayml/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/runwayml/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/sanity/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/sanity/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/sentry/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/sentry/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/shopify/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/shopify/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/slack/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/spacex/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/spacex/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/spotify/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/spotify/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/starbucks/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/starbucks/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/stripe/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/stripe/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/supabase/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/supabase/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/superhuman/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/superhuman/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/tesla/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/tesla/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/theverge/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/theverge/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/together.ai/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/together.ai/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/uber/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/uber/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/vercel/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/vercel/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/vodafone/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/vodafone/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/voltagent/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/voltagent/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/warp/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/warp/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/webflow/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/webflow/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/wired/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/wired/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/wise/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/wise/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/x.ai/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/x.ai/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/zapier/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/zapier/README.md +0 -0
- /package/skills/{fui-skill → fui}/examples/f-table-patterns.json +0 -0
- /package/skills/{fui-skill → fui}/examples/module-patterns.json +0 -0
- /package/skills/{fui-skill → fui}/examples/project-patterns.json +0 -0
- /package/skills/{fui-skill → fui}/references/coding-standards.md +0 -0
- /package/skills/{fui-skill → fui}/references/db-table-design.md +0 -0
- /package/skills/{fui-skill → fui}/references/fsheet.md +0 -0
- /package/skills/{fui-skill → fui}/references/tapi-file-api.md +0 -0
- /package/skills/{fui-skill → fui}/references/ui-templates.md +0 -0
- /package/skills/{fui-skill → fui}/scripts/fechart.js +0 -0
- /package/skills/{fui-skill → fui}/scripts/fsheet.js +0 -0
|
@@ -1,303 +1,303 @@
|
|
|
1
|
-
# Project Config — project.json & module set
|
|
2
|
-
|
|
3
|
-
> Owns: **`project.json`: menu, menuLeft, navigation, domainSetting, `right`, relation to module `set`**.
|
|
4
|
-
|
|
5
|
-
> Platform merge mechanism: [platform-architecture.md](platform-architecture.md) §2.1. This file is the **full property catalog** for project.json and `set`.
|
|
6
|
-
|
|
7
|
-
> **Edit and push:** `project.json` is **file-driven** like `module.json`: `fui project sync` (pull latest) → edit `{projectId}/project.json` → `fui project publish-config [-p project]`. It takes no config arguments, so local never drifts from server. (`fui project update` edits project **metadata**, not runtime config.)
|
|
8
|
-
|
|
9
|
-
## project.json ↔ module.json `set`
|
|
10
|
-
|
|
11
|
-
At render `fastproject.js` merges **3 layers** (later overrides earlier), then the domain override:
|
|
12
|
-
|
|
13
|
-
```
|
|
14
|
-
$projectGroupSetting (cấu hình nhóm project — thấp nhất)
|
|
15
|
-
⤵ override bởi
|
|
16
|
-
project.json (khối "data")
|
|
17
|
-
⤵ override bởi
|
|
18
|
-
module.json "set" ← ưu tiên cao nhất
|
|
19
|
-
⤵ cuối cùng override bởi
|
|
20
|
-
domainSetting[domain hiện tại] (nếu đang chạy trên domain đó)
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
Result: `vueData.v_Set` (read via `vueData.v_Set.*`). Module inherits all project config and may override any field. Normal pages **need no `set`**.
|
|
24
|
-
|
|
25
|
-
### `v_Set` properties (project.json.data or module.set; used by `fastproject.js`)
|
|
26
|
-
|
|
27
|
-
| Field | Type | Effect |
|
|
28
|
-
| ----------------- | ---------------- | ------------------------------------------------------------------------------- |
|
|
29
|
-
| `title` | string | Page title (header bar) |
|
|
30
|
-
| `apiDomain` | string | tAPI base URL — prepended to API paths without `http` |
|
|
31
|
-
| `login` | string | Login page URL; runtime opens it on missing token / 401 |
|
|
32
|
-
| `userInfo` | string | User info endpoint → `vueData.user`; empty = no fetch |
|
|
33
|
-
| `menu` | `false` \| array | Horizontal header menu; `false` = hidden (48px tall when shown) |
|
|
34
|
-
| `menuLeft` | array | Left sidebar menu |
|
|
35
|
-
| `menuComponent` | array | Custom components in the menu area |
|
|
36
|
-
| `menuStyle` | object | Top/left menu colors |
|
|
37
|
-
| `logo` | string | Header logo URL |
|
|
38
|
-
| `userProfile` | object/string | Header user profile block |
|
|
39
|
-
| `themeStyle` | object | Passed to `new Vuetify(...)` — page Vuetify theme |
|
|
40
|
-
| `disableValidate` | boolean | `true` = skip form validation on submit |
|
|
41
|
-
| `authDomain` | string | Domain whose cookies `logout()` clears |
|
|
42
|
-
| `fileDomain` | string | File server base URL |
|
|
43
|
-
| `domainSetting` | object | Per-domain config override (multi-domain) |
|
|
44
|
-
| `webSocket` | object | SignalR real-time group config ([websocket-realtime.md](websocket-realtime.md)) |
|
|
45
|
-
|
|
46
|
-
Example — only this landing page hides the header menu:
|
|
47
|
-
|
|
48
|
-
```json
|
|
49
|
-
// module.json — set của trang đó
|
|
50
|
-
"set": {
|
|
51
|
-
"menu": false
|
|
52
|
-
}
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
## project.json structure
|
|
56
|
-
|
|
57
|
-
`project.json` holds a single `data` object (not an array like module.json):
|
|
58
|
-
|
|
59
|
-
```json
|
|
60
|
-
{
|
|
61
|
-
"data": {
|
|
62
|
-
"ProjectName": "...",
|
|
63
|
-
"title": "...",
|
|
64
|
-
"apiDomain": "...",
|
|
65
|
-
"login": "...",
|
|
66
|
-
"userInfo": "...",
|
|
67
|
-
"apiFile": "...",
|
|
68
|
-
"menuStyle": { ... },
|
|
69
|
-
"menu": false | [ ... ],
|
|
70
|
-
"menuLeft": [ ... ],
|
|
71
|
-
"menuComponent": [ ... ],
|
|
72
|
-
"domainSetting": { ... }
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
### `data` fields
|
|
78
|
-
|
|
79
|
-
Same meaning as the `v_Set` table above, plus:
|
|
80
|
-
|
|
81
|
-
| Field | Type | Description |
|
|
82
|
-
| ------------- | ------ | ---------------------------------------------------------------------------------------------- |
|
|
83
|
-
| `ProjectName` | string | Display name |
|
|
84
|
-
| `title` | string | Default page title |
|
|
85
|
-
| `logo` | string | Menu bar logo URL, e.g. `"/images/fastUI_White.png"` |
|
|
86
|
-
| `apiDomain` | string | tAPI base URL with apiName, e.g. `https://tapi.example2.vn/` or `https://tapi.example.vn/acc/` |
|
|
87
|
-
| `userInfo` | string | Public FUI: `"https://api.fui.vn/fp/userinfo"` |
|
|
88
|
-
| `apiFile` | string | File upload endpoint (optional) |
|
|
89
|
-
| `fileDomain` | string | File server base URL (optional) |
|
|
90
|
-
|
|
91
|
-
## menuStyle
|
|
92
|
-
|
|
93
|
-
Top and left menu colors:
|
|
94
|
-
|
|
95
|
-
```json
|
|
96
|
-
"menuStyle": {
|
|
97
|
-
"topmenu": {
|
|
98
|
-
"bgcolor": "#217D46",
|
|
99
|
-
"textcolor": "white"
|
|
100
|
-
},
|
|
101
|
-
"leftmenu": {
|
|
102
|
-
"bgcolor": "#217D46",
|
|
103
|
-
"textcolor": "white"
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
## menu (top header)
|
|
109
|
-
|
|
110
|
-
- `false` → header bar with menu fully hidden
|
|
111
|
-
- Array → top bar menu items
|
|
112
|
-
|
|
113
|
-
**Fixed height: 48px** when shown. Every "full screen" content height calc (`calc(100vh - Npx)`, `$(window).height() - N`, CSS `height: 100vh` on a container...) must also subtract 48px, or it overflows/scrolls:
|
|
114
|
-
|
|
115
|
-
```css
|
|
116
|
-
/* Có menu hiển thị */
|
|
117
|
-
height: calc(100vh - 48px);
|
|
118
|
-
|
|
119
|
-
/* menu: false → không trừ phần này */
|
|
120
|
-
height: 100vh;
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
Applies to `module.json` (`:height` of `f-table`, `f-echart`...) and to CSS in `header.html`/components for `HTMLOnly=true` pages.
|
|
124
|
-
|
|
125
|
-
## menuLeft (sidebar)
|
|
126
|
-
|
|
127
|
-
Array of sidebar items. One item:
|
|
128
|
-
|
|
129
|
-
```json
|
|
130
|
-
{
|
|
131
|
-
"name": "Tên mục",
|
|
132
|
-
"icon": "mdi-folder-multiple",
|
|
133
|
-
"url": "/duong-dan-module",
|
|
134
|
-
"submenu": [ ... ],
|
|
135
|
-
"right": {
|
|
136
|
-
"SystemRight": [1, 2, 3, 5, 9],
|
|
137
|
-
"FunctionRight": ["10", "20"]
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
### Menu item fields
|
|
143
|
-
|
|
144
|
-
| Field | Required | Description |
|
|
145
|
-
| --------- | -------- | ------------------------------------------------ |
|
|
146
|
-
| `name` | ✅ | Display name |
|
|
147
|
-
| `url` | ❌ | Module path (omit for a group with submenu only) |
|
|
148
|
-
| `icon` | ❌ | Material Design icon (`mdi-*`) |
|
|
149
|
-
| `submenu` | ❌ | Child items (same structure) |
|
|
150
|
-
| `right` | ❌ | Visibility by right — missing = visible to all |
|
|
151
|
-
|
|
152
|
-
### Menu visibility (`right`)
|
|
153
|
-
|
|
154
|
-
> `SystemRight` (rank) vs `FunctionRight` (per-function) and managing right definitions: [permission-system.md](permission-system.md).
|
|
155
|
-
|
|
156
|
-
```json
|
|
157
|
-
"right": {
|
|
158
|
-
"SystemRight": [1, 2, 5, 9],
|
|
159
|
-
"FunctionRight": ["10", "20"]
|
|
160
|
-
}
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
- **`SystemRight`**: `user.SystemRight` (number) must be in the array
|
|
164
|
-
- **`FunctionRight`**: `user.FunctionRight` (string array) must intersect the array (at least one code)
|
|
165
|
-
- **`UserID`**: only listed `UserID`s see it — for internal/DEV menus
|
|
166
|
-
- Multiple keys: user must satisfy **all** (`rightTest` checks each key's intersection with `vueData.user[key]`)
|
|
167
|
-
- No `right` → visible to every logged-in user
|
|
168
|
-
|
|
169
|
-
**Real example — DEV menu for a few UserIDs:**
|
|
170
|
-
|
|
171
|
-
```json
|
|
172
|
-
{
|
|
173
|
-
"name": "DEV",
|
|
174
|
-
"right": { "UserID": ["NV0000177", "NV0001078", "NV0000605"] },
|
|
175
|
-
"submenu": [{ "name": "Tra cứu lịch sử OTP", "url": "/hoso-sms-history" }]
|
|
176
|
-
}
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
> Top menu parents usually have only `name` + `right` + `submenu` (no `url`); `url` sits on each `submenu` item.
|
|
180
|
-
|
|
181
|
-
**Real example — menu for level-1 Team lead/BGH only:**
|
|
182
|
-
|
|
183
|
-
```json
|
|
184
|
-
{
|
|
185
|
-
"name": "Cấp 1 - Tổ trưởng",
|
|
186
|
-
"icon": "mdi-account-tie",
|
|
187
|
-
"right": {
|
|
188
|
-
"FunctionRight": ["10"],
|
|
189
|
-
"SystemRight": [5, 9]
|
|
190
|
-
},
|
|
191
|
-
"submenu": [ ... ]
|
|
192
|
-
}
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
→ Visible only if `FunctionRight` contains `"10"` AND `SystemRight` is 5 or 9.
|
|
196
|
-
|
|
197
|
-
## menuComponent
|
|
198
|
-
|
|
199
|
-
Custom Vue components embedded in the menu area (e.g. school-year or role dropdown):
|
|
200
|
-
|
|
201
|
-
```json
|
|
202
|
-
"menuComponent": [
|
|
203
|
-
{ "el": "uc-nien-khoa" },
|
|
204
|
-
{ "el": "uc-vaitro" }
|
|
205
|
-
]
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
Components must be registered in the project's `components/_components.json`.
|
|
209
|
-
|
|
210
|
-
## userProfile (header account block)
|
|
211
|
-
|
|
212
|
-
Header-right avatar/account block:
|
|
213
|
-
|
|
214
|
-
```json
|
|
215
|
-
"userProfile": {
|
|
216
|
-
"color": "white",
|
|
217
|
-
"url-signout": "https://app.example.vn/Signout",
|
|
218
|
-
"url-change-password": "https://app.example.vn/ChangePassword",
|
|
219
|
-
"url-account": "https://app.example.vn",
|
|
220
|
-
"url-avatar": "https://file.example.vn/me/avatarOrigin/",
|
|
221
|
-
"url-app-list": "https://tapi.example.vn/me/auth/ApplicationList"
|
|
222
|
-
}
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
| Field | Meaning |
|
|
226
|
-
| --------------------- | ----------------------------------- |
|
|
227
|
-
| `color` | Text/icon color |
|
|
228
|
-
| `url-signout` | Sign-out URL |
|
|
229
|
-
| `url-change-password` | Change-password URL |
|
|
230
|
-
| `url-account` | Account page URL |
|
|
231
|
-
| `url-avatar` | Avatar base URL (`UserID` appended) |
|
|
232
|
-
| `url-app-list` | App list endpoint (app switcher) |
|
|
233
|
-
|
|
234
|
-
> `menuLeft: false` = no left sidebar (top menu only). `menuLeft: [ ... ]` = sidebar on.
|
|
235
|
-
|
|
236
|
-
## domainSetting (multi-domain)
|
|
237
|
-
|
|
238
|
-
Project on several domains (each with its own tAPI server and DB):
|
|
239
|
-
|
|
240
|
-
```json
|
|
241
|
-
"domainSetting": {
|
|
242
|
-
"sec.example.vn": {
|
|
243
|
-
"apiDomain": "https://tapi.example.vn/acc/",
|
|
244
|
-
"fileDomain": "https://file.example.vn/acc/",
|
|
245
|
-
"login": "https://app.example.vn",
|
|
246
|
-
"userInfo": "https://tapi.example.vn/acc/UserInfo/acc"
|
|
247
|
-
},
|
|
248
|
-
"sec.example3.vn": {
|
|
249
|
-
"apiDomain": "https://api.example3.vn/acc/",
|
|
250
|
-
"fileDomain": "https://file.example3.vn/acc/",
|
|
251
|
-
"login": "https://login.example3.vn",
|
|
252
|
-
"userInfo": "https://api.example3.vn/acc/UserInfo/acc"
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
Key = hostname; runtime applies the current domain's override.
|
|
258
|
-
|
|
259
|
-
## module.json `set` — per-page override
|
|
260
|
-
|
|
261
|
-
`set` can override any project.json field. Common:
|
|
262
|
-
|
|
263
|
-
```json
|
|
264
|
-
"set": {
|
|
265
|
-
"title": "Trang quản lý sinh viên",
|
|
266
|
-
"menu": false,
|
|
267
|
-
"menuLeft": [ ... ]
|
|
268
|
-
}
|
|
269
|
-
```
|
|
270
|
-
|
|
271
|
-
| Use `set` when | Example |
|
|
272
|
-
| ------------------------------ | ----------------------------------------- |
|
|
273
|
-
| Page has its own title | `"title": "Nhập điểm Tiểu học"` |
|
|
274
|
-
| Page hides header menu | `"menu": false` |
|
|
275
|
-
| Page needs a different sidebar | Override `"menuLeft"` with a shorter list |
|
|
276
|
-
| Landing / public page | `"menu": false, "menuLeft": []` |
|
|
277
|
-
|
|
278
|
-
**Do not repeat unchanged fields** — declare only what differs from the project default.
|
|
279
|
-
|
|
280
|
-
## File relation summary
|
|
281
|
-
|
|
282
|
-
```
|
|
283
|
-
project.json.data ← config mặc định cho toàn project
|
|
284
|
-
↓ bị override bởi
|
|
285
|
-
module.json.set ← config riêng cho từng trang
|
|
286
|
-
|
|
287
|
-
Effective config = merge(project.json.data, module.json.set)
|
|
288
|
-
(module.set được ưu tiên)
|
|
289
|
-
```
|
|
290
|
-
|
|
291
|
-
## openWindow — URL parameter rule
|
|
292
|
-
|
|
293
|
-
Pass in the `openWindow` URL **only the parent record ID** the child queries the DB with. Never pass names, labels or any field the child can look up.
|
|
294
|
-
|
|
295
|
-
```json
|
|
296
|
-
// ✅ Đúng — chỉ truyền ID cần thiết
|
|
297
|
-
"url": "`/ContestDetail?ContestID={{sContestID}}"
|
|
298
|
-
|
|
299
|
-
// ❌ Sai — truyền thừa field có thể tra từ DB
|
|
300
|
-
"url": "`/ContestDetail?ContestID={{sContestID}}&ContestName={{sContestName}}"
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
Why: the child loads everything by ID; extra fields lengthen the URL, go stale, and couple the modules.
|
|
1
|
+
# Project Config — project.json & module set
|
|
2
|
+
|
|
3
|
+
> Owns: **`project.json`: menu, menuLeft, navigation, domainSetting, `right`, relation to module `set`**.
|
|
4
|
+
|
|
5
|
+
> Platform merge mechanism: [platform-architecture.md](platform-architecture.md) §2.1. This file is the **full property catalog** for project.json and `set`.
|
|
6
|
+
|
|
7
|
+
> **Edit and push:** `project.json` is **file-driven** like `module.json`: `fui project sync` (pull latest) → edit `{projectId}/project.json` → `fui project publish-config [-p project]`. It takes no config arguments, so local never drifts from server. (`fui project update` edits project **metadata**, not runtime config.)
|
|
8
|
+
|
|
9
|
+
## project.json ↔ module.json `set`
|
|
10
|
+
|
|
11
|
+
At render `fastproject.js` merges **3 layers** (later overrides earlier), then the domain override:
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
$projectGroupSetting (cấu hình nhóm project — thấp nhất)
|
|
15
|
+
⤵ override bởi
|
|
16
|
+
project.json (khối "data")
|
|
17
|
+
⤵ override bởi
|
|
18
|
+
module.json "set" ← ưu tiên cao nhất
|
|
19
|
+
⤵ cuối cùng override bởi
|
|
20
|
+
domainSetting[domain hiện tại] (nếu đang chạy trên domain đó)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Result: `vueData.v_Set` (read via `vueData.v_Set.*`). Module inherits all project config and may override any field. Normal pages **need no `set`**.
|
|
24
|
+
|
|
25
|
+
### `v_Set` properties (project.json.data or module.set; used by `fastproject.js`)
|
|
26
|
+
|
|
27
|
+
| Field | Type | Effect |
|
|
28
|
+
| ----------------- | ---------------- | ------------------------------------------------------------------------------- |
|
|
29
|
+
| `title` | string | Page title (header bar) |
|
|
30
|
+
| `apiDomain` | string | tAPI base URL — prepended to API paths without `http` |
|
|
31
|
+
| `login` | string | Login page URL; runtime opens it on missing token / 401 |
|
|
32
|
+
| `userInfo` | string | User info endpoint → `vueData.user`; empty = no fetch |
|
|
33
|
+
| `menu` | `false` \| array | Horizontal header menu; `false` = hidden (48px tall when shown) |
|
|
34
|
+
| `menuLeft` | array | Left sidebar menu |
|
|
35
|
+
| `menuComponent` | array | Custom components in the menu area |
|
|
36
|
+
| `menuStyle` | object | Top/left menu colors |
|
|
37
|
+
| `logo` | string | Header logo URL |
|
|
38
|
+
| `userProfile` | object/string | Header user profile block |
|
|
39
|
+
| `themeStyle` | object | Passed to `new Vuetify(...)` — page Vuetify theme |
|
|
40
|
+
| `disableValidate` | boolean | `true` = skip form validation on submit |
|
|
41
|
+
| `authDomain` | string | Domain whose cookies `logout()` clears |
|
|
42
|
+
| `fileDomain` | string | File server base URL |
|
|
43
|
+
| `domainSetting` | object | Per-domain config override (multi-domain) |
|
|
44
|
+
| `webSocket` | object | SignalR real-time group config ([websocket-realtime.md](websocket-realtime.md)) |
|
|
45
|
+
|
|
46
|
+
Example — only this landing page hides the header menu:
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
// module.json — set của trang đó
|
|
50
|
+
"set": {
|
|
51
|
+
"menu": false
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## project.json structure
|
|
56
|
+
|
|
57
|
+
`project.json` holds a single `data` object (not an array like module.json):
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"data": {
|
|
62
|
+
"ProjectName": "...",
|
|
63
|
+
"title": "...",
|
|
64
|
+
"apiDomain": "...",
|
|
65
|
+
"login": "...",
|
|
66
|
+
"userInfo": "...",
|
|
67
|
+
"apiFile": "...",
|
|
68
|
+
"menuStyle": { ... },
|
|
69
|
+
"menu": false | [ ... ],
|
|
70
|
+
"menuLeft": [ ... ],
|
|
71
|
+
"menuComponent": [ ... ],
|
|
72
|
+
"domainSetting": { ... }
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### `data` fields
|
|
78
|
+
|
|
79
|
+
Same meaning as the `v_Set` table above, plus:
|
|
80
|
+
|
|
81
|
+
| Field | Type | Description |
|
|
82
|
+
| ------------- | ------ | ---------------------------------------------------------------------------------------------- |
|
|
83
|
+
| `ProjectName` | string | Display name |
|
|
84
|
+
| `title` | string | Default page title |
|
|
85
|
+
| `logo` | string | Menu bar logo URL, e.g. `"/images/fastUI_White.png"` |
|
|
86
|
+
| `apiDomain` | string | tAPI base URL with apiName, e.g. `https://tapi.example2.vn/` or `https://tapi.example.vn/acc/` |
|
|
87
|
+
| `userInfo` | string | Public FUI: `"https://api.fui.vn/fp/userinfo"` |
|
|
88
|
+
| `apiFile` | string | File upload endpoint (optional) |
|
|
89
|
+
| `fileDomain` | string | File server base URL (optional) |
|
|
90
|
+
|
|
91
|
+
## menuStyle
|
|
92
|
+
|
|
93
|
+
Top and left menu colors:
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
"menuStyle": {
|
|
97
|
+
"topmenu": {
|
|
98
|
+
"bgcolor": "#217D46",
|
|
99
|
+
"textcolor": "white"
|
|
100
|
+
},
|
|
101
|
+
"leftmenu": {
|
|
102
|
+
"bgcolor": "#217D46",
|
|
103
|
+
"textcolor": "white"
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## menu (top header)
|
|
109
|
+
|
|
110
|
+
- `false` → header bar with menu fully hidden
|
|
111
|
+
- Array → top bar menu items
|
|
112
|
+
|
|
113
|
+
**Fixed height: 48px** when shown. Every "full screen" content height calc (`calc(100vh - Npx)`, `$(window).height() - N`, CSS `height: 100vh` on a container...) must also subtract 48px, or it overflows/scrolls:
|
|
114
|
+
|
|
115
|
+
```css
|
|
116
|
+
/* Có menu hiển thị */
|
|
117
|
+
height: calc(100vh - 48px);
|
|
118
|
+
|
|
119
|
+
/* menu: false → không trừ phần này */
|
|
120
|
+
height: 100vh;
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Applies to `module.json` (`:height` of `f-table`, `f-echart`...) and to CSS in `header.html`/components for `HTMLOnly=true` pages.
|
|
124
|
+
|
|
125
|
+
## menuLeft (sidebar)
|
|
126
|
+
|
|
127
|
+
Array of sidebar items. One item:
|
|
128
|
+
|
|
129
|
+
```json
|
|
130
|
+
{
|
|
131
|
+
"name": "Tên mục",
|
|
132
|
+
"icon": "mdi-folder-multiple",
|
|
133
|
+
"url": "/duong-dan-module",
|
|
134
|
+
"submenu": [ ... ],
|
|
135
|
+
"right": {
|
|
136
|
+
"SystemRight": [1, 2, 3, 5, 9],
|
|
137
|
+
"FunctionRight": ["10", "20"]
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Menu item fields
|
|
143
|
+
|
|
144
|
+
| Field | Required | Description |
|
|
145
|
+
| --------- | -------- | ------------------------------------------------ |
|
|
146
|
+
| `name` | ✅ | Display name |
|
|
147
|
+
| `url` | ❌ | Module path (omit for a group with submenu only) |
|
|
148
|
+
| `icon` | ❌ | Material Design icon (`mdi-*`) |
|
|
149
|
+
| `submenu` | ❌ | Child items (same structure) |
|
|
150
|
+
| `right` | ❌ | Visibility by right — missing = visible to all |
|
|
151
|
+
|
|
152
|
+
### Menu visibility (`right`)
|
|
153
|
+
|
|
154
|
+
> `SystemRight` (rank) vs `FunctionRight` (per-function) and managing right definitions: [permission-system.md](permission-system.md).
|
|
155
|
+
|
|
156
|
+
```json
|
|
157
|
+
"right": {
|
|
158
|
+
"SystemRight": [1, 2, 5, 9],
|
|
159
|
+
"FunctionRight": ["10", "20"]
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
- **`SystemRight`**: `user.SystemRight` (number) must be in the array
|
|
164
|
+
- **`FunctionRight`**: `user.FunctionRight` (string array) must intersect the array (at least one code)
|
|
165
|
+
- **`UserID`**: only listed `UserID`s see it — for internal/DEV menus
|
|
166
|
+
- Multiple keys: user must satisfy **all** (`rightTest` checks each key's intersection with `vueData.user[key]`)
|
|
167
|
+
- No `right` → visible to every logged-in user
|
|
168
|
+
|
|
169
|
+
**Real example — DEV menu for a few UserIDs:**
|
|
170
|
+
|
|
171
|
+
```json
|
|
172
|
+
{
|
|
173
|
+
"name": "DEV",
|
|
174
|
+
"right": { "UserID": ["NV0000177", "NV0001078", "NV0000605"] },
|
|
175
|
+
"submenu": [{ "name": "Tra cứu lịch sử OTP", "url": "/hoso-sms-history" }]
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
> Top menu parents usually have only `name` + `right` + `submenu` (no `url`); `url` sits on each `submenu` item.
|
|
180
|
+
|
|
181
|
+
**Real example — menu for level-1 Team lead/BGH only:**
|
|
182
|
+
|
|
183
|
+
```json
|
|
184
|
+
{
|
|
185
|
+
"name": "Cấp 1 - Tổ trưởng",
|
|
186
|
+
"icon": "mdi-account-tie",
|
|
187
|
+
"right": {
|
|
188
|
+
"FunctionRight": ["10"],
|
|
189
|
+
"SystemRight": [5, 9]
|
|
190
|
+
},
|
|
191
|
+
"submenu": [ ... ]
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
→ Visible only if `FunctionRight` contains `"10"` AND `SystemRight` is 5 or 9.
|
|
196
|
+
|
|
197
|
+
## menuComponent
|
|
198
|
+
|
|
199
|
+
Custom Vue components embedded in the menu area (e.g. school-year or role dropdown):
|
|
200
|
+
|
|
201
|
+
```json
|
|
202
|
+
"menuComponent": [
|
|
203
|
+
{ "el": "uc-nien-khoa" },
|
|
204
|
+
{ "el": "uc-vaitro" }
|
|
205
|
+
]
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Components must be registered in the project's `components/_components.json`.
|
|
209
|
+
|
|
210
|
+
## userProfile (header account block)
|
|
211
|
+
|
|
212
|
+
Header-right avatar/account block:
|
|
213
|
+
|
|
214
|
+
```json
|
|
215
|
+
"userProfile": {
|
|
216
|
+
"color": "white",
|
|
217
|
+
"url-signout": "https://app.example.vn/Signout",
|
|
218
|
+
"url-change-password": "https://app.example.vn/ChangePassword",
|
|
219
|
+
"url-account": "https://app.example.vn",
|
|
220
|
+
"url-avatar": "https://file.example.vn/me/avatarOrigin/",
|
|
221
|
+
"url-app-list": "https://tapi.example.vn/me/auth/ApplicationList"
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
| Field | Meaning |
|
|
226
|
+
| --------------------- | ----------------------------------- |
|
|
227
|
+
| `color` | Text/icon color |
|
|
228
|
+
| `url-signout` | Sign-out URL |
|
|
229
|
+
| `url-change-password` | Change-password URL |
|
|
230
|
+
| `url-account` | Account page URL |
|
|
231
|
+
| `url-avatar` | Avatar base URL (`UserID` appended) |
|
|
232
|
+
| `url-app-list` | App list endpoint (app switcher) |
|
|
233
|
+
|
|
234
|
+
> `menuLeft: false` = no left sidebar (top menu only). `menuLeft: [ ... ]` = sidebar on.
|
|
235
|
+
|
|
236
|
+
## domainSetting (multi-domain)
|
|
237
|
+
|
|
238
|
+
Project on several domains (each with its own tAPI server and DB):
|
|
239
|
+
|
|
240
|
+
```json
|
|
241
|
+
"domainSetting": {
|
|
242
|
+
"sec.example.vn": {
|
|
243
|
+
"apiDomain": "https://tapi.example.vn/acc/",
|
|
244
|
+
"fileDomain": "https://file.example.vn/acc/",
|
|
245
|
+
"login": "https://app.example.vn",
|
|
246
|
+
"userInfo": "https://tapi.example.vn/acc/UserInfo/acc"
|
|
247
|
+
},
|
|
248
|
+
"sec.example3.vn": {
|
|
249
|
+
"apiDomain": "https://api.example3.vn/acc/",
|
|
250
|
+
"fileDomain": "https://file.example3.vn/acc/",
|
|
251
|
+
"login": "https://login.example3.vn",
|
|
252
|
+
"userInfo": "https://api.example3.vn/acc/UserInfo/acc"
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Key = hostname; runtime applies the current domain's override.
|
|
258
|
+
|
|
259
|
+
## module.json `set` — per-page override
|
|
260
|
+
|
|
261
|
+
`set` can override any project.json field. Common:
|
|
262
|
+
|
|
263
|
+
```json
|
|
264
|
+
"set": {
|
|
265
|
+
"title": "Trang quản lý sinh viên",
|
|
266
|
+
"menu": false,
|
|
267
|
+
"menuLeft": [ ... ]
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
| Use `set` when | Example |
|
|
272
|
+
| ------------------------------ | ----------------------------------------- |
|
|
273
|
+
| Page has its own title | `"title": "Nhập điểm Tiểu học"` |
|
|
274
|
+
| Page hides header menu | `"menu": false` |
|
|
275
|
+
| Page needs a different sidebar | Override `"menuLeft"` with a shorter list |
|
|
276
|
+
| Landing / public page | `"menu": false, "menuLeft": []` |
|
|
277
|
+
|
|
278
|
+
**Do not repeat unchanged fields** — declare only what differs from the project default.
|
|
279
|
+
|
|
280
|
+
## File relation summary
|
|
281
|
+
|
|
282
|
+
```
|
|
283
|
+
project.json.data ← config mặc định cho toàn project
|
|
284
|
+
↓ bị override bởi
|
|
285
|
+
module.json.set ← config riêng cho từng trang
|
|
286
|
+
|
|
287
|
+
Effective config = merge(project.json.data, module.json.set)
|
|
288
|
+
(module.set được ưu tiên)
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## openWindow — URL parameter rule
|
|
292
|
+
|
|
293
|
+
Pass in the `openWindow` URL **only the parent record ID** the child queries the DB with. Never pass names, labels or any field the child can look up.
|
|
294
|
+
|
|
295
|
+
```json
|
|
296
|
+
// ✅ Đúng — chỉ truyền ID cần thiết
|
|
297
|
+
"url": "`/ContestDetail?ContestID={{sContestID}}"
|
|
298
|
+
|
|
299
|
+
// ❌ Sai — truyền thừa field có thể tra từ DB
|
|
300
|
+
"url": "`/ContestDetail?ContestID={{sContestID}}&ContestName={{sContestName}}"
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Why: the child loads everything by ID; extra fields lengthen the URL, go stale, and couple the modules.
|
|
@@ -51,7 +51,7 @@ Begin
|
|
|
51
51
|
RAISERROR(N'Bạn không có quyền tạo dự án', 16, 1)
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
**No right ⇒ STOP, report, do nothing else.** Project goes before the DB because `INSERT INTO tblA_Project` needs only `ProjectID`/`ProjectName`/`GroupName`/`Framework`/`pDomain`/`Router` — no database. DB-first then rejection leaves a real DB, two SQL logins and an orphan `acc` record;
|
|
54
|
+
**No right ⇒ STOP, report, do nothing else.** Project goes before the DB because `INSERT INTO tblA_Project` needs only `ProjectID`/`ProjectName`/`GroupName`/`Framework`/`pDomain`/`Router` — no database. DB-first then rejection leaves a real DB, two SQL logins and an orphan `acc` record; never run `DROP DATABASE` (fui has no guard for it), so cleanup is the user's manual job. `fui project new` _is_ the gate A check (no extra call); rejected ⇒ nothing created (if B was checked first).
|
|
55
55
|
|
|
56
56
|
| Operation | SP | Condition |
|
|
57
57
|
| ----------------------------------------------------- | --------------------------------------------------------- | ------------------------------------------ |
|
|
@@ -207,7 +207,7 @@ fui calls `SM_Modules_Update` **twice**: `UpdateConnection=0` creates the record
|
|
|
207
207
|
|
|
208
208
|
- 🔴 **Credentials = Step 3 APP account, NEVER the dev account.** Dev account ⇒ every app API call runs as `db_owner`, with no visible error.
|
|
209
209
|
- New `ModuleID` = `MAX(ModuleID)+1` over **all** `acc.tblModules` ⇒ needs an `acc` connection (`fui db add-by-name acc`). Can't use `fui db alias list`: it lists only modules **you** manage; MAX over a subset may collide.
|
|
210
|
-
- Existing alias ⇒ hard error, no overwrite.
|
|
210
|
+
- Existing alias ⇒ hard error, no overwrite. An explicit `--module-id` that is already in `acc.tblModules` (or used by an alias you manage) ⇒ hard error too; without an `acc` connection it can only be checked against the aliases you manage, and fui says so.
|
|
211
211
|
|
|
212
212
|
Then `fui db add-by-name tenalias` works — [db-workflow.md](db-workflow.md) §2b.
|
|
213
213
|
|
|
@@ -223,7 +223,7 @@ Order is **mandatory**:
|
|
|
223
223
|
3. fui user add DEV001 --right 3
|
|
224
224
|
```
|
|
225
225
|
|
|
226
|
-
**`fui user add`/`set`/`remove` take only the exact `UserID`** — a structural identity gate: you must run `fui user search` and see `
|
|
226
|
+
**`fui user add`/`set`/`remove` take only the exact `UserID`** — a structural identity gate: you must run `fui user search` and see its `userId`, `username`, `fullName` columns. **Show that table and let the user pick before granting** — duplicate names are common; a wrong Admin grant can't be undone in one call.
|
|
227
227
|
|
|
228
228
|
| Level | Meaning |
|
|
229
229
|
| ----- | -------------------------------------------------------------------------------------- |
|
|
@@ -243,7 +243,7 @@ Order is **mandatory**:
|
|
|
243
243
|
|
|
244
244
|
Step 6 ends **phase 1 only**: the project is empty, runtime config still SP-seeded empty strings, no tables/modules/right codes. Don't report "done"; don't jump into module.json.
|
|
245
245
|
|
|
246
|
-
1. `fui project sync
|
|
246
|
+
1. `fui project sync -p <projectId>`, then work from `{workspace}/<projectId>` (or pass `-p <projectId>`) — pulls the project into the workspace; `fui use` needs a module.
|
|
247
247
|
2. **Runtime config (mandatory)**: edit `{projectId}/project.json` (`apiDomain` → new alias, `title`, `login`, `menu`), then `fui project publish-config` — [project-config.md](project-config.md). Skipped ⇒ the project can't call any API.
|
|
248
248
|
3. `fui db add-by-name <apiName>` ([db-workflow.md](db-workflow.md) §2c) — also **proves** Steps 3 and 5 (app account + `acc` record OK).
|
|
249
249
|
4. Resume [system-design.md](system-design.md) at **Step 1**, following the index.
|