@fui-org/fui-cli 2.1.3 → 2.2.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 +27 -7
- package/dist/fui-qrb1j9jz.js +397 -0
- package/dist/fui.js +1 -1
- package/package.json +1 -1
- package/skills/fui/SKILL.md +143 -38
- package/skills/fui/agents/fui-builder.md +62 -0
- package/skills/fui/agents/fui-tester.md +77 -0
- package/skills/{fui-skill → fui}/assets/projectdefaultstyle.css +489 -489
- package/skills/{fui-skill → fui}/examples/component.vue +162 -162
- package/skills/{fui-skill → fui}/references/INDEX.md +5 -4
- package/skills/{fui-skill → fui}/references/advanced-techniques.md +170 -168
- package/skills/{fui-skill → fui}/references/component-design.md +2 -2
- package/skills/{fui-skill → fui}/references/component-quickref.md +78 -78
- package/skills/{fui-skill → fui}/references/component-table.md +287 -287
- package/skills/{fui-skill → fui}/references/components-dialog.md +191 -191
- package/skills/{fui-skill → fui}/references/components-display.md +141 -141
- package/skills/{fui-skill → fui}/references/components-echart.md +316 -316
- package/skills/{fui-skill → fui}/references/components-input.md +335 -335
- package/skills/{fui-skill → fui}/references/controls-patterns.md +2 -2
- package/skills/{fui-skill → fui}/references/controls-styling-vocabulary.md +173 -173
- package/skills/{fui-skill → fui}/references/db-workflow.md +9 -9
- package/skills/{fui-skill → fui}/references/default-function.md +456 -456
- package/skills/{fui-skill → fui}/references/design-modes.md +57 -57
- package/skills/{fui-skill → fui}/references/echart-templates.md +489 -489
- package/skills/{fui-skill → fui}/references/fastproject.md +99 -99
- package/skills/{fui-skill → fui}/references/fullstack-workflow.md +314 -313
- package/skills/{fui-skill → fui}/references/module-data-patterns.md +117 -117
- package/skills/{fui-skill → fui}/references/module-json-anatomy.md +132 -132
- package/skills/{fui-skill → fui}/references/module-structure.md +144 -144
- package/skills/{fui-skill → fui}/references/new-session.md +1 -2
- package/skills/{fui-skill → fui}/references/pdfmake.md +60 -60
- package/skills/{fui-skill → fui}/references/permission-system.md +1 -1
- package/skills/{fui-skill → fui}/references/platform-architecture.md +269 -269
- package/skills/{fui-skill → fui}/references/project-config.md +303 -303
- package/skills/{fui-skill → fui}/references/project-provisioning.md +4 -4
- package/skills/{fui-skill → fui}/references/script-map.md +262 -262
- package/skills/{fui-skill → fui}/references/sql-clr-functions.md +225 -225
- package/skills/{fui-skill → fui}/references/system-design.md +2 -2
- package/skills/{fui-skill → fui}/references/tapi-permission-patterns.md +156 -156
- package/skills/{fui-skill → fui}/references/tapi-reference.md +474 -474
- package/skills/fui/references/team-run.md +141 -0
- package/skills/fui/references/tools-registry.md +211 -0
- package/skills/{fui-skill → fui}/references/ui-crosswindow-patterns.md +321 -321
- package/skills/{fui-skill → fui}/references/ui-dialog-patterns.md +255 -255
- package/skills/{fui-skill → fui}/references/ui-layout-patterns.md +176 -176
- package/skills/{fui-skill → fui}/references/ui-patterns.md +303 -303
- package/skills/{fui-skill → fui}/references/ui-screenshot-review.md +95 -95
- package/skills/{fui-skill → fui}/references/ui-table-cell-patterns.md +318 -318
- package/skills/{fui-skill → fui}/references/verification.md +11 -11
- package/skills/{fui-skill → fui}/references/watcher-patterns.md +163 -163
- package/skills/{fui-skill → fui}/references/websocket-realtime.md +271 -271
- package/skills/{fui-skill → fui}/scripts/component-3.0.js +2715 -2715
- package/skills/{fui-skill → fui}/scripts/component.js +3147 -3147
- package/skills/{fui-skill → fui}/scripts/componentTable-3.0.js +1038 -1038
- package/skills/{fui-skill → fui}/scripts/componentTable.js +891 -891
- package/skills/{fui-skill → fui}/scripts/defaultfunction-3.0.js +866 -866
- package/skills/{fui-skill → fui}/scripts/defaultfunction.js +1051 -1051
- package/skills/{fui-skill → fui}/scripts/fastproject-3.0.js +870 -870
- package/skills/{fui-skill → fui}/scripts/fastproject.js +828 -828
- package/skills/fui-target/hooks/register.tsx +2 -0
- package/skills/fui-target/types/index.d.ts +1 -0
- package/dist/fui-hk5frcn2.js +0 -381
- package/skills/fui-skill/SKILL.md +0 -139
- package/skills/fui-skill/references/tools-registry.md +0 -84
- /package/skills/{fui-skill → fui}/assets/projectdefaultstyle-3.0.css +0 -0
- /package/skills/{fui-skill → fui}/design-md/airbnb/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/airbnb/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/airtable/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/airtable/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/apple/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/apple/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/asu/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/asu/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/binance/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/binance/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/bmw/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/bmw/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/bmw-m/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/bmw-m/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/bugatti/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/bugatti/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/cal/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/cal/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/claude/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/claude/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/clay/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/clay/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/clickhouse/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/clickhouse/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/cohere/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/cohere/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/coinbase/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/coinbase/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/composio/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/composio/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/cursor/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/cursor/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/elevenlabs/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/elevenlabs/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/expo/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/expo/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/ferrari/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/ferrari/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/figma/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/figma/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/framer/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/framer/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/fui/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/hashicorp/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/hashicorp/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/ibm/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/ibm/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/intercom/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/intercom/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/kraken/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/kraken/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/lamborghini/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/lamborghini/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/linear.app/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/linear.app/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/lovable/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/lovable/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mastercard/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mastercard/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/meta/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/meta/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/minimax/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/minimax/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mintlify/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mintlify/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/miro/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/miro/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mistral.ai/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mistral.ai/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mongodb/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/mongodb/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/nike/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/nike/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/notion/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/notion/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/nvidia/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/nvidia/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/ollama/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/ollama/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/opencode.ai/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/opencode.ai/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/pinterest/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/pinterest/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/playstation/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/playstation/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/posthog/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/posthog/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/raycast/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/raycast/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/renault/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/renault/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/replicate/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/replicate/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/resend/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/resend/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/revolut/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/revolut/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/runwayml/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/runwayml/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/sanity/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/sanity/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/sentry/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/sentry/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/shopify/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/shopify/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/slack/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/spacex/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/spacex/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/spotify/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/spotify/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/starbucks/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/starbucks/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/stripe/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/stripe/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/supabase/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/supabase/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/superhuman/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/superhuman/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/tesla/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/tesla/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/theverge/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/theverge/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/together.ai/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/together.ai/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/uber/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/uber/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/vercel/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/vercel/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/vodafone/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/vodafone/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/voltagent/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/voltagent/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/warp/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/warp/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/webflow/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/webflow/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/wired/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/wired/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/wise/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/wise/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/x.ai/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/x.ai/README.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/zapier/DESIGN.md +0 -0
- /package/skills/{fui-skill → fui}/design-md/zapier/README.md +0 -0
- /package/skills/{fui-skill → fui}/examples/f-table-patterns.json +0 -0
- /package/skills/{fui-skill → fui}/examples/module-patterns.json +0 -0
- /package/skills/{fui-skill → fui}/examples/project-patterns.json +0 -0
- /package/skills/{fui-skill → fui}/references/coding-standards.md +0 -0
- /package/skills/{fui-skill → fui}/references/db-table-design.md +0 -0
- /package/skills/{fui-skill → fui}/references/fsheet.md +0 -0
- /package/skills/{fui-skill → fui}/references/tapi-file-api.md +0 -0
- /package/skills/{fui-skill → fui}/references/ui-templates.md +0 -0
- /package/skills/{fui-skill → fui}/scripts/fechart.js +0 -0
- /package/skills/{fui-skill → fui}/scripts/fsheet.js +0 -0
|
@@ -1,316 +1,316 @@
|
|
|
1
|
-
# f-echart — ECharts V5 (Core)
|
|
2
|
-
|
|
3
|
-
> Owns: **`f-echart` core: import, architecture, props, config, theme, DataView, common errors**. Per-chart templates + module.json patterns: [echart-templates.md](echart-templates.md).
|
|
4
|
-
|
|
5
|
-
Use `f-echart` for all charts: bar, line, pie, scatter, gauge, radar, heatmap, sankey, tree, map, graph...
|
|
6
|
-
|
|
7
|
-
> ⚠️ `:option`/`:config` most often break **"`:attr` takes
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## Import — required, in order
|
|
12
|
-
|
|
13
|
-
Missing → no component, blank page.
|
|
14
|
-
|
|
15
|
-
```
|
|
16
|
-
1. /include/chart/echarts.min.js ← ECharts core (phải đứng TRƯỚC)
|
|
17
|
-
2. /include/chart/fechart.js ← FUI wrapper (phải đứng SAU)
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
`fui import list` → `fui import new` → `fui import delete`: [component-quickref.md](component-quickref.md). Sort = load order; `fechart.js` needs global `echarts` → **larger sort** (e.g. 100 / 110).
|
|
21
|
-
|
|
22
|
-
---
|
|
23
|
-
|
|
24
|
-
## Architecture — Direct Mode only
|
|
25
|
-
|
|
26
|
-
Built **entirely on Apache ECharts V5**; `fechart.js` is a thin wrapper (themes, `config` shortcuts, DataView, events). **`option` is plain ECharts option** — use ECharts knowledge; only `config` is FUI-specific. No Auto mode, no `config.type`.
|
|
27
|
-
|
|
28
|
-
- `option` — **primary**, full ECharts option → `echarts.setOption()`.
|
|
29
|
-
- `config` — FUI shortcuts (height, theme, title, toolbox...); defaults, overridden by `option`.
|
|
30
|
-
|
|
31
|
-
**Merge:** `config` base, then lodash `_.merge` with `option`: objects deep-merge, primitives replace, **arrays merge by index, NOT replace** ⚠️. `config` produces only `color` (`config.color`) and `dataZoom` (`config.zoom`); with both `config.color` and `option.color` the shorter can't drop the longer's tail:
|
|
32
|
-
|
|
33
|
-
```js
|
|
34
|
-
config.color = ["#111", "#222", "#333", "#444"];
|
|
35
|
-
option.color = ["#aaa", "#bbb"];
|
|
36
|
-
// → ['#aaa','#bbb','#333','#444'] ← 2 màu cũ còn sót lại
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
⇒ Palette in **one place** (prefer `config.color`). `series`/`xAxis`/`yAxis`/`grid` never come from `config` — declare freely.
|
|
40
|
-
|
|
41
|
-
Rendered with `setOption(opt, true)` (**notMerge**): each render replaces the whole option.
|
|
42
|
-
|
|
43
|
-
---
|
|
44
|
-
|
|
45
|
-
## DEFAULT conventions (apply unprompted, unless user asks otherwise)
|
|
46
|
-
|
|
47
|
-
1. **Show values** — bar: `label: { show: true, position: 'top' }` (horizontal → `'right'`); line: `label: { show: true, position: 'top' }`.
|
|
48
|
-
2. **Grid**: axis charts default `left: 0`, `right: 0`, `top: 50`, `bottom: 50` (top/bottom auto-grow for title/legend). Declare only sides the user wants changed; `containLabel: true` if labels may clip.
|
|
49
|
-
3. **Full numbers** (`1,500,000`), no `1.5M`/`1.2k` unless asked; tooltip default `formatFull` is correct — don't override.
|
|
50
|
-
4. **Pie**: no long messy labelLines; short labels (name + % if needed) or rely on legend + tooltip.
|
|
51
|
-
5. **No hand-built "View data" button** — `config.dataView: true`.
|
|
52
|
-
|
|
53
|
-
---
|
|
54
|
-
|
|
55
|
-
## Props
|
|
56
|
-
|
|
57
|
-
Only `config` (Object, `{}`) and `option` (Object, `{}`, primary). **No `data` prop.**
|
|
58
|
-
|
|
59
|
-
## config keys
|
|
60
|
-
|
|
61
|
-
| Key | Default | Notes |
|
|
62
|
-
| -------------------------------------------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
63
|
-
| `height` | `400` | number = px; string e.g. `'50vh'` |
|
|
64
|
-
| `theme` | `'fui'` | **Only** `'fui'`\|`'shine'`\|`'v5'`; others (incl. `echarts.registerTheme` names) or `null` **silently → `'fui'`**. Recolor via `config.color` |
|
|
65
|
-
| `backgroundColor` | `'transparent'` | |
|
|
66
|
-
| `title` / `subtitle` | | → `option.title.text` / `.subtext` |
|
|
67
|
-
| `titleAlign` | `'center'` | `'left'`/`'center'`/`'right'` (old alias `titleLeft`) |
|
|
68
|
-
| `titleSize` | `16` | px |
|
|
69
|
-
| `titleColor` | | empty = theme |
|
|
70
|
-
| `titleWeight` | `'bold'` | `'normal'`/`'bold'`/`500`... |
|
|
71
|
-
| `titleFont` | | font-family, title + subtitle |
|
|
72
|
-
| `titleStyle` | | full ECharts textStyle — **wins over all `title*`** |
|
|
73
|
-
| `subtitleSize` / `subtitleColor` / `subtitleStyle` | `12` | same, subtitle |
|
|
74
|
-
| `color` | | palette (string[]) |
|
|
75
|
-
| `animation` | | `false` = off |
|
|
76
|
-
| `animationDuration` / `animationEasing` | | ms / ECharts easing |
|
|
77
|
-
| `legend` | | `false` hide; `true`/Object show, merged with `{ type:'scroll', left:'center' }`. **No top/bottom** — engine sets it |
|
|
78
|
-
| `legendPosition` | `'bottom'` | `'top'`/`'bottom'`; **only when legend enabled via `config.legend`/`config.legendConfig`** |
|
|
79
|
-
| `legendRows` | `1` | rows to reserve; >1 when items wrap. Any legend declaration |
|
|
80
|
-
| `legendConfig` | | extra legend props (after `legend`) |
|
|
81
|
-
| `tooltip` | | merged into `{ confine: true }`. **No default `trigger`** → `'item'`; multi-series axis charts need `tooltip: { trigger: 'axis' }` in `option` |
|
|
82
|
-
| `tooltipFormat` | `'auto'` | `'number'`/`'percent'`/`'currency'`/`'short'`/`'auto'`; `'auto'` = full with separators; `'short'` = `1.5M`, only if asked |
|
|
83
|
-
| `decimals` | | for `'number'`/`'percent'`/`'currency'` |
|
|
84
|
-
| `symbol` | `'₫'` | for `'currency'` |
|
|
85
|
-
| `tooltipFormatter` | | Function, **wins over `tooltipFormat`** |
|
|
86
|
-
| `toolbox` | `false` | saveAsImage + restore |
|
|
87
|
-
| `toolboxConfig` | | override/extend toolbox |
|
|
88
|
-
| `dataView` | `false` | table button (Vue overlay), data from `option` |
|
|
89
|
-
| `zoom` | | dataZoom inside + slider |
|
|
90
|
-
| `zoomConfig` | | Array, override dataZoom |
|
|
91
|
-
| `refreshInterval` | | ms auto-refresh |
|
|
92
|
-
|
|
93
|
-
**Default tooltip needs no config** (centered category title; dot + left label + right value; full numbers `2,535` not `2.5K`). Use `tooltipFormat`/`tooltipFormatter` only for another format.
|
|
94
|
-
|
|
95
|
-
---
|
|
96
|
-
|
|
97
|
-
## Grid padding vs `boundaryGap`
|
|
98
|
-
|
|
99
|
-
- Plot area ↔ canvas edge → `option.grid.top`/`bottom`/`left`/`right`.
|
|
100
|
-
- First/last point/bar ↔ axis ends → `boundaryGap` on that `xAxis`/`yAxis`.
|
|
101
|
-
|
|
102
|
-
No `padding` prop; `boundaryGap` doesn't replace `grid`. Numbers = px, `'10%'` = percent. Cartesian default:
|
|
103
|
-
|
|
104
|
-
```js
|
|
105
|
-
grid: { top: 50, bottom: 50, left: 0, right: 0 }
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
`top`/`bottom` are minimums (engine grows them for title/legend). A side declared in `option.grid` wins and is no longer auto-computed. Only horizontal change → declare only those:
|
|
109
|
-
|
|
110
|
-
```js
|
|
111
|
-
grid: { left: 12, right: 24, containLabel: true }
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
Exact values for all four → declare all four; check title/legend don't overlap:
|
|
115
|
-
|
|
116
|
-
```js
|
|
117
|
-
grid: { top: 40, bottom: 60, left: 12, right: 24, containLabel: true }
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
`containLabel: true` keeps long/edge axis labels inside. `grid` affects only axis charts; pie, funnel, treemap, sankey, graph ignore it → `series.top`/`bottom`/`left`/`right` (or `center`/`radius`).
|
|
121
|
-
|
|
122
|
-
No `boundaryGap` set by `fechart.js`; ECharts defaults: `category` → `true`; `value`/`time`/`log` → `[0, 0]`. Line touching both category ends:
|
|
123
|
-
|
|
124
|
-
```js
|
|
125
|
-
xAxis: { type: 'category', boundaryGap: false, data: labels }
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
Bars usually keep `true`. Horizontal bar → `yAxis.boundaryGap`. Value/time margins:
|
|
129
|
-
|
|
130
|
-
```js
|
|
131
|
-
xAxis: { type: 'time', boundaryGap: ['5%', '5%'] }
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
"Blank space at line ends" → `boundaryGap`; "padding around chart/canvas" → `grid`.
|
|
135
|
-
|
|
136
|
-
---
|
|
137
|
-
|
|
138
|
-
## Vertical layout engine
|
|
139
|
-
|
|
140
|
-
ECharts places title/legend/plot absolutely. After merge, `f-echart` stacks top-down and bottom-up: each present block takes **its height + a gap**; remainder = plot.
|
|
141
|
-
|
|
142
|
-
| Constant | px |
|
|
143
|
-
| ---------------------- | ----------------------------------------- |
|
|
144
|
-
| padding to canvas edge | 8 |
|
|
145
|
-
| min gap between blocks | 12 |
|
|
146
|
-
| grid left / right | 0 / 0 (overridable) |
|
|
147
|
-
| grid top / bottom min | 50 / 50 |
|
|
148
|
-
| title height | `round(fontSize × 1.4)` (16px → 22) |
|
|
149
|
-
| subtitle height | `4 + round(fontSize × 1.4)` (12px → 4+17) |
|
|
150
|
-
| legend row | 24 × `legendRows` |
|
|
151
|
-
| x-axis label reserve | +26 to `grid.bottom` |
|
|
152
|
-
| y-axis label reserve | +10 to `grid.top` |
|
|
153
|
-
|
|
154
|
-
Axis charts get the remainder as `grid.top`/`grid.bottom`; **pie / funnel / treemap / sankey / graph** as `series.top`/`series.bottom`, except a series with px `center` (left untouched).
|
|
155
|
-
|
|
156
|
-
Measured (axis chart, default title):
|
|
157
|
-
|
|
158
|
-
| Config | `title.top` | `legend` | `grid.top` | `grid.bottom` |
|
|
159
|
-
| -------------------------------- | ----------- | ----------- | ---------- | ------------- |
|
|
160
|
-
| no title, no legend | — | — | 50 | 50 |
|
|
161
|
-
| title only | 8 | — | 52 | 50 |
|
|
162
|
-
| title + `config.legend: true` | 8 | `bottom: 8` | 52 | 70 |
|
|
163
|
-
| title + `legendPosition:'top'` | 8 | `top: 42` | 88 | 50 |
|
|
164
|
-
| as above + `legendRows: 2` | 8 | `top: 42` | 112 | 50 |
|
|
165
|
-
| title `titleSize: 24` + subtitle | 8 | — | 85 | 50 |
|
|
166
|
-
|
|
167
|
-
**Legend edge, first match wins:**
|
|
168
|
-
|
|
169
|
-
1. `legend.top` set → top; `legend.bottom` set → bottom (value kept).
|
|
170
|
-
2. Enabled via `config.legend`/`config.legendConfig` → `config.legendPosition` (default **`'bottom'`**).
|
|
171
|
-
3. Only in `option.legend` → **top**; ⚠️ `config.legendPosition` ignored — use `option.legend.bottom: 0` or `config.legend: true`.
|
|
172
|
-
|
|
173
|
-
Don't tweak `grid.top`/`grid.bottom`/`legend.top` to dodge a title — space is re-added. **Module-declared values are always kept** (`grid.top: 5` stays; later blocks stack from it) → declare only for a custom layout. Wrapping legend → `legendRows`.
|
|
174
|
-
|
|
175
|
-
Limits: only **px numbers** are computed (`'10%'`/`'middle'` keep just a minimum); array `option.grid` (multi-grid) is ignored — module handles layout.
|
|
176
|
-
|
|
177
|
-
---
|
|
178
|
-
|
|
179
|
-
## DataView
|
|
180
|
-
|
|
181
|
-
`config.dataView: true` → table icon top-right → overlay table. Data from `option.dataset.source` (object-array or array-of-arrays), else `option.series[].data` + `xAxis.data`/`yAxis.data`; none → button hidden.
|
|
182
|
-
|
|
183
|
-
```json
|
|
184
|
-
{
|
|
185
|
-
"el": "f-echart",
|
|
186
|
-
"w": "12",
|
|
187
|
-
"attr": {
|
|
188
|
-
":config": "{ title: 'Doanh thu', dataView: true, height: 350 }",
|
|
189
|
-
":option": "vueData.chartOpt"
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
Custom **Vue overlay**, **not** ECharts toolbox DataView (`config.toolbox: true` = saveAsImage + restore only). Omit without a clear series array (single-value gauge, sankey node/link, hierarchical tree...).
|
|
195
|
-
|
|
196
|
-
---
|
|
197
|
-
|
|
198
|
-
## Series auto-enhancement (theme policy)
|
|
199
|
-
|
|
200
|
-
| Policy | Applies to | Effect |
|
|
201
|
-
| ----------- | -------------------------- | ------------------------------------------------------------------------------------------- |
|
|
202
|
-
| `lighten` | `bar`, `pie`, `funnel` | 50% lighter fill + original-color border; bar per-series, pie/funnel per-item palette color |
|
|
203
|
-
| `separator` | `treemap`, `sunburst` | faint white border between cells/layers |
|
|
204
|
-
| `outline` | `scatter`, `effectScatter` | white outline around points |
|
|
205
|
-
|
|
206
|
-
Disable: bar → `itemStyle.color` in `option.series[]`; pie item → `itemStyle.color` on each `data[]` item; separator/outline → `itemStyle.borderWidth: 0` in `option.series[]`.
|
|
207
|
-
|
|
208
|
-
---
|
|
209
|
-
|
|
210
|
-
## Theme
|
|
211
|
-
|
|
212
|
-
Built in: `'fui'` (56 colors, **default**), `'shine'` (8, classic axis, strong colors), `'v5'` (9, ECharts V5 default).
|
|
213
|
-
|
|
214
|
-
```json
|
|
215
|
-
":config": "{ theme: 'shine' }"
|
|
216
|
-
":config": "{ theme: 'v5' }"
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
⚠️ **Only these three.** Other names (incl. `echarts.registerTheme('myTheme', …)`) and `null` **silently → `'fui'`**, no error. Recolor with `config.color` (also read by auto-enhancement):
|
|
220
|
-
|
|
221
|
-
```json
|
|
222
|
-
":config": "{ color: ['#e63946', '#457b9d', '#2a9d8f'] }"
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
---
|
|
226
|
-
|
|
227
|
-
## Events & methods
|
|
228
|
-
|
|
229
|
-
Events: `chart-click` (params) · `drilldown` (alias) · `legend-change` (params) · `rendered` (after each `setOption`) · `finished` (animation done).
|
|
230
|
-
|
|
231
|
-
```json
|
|
232
|
-
{ "v-on:drilldown": "CALL(vueData.loadDetail, { row: $event })" }
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
```js
|
|
236
|
-
this.$refs.myChart.getInstance(); // ECharts instance gốc
|
|
237
|
-
this.$refs.myChart.refresh(); // Force re-render (gọi lại setOption)
|
|
238
|
-
this.$refs.myChart.exportImage("chart.png");
|
|
239
|
-
this.$refs.myChart.resizeChart(); // Hiếm khi cần — xem ghi chú dưới
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
`resizeChart()` rarely needed: listens to `window.resize` **and** a `ResizeObserver` on its container (Vuetify grid settle, tab/dialog open, sidebar collapse).
|
|
243
|
-
|
|
244
|
-
```js
|
|
245
|
-
// Native ECharts API
|
|
246
|
-
const chart = vueData.$refs.myChart.getInstance();
|
|
247
|
-
chart.dispatchAction({ type: "highlight", seriesIndex: 0, dataIndex: 2 });
|
|
248
|
-
chart.on("mouseover", (p) => {
|
|
249
|
-
vueData.hoverLabel = p.name;
|
|
250
|
-
});
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
---
|
|
254
|
-
|
|
255
|
-
## FEchartFormatter (global, script.js) — exactly 6 functions
|
|
256
|
-
|
|
257
|
-
```js
|
|
258
|
-
FEchartFormatter.build("currency", { symbol: "₫", decimals: 0 })(1500000); // → '₫1,500,000'
|
|
259
|
-
FEchartFormatter.formatShort(1500000); // → '1.5M' (co gọn — chỉ dùng khi cần tiết kiệm chỗ, vd axis label)
|
|
260
|
-
FEchartFormatter.formatNumber(1500000); // → '1,500,000' (decimals thứ 2, mặc định 0)
|
|
261
|
-
FEchartFormatter.formatFull(2535.678); // → '2,535.68' (đầy đủ, tối đa 2 số thập phân — mặc định của tooltip)
|
|
262
|
-
// Tạo tooltip formatter từ config.tooltipFormat ('auto'/không khai → formatFull, số đầy đủ)
|
|
263
|
-
FEchartFormatter.buildTooltip({ tooltipFormat: "currency", symbol: "₫" });
|
|
264
|
-
FEchartFormatter.buildDefault(); // formatter tooltip mặc định (formatFull)
|
|
265
|
-
```
|
|
266
|
-
|
|
267
|
-
- **No** `formatPercent`/`formatCurrency` → `build('percent', { decimals })` / `build('currency', { symbol, decimals })`.
|
|
268
|
-
- Bare `FEchartFormatter` only; `window.FEchartFormatter` is `undefined` (script-level `const`).
|
|
269
|
-
|
|
270
|
-
---
|
|
271
|
-
|
|
272
|
-
## Sorting
|
|
273
|
-
|
|
274
|
-
No `config.sort`; sort in `script.js` before assigning. Never ECharts `dataset.transform sort` on object-arrays (unstable in V5).
|
|
275
|
-
|
|
276
|
-
```js
|
|
277
|
-
vueData.rows = (d.data || []).slice().sort((a, b) => b.doanhThu - a.doanhThu);
|
|
278
|
-
// String sort
|
|
279
|
-
vueData.rows = (d.data || []).slice().sort((a, b) => a.name.localeCompare(b.name));
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
---
|
|
283
|
-
|
|
284
|
-
## module.json pattern
|
|
285
|
-
|
|
286
|
-
```json
|
|
287
|
-
{
|
|
288
|
-
"el": "f-echart",
|
|
289
|
-
"w": "12",
|
|
290
|
-
"attr": {
|
|
291
|
-
":config": "{ title: 'Tiêu đề', height: 350, dataView: true }",
|
|
292
|
-
":option": "vueData.chartOpt"
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
Build `vueData.chartOpt` in `script.js` (fetch callback). Per-type templates + 5 patterns (inline, vueData ref, dynamic height, conditional option, no-dataview): [echart-templates.md](echart-templates.md).
|
|
298
|
-
|
|
299
|
-
---
|
|
300
|
-
|
|
301
|
-
## Common errors
|
|
302
|
-
|
|
303
|
-
| Symptom | Cause | Fix |
|
|
304
|
-
| ------------------------------------------------ | ------------------------------------------------------------------------------ | ------------------------------------------------------------------- |
|
|
305
|
-
| Blank page / no component | `fechart.js` not imported | `fui import list` → add |
|
|
306
|
-
| `echarts is not defined` | `fechart.js` sort < `echarts.min.js` | swap sorts |
|
|
307
|
-
| Empty chart with data | data outside `option` (no `data` prop) | `option.series` |
|
|
308
|
-
| No DataView button | nothing extractable from `option` | add series/axis data |
|
|
309
|
-
| Pale bar fill | lighten (50%) | `itemStyle.color` in `option.series[]` |
|
|
310
|
-
| No update on filter change | `config`/`option` are **deep-watched** ⇒ variable **not declared in `data[]`** | declare in module.json `data[]`; at worst `$refs.myChart.refresh()` |
|
|
311
|
-
| `config.type` ignored | doesn't exist | `option.series[].type` |
|
|
312
|
-
| Custom theme ignored | silently `'fui'` | `config.color` |
|
|
313
|
-
| Stray colors | palette in both `config.color` and `option.color` | one place |
|
|
314
|
-
| Legend on top despite `legendPosition: 'bottom'` | legend in `option`, not `config.legend` | `option.legend.bottom: 0` or `config.legend: true` |
|
|
315
|
-
| Tooltip shows 1 series | no default `trigger` | `tooltip: { trigger: 'axis' }` in `option` |
|
|
316
|
-
| Multi-row legend overlaps | engine reserves **1 row** | `config.legendRows: 2`+ |
|
|
1
|
+
# f-echart — ECharts V5 (Core)
|
|
2
|
+
|
|
3
|
+
> Owns: **`f-echart` core: import, architecture, props, config, theme, DataView, common errors**. Per-chart templates + module.json patterns: [echart-templates.md](echart-templates.md).
|
|
4
|
+
|
|
5
|
+
Use `f-echart` for all charts: bar, line, pie, scatter, gauge, radar, heatmap, sankey, tree, map, graph...
|
|
6
|
+
|
|
7
|
+
> ⚠️ `:option`/`:config` most often break **"`:attr` takes one expression only"**: a `;` (even at the end) or a leading `var`/`return` in JSON ⇒ blank page. Multi-step option building → `script.js` function called via `EXE`. See [advanced-techniques.md](advanced-techniques.md) §1.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Import — required, in order
|
|
12
|
+
|
|
13
|
+
Missing → no component, blank page.
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
1. /include/chart/echarts.min.js ← ECharts core (phải đứng TRƯỚC)
|
|
17
|
+
2. /include/chart/fechart.js ← FUI wrapper (phải đứng SAU)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`fui import list` → `fui import new` → `fui import delete`: [component-quickref.md](component-quickref.md). Sort = load order; `fechart.js` needs global `echarts` → **larger sort** (e.g. 100 / 110).
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Architecture — Direct Mode only
|
|
25
|
+
|
|
26
|
+
Built **entirely on Apache ECharts V5**; `fechart.js` is a thin wrapper (themes, `config` shortcuts, DataView, events). **`option` is plain ECharts option** — use ECharts knowledge; only `config` is FUI-specific. No Auto mode, no `config.type`.
|
|
27
|
+
|
|
28
|
+
- `option` — **primary**, full ECharts option → `echarts.setOption()`.
|
|
29
|
+
- `config` — FUI shortcuts (height, theme, title, toolbox...); defaults, overridden by `option`.
|
|
30
|
+
|
|
31
|
+
**Merge:** `config` base, then lodash `_.merge` with `option`: objects deep-merge, primitives replace, **arrays merge by index, NOT replace** ⚠️. `config` produces only `color` (`config.color`) and `dataZoom` (`config.zoom`); with both `config.color` and `option.color` the shorter can't drop the longer's tail:
|
|
32
|
+
|
|
33
|
+
```js
|
|
34
|
+
config.color = ["#111", "#222", "#333", "#444"];
|
|
35
|
+
option.color = ["#aaa", "#bbb"];
|
|
36
|
+
// → ['#aaa','#bbb','#333','#444'] ← 2 màu cũ còn sót lại
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
⇒ Palette in **one place** (prefer `config.color`). `series`/`xAxis`/`yAxis`/`grid` never come from `config` — declare freely.
|
|
40
|
+
|
|
41
|
+
Rendered with `setOption(opt, true)` (**notMerge**): each render replaces the whole option.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## DEFAULT conventions (apply unprompted, unless user asks otherwise)
|
|
46
|
+
|
|
47
|
+
1. **Show values** — bar: `label: { show: true, position: 'top' }` (horizontal → `'right'`); line: `label: { show: true, position: 'top' }`.
|
|
48
|
+
2. **Grid**: axis charts default `left: 0`, `right: 0`, `top: 50`, `bottom: 50` (top/bottom auto-grow for title/legend). Declare only sides the user wants changed; `containLabel: true` if labels may clip.
|
|
49
|
+
3. **Full numbers** (`1,500,000`), no `1.5M`/`1.2k` unless asked; tooltip default `formatFull` is correct — don't override.
|
|
50
|
+
4. **Pie**: no long messy labelLines; short labels (name + % if needed) or rely on legend + tooltip.
|
|
51
|
+
5. **No hand-built "View data" button** — `config.dataView: true`.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Props
|
|
56
|
+
|
|
57
|
+
Only `config` (Object, `{}`) and `option` (Object, `{}`, primary). **No `data` prop.**
|
|
58
|
+
|
|
59
|
+
## config keys
|
|
60
|
+
|
|
61
|
+
| Key | Default | Notes |
|
|
62
|
+
| -------------------------------------------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
63
|
+
| `height` | `400` | number = px; string e.g. `'50vh'` |
|
|
64
|
+
| `theme` | `'fui'` | **Only** `'fui'`\|`'shine'`\|`'v5'`; others (incl. `echarts.registerTheme` names) or `null` **silently → `'fui'`**. Recolor via `config.color` |
|
|
65
|
+
| `backgroundColor` | `'transparent'` | |
|
|
66
|
+
| `title` / `subtitle` | | → `option.title.text` / `.subtext` |
|
|
67
|
+
| `titleAlign` | `'center'` | `'left'`/`'center'`/`'right'` (old alias `titleLeft`) |
|
|
68
|
+
| `titleSize` | `16` | px |
|
|
69
|
+
| `titleColor` | | empty = theme |
|
|
70
|
+
| `titleWeight` | `'bold'` | `'normal'`/`'bold'`/`500`... |
|
|
71
|
+
| `titleFont` | | font-family, title + subtitle |
|
|
72
|
+
| `titleStyle` | | full ECharts textStyle — **wins over all `title*`** |
|
|
73
|
+
| `subtitleSize` / `subtitleColor` / `subtitleStyle` | `12` | same, subtitle |
|
|
74
|
+
| `color` | | palette (string[]) |
|
|
75
|
+
| `animation` | | `false` = off |
|
|
76
|
+
| `animationDuration` / `animationEasing` | | ms / ECharts easing |
|
|
77
|
+
| `legend` | | `false` hide; `true`/Object show, merged with `{ type:'scroll', left:'center' }`. **No top/bottom** — engine sets it |
|
|
78
|
+
| `legendPosition` | `'bottom'` | `'top'`/`'bottom'`; **only when legend enabled via `config.legend`/`config.legendConfig`** |
|
|
79
|
+
| `legendRows` | `1` | rows to reserve; >1 when items wrap. Any legend declaration |
|
|
80
|
+
| `legendConfig` | | extra legend props (after `legend`) |
|
|
81
|
+
| `tooltip` | | merged into `{ confine: true }`. **No default `trigger`** → `'item'`; multi-series axis charts need `tooltip: { trigger: 'axis' }` in `option` |
|
|
82
|
+
| `tooltipFormat` | `'auto'` | `'number'`/`'percent'`/`'currency'`/`'short'`/`'auto'`; `'auto'` = full with separators; `'short'` = `1.5M`, only if asked |
|
|
83
|
+
| `decimals` | | for `'number'`/`'percent'`/`'currency'` |
|
|
84
|
+
| `symbol` | `'₫'` | for `'currency'` |
|
|
85
|
+
| `tooltipFormatter` | | Function, **wins over `tooltipFormat`** |
|
|
86
|
+
| `toolbox` | `false` | saveAsImage + restore |
|
|
87
|
+
| `toolboxConfig` | | override/extend toolbox |
|
|
88
|
+
| `dataView` | `false` | table button (Vue overlay), data from `option` |
|
|
89
|
+
| `zoom` | | dataZoom inside + slider |
|
|
90
|
+
| `zoomConfig` | | Array, override dataZoom |
|
|
91
|
+
| `refreshInterval` | | ms auto-refresh |
|
|
92
|
+
|
|
93
|
+
**Default tooltip needs no config** (centered category title; dot + left label + right value; full numbers `2,535` not `2.5K`). Use `tooltipFormat`/`tooltipFormatter` only for another format.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Grid padding vs `boundaryGap`
|
|
98
|
+
|
|
99
|
+
- Plot area ↔ canvas edge → `option.grid.top`/`bottom`/`left`/`right`.
|
|
100
|
+
- First/last point/bar ↔ axis ends → `boundaryGap` on that `xAxis`/`yAxis`.
|
|
101
|
+
|
|
102
|
+
No `padding` prop; `boundaryGap` doesn't replace `grid`. Numbers = px, `'10%'` = percent. Cartesian default:
|
|
103
|
+
|
|
104
|
+
```js
|
|
105
|
+
grid: { top: 50, bottom: 50, left: 0, right: 0 }
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`top`/`bottom` are minimums (engine grows them for title/legend). A side declared in `option.grid` wins and is no longer auto-computed. Only horizontal change → declare only those:
|
|
109
|
+
|
|
110
|
+
```js
|
|
111
|
+
grid: { left: 12, right: 24, containLabel: true }
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Exact values for all four → declare all four; check title/legend don't overlap:
|
|
115
|
+
|
|
116
|
+
```js
|
|
117
|
+
grid: { top: 40, bottom: 60, left: 12, right: 24, containLabel: true }
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
`containLabel: true` keeps long/edge axis labels inside. `grid` affects only axis charts; pie, funnel, treemap, sankey, graph ignore it → `series.top`/`bottom`/`left`/`right` (or `center`/`radius`).
|
|
121
|
+
|
|
122
|
+
No `boundaryGap` set by `fechart.js`; ECharts defaults: `category` → `true`; `value`/`time`/`log` → `[0, 0]`. Line touching both category ends:
|
|
123
|
+
|
|
124
|
+
```js
|
|
125
|
+
xAxis: { type: 'category', boundaryGap: false, data: labels }
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Bars usually keep `true`. Horizontal bar → `yAxis.boundaryGap`. Value/time margins:
|
|
129
|
+
|
|
130
|
+
```js
|
|
131
|
+
xAxis: { type: 'time', boundaryGap: ['5%', '5%'] }
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
"Blank space at line ends" → `boundaryGap`; "padding around chart/canvas" → `grid`.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Vertical layout engine
|
|
139
|
+
|
|
140
|
+
ECharts places title/legend/plot absolutely. After merge, `f-echart` stacks top-down and bottom-up: each present block takes **its height + a gap**; remainder = plot.
|
|
141
|
+
|
|
142
|
+
| Constant | px |
|
|
143
|
+
| ---------------------- | ----------------------------------------- |
|
|
144
|
+
| padding to canvas edge | 8 |
|
|
145
|
+
| min gap between blocks | 12 |
|
|
146
|
+
| grid left / right | 0 / 0 (overridable) |
|
|
147
|
+
| grid top / bottom min | 50 / 50 |
|
|
148
|
+
| title height | `round(fontSize × 1.4)` (16px → 22) |
|
|
149
|
+
| subtitle height | `4 + round(fontSize × 1.4)` (12px → 4+17) |
|
|
150
|
+
| legend row | 24 × `legendRows` |
|
|
151
|
+
| x-axis label reserve | +26 to `grid.bottom` |
|
|
152
|
+
| y-axis label reserve | +10 to `grid.top` |
|
|
153
|
+
|
|
154
|
+
Axis charts get the remainder as `grid.top`/`grid.bottom`; **pie / funnel / treemap / sankey / graph** as `series.top`/`series.bottom`, except a series with px `center` (left untouched).
|
|
155
|
+
|
|
156
|
+
Measured (axis chart, default title):
|
|
157
|
+
|
|
158
|
+
| Config | `title.top` | `legend` | `grid.top` | `grid.bottom` |
|
|
159
|
+
| -------------------------------- | ----------- | ----------- | ---------- | ------------- |
|
|
160
|
+
| no title, no legend | — | — | 50 | 50 |
|
|
161
|
+
| title only | 8 | — | 52 | 50 |
|
|
162
|
+
| title + `config.legend: true` | 8 | `bottom: 8` | 52 | 70 |
|
|
163
|
+
| title + `legendPosition:'top'` | 8 | `top: 42` | 88 | 50 |
|
|
164
|
+
| as above + `legendRows: 2` | 8 | `top: 42` | 112 | 50 |
|
|
165
|
+
| title `titleSize: 24` + subtitle | 8 | — | 85 | 50 |
|
|
166
|
+
|
|
167
|
+
**Legend edge, first match wins:**
|
|
168
|
+
|
|
169
|
+
1. `legend.top` set → top; `legend.bottom` set → bottom (value kept).
|
|
170
|
+
2. Enabled via `config.legend`/`config.legendConfig` → `config.legendPosition` (default **`'bottom'`**).
|
|
171
|
+
3. Only in `option.legend` → **top**; ⚠️ `config.legendPosition` ignored — use `option.legend.bottom: 0` or `config.legend: true`.
|
|
172
|
+
|
|
173
|
+
Don't tweak `grid.top`/`grid.bottom`/`legend.top` to dodge a title — space is re-added. **Module-declared values are always kept** (`grid.top: 5` stays; later blocks stack from it) → declare only for a custom layout. Wrapping legend → `legendRows`.
|
|
174
|
+
|
|
175
|
+
Limits: only **px numbers** are computed (`'10%'`/`'middle'` keep just a minimum); array `option.grid` (multi-grid) is ignored — module handles layout.
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## DataView
|
|
180
|
+
|
|
181
|
+
`config.dataView: true` → table icon top-right → overlay table. Data from `option.dataset.source` (object-array or array-of-arrays), else `option.series[].data` + `xAxis.data`/`yAxis.data`; none → button hidden.
|
|
182
|
+
|
|
183
|
+
```json
|
|
184
|
+
{
|
|
185
|
+
"el": "f-echart",
|
|
186
|
+
"w": "12",
|
|
187
|
+
"attr": {
|
|
188
|
+
":config": "{ title: 'Doanh thu', dataView: true, height: 350 }",
|
|
189
|
+
":option": "vueData.chartOpt"
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Custom **Vue overlay**, **not** ECharts toolbox DataView (`config.toolbox: true` = saveAsImage + restore only). Omit without a clear series array (single-value gauge, sankey node/link, hierarchical tree...).
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Series auto-enhancement (theme policy)
|
|
199
|
+
|
|
200
|
+
| Policy | Applies to | Effect |
|
|
201
|
+
| ----------- | -------------------------- | ------------------------------------------------------------------------------------------- |
|
|
202
|
+
| `lighten` | `bar`, `pie`, `funnel` | 50% lighter fill + original-color border; bar per-series, pie/funnel per-item palette color |
|
|
203
|
+
| `separator` | `treemap`, `sunburst` | faint white border between cells/layers |
|
|
204
|
+
| `outline` | `scatter`, `effectScatter` | white outline around points |
|
|
205
|
+
|
|
206
|
+
Disable: bar → `itemStyle.color` in `option.series[]`; pie item → `itemStyle.color` on each `data[]` item; separator/outline → `itemStyle.borderWidth: 0` in `option.series[]`.
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Theme
|
|
211
|
+
|
|
212
|
+
Built in: `'fui'` (56 colors, **default**), `'shine'` (8, classic axis, strong colors), `'v5'` (9, ECharts V5 default).
|
|
213
|
+
|
|
214
|
+
```json
|
|
215
|
+
":config": "{ theme: 'shine' }"
|
|
216
|
+
":config": "{ theme: 'v5' }"
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
⚠️ **Only these three.** Other names (incl. `echarts.registerTheme('myTheme', …)`) and `null` **silently → `'fui'`**, no error. Recolor with `config.color` (also read by auto-enhancement):
|
|
220
|
+
|
|
221
|
+
```json
|
|
222
|
+
":config": "{ color: ['#e63946', '#457b9d', '#2a9d8f'] }"
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## Events & methods
|
|
228
|
+
|
|
229
|
+
Events: `chart-click` (params) · `drilldown` (alias) · `legend-change` (params) · `rendered` (after each `setOption`) · `finished` (animation done).
|
|
230
|
+
|
|
231
|
+
```json
|
|
232
|
+
{ "v-on:drilldown": "CALL(vueData.loadDetail, { row: $event })" }
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
```js
|
|
236
|
+
this.$refs.myChart.getInstance(); // ECharts instance gốc
|
|
237
|
+
this.$refs.myChart.refresh(); // Force re-render (gọi lại setOption)
|
|
238
|
+
this.$refs.myChart.exportImage("chart.png");
|
|
239
|
+
this.$refs.myChart.resizeChart(); // Hiếm khi cần — xem ghi chú dưới
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
`resizeChart()` rarely needed: listens to `window.resize` **and** a `ResizeObserver` on its container (Vuetify grid settle, tab/dialog open, sidebar collapse).
|
|
243
|
+
|
|
244
|
+
```js
|
|
245
|
+
// Native ECharts API
|
|
246
|
+
const chart = vueData.$refs.myChart.getInstance();
|
|
247
|
+
chart.dispatchAction({ type: "highlight", seriesIndex: 0, dataIndex: 2 });
|
|
248
|
+
chart.on("mouseover", (p) => {
|
|
249
|
+
vueData.hoverLabel = p.name;
|
|
250
|
+
});
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## FEchartFormatter (global, script.js) — exactly 6 functions
|
|
256
|
+
|
|
257
|
+
```js
|
|
258
|
+
FEchartFormatter.build("currency", { symbol: "₫", decimals: 0 })(1500000); // → '₫1,500,000'
|
|
259
|
+
FEchartFormatter.formatShort(1500000); // → '1.5M' (co gọn — chỉ dùng khi cần tiết kiệm chỗ, vd axis label)
|
|
260
|
+
FEchartFormatter.formatNumber(1500000); // → '1,500,000' (decimals thứ 2, mặc định 0)
|
|
261
|
+
FEchartFormatter.formatFull(2535.678); // → '2,535.68' (đầy đủ, tối đa 2 số thập phân — mặc định của tooltip)
|
|
262
|
+
// Tạo tooltip formatter từ config.tooltipFormat ('auto'/không khai → formatFull, số đầy đủ)
|
|
263
|
+
FEchartFormatter.buildTooltip({ tooltipFormat: "currency", symbol: "₫" });
|
|
264
|
+
FEchartFormatter.buildDefault(); // formatter tooltip mặc định (formatFull)
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
- **No** `formatPercent`/`formatCurrency` → `build('percent', { decimals })` / `build('currency', { symbol, decimals })`.
|
|
268
|
+
- Bare `FEchartFormatter` only; `window.FEchartFormatter` is `undefined` (script-level `const`).
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## Sorting
|
|
273
|
+
|
|
274
|
+
No `config.sort`; sort in `script.js` before assigning. Never ECharts `dataset.transform sort` on object-arrays (unstable in V5).
|
|
275
|
+
|
|
276
|
+
```js
|
|
277
|
+
vueData.rows = (d.data || []).slice().sort((a, b) => b.doanhThu - a.doanhThu);
|
|
278
|
+
// String sort
|
|
279
|
+
vueData.rows = (d.data || []).slice().sort((a, b) => a.name.localeCompare(b.name));
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## module.json pattern
|
|
285
|
+
|
|
286
|
+
```json
|
|
287
|
+
{
|
|
288
|
+
"el": "f-echart",
|
|
289
|
+
"w": "12",
|
|
290
|
+
"attr": {
|
|
291
|
+
":config": "{ title: 'Tiêu đề', height: 350, dataView: true }",
|
|
292
|
+
":option": "vueData.chartOpt"
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Build `vueData.chartOpt` in `script.js` (fetch callback). Per-type templates + 5 patterns (inline, vueData ref, dynamic height, conditional option, no-dataview): [echart-templates.md](echart-templates.md).
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
## Common errors
|
|
302
|
+
|
|
303
|
+
| Symptom | Cause | Fix |
|
|
304
|
+
| ------------------------------------------------ | ------------------------------------------------------------------------------ | ------------------------------------------------------------------- |
|
|
305
|
+
| Blank page / no component | `fechart.js` not imported | `fui import list` → add |
|
|
306
|
+
| `echarts is not defined` | `fechart.js` sort < `echarts.min.js` | swap sorts |
|
|
307
|
+
| Empty chart with data | data outside `option` (no `data` prop) | `option.series` |
|
|
308
|
+
| No DataView button | nothing extractable from `option` | add series/axis data |
|
|
309
|
+
| Pale bar fill | lighten (50%) | `itemStyle.color` in `option.series[]` |
|
|
310
|
+
| No update on filter change | `config`/`option` are **deep-watched** ⇒ variable **not declared in `data[]`** | declare in module.json `data[]`; at worst `$refs.myChart.refresh()` |
|
|
311
|
+
| `config.type` ignored | doesn't exist | `option.series[].type` |
|
|
312
|
+
| Custom theme ignored | silently `'fui'` | `config.color` |
|
|
313
|
+
| Stray colors | palette in both `config.color` and `option.color` | one place |
|
|
314
|
+
| Legend on top despite `legendPosition: 'bottom'` | legend in `option`, not `config.legend` | `option.legend.bottom: 0` or `config.legend: true` |
|
|
315
|
+
| Tooltip shows 1 series | no default `trigger` | `tooltip: { trigger: 'axis' }` in `option` |
|
|
316
|
+
| Multi-row legend overlaps | engine reserves **1 row** | `config.legendRows: 2`+ |
|