@fui-org/fui-cli 0.2.0 → 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.
Files changed (212) hide show
  1. package/README.md +13 -1
  2. package/dist/fui.js +160 -43
  3. package/package.json +4 -2
  4. package/skills/fui/SKILL.md +131 -66
  5. package/skills/fui-skill/README.md +112 -0
  6. package/skills/fui-skill/SKILL.md +269 -0
  7. package/skills/fui-skill/assets/projectdefaultstyle.css +518 -0
  8. package/skills/fui-skill/design-md/airbnb/DESIGN.md +545 -0
  9. package/skills/fui-skill/design-md/airbnb/README.md +5 -0
  10. package/skills/fui-skill/design-md/airtable/DESIGN.md +554 -0
  11. package/skills/fui-skill/design-md/airtable/README.md +5 -0
  12. package/skills/fui-skill/design-md/apple/DESIGN.md +562 -0
  13. package/skills/fui-skill/design-md/apple/README.md +5 -0
  14. package/skills/fui-skill/design-md/asu/DESIGN.md +179 -0
  15. package/skills/fui-skill/design-md/asu/README.md +107 -0
  16. package/skills/fui-skill/design-md/binance/DESIGN.md +634 -0
  17. package/skills/fui-skill/design-md/binance/README.md +5 -0
  18. package/skills/fui-skill/design-md/bmw/DESIGN.md +544 -0
  19. package/skills/fui-skill/design-md/bmw/README.md +5 -0
  20. package/skills/fui-skill/design-md/bmw-m/DESIGN.md +503 -0
  21. package/skills/fui-skill/design-md/bmw-m/README.md +5 -0
  22. package/skills/fui-skill/design-md/bugatti/DESIGN.md +454 -0
  23. package/skills/fui-skill/design-md/bugatti/README.md +5 -0
  24. package/skills/fui-skill/design-md/cal/DESIGN.md +542 -0
  25. package/skills/fui-skill/design-md/cal/README.md +5 -0
  26. package/skills/fui-skill/design-md/claude/DESIGN.md +589 -0
  27. package/skills/fui-skill/design-md/claude/README.md +5 -0
  28. package/skills/fui-skill/design-md/clay/DESIGN.md +541 -0
  29. package/skills/fui-skill/design-md/clay/README.md +5 -0
  30. package/skills/fui-skill/design-md/clickhouse/DESIGN.md +544 -0
  31. package/skills/fui-skill/design-md/clickhouse/README.md +5 -0
  32. package/skills/fui-skill/design-md/cohere/DESIGN.md +451 -0
  33. package/skills/fui-skill/design-md/cohere/README.md +5 -0
  34. package/skills/fui-skill/design-md/coinbase/DESIGN.md +570 -0
  35. package/skills/fui-skill/design-md/coinbase/README.md +5 -0
  36. package/skills/fui-skill/design-md/composio/DESIGN.md +506 -0
  37. package/skills/fui-skill/design-md/composio/README.md +5 -0
  38. package/skills/fui-skill/design-md/cursor/DESIGN.md +537 -0
  39. package/skills/fui-skill/design-md/cursor/README.md +5 -0
  40. package/skills/fui-skill/design-md/elevenlabs/DESIGN.md +504 -0
  41. package/skills/fui-skill/design-md/elevenlabs/README.md +5 -0
  42. package/skills/fui-skill/design-md/expo/DESIGN.md +526 -0
  43. package/skills/fui-skill/design-md/expo/README.md +5 -0
  44. package/skills/fui-skill/design-md/ferrari/DESIGN.md +531 -0
  45. package/skills/fui-skill/design-md/ferrari/README.md +5 -0
  46. package/skills/fui-skill/design-md/figma/DESIGN.md +578 -0
  47. package/skills/fui-skill/design-md/figma/README.md +5 -0
  48. package/skills/fui-skill/design-md/framer/DESIGN.md +544 -0
  49. package/skills/fui-skill/design-md/framer/README.md +5 -0
  50. package/skills/fui-skill/design-md/fui/DESIGN.md +532 -0
  51. package/skills/fui-skill/design-md/hashicorp/DESIGN.md +575 -0
  52. package/skills/fui-skill/design-md/hashicorp/README.md +5 -0
  53. package/skills/fui-skill/design-md/ibm/DESIGN.md +550 -0
  54. package/skills/fui-skill/design-md/ibm/README.md +5 -0
  55. package/skills/fui-skill/design-md/intercom/DESIGN.md +546 -0
  56. package/skills/fui-skill/design-md/intercom/README.md +5 -0
  57. package/skills/fui-skill/design-md/kraken/DESIGN.md +125 -0
  58. package/skills/fui-skill/design-md/kraken/README.md +5 -0
  59. package/skills/fui-skill/design-md/lamborghini/DESIGN.md +288 -0
  60. package/skills/fui-skill/design-md/lamborghini/README.md +5 -0
  61. package/skills/fui-skill/design-md/linear.app/DESIGN.md +548 -0
  62. package/skills/fui-skill/design-md/linear.app/README.md +5 -0
  63. package/skills/fui-skill/design-md/lovable/DESIGN.md +298 -0
  64. package/skills/fui-skill/design-md/lovable/README.md +5 -0
  65. package/skills/fui-skill/design-md/mastercard/DESIGN.md +365 -0
  66. package/skills/fui-skill/design-md/mastercard/README.md +5 -0
  67. package/skills/fui-skill/design-md/meta/DESIGN.md +683 -0
  68. package/skills/fui-skill/design-md/meta/README.md +5 -0
  69. package/skills/fui-skill/design-md/minimax/DESIGN.md +746 -0
  70. package/skills/fui-skill/design-md/minimax/README.md +5 -0
  71. package/skills/fui-skill/design-md/mintlify/DESIGN.md +852 -0
  72. package/skills/fui-skill/design-md/mintlify/README.md +5 -0
  73. package/skills/fui-skill/design-md/miro/DESIGN.md +825 -0
  74. package/skills/fui-skill/design-md/miro/README.md +5 -0
  75. package/skills/fui-skill/design-md/mistral.ai/DESIGN.md +773 -0
  76. package/skills/fui-skill/design-md/mistral.ai/README.md +5 -0
  77. package/skills/fui-skill/design-md/mongodb/DESIGN.md +767 -0
  78. package/skills/fui-skill/design-md/mongodb/README.md +5 -0
  79. package/skills/fui-skill/design-md/nike/DESIGN.md +575 -0
  80. package/skills/fui-skill/design-md/nike/README.md +5 -0
  81. package/skills/fui-skill/design-md/notion/DESIGN.md +821 -0
  82. package/skills/fui-skill/design-md/notion/README.md +5 -0
  83. package/skills/fui-skill/design-md/nvidia/DESIGN.md +640 -0
  84. package/skills/fui-skill/design-md/nvidia/README.md +5 -0
  85. package/skills/fui-skill/design-md/ollama/DESIGN.md +539 -0
  86. package/skills/fui-skill/design-md/ollama/README.md +5 -0
  87. package/skills/fui-skill/design-md/opencode.ai/DESIGN.md +521 -0
  88. package/skills/fui-skill/design-md/opencode.ai/README.md +5 -0
  89. package/skills/fui-skill/design-md/pinterest/DESIGN.md +597 -0
  90. package/skills/fui-skill/design-md/pinterest/README.md +5 -0
  91. package/skills/fui-skill/design-md/playstation/DESIGN.md +661 -0
  92. package/skills/fui-skill/design-md/playstation/README.md +5 -0
  93. package/skills/fui-skill/design-md/posthog/DESIGN.md +690 -0
  94. package/skills/fui-skill/design-md/posthog/README.md +5 -0
  95. package/skills/fui-skill/design-md/raycast/DESIGN.md +669 -0
  96. package/skills/fui-skill/design-md/raycast/README.md +5 -0
  97. package/skills/fui-skill/design-md/renault/DESIGN.md +589 -0
  98. package/skills/fui-skill/design-md/renault/README.md +5 -0
  99. package/skills/fui-skill/design-md/replicate/DESIGN.md +616 -0
  100. package/skills/fui-skill/design-md/replicate/README.md +5 -0
  101. package/skills/fui-skill/design-md/resend/DESIGN.md +585 -0
  102. package/skills/fui-skill/design-md/resend/README.md +5 -0
  103. package/skills/fui-skill/design-md/revolut/DESIGN.md +636 -0
  104. package/skills/fui-skill/design-md/revolut/README.md +5 -0
  105. package/skills/fui-skill/design-md/runwayml/DESIGN.md +244 -0
  106. package/skills/fui-skill/design-md/runwayml/README.md +5 -0
  107. package/skills/fui-skill/design-md/sanity/DESIGN.md +357 -0
  108. package/skills/fui-skill/design-md/sanity/README.md +5 -0
  109. package/skills/fui-skill/design-md/sentry/DESIGN.md +551 -0
  110. package/skills/fui-skill/design-md/sentry/README.md +5 -0
  111. package/skills/fui-skill/design-md/shopify/DESIGN.md +516 -0
  112. package/skills/fui-skill/design-md/shopify/README.md +5 -0
  113. package/skills/fui-skill/design-md/slack/DESIGN.md +482 -0
  114. package/skills/fui-skill/design-md/spacex/DESIGN.md +363 -0
  115. package/skills/fui-skill/design-md/spacex/README.md +5 -0
  116. package/skills/fui-skill/design-md/spotify/DESIGN.md +246 -0
  117. package/skills/fui-skill/design-md/spotify/README.md +5 -0
  118. package/skills/fui-skill/design-md/starbucks/DESIGN.md +580 -0
  119. package/skills/fui-skill/design-md/starbucks/README.md +5 -0
  120. package/skills/fui-skill/design-md/stripe/DESIGN.md +487 -0
  121. package/skills/fui-skill/design-md/stripe/README.md +5 -0
  122. package/skills/fui-skill/design-md/supabase/DESIGN.md +462 -0
  123. package/skills/fui-skill/design-md/supabase/README.md +5 -0
  124. package/skills/fui-skill/design-md/superhuman/DESIGN.md +448 -0
  125. package/skills/fui-skill/design-md/superhuman/README.md +5 -0
  126. package/skills/fui-skill/design-md/tesla/DESIGN.md +286 -0
  127. package/skills/fui-skill/design-md/tesla/README.md +5 -0
  128. package/skills/fui-skill/design-md/theverge/DESIGN.md +339 -0
  129. package/skills/fui-skill/design-md/theverge/README.md +5 -0
  130. package/skills/fui-skill/design-md/together.ai/DESIGN.md +633 -0
  131. package/skills/fui-skill/design-md/together.ai/README.md +5 -0
  132. package/skills/fui-skill/design-md/uber/DESIGN.md +636 -0
  133. package/skills/fui-skill/design-md/uber/README.md +5 -0
  134. package/skills/fui-skill/design-md/vercel/DESIGN.md +736 -0
  135. package/skills/fui-skill/design-md/vercel/README.md +5 -0
  136. package/skills/fui-skill/design-md/vodafone/DESIGN.md +538 -0
  137. package/skills/fui-skill/design-md/vodafone/README.md +5 -0
  138. package/skills/fui-skill/design-md/voltagent/DESIGN.md +521 -0
  139. package/skills/fui-skill/design-md/voltagent/README.md +5 -0
  140. package/skills/fui-skill/design-md/warp/DESIGN.md +526 -0
  141. package/skills/fui-skill/design-md/warp/README.md +5 -0
  142. package/skills/fui-skill/design-md/webflow/DESIGN.md +588 -0
  143. package/skills/fui-skill/design-md/webflow/README.md +5 -0
  144. package/skills/fui-skill/design-md/wired/DESIGN.md +497 -0
  145. package/skills/fui-skill/design-md/wired/README.md +5 -0
  146. package/skills/fui-skill/design-md/wise/DESIGN.md +544 -0
  147. package/skills/fui-skill/design-md/wise/README.md +5 -0
  148. package/skills/fui-skill/design-md/x.ai/DESIGN.md +465 -0
  149. package/skills/fui-skill/design-md/x.ai/README.md +5 -0
  150. package/skills/fui-skill/design-md/zapier/DESIGN.md +537 -0
  151. package/skills/fui-skill/design-md/zapier/README.md +5 -0
  152. package/skills/fui-skill/examples/component.vue +162 -0
  153. package/skills/fui-skill/examples/f-table-patterns.json +331 -0
  154. package/skills/fui-skill/examples/module-patterns.json +973 -0
  155. package/skills/fui-skill/examples/project-patterns.json +222 -0
  156. package/skills/fui-skill/metadata.json +75 -0
  157. package/skills/fui-skill/references/INDEX.md +144 -0
  158. package/skills/fui-skill/references/advanced-techniques.md +160 -0
  159. package/skills/fui-skill/references/coding-standards.md +112 -0
  160. package/skills/fui-skill/references/component-design.md +455 -0
  161. package/skills/fui-skill/references/component-quickref.md +77 -0
  162. package/skills/fui-skill/references/component-table.md +276 -0
  163. package/skills/fui-skill/references/components-dialog.md +192 -0
  164. package/skills/fui-skill/references/components-display.md +147 -0
  165. package/skills/fui-skill/references/components-echart.md +391 -0
  166. package/skills/fui-skill/references/components-input.md +359 -0
  167. package/skills/fui-skill/references/controls-patterns.md +847 -0
  168. package/skills/fui-skill/references/controls-styling-vocabulary.md +140 -0
  169. package/skills/fui-skill/references/db-table-design.md +77 -0
  170. package/skills/fui-skill/references/db-workflow.md +504 -0
  171. package/skills/fui-skill/references/default-function.md +415 -0
  172. package/skills/fui-skill/references/design-modes.md +85 -0
  173. package/skills/fui-skill/references/echart-templates.md +481 -0
  174. package/skills/fui-skill/references/fastproject.md +97 -0
  175. package/skills/fui-skill/references/fsheet.md +218 -0
  176. package/skills/fui-skill/references/fullstack-workflow.md +351 -0
  177. package/skills/fui-skill/references/module-data-patterns.md +126 -0
  178. package/skills/fui-skill/references/module-json-anatomy.md +137 -0
  179. package/skills/fui-skill/references/module-structure.md +260 -0
  180. package/skills/fui-skill/references/new-session.md +108 -0
  181. package/skills/fui-skill/references/pdfmake.md +60 -0
  182. package/skills/fui-skill/references/permission-system.md +169 -0
  183. package/skills/fui-skill/references/platform-architecture.md +294 -0
  184. package/skills/fui-skill/references/project-config.md +335 -0
  185. package/skills/fui-skill/references/project-provisioning.md +383 -0
  186. package/skills/fui-skill/references/script-map.md +296 -0
  187. package/skills/fui-skill/references/sql-clr-functions.md +224 -0
  188. package/skills/fui-skill/references/system-design.md +116 -0
  189. package/skills/fui-skill/references/tapi-file-api.md +191 -0
  190. package/skills/fui-skill/references/tapi-permission-patterns.md +158 -0
  191. package/skills/fui-skill/references/tapi-reference.md +549 -0
  192. package/skills/fui-skill/references/tools-registry.md +460 -0
  193. package/skills/fui-skill/references/ui-crosswindow-patterns.md +317 -0
  194. package/skills/fui-skill/references/ui-dialog-patterns.md +229 -0
  195. package/skills/fui-skill/references/ui-layout-patterns.md +176 -0
  196. package/skills/fui-skill/references/ui-patterns.md +315 -0
  197. package/skills/fui-skill/references/ui-screenshot-review.md +94 -0
  198. package/skills/fui-skill/references/ui-table-cell-patterns.md +316 -0
  199. package/skills/fui-skill/references/ui-templates.md +29 -0
  200. package/skills/fui-skill/references/verification.md +246 -0
  201. package/skills/fui-skill/references/watcher-patterns.md +196 -0
  202. package/skills/fui-skill/references/websocket-realtime.md +254 -0
  203. package/skills/fui-skill/scripts/component-3.0.js +2549 -0
  204. package/skills/fui-skill/scripts/component.js +3142 -0
  205. package/skills/fui-skill/scripts/componentTable-3.0.js +909 -0
  206. package/skills/fui-skill/scripts/componentTable.js +769 -0
  207. package/skills/fui-skill/scripts/defaultfunction-3.0.js +781 -0
  208. package/skills/fui-skill/scripts/defaultfunction.js +966 -0
  209. package/skills/fui-skill/scripts/fastproject-3.0.js +870 -0
  210. package/skills/fui-skill/scripts/fastproject.js +828 -0
  211. package/skills/fui-skill/scripts/fechart.js +890 -0
  212. package/skills/fui-skill/scripts/fsheet.js +1330 -0
