@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,415 @@
|
|
|
1
|
+
# Default Functions
|
|
2
|
+
|
|
3
|
+
> File này sở hữu: **hàm tiện ích FUI dùng được trong `FUN`/`EXE`/`script.js`** (openWindow, ajaxCALL, jsonToExcel, groupBy...). Vị trí trong source xem [script-map.md](script-map.md).
|
|
4
|
+
|
|
5
|
+
Các hàm và biến global được định nghĩa trong `defaultfunction.js` — tự động load trong mọi module FUI V2.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Global Variables
|
|
10
|
+
|
|
11
|
+
| Variable | Mô tả |
|
|
12
|
+
|---|---|
|
|
13
|
+
| `$isMobile` | `true` nếu user agent là thiết bị mobile |
|
|
14
|
+
| `$isPhone` | `true` nếu là điện thoại (không tính tablet) |
|
|
15
|
+
| `$isApple` | `true` nếu là thiết bị Apple (iPad, iPhone, iPod) |
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Dialog & Thông báo
|
|
20
|
+
|
|
21
|
+
### confirm(obj)
|
|
22
|
+
|
|
23
|
+
Hiển thị hộp thoại xác nhận OK/Cancel có style. Wrapper của `$.confirm` (jquery-confirm).
|
|
24
|
+
|
|
25
|
+
| Param | Mô tả |
|
|
26
|
+
|---|---|
|
|
27
|
+
| `title` | Tiêu đề dialog |
|
|
28
|
+
| `message` | Nội dung |
|
|
29
|
+
| `action` | Function chạy khi nhấn OK |
|
|
30
|
+
| `cancel` | Function chạy khi nhấn Cancel (tùy chọn) |
|
|
31
|
+
| `type` | Màu: `'blue'` (mặc định), `'red'`, `'green'`, `'orange'` |
|
|
32
|
+
| `icon` | Icon class, ví dụ `'mdi mdi-delete'` |
|
|
33
|
+
|
|
34
|
+
```js
|
|
35
|
+
confirm({
|
|
36
|
+
title: 'Xác nhận xóa',
|
|
37
|
+
message: 'Bạn có chắc muốn xóa bản ghi này không?',
|
|
38
|
+
type: 'red',
|
|
39
|
+
icon: 'mdi mdi-delete',
|
|
40
|
+
action: function() {
|
|
41
|
+
CALL(vueData.actDelete);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
### showMessage(obj)
|
|
49
|
+
|
|
50
|
+
Hiển thị hộp thoại thông báo (chỉ có nút Close). Wrapper của `$.dialog`.
|
|
51
|
+
|
|
52
|
+
| Param | Mô tả |
|
|
53
|
+
|---|---|
|
|
54
|
+
| `title` | Tiêu đề |
|
|
55
|
+
| `message` | Nội dung |
|
|
56
|
+
| `type` | Màu: `'blue'`, `'red'`, `'green'` |
|
|
57
|
+
| `icon` | Icon class |
|
|
58
|
+
| `onclose` | FUI action object chạy sau khi đóng dialog |
|
|
59
|
+
|
|
60
|
+
```js
|
|
61
|
+
showMessage({ title: 'Thông báo', message: 'Lưu thành công!' });
|
|
62
|
+
|
|
63
|
+
showMessage({
|
|
64
|
+
title: 'Lỗi',
|
|
65
|
+
message: 'Không tìm thấy dữ liệu',
|
|
66
|
+
type: 'red',
|
|
67
|
+
icon: 'mdi mdi-alert-circle'
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Chạy action sau khi đóng
|
|
71
|
+
showMessage({
|
|
72
|
+
title: 'Hoàn tất',
|
|
73
|
+
message: 'Import xong!',
|
|
74
|
+
onclose: vueData.actReload
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Navigation & Window
|
|
81
|
+
|
|
82
|
+
### pushRouter(router)
|
|
83
|
+
|
|
84
|
+
Cập nhật URL trên thanh địa chỉ trình duyệt (không reload trang). Dùng để đồng bộ URL với trạng thái UI hiện tại.
|
|
85
|
+
|
|
86
|
+
```js
|
|
87
|
+
pushRouter('/fp/don-hang/danh-sach');
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
### openWindow(obj)
|
|
93
|
+
|
|
94
|
+
Mở một cửa sổ FUI (`f-window`) overlay trên trang hiện tại.
|
|
95
|
+
|
|
96
|
+
| Param | Mô tả |
|
|
97
|
+
|---|---|
|
|
98
|
+
| `url` | URL nội dung cửa sổ (bắt buộc) |
|
|
99
|
+
| `title` | Tiêu đề cửa sổ |
|
|
100
|
+
| `id` | ID định danh (tự sinh nếu bỏ qua) — dùng để tránh mở trùng |
|
|
101
|
+
| `width` | Chiều rộng (px hoặc %) |
|
|
102
|
+
| `height` | Chiều cao |
|
|
103
|
+
|
|
104
|
+
```js
|
|
105
|
+
// Cách đơn giản — chỉ truyền URL
|
|
106
|
+
openWindow('/fp/san-pham/chi-tiet?id=123');
|
|
107
|
+
|
|
108
|
+
// Đầy đủ options
|
|
109
|
+
openWindow({
|
|
110
|
+
id: 'winChiTiet',
|
|
111
|
+
url: '/fp/san-pham/chi-tiet?id=' + vueData.selectedId,
|
|
112
|
+
title: 'Chi tiết sản phẩm',
|
|
113
|
+
width: '80%'
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
> Nếu gọi từ bên trong một `f-window` (iframe), FUI tự động chuyển sang `windowSendMessage('#PARENT', 'openWindow', obj)` để mở ở cửa sổ cha.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
### redirect(obj, query)
|
|
122
|
+
|
|
123
|
+
Chuyển hướng trang hiện tại sang URL khác.
|
|
124
|
+
|
|
125
|
+
```js
|
|
126
|
+
redirect('/fp/trang-chu');
|
|
127
|
+
redirect({ url: '/fp/don-hang', query: true }); // giữ lại query params hiện tại
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
### reload()
|
|
133
|
+
|
|
134
|
+
Tải lại trang hiện tại.
|
|
135
|
+
|
|
136
|
+
```js
|
|
137
|
+
reload();
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
### windowSendMessage(window, cmd, data)
|
|
143
|
+
|
|
144
|
+
Gửi `postMessage` đến một cửa sổ/iframe khác. Dùng để giao tiếp giữa các `f-window`.
|
|
145
|
+
|
|
146
|
+
| Param | Mô tả |
|
|
147
|
+
|---|---|
|
|
148
|
+
| `window` | Object window, `'#PARENT'` (cửa sổ cha), hoặc `'#iframeId'` (ID của iframe) |
|
|
149
|
+
| `cmd` | Tên lệnh |
|
|
150
|
+
| `data` | Dữ liệu gửi kèm |
|
|
151
|
+
|
|
152
|
+
```js
|
|
153
|
+
// Gửi lên cửa sổ cha
|
|
154
|
+
windowSendMessage('#PARENT', 'refreshData', { id: vueData.selectedId });
|
|
155
|
+
|
|
156
|
+
// Gửi đến iframe có id="iframeChild"
|
|
157
|
+
windowSendMessage('#iframeChild', 'loadItem', { itemId: 5 });
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## AJAX
|
|
163
|
+
|
|
164
|
+
### ajaxCALL(URL, DATA, callBack, errorCallBack, header)
|
|
165
|
+
|
|
166
|
+
Gọi API bằng AJAX POST. URL chỉ cần truyền path — hệ thống tự ghép domain qua `fixURL`.
|
|
167
|
+
|
|
168
|
+
```js
|
|
169
|
+
ajaxCALL('/SP_GetDonHang', { maKH: vueData.maKH }, function(res) {
|
|
170
|
+
vueData.danhSach = res.data;
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// Với error handler
|
|
174
|
+
ajaxCALL('/SP_SaveDonHang', vueData.form,
|
|
175
|
+
function(res) { Vue.$toast.success('Lưu thành công', { position: 'top' }); },
|
|
176
|
+
function(err) { showMessage({ title: 'Lỗi', message: err.message, type: 'red' }); }
|
|
177
|
+
);
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Xuất file
|
|
183
|
+
|
|
184
|
+
### jsonToExcel(Obj)
|
|
185
|
+
|
|
186
|
+
Xuất dữ liệu JSON ra file `.xlsx`. Tự động load ExcelJS nếu chưa có.
|
|
187
|
+
|
|
188
|
+
**Single sheet:**
|
|
189
|
+
```js
|
|
190
|
+
jsonToExcel({
|
|
191
|
+
data: vueData.danhSach, // Array of objects
|
|
192
|
+
filename: 'danh-sach.xlsx',
|
|
193
|
+
worksheet: 'DanhSach'
|
|
194
|
+
});
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**Multi sheet:**
|
|
198
|
+
```js
|
|
199
|
+
jsonToExcel({
|
|
200
|
+
filename: 'bao-cao.xlsx',
|
|
201
|
+
sheets: [
|
|
202
|
+
{ name: 'Sheet1', data: vueData.list1 },
|
|
203
|
+
{ name: 'Sheet2', data: vueData.list2 }
|
|
204
|
+
]
|
|
205
|
+
});
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Data Utilities
|
|
211
|
+
|
|
212
|
+
### groupBy(arrayObj, groupField, sumArr)
|
|
213
|
+
|
|
214
|
+
Group mảng object theo một field và tính tổng các cột số. Trả về mảng đã group kèm `count`.
|
|
215
|
+
|
|
216
|
+
```js
|
|
217
|
+
// groupBy(data, 'Khoa', ['SLNghiHoc', 'SLTotNghiep'])
|
|
218
|
+
var result = groupBy(vueData.data, 'phongBan', ['soLuong', 'doanhThu']);
|
|
219
|
+
// result: [{ phongBan: 'A', soLuong: 10, doanhThu: 5000, count: 3 }, ...]
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
> Với nhu cầu group phức tạp hơn, dùng `_.groupBy` của Lodash trực tiếp.
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
### findInArray(obj)
|
|
227
|
+
|
|
228
|
+
Tìm phần tử trong mảng theo điều kiện.
|
|
229
|
+
|
|
230
|
+
```js
|
|
231
|
+
var item = findInArray({ array: vueData.list, value: { id: 5 } });
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
> Thường dùng `_.find(array, condition)` của Lodash thay thế vì ngắn gọn hơn.
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
### fillData(obj)
|
|
239
|
+
|
|
240
|
+
Copy dữ liệu từ mảng nguồn vào object đích theo key và mapping.
|
|
241
|
+
|
|
242
|
+
- **Parameters:** `{ src, des, key, map }` — src: array nguồn, des: object đích, key: giá trị khớp, map: mapping field
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
### buildHeader(obj, mapCol)
|
|
247
|
+
|
|
248
|
+
Tự sinh mảng `headers` cho `f-table` từ dữ liệu. Không truyền `mapCol` → suy header từ key của bản ghi đầu tiên (cột số tự gán `el: 't-num'`); có `mapCol` → map từ `{ text, value }`.
|
|
249
|
+
|
|
250
|
+
```js
|
|
251
|
+
// Tự dựng header từ dữ liệu API
|
|
252
|
+
vueData.headers = buildHeader(vueData.dsData);
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### capacityText(numb)
|
|
256
|
+
|
|
257
|
+
Đổi số byte → chuỗi dung lượng (`'1.5 Mb'`, `'320 Kb'`) — dùng hiển thị kích thước file.
|
|
258
|
+
|
|
259
|
+
### appCommand(cmdData)
|
|
260
|
+
|
|
261
|
+
Gửi lệnh sang app native (React Native / iOS webkit / Android) khi trang FUI nhúng trong app di động.
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## PDF
|
|
266
|
+
|
|
267
|
+
### printPDF(obj)
|
|
268
|
+
|
|
269
|
+
Tạo và in/tải PDF dùng `pdfMake`. Tự động load pdfMake nếu chưa có.
|
|
270
|
+
|
|
271
|
+
```js
|
|
272
|
+
printPDF({
|
|
273
|
+
data: docDefinition, // pdfMake docDefinition object
|
|
274
|
+
download: true, // true = tải file, false = mở viewer
|
|
275
|
+
callBack: function() { console.log('Done'); }
|
|
276
|
+
});
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## Clipboard & String
|
|
282
|
+
|
|
283
|
+
### copyToClipboard(textToCopy)
|
|
284
|
+
|
|
285
|
+
Copy chuỗi vào clipboard. Tự động dùng `navigator.clipboard` nếu có (HTTPS), fallback về `execCommand`.
|
|
286
|
+
|
|
287
|
+
```js
|
|
288
|
+
copyToClipboard(vueData.maHoaDon);
|
|
289
|
+
copyToClipboard(JSON.stringify(vueData.data));
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
### fileNameClear(fname)
|
|
295
|
+
|
|
296
|
+
Xóa các ký tự đặc biệt khỏi tên file (chỉ giữ chữ, số, khoảng trắng, dấu gạch).
|
|
297
|
+
|
|
298
|
+
```js
|
|
299
|
+
var safeName = fileNameClear(vueData.tenFile); // "report (2025)!" → "report 2025"
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
### hashCode(s)
|
|
305
|
+
|
|
306
|
+
Tạo hash số nguyên từ chuỗi (dùng để generate key duy nhất).
|
|
307
|
+
|
|
308
|
+
```js
|
|
309
|
+
var key = hashCode('unique-string-' + Date.now());
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
### generateID()
|
|
315
|
+
|
|
316
|
+
Tạo ID ngẫu nhiên dạng chuỗi.
|
|
317
|
+
|
|
318
|
+
```js
|
|
319
|
+
var newId = 'win_' + generateID();
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## Cookie & Auth
|
|
325
|
+
|
|
326
|
+
### getCookie(cname) / setCookie(name, value, days, domain)
|
|
327
|
+
|
|
328
|
+
Đọc/ghi cookie trình duyệt.
|
|
329
|
+
|
|
330
|
+
```js
|
|
331
|
+
var token = getCookie('awt');
|
|
332
|
+
setCookie('theme', 'dark', 30); // hết hạn sau 30 ngày
|
|
333
|
+
setCookie('lang', 'vi', 365, '.fui.vn'); // share cookie toàn domain
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
### logout(cookiesName, domain, url)
|
|
339
|
+
|
|
340
|
+
Xóa cookie và chuyển hướng đến trang login.
|
|
341
|
+
|
|
342
|
+
```js
|
|
343
|
+
logout(); // xóa cookie 'awt', redirect về login mặc định
|
|
344
|
+
logout('myToken', '.fui.vn', '/login');
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
---
|
|
348
|
+
|
|
349
|
+
## Quyền hạn
|
|
350
|
+
|
|
351
|
+
### rightTest(rightObject)
|
|
352
|
+
|
|
353
|
+
Kiểm tra user hiện tại có quyền hạn yêu cầu không. So sánh với `vueData.user`.
|
|
354
|
+
|
|
355
|
+
```js
|
|
356
|
+
// Kiểm tra user có role 'admin' hoặc 'manager'
|
|
357
|
+
if (rightTest({ role: ['admin', 'manager'] })) {
|
|
358
|
+
vueData.showDelete = true;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Kiểm tra nhiều điều kiện cùng lúc (AND)
|
|
362
|
+
if (rightTest({ dept: 'IT', level: [1, 2] })) { ... }
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
## Color Utilities
|
|
368
|
+
|
|
369
|
+
### colorLib(color)
|
|
370
|
+
|
|
371
|
+
Utility xử lý màu sắc (hex, rgb, hsl). Dùng chủ yếu trong chart.
|
|
372
|
+
|
|
373
|
+
```js
|
|
374
|
+
var c = colorLib('#4e73df');
|
|
375
|
+
c.hexString() // '#4e73df'
|
|
376
|
+
c.rgbString() // 'rgb(78, 115, 223)'
|
|
377
|
+
c.lighten(0.2) // làm sáng 20%
|
|
378
|
+
c.darken(0.1) // làm tối 10%
|
|
379
|
+
c.alpha(0.5).rgbString() // 'rgba(78, 115, 223, 0.5)'
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
### transparentize(value, opacity)
|
|
383
|
+
|
|
384
|
+
Shortcut tạo màu trong suốt từ mã màu.
|
|
385
|
+
|
|
386
|
+
```js
|
|
387
|
+
transparentize('#4e73df', 0.5) // 'rgba(78, 115, 223, 0.5)'
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
---
|
|
391
|
+
|
|
392
|
+
## Script Loader
|
|
393
|
+
|
|
394
|
+
### loadScripts(scriptsArray, callbackFunc)
|
|
395
|
+
|
|
396
|
+
Load thêm script từ URL và chạy callback khi xong. Dùng để load thư viện on-demand.
|
|
397
|
+
|
|
398
|
+
```js
|
|
399
|
+
loadScripts(['/include/pdfmake/pdfmake.min.js', '/include/pdfmake/vfs_fonts.js'], function() {
|
|
400
|
+
printPDF({ data: docDef, download: true });
|
|
401
|
+
});
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
## Device Detection
|
|
407
|
+
|
|
408
|
+
### extractHostname(url) / getDomainWithoutSubdomain(url)
|
|
409
|
+
|
|
410
|
+
Trích xuất hostname/domain từ URL. Dùng nội bộ, ít dùng trực tiếp.
|
|
411
|
+
|
|
412
|
+
```js
|
|
413
|
+
extractHostname('https://api.fui.vn/fp/test') // 'api.fui.vn'
|
|
414
|
+
getDomainWithoutSubdomain('https://api.fui.vn') // 'fui.vn'
|
|
415
|
+
```
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Design System — App-mode vs Web-mode
|
|
2
|
+
|
|
3
|
+
> File này sở hữu: **quyết định chế độ thẩm mỹ, giới hạn `border-radius`, vòng tích luỹ
|
|
4
|
+
> `{projectId}/DESIGN.md`**. Về class/prop/style cụ thể được duyệt xem
|
|
5
|
+
> [controls-styling-vocabulary.md](controls-styling-vocabulary.md). Về anatomy dialog/form chuẩn
|
|
6
|
+
> dùng tool `design_read("fui")`.
|
|
7
|
+
|
|
8
|
+
Trước khi viết bất kỳ `controls` nào, xác định module thuộc chế độ nào. Đây là quyết định đầu tiên
|
|
9
|
+
và **cứng** — hai chế độ có bộ quy tắc thẩm mỹ riêng, không được vay mượn qua lại.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 1. Hai chế độ
|
|
14
|
+
|
|
15
|
+
| | **App-mode** (mặc định) | **Web-mode** |
|
|
16
|
+
|---|---|---|
|
|
17
|
+
| Áp dụng cho | Phần mềm quản lý: CRUD, danh sách, form, dashboard nội bộ, phân quyền | Landing page, trang giới thiệu sản phẩm, trang công khai (thường `HTMLOnly: true`) |
|
|
18
|
+
| Nguồn thẩm mỹ | **DUY NHẤT** `design_read("fui")` + `controls-styling-vocabulary.md` | Brand DESIGN.md (`design_list` → `design_read("<brand>")`) |
|
|
19
|
+
| Brand DESIGN.md (nike, linear, bmw...) | **CẤM** — kể cả khi "chỉ lấy màu" | Được phép, chọn theo tone/ngành |
|
|
20
|
+
| Style/class tự chế | **CẤM** (theo `controls-styling-vocabulary.md`) | Được phép trong `header.html` |
|
|
21
|
+
| Ưu tiên component | `f-*` / `v-*` theo chuẩn FUI | Tự do HTML/CSS |
|
|
22
|
+
|
|
23
|
+
**Tín hiệu nhận diện Web-mode**: `HTMLOnly: true` trong `_moduleInfo.json`, hoặc user mô tả
|
|
24
|
+
"landing page / trang giới thiệu / trang chủ công khai". Mọi trường hợp còn lại → App-mode.
|
|
25
|
+
Không chắc → **hỏi user**.
|
|
26
|
+
|
|
27
|
+
⚠️ `HTMLOnly: true` không chỉ là tín hiệu thẩm mỹ: ở chế độ đó server **không nhúng sẵn** thư viện
|
|
28
|
+
JS/CSS nào của V2/V3 (kể cả Vue, Vuetify, jQuery, lodash, MDI, `projectdefaultstyle.css`) — cần gì
|
|
29
|
+
phải tự khai trong chính module. Xem [module-structure.md §HTMLOnly=true: KHÔNG có thư viện nào được
|
|
30
|
+
nhúng sẵn](module-structure.md#htmlonlytrue-không-có-thư-viện-nào-được-nhúng-sẵn) **trước khi** viết
|
|
31
|
+
thẻ đầu tiên.
|
|
32
|
+
|
|
33
|
+
**Bắt buộc với App-mode**: trước khi viết/chỉnh sửa layout, dialog, form → nạp `design_read("fui")`
|
|
34
|
+
(nếu chưa nạp trong phiên) để lấy anatomy chuẩn (dialog `v-dialog` phong cách f-dialog, form
|
|
35
|
+
floating label, bảng màu/icon), đồng thời đọc `controls-styling-vocabulary.md`. AI phải hoàn tất bước
|
|
36
|
+
đọc này **trước khi phác thảo hoặc viết control**, không vừa thiết kế vừa đoán quy tắc từ trí nhớ.
|
|
37
|
+
|
|
38
|
+
Trước khi bắt đầu thiết kế, tự xác nhận đã hiểu ba nhóm quy tắc giao diện cốt lõi trong các file chủ
|
|
39
|
+
sở hữu: kích thước icon và từ vựng styling (`controls-styling-vocabulary.md`), anatomy/action của
|
|
40
|
+
dialog (`design_read("fui")` + `ui-dialog-patterns.md`), và canh dọc control trong một hàng
|
|
41
|
+
(`controls-patterns.md` §Canh giữa control theo chiều dọc). Nếu task có dialog thì phải đọc thêm
|
|
42
|
+
`ui-dialog-patterns.md` trước khi phác thảo dialog.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 2. Override theo project — `{projectId}/DESIGN.md`
|
|
47
|
+
|
|
48
|
+
Nếu `{projectId}/DESIGN.md` tồn tại trong workspace → file đó **ghi đè** quy tắc cùng chủ đề của
|
|
49
|
+
chế độ đang dùng (những gì không đề cập vẫn theo chuẩn chung). **Luôn kiểm tra file này trước khi
|
|
50
|
+
thiết kế.**
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 3. Vòng tích luỹ chuẩn thiết kế (BẮT BUỘC)
|
|
55
|
+
|
|
56
|
+
Khi user góp ý/chê một chi tiết UI ("dialog phải...", "nút này màu...", "đừng dùng..."):
|
|
57
|
+
|
|
58
|
+
1. Sửa module theo góp ý.
|
|
59
|
+
2. **Ghi ngay quy tắc đó vào `{projectId}/DESIGN.md`** (tạo file nếu chưa có — theo template ở cuối
|
|
60
|
+
`design_read("fui")`), diễn đạt thành **quy tắc tổng quát + ví dụ JSON đúng/sai**, không chỉ ghi
|
|
61
|
+
lại tình huống cụ thể.
|
|
62
|
+
3. Nếu quy tắc rõ ràng áp dụng cho MỌI project (không riêng project này) → nói với user: "quy tắc
|
|
63
|
+
này nên đưa vào chuẩn chung `fui/DESIGN.md`" và đề xuất nội dung — **chỉ user quyết định** cập
|
|
64
|
+
nhật chuẩn chung.
|
|
65
|
+
|
|
66
|
+
Nhờ vòng này, góp ý của user không bị mất sau phiên chat — lần thiết kế sau tự động tuân theo.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## 4. Giới hạn `border-radius`
|
|
71
|
+
|
|
72
|
+
Áp dụng cho **MỌI brand/style, kể cả khi DESIGN.md của brand đề xuất giá trị lớn hơn**:
|
|
73
|
+
|
|
74
|
+
| Loại khung | `border-radius` tối đa |
|
|
75
|
+
|---|---|
|
|
76
|
+
| Khung lớn tổng quát (card container, dialog, section wrapper, panel chính) | `12px` |
|
|
77
|
+
| Khung vừa (button lớn, input group, panel con, toolbar) | `8px` |
|
|
78
|
+
| Khung nhỏ (button nhỏ, chip, badge, tag, icon container) | `6px` |
|
|
79
|
+
|
|
80
|
+
Nếu brand DESIGN.md đề xuất `border-radius` lớn hơn mức tương ứng ở trên (ví dụ các brand có bo
|
|
81
|
+
tròn mạnh như Airbnb, Notion, Nike...) → **hạ xuống mức tối đa cho phép**, không áp dụng nguyên giá
|
|
82
|
+
trị gốc.
|
|
83
|
+
|
|
84
|
+
**Ngoại lệ**: phần tử tròn hoàn toàn (avatar, icon button dạng chấm tròn) dùng `border-radius: 50%`
|
|
85
|
+
— không thuộc phạm vi giới hạn này.
|