@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,481 @@
|
|
|
1
|
+
# f-echart — Chart Type Templates & Real-World Patterns
|
|
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`.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Templates — Chart types
|
|
12
|
+
|
|
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).
|
|
14
|
+
|
|
15
|
+
### Bar chart
|
|
16
|
+
|
|
17
|
+
```js
|
|
18
|
+
// script.js — callback sau fetch
|
|
19
|
+
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: {}
|
|
25
|
+
};
|
|
26
|
+
vueData.rows = d.data;
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Line chart (có smooth)
|
|
30
|
+
|
|
31
|
+
```js
|
|
32
|
+
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' }
|
|
37
|
+
};
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Multi-series + Dual Y-axis
|
|
41
|
+
|
|
42
|
+
```js
|
|
43
|
+
vueData.chartOpt = {
|
|
44
|
+
xAxis: { type: 'category', data: d.data.map(r => r.thang) },
|
|
45
|
+
yAxis: [{ name: 'VNĐ' }, { name: '%', position: 'right' }],
|
|
46
|
+
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 }
|
|
49
|
+
],
|
|
50
|
+
tooltip: { trigger: 'axis' },
|
|
51
|
+
legend: {}
|
|
52
|
+
};
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Stacked bar
|
|
56
|
+
|
|
57
|
+
```js
|
|
58
|
+
vueData.chartOpt = {
|
|
59
|
+
xAxis: { type: 'category', data: d.data.map(r => r.thang) },
|
|
60
|
+
yAxis: { type: 'value' },
|
|
61
|
+
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' }
|
|
65
|
+
],
|
|
66
|
+
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
|
|
67
|
+
legend: {}
|
|
68
|
+
};
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Pie chart
|
|
72
|
+
|
|
73
|
+
```js
|
|
74
|
+
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 }
|
|
83
|
+
};
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Doughnut: `radius: ['40%', '70%']`
|
|
87
|
+
|
|
88
|
+
### Scatter plot
|
|
89
|
+
|
|
90
|
+
```js
|
|
91
|
+
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 }],
|
|
95
|
+
tooltip: {
|
|
96
|
+
formatter: p => `${p.value[2]}<br>Điểm: ${p.value[0]} — GPA: ${p.value[1]}`
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Gauge
|
|
102
|
+
|
|
103
|
+
```js
|
|
104
|
+
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: {}
|
|
113
|
+
};
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Radar
|
|
117
|
+
|
|
118
|
+
```js
|
|
119
|
+
vueData.chartOpt = {
|
|
120
|
+
radar: {
|
|
121
|
+
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
|
+
]
|
|
125
|
+
},
|
|
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
|
+
}],
|
|
133
|
+
tooltip: {},
|
|
134
|
+
legend: {}
|
|
135
|
+
};
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Heatmap (Cartesian)
|
|
139
|
+
|
|
140
|
+
```js
|
|
141
|
+
// 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))];
|
|
144
|
+
vueData.chartOpt = {
|
|
145
|
+
xAxis: { type: 'category', data: xData },
|
|
146
|
+
yAxis: { type: 'category', data: yData },
|
|
147
|
+
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: {}
|
|
153
|
+
};
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Candlestick (OHLC)
|
|
157
|
+
|
|
158
|
+
```js
|
|
159
|
+
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' }
|
|
167
|
+
};
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Funnel
|
|
171
|
+
|
|
172
|
+
```js
|
|
173
|
+
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: {}
|
|
181
|
+
};
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Treemap
|
|
185
|
+
|
|
186
|
+
```js
|
|
187
|
+
// data là hierarchical: { name, value, children: [...] }
|
|
188
|
+
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: {}
|
|
197
|
+
};
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Tree (org chart, org tree)
|
|
201
|
+
|
|
202
|
+
```js
|
|
203
|
+
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' }
|
|
214
|
+
};
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Sankey
|
|
218
|
+
|
|
219
|
+
```js
|
|
220
|
+
// nodes: [{name: '...'}], links: [{source: '...', target: '...', value: N}]
|
|
221
|
+
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' }
|
|
230
|
+
};
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Graph (network)
|
|
234
|
+
|
|
235
|
+
```js
|
|
236
|
+
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
|
+
}],
|
|
247
|
+
legend: {},
|
|
248
|
+
tooltip: {}
|
|
249
|
+
};
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Calendar heatmap
|
|
253
|
+
|
|
254
|
+
```js
|
|
255
|
+
// data: [{date: '2024-01-01', count: 5}, ...]
|
|
256
|
+
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]}` }
|
|
265
|
+
};
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## Zoom + Tooltip format
|
|
271
|
+
|
|
272
|
+
```js
|
|
273
|
+
// Zoom (trong option)
|
|
274
|
+
vueData.chartOpt = {
|
|
275
|
+
...baseOpt,
|
|
276
|
+
dataZoom: [{ type: 'inside' }, { type: 'slider', height: 20 }]
|
|
277
|
+
};
|
|
278
|
+
// Hoặc dùng config shortcut:
|
|
279
|
+
// config: { zoom: true }
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
```js
|
|
283
|
+
// Tooltip tiền tệ — dùng config shortcut
|
|
284
|
+
// config: { tooltipFormat: 'currency', symbol: '₫', decimals: 0 }
|
|
285
|
+
|
|
286
|
+
// Hoặc custom formatter trong option
|
|
287
|
+
tooltip: {
|
|
288
|
+
formatter: p => {
|
|
289
|
+
const fmt = FEchartFormatter.build('currency', { symbol: '₫', decimals: 0 });
|
|
290
|
+
return `${p.name}: ${fmt(p.value)}`;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
## Dataset + encode (khi cần map cột tùy ý)
|
|
298
|
+
|
|
299
|
+
```js
|
|
300
|
+
// script.js — build option dùng dataset (không map thủ công)
|
|
301
|
+
vueData.chartOpt = {
|
|
302
|
+
dataset: { source: vueData.rows },
|
|
303
|
+
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 }
|
|
306
|
+
],
|
|
307
|
+
xAxis: { type: 'category' },
|
|
308
|
+
yAxis: [{}, { position: 'right' }],
|
|
309
|
+
tooltip: { trigger: 'axis' }
|
|
310
|
+
};
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
## Patterns thực tế
|
|
316
|
+
|
|
317
|
+
Hai pattern chính khi dùng `f-echart` trong module.json:
|
|
318
|
+
|
|
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
|
+
---
|
|
325
|
+
|
|
326
|
+
### Pattern 1 — Inline pie (không cần script.js)
|
|
327
|
+
|
|
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.
|
|
329
|
+
|
|
330
|
+
**module.json controls:**
|
|
331
|
+
```json
|
|
332
|
+
{
|
|
333
|
+
"el": "f-echart", "w": "6",
|
|
334
|
+
"attr": {
|
|
335
|
+
":config": "{ title: 'Cơ cấu sinh viên', height: 350 }",
|
|
336
|
+
":option": "{
|
|
337
|
+
series: [{
|
|
338
|
+
type: 'pie',
|
|
339
|
+
data: vueData.rows.map(r => ({ name: r.khoa, value: r.soLuong })),
|
|
340
|
+
radius: ['40%', '70%'],
|
|
341
|
+
label: { formatter: '{b}: {d}%' }
|
|
342
|
+
}],
|
|
343
|
+
tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' },
|
|
344
|
+
legend: { bottom: 0 }
|
|
345
|
+
}"
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
```
|
|
349
|
+
|
|
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
|
+
---
|
|
353
|
+
|
|
354
|
+
### Pattern 2 — Inline horizontal bar
|
|
355
|
+
|
|
356
|
+
Horizontal bar: đổi `xAxis`/`yAxis`, dùng `inverse: true` và `containLabel: true`.
|
|
357
|
+
|
|
358
|
+
**module.json controls:**
|
|
359
|
+
```json
|
|
360
|
+
{
|
|
361
|
+
"el": "f-echart", "w": "12",
|
|
362
|
+
"attr": {
|
|
363
|
+
":config": "{ height: Math.max(300, vueData.rows.length * 36) }",
|
|
364
|
+
":option": "{
|
|
365
|
+
xAxis: { type: 'value' },
|
|
366
|
+
yAxis: {
|
|
367
|
+
type: 'category',
|
|
368
|
+
data: vueData.rows.map(r => r.tenKhoa),
|
|
369
|
+
inverse: true
|
|
370
|
+
},
|
|
371
|
+
grid: { containLabel: true },
|
|
372
|
+
series: [{
|
|
373
|
+
type: 'bar',
|
|
374
|
+
data: vueData.rows.map(r => r.soLuong),
|
|
375
|
+
name: 'Sinh viên',
|
|
376
|
+
label: { show: true, position: 'right' }
|
|
377
|
+
}],
|
|
378
|
+
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } }
|
|
379
|
+
}"
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
```
|
|
383
|
+
|
|
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.
|
|
389
|
+
|
|
390
|
+
---
|
|
391
|
+
|
|
392
|
+
### Pattern 3 — vueData ref + dynamic height (fetch từ API)
|
|
393
|
+
|
|
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
|
+
```json
|
|
398
|
+
{
|
|
399
|
+
"el": "f-echart", "w": "12",
|
|
400
|
+
"attr": {
|
|
401
|
+
":config": "{ title: 'Doanh thu theo tháng', height: 400, dataView: true }",
|
|
402
|
+
":option": "vueData.chartOpt"
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
**script.js:**
|
|
408
|
+
```js
|
|
409
|
+
// Trong callback fetch data
|
|
410
|
+
vueData.rows = d.data.slice().sort((a, b) => a.thang.localeCompare(b.thang));
|
|
411
|
+
vueData.chartOpt = {
|
|
412
|
+
xAxis: { type: 'category', data: vueData.rows.map(r => r.thang) },
|
|
413
|
+
yAxis: [
|
|
414
|
+
{ type: 'value', name: 'Triệu đồng', axisLabel: { formatter: v => FEchartFormatter.formatShort(v) } },
|
|
415
|
+
{ type: 'value', name: '%', position: 'right', max: 100 }
|
|
416
|
+
],
|
|
417
|
+
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 }
|
|
420
|
+
],
|
|
421
|
+
grid: { containLabel: true },
|
|
422
|
+
tooltip: { trigger: 'axis' },
|
|
423
|
+
legend: {}
|
|
424
|
+
};
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
---
|
|
428
|
+
|
|
429
|
+
### Pattern 4 — Conditional option (null guard)
|
|
430
|
+
|
|
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.
|
|
432
|
+
|
|
433
|
+
**module.json controls:**
|
|
434
|
+
```json
|
|
435
|
+
{
|
|
436
|
+
"el": "f-echart", "w": "12",
|
|
437
|
+
"attr": {
|
|
438
|
+
":config": "{ height: 350 }",
|
|
439
|
+
":option": "vueData.baseOpt ? Object.assign({}, vueData.baseOpt, { series: vueData.series }) : null"
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
**script.js:**
|
|
445
|
+
```js
|
|
446
|
+
// Base option — khởi tạo một lần
|
|
447
|
+
vueData.baseOpt = {
|
|
448
|
+
xAxis: { type: 'category', data: [] },
|
|
449
|
+
yAxis: { type: 'value' },
|
|
450
|
+
tooltip: { trigger: 'axis' },
|
|
451
|
+
legend: {},
|
|
452
|
+
grid: { containLabel: true }
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
// Khi fetch data xong — reactive update
|
|
456
|
+
vueData.baseOpt.xAxis.data = d.data.map(r => r.thang);
|
|
457
|
+
vueData.series = d.data.map((r, i) => ({
|
|
458
|
+
type: 'bar', name: r.name, data: r.values, stack: 'total'
|
|
459
|
+
}));
|
|
460
|
+
// Object.assign trong :option tạo object mới → Vue trigger re-render
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
> `Object.assign({}, base, override)` trong `:option` expression — tạo object mới mỗi lần evaluate, đảm bảo Vue detect change.
|
|
464
|
+
|
|
465
|
+
---
|
|
466
|
+
|
|
467
|
+
### Pattern 5 — Chart không có DataView (phổ biến nhất)
|
|
468
|
+
|
|
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).
|
|
470
|
+
|
|
471
|
+
```json
|
|
472
|
+
{
|
|
473
|
+
"el": "f-echart", "w": "6",
|
|
474
|
+
"attr": {
|
|
475
|
+
":config": "{ height: 300 }",
|
|
476
|
+
":option": "vueData.pieOpt"
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
```
|
|
480
|
+
|
|
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`.
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# FastProject (Core Framework)
|
|
2
|
+
|
|
3
|
+
> File này sở hữu: **bootstrapper `fastproject.js`: vòng đời khởi tạo project/module**. Bản đồ toàn bộ script xem [script-map.md](script-map.md).
|
|
4
|
+
|
|
5
|
+
This document references the core framework logic defined in `fastproject.js`. This script is responsible for initializing the application, building the UI from JSON configurations, and handling the logic engine.
|
|
6
|
+
|
|
7
|
+
## Global State
|
|
8
|
+
|
|
9
|
+
### vueData
|
|
10
|
+
The central reactive state object for the application.
|
|
11
|
+
- `p_domain`, `p_routers`, `p_params`: Routing and environment info.
|
|
12
|
+
- `user`: Current user information.
|
|
13
|
+
- `v_Set`: Application settings (title, menu, attributes).
|
|
14
|
+
- `v_Loading`: Global loading state.
|
|
15
|
+
|
|
16
|
+
### vueOBJ
|
|
17
|
+
The Vue instance configuration object.
|
|
18
|
+
|
|
19
|
+
## Core Functions
|
|
20
|
+
|
|
21
|
+
### loadModuleInfo()
|
|
22
|
+
Initializes the module by:
|
|
23
|
+
1. Loading project and module settings.
|
|
24
|
+
2. Building URL parameters.
|
|
25
|
+
3. Fetching user info (if configured).
|
|
26
|
+
4. Calling `createModuleDom()`.
|
|
27
|
+
|
|
28
|
+
### createModuleDom()
|
|
29
|
+
Builds the Vue application structure:
|
|
30
|
+
1. Executes initial data actions (`$moduleUI.data`).
|
|
31
|
+
2. Generates the HTML structure using `buildModuleUI`.
|
|
32
|
+
3. Initializes Vue watchers and WebSocket connections.
|
|
33
|
+
4. Mounts the Vue instance.
|
|
34
|
+
|
|
35
|
+
### buildModuleUI(controlsList, target)
|
|
36
|
+
Recursively builds the DOM structure from a JSON list of controls.
|
|
37
|
+
- **Parameters:**
|
|
38
|
+
- `controlsList` (Array): List of control definitions.
|
|
39
|
+
- `target` (Object): Target object for data binding (usually `vueData`).
|
|
40
|
+
- **Returns:** jQuery object containing the generated HTML.
|
|
41
|
+
|
|
42
|
+
### buildControl(controlsList, flex, target)
|
|
43
|
+
Helper function for `buildModuleUI` to generate individual controls.
|
|
44
|
+
- Supports grid layout (`v-layout`, `v-flex`) and nested components.
|
|
45
|
+
- Handles attribute binding and event mapping.
|
|
46
|
+
|
|
47
|
+
### runAction(obj, includeData)
|
|
48
|
+
The central logic engine. Executes a sequence of actions defined in JSON.
|
|
49
|
+
- **Parameters:**
|
|
50
|
+
- `obj` (Object|Array): The action(s) to execute.
|
|
51
|
+
- `includeData` (Object): Context data.
|
|
52
|
+
- **Supported Action Types:**
|
|
53
|
+
- `CONFIRM`: Show confirmation dialog.
|
|
54
|
+
- `API`: Call an API.
|
|
55
|
+
- `CALL`: Recursive call to another action.
|
|
56
|
+
- `EXE`: Execute raw JavaScript.
|
|
57
|
+
- `FUN`: Call a global function.
|
|
58
|
+
- `IF/THEN/ELSE`: Conditional logic.
|
|
59
|
+
- `MESS`/`MESSBOX`: Show messages.
|
|
60
|
+
- `IN`/`OUT`: Data mapping.
|
|
61
|
+
|
|
62
|
+
### vueAction(objAction, includeData, callBack)
|
|
63
|
+
Internal function used by `runAction` to execute a single action step.
|
|
64
|
+
|
|
65
|
+
### callAPI(objApi, callBack, includeData)
|
|
66
|
+
Executes an API call defined in a JSON action object.
|
|
67
|
+
- Handles `BEFORE`, `AFTER` actions, and success/error callbacks.
|
|
68
|
+
- Automatically maps response data to `vueData` if `OUT` is specified.
|
|
69
|
+
|
|
70
|
+
### mapData(map, target, src, includeData)
|
|
71
|
+
Maps data from a source object to a target object based on a mapping definition.
|
|
72
|
+
- Supports cross-window data mapping using `#PARENT` or window IDs.
|
|
73
|
+
- **Parameters:**
|
|
74
|
+
- `map` (Object|String): Mapping definition.
|
|
75
|
+
- `target` (Object): Destination object.
|
|
76
|
+
- `src` (Object): Source object.
|
|
77
|
+
|
|
78
|
+
### getVueData(key, src)
|
|
79
|
+
Resolves a value from a key string, supporting deep paths and template syntax (e.g., `{{user.Name}}`).
|
|
80
|
+
|
|
81
|
+
### bindData(obj)
|
|
82
|
+
Resolves all dynamic values within an object.
|
|
83
|
+
|
|
84
|
+
### createWatch(obj)
|
|
85
|
+
Sets up Vue watchers based on a JSON configuration.
|
|
86
|
+
- Supports deep watching.
|
|
87
|
+
- Executes actions when watched values change.
|
|
88
|
+
|
|
89
|
+
### loginFUN()
|
|
90
|
+
Handles user login redirection.
|
|
91
|
+
- Opens a login window or redirects to the login page.
|
|
92
|
+
|
|
93
|
+
### errorMess(code, message)
|
|
94
|
+
Displays standard error messages based on HTTP status codes.
|
|
95
|
+
- **Parameters:**
|
|
96
|
+
- `code` (String|Number): HTTP status code.
|
|
97
|
+
- `message` (String): Custom error message.
|