@@ -0,0 +1,359 @@
1
+ # FUI Input & Interaction Components
2
+
3
+ > File này sở hữu: **component nhập liệu: `f-date`, `f-time`, `f-search`, `f-radiobox`, `f-button`, `f-menu`, upload**.
4
+
5
+ Components dùng để nhận input hoặc tương tác với người dùng. Xem prefix/binding rules tại [components-display.md](components-display.md).
6
+
7
+ ## f-radiobox
8
+
9
+ - Props (source): `label`, `items`, `itemValue`, `itemText`
10
+ - Emit: `input` on change
11
+ - Example:
12
+
13
+ ```json
14
+ {
15
+ "el": "f-radiobox",
16
+ "w": "6",
17
+ "attr": {
18
+ "label": "Loai",
19
+ "v-model": "formData.loai",
20
+ ":items": "loaiOptions",
21
+ "item-value": "value",
22
+ "item-text": "text"
23
+ }
24
+ }
25
+ ```
26
+
27
+ ## f-menu
28
+
29
+ - Props (source): `items`, `label`, `iconText`, `menuAttr`
30
+ - Behavior: dropdown button với danh sách menu items
31
+ - Nếu `label` rỗng hoặc undefined → hiển thị icon-only button
32
+ - `items` format: mảng `{ text, visible, color, "icon-text", action, href, target }`
33
+ - `action`: FUI action object — chạy qua `runAction`
34
+ - `href` + `target`: điều hướng như thẻ `<a>`
35
+ - `visible`: ẩn/hiện item (default `true`)
36
+ - Example:
37
+
38
+ ```json
39
+ {
40
+ "el": "f-menu",
41
+ "w": "3",
42
+ "attr": {
43
+ "label": "Tac vu",
44
+ "icon-text": "mdi-dots-vertical",
45
+ ":items": [
46
+ { "text": "Xem chi tiết", "icon-text": "mdi-eye", "action": { "CALL": "vueData.viewDetail" } },
47
+ { "text": "Xuất Excel", "icon-text": "mdi-file-excel", "action": { "CALL": "vueData.exportExcel" } },
48
+ { "text": "Trang chủ", "icon-text": "mdi-home", "href": "/", "target": "_blank" }
49
+ ]
50
+ }
51
+ }
52
+ ```
53
+
54
+ ## f-search
55
+
56
+ - Props (source): `api`, `apiData`, `items`
57
+ - Emit: `input` when selection changes
58
+ - Behavior: debounced API search (500ms), bỏ qua keyword < 2 ký tự
59
+ - Example:
60
+
61
+ ```json
62
+ {
63
+ "el": "f-search",
64
+ "w": "6",
65
+ "attr": {
66
+ "v-model": "filter.userID",
67
+ "api": "/api/user/search",
68
+ ":api-data": { "Keyword": "TEXT", "GroupID": "vueData.groupID" },
69
+ "item-text": "UserName",
70
+ "item-value": "UserID"
71
+ }
72
+ }
73
+ ```
74
+
75
+ ## f-button
76
+
77
+ - Props (source): `label`, `checkvalid`, `ctrlhotkey`, `iconText`, `action`, `includeData`
78
+ - Behavior:
79
+ - debounced click (500ms, leading)
80
+ - validates form khi `checkvalid=true`
81
+ - nếu `$attrs.target == 'dialog'`, mở window theo attrs (`wid`, `title`, `url`, `onclose`)
82
+ - Example:
83
+
84
+ ```json
85
+ {
86
+ "el": "f-button",
87
+ "w": "3",
88
+ "attr": {
89
+ "label": "Luu",
90
+ "color": "primary",
91
+ ":action": { "CALL": "handleSubmit" }
92
+ }
93
+ }
94
+ ```
95
+
96
+ ## f-date
97
+
98
+ - Props (source): `value`, `label`, `dateAdd`, `required`
99
+ - Emit: `input` with normalized date (hoặc `null`)
100
+ - Behavior:
101
+ - hỗ trợ typed mask và picker
102
+ - chấp nhận nhiều định dạng input khi khởi tạo
103
+ - `dateAdd` tự set ngày tương đối so với hôm nay
104
+ - Example:
105
+
106
+ ```json
107
+ {
108
+ "el": "f-date",
109
+ "w": "6",
110
+ "attr": {
111
+ "v-model": "filter.fromDate",
112
+ "label": "Tu ngay",
113
+ ":required": false,
114
+ ":date-add": -7
115
+ }
116
+ }
117
+ ```
118
+
119
+ ## f-time
120
+
121
+ - Props (source): `value`, `label`, `timeAdd`, `required`
122
+ - Emit: `input` with `HH:mm` (hoặc `null`)
123
+ - Behavior: typed mask + time picker
124
+ - Example:
125
+
126
+ ```json
127
+ {
128
+ "el": "f-time",
129
+ "w": "6",
130
+ "attr": {
131
+ "v-model": "formData.startTime",
132
+ "label": "Gio bat dau",
133
+ ":required": false,
134
+ ":time-add": 30
135
+ }
136
+ }
137
+ ```
138
+
139
+ ## f-time-counter
140
+
141
+ - Props (source): `value`, `type`, `labelFormat`, `format`
142
+ - `value`: số giây (Number) hoặc datetime string (`"yyyy-MM-dd HH:mm:ss"`)
143
+ - `type`:
144
+ - `0` — đếm ngược, hiển thị ngày/giờ/phút/giây đầy đủ
145
+ - `1` — đếm ngược, chỉ hiển thị tổng số giây còn lại
146
+ - `2` — đồng hồ thời gian thực (hiển thị giờ hiện tại)
147
+ - `format`: `{ label, day, hour, minute, second }` — nhãn đơn vị
148
+ - Emit:
149
+ - `input` (số giây còn lại) khi đếm từ seconds (value là Number)
150
+ - `time-end` khi timer về 0
151
+ - Example:
152
+
153
+ ```json
154
+ {
155
+ "el": "f-time-counter",
156
+ "w": "4",
157
+ "attr": {
158
+ ":value": 300,
159
+ ":type": 0,
160
+ ":format": { "label": "Còn lại: ", "day": " ngày", "hour": " giờ", "minute": " phút", "second": " giây" }
161
+ }
162
+ }
163
+ ```
164
+
165
+ ## f-qrcode
166
+
167
+ - Props (source): `value`, `logo`, `color`, `size`
168
+ - Behavior: render QR image từ value
169
+ - Example:
170
+
171
+ ```json
172
+ {
173
+ "el": "f-qrcode",
174
+ "w": "4",
175
+ "attr": {
176
+ "v-model": "formData.qrText",
177
+ ":size": 200,
178
+ "color": "#1e90ff"
179
+ }
180
+ }
181
+ ```
182
+
183
+ ## f-qrcode-reader
184
+
185
+ - Props (source): `value`, `label`, `width`
186
+ - Emit: `input` (đóng dialog), `update` (decoded content)
187
+ - Example:
188
+
189
+ ```json
190
+ {
191
+ "el": "f-qrcode-reader",
192
+ "w": "12",
193
+ "attr": {
194
+ "v-model": "scanDialog",
195
+ "label": "Doc ma QR",
196
+ "v-on:update": "CALL(vueData.handleScanResult, { item: $event })"
197
+ }
198
+ }
199
+ ```
200
+
201
+ ## f-image-update
202
+
203
+ - Props (source): `value`, `label`, `title`, `apiUpload`, `iconText`, `imageType`, `size`, `quality`, `dialogWidth`, `dialogHeight`, `imageBoxAttr`, `imageAttr`, `buttonAttr`, `croperAttr`
204
+ - Emit: `update` sau khi upload thành công (`{ returnData, imageData }`)
205
+ - Example:
206
+
207
+ ```json
208
+ {
209
+ "el": "f-image-update",
210
+ "w": "4",
211
+ "attr": {
212
+ "v-model": "formData.avatar",
213
+ "label": "Anh dai dien",
214
+ "api-upload": "/api/upload/avatar",
215
+ ":dialog-width": 600,
216
+ ":dialog-height": 420
217
+ }
218
+ }
219
+ ```
220
+
221
+ ## f-file-upload
222
+
223
+ - Props (source): `label`, `url`, `autoclose`, `iconText`, `filters`, `resize`, `onclose`
224
+ - Emit: `input` on upload complete
225
+ - Behavior:
226
+ - dùng Plupload
227
+ - tự upload khi chọn file
228
+ - chạy `onclose` action khi dialog đóng
229
+ - Example:
230
+
231
+ ```json
232
+ {
233
+ "el": "f-file-upload",
234
+ "w": "4",
235
+ "attr": {
236
+ "label": "Tai tep",
237
+ "url": "/api/upload/file",
238
+ ":filters": {
239
+ "max_file_size": "20mb",
240
+ "mime_types": [{ "title": "PDF", "extensions": "pdf" }]
241
+ },
242
+ ":onclose": { "CALL": "handleUploadClosed" }
243
+ }
244
+ }
245
+ ```
246
+
247
+ ## f-excel-reader
248
+
249
+ Nút upload đọc file Excel/CSV → đẩy dữ liệu đã parse vào `v-model` (mảng object, hoặc mảng lồng mảng tuỳ `:header`). Thư viện XLSX lazy-load khi người dùng chọn file lần đầu, **không cần khai import** trong module.json.
250
+
251
+ **Đầu ra:**
252
+ - `v-model` — mảng dữ liệu đã parse
253
+ - `load` — object metadata: `{ rows, rowCount, headers, sheetName, sheetNames, fileName }`
254
+ - `error` — `{ message, fileName }` khi file quá lớn, đọc thất bại, hoặc XLSX load timeout
255
+ - `action-error` — `{ message, fileName }` khi action callback ném lỗi (dữ liệu Excel vẫn hợp lệ)
256
+
257
+ **Props — Hiển thị:**
258
+
259
+ | Prop | Default | Mô tả |
260
+ |---|---|---|
261
+ | `label` | `'Load Excel'` | Nhãn nút |
262
+ | `icon-text` | `'mdi-grid'` | Icon Vuetify MDI; `null`/`''` để ẩn icon |
263
+ | `accept` | `.xlsx,.xlsb,.xlsm,.xls,.csv,.txt,.ods,.dbf,.prn,.html,.htm` | Loại file chấp nhận |
264
+
265
+ **Props — Đọc dữ liệu:**
266
+
267
+ | Prop | Default | Mô tả |
268
+ |---|---|---|
269
+ | `:header` | `null` | Quyết định key của mỗi object trong mảng kết quả (xem bảng bên dưới) |
270
+ | `:raw` | `false` | `false` = parse số và ngày; `true` = giữ nguyên raw string |
271
+ | `date-format` | `'dd/mm/yyyy'` | Format ngày khi `:raw="false"` |
272
+ | `:defval` | `''` | Giá trị thay thế cho ô trống |
273
+ | `:sheet` | `0` | Sheet cần đọc — số (index 0-based) hoặc tên sheet (string) |
274
+ | `:skip-rows` | `0` | Bỏ qua N dòng đầu trước khi đọc header/data |
275
+
276
+ **Giá trị của `:header`:**
277
+
278
+ | Giá trị | Kết quả |
279
+ |---|---|
280
+ | `null` _(mặc định)_ | Dùng dòng đầu tiên của sheet làm tên key: `[{MSSV:"1234", HoTen:"An"}]` |
281
+ | `1` | Trả mảng lồng mảng, không có key: `[["MSSV","HoTen"],["1234","An"]]` |
282
+ | `"A"` | Dùng chữ cái cột Excel làm key: `[{A:"MSSV", B:"HoTen"}]` |
283
+ | `["ma","ten"]` | Tự đặt tên key theo thứ tự cột (không dùng dòng header trong file) |
284
+
285
+ ⚠️ **Bẫy `:sheet` — sai tên thì KHÔNG báo lỗi.** Hai kiểu giá trị hỏng khác hẳn nhau:
286
+
287
+ - `:sheet` là **tên** không tồn tại → **im lặng đọc sheet đầu tiên**. Vẫn ra dữ liệu, chỉ là dữ liệu của sheet khác — gõ sai tên sheet không có dấu hiệu nào.
288
+ - `:sheet` là **số** vượt quá số sheet → ném lỗi `Không tìm thấy sheet: N` qua event `error`.
289
+
290
+ Dùng tên sheet thì đối chiếu `sheetName` trong event `load` với tên mình mong đợi.
291
+
292
+ **Props — Giới hạn & timeout:**
293
+
294
+ | Prop | Default | Mô tả |
295
+ |---|---|---|
296
+ | `:max-size` | `20` | Giới hạn kích thước file, **đơn vị MB** (tên prop không mang chữ "MB"); `0` = bỏ giới hạn |
297
+ | `:load-timeout` | `15000` | Timeout nạp thư viện XLSX (ms) |
298
+
299
+ Giá trị `:max-size` không parse được thành số thì **quay về 20 MB**, không phải bỏ giới hạn — muốn bỏ giới hạn phải truyền đúng số `0`.
300
+
301
+ **Prop — Callback:**
302
+
303
+ | Prop | Mô tả |
304
+ |---|---|
305
+ | `:action` | Action object chạy sau khi parse xong và `v-model` đã nhận dữ liệu — thường dùng `{ "CALL": "tenAction" }` |
306
+
307
+ **Ranh giới lỗi — đọc Excel và chạy action là hai giai đoạn tách hẳn:**
308
+
309
+ - `action` chỉ chạy sau khi `v-model` đã nhận dữ liệu mới (qua `$nextTick`), nên trong action cứ đọc thẳng `vueData.excelRows`.
310
+ - Một khi Excel đã đọc xong, **không lỗi nào còn biến thành event `error` được nữa** — lỗi của action đi ra `action-error`, và dữ liệu đã parse vẫn nguyên vẹn, không rollback. Nghĩa là nhận được `error` ⇒ chắc chắn hỏng ở khâu đọc file; nhận được `action-error` ⇒ file đọc tốt, hỏng ở xử lý phía sau.
311
+ - Bấm nút lần nữa khi đang parse thì bị bỏ qua; nếu chọn file mới trước khi file cũ parse xong thì kết quả của lượt cũ bị vứt, không ghi đè lượt mới.
312
+
313
+ **Ví dụ cơ bản — header từ dòng đầu (mặc định):**
314
+
315
+ ```json
316
+ {
317
+ "el": "f-excel-reader",
318
+ "w": "4",
319
+ "attr": {
320
+ "label": "Nhập từ Excel",
321
+ "icon-text": "mdi-microsoft-excel",
322
+ ":action": { "CALL": "cmdImportExcel" },
323
+ "v-model": "excelRows"
324
+ }
325
+ }
326
+ ```
327
+
328
+ **Ví dụ — đọc sheet theo tên, bỏ 2 dòng đầu, giữ raw string:**
329
+
330
+ ```json
331
+ {
332
+ "el": "f-excel-reader",
333
+ "w": "4",
334
+ "attr": {
335
+ "label": "Nhập DanhSach",
336
+ ":sheet": "DanhSach",
337
+ ":skip-rows": 2,
338
+ ":raw": true,
339
+ ":action": { "CALL": "cmdImportExcel" },
340
+ "v-model": "excelRows"
341
+ }
342
+ }
343
+ ```
344
+
345
+ **Ví dụ — tự đặt tên cột, giới hạn 5 MB:**
346
+
347
+ ```json
348
+ {
349
+ "el": "f-excel-reader",
350
+ "w": "4",
351
+ "attr": {
352
+ "label": "Nhập Excel",
353
+ ":header": ["mssv", "hoTen", "diemTB"],
354
+ ":max-size": 5,
355
+ ":action": { "CALL": "cmdImportExcel" },
356
+ "v-model": "excelRows"
357
+ }
358
+ }
359
+ ```