@fui-org/fui-cli 2.1.4 → 2.2.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 +27 -7
- package/dist/fui-qrb1j9jz.js +397 -0
- package/dist/fui.js +1 -1
- package/package.json +1 -1
- package/skills/fui/SKILL.md +143 -38
- package/skills/fui/agents/fui-builder.md +62 -0
- package/skills/fui/agents/fui-tester.md +77 -0
- 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 +5 -4
- 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 +1 -2
- 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 +2 -2
- 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/team-run.md +141 -0
- package/skills/fui/references/tools-registry.md +211 -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 +11 -11
- 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
|
-
# FUI UI Patterns — Core
|
|
2
|
-
|
|
3
|
-
> Owns: **core UI patterns: toolbar/filter, permission gate, UI design checklist**. Table/dialog/cross-window/layout live in separate `ui-*` files.
|
|
4
|
-
|
|
5
|
-
Patterns taken from project **security-manager** — use as reference for new modules. Load specialised files as needed:
|
|
6
|
-
|
|
7
|
-
| Need | File |
|
|
8
|
-
| -------------------------------------------------------------------------------------- | -------------------------------------------------------- |
|
|
9
|
-
| f-table + in-cell controls (t-check/t-button/t-select/t-menu, ctrl-update, chip-group) | [ui-table-cell-patterns.md](ui-table-cell-patterns.md) |
|
|
10
|
-
| Dialog/popup form (hidden-container, open dialog with row, checkbox-list) | [ui-dialog-patterns.md](ui-dialog-patterns.md) |
|
|
11
|
-
| Sub-window & data exchange (`openWindow`, `#PARENT`/`#Win`) | [ui-crosswindow-patterns.md](ui-crosswindow-patterns.md) |
|
|
12
|
-
| Layout (page-container, master-detail, v-switch, date init, dynamic label) | [ui-layout-patterns.md](ui-layout-patterns.md) |
|
|
13
|
-
|
|
14
|
-
---
|
|
15
|
-
|
|
16
|
-
## 1. `data[]` structure
|
|
17
|
-
|
|
18
|
-
`data` initialises state + actions, processed top to bottom.
|
|
19
|
-
|
|
20
|
-
- Object with **no action key** (no API, CALL, IF…) → state init
|
|
21
|
-
- Object whose **key is a function name** → named (reusable) action
|
|
22
|
-
- Trailing `{ "API": ..., "OUT": ... }` or `{ "CALL": ... }` → runs automatically on load
|
|
23
|
-
|
|
24
|
-
```json
|
|
25
|
-
"data": [
|
|
26
|
-
{
|
|
27
|
-
"dsUser": [],
|
|
28
|
-
"dsGroup": [],
|
|
29
|
-
"sDepartment": null,
|
|
30
|
-
"dlUserInfo": false
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
"getDepartment": { "API": "SM_DepartmentSelectAll", "OUT": "dsDepartment" },
|
|
34
|
-
"getGroup": { "API": "SM_Group_SelectAll", "OUT": "dsGroup" },
|
|
35
|
-
"getUser": {
|
|
36
|
-
"API": "SM_Users_SelectByDepGroup",
|
|
37
|
-
"IN": { "GroupID": "sGroup", "DepartmentID": "sDepartment" },
|
|
38
|
-
"OUT": "dsUser"
|
|
39
|
-
}
|
|
40
|
-
},
|
|
41
|
-
{ "CALL": "getDepartment" },
|
|
42
|
-
{ "CALL": "getGroup" }
|
|
43
|
-
]
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
> **Auto-startup**: trailing CALLs in `data[]` run on module load — no event handler needed.
|
|
47
|
-
|
|
48
|
-
---
|
|
49
|
-
|
|
50
|
-
## 2. Cascading API init
|
|
51
|
-
|
|
52
|
-
`CALLBACK` sets a value from the first API result; the next API runs via `watch`.
|
|
53
|
-
|
|
54
|
-
```json
|
|
55
|
-
"data": [
|
|
56
|
-
{ "dsModule": [], "sModuleID": null },
|
|
57
|
-
{
|
|
58
|
-
"API": "/SM_Modules_SelectAll",
|
|
59
|
-
"OUT": "dsModule",
|
|
60
|
-
"CALLBACK": { "sModuleID": "dsModule[0].ModuleID" }
|
|
61
|
-
}
|
|
62
|
-
],
|
|
63
|
-
"watch": {
|
|
64
|
-
"sModuleID": { "CALL": "getFunctionRight" }
|
|
65
|
-
}
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
> CALLBACK sets `sModuleID` → watch fires `getFunctionRight`. No manual call.
|
|
69
|
-
|
|
70
|
-
---
|
|
71
|
-
|
|
72
|
-
## 3. Watch — cascading filter
|
|
73
|
-
|
|
74
|
-
Filter change → reload. Several actions → array of CALL.
|
|
75
|
-
|
|
76
|
-
```json
|
|
77
|
-
"watch": {
|
|
78
|
-
"sDepartment": { "CALL": "getUser" },
|
|
79
|
-
"sGroup": { "CALL": "getUser" },
|
|
80
|
-
"sModuleID": [
|
|
81
|
-
{ "CALL": "getUserModule" },
|
|
82
|
-
{ "CALL": "getSysRight" },
|
|
83
|
-
{ "CALL": "getFunctionRight" }
|
|
84
|
-
]
|
|
85
|
-
}
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
---
|
|
89
|
-
|
|
90
|
-
## 4. Toolbar / filter row
|
|
91
|
-
|
|
92
|
-
Row of filter fields + action buttons; `v-spacer` pushes the button group right.
|
|
93
|
-
|
|
94
|
-
> **Rule: EVERY column is `"col": "shrink"` except `v-spacer`.** A non-`shrink` column takes a share of free space — including an empty-`w` column and buttons. Missing `shrink` → buttons stretch, gaps appear between buttons, and the trailing group doesn't reach the right edge even with `v-spacer`. Standard template + measurements: [controls-patterns.md](controls-patterns.md) §Toolbar row.
|
|
95
|
-
|
|
96
|
-
```json
|
|
97
|
-
{
|
|
98
|
-
"prop": "",
|
|
99
|
-
"cols": [
|
|
100
|
-
{
|
|
101
|
-
"el": "v-autocomplete",
|
|
102
|
-
"col": { "class": "shrink" },
|
|
103
|
-
"attr": {
|
|
104
|
-
"class": "flex-md-grow-0",
|
|
105
|
-
"style": "width:200px",
|
|
106
|
-
"v-model": "sGroup",
|
|
107
|
-
"label": "Group",
|
|
108
|
-
":items": "dsGroup",
|
|
109
|
-
"item-text": "GroupName",
|
|
110
|
-
"item-value": "GroupID",
|
|
111
|
-
":required": false
|
|
112
|
-
},
|
|
113
|
-
"w": "200"
|
|
114
|
-
},
|
|
115
|
-
{
|
|
116
|
-
"el": "v-text-field",
|
|
117
|
-
"col": { "class": "shrink" },
|
|
118
|
-
"attr": {
|
|
119
|
-
"class": "flex-md-grow-0",
|
|
120
|
-
"style": "width:200px",
|
|
121
|
-
"v-model": "UserID",
|
|
122
|
-
"label": "User ID",
|
|
123
|
-
"clearable": ""
|
|
124
|
-
},
|
|
125
|
-
"w": "200"
|
|
126
|
-
},
|
|
127
|
-
{
|
|
128
|
-
"el": "f-button",
|
|
129
|
-
"col": { "class": "shrink" },
|
|
130
|
-
"attr": {
|
|
131
|
-
":checkvalid": false,
|
|
132
|
-
"color": "primary",
|
|
133
|
-
"label": "Tìm",
|
|
134
|
-
"icon-text": "mdi-magnify",
|
|
135
|
-
":action": { "CALL": "getUser" }
|
|
136
|
-
}
|
|
137
|
-
},
|
|
138
|
-
{ "el": "v-spacer", "attr": {} },
|
|
139
|
-
{
|
|
140
|
-
"el": "f-button",
|
|
141
|
-
"col": { "v-if": "vueData.user.SystemRight==9", "class": "shrink" },
|
|
142
|
-
"attr": {
|
|
143
|
-
":checkvalid": false,
|
|
144
|
-
":disabled": "!sGroup",
|
|
145
|
-
"color": "primary",
|
|
146
|
-
"label": "Add User",
|
|
147
|
-
"icon-text": "mdi-account-multiple-plus",
|
|
148
|
-
":action": [{ "addNew": 1, "sUserID": "", "sUserName": "" }, { "dlUserInfo": true }]
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
]
|
|
152
|
-
}
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
- Filter fields: `w` in px (`>= 25`) + `col: "shrink"`. The `attr: { class: "flex-md-grow-0", style: "width:Xpx" }` pair above is **redundant** — `w` + `shrink` already gives that width (measured: `w:150` + `shrink` → exactly 150px on V2 and V3). It stays only because the example is copied from production; omit it in new code.
|
|
156
|
-
- Search button right after the fields, also `col: "shrink"` (a non-shrink button stretches).
|
|
157
|
-
- **Always `class: "shrink"` / `"col": "shrink"`, never `shrink: true`** — the bare attribute works only on V2; on V3 it's inert and the column goes full width.
|
|
158
|
-
- `v-spacer` is the **only** non-`shrink` column — it must grow to push the following group right.
|
|
159
|
-
- Admin Add/Delete buttons: after the spacer, permission `v-if` — `col: { "v-if": "…", "class": "shrink" }` (object form when extra keys are needed; keep `shrink`).
|
|
160
|
-
- Right-align the whole row without `v-spacer`: `"prop": "row justify-end"`.
|
|
161
|
-
|
|
162
|
-
---
|
|
163
|
-
|
|
164
|
-
## 5. Permission gating
|
|
165
|
-
|
|
166
|
-
Show/hide by the user's `SystemRight`.
|
|
167
|
-
|
|
168
|
-
```json
|
|
169
|
-
"col": { "v-if": "vueData.user.SystemRight>1" }
|
|
170
|
-
"col": { "v-if": "vueData.user.SystemRight==9" }
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
- `>1` — any user with rights (not the lowest level)
|
|
174
|
-
- `==9` — top admin only
|
|
175
|
-
|
|
176
|
-
---
|
|
177
|
-
|
|
178
|
-
## 6. IF/THEN/ELSE
|
|
179
|
-
|
|
180
|
-
Branching inside an action or CALLBACK.
|
|
181
|
-
|
|
182
|
-
```json
|
|
183
|
-
{
|
|
184
|
-
"IF": "TypeLoad==1",
|
|
185
|
-
"THEN": { "CALL": "getComputer" },
|
|
186
|
-
"ELSE": {
|
|
187
|
-
"IF": "TypeLoad==2",
|
|
188
|
-
"THEN": { "CALL": "getComputerDinied" },
|
|
189
|
-
"ELSE": { "CALL": "getComputerNotVisited" }
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
- Condition: JS expression using vueData fields directly (no `vueData.` prefix).
|
|
195
|
-
- Nest IF/ELSE for multi-branch switch.
|
|
196
|
-
|
|
197
|
-
Real use: module with several view modes (`TypeLoad`) — after an update, refresh the list currently shown.
|
|
198
|
-
|
|
199
|
-
```json
|
|
200
|
-
"data": [
|
|
201
|
-
{ "TypeLoad": 1 }
|
|
202
|
-
]
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
---
|
|
206
|
-
|
|
207
|
-
## 7. CALLBACK chaining
|
|
208
|
-
|
|
209
|
-
Array CALLBACK → runs in order.
|
|
210
|
-
|
|
211
|
-
> **Several commands MUST be an array, one `{}` per command.** Merging `CALL` + `MESS` + `EXE` into one object runs only **one** (priority `API` → `CALL` → `EXE` → `FUN` → `IF` → `MESS` → `MESSBOX`, each branch `return`s immediately); the rest are dropped silently — no error, no `[Vue warn]`. Full rule + runtime reason: [controls-patterns.md](controls-patterns.md) §CORE RULE.
|
|
212
|
-
|
|
213
|
-
```json
|
|
214
|
-
"CALLBACK": [
|
|
215
|
-
{ "MESS": "Cập nhật thành công." },
|
|
216
|
-
{ "dlUserInfo": false },
|
|
217
|
-
{ "CALL": "getUser" }
|
|
218
|
-
]
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
```json
|
|
222
|
-
"CALLBACK": [
|
|
223
|
-
{ "CALL": "getUserModule" },
|
|
224
|
-
{ "sUserDeptSelected": [], "dlAddUser": false },
|
|
225
|
-
{ "MESS": "Thêm user thành công." }
|
|
226
|
-
]
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
Usual order: reload data → reset state → show message.
|
|
230
|
-
|
|
231
|
-
---
|
|
232
|
-
|
|
233
|
-
## 8. `set` — module-level config
|
|
234
|
-
|
|
235
|
-
`set` at module.json root configures the whole module.
|
|
236
|
-
|
|
237
|
-
```json
|
|
238
|
-
"set": {
|
|
239
|
-
"menu": false
|
|
240
|
-
}
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
- `menu: false` — hide navigation menu (sub-windows, log viewers).
|
|
244
|
-
|
|
245
|
-
> Module opened via `openWindow`: declare `"set": { "menu": false }` in the child module's module.json — **don't** pass `?menu=0` in the URL. Full pattern (incl. setting it dynamically from `data[]`): [ui-crosswindow-patterns.md](ui-crosswindow-patterns.md).
|
|
246
|
-
|
|
247
|
-
---
|
|
248
|
-
|
|
249
|
-
## 9. CALLBACK with JS expression
|
|
250
|
-
|
|
251
|
-
`{{ expression }}` in CALLBACK runs complex code after the API.
|
|
252
|
-
|
|
253
|
-
```json
|
|
254
|
-
"CALLBACK": "{{ _.forEach(dsUsers, (x)=> x.FunctionName = JSON.parse(x.FunctionName)) }}"
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
```json
|
|
258
|
-
"CALLBACK": "{{ _.forEach(dsUserFunction, (x)=> x.FunctionName = JSON.parse(x.FunctionName)), _.forEach(dsUserFunction, (x)=> x.UserSysItem = JSON.parse(x.UserSysItem)) }}"
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
> Use when the API returns a JSON string that must be parsed to object/array before render.
|
|
262
|
-
|
|
263
|
-
---
|
|
264
|
-
|
|
265
|
-
## 10. script.js — helpers
|
|
266
|
-
|
|
267
|
-
`script.js` holds helpers called from `v-on:click` or actions. They access `vueData` directly; use for logic plain action JSON can't express.
|
|
268
|
-
|
|
269
|
-
```javascript
|
|
270
|
-
// script.js
|
|
271
|
-
function setFunction(user, fc) {
|
|
272
|
-
vueData.sFunctionSelected = [];
|
|
273
|
-
vueData.sUserSelected = [];
|
|
274
|
-
vueData.sUserSelected.push(user);
|
|
275
|
-
if (!fc) return;
|
|
276
|
-
vueData.dsFunctionRight.map((item) => {
|
|
277
|
-
if (fc.some((fci) => fci.FunctionID == item.FunctionID)) {
|
|
278
|
-
vueData.sFunctionSelected.push(item.FunctionID.toString());
|
|
279
|
-
}
|
|
280
|
-
});
|
|
281
|
-
}
|
|
282
|
-
```
|
|
283
|
-
|
|
284
|
-
Called from `v-on:click` in a header:
|
|
285
|
-
|
|
286
|
-
```json
|
|
287
|
-
"v-on:click": "vueData.dlFunction = true; CALL(setFunction(item, item.FunctionName));"
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
---
|
|
291
|
-
|
|
292
|
-
## UI design checklist
|
|
293
|
-
|
|
294
|
-
> `module.json` structure checklist (`data[]` order, grid wrapper, `w`, `v-on:`, `set`, valid JSON) is in [module-json-anatomy.md](module-json-anatomy.md). Below is UI-specific only.
|
|
295
|
-
|
|
296
|
-
1. **Toolbar row**: shrink filters + v-spacer + permission-gated buttons
|
|
297
|
-
2. **f-table**: fixed-header, item-class for row highlight, correct item-key — [ui-table-cell-patterns.md](ui-table-cell-patterns.md)
|
|
298
|
-
3. **Dialogs**: always in hidden-container, v-card[padding:0] + v-toolbar[dense flat] — [ui-dialog-patterns.md](ui-dialog-patterns.md)
|
|
299
|
-
4. **menuChucNang**: define in data[], reference via t-menu in headers — [ui-table-cell-patterns.md](ui-table-cell-patterns.md)
|
|
300
|
-
5. **ctrl-update**: simple CRUD; manual dialog for complex forms
|
|
301
|
-
6. **CALLBACK**: chaining array — reload → reset state → MESS
|
|
302
|
-
7. **#PARENT**: set at the top of data[] before startup CALLs — [ui-crosswindow-patterns.md](ui-crosswindow-patterns.md)
|
|
303
|
-
8. **Permission**: `v-if: "vueData.user.SystemRight>1"` for admin-only sections
|
|
1
|
+
# FUI UI Patterns — Core
|
|
2
|
+
|
|
3
|
+
> Owns: **core UI patterns: toolbar/filter, permission gate, UI design checklist**. Table/dialog/cross-window/layout live in separate `ui-*` files.
|
|
4
|
+
|
|
5
|
+
Patterns taken from project **security-manager** — use as reference for new modules. Load specialised files as needed:
|
|
6
|
+
|
|
7
|
+
| Need | File |
|
|
8
|
+
| -------------------------------------------------------------------------------------- | -------------------------------------------------------- |
|
|
9
|
+
| f-table + in-cell controls (t-check/t-button/t-select/t-menu, ctrl-update, chip-group) | [ui-table-cell-patterns.md](ui-table-cell-patterns.md) |
|
|
10
|
+
| Dialog/popup form (hidden-container, open dialog with row, checkbox-list) | [ui-dialog-patterns.md](ui-dialog-patterns.md) |
|
|
11
|
+
| Sub-window & data exchange (`openWindow`, `#PARENT`/`#Win`) | [ui-crosswindow-patterns.md](ui-crosswindow-patterns.md) |
|
|
12
|
+
| Layout (page-container, master-detail, v-switch, date init, dynamic label) | [ui-layout-patterns.md](ui-layout-patterns.md) |
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 1. `data[]` structure
|
|
17
|
+
|
|
18
|
+
`data` initialises state + actions, processed top to bottom.
|
|
19
|
+
|
|
20
|
+
- Object with **no action key** (no API, CALL, IF…) → state init
|
|
21
|
+
- Object whose **key is a function name** → named (reusable) action
|
|
22
|
+
- Trailing `{ "API": ..., "OUT": ... }` or `{ "CALL": ... }` → runs automatically on load
|
|
23
|
+
|
|
24
|
+
```json
|
|
25
|
+
"data": [
|
|
26
|
+
{
|
|
27
|
+
"dsUser": [],
|
|
28
|
+
"dsGroup": [],
|
|
29
|
+
"sDepartment": null,
|
|
30
|
+
"dlUserInfo": false
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"getDepartment": { "API": "SM_DepartmentSelectAll", "OUT": "dsDepartment" },
|
|
34
|
+
"getGroup": { "API": "SM_Group_SelectAll", "OUT": "dsGroup" },
|
|
35
|
+
"getUser": {
|
|
36
|
+
"API": "SM_Users_SelectByDepGroup",
|
|
37
|
+
"IN": { "GroupID": "sGroup", "DepartmentID": "sDepartment" },
|
|
38
|
+
"OUT": "dsUser"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
{ "CALL": "getDepartment" },
|
|
42
|
+
{ "CALL": "getGroup" }
|
|
43
|
+
]
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
> **Auto-startup**: trailing CALLs in `data[]` run on module load — no event handler needed.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## 2. Cascading API init
|
|
51
|
+
|
|
52
|
+
`CALLBACK` sets a value from the first API result; the next API runs via `watch`.
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
"data": [
|
|
56
|
+
{ "dsModule": [], "sModuleID": null },
|
|
57
|
+
{
|
|
58
|
+
"API": "/SM_Modules_SelectAll",
|
|
59
|
+
"OUT": "dsModule",
|
|
60
|
+
"CALLBACK": { "sModuleID": "dsModule[0].ModuleID" }
|
|
61
|
+
}
|
|
62
|
+
],
|
|
63
|
+
"watch": {
|
|
64
|
+
"sModuleID": { "CALL": "getFunctionRight" }
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
> CALLBACK sets `sModuleID` → watch fires `getFunctionRight`. No manual call.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## 3. Watch — cascading filter
|
|
73
|
+
|
|
74
|
+
Filter change → reload. Several actions → array of CALL.
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
"watch": {
|
|
78
|
+
"sDepartment": { "CALL": "getUser" },
|
|
79
|
+
"sGroup": { "CALL": "getUser" },
|
|
80
|
+
"sModuleID": [
|
|
81
|
+
{ "CALL": "getUserModule" },
|
|
82
|
+
{ "CALL": "getSysRight" },
|
|
83
|
+
{ "CALL": "getFunctionRight" }
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 4. Toolbar / filter row
|
|
91
|
+
|
|
92
|
+
Row of filter fields + action buttons; `v-spacer` pushes the button group right.
|
|
93
|
+
|
|
94
|
+
> **Rule: EVERY column is `"col": "shrink"` except `v-spacer`.** A non-`shrink` column takes a share of free space — including an empty-`w` column and buttons. Missing `shrink` → buttons stretch, gaps appear between buttons, and the trailing group doesn't reach the right edge even with `v-spacer`. Standard template + measurements: [controls-patterns.md](controls-patterns.md) §Toolbar row.
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"prop": "",
|
|
99
|
+
"cols": [
|
|
100
|
+
{
|
|
101
|
+
"el": "v-autocomplete",
|
|
102
|
+
"col": { "class": "shrink" },
|
|
103
|
+
"attr": {
|
|
104
|
+
"class": "flex-md-grow-0",
|
|
105
|
+
"style": "width:200px",
|
|
106
|
+
"v-model": "sGroup",
|
|
107
|
+
"label": "Group",
|
|
108
|
+
":items": "dsGroup",
|
|
109
|
+
"item-text": "GroupName",
|
|
110
|
+
"item-value": "GroupID",
|
|
111
|
+
":required": false
|
|
112
|
+
},
|
|
113
|
+
"w": "200"
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"el": "v-text-field",
|
|
117
|
+
"col": { "class": "shrink" },
|
|
118
|
+
"attr": {
|
|
119
|
+
"class": "flex-md-grow-0",
|
|
120
|
+
"style": "width:200px",
|
|
121
|
+
"v-model": "UserID",
|
|
122
|
+
"label": "User ID",
|
|
123
|
+
"clearable": ""
|
|
124
|
+
},
|
|
125
|
+
"w": "200"
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"el": "f-button",
|
|
129
|
+
"col": { "class": "shrink" },
|
|
130
|
+
"attr": {
|
|
131
|
+
":checkvalid": false,
|
|
132
|
+
"color": "primary",
|
|
133
|
+
"label": "Tìm",
|
|
134
|
+
"icon-text": "mdi-magnify",
|
|
135
|
+
":action": { "CALL": "getUser" }
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
{ "el": "v-spacer", "attr": {} },
|
|
139
|
+
{
|
|
140
|
+
"el": "f-button",
|
|
141
|
+
"col": { "v-if": "vueData.user.SystemRight==9", "class": "shrink" },
|
|
142
|
+
"attr": {
|
|
143
|
+
":checkvalid": false,
|
|
144
|
+
":disabled": "!sGroup",
|
|
145
|
+
"color": "primary",
|
|
146
|
+
"label": "Add User",
|
|
147
|
+
"icon-text": "mdi-account-multiple-plus",
|
|
148
|
+
":action": [{ "addNew": 1, "sUserID": "", "sUserName": "" }, { "dlUserInfo": true }]
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
]
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
- Filter fields: `w` in px (`>= 25`) + `col: "shrink"`. The `attr: { class: "flex-md-grow-0", style: "width:Xpx" }` pair above is **redundant** — `w` + `shrink` already gives that width (measured: `w:150` + `shrink` → exactly 150px on V2 and V3). It stays only because the example is copied from production; omit it in new code.
|
|
156
|
+
- Search button right after the fields, also `col: "shrink"` (a non-shrink button stretches).
|
|
157
|
+
- **Always `class: "shrink"` / `"col": "shrink"`, never `shrink: true`** — the bare attribute works only on V2; on V3 it's inert and the column goes full width.
|
|
158
|
+
- `v-spacer` is the **only** non-`shrink` column — it must grow to push the following group right.
|
|
159
|
+
- Admin Add/Delete buttons: after the spacer, permission `v-if` — `col: { "v-if": "…", "class": "shrink" }` (object form when extra keys are needed; keep `shrink`).
|
|
160
|
+
- Right-align the whole row without `v-spacer`: `"prop": "row justify-end"`.
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## 5. Permission gating
|
|
165
|
+
|
|
166
|
+
Show/hide by the user's `SystemRight`.
|
|
167
|
+
|
|
168
|
+
```json
|
|
169
|
+
"col": { "v-if": "vueData.user.SystemRight>1" }
|
|
170
|
+
"col": { "v-if": "vueData.user.SystemRight==9" }
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
- `>1` — any user with rights (not the lowest level)
|
|
174
|
+
- `==9` — top admin only
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## 6. IF/THEN/ELSE
|
|
179
|
+
|
|
180
|
+
Branching inside an action or CALLBACK.
|
|
181
|
+
|
|
182
|
+
```json
|
|
183
|
+
{
|
|
184
|
+
"IF": "TypeLoad==1",
|
|
185
|
+
"THEN": { "CALL": "getComputer" },
|
|
186
|
+
"ELSE": {
|
|
187
|
+
"IF": "TypeLoad==2",
|
|
188
|
+
"THEN": { "CALL": "getComputerDinied" },
|
|
189
|
+
"ELSE": { "CALL": "getComputerNotVisited" }
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
- Condition: JS expression using vueData fields directly (no `vueData.` prefix).
|
|
195
|
+
- Nest IF/ELSE for multi-branch switch.
|
|
196
|
+
|
|
197
|
+
Real use: module with several view modes (`TypeLoad`) — after an update, refresh the list currently shown.
|
|
198
|
+
|
|
199
|
+
```json
|
|
200
|
+
"data": [
|
|
201
|
+
{ "TypeLoad": 1 }
|
|
202
|
+
]
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## 7. CALLBACK chaining
|
|
208
|
+
|
|
209
|
+
Array CALLBACK → runs in order.
|
|
210
|
+
|
|
211
|
+
> **Several commands MUST be an array, one `{}` per command.** Merging `CALL` + `MESS` + `EXE` into one object runs only **one** (priority `API` → `CALL` → `EXE` → `FUN` → `IF` → `MESS` → `MESSBOX`, each branch `return`s immediately); the rest are dropped silently — no error, no `[Vue warn]`. Full rule + runtime reason: [controls-patterns.md](controls-patterns.md) §CORE RULE.
|
|
212
|
+
|
|
213
|
+
```json
|
|
214
|
+
"CALLBACK": [
|
|
215
|
+
{ "MESS": "Cập nhật thành công." },
|
|
216
|
+
{ "dlUserInfo": false },
|
|
217
|
+
{ "CALL": "getUser" }
|
|
218
|
+
]
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
```json
|
|
222
|
+
"CALLBACK": [
|
|
223
|
+
{ "CALL": "getUserModule" },
|
|
224
|
+
{ "sUserDeptSelected": [], "dlAddUser": false },
|
|
225
|
+
{ "MESS": "Thêm user thành công." }
|
|
226
|
+
]
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Usual order: reload data → reset state → show message.
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## 8. `set` — module-level config
|
|
234
|
+
|
|
235
|
+
`set` at module.json root configures the whole module.
|
|
236
|
+
|
|
237
|
+
```json
|
|
238
|
+
"set": {
|
|
239
|
+
"menu": false
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
- `menu: false` — hide navigation menu (sub-windows, log viewers).
|
|
244
|
+
|
|
245
|
+
> Module opened via `openWindow`: declare `"set": { "menu": false }` in the child module's module.json — **don't** pass `?menu=0` in the URL. Full pattern (incl. setting it dynamically from `data[]`): [ui-crosswindow-patterns.md](ui-crosswindow-patterns.md).
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## 9. CALLBACK with JS expression
|
|
250
|
+
|
|
251
|
+
`{{ expression }}` in CALLBACK runs complex code after the API.
|
|
252
|
+
|
|
253
|
+
```json
|
|
254
|
+
"CALLBACK": "{{ _.forEach(dsUsers, (x)=> x.FunctionName = JSON.parse(x.FunctionName)) }}"
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
```json
|
|
258
|
+
"CALLBACK": "{{ _.forEach(dsUserFunction, (x)=> x.FunctionName = JSON.parse(x.FunctionName)), _.forEach(dsUserFunction, (x)=> x.UserSysItem = JSON.parse(x.UserSysItem)) }}"
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
> Use when the API returns a JSON string that must be parsed to object/array before render.
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## 10. script.js — helpers
|
|
266
|
+
|
|
267
|
+
`script.js` holds helpers called from `v-on:click` or actions. They access `vueData` directly; use for logic plain action JSON can't express.
|
|
268
|
+
|
|
269
|
+
```javascript
|
|
270
|
+
// script.js
|
|
271
|
+
function setFunction(user, fc) {
|
|
272
|
+
vueData.sFunctionSelected = [];
|
|
273
|
+
vueData.sUserSelected = [];
|
|
274
|
+
vueData.sUserSelected.push(user);
|
|
275
|
+
if (!fc) return;
|
|
276
|
+
vueData.dsFunctionRight.map((item) => {
|
|
277
|
+
if (fc.some((fci) => fci.FunctionID == item.FunctionID)) {
|
|
278
|
+
vueData.sFunctionSelected.push(item.FunctionID.toString());
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
Called from `v-on:click` in a header:
|
|
285
|
+
|
|
286
|
+
```json
|
|
287
|
+
"v-on:click": "vueData.dlFunction = true; CALL(setFunction(item, item.FunctionName));"
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## UI design checklist
|
|
293
|
+
|
|
294
|
+
> `module.json` structure checklist (`data[]` order, grid wrapper, `w`, `v-on:`, `set`, valid JSON) is in [module-json-anatomy.md](module-json-anatomy.md). Below is UI-specific only.
|
|
295
|
+
|
|
296
|
+
1. **Toolbar row**: shrink filters + v-spacer + permission-gated buttons
|
|
297
|
+
2. **f-table**: fixed-header, item-class for row highlight, correct item-key — [ui-table-cell-patterns.md](ui-table-cell-patterns.md)
|
|
298
|
+
3. **Dialogs**: always in hidden-container, v-card[padding:0] + v-toolbar[dense flat] — [ui-dialog-patterns.md](ui-dialog-patterns.md)
|
|
299
|
+
4. **menuChucNang**: define in data[], reference via t-menu in headers — [ui-table-cell-patterns.md](ui-table-cell-patterns.md)
|
|
300
|
+
5. **ctrl-update**: simple CRUD; manual dialog for complex forms
|
|
301
|
+
6. **CALLBACK**: chaining array — reload → reset state → MESS
|
|
302
|
+
7. **#PARENT**: set at the top of data[] before startup CALLs — [ui-crosswindow-patterns.md](ui-crosswindow-patterns.md)
|
|
303
|
+
8. **Permission**: `v-if: "vueData.user.SystemRight>1"` for admin-only sections
|