@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.
- package/README.md +13 -1
- 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,973 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "Module Patterns Reference — Tổng hợp các pattern UI nâng cao từ thực tế. Dùng làm tài liệu tham chiếu khi thiết kế module có bảng dữ liệu phức tạp, nhiều dialog, phân quyền inline, và luồng điều kiện.",
|
|
3
|
+
"_patterns": {
|
|
4
|
+
"data_init": [
|
|
5
|
+
"State + action khai báo trong cùng object data[0]",
|
|
6
|
+
"Immediate CALL khi load: {CALL: 'actionName'} đứng độc lập cuối data array",
|
|
7
|
+
"Multi CALL khi load: nhiều {CALL} riêng biệt",
|
|
8
|
+
"menuChucNang array trong data — định nghĩa menu hành động dùng chung cho t-menu",
|
|
9
|
+
"IF startup — auto CALL hoặc openWindow khi URL params thỏa điều kiện (typeof(x)!='undefined'). Đặt ĐẦU data[] để chạy trước CALL",
|
|
10
|
+
"_.find() trong CALLBACK/state — \"[_.find(ds, {ID: sID})]\" để set selected item từ array sau khi load",
|
|
11
|
+
"Named column arrays — định nghĩa tableHeaders + tableDB riêng trong data[], dùng IF/THEN ghép bằng spread '[...tableHeaders, ...tableDB]' để ẩn/hiện cột theo quyền. F-table bind ':headers': 'tableHeaders' (state ref thay vì inline array)"
|
|
12
|
+
],
|
|
13
|
+
"watch": [
|
|
14
|
+
"Watch 1 action: 'key': {CALL: 'action'}",
|
|
15
|
+
"Watch nhiều action: 'key': [{CALL: 'a1'}, {CALL: 'a2'}]",
|
|
16
|
+
"Watch set state + CALL: 'key': [{TypeLoad: 1, fTitle: '...'}, {CALL: 'getData'}]",
|
|
17
|
+
"Watch với EXE pushRouter — sync URL khi state thay đổi: {EXE: \"pushRouter(`/path?id=${id}`)\"}",
|
|
18
|
+
"Watch với IF/THEN/ELSE + EXE — conditional URL push theo giá trị mới của state"
|
|
19
|
+
],
|
|
20
|
+
"table_columns": [
|
|
21
|
+
"t-select — dropdown inline trong ô, có CONFIRM trước khi lưu",
|
|
22
|
+
"t-check — toggle boolean với action chain",
|
|
23
|
+
"t-button — nút trong ô, hỗ trợ :color/:icon-text/:label động từ item",
|
|
24
|
+
"t-link — text link trong ô: {el: 't-link', attr: {':url': 'item.pageUrl', 'target': '_blank'}}",
|
|
25
|
+
"t-menu — menu thả xuống liên kết menuChucNang",
|
|
26
|
+
"v-chip-group + v-chip — hiển thị mảng giá trị, click mở dialog",
|
|
27
|
+
"v-icon — icon có v-if điều kiện hoặc v-on:click để mở dialog",
|
|
28
|
+
"divider: true — đường kẻ dọc phân cách nhóm cột, đặt trên cột đầu tiên của nhóm mới",
|
|
29
|
+
":item-class — tô màu dòng: item => item.Flag>0 ? 'danger' : ''",
|
|
30
|
+
":show-select + v-model — chọn nhiều dòng vào mảng state",
|
|
31
|
+
"excel: true — nút xuất Excel",
|
|
32
|
+
"Named header array binding — ':headers': 'tableHeaders' (state reference) thay vì inline array khi cột cần thay đổi theo quyền. Định nghĩa tableHeaders trong data[], bind bằng tên biến"
|
|
33
|
+
],
|
|
34
|
+
"table_inline_crud": [
|
|
35
|
+
":update-form — v-if: dialogModel=='new' để ẩn/hiện field theo chế độ",
|
|
36
|
+
":update-api — {default-item, new, edit} — bỏ delete nếu không cần",
|
|
37
|
+
":disabled-update — khóa nút sửa theo quyền",
|
|
38
|
+
":update-form-attr — {width: N} tùy chỉnh kích thước dialog form inline",
|
|
39
|
+
"$row trong IN — 'jsonObj': '$row' truyền toàn bộ object row thay vì map từng field",
|
|
40
|
+
"default-item với giá trị từ state — 'Field': 'dsParent[0].Field' kế thừa context hiện tại",
|
|
41
|
+
":update-api với CHỈ default-item (không có new/edit/delete) — inject extra fields vào items client-side"
|
|
42
|
+
],
|
|
43
|
+
"callback_patterns": [
|
|
44
|
+
"CALLBACK object: {CALL: 'reload'}",
|
|
45
|
+
"CALLBACK array nhiều bước: [{CALL}, {reset state}, {MESS}]",
|
|
46
|
+
"CALLBACK JS expression: '{{ _.forEach(ds, x => x.F = JSON.parse(x.F)) }}'",
|
|
47
|
+
"Nested IF/THEN/ELSE trong CALLBACK — routing theo TypeLoad state",
|
|
48
|
+
"CALLBACK string 'location.reload()' — reload toàn trang (dùng khi cần re-init hoàn toàn)",
|
|
49
|
+
"CALLBACK set state: { 'sModuleID': 'dsModule[0].ModuleID' } — auto-select first item sau khi load",
|
|
50
|
+
"CALLBACK reset multi-state cùng lúc: { 'sSelected': [], 'dlDialog': false } — clear + close dialog"
|
|
51
|
+
],
|
|
52
|
+
"dialog_patterns": [
|
|
53
|
+
"hidden-container — wrapper cho dialog",
|
|
54
|
+
"addNew flag (0/1) — tạo mới vs cập nhật trong cùng dialog; :disabled: 'addNew==0'",
|
|
55
|
+
"v-switch — toggle hiện/ẩn field phụ",
|
|
56
|
+
"v-checkbox v-for — checkbox list động cho phân quyền hàng loạt",
|
|
57
|
+
"dual-toolbar dialog — v-toolbar thứ 2 làm filter bar trong dialog (thay vì v-card-text row riêng)",
|
|
58
|
+
"f-table trong dialog — show-select để chọn items, max-height thay vì :height"
|
|
59
|
+
],
|
|
60
|
+
"navigation": [
|
|
61
|
+
"FUN: openWindow — mở module khác trong floating window: {id, title, url}",
|
|
62
|
+
"openWindow button — custom button trong window header: {label, icon-text, ctrlhotkey: 83, action: {'#WinID': {CALL: 'cmdSAVE'}}}",
|
|
63
|
+
"openWindow onclose — action hoặc reset state khi đóng window: {onclose: {stateKey: null}}",
|
|
64
|
+
"f-button :href + target — nút link external: {':href': '`https://.../${id}`', 'target': '_blank'}",
|
|
65
|
+
"f-menu item href+target — menu item dạng external link thay vì action",
|
|
66
|
+
"f-menu inline :items — items có action/href/target định nghĩa trực tiếp trong attr (không qua menuChucNang)",
|
|
67
|
+
"TypeLoad — biến int kiểm soát chế độ hiển thị của cùng một bảng",
|
|
68
|
+
"row prop object — {'v-if': '...', 'class': '...'} để ẩn/hiện cả row theo điều kiện",
|
|
69
|
+
"pushRouter(url) — navigate URL bar từ EXE: \"pushRouter(`/path?pid=${pid}`)\""
|
|
70
|
+
]
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
"data": [
|
|
74
|
+
{
|
|
75
|
+
"TypeLoad": 1,
|
|
76
|
+
"fTableTitle": "",
|
|
77
|
+
"dsItems": [],
|
|
78
|
+
"dsCategory": [],
|
|
79
|
+
"dsRights": [],
|
|
80
|
+
"sItemSelected": [],
|
|
81
|
+
"sRightSelected": [],
|
|
82
|
+
"dongChon": null,
|
|
83
|
+
"dlInfo": false,
|
|
84
|
+
"dlRight": false,
|
|
85
|
+
"dlAssign": false,
|
|
86
|
+
"addNew": 0
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"_note": "menuChucNang — định nghĩa trong data, bind vào t-menu :items",
|
|
90
|
+
"menuChucNang": [
|
|
91
|
+
{
|
|
92
|
+
"color": "primary",
|
|
93
|
+
"icon-text": "mdi-account-details",
|
|
94
|
+
"text": "Cập nhật thông tin",
|
|
95
|
+
"action": [
|
|
96
|
+
{
|
|
97
|
+
"addNew": 0,
|
|
98
|
+
"dongChon": "item",
|
|
99
|
+
"sID": "item.ID",
|
|
100
|
+
"sName": "item.Name",
|
|
101
|
+
"sEmail": "item.Email"
|
|
102
|
+
},
|
|
103
|
+
{ "dlInfo": true }
|
|
104
|
+
]
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"color": "primary",
|
|
108
|
+
"icon-text": "mdi-shield-account-outline",
|
|
109
|
+
"text": "Phân quyền",
|
|
110
|
+
"action": [
|
|
111
|
+
{ "dongChon": "item", "sID": "item.ID" },
|
|
112
|
+
{
|
|
113
|
+
"_note": "FUN openWindow — mở module khác trong popup",
|
|
114
|
+
"FUN": "openWindow",
|
|
115
|
+
"IN": {
|
|
116
|
+
"id": "`WinPermission",
|
|
117
|
+
"title": "`Phân quyền",
|
|
118
|
+
"url": "`/permission?id={{item.ID}}"
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
]
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"color": "error",
|
|
125
|
+
"icon-text": "mdi-delete",
|
|
126
|
+
"text": "Xóa",
|
|
127
|
+
"action": [
|
|
128
|
+
{ "dongChon": "item" },
|
|
129
|
+
{
|
|
130
|
+
"CONFIRM": "Bạn có chắc muốn xóa <span style='color:red'>{{item.Name}}</span>?",
|
|
131
|
+
"API": "/EntityDelete",
|
|
132
|
+
"IN": { "ID": "dongChon.ID" },
|
|
133
|
+
"CALLBACK": [
|
|
134
|
+
{ "MESS": "Xóa thành công." },
|
|
135
|
+
{ "CALL": "getData" }
|
|
136
|
+
]
|
|
137
|
+
}
|
|
138
|
+
]
|
|
139
|
+
}
|
|
140
|
+
]
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"getData": {
|
|
144
|
+
"API": "/EntityList",
|
|
145
|
+
"IN": { "CategoryID": "sCategoryID", "Keyword": "sKeyword" },
|
|
146
|
+
"OUT": "dsItems"
|
|
147
|
+
},
|
|
148
|
+
"getCategory": {
|
|
149
|
+
"API": "/CategorySelectAll",
|
|
150
|
+
"OUT": "dsCategory",
|
|
151
|
+
"CALLBACK": { "sCategoryID": "dsCategory[0].CategoryID" }
|
|
152
|
+
},
|
|
153
|
+
"getRights": {
|
|
154
|
+
"API": "/RightSelectByID",
|
|
155
|
+
"IN": { "EntityID": "sID" },
|
|
156
|
+
"OUT": "dsRights"
|
|
157
|
+
},
|
|
158
|
+
"_note_typeload": "TypeLoad pattern — cùng OUT 'dsItems', switch API để đổi chế độ xem",
|
|
159
|
+
"getItemsDenied": {
|
|
160
|
+
"API": "/EntityDeniedList",
|
|
161
|
+
"OUT": "dsItems"
|
|
162
|
+
},
|
|
163
|
+
"getItemsInactive": {
|
|
164
|
+
"API": "/EntityInactiveList",
|
|
165
|
+
"OUT": "dsItems"
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"saveRights": {
|
|
170
|
+
"API": "/RightUpdate",
|
|
171
|
+
"IN": { "EntityID": "sID", "RightList": "sRightSelected" },
|
|
172
|
+
"CALLBACK": { "CALL": "getRights" }
|
|
173
|
+
},
|
|
174
|
+
"_note_bulk": "Thao tác hàng loạt — sItemSelected từ show-select",
|
|
175
|
+
"bulkDelete": {
|
|
176
|
+
"API": "/EntityBulkDelete",
|
|
177
|
+
"IN": { "IDList": "sItemSelected" },
|
|
178
|
+
"CALLBACK": [
|
|
179
|
+
{ "CALL": "getData" },
|
|
180
|
+
{ "sItemSelected": [] },
|
|
181
|
+
{ "MESS": "Đã xóa các mục đã chọn." }
|
|
182
|
+
]
|
|
183
|
+
},
|
|
184
|
+
"_note_callback_js": "CALLBACK JS — parse JSON từ server response bằng lodash",
|
|
185
|
+
"getItemsWithParsing": {
|
|
186
|
+
"API": "/EntityListWithJson",
|
|
187
|
+
"OUT": "dsItems",
|
|
188
|
+
"CALLBACK": "{{ _.forEach(dsItems, (x) => x.Functions = JSON.parse(x.Functions)) }}"
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
"_note_lodash_find": "_.find() trong CALLBACK — set selected item từ array sau khi load list",
|
|
193
|
+
"getWithFind": {
|
|
194
|
+
"API": "/EntityList",
|
|
195
|
+
"OUT": "dsItems",
|
|
196
|
+
"CALLBACK": {
|
|
197
|
+
"itemSelected": "[_.find(dsItems, { ItemID: selectedID })]"
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
"_note_named_headers": "Named column arrays — định nghĩa 2 mảng header riêng, ghép có điều kiện theo quyền",
|
|
203
|
+
"_rule": "Dùng khi một nhóm cột chỉ hiện với user có quyền nhất định. Tách thành tableHeaders (base) + tableExtra (admin-only), IF/THEN ghép bằng spread. F-table bind ':headers': 'tableHeaders' (state ref, không phải inline array)",
|
|
204
|
+
"tableHeaders": [
|
|
205
|
+
{ "text": "ID", "value": "ModuleID", "align": "center", "width": "80px" },
|
|
206
|
+
{ "text": "Tên module", "value": "ModuleName" },
|
|
207
|
+
{ "text": "API Name", "value": "APIName" },
|
|
208
|
+
{ "text": "Last Used", "value": "LastUsed", "align": "center" }
|
|
209
|
+
],
|
|
210
|
+
"tableDB": [
|
|
211
|
+
{
|
|
212
|
+
"_note": "Cột admin-only: DB config button",
|
|
213
|
+
"el": "t-button",
|
|
214
|
+
"text": "DB",
|
|
215
|
+
"value": "DBConnection",
|
|
216
|
+
"align": "center",
|
|
217
|
+
"attr": {
|
|
218
|
+
"color": "green",
|
|
219
|
+
"icon-text": "mdi-database-cog-outline",
|
|
220
|
+
":action": [
|
|
221
|
+
{ "dbSelected": "item" },
|
|
222
|
+
{ "dlModuleConnection": true }
|
|
223
|
+
]
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
{ "text": "Update", "value": "ctrl-update", "align": "center" }
|
|
227
|
+
]
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
"_note_conditional_merge": "IF/THEN merge arrays — spread syntax trong string expression. Chạy sau khi khai báo mảng, trước CALL",
|
|
231
|
+
"_spread_rule": "Giá trị '[...arr1, ...arr2]' là string nhưng FUI runtime eval như JS expression — tạo mảng mới gộp cả 2. Dùng cho: thêm cột, thêm menu items, gộp dataset",
|
|
232
|
+
"IF": "vueData.user.SystemRight==9",
|
|
233
|
+
"THEN": {
|
|
234
|
+
"tableHeaders": "[...tableHeaders, ...tableDB]"
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
"_note_startup_if": "IF startup — đặt ĐẦU data[] để chạy trước CALL. Auto openWindow khi URL params thỏa điều kiện",
|
|
239
|
+
"IF": "typeof(parentID) != 'undefined' && typeof(childID) != 'undefined'",
|
|
240
|
+
"THEN": {
|
|
241
|
+
"FUN": "openWindow",
|
|
242
|
+
"IN": {
|
|
243
|
+
"id": "`WinEditor",
|
|
244
|
+
"title": "`Chi tiết: {{parentID}} / {{childID}}",
|
|
245
|
+
"url": "`/editor?parentID={{parentID}}&childID={{childID}}",
|
|
246
|
+
"_note_button": "button: custom button trong window header. ctrlhotkey: 83 = Ctrl+S",
|
|
247
|
+
"button": {
|
|
248
|
+
"label": "Save",
|
|
249
|
+
"icon-text": "mdi-cloud-upload-outline",
|
|
250
|
+
"ctrlhotkey": 83,
|
|
251
|
+
"action": { "#WinEditor": { "CALL": "cmdSAVE" } }
|
|
252
|
+
},
|
|
253
|
+
"_note_onclose": "onclose: reset state về null khi đóng window",
|
|
254
|
+
"onclose": { "childID": null }
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
{ "CALL": "getCategory" },
|
|
259
|
+
{ "CALL": "getData" }
|
|
260
|
+
],
|
|
261
|
+
|
|
262
|
+
"watch": {
|
|
263
|
+
"sCategoryID": [
|
|
264
|
+
{ "TypeLoad": 1, "fTableTitle": "" },
|
|
265
|
+
{ "CALL": "getData" }
|
|
266
|
+
],
|
|
267
|
+
"sKeyword": { "CALL": "getData" },
|
|
268
|
+
"sID": { "CALL": "getRights" },
|
|
269
|
+
"_note_push_router": "pushRouter — sync URL bar khi state thay đổi (pid, mid là URL param được route)",
|
|
270
|
+
"pid": [
|
|
271
|
+
{ "CALL": "getItems" },
|
|
272
|
+
{ "EXE": "pushRouter(`/path?pid=${pid}`)" }
|
|
273
|
+
],
|
|
274
|
+
"_note_watch_if_exe": "Watch IF/THEN/ELSE + EXE — conditional URL push theo giá trị mới của state",
|
|
275
|
+
"childID": {
|
|
276
|
+
"IF": "_.isNull(childID) || _.isUndefined(childID)",
|
|
277
|
+
"THEN": { "EXE": "pushRouter(`/path?pid=${pid}`)" },
|
|
278
|
+
"ELSE": { "EXE": "pushRouter(`/path?pid=${pid}&childID=${childID}`)" }
|
|
279
|
+
}
|
|
280
|
+
},
|
|
281
|
+
|
|
282
|
+
"controls": [
|
|
283
|
+
{
|
|
284
|
+
"prop": "grid-list-md fluid",
|
|
285
|
+
"rows": [
|
|
286
|
+
{
|
|
287
|
+
"prop": "",
|
|
288
|
+
"cols": [
|
|
289
|
+
{
|
|
290
|
+
"el": "f-title",
|
|
291
|
+
"col": { "text-center": true },
|
|
292
|
+
"attr": { "label": "ENTITY MANAGER" }
|
|
293
|
+
}
|
|
294
|
+
]
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
"prop": "",
|
|
298
|
+
"cols": [
|
|
299
|
+
{
|
|
300
|
+
"el": "v-autocomplete",
|
|
301
|
+
"col": { "shrink": "true", "class": "shrink" },
|
|
302
|
+
"attr": {
|
|
303
|
+
"class": "flex-md-grow-0",
|
|
304
|
+
"v-model": "sCategoryID",
|
|
305
|
+
"label": "Danh mục",
|
|
306
|
+
":items": "dsCategory",
|
|
307
|
+
"item-text": "CategoryName",
|
|
308
|
+
"item-value": "CategoryID"
|
|
309
|
+
},
|
|
310
|
+
"w": "300"
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
"el": "v-text-field",
|
|
314
|
+
"attr": { "v-model": "sKeyword", "label": "Tìm kiếm", "clearable": "" },
|
|
315
|
+
"col": "shrink",
|
|
316
|
+
"w": "200"
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
"_note": "TypeLoad — nút switch chế độ xem",
|
|
320
|
+
"el": "f-button",
|
|
321
|
+
"col": { "shrink": true },
|
|
322
|
+
"attr": {
|
|
323
|
+
":checkvalid": false,
|
|
324
|
+
"color": "warning",
|
|
325
|
+
"label": "Bị từ chối",
|
|
326
|
+
"icon-text": "mdi-cancel",
|
|
327
|
+
":action": [
|
|
328
|
+
{ "TypeLoad": 2, "fTableTitle": "Danh sách bị từ chối" },
|
|
329
|
+
{ "CALL": "getItemsDenied" }
|
|
330
|
+
]
|
|
331
|
+
},
|
|
332
|
+
"w": ""
|
|
333
|
+
},
|
|
334
|
+
{ "el": "v-spacer", "attr": {} },
|
|
335
|
+
{
|
|
336
|
+
"_note": "Bulk action — thao tác trên sItemSelected",
|
|
337
|
+
"el": "f-button",
|
|
338
|
+
"col": { "v-if": "vueData.user.SystemRight>1", "shrink": true },
|
|
339
|
+
"attr": {
|
|
340
|
+
":checkvalid": false,
|
|
341
|
+
"color": "error",
|
|
342
|
+
"label": "Xóa đã chọn",
|
|
343
|
+
"icon-text": "mdi-delete-sweep",
|
|
344
|
+
":action": {
|
|
345
|
+
"CONFIRM": "Xóa tất cả các mục đã chọn?",
|
|
346
|
+
"CALL": "bulkDelete"
|
|
347
|
+
}
|
|
348
|
+
},
|
|
349
|
+
"w": ""
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
"el": "f-button",
|
|
353
|
+
"col": { "v-if": "vueData.user.SystemRight==9", "shrink": true },
|
|
354
|
+
"attr": {
|
|
355
|
+
":checkvalid": false,
|
|
356
|
+
"color": "primary",
|
|
357
|
+
"label": "Thêm mới",
|
|
358
|
+
"icon-text": "mdi-plus",
|
|
359
|
+
":action": [
|
|
360
|
+
{ "addNew": 1, "sID": "", "sName": "", "sEmail": "" },
|
|
361
|
+
{ "dlInfo": true }
|
|
362
|
+
]
|
|
363
|
+
},
|
|
364
|
+
"w": ""
|
|
365
|
+
}
|
|
366
|
+
]
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
"prop": "",
|
|
370
|
+
"cols": [
|
|
371
|
+
{
|
|
372
|
+
"el": "f-table",
|
|
373
|
+
"col": { "v-if": "vueData.user.SystemRight>1" },
|
|
374
|
+
"attr": {
|
|
375
|
+
"_note_label": "Dynamic label từ TypeLoad state",
|
|
376
|
+
":label": "`${fTableTitle=='' ? 'Danh sách' : fTableTitle}`",
|
|
377
|
+
":fixed-header": true,
|
|
378
|
+
":items-per-page": 50,
|
|
379
|
+
"_note_excel": "excel: true — nút xuất Excel",
|
|
380
|
+
"excel": true,
|
|
381
|
+
":show-select": true,
|
|
382
|
+
"v-model": "sItemSelected",
|
|
383
|
+
":items": "dsItems",
|
|
384
|
+
"item-key": "ID",
|
|
385
|
+
":item-class": "item => item.IsBlocked>0 ? 'danger' : ''",
|
|
386
|
+
":disabled-update": "vueData.user.SystemRight!=9",
|
|
387
|
+
":headers": [
|
|
388
|
+
{ "text": "ID", "value": "ID", "align": "center" },
|
|
389
|
+
{ "text": "Tên", "value": "Name" },
|
|
390
|
+
{
|
|
391
|
+
"_note": "t-check — toggle boolean với action chain",
|
|
392
|
+
"text": "Kích hoạt",
|
|
393
|
+
"value": "Enable",
|
|
394
|
+
"align": "center",
|
|
395
|
+
"el": "t-check",
|
|
396
|
+
"attr": {
|
|
397
|
+
"on-icon": "mdi-check",
|
|
398
|
+
"off-icon": "mdi-lock",
|
|
399
|
+
":action": [
|
|
400
|
+
{ "API": "/EntitySetActive", "IN": { "ID": "item.ID" } },
|
|
401
|
+
{ "dsItems": [] },
|
|
402
|
+
{ "CALL": "getData" }
|
|
403
|
+
]
|
|
404
|
+
}
|
|
405
|
+
},
|
|
406
|
+
{
|
|
407
|
+
"_note": "t-select — dropdown inline có CONFIRM",
|
|
408
|
+
"text": "Phân loại",
|
|
409
|
+
"value": "RightLevel",
|
|
410
|
+
"el": "t-select",
|
|
411
|
+
"width": "200px",
|
|
412
|
+
"attr": {
|
|
413
|
+
":items": "vueData.dsRights",
|
|
414
|
+
"item-text": "Note",
|
|
415
|
+
"item-value": "Level",
|
|
416
|
+
":action": [
|
|
417
|
+
{
|
|
418
|
+
"CONFIRM": "Xác nhận thay đổi phân loại?",
|
|
419
|
+
"API": "/EntityUpdateRight",
|
|
420
|
+
"IN": { "ID": "item.ID", "Level": "item.RightLevel" },
|
|
421
|
+
"CALLBACK": { "MESS": "Cập nhật thành công." }
|
|
422
|
+
}
|
|
423
|
+
]
|
|
424
|
+
}
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
"_note": "v-chip-group — hiển thị mảng tags, click mở dialog",
|
|
428
|
+
"text": "Chức năng",
|
|
429
|
+
"value": "Functions",
|
|
430
|
+
"el": "v-chip-group",
|
|
431
|
+
"attr": { "column": true, "v-if": "item.Functions" },
|
|
432
|
+
"innerHTML": [
|
|
433
|
+
{
|
|
434
|
+
"el": "v-chip",
|
|
435
|
+
"attr": {
|
|
436
|
+
":small": true,
|
|
437
|
+
"color": "primary",
|
|
438
|
+
"outlined": "",
|
|
439
|
+
"v-for": "fn in item.Functions",
|
|
440
|
+
":key": "fn.ID",
|
|
441
|
+
"v-on:click": "vueData.dlRight = true; CALL(loadRight(item));"
|
|
442
|
+
},
|
|
443
|
+
"innerHTML": "<div>{{fn.Code}}-{{fn.Name}}</div>"
|
|
444
|
+
}
|
|
445
|
+
]
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
"_note": "t-button — :color/:icon-text/:label động từ item data",
|
|
449
|
+
"text": "Sub-items",
|
|
450
|
+
"value": "SubCount",
|
|
451
|
+
"el": "t-button",
|
|
452
|
+
"align": "center",
|
|
453
|
+
"attr": {
|
|
454
|
+
":color": "(item.SubCount > 0 ? 'primary' : 'red')",
|
|
455
|
+
":icon-text": "`${item.SubCount > 0 ? 'mdi-check-bold' : 'mdi-lock'}`",
|
|
456
|
+
":label": "item.SubCount",
|
|
457
|
+
":action": [
|
|
458
|
+
{ "dongChon": "item", "sID": "item.ID" },
|
|
459
|
+
{ "CALL": "getRights" },
|
|
460
|
+
{ "dlAssign": true }
|
|
461
|
+
]
|
|
462
|
+
}
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
"_note": "v-icon — icon điều kiện",
|
|
466
|
+
"text": "Cảnh báo",
|
|
467
|
+
"value": "HasWarning",
|
|
468
|
+
"el": "v-icon",
|
|
469
|
+
"align": "center",
|
|
470
|
+
"attr": { "v-if": "item.HasWarning", "color": "red darken-2", "dense": "" },
|
|
471
|
+
"innerHTML": "mdi-alert-circle"
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
"_note": "t-link — text link trong cell, :url là expression, target là attr thường",
|
|
475
|
+
"el": "t-link",
|
|
476
|
+
"text": "Tên trang",
|
|
477
|
+
"value": "PageName",
|
|
478
|
+
"attr": {
|
|
479
|
+
":url": "item.pageUrl",
|
|
480
|
+
"target": "_blank"
|
|
481
|
+
}
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
"_note": "t-menu — bind menuChucNang từ data",
|
|
485
|
+
"el": "t-menu",
|
|
486
|
+
"width": "150px",
|
|
487
|
+
"align": "center",
|
|
488
|
+
"text": "Thao tác",
|
|
489
|
+
"value": "Actions",
|
|
490
|
+
"attr": { ":items": "vueData.menuChucNang" }
|
|
491
|
+
}
|
|
492
|
+
],
|
|
493
|
+
"_note_ufa": ":update-form-attr — {width: N} tùy chỉnh kích thước dialog form (mặc định auto)",
|
|
494
|
+
":update-form-attr": { "width": 700 },
|
|
495
|
+
"_note_inline_crud": "update-form/update-api — inline add+edit không cần dialog riêng",
|
|
496
|
+
":update-form": [
|
|
497
|
+
{
|
|
498
|
+
"el": "v-text-field",
|
|
499
|
+
"attr": {
|
|
500
|
+
"_note": "v-if: dialogModel=='new' — chỉ hiện khi tạo mới",
|
|
501
|
+
"v-if": "dialogModel=='new'",
|
|
502
|
+
"v-model": "ID",
|
|
503
|
+
"label": "Mã"
|
|
504
|
+
}
|
|
505
|
+
},
|
|
506
|
+
{ "el": "v-text-field", "attr": { "v-model": "Name", "label": "Tên" } },
|
|
507
|
+
{ "el": "v-text-field", "attr": { "v-model": "Email", "label": "Email", ":required": false } }
|
|
508
|
+
],
|
|
509
|
+
":update-api": {
|
|
510
|
+
"_note_default_item": "default-item: giá trị mặc định khi tạo mới. Có thể dùng state expression để kế thừa context",
|
|
511
|
+
"default-item": {
|
|
512
|
+
"ID": "",
|
|
513
|
+
"Name": "",
|
|
514
|
+
"Email": "",
|
|
515
|
+
"_note_from_state": "Ví dụ kế thừa từ state: 'CategoryID': 'sCategoryID', 'Framework': 'dsProject[0].Framework'"
|
|
516
|
+
},
|
|
517
|
+
"new": {
|
|
518
|
+
"API": "/EntityInsert",
|
|
519
|
+
"IN": { "ID": "item.ID", "Name": "item.Name", "Email": "item.Email" },
|
|
520
|
+
"CALLBACK": { "CALL": "getData" }
|
|
521
|
+
},
|
|
522
|
+
"edit": {
|
|
523
|
+
"_note_row": "$row trong IN — dùng '$row' để truyền toàn bộ row object thay vì map từng field",
|
|
524
|
+
"CONFIRM": "Xác nhận cập nhật?",
|
|
525
|
+
"API": "/EntityUpdate",
|
|
526
|
+
"IN": { "ID": "item.ID", "Name": "item.Name", "Email": "item.Email" },
|
|
527
|
+
"_note_row_alt": "Hoặc dùng: 'IN': { 'rowData': '$row' } — server nhận toàn bộ object",
|
|
528
|
+
"CALLBACK": { "CALL": "getData" }
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
},
|
|
532
|
+
"w": 12
|
|
533
|
+
}
|
|
534
|
+
]
|
|
535
|
+
}
|
|
536
|
+
]
|
|
537
|
+
},
|
|
538
|
+
|
|
539
|
+
{
|
|
540
|
+
"_note": "PATTERN: named headers — f-table bind ':headers' bằng state ref thay vì inline array. Cột được quyết định bởi IF/THEN trong data[]",
|
|
541
|
+
"prop": "grid-list-md fluid",
|
|
542
|
+
"rows": [
|
|
543
|
+
{
|
|
544
|
+
"prop": "",
|
|
545
|
+
"cols": [
|
|
546
|
+
{
|
|
547
|
+
"el": "f-table",
|
|
548
|
+
"attr": {
|
|
549
|
+
"label": "Modules list",
|
|
550
|
+
"item-key": "ModuleID",
|
|
551
|
+
":items": "dsModule",
|
|
552
|
+
":items-per-page": -1,
|
|
553
|
+
":hide-default-footer": true,
|
|
554
|
+
":disabled-update": "vueData.user.SystemRight!=9",
|
|
555
|
+
"_note_headers": "':headers' nhận state variable (string), không phải inline array — FUI bind vào tableHeaders đã được ghép trong data[]",
|
|
556
|
+
":headers": "tableHeaders",
|
|
557
|
+
":update-form": [
|
|
558
|
+
{ "el": "v-text-field", "attr": { "v-model": "ModuleName", "label": "Tên module" } }
|
|
559
|
+
],
|
|
560
|
+
":update-api": {
|
|
561
|
+
"default-item": { "ModuleName": "", "APIName": "" },
|
|
562
|
+
"edit": {
|
|
563
|
+
"API": "/ModuleUpdate",
|
|
564
|
+
"IN": { "ModuleID": "item.ModuleID", "ModuleName": "item.ModuleName" },
|
|
565
|
+
"CALLBACK": { "CALL": "getModule" }
|
|
566
|
+
},
|
|
567
|
+
"new": {
|
|
568
|
+
"API": "/ModuleInsert",
|
|
569
|
+
"IN": { "ModuleName": "item.ModuleName", "APIName": "item.APIName" },
|
|
570
|
+
"CALLBACK": { "CALL": "getModule" }
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
},
|
|
574
|
+
"w": 12
|
|
575
|
+
}
|
|
576
|
+
]
|
|
577
|
+
}
|
|
578
|
+
]
|
|
579
|
+
},
|
|
580
|
+
|
|
581
|
+
{
|
|
582
|
+
"_note": "PATTERN: advanced toolbar — row-prop-object, f-menu inline :items, f-button :href, openWindow+button",
|
|
583
|
+
"prop": "fluid grid-list-md",
|
|
584
|
+
"rows": [
|
|
585
|
+
{
|
|
586
|
+
"_note_row_prop_object": "Row prop dạng object — v-if ẩn cả row, class cho alignment",
|
|
587
|
+
"prop": { "v-if": "showAdvanced && itemSelected[0]", "class": "justify-end" },
|
|
588
|
+
"cols": [
|
|
589
|
+
{
|
|
590
|
+
"_note": "f-button external link — :href + target thay vì :action",
|
|
591
|
+
"el": "f-button",
|
|
592
|
+
"col": { "class": "shrink" },
|
|
593
|
+
"attr": {
|
|
594
|
+
"label": "CDN Files",
|
|
595
|
+
"icon-text": "mdi-cloud-arrow-up-outline",
|
|
596
|
+
"outlined": "",
|
|
597
|
+
"text": "",
|
|
598
|
+
":href": "`https://cdn.example.vn/?folder=${pid}`",
|
|
599
|
+
"target": "_blank"
|
|
600
|
+
}
|
|
601
|
+
},
|
|
602
|
+
{
|
|
603
|
+
"_note": "f-menu inline :items — items định nghĩa trực tiếp, không qua menuChucNang. Dùng khi menu chỉ xuất hiện ở 1 chỗ",
|
|
604
|
+
"el": "f-menu",
|
|
605
|
+
"col": { "class": "shrink" },
|
|
606
|
+
"attr": {
|
|
607
|
+
"label": "Cài đặt",
|
|
608
|
+
"icon-text": "mdi-cog",
|
|
609
|
+
":open-on-hover": true,
|
|
610
|
+
":items": [
|
|
611
|
+
{
|
|
612
|
+
"text": "`Import",
|
|
613
|
+
"icon-text": "`mdi-import",
|
|
614
|
+
"action": {
|
|
615
|
+
"FUN": "openWindow",
|
|
616
|
+
"IN": {
|
|
617
|
+
"id": "`WinImport",
|
|
618
|
+
"title": "`Import files",
|
|
619
|
+
"url": "`/import?pid={{pid}}"
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
},
|
|
623
|
+
{
|
|
624
|
+
"_note": "item dạng external link — href + target thay vì action",
|
|
625
|
+
"text": "`File CDN",
|
|
626
|
+
"icon-text": "`mdi-folder-multiple",
|
|
627
|
+
"href": "`https://cdn.example.vn/?folder={{pid}}",
|
|
628
|
+
"target": "`_blank"
|
|
629
|
+
},
|
|
630
|
+
{
|
|
631
|
+
"text": "`Chỉnh sửa cấu hình",
|
|
632
|
+
"icon-text": "`mdi-cog-outline",
|
|
633
|
+
"action": {
|
|
634
|
+
"FUN": "openWindow",
|
|
635
|
+
"IN": {
|
|
636
|
+
"id": "`WinSettings",
|
|
637
|
+
"title": "`Cài đặt: {{pid}}",
|
|
638
|
+
"url": "`/settings?pid={{pid}}",
|
|
639
|
+
"_note_button": "button: custom Save button trong window header, ctrlhotkey: 83 = Ctrl+S",
|
|
640
|
+
"button": {
|
|
641
|
+
"label": "Save",
|
|
642
|
+
"icon-text": "mdi-cloud-upload-outline",
|
|
643
|
+
"ctrlhotkey": 83,
|
|
644
|
+
"action": { "#WinSettings": { "CALL": "cmdSAVE" } }
|
|
645
|
+
},
|
|
646
|
+
"onclose": { "pid": null }
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
]
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
]
|
|
654
|
+
}
|
|
655
|
+
]
|
|
656
|
+
},
|
|
657
|
+
|
|
658
|
+
{
|
|
659
|
+
"_note": "DIALOG 1 — addNew flag pattern: tạo mới vs cập nhật trong cùng dialog",
|
|
660
|
+
"prop": "hidden-container",
|
|
661
|
+
"rows": [
|
|
662
|
+
{
|
|
663
|
+
"prop": "",
|
|
664
|
+
"cols": [
|
|
665
|
+
{
|
|
666
|
+
"el": "v-dialog",
|
|
667
|
+
"col": {},
|
|
668
|
+
"attr": { "v-model": "dlInfo", "width": 650 },
|
|
669
|
+
"innerHTML": [
|
|
670
|
+
{
|
|
671
|
+
"el": "v-card",
|
|
672
|
+
"attr": {},
|
|
673
|
+
"innerHTML": [
|
|
674
|
+
{
|
|
675
|
+
"el": "v-toolbar",
|
|
676
|
+
"attr": { "dense": "", "flat": "" },
|
|
677
|
+
"innerHTML": [
|
|
678
|
+
{ "el": "v-toolbar-title", "attr": {}, "innerHTML": "Thông tin chi tiết" },
|
|
679
|
+
{ "el": "v-spacer", "attr": {} },
|
|
680
|
+
{ "el": "v-btn", "innerHTML": "<v-icon>mdi-close</v-icon>", "attr": { "icon": "", "v-on:click": "vueData.dlInfo = false" } }
|
|
681
|
+
],
|
|
682
|
+
"w": 12
|
|
683
|
+
},
|
|
684
|
+
{
|
|
685
|
+
"el": "v-container",
|
|
686
|
+
"attr": { "class": "grid-list-md px-4" },
|
|
687
|
+
"innerHTML": [
|
|
688
|
+
{
|
|
689
|
+
"el": "v-layout", "w": 12, "attr": {},
|
|
690
|
+
"innerHTML": [
|
|
691
|
+
{ "el": "div", "attr": { "class": "mt-2" }, "innerHTML": "Mã", "w": 3 },
|
|
692
|
+
{
|
|
693
|
+
"el": "v-text-field", "col": {},
|
|
694
|
+
"attr": {
|
|
695
|
+
"class": "flex-md-grow-0 ml-2",
|
|
696
|
+
"_note": ":disabled khi edit (addNew==0), nhập được khi tạo mới (addNew==1)",
|
|
697
|
+
":disabled": "addNew==0",
|
|
698
|
+
"v-model": "sID",
|
|
699
|
+
":required": true
|
|
700
|
+
},
|
|
701
|
+
"w": 9
|
|
702
|
+
}
|
|
703
|
+
]
|
|
704
|
+
},
|
|
705
|
+
{
|
|
706
|
+
"el": "v-layout", "w": 12, "attr": {},
|
|
707
|
+
"innerHTML": [
|
|
708
|
+
{ "el": "div", "attr": { "class": "mt-2" }, "innerHTML": "Tên", "w": 3 },
|
|
709
|
+
{ "el": "v-text-field", "col": {}, "attr": { "class": "flex-md-grow-0 ml-2", "v-model": "sName", ":required": true }, "w": 9 }
|
|
710
|
+
]
|
|
711
|
+
},
|
|
712
|
+
{
|
|
713
|
+
"el": "v-layout", "w": 12, "attr": {},
|
|
714
|
+
"innerHTML": [
|
|
715
|
+
{
|
|
716
|
+
"_note": "v-switch — toggle hiện/ẩn field phụ",
|
|
717
|
+
"el": "v-switch", "col": {},
|
|
718
|
+
"attr": { "class": "flex-md-grow-0 ml-2", "v-model": "showExtra", "label": "Cài đặt thêm" },
|
|
719
|
+
"w": "12"
|
|
720
|
+
}
|
|
721
|
+
]
|
|
722
|
+
},
|
|
723
|
+
{
|
|
724
|
+
"el": "v-layout", "w": 12, "attr": { "v-if": "showExtra" },
|
|
725
|
+
"innerHTML": [
|
|
726
|
+
{ "el": "div", "attr": { "class": "mt-2" }, "innerHTML": "Ghi chú", "w": 3 },
|
|
727
|
+
{ "el": "v-textarea", "col": {}, "attr": { "class": "flex-md-grow-0 ml-2", "v-model": "sNote", ":rows": 2 }, "w": 9 }
|
|
728
|
+
]
|
|
729
|
+
}
|
|
730
|
+
]
|
|
731
|
+
},
|
|
732
|
+
{
|
|
733
|
+
"el": "v-card-actions", "w": 12, "attr": {},
|
|
734
|
+
"innerHTML": [
|
|
735
|
+
{ "el": "v-spacer", "attr": {} },
|
|
736
|
+
{
|
|
737
|
+
"el": "f-button",
|
|
738
|
+
"attr": {
|
|
739
|
+
":checkvalid": true, "color": "primary", "label": "Lưu", "icon-text": "mdi-content-save",
|
|
740
|
+
":action": {
|
|
741
|
+
"API": "/EntitySave",
|
|
742
|
+
"IN": { "Type": "addNew", "ID": "sID", "Name": "sName", "Note": "sNote" },
|
|
743
|
+
"CALLBACK": [
|
|
744
|
+
{ "MESS": "Lưu thành công." },
|
|
745
|
+
{ "dlInfo": false },
|
|
746
|
+
{ "CALL": "getData" }
|
|
747
|
+
]
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
]
|
|
752
|
+
}
|
|
753
|
+
]
|
|
754
|
+
}
|
|
755
|
+
],
|
|
756
|
+
"w": ""
|
|
757
|
+
}
|
|
758
|
+
]
|
|
759
|
+
}
|
|
760
|
+
]
|
|
761
|
+
},
|
|
762
|
+
|
|
763
|
+
{
|
|
764
|
+
"_note": "DIALOG 2 — v-checkbox v-for: checkbox list động cho phân quyền hàng loạt",
|
|
765
|
+
"prop": "hidden-container",
|
|
766
|
+
"rows": [
|
|
767
|
+
{
|
|
768
|
+
"prop": "",
|
|
769
|
+
"cols": [
|
|
770
|
+
{
|
|
771
|
+
"el": "v-dialog",
|
|
772
|
+
"col": {},
|
|
773
|
+
"attr": { "v-model": "dlRight", "width": 400 },
|
|
774
|
+
"innerHTML": [
|
|
775
|
+
{
|
|
776
|
+
"el": "v-card",
|
|
777
|
+
"attr": { "class": "pa-0" },
|
|
778
|
+
"innerHTML": [
|
|
779
|
+
{
|
|
780
|
+
"el": "v-toolbar",
|
|
781
|
+
"attr": { "dense": "", "flat": "" },
|
|
782
|
+
"innerHTML": [
|
|
783
|
+
{ "el": "v-toolbar-title", "attr": {}, "innerHTML": "Phân quyền chức năng" },
|
|
784
|
+
{ "el": "v-spacer", "attr": {} },
|
|
785
|
+
{ "el": "v-btn", "innerHTML": "<v-icon>mdi-close</v-icon>", "attr": { "icon": "", "v-on:click": "vueData.dlRight = false" } }
|
|
786
|
+
],
|
|
787
|
+
"w": 12
|
|
788
|
+
},
|
|
789
|
+
{
|
|
790
|
+
"el": "v-card-text", "attr": {},
|
|
791
|
+
"innerHTML": [
|
|
792
|
+
{
|
|
793
|
+
"el": "v-layout",
|
|
794
|
+
"attr": { "class": "fluid row justify-center grid-list-lg fill-height mt-3" },
|
|
795
|
+
"innerHTML": [
|
|
796
|
+
{
|
|
797
|
+
"_note": "v-checkbox v-for — v-model là mảng string ID, check = auto CALL saveRights",
|
|
798
|
+
"el": "div",
|
|
799
|
+
"attr": { "v-for": "r in vueData.dsRights", ":key": "r.ID" },
|
|
800
|
+
"innerHTML": [
|
|
801
|
+
{
|
|
802
|
+
"el": "v-checkbox",
|
|
803
|
+
"attr": {
|
|
804
|
+
"color": "primary",
|
|
805
|
+
"v-model": "sRightSelected",
|
|
806
|
+
":value": "`${r.ID}`",
|
|
807
|
+
":label": "`${r.Code} - ${r.Name}`",
|
|
808
|
+
"v-on:click": "CALL(saveRights)"
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
]
|
|
812
|
+
}
|
|
813
|
+
]
|
|
814
|
+
}
|
|
815
|
+
]
|
|
816
|
+
}
|
|
817
|
+
]
|
|
818
|
+
}
|
|
819
|
+
]
|
|
820
|
+
}
|
|
821
|
+
]
|
|
822
|
+
}
|
|
823
|
+
]
|
|
824
|
+
},
|
|
825
|
+
|
|
826
|
+
{
|
|
827
|
+
"_note": "DIALOG — dual-toolbar: v-toolbar thứ 2 làm filter bar trong dialog + f-table với show-select",
|
|
828
|
+
"prop": "hidden-container",
|
|
829
|
+
"rows": [{
|
|
830
|
+
"prop": "",
|
|
831
|
+
"cols": [{
|
|
832
|
+
"el": "v-dialog",
|
|
833
|
+
"col": {},
|
|
834
|
+
"attr": { "v-model": "dlAddUser", "width": 800, "max-height": 800, ":scrollable": true },
|
|
835
|
+
"innerHTML": [{
|
|
836
|
+
"el": "v-card",
|
|
837
|
+
"attr": { "class": "pa-0" },
|
|
838
|
+
"innerHTML": [
|
|
839
|
+
{
|
|
840
|
+
"el": "v-toolbar", "attr": { "dense": "", "flat": "" }, "w": 12,
|
|
841
|
+
"innerHTML": [
|
|
842
|
+
{ "el": "v-toolbar-title", "attr": {}, "innerHTML": "Thêm users" },
|
|
843
|
+
{ "el": "v-spacer" },
|
|
844
|
+
{ "el": "v-btn", "innerHTML": "<v-icon>mdi-close</v-icon>", "attr": { "icon": "", "v-on:click": "vueData.dlAddUser = false" } }
|
|
845
|
+
]
|
|
846
|
+
},
|
|
847
|
+
{
|
|
848
|
+
"_note": "v-toolbar thứ 2 — filter bar trong dialog",
|
|
849
|
+
"el": "v-toolbar", "attr": { "dense": "", "flat": "" }, "w": 12,
|
|
850
|
+
"innerHTML": [
|
|
851
|
+
{
|
|
852
|
+
"el": "v-select", "attr": {
|
|
853
|
+
"style": "width:300px", "v-model": "sDepartment",
|
|
854
|
+
"label": "Department", ":items": "dsDepartment",
|
|
855
|
+
"item-text": "DepartmentName", "item-value": "DepartmentID",
|
|
856
|
+
":required": false
|
|
857
|
+
}
|
|
858
|
+
},
|
|
859
|
+
{
|
|
860
|
+
"el": "f-button", "attr": {
|
|
861
|
+
":checkvalid": false, "color": "primary", "icon-text": "mdi-magnify",
|
|
862
|
+
":action": [{ "CALL": "getUserByDept" }]
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
]
|
|
866
|
+
},
|
|
867
|
+
{
|
|
868
|
+
"el": "v-card-text", "attr": { "class": "px-4" },
|
|
869
|
+
"innerHTML": [{
|
|
870
|
+
"_note": "f-table trong dialog — max-height thay vì :height",
|
|
871
|
+
"el": "f-table",
|
|
872
|
+
"attr": {
|
|
873
|
+
"label": "Danh sách users", "item-key": "UserID",
|
|
874
|
+
":items": "dsUserDept", ":show-select": true, "v-model": "sUserDeptSelected",
|
|
875
|
+
":items-per-page": -1, ":hide-default-footer": true, "max-height": 400,
|
|
876
|
+
":headers": [
|
|
877
|
+
{ "text": "UserID", "value": "UserID" },
|
|
878
|
+
{ "text": "Username", "value": "UserName" },
|
|
879
|
+
{ "text": "Fullname", "value": "FullName" }
|
|
880
|
+
]
|
|
881
|
+
}, "w": 12
|
|
882
|
+
}]
|
|
883
|
+
},
|
|
884
|
+
{
|
|
885
|
+
"el": "v-card-actions", "attr": {}, "w": 12,
|
|
886
|
+
"innerHTML": [
|
|
887
|
+
{ "el": "v-spacer", "attr": {} },
|
|
888
|
+
{
|
|
889
|
+
"el": "f-button", "attr": {
|
|
890
|
+
":checkvalid": false, "color": "primary", "label": "Save",
|
|
891
|
+
"icon-text": "mdi-content-save",
|
|
892
|
+
"_note_callback": "reset sUserDeptSelected + đóng dialog trong cùng 1 CALLBACK step",
|
|
893
|
+
":action": {
|
|
894
|
+
"CALL": "addUsers"
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
]
|
|
899
|
+
}
|
|
900
|
+
]
|
|
901
|
+
}]
|
|
902
|
+
}]
|
|
903
|
+
}]
|
|
904
|
+
},
|
|
905
|
+
|
|
906
|
+
{
|
|
907
|
+
"_note": "DIALOG 3 — Nested IF/THEN/ELSE trong CALLBACK: routing theo TypeLoad sau khi lưu",
|
|
908
|
+
"prop": "hidden-container",
|
|
909
|
+
"rows": [
|
|
910
|
+
{
|
|
911
|
+
"prop": "",
|
|
912
|
+
"cols": [
|
|
913
|
+
{
|
|
914
|
+
"el": "v-dialog",
|
|
915
|
+
"col": {},
|
|
916
|
+
"attr": { "v-model": "dlAssign", "width": 600, ":scrollable": true },
|
|
917
|
+
"innerHTML": [
|
|
918
|
+
{
|
|
919
|
+
"el": "v-card",
|
|
920
|
+
"attr": { "class": "pa-0" },
|
|
921
|
+
"innerHTML": [
|
|
922
|
+
{
|
|
923
|
+
"el": "v-toolbar",
|
|
924
|
+
"attr": { "dense": "", "flat": "" },
|
|
925
|
+
"innerHTML": [
|
|
926
|
+
{ "el": "v-toolbar-title", "attr": {}, "innerHTML": "Gán sub-items" },
|
|
927
|
+
{ "el": "v-spacer" },
|
|
928
|
+
{ "el": "v-btn", "innerHTML": "<v-icon>mdi-close</v-icon>", "attr": { "icon": "", "v-on:click": "vueData.dlAssign = false" } }
|
|
929
|
+
],
|
|
930
|
+
"w": 12
|
|
931
|
+
},
|
|
932
|
+
{
|
|
933
|
+
"el": "v-card-actions", "attr": {}, "w": 12,
|
|
934
|
+
"innerHTML": [
|
|
935
|
+
{ "el": "v-spacer", "attr": {} },
|
|
936
|
+
{
|
|
937
|
+
"el": "f-button",
|
|
938
|
+
"attr": {
|
|
939
|
+
":checkvalid": false, "color": "primary", "label": "Lưu", "icon-text": "mdi-content-save",
|
|
940
|
+
":action": {
|
|
941
|
+
"API": "/AssignSave",
|
|
942
|
+
"IN": { "ID": "sID", "SubList": "sRightSelected" },
|
|
943
|
+
"CALLBACK": [
|
|
944
|
+
{ "dlAssign": false },
|
|
945
|
+
{
|
|
946
|
+
"_note": "Nested IF/THEN/ELSE — reload đúng dataset theo TypeLoad hiện tại",
|
|
947
|
+
"IF": "TypeLoad==1",
|
|
948
|
+
"THEN": { "CALL": "getData" },
|
|
949
|
+
"ELSE": {
|
|
950
|
+
"IF": "TypeLoad==2",
|
|
951
|
+
"THEN": { "CALL": "getItemsDenied" },
|
|
952
|
+
"ELSE": { "CALL": "getItemsInactive" }
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
]
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
]
|
|
960
|
+
}
|
|
961
|
+
]
|
|
962
|
+
}
|
|
963
|
+
],
|
|
964
|
+
"w": ""
|
|
965
|
+
}
|
|
966
|
+
]
|
|
967
|
+
}
|
|
968
|
+
]
|
|
969
|
+
}
|
|
970
|
+
],
|
|
971
|
+
|
|
972
|
+
"set": {}
|
|
973
|
+
}
|