@fui-org/fui-cli 1.3.1 → 2.0.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 (111) hide show
  1. package/README.md +19 -3
  2. package/dist/fui-bmg5pnmq.js +379 -0
  3. package/dist/fui.js +1 -1
  4. package/package.json +7 -6
  5. package/skills/fui/SKILL.md +9 -41
  6. package/skills/fui-skill/SKILL.md +95 -225
  7. package/skills/fui-skill/assets/projectdefaultstyle-3.0.css +555 -0
  8. package/skills/fui-skill/assets/projectdefaultstyle.css +207 -235
  9. package/skills/fui-skill/references/INDEX.md +105 -137
  10. package/skills/fui-skill/references/advanced-techniques.md +76 -68
  11. package/skills/fui-skill/references/coding-standards.md +56 -56
  12. package/skills/fui-skill/references/component-design.md +166 -173
  13. package/skills/fui-skill/references/component-quickref.md +61 -60
  14. package/skills/fui-skill/references/component-table.md +128 -117
  15. package/skills/fui-skill/references/components-dialog.md +55 -56
  16. package/skills/fui-skill/references/components-display.md +24 -30
  17. package/skills/fui-skill/references/components-echart.md +186 -261
  18. package/skills/fui-skill/references/components-input.md +72 -96
  19. package/skills/fui-skill/references/controls-patterns.md +196 -342
  20. package/skills/fui-skill/references/controls-styling-vocabulary.md +130 -97
  21. package/skills/fui-skill/references/db-table-design.md +24 -28
  22. package/skills/fui-skill/references/db-workflow.md +191 -390
  23. package/skills/fui-skill/references/default-function.md +169 -128
  24. package/skills/fui-skill/references/design-modes.md +35 -63
  25. package/skills/fui-skill/references/echart-templates.md +204 -196
  26. package/skills/fui-skill/references/fastproject.md +62 -60
  27. package/skills/fui-skill/references/fsheet.md +109 -124
  28. package/skills/fui-skill/references/fullstack-workflow.md +90 -128
  29. package/skills/fui-skill/references/module-data-patterns.md +31 -40
  30. package/skills/fui-skill/references/module-json-anatomy.md +47 -52
  31. package/skills/fui-skill/references/module-structure.md +80 -196
  32. package/skills/fui-skill/references/new-session.md +49 -51
  33. package/skills/fui-skill/references/pdfmake.md +17 -17
  34. package/skills/fui-skill/references/permission-system.md +89 -108
  35. package/skills/fui-skill/references/platform-architecture.md +128 -153
  36. package/skills/fui-skill/references/project-config.md +102 -134
  37. package/skills/fui-skill/references/project-provisioning.md +139 -244
  38. package/skills/fui-skill/references/script-map.md +208 -242
  39. package/skills/fui-skill/references/sql-clr-functions.md +98 -97
  40. package/skills/fui-skill/references/system-design.md +63 -88
  41. package/skills/fui-skill/references/tapi-file-api.md +46 -52
  42. package/skills/fui-skill/references/tapi-permission-patterns.md +51 -53
  43. package/skills/fui-skill/references/tapi-reference.md +132 -207
  44. package/skills/fui-skill/references/tools-registry.md +84 -460
  45. package/skills/fui-skill/references/ui-crosswindow-patterns.md +79 -75
  46. package/skills/fui-skill/references/ui-dialog-patterns.md +98 -72
  47. package/skills/fui-skill/references/ui-layout-patterns.md +26 -26
  48. package/skills/fui-skill/references/ui-patterns.md +71 -83
  49. package/skills/fui-skill/references/ui-screenshot-review.md +63 -62
  50. package/skills/fui-skill/references/ui-table-cell-patterns.md +61 -59
  51. package/skills/fui-skill/references/ui-templates.md +16 -23
  52. package/skills/fui-skill/references/verification.md +236 -246
  53. package/skills/fui-skill/references/watcher-patterns.md +30 -63
  54. package/skills/fui-skill/references/websocket-realtime.md +83 -66
  55. package/skills/fui-skill/scripts/component-3.0.js +298 -131
  56. package/skills/fui-skill/scripts/component.js +277 -271
  57. package/skills/fui-skill/scripts/componentTable-3.0.js +182 -53
  58. package/skills/fui-skill/scripts/componentTable.js +171 -49
  59. package/skills/fui-skill/scripts/defaultfunction-3.0.js +88 -3
  60. package/skills/fui-skill/scripts/defaultfunction.js +88 -3
  61. package/skills/fui-skill/scripts/fsheet.js +38 -0
  62. package/dist/fui-x7kph78r.js +0 -420
  63. package/skills/fui-skill/README.md +0 -112
  64. package/skills/fui-skill/metadata.json +0 -75
  65. package/skills/fui-skill-cli/SKILL.md +0 -139
  66. package/skills/fui-skill-cli/references/INDEX.md +0 -110
  67. package/skills/fui-skill-cli/references/advanced-techniques.md +0 -168
  68. package/skills/fui-skill-cli/references/coding-standards.md +0 -112
  69. package/skills/fui-skill-cli/references/component-design.md +0 -448
  70. package/skills/fui-skill-cli/references/component-quickref.md +0 -78
  71. package/skills/fui-skill-cli/references/component-table.md +0 -248
  72. package/skills/fui-skill-cli/references/components-dialog.md +0 -191
  73. package/skills/fui-skill-cli/references/components-display.md +0 -141
  74. package/skills/fui-skill-cli/references/components-echart.md +0 -316
  75. package/skills/fui-skill-cli/references/components-input.md +0 -335
  76. package/skills/fui-skill-cli/references/controls-patterns.md +0 -701
  77. package/skills/fui-skill-cli/references/controls-styling-vocabulary.md +0 -137
  78. package/skills/fui-skill-cli/references/db-table-design.md +0 -73
  79. package/skills/fui-skill-cli/references/db-workflow.md +0 -288
  80. package/skills/fui-skill-cli/references/default-function.md +0 -425
  81. package/skills/fui-skill-cli/references/design-modes.md +0 -57
  82. package/skills/fui-skill-cli/references/echart-templates.md +0 -489
  83. package/skills/fui-skill-cli/references/fastproject.md +0 -99
  84. package/skills/fui-skill-cli/references/fsheet.md +0 -203
  85. package/skills/fui-skill-cli/references/fullstack-workflow.md +0 -313
  86. package/skills/fui-skill-cli/references/module-data-patterns.md +0 -117
  87. package/skills/fui-skill-cli/references/module-json-anatomy.md +0 -132
  88. package/skills/fui-skill-cli/references/module-structure.md +0 -141
  89. package/skills/fui-skill-cli/references/new-session.md +0 -85
  90. package/skills/fui-skill-cli/references/pdfmake.md +0 -60
  91. package/skills/fui-skill-cli/references/permission-system.md +0 -150
  92. package/skills/fui-skill-cli/references/platform-architecture.md +0 -269
  93. package/skills/fui-skill-cli/references/project-config.md +0 -303
  94. package/skills/fui-skill-cli/references/project-provisioning.md +0 -278
  95. package/skills/fui-skill-cli/references/script-map.md +0 -262
  96. package/skills/fui-skill-cli/references/sql-clr-functions.md +0 -225
  97. package/skills/fui-skill-cli/references/system-design.md +0 -89
  98. package/skills/fui-skill-cli/references/tapi-file-api.md +0 -185
  99. package/skills/fui-skill-cli/references/tapi-permission-patterns.md +0 -156
  100. package/skills/fui-skill-cli/references/tapi-reference.md +0 -474
  101. package/skills/fui-skill-cli/references/tools-registry.md +0 -84
  102. package/skills/fui-skill-cli/references/ui-crosswindow-patterns.md +0 -321
  103. package/skills/fui-skill-cli/references/ui-dialog-patterns.md +0 -255
  104. package/skills/fui-skill-cli/references/ui-layout-patterns.md +0 -176
  105. package/skills/fui-skill-cli/references/ui-patterns.md +0 -303
  106. package/skills/fui-skill-cli/references/ui-screenshot-review.md +0 -95
  107. package/skills/fui-skill-cli/references/ui-table-cell-patterns.md +0 -318
  108. package/skills/fui-skill-cli/references/ui-templates.md +0 -22
  109. package/skills/fui-skill-cli/references/verification.md +0 -236
  110. package/skills/fui-skill-cli/references/watcher-patterns.md +0 -163
  111. package/skills/fui-skill-cli/references/websocket-realtime.md +0 -271
