@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,489 +0,0 @@
|
|
|
1
|
-
# f-echart — Chart Type Templates & Real-World Patterns
|
|
2
|
-
|
|
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).
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## Templates — Chart types
|
|
8
|
-
|
|
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`.
|
|
10
|
-
|
|
11
|
-
### Bar chart
|
|
12
|
-
|
|
13
|
-
```js
|
|
14
|
-
// script.js — callback sau fetch
|
|
15
|
-
vueData.chartOpt = {
|
|
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: {},
|
|
21
|
-
};
|
|
22
|
-
vueData.rows = d.data;
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
### Line chart (smooth)
|
|
26
|
-
|
|
27
|
-
```js
|
|
28
|
-
vueData.chartOpt = {
|
|
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" },
|
|
33
|
-
};
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
### Multi-series + Dual Y-axis
|
|
37
|
-
|
|
38
|
-
```js
|
|
39
|
-
vueData.chartOpt = {
|
|
40
|
-
xAxis: { type: "category", data: d.data.map((r) => r.thang) },
|
|
41
|
-
yAxis: [{ name: "VNĐ" }, { name: "%", position: "right" }],
|
|
42
|
-
series: [
|
|
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 },
|
|
45
|
-
],
|
|
46
|
-
tooltip: { trigger: "axis" },
|
|
47
|
-
legend: {},
|
|
48
|
-
};
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
### Stacked bar
|
|
52
|
-
|
|
53
|
-
```js
|
|
54
|
-
vueData.chartOpt = {
|
|
55
|
-
xAxis: { type: "category", data: d.data.map((r) => r.thang) },
|
|
56
|
-
yAxis: { type: "value" },
|
|
57
|
-
series: [
|
|
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" },
|
|
61
|
-
],
|
|
62
|
-
tooltip: { trigger: "axis", axisPointer: { type: "shadow" } },
|
|
63
|
-
legend: {},
|
|
64
|
-
};
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
### Pie chart
|
|
68
|
-
|
|
69
|
-
```js
|
|
70
|
-
vueData.chartOpt = {
|
|
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 },
|
|
81
|
-
};
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
Doughnut: `radius: ['40%', '70%']`
|
|
85
|
-
|
|
86
|
-
### Scatter plot
|
|
87
|
-
|
|
88
|
-
```js
|
|
89
|
-
vueData.chartOpt = {
|
|
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 }],
|
|
93
|
-
tooltip: {
|
|
94
|
-
formatter: (p) => `${p.value[2]}<br>Điểm: ${p.value[0]} — GPA: ${p.value[1]}`,
|
|
95
|
-
},
|
|
96
|
-
};
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
### Gauge
|
|
100
|
-
|
|
101
|
-
```js
|
|
102
|
-
vueData.chartOpt = {
|
|
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: {},
|
|
114
|
-
};
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
### Radar
|
|
118
|
-
|
|
119
|
-
```js
|
|
120
|
-
vueData.chartOpt = {
|
|
121
|
-
radar: {
|
|
122
|
-
indicator: [
|
|
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
|
-
],
|
|
129
|
-
},
|
|
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
|
-
],
|
|
139
|
-
tooltip: {},
|
|
140
|
-
legend: {},
|
|
141
|
-
};
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
### Heatmap (Cartesian)
|
|
145
|
-
|
|
146
|
-
```js
|
|
147
|
-
// data: [{x: 'Mon', y: '8h', value: 5}, ...]
|
|
148
|
-
const xData = [...new Set(d.data.map((r) => r.x))];
|
|
149
|
-
const yData = [...new Set(d.data.map((r) => r.y))];
|
|
150
|
-
vueData.chartOpt = {
|
|
151
|
-
xAxis: { type: "category", data: xData },
|
|
152
|
-
yAxis: { type: "category", data: yData },
|
|
153
|
-
visualMap: { min: 0, max: 10, calculable: true },
|
|
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: {},
|
|
161
|
-
};
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
### Candlestick (OHLC)
|
|
165
|
-
|
|
166
|
-
```js
|
|
167
|
-
vueData.chartOpt = {
|
|
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" },
|
|
177
|
-
};
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
### Funnel
|
|
181
|
-
|
|
182
|
-
```js
|
|
183
|
-
vueData.chartOpt = {
|
|
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: {},
|
|
193
|
-
};
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
### Treemap
|
|
197
|
-
|
|
198
|
-
```js
|
|
199
|
-
// data là hierarchical: { name, value, children: [...] }
|
|
200
|
-
vueData.chartOpt = {
|
|
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: {},
|
|
211
|
-
};
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
### Tree (org chart)
|
|
215
|
-
|
|
216
|
-
```js
|
|
217
|
-
vueData.chartOpt = {
|
|
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" },
|
|
230
|
-
};
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
### Sankey
|
|
234
|
-
|
|
235
|
-
```js
|
|
236
|
-
// nodes: [{name: '...'}], links: [{source: '...', target: '...', value: N}]
|
|
237
|
-
vueData.chartOpt = {
|
|
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" },
|
|
248
|
-
};
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
### Graph (network)
|
|
252
|
-
|
|
253
|
-
```js
|
|
254
|
-
vueData.chartOpt = {
|
|
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
|
-
],
|
|
267
|
-
legend: {},
|
|
268
|
-
tooltip: {},
|
|
269
|
-
};
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
### Calendar heatmap
|
|
273
|
-
|
|
274
|
-
```js
|
|
275
|
-
// data: [{date: '2024-01-01', count: 5}, ...]
|
|
276
|
-
vueData.chartOpt = {
|
|
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]}` },
|
|
287
|
-
};
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
---
|
|
291
|
-
|
|
292
|
-
## Zoom + Tooltip format
|
|
293
|
-
|
|
294
|
-
```js
|
|
295
|
-
// Zoom (trong option)
|
|
296
|
-
vueData.chartOpt = {
|
|
297
|
-
...baseOpt,
|
|
298
|
-
dataZoom: [{ type: "inside" }, { type: "slider", height: 20 }],
|
|
299
|
-
};
|
|
300
|
-
// Hoặc dùng config shortcut:
|
|
301
|
-
// config: { zoom: true }
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
```js
|
|
305
|
-
// Tooltip tiền tệ — dùng config shortcut
|
|
306
|
-
// config: { tooltipFormat: 'currency', symbol: '₫', decimals: 0 }
|
|
307
|
-
|
|
308
|
-
// Hoặc custom formatter trong option
|
|
309
|
-
tooltip: {
|
|
310
|
-
formatter: (p) => {
|
|
311
|
-
const fmt = FEchartFormatter.build("currency", { symbol: "₫", decimals: 0 });
|
|
312
|
-
return `${p.name}: ${fmt(p.value)}`;
|
|
313
|
-
};
|
|
314
|
-
}
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
---
|
|
318
|
-
|
|
319
|
-
## Dataset + encode (arbitrary column mapping)
|
|
320
|
-
|
|
321
|
-
```js
|
|
322
|
-
// script.js — build option dùng dataset (không map thủ công)
|
|
323
|
-
vueData.chartOpt = {
|
|
324
|
-
dataset: { source: vueData.rows },
|
|
325
|
-
series: [
|
|
326
|
-
{ type: "bar", name: "Doanh thu", encode: { x: "thang", y: "doanhThu" } },
|
|
327
|
-
{ type: "line", name: "Chi phí", encode: { x: "thang", y: "chiPhi" }, yAxisIndex: 1 },
|
|
328
|
-
],
|
|
329
|
-
xAxis: { type: "category" },
|
|
330
|
-
yAxis: [{}, { position: "right" }],
|
|
331
|
-
tooltip: { trigger: "axis" },
|
|
332
|
-
};
|
|
333
|
-
```
|
|
334
|
-
|
|
335
|
-
---
|
|
336
|
-
|
|
337
|
-
## Real-world patterns
|
|
338
|
-
|
|
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 |
|
|
343
|
-
|
|
344
|
-
### Pattern 1 — Inline pie (no script.js)
|
|
345
|
-
|
|
346
|
-
Data already in `vueData.rows` → `option` in the attr, no `chartOpt`/script.js.
|
|
347
|
-
|
|
348
|
-
```json
|
|
349
|
-
{
|
|
350
|
-
"el": "f-echart", "w": "6",
|
|
351
|
-
"attr": {
|
|
352
|
-
":config": "{ title: 'Cơ cấu sinh viên', height: 350 }",
|
|
353
|
-
":option": "{
|
|
354
|
-
series: [{
|
|
355
|
-
type: 'pie',
|
|
356
|
-
data: vueData.rows.map(r => ({ name: r.khoa, value: r.soLuong })),
|
|
357
|
-
radius: ['40%', '70%'],
|
|
358
|
-
label: { formatter: '{b}: {d}%' }
|
|
359
|
-
}],
|
|
360
|
-
tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' },
|
|
361
|
-
legend: { bottom: 0 }
|
|
362
|
-
}"
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
```
|
|
366
|
-
|
|
367
|
-
> `vueData.rows` must be assigned first (e.g. from another control's fetch callback in the same module).
|
|
368
|
-
|
|
369
|
-
### Pattern 2 — Inline horizontal bar
|
|
370
|
-
|
|
371
|
-
```json
|
|
372
|
-
{
|
|
373
|
-
"el": "f-echart", "w": "12",
|
|
374
|
-
"attr": {
|
|
375
|
-
":config": "{ height: Math.max(300, vueData.rows.length * 36) }",
|
|
376
|
-
":option": "{
|
|
377
|
-
xAxis: { type: 'value' },
|
|
378
|
-
yAxis: {
|
|
379
|
-
type: 'category',
|
|
380
|
-
data: vueData.rows.map(r => r.tenKhoa),
|
|
381
|
-
inverse: true
|
|
382
|
-
},
|
|
383
|
-
grid: { containLabel: true },
|
|
384
|
-
series: [{
|
|
385
|
-
type: 'bar',
|
|
386
|
-
data: vueData.rows.map(r => r.soLuong),
|
|
387
|
-
name: 'Sinh viên',
|
|
388
|
-
label: { show: true, position: 'right' }
|
|
389
|
-
}],
|
|
390
|
-
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } }
|
|
391
|
-
}"
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
```
|
|
395
|
-
|
|
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.
|
|
399
|
-
|
|
400
|
-
### Pattern 3 — vueData ref + dynamic height (API fetch)
|
|
401
|
-
|
|
402
|
-
```json
|
|
403
|
-
{
|
|
404
|
-
"el": "f-echart",
|
|
405
|
-
"w": "12",
|
|
406
|
-
"attr": {
|
|
407
|
-
":config": "{ title: 'Doanh thu theo tháng', height: 400, dataView: true }",
|
|
408
|
-
":option": "vueData.chartOpt"
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
```
|
|
412
|
-
|
|
413
|
-
**script.js:**
|
|
414
|
-
|
|
415
|
-
```js
|
|
416
|
-
// Trong callback fetch data
|
|
417
|
-
vueData.rows = d.data.slice().sort((a, b) => a.thang.localeCompare(b.thang));
|
|
418
|
-
vueData.chartOpt = {
|
|
419
|
-
xAxis: { type: "category", data: vueData.rows.map((r) => r.thang) },
|
|
420
|
-
yAxis: [
|
|
421
|
-
{ type: "value", name: "Triệu đồng", axisLabel: { formatter: (v) => FEchartFormatter.formatShort(v) } },
|
|
422
|
-
{ type: "value", name: "%", position: "right", max: 100 },
|
|
423
|
-
],
|
|
424
|
-
series: [
|
|
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 },
|
|
427
|
-
],
|
|
428
|
-
grid: { containLabel: true },
|
|
429
|
-
tooltip: { trigger: "axis" },
|
|
430
|
-
legend: {},
|
|
431
|
-
};
|
|
432
|
-
```
|
|
433
|
-
|
|
434
|
-
### Pattern 4 — Conditional option (null guard)
|
|
435
|
-
|
|
436
|
-
Several data sources → guard against rendering empty data.
|
|
437
|
-
|
|
438
|
-
```json
|
|
439
|
-
{
|
|
440
|
-
"el": "f-echart",
|
|
441
|
-
"w": "12",
|
|
442
|
-
"attr": {
|
|
443
|
-
":config": "{ height: 350 }",
|
|
444
|
-
":option": "vueData.baseOpt ? Object.assign({}, vueData.baseOpt, { series: vueData.series }) : null"
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
```
|
|
448
|
-
|
|
449
|
-
**script.js:**
|
|
450
|
-
|
|
451
|
-
```js
|
|
452
|
-
// Base option — khởi tạo một lần
|
|
453
|
-
vueData.baseOpt = {
|
|
454
|
-
xAxis: { type: "category", data: [] },
|
|
455
|
-
yAxis: { type: "value" },
|
|
456
|
-
tooltip: { trigger: "axis" },
|
|
457
|
-
legend: {},
|
|
458
|
-
grid: { containLabel: true },
|
|
459
|
-
};
|
|
460
|
-
|
|
461
|
-
// Khi fetch data xong — reactive update
|
|
462
|
-
vueData.baseOpt.xAxis.data = d.data.map((r) => r.thang);
|
|
463
|
-
vueData.series = d.data.map((r, i) => ({
|
|
464
|
-
type: "bar",
|
|
465
|
-
name: r.name,
|
|
466
|
-
data: r.values,
|
|
467
|
-
stack: "total",
|
|
468
|
-
}));
|
|
469
|
-
// Object.assign trong :option tạo object mới → Vue trigger re-render
|
|
470
|
-
```
|
|
471
|
-
|
|
472
|
-
> `Object.assign({}, base, override)` in `:option` yields a new object each evaluation, so Vue detects the change.
|
|
473
|
-
|
|
474
|
-
### Pattern 5 — No DataView (most common)
|
|
475
|
-
|
|
476
|
-
Most production charts omit `dataView`. There is no `:data` prop.
|
|
477
|
-
|
|
478
|
-
```json
|
|
479
|
-
{
|
|
480
|
-
"el": "f-echart",
|
|
481
|
-
"w": "6",
|
|
482
|
-
"attr": {
|
|
483
|
-
":config": "{ height: 300 }",
|
|
484
|
-
":option": "vueData.pieOpt"
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
```
|
|
488
|
-
|
|
489
|
-
Add `dataView: true` only when the user needs the data table (auto-extracted from `option`).
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
# FastProject (Core Framework)
|
|
2
|
-
|
|
3
|
-
> Owns: **the `fastproject.js` bootstrapper: project/module init lifecycle**. Full script map: [script-map.md](script-map.md).
|
|
4
|
-
|
|
5
|
-
`fastproject.js` initializes the app, builds UI from JSON config, and runs the logic engine.
|
|
6
|
-
|
|
7
|
-
## Global State
|
|
8
|
-
|
|
9
|
-
### vueData
|
|
10
|
-
|
|
11
|
-
Central reactive state.
|
|
12
|
-
|
|
13
|
-
- `p_domain`, `p_routers`, `p_params`: routing and environment.
|
|
14
|
-
- `user`: current user.
|
|
15
|
-
- `v_Set`: app settings (title, menu, attributes).
|
|
16
|
-
- `v_Loading`: global loading state.
|
|
17
|
-
|
|
18
|
-
### vueOBJ
|
|
19
|
-
|
|
20
|
-
Vue instance config object.
|
|
21
|
-
|
|
22
|
-
## Core Functions
|
|
23
|
-
|
|
24
|
-
### loadModuleInfo()
|
|
25
|
-
|
|
26
|
-
1. Load project and module settings.
|
|
27
|
-
2. Build URL parameters.
|
|
28
|
-
3. Fetch user info (if configured).
|
|
29
|
-
4. Call `createModuleDom()`.
|
|
30
|
-
|
|
31
|
-
### createModuleDom()
|
|
32
|
-
|
|
33
|
-
1. Run initial data actions (`$moduleUI.data`).
|
|
34
|
-
2. Generate HTML via `buildModuleUI`.
|
|
35
|
-
3. Init Vue watchers and WebSocket connections.
|
|
36
|
-
4. Mount the Vue instance.
|
|
37
|
-
|
|
38
|
-
### buildModuleUI(controlsList, target)
|
|
39
|
-
|
|
40
|
-
Recursively builds DOM from a JSON control list.
|
|
41
|
-
|
|
42
|
-
- `controlsList` (Array): control definitions.
|
|
43
|
-
- `target` (Object): binding target (usually `vueData`).
|
|
44
|
-
- Returns: jQuery object with the generated HTML.
|
|
45
|
-
|
|
46
|
-
### buildControl(controlsList, flex, target)
|
|
47
|
-
|
|
48
|
-
`buildModuleUI` helper for individual controls.
|
|
49
|
-
|
|
50
|
-
- Grid layout (`v-layout`, `v-flex`) and nested components.
|
|
51
|
-
- Attribute binding and event mapping.
|
|
52
|
-
|
|
53
|
-
### runAction(obj, includeData)
|
|
54
|
-
|
|
55
|
-
Central logic engine; runs a JSON action sequence.
|
|
56
|
-
|
|
57
|
-
- `obj` (Object|Array): action(s).
|
|
58
|
-
- `includeData` (Object): context data.
|
|
59
|
-
- Action types: `CONFIRM` (confirm dialog), `API` (call API), `CALL` (another action, recursive), `EXE` (raw JS), `FUN` (global function), `IF/THEN/ELSE`, `MESS`/`MESSBOX` (messages), `IN`/`OUT` (data mapping).
|
|
60
|
-
|
|
61
|
-
### vueAction(objAction, includeData, callBack)
|
|
62
|
-
|
|
63
|
-
Internal; runs a single action step for `runAction`.
|
|
64
|
-
|
|
65
|
-
### callAPI(objApi, callBack, includeData)
|
|
66
|
-
|
|
67
|
-
Runs an API call from a JSON action.
|
|
68
|
-
|
|
69
|
-
- Handles `BEFORE`, `AFTER` actions, success/error callbacks.
|
|
70
|
-
- Maps response into `vueData` when `OUT` is set.
|
|
71
|
-
|
|
72
|
-
### mapData(map, target, src, includeData)
|
|
73
|
-
|
|
74
|
-
Maps source → target by a mapping definition.
|
|
75
|
-
|
|
76
|
-
- Cross-window mapping via `#PARENT` or window IDs.
|
|
77
|
-
- `map` (Object|String): mapping; `target` (Object): destination; `src` (Object): source.
|
|
78
|
-
|
|
79
|
-
### getVueData(key, src)
|
|
80
|
-
|
|
81
|
-
Resolves a value from a key string; deep paths and template syntax (e.g. `{{user.Name}}`).
|
|
82
|
-
|
|
83
|
-
### bindData(obj)
|
|
84
|
-
|
|
85
|
-
Resolves all dynamic values in an object.
|
|
86
|
-
|
|
87
|
-
### createWatch(obj)
|
|
88
|
-
|
|
89
|
-
Vue watchers from JSON config; supports deep watch; runs actions on change.
|
|
90
|
-
|
|
91
|
-
### loginFUN()
|
|
92
|
-
|
|
93
|
-
Login redirect: opens a login window or redirects to the login page.
|
|
94
|
-
|
|
95
|
-
### errorMess(code, message)
|
|
96
|
-
|
|
97
|
-
Standard error message by HTTP status.
|
|
98
|
-
|
|
99
|
-
- `code` (String|Number): HTTP status; `message` (String): custom message.
|