@fui-org/fui-cli 0.1.1 → 0.3.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 +18 -3
- package/dist/fui.js +160 -43
- package/package.json +4 -2
- package/skills/fui/SKILL.md +131 -66
- package/skills/fui-skill/README.md +112 -0
- package/skills/fui-skill/SKILL.md +269 -0
- package/skills/fui-skill/assets/projectdefaultstyle.css +518 -0
- package/skills/fui-skill/design-md/airbnb/DESIGN.md +545 -0
- package/skills/fui-skill/design-md/airbnb/README.md +5 -0
- package/skills/fui-skill/design-md/airtable/DESIGN.md +554 -0
- package/skills/fui-skill/design-md/airtable/README.md +5 -0
- package/skills/fui-skill/design-md/apple/DESIGN.md +562 -0
- package/skills/fui-skill/design-md/apple/README.md +5 -0
- package/skills/fui-skill/design-md/asu/DESIGN.md +179 -0
- package/skills/fui-skill/design-md/asu/README.md +107 -0
- package/skills/fui-skill/design-md/binance/DESIGN.md +634 -0
- package/skills/fui-skill/design-md/binance/README.md +5 -0
- package/skills/fui-skill/design-md/bmw/DESIGN.md +544 -0
- package/skills/fui-skill/design-md/bmw/README.md +5 -0
- package/skills/fui-skill/design-md/bmw-m/DESIGN.md +503 -0
- package/skills/fui-skill/design-md/bmw-m/README.md +5 -0
- package/skills/fui-skill/design-md/bugatti/DESIGN.md +454 -0
- package/skills/fui-skill/design-md/bugatti/README.md +5 -0
- package/skills/fui-skill/design-md/cal/DESIGN.md +542 -0
- package/skills/fui-skill/design-md/cal/README.md +5 -0
- package/skills/fui-skill/design-md/claude/DESIGN.md +589 -0
- package/skills/fui-skill/design-md/claude/README.md +5 -0
- package/skills/fui-skill/design-md/clay/DESIGN.md +541 -0
- package/skills/fui-skill/design-md/clay/README.md +5 -0
- package/skills/fui-skill/design-md/clickhouse/DESIGN.md +544 -0
- package/skills/fui-skill/design-md/clickhouse/README.md +5 -0
- package/skills/fui-skill/design-md/cohere/DESIGN.md +451 -0
- package/skills/fui-skill/design-md/cohere/README.md +5 -0
- package/skills/fui-skill/design-md/coinbase/DESIGN.md +570 -0
- package/skills/fui-skill/design-md/coinbase/README.md +5 -0
- package/skills/fui-skill/design-md/composio/DESIGN.md +506 -0
- package/skills/fui-skill/design-md/composio/README.md +5 -0
- package/skills/fui-skill/design-md/cursor/DESIGN.md +537 -0
- package/skills/fui-skill/design-md/cursor/README.md +5 -0
- package/skills/fui-skill/design-md/elevenlabs/DESIGN.md +504 -0
- package/skills/fui-skill/design-md/elevenlabs/README.md +5 -0
- package/skills/fui-skill/design-md/expo/DESIGN.md +526 -0
- package/skills/fui-skill/design-md/expo/README.md +5 -0
- package/skills/fui-skill/design-md/ferrari/DESIGN.md +531 -0
- package/skills/fui-skill/design-md/ferrari/README.md +5 -0
- package/skills/fui-skill/design-md/figma/DESIGN.md +578 -0
- package/skills/fui-skill/design-md/figma/README.md +5 -0
- package/skills/fui-skill/design-md/framer/DESIGN.md +544 -0
- package/skills/fui-skill/design-md/framer/README.md +5 -0
- package/skills/fui-skill/design-md/fui/DESIGN.md +532 -0
- package/skills/fui-skill/design-md/hashicorp/DESIGN.md +575 -0
- package/skills/fui-skill/design-md/hashicorp/README.md +5 -0
- package/skills/fui-skill/design-md/ibm/DESIGN.md +550 -0
- package/skills/fui-skill/design-md/ibm/README.md +5 -0
- package/skills/fui-skill/design-md/intercom/DESIGN.md +546 -0
- package/skills/fui-skill/design-md/intercom/README.md +5 -0
- package/skills/fui-skill/design-md/kraken/DESIGN.md +125 -0
- package/skills/fui-skill/design-md/kraken/README.md +5 -0
- package/skills/fui-skill/design-md/lamborghini/DESIGN.md +288 -0
- package/skills/fui-skill/design-md/lamborghini/README.md +5 -0
- package/skills/fui-skill/design-md/linear.app/DESIGN.md +548 -0
- package/skills/fui-skill/design-md/linear.app/README.md +5 -0
- package/skills/fui-skill/design-md/lovable/DESIGN.md +298 -0
- package/skills/fui-skill/design-md/lovable/README.md +5 -0
- package/skills/fui-skill/design-md/mastercard/DESIGN.md +365 -0
- package/skills/fui-skill/design-md/mastercard/README.md +5 -0
- package/skills/fui-skill/design-md/meta/DESIGN.md +683 -0
- package/skills/fui-skill/design-md/meta/README.md +5 -0
- package/skills/fui-skill/design-md/minimax/DESIGN.md +746 -0
- package/skills/fui-skill/design-md/minimax/README.md +5 -0
- package/skills/fui-skill/design-md/mintlify/DESIGN.md +852 -0
- package/skills/fui-skill/design-md/mintlify/README.md +5 -0
- package/skills/fui-skill/design-md/miro/DESIGN.md +825 -0
- package/skills/fui-skill/design-md/miro/README.md +5 -0
- package/skills/fui-skill/design-md/mistral.ai/DESIGN.md +773 -0
- package/skills/fui-skill/design-md/mistral.ai/README.md +5 -0
- package/skills/fui-skill/design-md/mongodb/DESIGN.md +767 -0
- package/skills/fui-skill/design-md/mongodb/README.md +5 -0
- package/skills/fui-skill/design-md/nike/DESIGN.md +575 -0
- package/skills/fui-skill/design-md/nike/README.md +5 -0
- package/skills/fui-skill/design-md/notion/DESIGN.md +821 -0
- package/skills/fui-skill/design-md/notion/README.md +5 -0
- package/skills/fui-skill/design-md/nvidia/DESIGN.md +640 -0
- package/skills/fui-skill/design-md/nvidia/README.md +5 -0
- package/skills/fui-skill/design-md/ollama/DESIGN.md +539 -0
- package/skills/fui-skill/design-md/ollama/README.md +5 -0
- package/skills/fui-skill/design-md/opencode.ai/DESIGN.md +521 -0
- package/skills/fui-skill/design-md/opencode.ai/README.md +5 -0
- package/skills/fui-skill/design-md/pinterest/DESIGN.md +597 -0
- package/skills/fui-skill/design-md/pinterest/README.md +5 -0
- package/skills/fui-skill/design-md/playstation/DESIGN.md +661 -0
- package/skills/fui-skill/design-md/playstation/README.md +5 -0
- package/skills/fui-skill/design-md/posthog/DESIGN.md +690 -0
- package/skills/fui-skill/design-md/posthog/README.md +5 -0
- package/skills/fui-skill/design-md/raycast/DESIGN.md +669 -0
- package/skills/fui-skill/design-md/raycast/README.md +5 -0
- package/skills/fui-skill/design-md/renault/DESIGN.md +589 -0
- package/skills/fui-skill/design-md/renault/README.md +5 -0
- package/skills/fui-skill/design-md/replicate/DESIGN.md +616 -0
- package/skills/fui-skill/design-md/replicate/README.md +5 -0
- package/skills/fui-skill/design-md/resend/DESIGN.md +585 -0
- package/skills/fui-skill/design-md/resend/README.md +5 -0
- package/skills/fui-skill/design-md/revolut/DESIGN.md +636 -0
- package/skills/fui-skill/design-md/revolut/README.md +5 -0
- package/skills/fui-skill/design-md/runwayml/DESIGN.md +244 -0
- package/skills/fui-skill/design-md/runwayml/README.md +5 -0
- package/skills/fui-skill/design-md/sanity/DESIGN.md +357 -0
- package/skills/fui-skill/design-md/sanity/README.md +5 -0
- package/skills/fui-skill/design-md/sentry/DESIGN.md +551 -0
- package/skills/fui-skill/design-md/sentry/README.md +5 -0
- package/skills/fui-skill/design-md/shopify/DESIGN.md +516 -0
- package/skills/fui-skill/design-md/shopify/README.md +5 -0
- package/skills/fui-skill/design-md/slack/DESIGN.md +482 -0
- package/skills/fui-skill/design-md/spacex/DESIGN.md +363 -0
- package/skills/fui-skill/design-md/spacex/README.md +5 -0
- package/skills/fui-skill/design-md/spotify/DESIGN.md +246 -0
- package/skills/fui-skill/design-md/spotify/README.md +5 -0
- package/skills/fui-skill/design-md/starbucks/DESIGN.md +580 -0
- package/skills/fui-skill/design-md/starbucks/README.md +5 -0
- package/skills/fui-skill/design-md/stripe/DESIGN.md +487 -0
- package/skills/fui-skill/design-md/stripe/README.md +5 -0
- package/skills/fui-skill/design-md/supabase/DESIGN.md +462 -0
- package/skills/fui-skill/design-md/supabase/README.md +5 -0
- package/skills/fui-skill/design-md/superhuman/DESIGN.md +448 -0
- package/skills/fui-skill/design-md/superhuman/README.md +5 -0
- package/skills/fui-skill/design-md/tesla/DESIGN.md +286 -0
- package/skills/fui-skill/design-md/tesla/README.md +5 -0
- package/skills/fui-skill/design-md/theverge/DESIGN.md +339 -0
- package/skills/fui-skill/design-md/theverge/README.md +5 -0
- package/skills/fui-skill/design-md/together.ai/DESIGN.md +633 -0
- package/skills/fui-skill/design-md/together.ai/README.md +5 -0
- package/skills/fui-skill/design-md/uber/DESIGN.md +636 -0
- package/skills/fui-skill/design-md/uber/README.md +5 -0
- package/skills/fui-skill/design-md/vercel/DESIGN.md +736 -0
- package/skills/fui-skill/design-md/vercel/README.md +5 -0
- package/skills/fui-skill/design-md/vodafone/DESIGN.md +538 -0
- package/skills/fui-skill/design-md/vodafone/README.md +5 -0
- package/skills/fui-skill/design-md/voltagent/DESIGN.md +521 -0
- package/skills/fui-skill/design-md/voltagent/README.md +5 -0
- package/skills/fui-skill/design-md/warp/DESIGN.md +526 -0
- package/skills/fui-skill/design-md/warp/README.md +5 -0
- package/skills/fui-skill/design-md/webflow/DESIGN.md +588 -0
- package/skills/fui-skill/design-md/webflow/README.md +5 -0
- package/skills/fui-skill/design-md/wired/DESIGN.md +497 -0
- package/skills/fui-skill/design-md/wired/README.md +5 -0
- package/skills/fui-skill/design-md/wise/DESIGN.md +544 -0
- package/skills/fui-skill/design-md/wise/README.md +5 -0
- package/skills/fui-skill/design-md/x.ai/DESIGN.md +465 -0
- package/skills/fui-skill/design-md/x.ai/README.md +5 -0
- package/skills/fui-skill/design-md/zapier/DESIGN.md +537 -0
- package/skills/fui-skill/design-md/zapier/README.md +5 -0
- package/skills/fui-skill/examples/component.vue +162 -0
- package/skills/fui-skill/examples/f-table-patterns.json +331 -0
- package/skills/fui-skill/examples/module-patterns.json +973 -0
- package/skills/fui-skill/examples/project-patterns.json +222 -0
- package/skills/fui-skill/metadata.json +75 -0
- package/skills/fui-skill/references/INDEX.md +144 -0
- package/skills/fui-skill/references/advanced-techniques.md +160 -0
- package/skills/fui-skill/references/coding-standards.md +112 -0
- package/skills/fui-skill/references/component-design.md +455 -0
- package/skills/fui-skill/references/component-quickref.md +77 -0
- package/skills/fui-skill/references/component-table.md +276 -0
- package/skills/fui-skill/references/components-dialog.md +192 -0
- package/skills/fui-skill/references/components-display.md +147 -0
- package/skills/fui-skill/references/components-echart.md +391 -0
- package/skills/fui-skill/references/components-input.md +359 -0
- package/skills/fui-skill/references/controls-patterns.md +847 -0
- package/skills/fui-skill/references/controls-styling-vocabulary.md +140 -0
- package/skills/fui-skill/references/db-table-design.md +77 -0
- package/skills/fui-skill/references/db-workflow.md +504 -0
- package/skills/fui-skill/references/default-function.md +415 -0
- package/skills/fui-skill/references/design-modes.md +85 -0
- package/skills/fui-skill/references/echart-templates.md +481 -0
- package/skills/fui-skill/references/fastproject.md +97 -0
- package/skills/fui-skill/references/fsheet.md +218 -0
- package/skills/fui-skill/references/fullstack-workflow.md +351 -0
- package/skills/fui-skill/references/module-data-patterns.md +126 -0
- package/skills/fui-skill/references/module-json-anatomy.md +137 -0
- package/skills/fui-skill/references/module-structure.md +260 -0
- package/skills/fui-skill/references/new-session.md +108 -0
- package/skills/fui-skill/references/pdfmake.md +60 -0
- package/skills/fui-skill/references/permission-system.md +169 -0
- package/skills/fui-skill/references/platform-architecture.md +294 -0
- package/skills/fui-skill/references/project-config.md +335 -0
- package/skills/fui-skill/references/project-provisioning.md +383 -0
- package/skills/fui-skill/references/script-map.md +296 -0
- package/skills/fui-skill/references/sql-clr-functions.md +224 -0
- package/skills/fui-skill/references/system-design.md +116 -0
- package/skills/fui-skill/references/tapi-file-api.md +191 -0
- package/skills/fui-skill/references/tapi-permission-patterns.md +158 -0
- package/skills/fui-skill/references/tapi-reference.md +549 -0
- package/skills/fui-skill/references/tools-registry.md +460 -0
- package/skills/fui-skill/references/ui-crosswindow-patterns.md +317 -0
- package/skills/fui-skill/references/ui-dialog-patterns.md +229 -0
- package/skills/fui-skill/references/ui-layout-patterns.md +176 -0
- package/skills/fui-skill/references/ui-patterns.md +315 -0
- package/skills/fui-skill/references/ui-screenshot-review.md +94 -0
- package/skills/fui-skill/references/ui-table-cell-patterns.md +316 -0
- package/skills/fui-skill/references/ui-templates.md +29 -0
- package/skills/fui-skill/references/verification.md +246 -0
- package/skills/fui-skill/references/watcher-patterns.md +196 -0
- package/skills/fui-skill/references/websocket-realtime.md +254 -0
- package/skills/fui-skill/scripts/component-3.0.js +2549 -0
- package/skills/fui-skill/scripts/component.js +3142 -0
- package/skills/fui-skill/scripts/componentTable-3.0.js +909 -0
- package/skills/fui-skill/scripts/componentTable.js +769 -0
- package/skills/fui-skill/scripts/defaultfunction-3.0.js +781 -0
- package/skills/fui-skill/scripts/defaultfunction.js +966 -0
- package/skills/fui-skill/scripts/fastproject-3.0.js +870 -0
- package/skills/fui-skill/scripts/fastproject.js +828 -0
- package/skills/fui-skill/scripts/fechart.js +890 -0
- package/skills/fui-skill/scripts/fsheet.js +1330 -0
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
# FUI Cross-window Patterns — openWindow & data exchange
|
|
2
|
+
|
|
3
|
+
> File này sở hữu: **`openWindow`, trao đổi dữ liệu giữa cửa sổ (`#PARENT`/`#Win`), `set.menu` cho module con, `onclose`**.
|
|
4
|
+
|
|
5
|
+
Mở module như sub-window và trao đổi dữ liệu giữa các window/iframe (`#PARENT`, `#WinID`). Rút ra từ project **security-manager** và các ví dụ `ex-v3-*`.
|
|
6
|
+
|
|
7
|
+
> Xem thêm: [ui-patterns.md](ui-patterns.md), [default-function.md](default-function.md) (`openWindow`, `windowSendMessage`).
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 1. Mở Sub-window (openWindow) — Đầy đủ
|
|
12
|
+
|
|
13
|
+
Mở module khác như popup window riêng biệt. `id` là định danh để tham chiếu sau này.
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"FUN": "openWindow",
|
|
18
|
+
"IN": {
|
|
19
|
+
"id": "`Win1",
|
|
20
|
+
"title": "`User Permission",
|
|
21
|
+
"url": "`/UserPermission",
|
|
22
|
+
"data": "danhSachChoTable",
|
|
23
|
+
"width": "`800px",
|
|
24
|
+
"height": "`600px",
|
|
25
|
+
"onclose": {
|
|
26
|
+
"CALL": "reloadData"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
| Param | Bắt buộc | Mô tả |
|
|
33
|
+
|---|---|---|
|
|
34
|
+
| `id` | ✅ | Định danh window (backtick = literal) — dùng để ref qua `#Win1` |
|
|
35
|
+
| `url` | ✅ | URL module — không truyền `?menu=0`; module con tự khai báo `"set": { "menu": false }` |
|
|
36
|
+
| `title` | ❌ | Tiêu đề dialog |
|
|
37
|
+
| `data` | ❌ | Truyền dữ liệu vào window con — window con đọc qua `#PARENT.data` |
|
|
38
|
+
| `width` / `height` | ❌ | Kích thước window (`"800px"`, `"50%"`) |
|
|
39
|
+
| `onclose` | ❌ | Action chạy sau khi sub-window đóng |
|
|
40
|
+
|
|
41
|
+
> **Module con mở qua openWindow:** Không truyền `?menu=0` trong URL. Module con tự khai báo `"set": { "menu": false }` trong module.json để ẩn menu vĩnh viễn cho module đó — bất kể cách mở. URL chỉ truyền ID cần thiết để query DB.
|
|
42
|
+
|
|
43
|
+
**`onclose` targeting window khác:**
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
"onclose": {
|
|
47
|
+
"#Win1": { "CALL": "cmdThongBao" }
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
> Khi Win2 đóng → chạy `cmdThongBao` trong Win1 (không phải trong window hiện tại).
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 2. Cross-window Data Exchange — Đầy đủ
|
|
56
|
+
|
|
57
|
+
FUI dùng `#PARENT` và `#WinID` để trao đổi dữ liệu giữa các window (iframe). Hoạt động cả same-domain và cross-domain (qua `postMessage`).
|
|
58
|
+
|
|
59
|
+
### Đọc dữ liệu từ window khác (value position)
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
"data": [
|
|
63
|
+
{
|
|
64
|
+
"sUserID": "#PARENT.sUserID",
|
|
65
|
+
"sFullName": "#PARENT.sFullName",
|
|
66
|
+
"dataFromWin1": "#Win1.apiChart"
|
|
67
|
+
}
|
|
68
|
+
]
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
| Cú pháp | Ý nghĩa |
|
|
72
|
+
|---|---|
|
|
73
|
+
| `"#PARENT.field"` | Đọc `vueData.field` từ window cha |
|
|
74
|
+
| `"#Win1.field"` | Đọc `vueData.field` từ iframe có id `Win1` |
|
|
75
|
+
|
|
76
|
+
### Ghi dữ liệu vào window khác (key position)
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
"data": [
|
|
80
|
+
{
|
|
81
|
+
"#PARENT.abcdef": 1234567890,
|
|
82
|
+
"#Win1.dulieuTuForm2": "`huynh cao tuan"
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
":action": {
|
|
89
|
+
"#PARENT.val4": "DauRaTuCauTruc_IN"
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
| Cú pháp | Ý nghĩa |
|
|
94
|
+
|---|---|
|
|
95
|
+
| `"#PARENT.field": value` | Ghi value vào `vueData.field` của parent |
|
|
96
|
+
| `"#Win1.field": value` | Ghi value vào `vueData.field` của Win1 |
|
|
97
|
+
|
|
98
|
+
### Gọi action trong window khác
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
":action": {
|
|
102
|
+
"#PARENT": { "CALL": "cmdSETData" }
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
```json
|
|
107
|
+
"#Win1": { "CALL": "cmdThongBao" }
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
- `"#PARENT"` (không có field) với value là action → `runAction(value)` trong parent
|
|
111
|
+
- `"#Win1"` với value là action → `runAction(value)` trong Win1
|
|
112
|
+
|
|
113
|
+
### Nhận dữ liệu được truyền khi openWindow
|
|
114
|
+
|
|
115
|
+
Khi window cha mở con với `"data": "sourceData"`:
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
"data": [
|
|
119
|
+
{
|
|
120
|
+
"LayDuLieuWinCha": "#PARENT.Win1"
|
|
121
|
+
}
|
|
122
|
+
]
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
> `#PARENT.Win1` đọc biến `Win1` từ parent. Khi cha gọi `openWindow` với `id: "Win1"`, FUI tự set `vueData.Win1 = data_đã_truyền` trong parent trước khi mở window.
|
|
126
|
+
|
|
127
|
+
### Pattern đầy đủ — Sub-window nhận data từ parent
|
|
128
|
+
|
|
129
|
+
**Module cha (ex-v3-1SetData):**
|
|
130
|
+
```json
|
|
131
|
+
":action": {
|
|
132
|
+
"FUN": "openWindow",
|
|
133
|
+
"IN": {
|
|
134
|
+
"id": "`Win1",
|
|
135
|
+
"url": "`/ex/ex-v3-2CauTruc",
|
|
136
|
+
"data": "danhSachChoTable"
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**Module con (ex-v3-2CauTruc):**
|
|
142
|
+
```json
|
|
143
|
+
"data": [
|
|
144
|
+
{
|
|
145
|
+
"IF": "typeof menu != 'undefined' && menu=='0'",
|
|
146
|
+
"THEN": { "v_Set.menu": false }
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"LayDuLieuWinCha": "#PARENT.Win1",
|
|
150
|
+
"GiaTriTuCHA.thu.xem": "#PARENT.val7",
|
|
151
|
+
"GiaTriSetTuCHA": null
|
|
152
|
+
}
|
|
153
|
+
]
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
> Đặt `#PARENT.*` TRƯỚC các CALL startup để data sẵn sàng trước khi API đầu tiên chạy.
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## 3. Sub-window Checklist
|
|
161
|
+
|
|
162
|
+
Module được mở qua `openWindow` cần:
|
|
163
|
+
|
|
164
|
+
- `"set": { "menu": false }` — ẩn navigation header (khai báo trong module.json, không dùng `?menu=0` trong URL)
|
|
165
|
+
- URL params tự động inject vào `vueData` trước khi `data[]` chạy — `{ "CALL": "apiLoad" }` ở cuối `data[]` là đủ
|
|
166
|
+
- URL chỉ truyền **ID** của record cha mà module con thực sự dùng — không truyền name hay field có thể tra lại từ DB
|
|
167
|
+
|
|
168
|
+
```json
|
|
169
|
+
// module.json của module con
|
|
170
|
+
"set": { "menu": false },
|
|
171
|
+
"data": [
|
|
172
|
+
{ "ContestID": null },
|
|
173
|
+
{
|
|
174
|
+
"getDetail": {
|
|
175
|
+
"API": "/SM_Contest_Detail",
|
|
176
|
+
"IN": { "ContestID": "ContestID" },
|
|
177
|
+
"OUT": "dsDetail"
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
{ "CALL": "getDetail" }
|
|
181
|
+
]
|
|
182
|
+
// ContestID tự động inject từ URL ?ContestID=xxx trước khi data[] chạy
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## 4. Ví dụ hoàn chỉnh cha ↔ con (chuẩn `ex-v3-1SetData` → `ex-v3-2CauTruc`)
|
|
188
|
+
|
|
189
|
+
Đây là mẫu chuẩn: **cửa sổ cha mở con qua `openWindow`, truyền dữ liệu bảng, con đọc/ghi/gọi lệnh ngược lên cha.**
|
|
190
|
+
|
|
191
|
+
### 4.1 Cửa sổ CHA — f-table + nút mở con, truyền `data`
|
|
192
|
+
|
|
193
|
+
`data` trong `openWindow.IN` truyền thẳng object/array (ở đây là danh sách bảng `danhSachChoTable`) sang con; con đọc qua `#PARENT.<id>`.
|
|
194
|
+
|
|
195
|
+
```json
|
|
196
|
+
{
|
|
197
|
+
"el": "f-table",
|
|
198
|
+
"attr": {
|
|
199
|
+
"label": "Danh sách",
|
|
200
|
+
":items": "danhSachChoTable",
|
|
201
|
+
":headers": [
|
|
202
|
+
{ "text": "STT", "value": "STT" },
|
|
203
|
+
{ "text": "Họ", "value": "ho" },
|
|
204
|
+
{ "text": "Tên", "value": "ten" }
|
|
205
|
+
]
|
|
206
|
+
},
|
|
207
|
+
"w": "6"
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
"el": "f-button",
|
|
211
|
+
"attr": {
|
|
212
|
+
"label": "Open Window",
|
|
213
|
+
":action": {
|
|
214
|
+
"FUN": "openWindow",
|
|
215
|
+
"IN": {
|
|
216
|
+
"id": "`Win1",
|
|
217
|
+
"title": "`Chỉnh sửa cho module",
|
|
218
|
+
"url": "`/ex/ex-v3-2CauTruc",
|
|
219
|
+
"data": "danhSachChoTable"
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
"w": "3"
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Cha cũng có thể khai báo **action đặt tên** để con gọi ngược lên, và ghi thẳng vào con trong `data[]`:
|
|
228
|
+
|
|
229
|
+
```json
|
|
230
|
+
"data": [
|
|
231
|
+
{ "#PARENT.abcdef": 1234567890, "#Win1": { "CALL": "cmdTHONGBao" } },
|
|
232
|
+
{
|
|
233
|
+
"cmdSETData": [
|
|
234
|
+
{ "MESS": "Lệnh của form CHA được gọi từ form CON" },
|
|
235
|
+
{ "#Win1.GiaTriSetTuCHA": "Giá trị set từ CHA" }
|
|
236
|
+
]
|
|
237
|
+
}
|
|
238
|
+
]
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### 4.2 Cửa sổ CON — đọc data cha, ghi ngược, gọi lệnh cha, mở thêm con
|
|
242
|
+
|
|
243
|
+
```json
|
|
244
|
+
// data[] của con: đọc dữ liệu cha NGAY đầu tiên
|
|
245
|
+
"data": [
|
|
246
|
+
{
|
|
247
|
+
"LayDuLieuWinCha": "#PARENT.Win1", // = data cha đã truyền qua openWindow (id=Win1)
|
|
248
|
+
"GiaTriTuCHA.thu.xem": "#PARENT.val7",
|
|
249
|
+
"GiaTriSetTuCHA": null
|
|
250
|
+
}
|
|
251
|
+
],
|
|
252
|
+
"set": { "menu": false, "login": false }
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
```json
|
|
256
|
+
// Các nút trong controls của con:
|
|
257
|
+
{ "el": "f-button", "attr": { "label": "Lấy dữ liệu từ cha",
|
|
258
|
+
":action": { "USERINFO": "#PARENT.val4" } } },
|
|
259
|
+
|
|
260
|
+
{ "el": "f-button", "attr": { "label": "Set dữ liệu xuống cha",
|
|
261
|
+
":action": { "#PARENT.val4": "DauRaTuCauTruc_IN" } } },
|
|
262
|
+
|
|
263
|
+
{ "el": "f-button", "attr": { "label": "Gọi lệnh ở cha",
|
|
264
|
+
":action": { "#PARENT": { "CALL": "cmdSETData" } } } },
|
|
265
|
+
|
|
266
|
+
{ "el": "f-button", "attr": { "label": "Mở thêm form con",
|
|
267
|
+
":action": { "FUN": "openWindow", "IN": {
|
|
268
|
+
"id": "`Win2",
|
|
269
|
+
"url": "`https://fui.vn/ex/ex-v3-1SetData",
|
|
270
|
+
"onclose": { "#Win1": { "CALL": "cmdThongBaoCuaFormNay" } }
|
|
271
|
+
} } } }
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
**Tổng hợp cú pháp trao đổi (value = đọc, key = ghi):**
|
|
275
|
+
|
|
276
|
+
| Cú pháp | Ý nghĩa |
|
|
277
|
+
|---|---|
|
|
278
|
+
| `"x": "#PARENT.field"` | Đọc `field` của cha vào `x` |
|
|
279
|
+
| `"x": "#Win1.field"` | Đọc `field` của cửa sổ id `Win1` |
|
|
280
|
+
| `"#PARENT.field": "src"` | Ghi `src` vào `field` của cha |
|
|
281
|
+
| `"#Win1.field": "src"` | Ghi vào cửa sổ `Win1` |
|
|
282
|
+
| `"#PARENT": { "CALL": "cmd" }` | Gọi action `cmd` trong cha |
|
|
283
|
+
| `"#Win1": { "CALL": "cmd" }` | Gọi action trong `Win1` |
|
|
284
|
+
| `openWindow.IN.data` | Truyền object/array sang con; con đọc `#PARENT.<id>` |
|
|
285
|
+
| `openWindow.IN.onclose` | Action chạy sau khi con đóng (có thể nhắm `#WinX`) |
|
|
286
|
+
|
|
287
|
+
> Hoạt động cả **same-domain và cross-domain** (runtime tự dùng `postMessage` khi khác tên miền).
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## 5. f-table / f-sheet + mở cửa sổ chi tiết
|
|
292
|
+
|
|
293
|
+
Pattern rất phổ biến: bảng danh sách → click một dòng → mở cửa sổ con để xem/sửa chi tiết dòng đó.
|
|
294
|
+
|
|
295
|
+
- Trong `headers` của `f-table`, dùng `t-button` hoặc `t-menu` với `:action` gọi `openWindow`; `item` là dòng hiện tại.
|
|
296
|
+
- Truyền **chỉ ID** của dòng qua URL (con tự query DB), hoặc truyền cả object dòng qua `openWindow.IN.data`.
|
|
297
|
+
|
|
298
|
+
```json
|
|
299
|
+
{
|
|
300
|
+
"el": "t-button", "text": "Chi tiết", "value": "ID", "align": "center",
|
|
301
|
+
"attr": {
|
|
302
|
+
"icon-text": "mdi-eye", "color": "primary",
|
|
303
|
+
":action": {
|
|
304
|
+
"FUN": "openWindow",
|
|
305
|
+
"IN": {
|
|
306
|
+
"id": "`WinDetail",
|
|
307
|
+
"title": "`Chi tiết",
|
|
308
|
+
"url": "`/DetailModule?RecordID={{item.ID}}",
|
|
309
|
+
"onclose": { "CALL": "reloadList" }
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
- **`f-sheet`** (bảng tính nhập lưới) kết hợp tương tự: nút/menu trong sheet gọi `openWindow`, hoặc lưu cả `vueData` của sheet rồi mở con để nhập bổ sung; con ghi ngược qua `#PARENT.<field>` khi đóng.
|
|
317
|
+
- Reload sau khi con đóng: đặt `onclose: { "CALL": "reloadList" }` (chạy trong cửa sổ mở nó) — xem §1.
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# FUI Dialog Patterns — v-dialog thủ công (CHUẨN), hidden-container, checkbox-list
|
|
2
|
+
|
|
3
|
+
> File này sở hữu: **dialog thủ công: `hidden-container`, anatomy `v-card`/`v-toolbar`, mở dialog kèm row, checkbox-list**. `f-dialog` xem [components-dialog.md](components-dialog.md).
|
|
4
|
+
|
|
5
|
+
Các pattern dialog/form popup trong FUI. Rút ra từ **security-manager** và demo **ex-v3-dialog**.
|
|
6
|
+
|
|
7
|
+
> **CHUẨN thiết kế dialog: dựng `v-dialog` thủ công theo anatomy trong mục 1** (dễ đọc/chỉnh sửa từng phần), với **diện mạo mô phỏng `f-dialog`**: toolbar `dense flat` không màu + close icon, form floating label, hàng cuối là `v-card-actions`. Hàng action căn phải, button giữ chiều ngang tự nhiên; chỉ action chính mới được tô màu nền, action phụ dùng nút text. Anatomy đầy đủ + bảng quy tắc: xem `design_read("fui")` §4–5. `f-dialog` (mục 0) là lựa chọn phụ cho form rất nhỏ.
|
|
8
|
+
|
|
9
|
+
> Xem thêm: [ui-patterns.md](ui-patterns.md), [ui-table-cell-patterns.md](ui-table-cell-patterns.md) (menuChucNang mở dialog), [components-dialog.md](components-dialog.md).
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 0. `f-dialog` — component dialog gọn (lựa chọn phụ)
|
|
14
|
+
|
|
15
|
+
`f-dialog` gói sẵn card + toolbar + nút — ngắn hơn so với dựng `v-dialog` thủ công, nhưng khó tùy biến layout. Chỉ dùng cho form rất nhỏ/đơn giản; mặc định vẫn là `v-dialog` thủ công (mục 1). Các field bên trong dùng `v-model` **trỏ thẳng tới key của object `data`** (không cần `vueData.dialogData.X`).
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"el": "f-dialog",
|
|
20
|
+
"attr": {
|
|
21
|
+
"v-model": "dialogOpen", // boolean mở/đóng
|
|
22
|
+
"v-model:data": "dialogData", // object dữ liệu của form
|
|
23
|
+
"title": "Nhập dữ liệu",
|
|
24
|
+
":open-action": { "MESSBOX": "Bạn vừa mở dialog" },
|
|
25
|
+
":controls": [
|
|
26
|
+
{ "el": "v-text-field", "attr": { "v-model": "Domain", "label": "Domain" }, "w": 4 },
|
|
27
|
+
{ "el": "v-text-field", "attr": { "v-model": "ModuleID", "label": "ModuleID" }, "w": 3 },
|
|
28
|
+
{ "el": "v-checkbox", "attr": { "v-model": "checkBoxValue",
|
|
29
|
+
":label": "`Tôi đồng ý: ${checkBoxValue}`" } },
|
|
30
|
+
{ "el": "v-select", "attr": { "label": "Chọn", "v-model": "bienselect",
|
|
31
|
+
":items": "vueData.Mang", "item-title": "AppName", "item-value": "AppListID" } }
|
|
32
|
+
],
|
|
33
|
+
":watch": {
|
|
34
|
+
"bienselect": { "MESSBOX": "Watch {{bienselect}}" }
|
|
35
|
+
},
|
|
36
|
+
":button": [
|
|
37
|
+
{ "label": "Lưu", "type": "submit", ":disabled": "!checkBoxValue",
|
|
38
|
+
"action": [ { "MESSBOX": "Đã lưu" }, { "dialogOpen": false } ] },
|
|
39
|
+
{ "label": "Chạy API", "checkvalid": true, "v-if": "ModuleID==1",
|
|
40
|
+
"action": [ { "API": "/chaymotapi" } ] },
|
|
41
|
+
{ "label": "Đóng", "action": [ { "dialogOpen": false } ] }
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
| Prop | Vai trò |
|
|
48
|
+
|---|---|
|
|
49
|
+
| `v-model` | Biến boolean mở/đóng dialog |
|
|
50
|
+
| `v-model:data` | Object chứa dữ liệu form; field trong `:controls` bind trực tiếp tới key của nó |
|
|
51
|
+
| `title` | Tiêu đề dialog |
|
|
52
|
+
| `:open-action` | Action chạy khi dialog mở |
|
|
53
|
+
| `:controls` | Mảng Control Object = các field của form |
|
|
54
|
+
| `:watch` | Watcher phạm vi trong dialog (theo tên field) |
|
|
55
|
+
| `:button` | Mảng nút: `label`, `type:"submit"`, `checkvalid`, `:disabled`, `v-if`, `action` |
|
|
56
|
+
|
|
57
|
+
**Khi nào dùng gì:** mặc định mọi dialog → **`v-dialog` thủ công theo mục 1** (chuẩn project). `f-dialog` chỉ khi form rất nhỏ (1–3 field, không layout đặc biệt) và user không yêu cầu tùy biến.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 1. Dialog Pattern — v-dialog thủ công trong hidden-container (CHUẨN)
|
|
62
|
+
|
|
63
|
+
Dialogs luôn đặt trong container riêng với `prop: "hidden-container"` — không nằm trong rows chính.
|
|
64
|
+
|
|
65
|
+
**Cấu trúc chuẩn:**
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"prop": "hidden-container",
|
|
70
|
+
"rows": [
|
|
71
|
+
{
|
|
72
|
+
"prop": "",
|
|
73
|
+
"cols": [
|
|
74
|
+
{
|
|
75
|
+
"el": "v-dialog",
|
|
76
|
+
"attr": {
|
|
77
|
+
"v-model": "dlUserInfo",
|
|
78
|
+
"width": 650
|
|
79
|
+
},
|
|
80
|
+
"innerHTML": [
|
|
81
|
+
{
|
|
82
|
+
"el": "v-card",
|
|
83
|
+
"attr": { "class": "pa-0" },
|
|
84
|
+
"innerHTML": [
|
|
85
|
+
{
|
|
86
|
+
"el": "v-toolbar",
|
|
87
|
+
"attr": { "dense": "", "flat": "" },
|
|
88
|
+
"innerHTML": [
|
|
89
|
+
{ "el": "v-toolbar-title", "attr": {}, "innerHTML": "Update User Info" },
|
|
90
|
+
{ "el": "v-spacer", "attr": {} },
|
|
91
|
+
{
|
|
92
|
+
"el": "v-btn",
|
|
93
|
+
"innerHTML": "<v-icon>mdi-close</v-icon>",
|
|
94
|
+
"attr": { "icon": "", "v-on:click": "vueData.dlUserInfo = false" }
|
|
95
|
+
}
|
|
96
|
+
]
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"el": "v-container",
|
|
100
|
+
"attr": { "class": "grid-list-md px-4" },
|
|
101
|
+
"innerHTML": [
|
|
102
|
+
{ "el": "v-layout", "w": 12, "attr": { "row": "", "wrap": "" }, "innerHTML": [
|
|
103
|
+
{ "el": "v-text-field", "col": {}, "attr": { "v-model": "vueData.sUserName", "label": "Tên đăng nhập" }, "w": 6 },
|
|
104
|
+
{ "el": "v-text-field", "col": {}, "attr": { "v-model": "vueData.sLastName", "label": "Họ tên" }, "w": 6 }
|
|
105
|
+
]}
|
|
106
|
+
]
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"el": "v-card-actions",
|
|
110
|
+
"attr": { "class": "mr-1 mb-1" },
|
|
111
|
+
"innerHTML": [
|
|
112
|
+
{ "el": "v-spacer", "attr": {} },
|
|
113
|
+
{
|
|
114
|
+
"el": "f-button",
|
|
115
|
+
"attr": {
|
|
116
|
+
"text": "",
|
|
117
|
+
"label": "Đặt lại",
|
|
118
|
+
":action": { "CALL": "resetForm" }
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"el": "f-button",
|
|
123
|
+
"attr": {
|
|
124
|
+
"color": "primary",
|
|
125
|
+
":checkvalid": true,
|
|
126
|
+
"label": "Lưu",
|
|
127
|
+
"icon-text": "mdi-content-save",
|
|
128
|
+
":action": {
|
|
129
|
+
"API": "SM_Users_Update",
|
|
130
|
+
"IN": { "UserID": "sUserID", "UserName": "sUserName" },
|
|
131
|
+
"CALLBACK": [
|
|
132
|
+
{ "MESS": "Cập nhật thành công." },
|
|
133
|
+
{ "dlUserInfo": false },
|
|
134
|
+
{ "CALL": "getUser" }
|
|
135
|
+
]
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
]
|
|
140
|
+
}
|
|
141
|
+
]
|
|
142
|
+
}
|
|
143
|
+
]
|
|
144
|
+
}
|
|
145
|
+
]
|
|
146
|
+
}
|
|
147
|
+
]
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**Quy tắc dialog (phong cách f-dialog):**
|
|
152
|
+
- `v-card[class="pa-0"]` — triệt đệm để toolbar tràn sát mép (ưu tiên class Vuetify, không inline style); không elevation/outlined
|
|
153
|
+
- `v-toolbar[dense flat]` — thanh tiêu đề mỏng, **không** color/dark
|
|
154
|
+
- Nút đóng: `v-btn[icon]` mdi-close + `v-on:click: "vueData.dlXxx = false"` — không thêm nút "Đóng" ở actions
|
|
155
|
+
- Thân form: `v-container.grid-list-md.px-4` > `v-layout[row wrap]` > field **floating label** (`label` trong attr, w:12 dài / 6+6 ngắn) — KHÔNG dùng label div rời
|
|
156
|
+
- Hàng cuối chứa nút phải là `v-card-actions[class="mr-1 mb-1"]`, không đặt nhóm action trong một `v-layout`, `v-container` hay row form thông thường
|
|
157
|
+
- `v-spacer` đứng trước nhóm nút → toàn bộ action căn phải
|
|
158
|
+
- Button giữ chiều ngang tự nhiên theo nội dung: không đặt `w`, `width`, `block`, `flex-grow` hoặc bọc mỗi nút trong cột giãn
|
|
159
|
+
- Tối đa một action quan trọng nhất (ví dụ **Lưu**) được dùng nền `color="primary"`; action phụ/ít quan trọng dùng `f-button` + `"text": ""`, không tô nền. Giữ số nút ít, nhãn ngắn và trình bày nhã nhặn
|
|
160
|
+
- CALLBACK của Save: MESS → đóng dialog → reload data
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## 2. Mở Dialog với Dữ Liệu Row
|
|
165
|
+
|
|
166
|
+
Pattern 2 bước: copy dữ liệu vào state → set `dlXxx = true`.
|
|
167
|
+
|
|
168
|
+
**Từ menuChucNang:**
|
|
169
|
+
```json
|
|
170
|
+
"action": [
|
|
171
|
+
{
|
|
172
|
+
"addNew": 0,
|
|
173
|
+
"dongChon": "item",
|
|
174
|
+
"sUserID": "item.UserID",
|
|
175
|
+
"sUserName": "item.UserName",
|
|
176
|
+
"sLastName": "item.LastName"
|
|
177
|
+
},
|
|
178
|
+
{ "dlUserInfo": true }
|
|
179
|
+
]
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
**Từ f-button:**
|
|
183
|
+
```json
|
|
184
|
+
":action": [
|
|
185
|
+
{ "addNew": 1, "sUserID": "", "sUserName": "" },
|
|
186
|
+
{ "dlUserInfo": true }
|
|
187
|
+
]
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
> `addNew: 0` = edit mode, `addNew: 1` = new mode. Trong dialog, dùng `:disabled="addNew==0"` để khóa field ID khi edit.
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## 3. Checkbox List Dialog Pattern
|
|
195
|
+
|
|
196
|
+
Dialog chứa danh sách checkbox để chọn nhiều items, lưu ngay khi click.
|
|
197
|
+
|
|
198
|
+
```json
|
|
199
|
+
{
|
|
200
|
+
"el": "v-card-text",
|
|
201
|
+
"attr": {},
|
|
202
|
+
"innerHTML": [
|
|
203
|
+
{
|
|
204
|
+
"el": "v-layout",
|
|
205
|
+
"attr": { "class": "fluid row justify-center grid-list-lg fill-height mt-3" },
|
|
206
|
+
"innerHTML": [
|
|
207
|
+
{
|
|
208
|
+
"el": "div",
|
|
209
|
+
"attr": { "v-for": "fc in vueData.dsFunctionRight", ":key": "fc.FunctionID" },
|
|
210
|
+
"innerHTML": [
|
|
211
|
+
{
|
|
212
|
+
"el": "v-checkbox",
|
|
213
|
+
"attr": {
|
|
214
|
+
"color": "primary",
|
|
215
|
+
"v-model": "sFunctionSelected",
|
|
216
|
+
":value": "`${fc.FunctionID}`",
|
|
217
|
+
":label": "`${fc.FunctionName}`",
|
|
218
|
+
"v-on:click": "CALL(saveFunction)"
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
]
|
|
222
|
+
}
|
|
223
|
+
]
|
|
224
|
+
}
|
|
225
|
+
]
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
> `v-on:click` trên checkbox → save ngay không cần nút Save. `v-model` nhận array, `:value` dùng backtick literal.
|