@@ -1,39 +1,35 @@
1
1
  # f-echart — Chart Type Templates & Real-World Patterns
2
2
 
3
- > File này sở hữu: **template `option` theo từng loại chart + pattern module.json đi kèm**. Import/props/config nền tảng xem [components-echart.md](components-echart.md).
4
-
5
- Thư viện template cho từng loại biểu đồ + các pattern dùng `f-echart` trong module.json. Load file này khi đã biết cần vẽ loại chart nào.
6
-
7
- > Nền tảng (import, props, config, theme, DataView, lỗi thường gặp): xem [components-echart.md](components-echart.md). **Luôn đọc file core trước** để biết cách import và cấu trúc `option`/`config`.
3
+ > Owns: **`option` templates per chart type + matching module.json patterns**. Load once you know which chart to draw. **Read the core first** — import/props/config/theme/DataView/errors: [components-echart.md](components-echart.md).
8
4
 
9
5
  ---
10
6
 
11
7
  ## Templates — Chart types
12
8
 
13
- > Quy ước chung cho mọi template dưới đây: chỉ khai cạnh `grid` khi bố cục hoặc user yêu cầu khác mặc định; layout engine tự chừa chỗ theo title/legend. Cách phân biệt `grid` với `boundaryGap` và giá trị mặc định: [components-echart.md](components-echart.md) §Grid padding và boundaryGap. Legend khai kiểu `legend: {}` trong `option` sẽ **neo trên đầu** (dưới title); muốn nó xuống đáy thì `legend: { bottom: 0 }`, hoặc bật qua `config.legend: true` (mặc định của shortcut là đáy).
9
+ > All templates: declare `grid` sides only for non-default layout (engine reserves title/legend space); `grid` vs `boundaryGap`: [components-echart.md](components-echart.md) §Grid padding vs boundaryGap. `legend: {}` in `option` **anchors top**; for bottom use `legend: { bottom: 0 }` or `config.legend: true`.
14
10
 
