@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.
- package/README.md +19 -3
- package/dist/fui-bmg5pnmq.js +379 -0
- package/dist/fui.js +1 -1
- package/package.json +7 -6
- package/skills/fui/SKILL.md +9 -41
- package/skills/fui-skill/SKILL.md +95 -225
- package/skills/fui-skill/assets/projectdefaultstyle-3.0.css +555 -0
- package/skills/fui-skill/assets/projectdefaultstyle.css +207 -235
- package/skills/fui-skill/references/INDEX.md +105 -137
- package/skills/fui-skill/references/advanced-techniques.md +76 -68
- package/skills/fui-skill/references/coding-standards.md +56 -56
- package/skills/fui-skill/references/component-design.md +166 -173
- package/skills/fui-skill/references/component-quickref.md +61 -60
- package/skills/fui-skill/references/component-table.md +128 -117
- package/skills/fui-skill/references/components-dialog.md +55 -56
- package/skills/fui-skill/references/components-display.md +24 -30
- package/skills/fui-skill/references/components-echart.md +186 -261
- package/skills/fui-skill/references/components-input.md +72 -96
- package/skills/fui-skill/references/controls-patterns.md +196 -342
- package/skills/fui-skill/references/controls-styling-vocabulary.md +130 -97
- package/skills/fui-skill/references/db-table-design.md +24 -28
- package/skills/fui-skill/references/db-workflow.md +191 -390
- package/skills/fui-skill/references/default-function.md +169 -128
- package/skills/fui-skill/references/design-modes.md +35 -63
- package/skills/fui-skill/references/echart-templates.md +204 -196
- package/skills/fui-skill/references/fastproject.md +62 -60
- package/skills/fui-skill/references/fsheet.md +109 -124
- package/skills/fui-skill/references/fullstack-workflow.md +90 -128
- package/skills/fui-skill/references/module-data-patterns.md +31 -40
- package/skills/fui-skill/references/module-json-anatomy.md +47 -52
- package/skills/fui-skill/references/module-structure.md +80 -196
- package/skills/fui-skill/references/new-session.md +49 -51
- package/skills/fui-skill/references/pdfmake.md +17 -17
- package/skills/fui-skill/references/permission-system.md +89 -108
- package/skills/fui-skill/references/platform-architecture.md +128 -153
- package/skills/fui-skill/references/project-config.md +102 -134
- package/skills/fui-skill/references/project-provisioning.md +139 -244
- package/skills/fui-skill/references/script-map.md +208 -242
- package/skills/fui-skill/references/sql-clr-functions.md +98 -97
- package/skills/fui-skill/references/system-design.md +63 -88
- package/skills/fui-skill/references/tapi-file-api.md +46 -52
- package/skills/fui-skill/references/tapi-permission-patterns.md +51 -53
- package/skills/fui-skill/references/tapi-reference.md +132 -207
- package/skills/fui-skill/references/tools-registry.md +84 -460
- package/skills/fui-skill/references/ui-crosswindow-patterns.md +79 -75
- package/skills/fui-skill/references/ui-dialog-patterns.md +98 -72
- package/skills/fui-skill/references/ui-layout-patterns.md +26 -26
- package/skills/fui-skill/references/ui-patterns.md +71 -83
- package/skills/fui-skill/references/ui-screenshot-review.md +63 -62
- package/skills/fui-skill/references/ui-table-cell-patterns.md +61 -59
- package/skills/fui-skill/references/ui-templates.md +16 -23
- package/skills/fui-skill/references/verification.md +236 -246
- package/skills/fui-skill/references/watcher-patterns.md +30 -63
- package/skills/fui-skill/references/websocket-realtime.md +83 -66
- package/skills/fui-skill/scripts/component-3.0.js +298 -131
- package/skills/fui-skill/scripts/component.js +277 -271
- package/skills/fui-skill/scripts/componentTable-3.0.js +182 -53
- package/skills/fui-skill/scripts/componentTable.js +171 -49
- package/skills/fui-skill/scripts/defaultfunction-3.0.js +88 -3
- package/skills/fui-skill/scripts/defaultfunction.js +88 -3
- package/skills/fui-skill/scripts/fsheet.js +38 -0
- package/dist/fui-x7kph78r.js +0 -420
- package/skills/fui-skill/README.md +0 -112
- package/skills/fui-skill/metadata.json +0 -75
- package/skills/fui-skill-cli/SKILL.md +0 -139
- package/skills/fui-skill-cli/references/INDEX.md +0 -110
- package/skills/fui-skill-cli/references/advanced-techniques.md +0 -168
- package/skills/fui-skill-cli/references/coding-standards.md +0 -112
- package/skills/fui-skill-cli/references/component-design.md +0 -448
- package/skills/fui-skill-cli/references/component-quickref.md +0 -78
- package/skills/fui-skill-cli/references/component-table.md +0 -248
- package/skills/fui-skill-cli/references/components-dialog.md +0 -191
- package/skills/fui-skill-cli/references/components-display.md +0 -141
- package/skills/fui-skill-cli/references/components-echart.md +0 -316
- package/skills/fui-skill-cli/references/components-input.md +0 -335
- package/skills/fui-skill-cli/references/controls-patterns.md +0 -701
- package/skills/fui-skill-cli/references/controls-styling-vocabulary.md +0 -137
- package/skills/fui-skill-cli/references/db-table-design.md +0 -73
- package/skills/fui-skill-cli/references/db-workflow.md +0 -288
- package/skills/fui-skill-cli/references/default-function.md +0 -425
- package/skills/fui-skill-cli/references/design-modes.md +0 -57
- package/skills/fui-skill-cli/references/echart-templates.md +0 -489
- package/skills/fui-skill-cli/references/fastproject.md +0 -99
- package/skills/fui-skill-cli/references/fsheet.md +0 -203
- package/skills/fui-skill-cli/references/fullstack-workflow.md +0 -313
- package/skills/fui-skill-cli/references/module-data-patterns.md +0 -117
- package/skills/fui-skill-cli/references/module-json-anatomy.md +0 -132
- package/skills/fui-skill-cli/references/module-structure.md +0 -141
- package/skills/fui-skill-cli/references/new-session.md +0 -85
- package/skills/fui-skill-cli/references/pdfmake.md +0 -60
- package/skills/fui-skill-cli/references/permission-system.md +0 -150
- package/skills/fui-skill-cli/references/platform-architecture.md +0 -269
- package/skills/fui-skill-cli/references/project-config.md +0 -303
- package/skills/fui-skill-cli/references/project-provisioning.md +0 -278
- package/skills/fui-skill-cli/references/script-map.md +0 -262
- package/skills/fui-skill-cli/references/sql-clr-functions.md +0 -225
- package/skills/fui-skill-cli/references/system-design.md +0 -89
- package/skills/fui-skill-cli/references/tapi-file-api.md +0 -185
- package/skills/fui-skill-cli/references/tapi-permission-patterns.md +0 -156
- package/skills/fui-skill-cli/references/tapi-reference.md +0 -474
- package/skills/fui-skill-cli/references/tools-registry.md +0 -84
- package/skills/fui-skill-cli/references/ui-crosswindow-patterns.md +0 -321
- package/skills/fui-skill-cli/references/ui-dialog-patterns.md +0 -255
- package/skills/fui-skill-cli/references/ui-layout-patterns.md +0 -176
- package/skills/fui-skill-cli/references/ui-patterns.md +0 -303
- package/skills/fui-skill-cli/references/ui-screenshot-review.md +0 -95
- package/skills/fui-skill-cli/references/ui-table-cell-patterns.md +0 -318
- package/skills/fui-skill-cli/references/ui-templates.md +0 -22
- package/skills/fui-skill-cli/references/verification.md +0 -236
- package/skills/fui-skill-cli/references/watcher-patterns.md +0 -163
- 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
|
-
>
|
|
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
|
-
>
|
|
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:
|
|
21
|
-
yAxis: { type:
|
|
22
|
-
series: [{ type:
|
|
23
|
-
tooltip: { trigger:
|
|
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 (
|
|
25
|
+
### Line chart (smooth)
|
|
30
26
|
|
|
31
27
|
```js
|
|
32
28
|
vueData.chartOpt = {
|
|
33
|
-
xAxis: { type:
|
|
34
|
-
yAxis: { type:
|
|
35
|
-
series: [{ type:
|
|
36
|
-
tooltip: { trigger:
|
|
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:
|
|
45
|
-
yAxis: [{ name:
|
|
40
|
+
xAxis: { type: "category", data: d.data.map((r) => r.thang) },
|
|
41
|
+
yAxis: [{ name: "VNĐ" }, { name: "%", position: "right" }],
|
|
46
42
|
series: [
|
|
47
|
-
{ type:
|
|
48
|
-
{ type:
|
|
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:
|
|
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:
|
|
60
|
-
yAxis: { type:
|
|
55
|
+
xAxis: { type: "category", data: d.data.map((r) => r.thang) },
|
|
56
|
+
yAxis: { type: "value" },
|
|
61
57
|
series: [
|
|
62
|
-
{ type:
|
|
63
|
-
{ type:
|
|
64
|
-
{ type:
|
|
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:
|
|
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
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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:
|
|
93
|
-
yAxis: { type:
|
|
94
|
-
series: [{ type:
|
|
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
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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:
|
|
123
|
-
{ name:
|
|
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
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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:
|
|
146
|
-
yAxis: { type:
|
|
151
|
+
xAxis: { type: "category", data: xData },
|
|
152
|
+
yAxis: { type: "category", data: yData },
|
|
147
153
|
visualMap: { min: 0, max: 10, calculable: true },
|
|
148
|
-
series: [
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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:
|
|
161
|
-
yAxis: { type:
|
|
162
|
-
series: [
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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
|
|
214
|
+
### Tree (org chart)
|
|
201
215
|
|
|
202
216
|
```js
|
|
203
217
|
vueData.chartOpt = {
|
|
204
|
-
series: [
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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:
|
|
258
|
-
visualMap: { min: 0, max: 30, type:
|
|
259
|
-
series: [
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
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:
|
|
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(
|
|
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 (
|
|
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:
|
|
305
|
-
{ type:
|
|
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:
|
|
308
|
-
yAxis: [{}, { position:
|
|
309
|
-
tooltip: { trigger:
|
|
329
|
+
xAxis: { type: "category" },
|
|
330
|
+
yAxis: [{}, { position: "right" }],
|
|
331
|
+
tooltip: { trigger: "axis" },
|
|
310
332
|
};
|
|
311
333
|
```
|
|
312
334
|
|
|
313
335
|
---
|
|
314
336
|
|
|
315
|
-
##
|
|
316
|
-
|
|
317
|
-
Hai pattern chính khi dùng `f-echart` trong module.json:
|
|
337
|
+
## Real-world patterns
|
|
318
338
|
|
|
319
|
-
| Pattern
|
|
320
|
-
|
|
321
|
-
| **Inline** —
|
|
322
|
-
| **vueData ref** — `option`
|
|
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 (
|
|
344
|
+
### Pattern 1 — Inline pie (no script.js)
|
|
327
345
|
|
|
328
|
-
|
|
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`
|
|
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
|
-
|
|
385
|
-
- `
|
|
386
|
-
-
|
|
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",
|
|
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:
|
|
419
|
+
xAxis: { type: "category", data: vueData.rows.map((r) => r.thang) },
|
|
413
420
|
yAxis: [
|
|
414
|
-
{ type:
|
|
415
|
-
{ type:
|
|
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:
|
|
419
|
-
{ type:
|
|
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:
|
|
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
|
-
|
|
436
|
+
Several data sources → guard against rendering empty data.
|
|
432
437
|
|
|
433
|
-
**module.json controls:**
|
|
434
438
|
```json
|
|
435
439
|
{
|
|
436
|
-
"el": "f-echart",
|
|
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:
|
|
449
|
-
yAxis: { type:
|
|
450
|
-
tooltip: { trigger:
|
|
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:
|
|
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)`
|
|
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
|
-
|
|
476
|
+
Most production charts omit `dataView`. There is no `:data` prop.
|
|
470
477
|
|
|
471
478
|
```json
|
|
472
479
|
{
|
|
473
|
-
"el": "f-echart",
|
|
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
|
-
|
|
489
|
+
Add `dataView: true` only when the user needs the data table (auto-extracted from `option`).
|