@fui-org/fui-cli 2.1.3 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -7
- package/dist/fui-qrb1j9jz.js +397 -0
- package/dist/fui.js +1 -1
- package/package.json +1 -1
- package/skills/fui/SKILL.md +143 -38
- package/skills/fui/agents/fui-builder.md +62 -0
- package/skills/fui/agents/fui-tester.md +77 -0
- package/skills/{fui-skill → fui}/assets/projectdefaultstyle.css +489 -489
- package/skills/{fui-skill → fui}/examples/component.vue +162 -162
- package/skills/{fui-skill → fui}/references/INDEX.md +5 -4
- package/skills/{fui-skill → fui}/references/advanced-techniques.md +170 -168
- package/skills/{fui-skill → fui}/references/component-design.md +2 -2
- package/skills/{fui-skill → fui}/references/component-quickref.md +78 -78
- package/skills/{fui-skill → fui}/references/component-table.md +287 -287
- package/skills/{fui-skill → fui}/references/components-dialog.md +191 -191
- package/skills/{fui-skill → fui}/references/components-display.md +141 -141
- package/skills/{fui-skill → fui}/references/components-echart.md +316 -316
- package/skills/{fui-skill → fui}/references/components-input.md +335 -335
- package/skills/{fui-skill → fui}/references/controls-patterns.md +2 -2
- package/skills/{fui-skill → fui}/references/controls-styling-vocabulary.md +173 -173
- package/skills/{fui-skill → fui}/references/db-workflow.md +9 -9
- package/skills/{fui-skill → fui}/references/default-function.md +456 -456
- package/skills/{fui-skill → fui}/references/design-modes.md +57 -57
- package/skills/{fui-skill → fui}/references/echart-templates.md +489 -489
- package/skills/{fui-skill → fui}/references/fastproject.md +99 -99
- package/skills/{fui-skill → fui}/references/fullstack-workflow.md +314 -313
- package/skills/{fui-skill → fui}/references/module-data-patterns.md +117 -117
- package/skills/{fui-skill → fui}/references/module-json-anatomy.md +132 -132
- package/skills/{fui-skill → fui}/references/module-structure.md +144 -144
- package/skills/{fui-skill → fui}/references/new-session.md +1 -2
- package/skills/{fui-skill → fui}/references/pdfmake.md +60 -60
- package/skills/{fui-skill → fui}/references/permission-system.md +1 -1
- package/skills/{fui-skill → fui}/references/platform-architecture.md +269 -269
- package/skills/{fui-skill → fui}/references/project-config.md +303 -303
- package/skills/{fui-skill → fui}/references/project-provisioning.md +4 -4
- package/skills/{fui-skill → fui}/references/script-map.md +262 -262
- package/skills/{fui-skill → fui}/references/sql-clr-functions.md +225 -225
- package/skills/{fui-skill → fui}/references/system-design.md +2 -2
- package/skills/{fui-skill → fui}/references/tapi-permission-patterns.md +156 -156
- package/skills/{fui-skill → fui}/references/tapi-reference.md +474 -474
- package/skills/fui/references/team-run.md +141 -0
- package/skills/fui/references/tools-registry.md +211 -0
- package/skills/{fui-skill → fui}/references/ui-crosswindow-patterns.md +321 -321
- package/skills/{fui-skill → fui}/references/ui-dialog-patterns.md +255 -255
- package/skills/{fui-skill → fui}/references/ui-layout-patterns.md +176 -176
- package/skills/{fui-skill → fui}/references/ui-patterns.md +303 -303
- package/skills/{fui-skill → fui}/references/ui-screenshot-review.md +95 -95
- package/skills/{fui-skill → fui}/references/ui-table-cell-patterns.md +318 -318
- package/skills/{fui-skill → fui}/references/verification.md +11 -11
- package/skills/{fui-skill → fui}/references/watcher-patterns.md +163 -163
- package/skills/{fui-skill → fui}/references/websocket-realtime.md +271 -271
- package/skills/{fui-skill → fui}/scripts/component-3.0.js +2715 -2715
- package/skills/{fui-skill → fui}/scripts/component.js +3147 -3147
- package/skills/{fui-skill → fui}/scripts/componentTable-3.0.js +1038 -1038
- package/skills/{fui-skill → fui}/scripts/componentTable.js +891 -891
- package/skills/{fui-skill → fui}/scripts/defaultfunction-3.0.js +866 -866
- package/skills/{fui-skill → fui}/scripts/defaultfunction.js +1051 -1051
- package/skills/{fui-skill → fui}/scripts/fastproject-3.0.js +870 -870
- package/skills/{fui-skill → fui}/scripts/fastproject.js +828 -828
- package/skills/fui-target/hooks/register.tsx +2 -0
- package/skills/fui-target/types/index.d.ts +1 -0
- package/dist/fui-hk5frcn2.js +0 -381
- package/skills/fui-skill/SKILL.md +0 -139
- package/skills/fui-skill/references/tools-registry.md +0 -84
- /package/skills/{fui-skill → fui}/assets/projectdefaultstyle-3.0.css +0 -0
- /package/skills/{fui-skill → fui}/design-md/airbnb/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/airbnb/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/airtable/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/airtable/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/apple/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/apple/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/asu/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/asu/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/binance/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/binance/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/bmw/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/bmw/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/bmw-m/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/bmw-m/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/bugatti/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/bugatti/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/cal/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/cal/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/claude/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/claude/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/clay/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/clay/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/clickhouse/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/clickhouse/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/cohere/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/cohere/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/coinbase/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/coinbase/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/composio/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/composio/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/cursor/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/cursor/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/elevenlabs/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/elevenlabs/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/expo/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/expo/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/ferrari/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/ferrari/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/figma/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/figma/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/framer/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/framer/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/fui/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/hashicorp/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/hashicorp/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/ibm/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/ibm/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/intercom/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/intercom/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/kraken/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/kraken/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/lamborghini/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/lamborghini/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/linear.app/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/linear.app/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/lovable/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/lovable/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mastercard/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mastercard/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/meta/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/meta/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/minimax/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/minimax/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mintlify/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mintlify/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/miro/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/miro/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mistral.ai/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mistral.ai/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mongodb/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mongodb/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/nike/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/nike/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/notion/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/notion/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/nvidia/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/nvidia/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/ollama/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/ollama/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/opencode.ai/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/opencode.ai/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/pinterest/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/pinterest/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/playstation/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/playstation/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/posthog/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/posthog/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/raycast/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/raycast/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/renault/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/renault/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/replicate/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/replicate/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/resend/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/resend/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/revolut/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/revolut/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/runwayml/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/runwayml/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/sanity/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/sanity/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/sentry/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/sentry/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/shopify/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/shopify/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/slack/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/spacex/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/spacex/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/spotify/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/spotify/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/starbucks/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/starbucks/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/stripe/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/stripe/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/supabase/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/supabase/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/superhuman/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/superhuman/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/tesla/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/tesla/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/theverge/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/theverge/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/together.ai/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/together.ai/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/uber/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/uber/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/vercel/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/vercel/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/vodafone/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/vodafone/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/voltagent/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/voltagent/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/warp/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/warp/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/webflow/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/webflow/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/wired/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/wired/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/wise/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/wise/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/x.ai/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/x.ai/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/zapier/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/zapier/README.md +0 -0
- /package/skills/{fui-skill → fui}/examples/f-table-patterns.json +0 -0
- /package/skills/{fui-skill → fui}/examples/module-patterns.json +0 -0
- /package/skills/{fui-skill → fui}/examples/project-patterns.json +0 -0
- /package/skills/{fui-skill → fui}/references/coding-standards.md +0 -0
- /package/skills/{fui-skill → fui}/references/db-table-design.md +0 -0
- /package/skills/{fui-skill → fui}/references/fsheet.md +0 -0
- /package/skills/{fui-skill → fui}/references/tapi-file-api.md +0 -0
- /package/skills/{fui-skill → fui}/references/ui-templates.md +0 -0
- /package/skills/{fui-skill → fui}/scripts/fechart.js +0 -0
- /package/skills/{fui-skill → fui}/scripts/fsheet.js +0 -0
|
@@ -1,335 +1,335 @@
|
|
|
1
|
-
# FUI Input & Interaction Components
|
|
2
|
-
|
|
3
|
-
> Owns: **input components: `f-date`, `f-time`, `f-search`, `f-radiobox`, `f-button`, `f-menu`, upload**.
|
|
4
|
-
|
|
5
|
-
Components for input and user interaction. Prefix/binding rules: [components-display.md](components-display.md).
|
|
6
|
-
|
|
7
|
-
## f-radiobox
|
|
8
|
-
|
|
9
|
-
- Props (source): `label`, `items`, `itemValue`, `itemText`
|
|
10
|
-
- Emit: `input` on change
|
|
11
|
-
|
|
12
|
-
```json
|
|
13
|
-
{
|
|
14
|
-
"el": "f-radiobox",
|
|
15
|
-
"w": "6",
|
|
16
|
-
"attr": {
|
|
17
|
-
"label": "Loai",
|
|
18
|
-
"v-model": "formData.loai",
|
|
19
|
-
":items": "loaiOptions",
|
|
20
|
-
"item-value": "value",
|
|
21
|
-
"item-text": "text"
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
## f-menu
|
|
27
|
-
|
|
28
|
-
- Props (source): `items`, `label`, `iconText`, `menuAttr`
|
|
29
|
-
- Dropdown button with menu items; `label` empty/undefined → icon-only button
|
|
30
|
-
- `items`: array of `{ text, visible, color, "icon-text", action, href, target }`
|
|
31
|
-
- `action`: FUI action object, run via `runAction`
|
|
32
|
-
- `href` + `target`: navigate like `<a>`
|
|
33
|
-
- `visible`: show/hide item (default `true`)
|
|
34
|
-
|
|
35
|
-
```json
|
|
36
|
-
{
|
|
37
|
-
"el": "f-menu",
|
|
38
|
-
"w": "3",
|
|
39
|
-
"attr": {
|
|
40
|
-
"label": "Tac vu",
|
|
41
|
-
"icon-text": "mdi-dots-vertical",
|
|
42
|
-
":items": [
|
|
43
|
-
{ "text": "Xem chi tiết", "icon-text": "mdi-eye", "action": { "CALL": "vueData.viewDetail" } },
|
|
44
|
-
{ "text": "Xuất Excel", "icon-text": "mdi-file-excel", "action": { "CALL": "vueData.exportExcel" } },
|
|
45
|
-
{ "text": "Trang chủ", "icon-text": "mdi-home", "href": "/", "target": "_blank" }
|
|
46
|
-
]
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
## f-search
|
|
52
|
-
|
|
53
|
-
- Props (source): `api`, `apiData`, `items`
|
|
54
|
-
- Emit: `input` when selection changes
|
|
55
|
-
- Debounced API search (500ms); keywords < 2 chars ignored
|
|
56
|
-
|
|
57
|
-
```json
|
|
58
|
-
{
|
|
59
|
-
"el": "f-search",
|
|
60
|
-
"w": "6",
|
|
61
|
-
"attr": {
|
|
62
|
-
"v-model": "filter.userID",
|
|
63
|
-
"api": "/api/user/search",
|
|
64
|
-
":api-data": { "Keyword": "TEXT", "GroupID": "vueData.groupID" },
|
|
65
|
-
"item-text": "UserName",
|
|
66
|
-
"item-value": "UserID"
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
## f-button
|
|
72
|
-
|
|
73
|
-
- Props (source): `label`, `checkvalid`, `ctrlhotkey`, `iconText`, `action`, `includeData`
|
|
74
|
-
- Debounced click (500ms, leading); validates form when `checkvalid=true`
|
|
75
|
-
- `$attrs.target == 'dialog'` → opens a window from attrs (`wid`, `title`, `url`, `onclose`)
|
|
76
|
-
|
|
77
|
-
```json
|
|
78
|
-
{
|
|
79
|
-
"el": "f-button",
|
|
80
|
-
"w": "3",
|
|
81
|
-
"attr": {
|
|
82
|
-
"label": "Luu",
|
|
83
|
-
"color": "primary",
|
|
84
|
-
":action": { "CALL": "handleSubmit" }
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
## f-date
|
|
90
|
-
|
|
91
|
-
- Props (source): `value`, `label`, `dateAdd`, `required`
|
|
92
|
-
- Emit: `input` with normalized date (or `null`)
|
|
93
|
-
- Typed mask + picker; accepts many input formats on init; `dateAdd` sets a date relative to today
|
|
94
|
-
|
|
95
|
-
```json
|
|
96
|
-
{
|
|
97
|
-
"el": "f-date",
|
|
98
|
-
"w": "6",
|
|
99
|
-
"attr": {
|
|
100
|
-
"v-model": "filter.fromDate",
|
|
101
|
-
"label": "Tu ngay",
|
|
102
|
-
":required": false,
|
|
103
|
-
":date-add": -7
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
## f-time
|
|
109
|
-
|
|
110
|
-
- Props (source): `value`, `label`, `timeAdd`, `required`
|
|
111
|
-
- Emit: `input` with `HH:mm` (or `null`)
|
|
112
|
-
- Typed mask + time picker
|
|
113
|
-
|
|
114
|
-
```json
|
|
115
|
-
{
|
|
116
|
-
"el": "f-time",
|
|
117
|
-
"w": "6",
|
|
118
|
-
"attr": {
|
|
119
|
-
"v-model": "formData.startTime",
|
|
120
|
-
"label": "Gio bat dau",
|
|
121
|
-
":required": false,
|
|
122
|
-
":time-add": 30
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
## f-time-counter
|
|
128
|
-
|
|
129
|
-
- Props (source): `value`, `type`, `labelFormat`, `format`
|
|
130
|
-
- `value`: seconds (Number) or datetime string (`"yyyy-MM-dd HH:mm:ss"`)
|
|
131
|
-
- `type`: `0` countdown with full days/hours/minutes/seconds · `1` countdown showing total remaining seconds only · `2` real-time clock (current time)
|
|
132
|
-
- `format`: `{ label, day, hour, minute, second }` — unit labels
|
|
133
|
-
- Emit: `input` (remaining seconds) when counting from seconds (Number value); `time-end` when timer hits 0
|
|
134
|
-
|
|
135
|
-
```json
|
|
136
|
-
{
|
|
137
|
-
"el": "f-time-counter",
|
|
138
|
-
"w": "4",
|
|
139
|
-
"attr": {
|
|
140
|
-
":value": 300,
|
|
141
|
-
":type": 0,
|
|
142
|
-
":format": { "label": "Còn lại: ", "day": " ngày", "hour": " giờ", "minute": " phút", "second": " giây" }
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
## f-qrcode
|
|
148
|
-
|
|
149
|
-
- Props (source): `value`, `logo`, `color`, `size`
|
|
150
|
-
- Renders a QR image from value
|
|
151
|
-
|
|
152
|
-
```json
|
|
153
|
-
{
|
|
154
|
-
"el": "f-qrcode",
|
|
155
|
-
"w": "4",
|
|
156
|
-
"attr": {
|
|
157
|
-
"v-model": "formData.qrText",
|
|
158
|
-
":size": 200,
|
|
159
|
-
"color": "#1e90ff"
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
## f-qrcode-reader
|
|
165
|
-
|
|
166
|
-
- Props (source): `value`, `label`, `width`
|
|
167
|
-
- Emit: `input` (closes dialog), `update` (decoded content)
|
|
168
|
-
|
|
169
|
-
```json
|
|
170
|
-
{
|
|
171
|
-
"el": "f-qrcode-reader",
|
|
172
|
-
"w": "12",
|
|
173
|
-
"attr": {
|
|
174
|
-
"v-model": "scanDialog",
|
|
175
|
-
"label": "Doc ma QR",
|
|
176
|
-
"v-on:update": "CALL(vueData.handleScanResult, { item: $event })"
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
## f-image-update
|
|
182
|
-
|
|
183
|
-
- Props (source): `value`, `label`, `title`, `apiUpload`, `iconText`, `imageType`, `size`, `quality`, `dialogWidth`, `dialogHeight`, `imageBoxAttr`, `imageAttr`, `buttonAttr`, `croperAttr`
|
|
184
|
-
- Emit: `update` after successful upload (`{ returnData, imageData }`)
|
|
185
|
-
|
|
186
|
-
```json
|
|
187
|
-
{
|
|
188
|
-
"el": "f-image-update",
|
|
189
|
-
"w": "4",
|
|
190
|
-
"attr": {
|
|
191
|
-
"v-model": "formData.avatar",
|
|
192
|
-
"label": "Anh dai dien",
|
|
193
|
-
"api-upload": "/api/upload/avatar",
|
|
194
|
-
":dialog-width": 600,
|
|
195
|
-
":dialog-height": 420
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
## f-file-upload
|
|
201
|
-
|
|
202
|
-
- Props (source): `label`, `url`, `autoclose`, `iconText`, `filters`, `resize`, `onclose`
|
|
203
|
-
- Emit: `input` on upload complete
|
|
204
|
-
- Uses Plupload; uploads on file select; runs `onclose` action when the dialog closes
|
|
205
|
-
|
|
206
|
-
```json
|
|
207
|
-
{
|
|
208
|
-
"el": "f-file-upload",
|
|
209
|
-
"w": "4",
|
|
210
|
-
"attr": {
|
|
211
|
-
"label": "Tai tep",
|
|
212
|
-
"url": "/api/upload/file",
|
|
213
|
-
":filters": {
|
|
214
|
-
"max_file_size": "20mb",
|
|
215
|
-
"mime_types": [{ "title": "PDF", "extensions": "pdf" }]
|
|
216
|
-
},
|
|
217
|
-
":onclose": { "CALL": "handleUploadClosed" }
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
## f-excel-reader
|
|
223
|
-
|
|
224
|
-
Button: reads Excel/CSV → `v-model` (array of objects, or of arrays per `:header`). XLSX lazy-loads on first pick; **no import** in module.json.
|
|
225
|
-
|
|
226
|
-
**Outputs:**
|
|
227
|
-
|
|
228
|
-
- `v-model` — parsed data array
|
|
229
|
-
- `load` — metadata `{ rows, rowCount, headers, sheetName, sheetNames, fileName }`
|
|
230
|
-
- `error` — `{ message, fileName }` when file too large, read fails, or XLSX load times out
|
|
231
|
-
- `action-error` — `{ message, fileName }` when the action callback throws (Excel data still valid)
|
|
232
|
-
|
|
233
|
-
**Props — display:**
|
|
234
|
-
|
|
235
|
-
| Prop | Default | Description |
|
|
236
|
-
| ----------- | ------------------------------------------------------------ | ------------------------------ |
|
|
237
|
-
| `label` | `'Load Excel'` | Button label |
|
|
238
|
-
| `icon-text` | `'mdi-grid'` | MDI icon; `null`/`''` hides it |
|
|
239
|
-
| `accept` | `.xlsx,.xlsb,.xlsm,.xls,.csv,.txt,.ods,.dbf,.prn,.html,.htm` | Accepted file types |
|
|
240
|
-
|
|
241
|
-
**Props — reading:**
|
|
242
|
-
|
|
243
|
-
| Prop | Default | Description |
|
|
244
|
-
| ------------- | -------------- | ------------------------------------------------------------- |
|
|
245
|
-
| `:header` | `null` | Decides the object keys in the result (table below) |
|
|
246
|
-
| `:raw` | `false` | `false` = parse numbers and dates; `true` = keep raw strings |
|
|
247
|
-
| `date-format` | `'dd/mm/yyyy'` | Date format when `:raw="false"` |
|
|
248
|
-
| `:defval` | `''` | Value for empty cells |
|
|
249
|
-
| `:sheet` | `0` | Sheet to read — number (0-based index) or sheet name (string) |
|
|
250
|
-
| `:skip-rows` | `0` | Skip N leading rows before header/data |
|
|
251
|
-
|
|
252
|
-
**`:header` values:**
|
|
253
|
-
|
|
254
|
-
| Value | Result |
|
|
255
|
-
| ------------------ | ------------------------------------------------------------- |
|
|
256
|
-
| `null` _(default)_ | First sheet row as keys: `[{MSSV:"1234", HoTen:"An"}]` |
|
|
257
|
-
| `1` | Array of arrays, no keys: `[["MSSV","HoTen"],["1234","An"]]` |
|
|
258
|
-
| `"A"` | Excel column letters as keys: `[{A:"MSSV", B:"HoTen"}]` |
|
|
259
|
-
| `["ma","ten"]` | Your own key names by column order (file header row not used) |
|
|
260
|
-
|
|
261
|
-
⚠️ **`:sheet` trap — wrong name does NOT error:**
|
|
262
|
-
|
|
263
|
-
- Nonexistent **name** → **silently reads the first sheet** (wrong data, no sign).
|
|
264
|
-
- **Number** beyond sheet count → `Không tìm thấy sheet: N` via `error`.
|
|
265
|
-
|
|
266
|
-
With a name, compare `load`.`sheetName` to the expected name.
|
|
267
|
-
|
|
268
|
-
**Props — limits & timeout:**
|
|
269
|
-
|
|
270
|
-
| Prop | Default | Description |
|
|
271
|
-
| --------------- | ------- | ------------------------------------------------------------- |
|
|
272
|
-
| `:max-size` | `20` | File size limit, **in MB** (name has no "MB"); `0` = no limit |
|
|
273
|
-
| `:load-timeout` | `15000` | XLSX library load timeout (ms) |
|
|
274
|
-
|
|
275
|
-
Unparseable `:max-size` → **20 MB**, not unlimited; no limit = exactly number `0`.
|
|
276
|
-
|
|
277
|
-
**Prop — callback:**
|
|
278
|
-
|
|
279
|
-
| Prop | Description |
|
|
280
|
-
| --------- | ------------------------------------------------------------------------------------------------ |
|
|
281
|
-
| `:action` | Action object run after parsing, once `v-model` has the data — usually `{ "CALL": "tenAction" }` |
|
|
282
|
-
|
|
283
|
-
**Error boundary — reading and action are separate stages:**
|
|
284
|
-
|
|
285
|
-
- `action` runs after `v-model` has the data (`$nextTick`); read `vueData.excelRows` directly.
|
|
286
|
-
- After reading, **nothing becomes `error`** — action errors → `action-error`, parsed data kept, no rollback. `error` ⇒ reading failed; `action-error` ⇒ processing failed.
|
|
287
|
-
- Clicks while parsing are ignored; a new file picked before the old finishes discards the old result (never overwrites the new).
|
|
288
|
-
|
|
289
|
-
**Basic — header from first row (default):**
|
|
290
|
-
|
|
291
|
-
```json
|
|
292
|
-
{
|
|
293
|
-
"el": "f-excel-reader",
|
|
294
|
-
"w": "4",
|
|
295
|
-
"attr": {
|
|
296
|
-
"label": "Nhập từ Excel",
|
|
297
|
-
"icon-text": "mdi-microsoft-excel",
|
|
298
|
-
":action": { "CALL": "cmdImportExcel" },
|
|
299
|
-
"v-model": "excelRows"
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
**Sheet by name, skip 2 rows, keep raw strings:**
|
|
305
|
-
|
|
306
|
-
```json
|
|
307
|
-
{
|
|
308
|
-
"el": "f-excel-reader",
|
|
309
|
-
"w": "4",
|
|
310
|
-
"attr": {
|
|
311
|
-
"label": "Nhập DanhSach",
|
|
312
|
-
":sheet": "DanhSach",
|
|
313
|
-
":skip-rows": 2,
|
|
314
|
-
":raw": true,
|
|
315
|
-
":action": { "CALL": "cmdImportExcel" },
|
|
316
|
-
"v-model": "excelRows"
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
**Custom column names, 5 MB limit:**
|
|
322
|
-
|
|
323
|
-
```json
|
|
324
|
-
{
|
|
325
|
-
"el": "f-excel-reader",
|
|
326
|
-
"w": "4",
|
|
327
|
-
"attr": {
|
|
328
|
-
"label": "Nhập Excel",
|
|
329
|
-
":header": ["mssv", "hoTen", "diemTB"],
|
|
330
|
-
":max-size": 5,
|
|
331
|
-
":action": { "CALL": "cmdImportExcel" },
|
|
332
|
-
"v-model": "excelRows"
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
```
|
|
1
|
+
# FUI Input & Interaction Components
|
|
2
|
+
|
|
3
|
+
> Owns: **input components: `f-date`, `f-time`, `f-search`, `f-radiobox`, `f-button`, `f-menu`, upload**.
|
|
4
|
+
|
|
5
|
+
Components for input and user interaction. Prefix/binding rules: [components-display.md](components-display.md).
|
|
6
|
+
|
|
7
|
+
## f-radiobox
|
|
8
|
+
|
|
9
|
+
- Props (source): `label`, `items`, `itemValue`, `itemText`
|
|
10
|
+
- Emit: `input` on change
|
|
11
|
+
|
|
12
|
+
```json
|
|
13
|
+
{
|
|
14
|
+
"el": "f-radiobox",
|
|
15
|
+
"w": "6",
|
|
16
|
+
"attr": {
|
|
17
|
+
"label": "Loai",
|
|
18
|
+
"v-model": "formData.loai",
|
|
19
|
+
":items": "loaiOptions",
|
|
20
|
+
"item-value": "value",
|
|
21
|
+
"item-text": "text"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## f-menu
|
|
27
|
+
|
|
28
|
+
- Props (source): `items`, `label`, `iconText`, `menuAttr`
|
|
29
|
+
- Dropdown button with menu items; `label` empty/undefined → icon-only button
|
|
30
|
+
- `items`: array of `{ text, visible, color, "icon-text", action, href, target }`
|
|
31
|
+
- `action`: FUI action object, run via `runAction`
|
|
32
|
+
- `href` + `target`: navigate like `<a>`
|
|
33
|
+
- `visible`: show/hide item (default `true`)
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"el": "f-menu",
|
|
38
|
+
"w": "3",
|
|
39
|
+
"attr": {
|
|
40
|
+
"label": "Tac vu",
|
|
41
|
+
"icon-text": "mdi-dots-vertical",
|
|
42
|
+
":items": [
|
|
43
|
+
{ "text": "Xem chi tiết", "icon-text": "mdi-eye", "action": { "CALL": "vueData.viewDetail" } },
|
|
44
|
+
{ "text": "Xuất Excel", "icon-text": "mdi-file-excel", "action": { "CALL": "vueData.exportExcel" } },
|
|
45
|
+
{ "text": "Trang chủ", "icon-text": "mdi-home", "href": "/", "target": "_blank" }
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## f-search
|
|
52
|
+
|
|
53
|
+
- Props (source): `api`, `apiData`, `items`
|
|
54
|
+
- Emit: `input` when selection changes
|
|
55
|
+
- Debounced API search (500ms); keywords < 2 chars ignored
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"el": "f-search",
|
|
60
|
+
"w": "6",
|
|
61
|
+
"attr": {
|
|
62
|
+
"v-model": "filter.userID",
|
|
63
|
+
"api": "/api/user/search",
|
|
64
|
+
":api-data": { "Keyword": "TEXT", "GroupID": "vueData.groupID" },
|
|
65
|
+
"item-text": "UserName",
|
|
66
|
+
"item-value": "UserID"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## f-button
|
|
72
|
+
|
|
73
|
+
- Props (source): `label`, `checkvalid`, `ctrlhotkey`, `iconText`, `action`, `includeData`
|
|
74
|
+
- Debounced click (500ms, leading); validates form when `checkvalid=true`
|
|
75
|
+
- `$attrs.target == 'dialog'` → opens a window from attrs (`wid`, `title`, `url`, `onclose`)
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"el": "f-button",
|
|
80
|
+
"w": "3",
|
|
81
|
+
"attr": {
|
|
82
|
+
"label": "Luu",
|
|
83
|
+
"color": "primary",
|
|
84
|
+
":action": { "CALL": "handleSubmit" }
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## f-date
|
|
90
|
+
|
|
91
|
+
- Props (source): `value`, `label`, `dateAdd`, `required`
|
|
92
|
+
- Emit: `input` with normalized date (or `null`)
|
|
93
|
+
- Typed mask + picker; accepts many input formats on init; `dateAdd` sets a date relative to today
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"el": "f-date",
|
|
98
|
+
"w": "6",
|
|
99
|
+
"attr": {
|
|
100
|
+
"v-model": "filter.fromDate",
|
|
101
|
+
"label": "Tu ngay",
|
|
102
|
+
":required": false,
|
|
103
|
+
":date-add": -7
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## f-time
|
|
109
|
+
|
|
110
|
+
- Props (source): `value`, `label`, `timeAdd`, `required`
|
|
111
|
+
- Emit: `input` with `HH:mm` (or `null`)
|
|
112
|
+
- Typed mask + time picker
|
|
113
|
+
|
|
114
|
+
```json
|
|
115
|
+
{
|
|
116
|
+
"el": "f-time",
|
|
117
|
+
"w": "6",
|
|
118
|
+
"attr": {
|
|
119
|
+
"v-model": "formData.startTime",
|
|
120
|
+
"label": "Gio bat dau",
|
|
121
|
+
":required": false,
|
|
122
|
+
":time-add": 30
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## f-time-counter
|
|
128
|
+
|
|
129
|
+
- Props (source): `value`, `type`, `labelFormat`, `format`
|
|
130
|
+
- `value`: seconds (Number) or datetime string (`"yyyy-MM-dd HH:mm:ss"`)
|
|
131
|
+
- `type`: `0` countdown with full days/hours/minutes/seconds · `1` countdown showing total remaining seconds only · `2` real-time clock (current time)
|
|
132
|
+
- `format`: `{ label, day, hour, minute, second }` — unit labels
|
|
133
|
+
- Emit: `input` (remaining seconds) when counting from seconds (Number value); `time-end` when timer hits 0
|
|
134
|
+
|
|
135
|
+
```json
|
|
136
|
+
{
|
|
137
|
+
"el": "f-time-counter",
|
|
138
|
+
"w": "4",
|
|
139
|
+
"attr": {
|
|
140
|
+
":value": 300,
|
|
141
|
+
":type": 0,
|
|
142
|
+
":format": { "label": "Còn lại: ", "day": " ngày", "hour": " giờ", "minute": " phút", "second": " giây" }
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## f-qrcode
|
|
148
|
+
|
|
149
|
+
- Props (source): `value`, `logo`, `color`, `size`
|
|
150
|
+
- Renders a QR image from value
|
|
151
|
+
|
|
152
|
+
```json
|
|
153
|
+
{
|
|
154
|
+
"el": "f-qrcode",
|
|
155
|
+
"w": "4",
|
|
156
|
+
"attr": {
|
|
157
|
+
"v-model": "formData.qrText",
|
|
158
|
+
":size": 200,
|
|
159
|
+
"color": "#1e90ff"
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## f-qrcode-reader
|
|
165
|
+
|
|
166
|
+
- Props (source): `value`, `label`, `width`
|
|
167
|
+
- Emit: `input` (closes dialog), `update` (decoded content)
|
|
168
|
+
|
|
169
|
+
```json
|
|
170
|
+
{
|
|
171
|
+
"el": "f-qrcode-reader",
|
|
172
|
+
"w": "12",
|
|
173
|
+
"attr": {
|
|
174
|
+
"v-model": "scanDialog",
|
|
175
|
+
"label": "Doc ma QR",
|
|
176
|
+
"v-on:update": "CALL(vueData.handleScanResult, { item: $event })"
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## f-image-update
|
|
182
|
+
|
|
183
|
+
- Props (source): `value`, `label`, `title`, `apiUpload`, `iconText`, `imageType`, `size`, `quality`, `dialogWidth`, `dialogHeight`, `imageBoxAttr`, `imageAttr`, `buttonAttr`, `croperAttr`
|
|
184
|
+
- Emit: `update` after successful upload (`{ returnData, imageData }`)
|
|
185
|
+
|
|
186
|
+
```json
|
|
187
|
+
{
|
|
188
|
+
"el": "f-image-update",
|
|
189
|
+
"w": "4",
|
|
190
|
+
"attr": {
|
|
191
|
+
"v-model": "formData.avatar",
|
|
192
|
+
"label": "Anh dai dien",
|
|
193
|
+
"api-upload": "/api/upload/avatar",
|
|
194
|
+
":dialog-width": 600,
|
|
195
|
+
":dialog-height": 420
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## f-file-upload
|
|
201
|
+
|
|
202
|
+
- Props (source): `label`, `url`, `autoclose`, `iconText`, `filters`, `resize`, `onclose`
|
|
203
|
+
- Emit: `input` on upload complete
|
|
204
|
+
- Uses Plupload; uploads on file select; runs `onclose` action when the dialog closes
|
|
205
|
+
|
|
206
|
+
```json
|
|
207
|
+
{
|
|
208
|
+
"el": "f-file-upload",
|
|
209
|
+
"w": "4",
|
|
210
|
+
"attr": {
|
|
211
|
+
"label": "Tai tep",
|
|
212
|
+
"url": "/api/upload/file",
|
|
213
|
+
":filters": {
|
|
214
|
+
"max_file_size": "20mb",
|
|
215
|
+
"mime_types": [{ "title": "PDF", "extensions": "pdf" }]
|
|
216
|
+
},
|
|
217
|
+
":onclose": { "CALL": "handleUploadClosed" }
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## f-excel-reader
|
|
223
|
+
|
|
224
|
+
Button: reads Excel/CSV → `v-model` (array of objects, or of arrays per `:header`). XLSX lazy-loads on first pick; **no import** in module.json.
|
|
225
|
+
|
|
226
|
+
**Outputs:**
|
|
227
|
+
|
|
228
|
+
- `v-model` — parsed data array
|
|
229
|
+
- `load` — metadata `{ rows, rowCount, headers, sheetName, sheetNames, fileName }`
|
|
230
|
+
- `error` — `{ message, fileName }` when file too large, read fails, or XLSX load times out
|
|
231
|
+
- `action-error` — `{ message, fileName }` when the action callback throws (Excel data still valid)
|
|
232
|
+
|
|
233
|
+
**Props — display:**
|
|
234
|
+
|
|
235
|
+
| Prop | Default | Description |
|
|
236
|
+
| ----------- | ------------------------------------------------------------ | ------------------------------ |
|
|
237
|
+
| `label` | `'Load Excel'` | Button label |
|
|
238
|
+
| `icon-text` | `'mdi-grid'` | MDI icon; `null`/`''` hides it |
|
|
239
|
+
| `accept` | `.xlsx,.xlsb,.xlsm,.xls,.csv,.txt,.ods,.dbf,.prn,.html,.htm` | Accepted file types |
|
|
240
|
+
|
|
241
|
+
**Props — reading:**
|
|
242
|
+
|
|
243
|
+
| Prop | Default | Description |
|
|
244
|
+
| ------------- | -------------- | ------------------------------------------------------------- |
|
|
245
|
+
| `:header` | `null` | Decides the object keys in the result (table below) |
|
|
246
|
+
| `:raw` | `false` | `false` = parse numbers and dates; `true` = keep raw strings |
|
|
247
|
+
| `date-format` | `'dd/mm/yyyy'` | Date format when `:raw="false"` |
|
|
248
|
+
| `:defval` | `''` | Value for empty cells |
|
|
249
|
+
| `:sheet` | `0` | Sheet to read — number (0-based index) or sheet name (string) |
|
|
250
|
+
| `:skip-rows` | `0` | Skip N leading rows before header/data |
|
|
251
|
+
|
|
252
|
+
**`:header` values:**
|
|
253
|
+
|
|
254
|
+
| Value | Result |
|
|
255
|
+
| ------------------ | ------------------------------------------------------------- |
|
|
256
|
+
| `null` _(default)_ | First sheet row as keys: `[{MSSV:"1234", HoTen:"An"}]` |
|
|
257
|
+
| `1` | Array of arrays, no keys: `[["MSSV","HoTen"],["1234","An"]]` |
|
|
258
|
+
| `"A"` | Excel column letters as keys: `[{A:"MSSV", B:"HoTen"}]` |
|
|
259
|
+
| `["ma","ten"]` | Your own key names by column order (file header row not used) |
|
|
260
|
+
|
|
261
|
+
⚠️ **`:sheet` trap — wrong name does NOT error:**
|
|
262
|
+
|
|
263
|
+
- Nonexistent **name** → **silently reads the first sheet** (wrong data, no sign).
|
|
264
|
+
- **Number** beyond sheet count → `Không tìm thấy sheet: N` via `error`.
|
|
265
|
+
|
|
266
|
+
With a name, compare `load`.`sheetName` to the expected name.
|
|
267
|
+
|
|
268
|
+
**Props — limits & timeout:**
|
|
269
|
+
|
|
270
|
+
| Prop | Default | Description |
|
|
271
|
+
| --------------- | ------- | ------------------------------------------------------------- |
|
|
272
|
+
| `:max-size` | `20` | File size limit, **in MB** (name has no "MB"); `0` = no limit |
|
|
273
|
+
| `:load-timeout` | `15000` | XLSX library load timeout (ms) |
|
|
274
|
+
|
|
275
|
+
Unparseable `:max-size` → **20 MB**, not unlimited; no limit = exactly number `0`.
|
|
276
|
+
|
|
277
|
+
**Prop — callback:**
|
|
278
|
+
|
|
279
|
+
| Prop | Description |
|
|
280
|
+
| --------- | ------------------------------------------------------------------------------------------------ |
|
|
281
|
+
| `:action` | Action object run after parsing, once `v-model` has the data — usually `{ "CALL": "tenAction" }` |
|
|
282
|
+
|
|
283
|
+
**Error boundary — reading and action are separate stages:**
|
|
284
|
+
|
|
285
|
+
- `action` runs after `v-model` has the data (`$nextTick`); read `vueData.excelRows` directly.
|
|
286
|
+
- After reading, **nothing becomes `error`** — action errors → `action-error`, parsed data kept, no rollback. `error` ⇒ reading failed; `action-error` ⇒ processing failed.
|
|
287
|
+
- Clicks while parsing are ignored; a new file picked before the old finishes discards the old result (never overwrites the new).
|
|
288
|
+
|
|
289
|
+
**Basic — header from first row (default):**
|
|
290
|
+
|
|
291
|
+
```json
|
|
292
|
+
{
|
|
293
|
+
"el": "f-excel-reader",
|
|
294
|
+
"w": "4",
|
|
295
|
+
"attr": {
|
|
296
|
+
"label": "Nhập từ Excel",
|
|
297
|
+
"icon-text": "mdi-microsoft-excel",
|
|
298
|
+
":action": { "CALL": "cmdImportExcel" },
|
|
299
|
+
"v-model": "excelRows"
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
**Sheet by name, skip 2 rows, keep raw strings:**
|
|
305
|
+
|
|
306
|
+
```json
|
|
307
|
+
{
|
|
308
|
+
"el": "f-excel-reader",
|
|
309
|
+
"w": "4",
|
|
310
|
+
"attr": {
|
|
311
|
+
"label": "Nhập DanhSach",
|
|
312
|
+
":sheet": "DanhSach",
|
|
313
|
+
":skip-rows": 2,
|
|
314
|
+
":raw": true,
|
|
315
|
+
":action": { "CALL": "cmdImportExcel" },
|
|
316
|
+
"v-model": "excelRows"
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
**Custom column names, 5 MB limit:**
|
|
322
|
+
|
|
323
|
+
```json
|
|
324
|
+
{
|
|
325
|
+
"el": "f-excel-reader",
|
|
326
|
+
"w": "4",
|
|
327
|
+
"attr": {
|
|
328
|
+
"label": "Nhập Excel",
|
|
329
|
+
":header": ["mssv", "hoTen", "diemTB"],
|
|
330
|
+
":max-size": 5,
|
|
331
|
+
":action": { "CALL": "cmdImportExcel" },
|
|
332
|
+
"v-model": "excelRows"
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
```
|