15
11
  ### Bar chart
16
12
 
17
13
  ```js
18
14
  // script.js — callback sau fetch
19
15
  vueData.chartOpt = {
20
- xAxis: { type: 'category', data: d.data.map(r => r.thang) },
21
- yAxis: { type: 'value' },
22
- series: [{ type: 'bar', data: d.data.map(r => r.doanhThu), name: 'Doanh thu' }],
23
- tooltip: { trigger: 'axis' },
24
- legend: {}
16
+ xAxis: { type: "category", data: d.data.map((r) => r.thang) },
17
+ yAxis: { type: "value" },
18
+ series: [{ type: "bar", data: d.data.map((r) => r.doanhThu), name: "Doanh thu" }],
19
+ tooltip: { trigger: "axis" },
20
+ legend: {},
25
21
  };
26
22
  vueData.rows = d.data;
27
23
  ```
28
24
 
29
- ### Line chart (có smooth)
25
+ ### Line chart (smooth)
30
26
 
31
27
  ```js
32
28
  vueData.chartOpt = {
33
- xAxis: { type: 'category', data: d.data.map(r => r.thang) },
34
- yAxis: { type: 'value' },
35
- series: [{ type: 'line', data: d.data.map(r => r.val), smooth: true, areaStyle: {} }],
36
- tooltip: { trigger: 'axis' }
29
+ xAxis: { type: "category", data: d.data.map((r) => r.thang) },
30
+ yAxis: { type: "value" },
31
+ series: [{ type: "line", data: d.data.map((r) => r.val), smooth: true, areaStyle: {} }],
32
+ tooltip: { trigger: "axis" },
37
33
  };
38
34
  ```
39
35
 
@@ -41,14 +37,14 @@ vueData.chartOpt = {
41
37
 
42
38
  ```js
43
39
  vueData.chartOpt = {
44
- xAxis: { type: 'category', data: d.data.map(r => r.thang) },
45
- yAxis: [{ name: 'VNĐ' }, { name: '%', position: 'right' }],
40
+ xAxis: { type: "category", data: d.data.map((r) => r.thang) },
41
+ yAxis: [{ name: "VNĐ" }, { name: "%", position: "right" }],
46
42
  series: [
47
- { type: 'bar', name: 'Doanh thu', data: d.data.map(r => r.doanhThu), yAxisIndex: 0 },
48
- { type: 'line', name: 'Tăng trưởng', data: d.data.map(r => r.tangTruong), yAxisIndex: 1, smooth: true }
43
+ { type: "bar", name: "Doanh thu", data: d.data.map((r) => r.doanhThu), yAxisIndex: 0 },
44
+ { type: "line", name: "Tăng trưởng", data: d.data.map((r) => r.tangTruong), yAxisIndex: 1, smooth: true },
49
45
  ],
50
- tooltip: { trigger: 'axis' },
51
- legend: {}
46
+ tooltip: { trigger: "axis" },
47
+ legend: {},
52
48
  };
53
49
  ```
54
50
 
@@ -56,15 +52,15 @@ vueData.chartOpt = {
56
52
 
57
53
  ```js
58
54
  vueData.chartOpt = {
59
- xAxis: { type: 'category', data: d.data.map(r => r.thang) },
60
- yAxis: { type: 'value' },
55
+ xAxis: { type: "category", data: d.data.map((r) => r.thang) },
56
+ yAxis: { type: "value" },
61
57
  series: [
62
- { type: 'bar', name: 'SP A', data: d.data.map(r => r.spA), stack: 'total' },
63
- { type: 'bar', name: 'SP B', data: d.data.map(r => r.spB), stack: 'total' },
64
- { type: 'bar', name: 'SP C', data: d.data.map(r => r.spC), stack: 'total' }
58
+ { type: "bar", name: "SP A", data: d.data.map((r) => r.spA), stack: "total" },
59
+ { type: "bar", name: "SP B", data: d.data.map((r) => r.spB), stack: "total" },
60
+ { type: "bar", name: "SP C", data: d.data.map((r) => r.spC), stack: "total" },
65
61
  ],
66
- tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
67
- legend: {}
62
+ tooltip: { trigger: "axis", axisPointer: { type: "shadow" } },
63
+ legend: {},
68
64
  };
69
65
  ```
70
66
 
@@ -72,14 +68,16 @@ vueData.chartOpt = {
72
68
 
73
69
  ```js
74
70
  vueData.chartOpt = {
75
- series: [{
76
- type: 'pie',
77
- data: d.data.map(r => ({ name: r.khoa, value: r.soLuong })),
78
- radius: '70%',
79
- label: { formatter: '{b}: {d}%' }
80
- }],
81
- tooltip: { trigger: 'item' },
82
- legend: { bottom: 0 }
71
+ series: [
72
+ {
73
+ type: "pie",
74
+ data: d.data.map((r) => ({ name: r.khoa, value: r.soLuong })),
75
+ radius: "70%",
76
+ label: { formatter: "{b}: {d}%" },
77
+ },
78
+ ],
79
+ tooltip: { trigger: "item" },
80
+ legend: { bottom: 0 },
83
81
  };
84
82
  ```
85
83
 
@@ -89,12 +87,12 @@ Doughnut: `radius: ['40%', '70%']`
89
87
 
90
88
  ```js
91
89
  vueData.chartOpt = {
92
- xAxis: { type: 'value', name: 'Điểm thi' },
93
- yAxis: { type: 'value', name: 'GPA' },
94
- series: [{ type: 'scatter', data: d.data.map(r => [r.diemThi, r.gpa, r.hoTen]), symbolSize: 8 }],
90
+ xAxis: { type: "value", name: "Điểm thi" },
91
+ yAxis: { type: "value", name: "GPA" },
92
+ series: [{ type: "scatter", data: d.data.map((r) => [r.diemThi, r.gpa, r.hoTen]), symbolSize: 8 }],
95
93
  tooltip: {
96
- formatter: p => `${p.value[2]}<br>Điểm: ${p.value[0]} — GPA: ${p.value[1]}`
97
- }
94
+ formatter: (p) => `${p.value[2]}<br>Điểm: ${p.value[0]} — GPA: ${p.value[1]}`,
95
+ },
98
96
  };
99
97
  ```
100
98
 
@@ -102,14 +100,17 @@ vueData.chartOpt = {
102
100
 
103
101
  ```js
104
102
  vueData.chartOpt = {
105
- series: [{
106
- type: 'gauge',
107
- min: 0, max: 100,
108
- data: [{ value: vueData.cpuPercent, name: 'CPU %' }],
109
- detail: { formatter: '{value}%' },
110
- axisLabel: { color: 'auto' }
111
- }],
112
- tooltip: {}
103
+ series: [
104
+ {
105
+ type: "gauge",
106
+ min: 0,
107
+ max: 100,
108
+ data: [{ value: vueData.cpuPercent, name: "CPU %" }],
109
+ detail: { formatter: "{value}%" },
110
+ axisLabel: { color: "auto" },
111
+ },
112
+ ],
113
+ tooltip: {},
113
114
  };
114
115
  ```
115
116
 
@@ -119,19 +120,24 @@ vueData.chartOpt = {
119
120
  vueData.chartOpt = {
120
121
  radar: {
121
122
  indicator: [
122
- { name: 'Toán', max: 10 }, { name: 'Lý', max: 10 },
123
- { name: 'Hóa', max: 10 }, { name: 'Văn', max: 10 }, { name: 'Anh', max: 10 }
124
- ]
123
+ { name: "Toán", max: 10 },
124
+ { name: "Lý", max: 10 },
125
+ { name: "Hóa", max: 10 },
126
+ { name: "Văn", max: 10 },
127
+ { name: "Anh", max: 10 },
128
+ ],
125
129
  },
126
- series: [{
127
- type: 'radar',
128
- data: d.data.map(r => ({
129
- name: r.hoTen,
130
- value: [r.toan, r.ly, r.hoa, r.van, r.anh]
131
- }))
132
- }],
130
+ series: [
131
+ {
132
+ type: "radar",
133
+ data: d.data.map((r) => ({
134
+ name: r.hoTen,
135
+ value: [r.toan, r.ly, r.hoa, r.van, r.anh],
136
+ })),
137
+ },
138
+ ],
133
139
  tooltip: {},
134
- legend: {}
140
+ legend: {},
135
141
  };
136
142
  ```
137
143
 
@@ -139,17 +145,19 @@ vueData.chartOpt = {
139
145
 
140
146
  ```js
141
147
  // data: [{x: 'Mon', y: '8h', value: 5}, ...]
142
- const xData = [...new Set(d.data.map(r => r.x))];
143
- const yData = [...new Set(d.data.map(r => r.y))];
148
+ const xData = [...new Set(d.data.map((r) => r.x))];
149
+ const yData = [...new Set(d.data.map((r) => r.y))];
144
150
  vueData.chartOpt = {
145
- xAxis: { type: 'category', data: xData },
146
- yAxis: { type: 'category', data: yData },
151
+ xAxis: { type: "category", data: xData },
152
+ yAxis: { type: "category", data: yData },
147
153
  visualMap: { min: 0, max: 10, calculable: true },
148
- series: [{
149
- type: 'heatmap',
150
- data: d.data.map(r => [xData.indexOf(r.x), yData.indexOf(r.y), r.value])
151
- }],
152
- tooltip: {}
154
+ series: [
155
+ {
156
+ type: "heatmap",
157
+ data: d.data.map((r) => [xData.indexOf(r.x), yData.indexOf(r.y), r.value]),
158
+ },
159
+ ],
160
+ tooltip: {},
153
161
  };
154
162
  ```
155
163
 
@@ -157,13 +165,15 @@ vueData.chartOpt = {
157
165
 
158
166
  ```js
159
167
  vueData.chartOpt = {
160
- xAxis: { type: 'category', data: d.data.map(r => r.date) },
161
- yAxis: { type: 'value', scale: true },
162
- series: [{
163
- type: 'candlestick',
164
- data: d.data.map(r => [r.open, r.close, r.low, r.high])
165
- }],
166
- tooltip: { trigger: 'axis' }
168
+ xAxis: { type: "category", data: d.data.map((r) => r.date) },
169
+ yAxis: { type: "value", scale: true },
170
+ series: [
171
+ {
172
+ type: "candlestick",
173
+ data: d.data.map((r) => [r.open, r.close, r.low, r.high]),
174
+ },
175
+ ],
176
+ tooltip: { trigger: "axis" },
167
177
  };
168
178
  ```
169
179
 
@@ -171,13 +181,15 @@ vueData.chartOpt = {
171
181
 
172
182
  ```js
173
183
  vueData.chartOpt = {
174
- series: [{
175
- type: 'funnel',
176
- data: d.data.map(r => ({ name: r.buoc, value: r.soLuong })).sort((a, b) => b.value - a.value),
177
- label: { formatter: '{b}: {d}%' }
178
- }],
179
- tooltip: { trigger: 'item' },
180
- legend: {}
184
+ series: [
185
+ {
186
+ type: "funnel",
187
+ data: d.data.map((r) => ({ name: r.buoc, value: r.soLuong })).sort((a, b) => b.value - a.value),
188
+ label: { formatter: "{b}: {d}%" },
189
+ },
190
+ ],
191
+ tooltip: { trigger: "item" },
192
+ legend: {},
181
193
  };
182
194
  ```
183
195
 
@@ -186,31 +198,35 @@ vueData.chartOpt = {
186
198
  ```js
187
199
  // data là hierarchical: { name, value, children: [...] }
188
200
  vueData.chartOpt = {
189
- series: [{
190
- type: 'treemap',
191
- data: vueData.budgetTree, // hierarchical array
192
- roam: false,
193
- leafDepth: 2,
194
- label: { show: true }
195
- }],
196
- tooltip: {}
201
+ series: [
202
+ {
203
+ type: "treemap",
204
+ data: vueData.budgetTree, // hierarchical array
205
+ roam: false,
206
+ leafDepth: 2,
207
+ label: { show: true },
208
+ },
209
+ ],
210
+ tooltip: {},
197
211
  };
198
212
  ```
199
213
 
200
- ### Tree (org chart, org tree)
214
+ ### Tree (org chart)
201
215
 
202
216
  ```js
203
217
  vueData.chartOpt = {
204
- series: [{
205
- type: 'tree',
206
- data: [vueData.orgTree], // single root node
207
- orient: 'TB', // 'LR' | 'TB' | 'RL' | 'BT'
208
- expandAndCollapse: true,
209
- initialTreeDepth: 2,
210
- label: { position: 'top' },
211
- leaves: { label: { position: 'bottom' } }
212
- }],
213
- tooltip: { trigger: 'item' }
218
+ series: [
219
+ {
220
+ type: "tree",
221
+ data: [vueData.orgTree], // single root node
222
+ orient: "TB", // 'LR' | 'TB' | 'RL' | 'BT'
223
+ expandAndCollapse: true,
224
+ initialTreeDepth: 2,
225
+ label: { position: "top" },
226
+ leaves: { label: { position: "bottom" } },
227
+ },
228
+ ],
229
+ tooltip: { trigger: "item" },
214
230
  };
215
231
  ```
216
232
 
@@ -219,14 +235,16 @@ vueData.chartOpt = {
219
235
  ```js
220
236
  // nodes: [{name: '...'}], links: [{source: '...', target: '...', value: N}]
221
237
  vueData.chartOpt = {
222
- series: [{
223
- type: 'sankey',
224
- data: vueData.sankeyNodes, // [{name: 'A'}, {name: 'B'}, ...]
225
- links: vueData.sankeyLinks, // [{source: 'A', target: 'B', value: 5}, ...]
226
- emphasis: { focus: 'adjacency' },
227
- lineStyle: { color: 'gradient' }
228
- }],
229
- tooltip: { trigger: 'item' }
238
+ series: [
239
+ {
240
+ type: "sankey",
241
+ data: vueData.sankeyNodes, // [{name: 'A'}, {name: 'B'}, ...]
242
+ links: vueData.sankeyLinks, // [{source: 'A', target: 'B', value: 5}, ...]
243
+ emphasis: { focus: "adjacency" },
244
+ lineStyle: { color: "gradient" },
245
+ },
246
+ ],
247
+ tooltip: { trigger: "item" },
230
248
  };
231
249
  ```
232
250
 
@@ -234,18 +252,20 @@ vueData.chartOpt = {
234
252
 
235
253
  ```js
236
254
  vueData.chartOpt = {
237
- series: [{
238
- type: 'graph',
239
- layout: 'force', // 'force' | 'circular' | 'none'
240
- data: vueData.nodes, // [{name, value, category}, ...]
241
- links: vueData.edges, // [{source, target}, ...]
242
- categories: vueData.categories, // [{name}, ...]
243
- roam: true,
244
- label: { show: true },
245
- force: { repulsion: 1000 }
246
- }],
255
+ series: [
256
+ {
257
+ type: "graph",
258
+ layout: "force", // 'force' | 'circular' | 'none'
259
+ data: vueData.nodes, // [{name, value, category}, ...]
260
+ links: vueData.edges, // [{source, target}, ...]
261
+ categories: vueData.categories, // [{name}, ...]
262
+ roam: true,
263
+ label: { show: true },
264
+ force: { repulsion: 1000 },
265
+ },
266
+ ],
247
267
  legend: {},
248
- tooltip: {}
268
+ tooltip: {},
249
269
  };
250
270
  ```
251
271
 
@@ -254,14 +274,16 @@ vueData.chartOpt = {
254
274
  ```js
255
275
  // data: [{date: '2024-01-01', count: 5}, ...]
256
276
  vueData.chartOpt = {
257
- calendar: { range: '2024', cellSize: ['auto', 20] },
258
- visualMap: { min: 0, max: 30, type: 'piecewise', orient: 'horizontal', left: 'center' },
259
- series: [{
260
- type: 'heatmap',
261
- coordinateSystem: 'calendar',
262
- data: d.data.map(r => [r.date, r.count])
263
- }],
264
- tooltip: { formatter: p => `${p.value[0]}: ${p.value[1]}` }
277
+ calendar: { range: "2024", cellSize: ["auto", 20] },
278
+ visualMap: { min: 0, max: 30, type: "piecewise", orient: "horizontal", left: "center" },
279
+ series: [
280
+ {
281
+ type: "heatmap",
282
+ coordinateSystem: "calendar",
283
+ data: d.data.map((r) => [r.date, r.count]),
284
+ },
285
+ ],
286
+ tooltip: { formatter: (p) => `${p.value[0]}: ${p.value[1]}` },
265
287
  };
266
288
  ```
267
289
 
@@ -273,7 +295,7 @@ vueData.chartOpt = {
273
295
  // Zoom (trong option)
274
296
  vueData.chartOpt = {
275
297
  ...baseOpt,
276
- dataZoom: [{ type: 'inside' }, { type: 'slider', height: 20 }]
298
+ dataZoom: [{ type: "inside" }, { type: "slider", height: 20 }],
277
299
  };
278
300
  // Hoặc dùng config shortcut:
279
301
  // config: { zoom: true }
@@ -285,49 +307,44 @@ vueData.chartOpt = {
285
307
 
286
308
  // Hoặc custom formatter trong option
287
309
  tooltip: {
288
- formatter: p => {
289
- const fmt = FEchartFormatter.build('currency', { symbol: '₫', decimals: 0 });
310
+ formatter: (p) => {
311
+ const fmt = FEchartFormatter.build("currency", { symbol: "₫", decimals: 0 });
290
312
  return `${p.name}: ${fmt(p.value)}`;
291
- }
313
+ };
292
314
  }
293
315
  ```
294
316
 
295
317
  ---
296
318
 
297
- ## Dataset + encode (khi cần map cột tùy ý)
319
+ ## Dataset + encode (arbitrary column mapping)
298
320
 
299
321
  ```js
300
322
  // script.js — build option dùng dataset (không map thủ công)
301
323
  vueData.chartOpt = {
302
324
  dataset: { source: vueData.rows },
303
325
  series: [
304
- { type: 'bar', name: 'Doanh thu', encode: { x: 'thang', y: 'doanhThu' } },
305
- { type: 'line', name: 'Chi phí', encode: { x: 'thang', y: 'chiPhi' }, yAxisIndex: 1 }
326
+ { type: "bar", name: "Doanh thu", encode: { x: "thang", y: "doanhThu" } },
327
+ { type: "line", name: "Chi phí", encode: { x: "thang", y: "chiPhi" }, yAxisIndex: 1 },
306
328
  ],
307
- xAxis: { type: 'category' },
308
- yAxis: [{}, { position: 'right' }],
309
- tooltip: { trigger: 'axis' }
329
+ xAxis: { type: "category" },
330
+ yAxis: [{}, { position: "right" }],
331
+ tooltip: { trigger: "axis" },
310
332
  };
311
333
  ```
312
334
 
313
335
  ---
314
336
 
315
- ## Patterns thực tế
316
-
317
- Hai pattern chính khi dùng `f-echart` trong module.json:
337
+ ## Real-world patterns
318
338
 
319
- | Pattern | Khi nào dùng |
320
- |---|---|
321
- | **Inline** — viết `option` thẳng vào attr | Chart đơn giản, data đã có trong `vueData`, không cần biến trung gian |
322
- | **vueData ref** — `option` build trong `script.js` | Data cần fetch từ API; option phức tạp; cần sort/transform trước khi render |
323
-
324
- ---
339
+ | Pattern | When |
340
+ | ----------------------------------------------- | ------------------------------------------------------------------- |
341
+ | **Inline** — `option` written directly in attr | Simple chart, data already in `vueData`, no intermediate variable |
342
+ | **vueData ref** — `option` built in `script.js` | Data fetched from API; complex option; sort/transform before render |
325
343
 
326
- ### Pattern 1 — Inline pie (không cần script.js)
344
+ ### Pattern 1 — Inline pie (no script.js)
327
345
 
328
- Khi data đã có trong `vueData.rows`, viết `option` thẳng vào attr — không cần biến `chartOpt` hay script.js.
346
+ Data already in `vueData.rows` → `option` in the attr, no `chartOpt`/script.js.
329
347
 
330
- **module.json controls:**
331
348
  ```json
332
349
  {
333
350
  "el": "f-echart", "w": "6",
@@ -347,15 +364,10 @@ Khi data đã có trong `vueData.rows`, viết `option` thẳng vào attr — kh
347
364
  }
348
365
  ```
349
366
 
350
- > `vueData.rows` phải được gán trước (ví dụ: từ callback fetch của control khác trong cùng module).
351
-
352
- ---
367
+ > `vueData.rows` must be assigned first (e.g. from another control's fetch callback in the same module).
353
368
 
354
369
  ### Pattern 2 — Inline horizontal bar
355
370
 
356
- Horizontal bar: đổi `xAxis`/`yAxis`, dùng `inverse: true` và `containLabel: true`.
357
-
358
- **module.json controls:**
359
371
  ```json
360
372
  {
361
373
  "el": "f-echart", "w": "12",
@@ -368,7 +380,7 @@ Horizontal bar: đổi `xAxis`/`yAxis`, dùng `inverse: true` và `containLabel:
368
380
  data: vueData.rows.map(r => r.tenKhoa),
369
381
  inverse: true
370
382
  },
371
- grid: { containLabel: true },
383
+ grid: { containLabel: true },
372
384
  series: [{
373
385
  type: 'bar',
374
386
  data: vueData.rows.map(r => r.soLuong),
@@ -381,22 +393,16 @@ Horizontal bar: đổi `xAxis`/`yAxis`, dùng `inverse: true` và `containLabel:
381
393
  }
382
394
  ```
383
395
 
384
- **Lưu ý:**
385
- - `containLabel: true` trong `grid` — bắt buộc cho horizontal bar để nhãn `yAxis` không bị cắt.
386
- - Chỉ khai cạnh `grid` khi user yêu cầu khác mặc định; xem [components-echart.md](components-echart.md) — "Grid padding và boundaryGap".
387
- - `inverse: true` → item lớn nhất hiển thị ở trên cùng.
388
- - `height: Math.max(300, rows.length * 36)` — tự co dãn theo số hàng.
396
+ - `grid.containLabel: true` — required so `yAxis` labels aren't clipped; other `grid` sides only if the user asks.
397
+ - `inverse: true` → largest on top.
398
+ - `height: Math.max(300, rows.length * 36)` scales with rows.
389
399
 
390
- ---
391
-
392
- ### Pattern 3 — vueData ref + dynamic height (fetch từ API)
400
+ ### Pattern 3 — vueData ref + dynamic height (API fetch)
393
401
 
394
- Dùng khi option phức tạp hoặc data cần fetch từ API riêng.
395
-
396
- **module.json controls:**
397
402
  ```json
398
403
  {
399
- "el": "f-echart", "w": "12",
404
+ "el": "f-echart",
405
+ "w": "12",
400
406
  "attr": {
401
407
  ":config": "{ title: 'Doanh thu theo tháng', height: 400, dataView: true }",
402
408
  ":option": "vueData.chartOpt"
@@ -405,35 +411,34 @@ Dùng khi option phức tạp hoặc data cần fetch từ API riêng.
405
411
  ```
406
412
 
407
413
  **script.js:**
414
+
408
415
  ```js
409
416
  // Trong callback fetch data
410
417
  vueData.rows = d.data.slice().sort((a, b) => a.thang.localeCompare(b.thang));
411
418
  vueData.chartOpt = {
412
- xAxis: { type: 'category', data: vueData.rows.map(r => r.thang) },
419
+ xAxis: { type: "category", data: vueData.rows.map((r) => r.thang) },
413
420
  yAxis: [
414
- { type: 'value', name: 'Triệu đồng', axisLabel: { formatter: v => FEchartFormatter.formatShort(v) } },
415
- { type: 'value', name: '%', position: 'right', max: 100 }
421
+ { type: "value", name: "Triệu đồng", axisLabel: { formatter: (v) => FEchartFormatter.formatShort(v) } },
422
+ { type: "value", name: "%", position: "right", max: 100 },
416
423
  ],
417
424
  series: [
418
- { type: 'bar', name: 'Doanh thu', data: vueData.rows.map(r => r.doanhThu), yAxisIndex: 0 },
419
- { type: 'line', name: 'Tăng trưởng', data: vueData.rows.map(r => r.tangTruong), yAxisIndex: 1, smooth: true }
425
+ { type: "bar", name: "Doanh thu", data: vueData.rows.map((r) => r.doanhThu), yAxisIndex: 0 },
426
+ { type: "line", name: "Tăng trưởng", data: vueData.rows.map((r) => r.tangTruong), yAxisIndex: 1, smooth: true },
420
427
  ],
421
428
  grid: { containLabel: true },
422
- tooltip: { trigger: 'axis' },
423
- legend: {}
429
+ tooltip: { trigger: "axis" },
430
+ legend: {},
424
431
  };
425
432
  ```
426
433
 
427
- ---
428
-
429
434
  ### Pattern 4 — Conditional option (null guard)
430
435
 
431
- Khi option được build từ nhiều nguồn data, dùng điều kiện để tránh render chart với data rỗng.
436
+ Several data sources → guard against rendering empty data.
432
437
 
433
- **module.json controls:**
434
438
  ```json
435
439
  {
436
- "el": "f-echart", "w": "12",
440
+ "el": "f-echart",
441
+ "w": "12",
437
442
  "attr": {
438
443
  ":config": "{ height: 350 }",
439
444
  ":option": "vueData.baseOpt ? Object.assign({}, vueData.baseOpt, { series: vueData.series }) : null"
@@ -442,35 +447,38 @@ Khi option được build từ nhiều nguồn data, dùng điều kiện để
442
447
  ```
443
448
 
444
449
  **script.js:**
450
+
445
451
  ```js
446
452
  // Base option — khởi tạo một lần
447
453
  vueData.baseOpt = {
448
- xAxis: { type: 'category', data: [] },
449
- yAxis: { type: 'value' },
450
- tooltip: { trigger: 'axis' },
454
+ xAxis: { type: "category", data: [] },
455
+ yAxis: { type: "value" },
456
+ tooltip: { trigger: "axis" },
451
457
  legend: {},
452
- grid: { containLabel: true }
458
+ grid: { containLabel: true },
453
459
  };
454
460
 
455
461
  // Khi fetch data xong — reactive update
456
- vueData.baseOpt.xAxis.data = d.data.map(r => r.thang);
462
+ vueData.baseOpt.xAxis.data = d.data.map((r) => r.thang);
457
463
  vueData.series = d.data.map((r, i) => ({
458
- type: 'bar', name: r.name, data: r.values, stack: 'total'
464
+ type: "bar",
465
+ name: r.name,
466
+ data: r.values,
467
+ stack: "total",
459
468
  }));
460
469
  // Object.assign trong :option tạo object mới → Vue trigger re-render
461
470
  ```
462
471
 
463
- > `Object.assign({}, base, override)` trong `:option` expression — tạo object mới mỗi lần evaluate, đảm bảo Vue detect change.
472
+ > `Object.assign({}, base, override)` in `:option` yields a new object each evaluation, so Vue detects the change.
464
473
 
465
- ---
466
-
467
- ### Pattern 5 — Chart không có DataView (phổ biến nhất)
474
+ ### Pattern 5 — No DataView (most common)
468
475
 
469
- Đa số chart production không cần DataView — bỏ `dataView` để gọn. Không cần `:data` prop (không tồn tại).
476
+ Most production charts omit `dataView`. There is no `:data` prop.
470
477
 
471
478
  ```json
472
479
  {
473
- "el": "f-echart", "w": "6",
480
+ "el": "f-echart",
481
+ "w": "6",
474
482
  "attr": {
475
483
  ":config": "{ height: 300 }",
476
484
  ":option": "vueData.pieOpt"
@@ -478,4 +486,4 @@ vueData.series = d.data.map((r, i) => ({
478
486
  }
479
487
  ```
480
488
 
481
- Chỉ thêm `dataView: true` vào config khi user cần xem bảng số liệu — DataView tự trích xuất từ `option`.
489
+ Add `dataView: true` only when the user needs the data table (auto-extracted from `option`).