@fui-org/fui-cli 1.2.0 → 1.3.1
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/dist/fui-x7kph78r.js +420 -0
- package/dist/fui.js +1 -429
- package/package.json +3 -3
- package/skills/fui/SKILL.md +3 -3
- package/skills/fui-skill-cli/SKILL.md +139 -0
- package/skills/fui-skill-cli/references/INDEX.md +110 -0
- package/skills/fui-skill-cli/references/advanced-techniques.md +168 -0
- package/skills/fui-skill-cli/references/coding-standards.md +112 -0
- package/skills/fui-skill-cli/references/component-design.md +448 -0
- package/skills/fui-skill-cli/references/component-quickref.md +78 -0
- package/skills/fui-skill-cli/references/component-table.md +248 -0
- package/skills/fui-skill-cli/references/components-dialog.md +191 -0
- package/skills/fui-skill-cli/references/components-display.md +141 -0
- package/skills/fui-skill-cli/references/components-echart.md +316 -0
- package/skills/fui-skill-cli/references/components-input.md +335 -0
- package/skills/fui-skill-cli/references/controls-patterns.md +701 -0
- package/skills/fui-skill-cli/references/controls-styling-vocabulary.md +137 -0
- package/skills/fui-skill-cli/references/db-table-design.md +73 -0
- package/skills/fui-skill-cli/references/db-workflow.md +288 -0
- package/skills/fui-skill-cli/references/default-function.md +425 -0
- package/skills/fui-skill-cli/references/design-modes.md +57 -0
- package/skills/fui-skill-cli/references/echart-templates.md +489 -0
- package/skills/fui-skill-cli/references/fastproject.md +99 -0
- package/skills/fui-skill-cli/references/fsheet.md +203 -0
- package/skills/fui-skill-cli/references/fullstack-workflow.md +313 -0
- package/skills/fui-skill-cli/references/module-data-patterns.md +117 -0
- package/skills/fui-skill-cli/references/module-json-anatomy.md +132 -0
- package/skills/fui-skill-cli/references/module-structure.md +141 -0
- package/skills/fui-skill-cli/references/new-session.md +85 -0
- package/skills/fui-skill-cli/references/pdfmake.md +60 -0
- package/skills/fui-skill-cli/references/permission-system.md +150 -0
- package/skills/fui-skill-cli/references/platform-architecture.md +269 -0
- package/skills/fui-skill-cli/references/project-config.md +303 -0
- package/skills/fui-skill-cli/references/project-provisioning.md +278 -0
- package/skills/fui-skill-cli/references/script-map.md +262 -0
- package/skills/fui-skill-cli/references/sql-clr-functions.md +225 -0
- package/skills/fui-skill-cli/references/system-design.md +89 -0
- package/skills/fui-skill-cli/references/tapi-file-api.md +185 -0
- package/skills/fui-skill-cli/references/tapi-permission-patterns.md +156 -0
- package/skills/fui-skill-cli/references/tapi-reference.md +474 -0
- package/skills/fui-skill-cli/references/tools-registry.md +84 -0
- package/skills/fui-skill-cli/references/ui-crosswindow-patterns.md +321 -0
- package/skills/fui-skill-cli/references/ui-dialog-patterns.md +255 -0
- package/skills/fui-skill-cli/references/ui-layout-patterns.md +176 -0
- package/skills/fui-skill-cli/references/ui-patterns.md +303 -0
- package/skills/fui-skill-cli/references/ui-screenshot-review.md +95 -0
- package/skills/fui-skill-cli/references/ui-table-cell-patterns.md +318 -0
- package/skills/fui-skill-cli/references/ui-templates.md +22 -0
- package/skills/fui-skill-cli/references/verification.md +236 -0
- package/skills/fui-skill-cli/references/watcher-patterns.md +163 -0
- package/skills/fui-skill-cli/references/websocket-realtime.md +271 -0
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
# FUI Vue Component Design Rules
|
|
2
|
+
|
|
3
|
+
> Owns: **`uc-*.vue` rules: module/project scope, no `name:`/`components:{}`, props/emits/slots, no `_injectStyle`, manual `<style scoped>` scoping, verification (`fui component preview`, push project components before module render), `{projectId}/COMPONENTS.md`**. Approved classes/props: [controls-styling-vocabulary.md](controls-styling-vocabulary.md).
|
|
4
|
+
|
|
5
|
+
Mandatory for every `uc-*.vue`, dialog components included.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. Two scopes: module vs project
|
|
10
|
+
|
|
11
|
+
| | Module-scope | Project-scope (global) |
|
|
12
|
+
| -------------- | ---------------------------------------------------- | ----------------------------------------- |
|
|
13
|
+
| **File** | `{projectId}/modules/{moduleId}/components/uc-*.vue` | `{projectId}/components/uc-*.vue` |
|
|
14
|
+
| **Registry** | `modules/{moduleId}/components/_components.json` | `{projectId}/components/_components.json` |
|
|
15
|
+
| **Visible in** | That module only | **All modules in the project** |
|
|
16
|
+
| **Auto-load** | In its module | Automatic — **nothing to declare** |
|
|
17
|
+
| **Use when** | Used by one module | Reused across modules |
|
|
18
|
+
|
|
19
|
+
### Choosing scope
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
Component này sẽ dùng ở nhiều module? → project-scope
|
|
23
|
+
Component chỉ dùng trong một module? → module-scope
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Commands per scope
|
|
27
|
+
|
|
28
|
+
| Operation | Module-scope | Project-scope (global) |
|
|
29
|
+
| -------------------------- | --------------------------------------------------- | -------------------------------------------------- |
|
|
30
|
+
| **Create** (not on server) | `fui component new <name>` — uses session module | `fui component new <name> -p <pid>` — NO module |
|
|
31
|
+
| **Update** (on server) | `fui component update <name>` — uses session module | `fui component update <name> -p <pid>` — NO module |
|
|
32
|
+
| **Delete** | `fui component delete <name>` — uses session module | `fui component delete <name> -p <pid>` — NO module |
|
|
33
|
+
| **Read source** | `fui component get <name> -p <pid> -m <mid>` | `fui component get <name> -p <pid>` — omit module |
|
|
34
|
+
| **Sync list** | `fui component list -p <pid> -m <mid>` | `fui component list -p <pid>` — omit module |
|
|
35
|
+
| **Workspace file** | `{moduleDir}/components/uc-foo.vue` | `{projectDir}/components/uc-foo.vue` |
|
|
36
|
+
|
|
37
|
+
**Single rule:** module given → module-scope · no module, only project → project-scope. Flags: `fui component --help`.
|
|
38
|
+
|
|
39
|
+
**Create ≠ update — no implicit upsert.** Both read the **local** `.vue` (must exist on disk) and derive `ComID` from `_components.json` (never pass it):
|
|
40
|
+
|
|
41
|
+
- `fui component new` — INSERT only. Refreshes `_components.json` **from the server first**, then checks the name; duplicate = **hard error** (INSERT on a stale cache creates **two same-name records**, unfixable from fui). Re-syncs after to get the real `ComID`.
|
|
42
|
+
- `fui component update` — UPDATE only. No `ComID` = **hard error**, never creates: never created (→ `fui component new`) or stale cache (→ `fui component list`, retry).
|
|
43
|
+
|
|
44
|
+
`fui component delete` looks up ComID in local `_components.json`; stale → `fui component list` first.
|
|
45
|
+
|
|
46
|
+
**`fui module publish html` excludes project-scope components** — push them separately (`fui component new`/`update`).
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## 1a. Existing project components — `{projectId}/COMPONENTS.md`
|
|
51
|
+
|
|
52
|
+
Before designing a project-scope component, **check `{projectId}/COMPONENTS.md`** (if present): existing project `uc-*.vue` with purpose and main props. Don't rebuild one that exists.
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
{FUI_MCP_WORKDIR}/{projectId}/COMPONENTS.md ← kiểm tra TRƯỚC khi tạo component project-scope mới
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Accumulation:** after creating/updating a reusable project component, append an entry; no file → create with:
|
|
59
|
+
|
|
60
|
+
```markdown
|
|
61
|
+
# {projectId} — Danh sách component project-scope
|
|
62
|
+
|
|
63
|
+
## uc-ten-component
|
|
64
|
+
|
|
65
|
+
**Mục đích:** mô tả ngắn gọn component dùng để làm gì
|
|
66
|
+
**Props:** `propA` (kiểu, mặc định) · `propB` (kiểu, mặc định)
|
|
67
|
+
**Events:** `@eventName` — khi nào phát
|
|
68
|
+
**Ghi chú:** điều gì cần biết khi dùng (nếu có)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
> Mirrors `{projectId}/DESIGN.md` ([design-modes.md](design-modes.md) §2-3). **Never auto-remove entries** — remove only after `fui component delete` removed it from the server.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 2. Naming and file layout
|
|
76
|
+
|
|
77
|
+
- Prefix `uc-` (User Component; vs `f-*` FUI runtime, `v-*` Vuetify), kebab-case: `uc-trangthai-sukien.vue`, `uc-user-profile.vue`
|
|
78
|
+
- Only in `components/` at module or project root; no subfolders
|
|
79
|
+
- Registry `components/_components.json`: only `comName`, never set `comID`
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## 3. Registration — NO `components: {}`, NO `name:`
|
|
84
|
+
|
|
85
|
+
FUI auto-registers every `uc-*.vue` **globally**. Never declare `components: { ... }` (doesn't work in FUI) or `name:` (FUI derives it from the filename; may conflict):
|
|
86
|
+
|
|
87
|
+
```js
|
|
88
|
+
// ❌ SAI — không cần và có thể gây xung đột
|
|
89
|
+
export default {
|
|
90
|
+
name: 'uc-user-card',
|
|
91
|
+
props: { ... }
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ✅ ĐÚNG — bỏ name, FUI tự resolve từ tên file
|
|
95
|
+
export default {
|
|
96
|
+
props: { ... }
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
```js
|
|
101
|
+
// ❌ SAI — không dùng trong FUI
|
|
102
|
+
export default {
|
|
103
|
+
components: { "uc-user-card": UcUserCard },
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// ✅ ĐÚNG — dùng trực tiếp tag trong template
|
|
107
|
+
// <uc-user-card :data="data" />
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## 4. Props
|
|
113
|
+
|
|
114
|
+
- `script`: camelCase (`userName`, `itemList`); `template`: kebab-case (`:user-name="userName"`)
|
|
115
|
+
- Array/Object defaults use `function()`, not arrow functions:
|
|
116
|
+
|
|
117
|
+
```js
|
|
118
|
+
// ❌ SAI
|
|
119
|
+
props: { items: { default: () => [] } }
|
|
120
|
+
|
|
121
|
+
// ✅ ĐÚNG
|
|
122
|
+
props: { items: { default: function() { return [] } } }
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
- Prefer neutral reusable names (`items`, `value`, `label`, `loading`, `readonly`, `disabled`, `options`, `config`), not screen-specific ones
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## 5. Emits
|
|
130
|
+
|
|
131
|
+
- Emit to the parent; never mutate parent state
|
|
132
|
+
- Standard: `input`, `change`, `select`, `submit`, `remove`, `action`
|
|
133
|
+
- Dialog components always close with `('input', false)` — Vue 2 `v-model` compatible
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## 6. Template syntax constraints
|
|
138
|
+
|
|
139
|
+
### No backticks in `<template>`
|
|
140
|
+
|
|
141
|
+
Breaks in Vue 2 templates; concatenate:
|
|
142
|
+
|
|
143
|
+
```html
|
|
144
|
+
<!-- ❌ SAI -->
|
|
145
|
+
:label="`Total (${items.length})`"
|
|
146
|
+
|
|
147
|
+
<!-- ✅ ĐÚNG -->
|
|
148
|
+
:label="'Total (' + items.length + ')'"
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### No `@` shorthand in JSON
|
|
152
|
+
|
|
153
|
+
`module.json`: always `v-on:click`, never `@click`. `.vue` templates: `@click` is fine.
|
|
154
|
+
|
|
155
|
+
### Vue 2 needs a single root element
|
|
156
|
+
|
|
157
|
+
```html
|
|
158
|
+
<!-- ✅ ĐÚNG -->
|
|
159
|
+
<template>
|
|
160
|
+
<div>
|
|
161
|
+
<!-- nội dung -->
|
|
162
|
+
</div>
|
|
163
|
+
</template>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## 7. Style & class — use existing Vuetify classes, DON'T invent
|
|
169
|
+
|
|
170
|
+
**Use Vuetify utility classes for ALL spacing/flex/text/color/size. New class only when truly special** (effect/layout Vuetify lacks) — invented classes break consistency.
|
|
171
|
+
|
|
172
|
+
### ❌ NEVER inject CSS via JavaScript
|
|
173
|
+
|
|
174
|
+
No `<style>` created in JS and appended to `document.head` (`_injectStyle`) — severe anti-pattern (dodges the no-`<style>` rule, spawns invented classes):
|
|
175
|
+
|
|
176
|
+
```js
|
|
177
|
+
// ❌ SAI NGHIÊM TRỌNG — không bao giờ làm thế này trong .vue
|
|
178
|
+
methods: {
|
|
179
|
+
_injectStyle: function() {
|
|
180
|
+
var s = document.createElement('style')
|
|
181
|
+
s.textContent = ['.smg-root{...}', '.smg-card{...}', '.smg-badge{...}', ...].join('')
|
|
182
|
+
document.head.appendChild(s) // ❌ bơm CSS + hàng loạt class tự chế
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
`<style scoped>` is allowed for custom CSS (rarely needed), but **does not scope**: FUI concatenates every module component's `ComCSS` into ONE stylesheet (`ComponentCSSBuild()`) — no SFC compile, no `data-v-xxxx`. **Scope manually**:
|
|
188
|
+
|
|
189
|
+
1. Template root gets a class = component name (`uc-invoice-list` → `class="uc-invoice-list"`).
|
|
190
|
+
2. EVERY selector starts from that class — **no bare selectors** (`.title`, `.header`...); they leak into every component in the module.
|
|
191
|
+
|
|
192
|
+
```vue
|
|
193
|
+
<template>
|
|
194
|
+
<div class="uc-invoice-list">
|
|
195
|
+
<div class="uc-invoice-list__toolbar">
|
|
196
|
+
<span class="uc-invoice-list__title">{{ title }}</span>
|
|
197
|
+
</div>
|
|
198
|
+
<f-table class="uc-invoice-list__table" :items="rows"></f-table>
|
|
199
|
+
</div>
|
|
200
|
+
</template>
|
|
201
|
+
|
|
202
|
+
<style scoped>
|
|
203
|
+
.uc-invoice-list__toolbar {
|
|
204
|
+
display: flex;
|
|
205
|
+
align-items: center;
|
|
206
|
+
justify-content: space-between;
|
|
207
|
+
}
|
|
208
|
+
.uc-invoice-list__title {
|
|
209
|
+
font-weight: 600;
|
|
210
|
+
font-size: 14px;
|
|
211
|
+
}
|
|
212
|
+
.uc-invoice-list__table {
|
|
213
|
+
margin-top: 8px;
|
|
214
|
+
}
|
|
215
|
+
</style>
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
```vue
|
|
219
|
+
<!-- ❌ SAI — selector trần, đụng với component khác cùng module có class .title/.toolbar -->
|
|
220
|
+
<style scoped>
|
|
221
|
+
.title {
|
|
222
|
+
font-weight: 600;
|
|
223
|
+
}
|
|
224
|
+
.toolbar {
|
|
225
|
+
display: flex;
|
|
226
|
+
}
|
|
227
|
+
</style>
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Module-wide CSS → `style.css` (`fui module publish css`); `header.html` still works but isn't the only option.
|
|
231
|
+
|
|
232
|
+
### Before adding a class → check the approved vocabulary
|
|
233
|
+
|
|
234
|
+
Allowed Vuetify classes/props (spacing, flex, text, color, radius, elevation, breakpoint, size), priority, when a custom class is allowed → [controls-styling-vocabulary.md](controls-styling-vocabulary.md) §3. Applies unchanged in `.vue`, **except** no FUI grid `w`/`col` (`module.json` syntax translated by the runtime) — write the Vuetify grid directly.
|
|
235
|
+
|
|
236
|
+
**Grid syntax DIFFERS by framework** — FUI V2 = Vuetify 1.5, FUI V3 = Vuetify 3 (canonical: [controls-patterns.md](controls-patterns.md) §Vuetify version):
|
|
237
|
+
|
|
238
|
+
| | V2 (Vuetify 1.5) | V3 (Vuetify 3) |
|
|
239
|
+
| ------------------ | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
240
|
+
| Columns | `v-layout` > `v-flex xs8` | `v-row` > `v-col cols="8"` |
|
|
241
|
+
| `v-flex` | ✅ | ❌ **does not exist** — renders as an inert HTML element: no error, no `[Vue warn]`, just **layout gone** |
|
|
242
|
+
| `v-layout` | ✅ grid row | ⚠️ **exists with a different meaning**: app layout wrapper (with `v-main`, as `fastproject-3.0.js` uses), NOT a grid row. Using it instead of `v-row` gives a flex container without grid gutter/breakpoints |
|
|
243
|
+
| bare prop `shrink` | ✅ (`v-flex` prop) | ❌ no such prop → inert DOM attribute. Use `class="shrink"` |
|
|
244
|
+
|
|
245
|
+
§8 is **V3** (`v-row`/`v-col`); V2 projects use `v-layout`/`v-flex`. Silent breakage both ways: `v-flex` in V3 **shows content with wrong layout**, nothing reports it. Fixing an old component → `fui component preview` on **the project's framework** before pushing.
|
|
246
|
+
|
|
247
|
+
Component-specific CSS → its own `<style scoped>`, manually scoped — not `header.html` unless shared by several components/the module.
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## 8. Standard dialog in `uc-*.vue` — STANDARD TEMPLATE
|
|
252
|
+
|
|
253
|
+
EVERY form dialog: `v-dialog` + `v-card` with these **fixed conventions**. Vuetify utility classes only — no invented classes, no `style`.
|
|
254
|
+
|
|
255
|
+
```html
|
|
256
|
+
<v-dialog v-model="editDialog" max-width="600">
|
|
257
|
+
<v-card>
|
|
258
|
+
<!-- Tiêu đề: px-3 pt-2 pb-3, subtitle-1 font-weight-bold, nút X bên phải -->
|
|
259
|
+
<v-card-title class="d-flex align-center px-3 pt-2 pb-3 subtitle-1 font-weight-bold">
|
|
260
|
+
{{ editItem.RoundID ? 'Chỉnh sửa vòng thi' : 'Thêm vòng thi mới' }}
|
|
261
|
+
<v-spacer></v-spacer>
|
|
262
|
+
<v-btn icon @click="editDialog = false"><v-icon>mdi-close</v-icon></v-btn>
|
|
263
|
+
</v-card-title>
|
|
264
|
+
|
|
265
|
+
<!-- Nội dung: px-3 py-1; field dùng outlined dense hide-details, chia cột bằng v-row/v-col -->
|
|
266
|
+
<v-card-text class="px-3 py-1">
|
|
267
|
+
<v-row>
|
|
268
|
+
<v-col cols="8">
|
|
269
|
+
<v-text-field v-model="editItem.RoundName" label="Tên vòng thi" hide-details outlined dense></v-text-field>
|
|
270
|
+
</v-col>
|
|
271
|
+
<v-col cols="4">
|
|
272
|
+
<v-text-field
|
|
273
|
+
v-model.number="editItem.RoundOrder"
|
|
274
|
+
label="Thứ tự"
|
|
275
|
+
type="number"
|
|
276
|
+
hide-details
|
|
277
|
+
outlined
|
|
278
|
+
dense
|
|
279
|
+
></v-text-field>
|
|
280
|
+
</v-col>
|
|
281
|
+
</v-row>
|
|
282
|
+
</v-card-text>
|
|
283
|
+
|
|
284
|
+
<!-- Hàng cuối: action căn phải, button rộng tự nhiên; action phụ text, action chính primary -->
|
|
285
|
+
<v-card-actions class="pa-3">
|
|
286
|
+
<v-spacer></v-spacer>
|
|
287
|
+
<v-btn text @click="resetForm">Đặt lại</v-btn>
|
|
288
|
+
<v-btn color="primary" elevation="0" :loading="saving" @click="saveRound">Lưu</v-btn>
|
|
289
|
+
</v-card-actions>
|
|
290
|
+
</v-card>
|
|
291
|
+
</v-dialog>
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Mandatory conventions (don't change arbitrarily)
|
|
295
|
+
|
|
296
|
+
| Part | Class/prop | Note |
|
|
297
|
+
| ---------------- | ---------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
|
|
298
|
+
| `v-dialog` | `max-width="600"` | 600 normal form; 400 small; 800–900 wide form |
|
|
299
|
+
| `v-card-title` | `d-flex align-center px-3 pt-2 pb-3 subtitle-1 font-weight-bold` | Compact title + X button (`v-btn icon` → `mdi-close`) on the right via `v-spacer` |
|
|
300
|
+
| `v-card-text` | `px-3 py-1` | Consistent content padding |
|
|
301
|
+
| Input fields | `outlined dense hide-details` | Compact, equal height; columns via `v-row` + `v-col cols="N"` |
|
|
302
|
+
| `v-card-actions` | **Last row**; `pa-3` + `v-spacer` before buttons | All actions right-aligned |
|
|
303
|
+
| Button width | No `block`, `width`, grow class or stretched column wrapper | Natural width from label |
|
|
304
|
+
| Primary button | At most one with `color="primary" elevation="0"` + `:loading="saving"` | Filled to show priority; `:loading` during API call |
|
|
305
|
+
| Secondary button | `text`, no background `color` | Minor actions; understated |
|
|
306
|
+
|
|
307
|
+
- Mode-dependent title: `{{ editItem.ID ? 'Chỉnh sửa' : 'Thêm mới' }}`.
|
|
308
|
+
- Numbers: `v-model.number` + `type="number"`.
|
|
309
|
+
- Close: `@click="dialogVar = false"`; also set `false` in the callback after a successful save.
|
|
310
|
+
- Keep default icon size; never add `small`/`:small` to `v-icon` or icon buttons.
|
|
311
|
+
|
|
312
|
+
### Variants
|
|
313
|
+
|
|
314
|
+
- **Toolbar header** (color/icon emphasis): replace `v-card-title` with `v-toolbar dense flat` + `v-toolbar-title`.
|
|
315
|
+
- **Fullscreen** (large/multi-area form): `<v-dialog v-model="..." fullscreen>` + `v-toolbar` header, content in `v-container class="grid-list-md"`.
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
## 9. Design for reuse
|
|
320
|
+
|
|
321
|
+
- Shape as list, card, dialog body, filter panel, summary block or form section where possible
|
|
322
|
+
- Expose loading/empty/disabled/readonly via props/slots, don't hardcode a workflow
|
|
323
|
+
- API calls, routing, permissions stay in `module.json` or parent — component = presentation
|
|
324
|
+
- module.json `controls` > ~200 lines → extract UI to a Vue component; module.json keeps data/API
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
## 10. No `f-dialog` inside a component template
|
|
329
|
+
|
|
330
|
+
`f-dialog` works only directly in the module's `controls` JSON. In `uc-*.vue` use the §8 v-dialog.
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## 11. `<script>` syntax limits
|
|
335
|
+
|
|
336
|
+
### No `async / await / finally`
|
|
337
|
+
|
|
338
|
+
Unsupported by the FUI `.vue` parser; use callbacks:
|
|
339
|
+
|
|
340
|
+
```js
|
|
341
|
+
// ❌ SAI
|
|
342
|
+
async mounted() {
|
|
343
|
+
const res = await fetch(url)
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// ✅ ĐÚNG
|
|
347
|
+
mounted() {
|
|
348
|
+
ajaxCALL(url, {}, function(res) { ... })
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### No template literals (backticks) in `<script>`
|
|
353
|
+
|
|
354
|
+
Only work in standalone `script.js`:
|
|
355
|
+
|
|
356
|
+
```js
|
|
357
|
+
// ❌ SAI — trong .vue <script>
|
|
358
|
+
var url = `/api/Student/${this.studentID}`;
|
|
359
|
+
|
|
360
|
+
// ✅ ĐÚNG
|
|
361
|
+
var url = "/api/Student/" + this.studentID;
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
366
|
+
## 12. Reuse `defaultfunction.js` — DON'T rewrite
|
|
367
|
+
|
|
368
|
+
**Before writing logic (API call, confirm, notify, format, group, copy...) → check [default-function.md](default-function.md).** Write your own only if nothing fits.
|
|
369
|
+
|
|
370
|
+
### API calls — `ajaxCALL` (NOT hand-rolled `fetch`/`axios`/`$.ajax`)
|
|
371
|
+
|
|
372
|
+
`fetch` fails cross-domain (CORS/credentials). `ajaxCALL` handles credentials, Bearer token and base domain (`v_Set.apiDomain`) — don't prepend it.
|
|
373
|
+
|
|
374
|
+
```js
|
|
375
|
+
// ❌ SAI
|
|
376
|
+
fetch(url, { method: 'POST', body: JSON.stringify(data) })
|
|
377
|
+
.then(r => r.json()).then(res => { ... })
|
|
378
|
+
|
|
379
|
+
// ✅ ĐÚNG — ajaxCALL(URL, DATA, successCb, errorCb, header)
|
|
380
|
+
ajaxCALL('spAPI_RoundList', { ContestID: this.contestID }, function(res) {
|
|
381
|
+
// res đã parse JSON (json_data tự parse)
|
|
382
|
+
}, function(err) {
|
|
383
|
+
// tuỳ chọn — mặc định ajaxCALL đã showMessage lỗi
|
|
384
|
+
})
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### Confirm — `confirm` (NOT `window.confirm`)
|
|
388
|
+
|
|
389
|
+
```js
|
|
390
|
+
confirm({
|
|
391
|
+
title: 'Xác nhận xoá',
|
|
392
|
+
message: 'Bạn có chắc muốn xoá vòng thi này?',
|
|
393
|
+
action: function() { ajaxCALL('spAPI_RoundDelete', { RoundID: id }, function(){ ... }); }
|
|
394
|
+
});
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### Notify — `showMessage` or `Vue.$toast`
|
|
398
|
+
|
|
399
|
+
```js
|
|
400
|
+
showMessage({ title: "Đã lưu thành công" }); // hộp thoại
|
|
401
|
+
showMessage({ title: "Lỗi", message: "...", type: "red" });
|
|
402
|
+
Vue.$toast.success("Đã lưu", { position: "top" }); // toast góc màn hình
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### Other common helpers (don't rewrite)
|
|
406
|
+
|
|
407
|
+
`groupBy` · `findInArray` · `jsonToExcel` (Excel export) · `printPDF` · `copyToClipboard` · `openWindow` · `pushRouter` · `rightTest` (permission check) · `generateID` · `capacityText` · `buildHeader` (auto f-table headers). Details: [default-function.md](default-function.md).
|
|
408
|
+
|
|
409
|
+
> Globals — never add a CDN for them: `_` (Lodash: `_.groupBy`, `_.sumBy`, `_.cloneDeep`...), `moment` (dates), `numeral` (numbers).
|
|
410
|
+
|
|
411
|
+
---
|
|
412
|
+
|
|
413
|
+
## 13. Verify with `fui component preview` BEFORE assembling into the module
|
|
414
|
+
|
|
415
|
+
Seen only after assembly, component bugs and assembly bugs mix. `fui component preview` renders **one component** on its own page from the **local** `.vue` (no push needed) with sample data passed directly.
|
|
416
|
+
|
|
417
|
+
```
|
|
418
|
+
fui component preview uc-invoice-list -p <pid> --cases '[
|
|
419
|
+
{ "name": "danh sách rỗng", "props": { "items": [] } },
|
|
420
|
+
{ "name": "có dữ liệu", "props": { "items": [ {...}, {...} ], "title": "Hoá đơn" } },
|
|
421
|
+
{ "name": "click chọn dòng", "props": { "items": [...] },
|
|
422
|
+
"scenario": [ { "click": "Xem" }, { "assert": "vueData.selected != null" } ] }
|
|
423
|
+
]'
|
|
424
|
+
# -p only → project-scope; add -m <mid> → module-scope (same inference as fui component new/update)
|
|
425
|
+
# devices default: desktop + mobile
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
- **Props**: arrays/objects as **real values** (pushed into `data[]`, bound `:prop`). Strings → static attrs; keys starting `:`/`v-` kept verbatim (use `v-on:`/`v-slot:`, **not** `@`/`#`).
|
|
429
|
+
- **`cases × devices` ≤ 8** (each render = real browser session).
|
|
430
|
+
- Also accepted: `slot` (`innerHTML` content), `vueData`, mocks, `user` (test permission gates), `viewport` (force low height to expose self-measuring components). Flags: `fui component preview --help`.
|
|
431
|
+
- `scenario` uses **the exact `--render` grammar on the real DOM**: `set`/`click`/`call`/`exe`/`assert`; pick rows with `nth` (no `item`/`index`).
|
|
432
|
+
- **Module `style.css` NOT loaded** — component must stand on its own `<style>` + project theme.
|
|
433
|
+
- Returns: screenshots (max 4 inline, rest as paths) + JS errors + `[Vue warn]` + DOM audit. **Read the images and fix** before pushing.
|
|
434
|
+
|
|
435
|
+
### Project components: MUST push before rendering a module that uses them
|
|
436
|
+
|
|
437
|
+
`fui module simulate --render` builds the project layer from the **server** bundle; unpushed local edits **don't appear** (screenshot lies). So `--render` **hard-blocks** (no override) when a local project component differs from the server bundle.
|
|
438
|
+
|
|
439
|
+
```
|
|
440
|
+
sửa uc-*.vue cấp project
|
|
441
|
+
→ fui component preview (kiểm chứng bản local, chưa push)
|
|
442
|
+
→ fui component new (lần đầu) hoặc fui component update (các lần sau) — push lên server
|
|
443
|
+
→ fui module simulate --render ← chỉ chạy được sau khi đã push
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
Blocked and **server is newer** (edited in web IDE) → `fui project sync`; **don't push over it** (erases their work).
|
|
447
|
+
|
|
448
|
+
**Module-level** components are exempt: the module layer renders from local files.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# FUI Component Quick Reference
|
|
2
|
+
|
|
3
|
+
> Owns: **FUI component catalog, components needing JS/CSS imports, add/remove import workflow**. Per-component details: file in the right column.
|
|
4
|
+
|
|
5
|
+
Prefer `f-*`; use `v-*` only when no FUI equivalent exists.
|
|
6
|
+
|
|
7
|
+
| Component | Use for | Reference |
|
|
8
|
+
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
|
|
9
|
+
| `f-table` | **Any datatable** — CRUD via dialog form or view-only (readonly = omit `:update-api`); auto-builds headers when `:headers` omitted; totals row via `sum-format` | [component-table.md](component-table.md) |
|
|
10
|
+
| `f-dialog` | Modal with form, watch, buttons | [components-dialog.md](components-dialog.md) |
|
|
11
|
+
| `f-window` | Dialog opening a URL in an iframe | [components-dialog.md](components-dialog.md) |
|
|
12
|
+
| `f-button` | Button with action, hotkey, form validation | [components-input.md](components-input.md) |
|
|
13
|
+
| `f-date` | Date input with picker + typed mask | [components-input.md](components-input.md) |
|
|
14
|
+
| `f-time` | Time input with picker + typed mask | [components-input.md](components-input.md) |
|
|
15
|
+
| `f-time-counter` | Countdown / clock | [components-input.md](components-input.md) |
|
|
16
|
+
| `f-search` | API-backed autocomplete | [components-input.md](components-input.md) |
|
|
17
|
+
| `f-menu` | Dropdown button menu with icon/action/link | [components-input.md](components-input.md) |
|
|
18
|
+
| `f-radiobox` | Radio group from items array | [components-input.md](components-input.md) |
|
|
19
|
+
| `f-file-upload` | File upload (Plupload, progress dialog) | [components-input.md](components-input.md) |
|
|
20
|
+
| `f-image-update` | Image upload + crop | [components-input.md](components-input.md) |
|
|
21
|
+
| `f-excel-reader` | Excel/CSV → JSON (pick sheet, skip first N rows, name columns) | [components-input.md](components-input.md) |
|
|
22
|
+
| `f-qrcode` | Render QR code | [components-input.md](components-input.md) |
|
|
23
|
+
| `f-qrcode-reader` | Read QR via camera | [components-input.md](components-input.md) |
|
|
24
|
+
| `f-editor` | Rich text editor (CKEditor 5) | [components-dialog.md](components-dialog.md) |
|
|
25
|
+
| `f-editor-dialog` | Rich text editor in a modal | [components-dialog.md](components-dialog.md) |
|
|
26
|
+
| `f-echart` | **Charts** — ECharts V5: bar, line, pie, scatter, gauge, radar, heatmap, sankey, tree, map, graph... | [components-echart.md](components-echart.md) · [echart-templates.md](echart-templates.md) |
|
|
27
|
+
| `f-sheet` | **Excel-like in-grid editing** — edit cells in place, paste from Excel, no dialog (AG Grid) — **needs import** | [fsheet.md](fsheet.md) |
|
|
28
|
+
| `f-pdfmake` | Render PDF in iframe | [components-display.md](components-display.md) · [pdfmake.md](pdfmake.md) |
|
|
29
|
+
| `f-label` | Chip/badge for tags, status | [components-display.md](components-display.md) |
|
|
30
|
+
| `f-box` | Display/edit key-value rows | [components-display.md](components-display.md) |
|
|
31
|
+
| `f-header` | Large heading (display-1, primary color) | [components-display.md](components-display.md) |
|
|
32
|
+
| `f-title` | Small heading (title, primary color) | [components-display.md](components-display.md) |
|
|
33
|
+
| `f-slider` | Image carousel | [components-display.md](components-display.md) |
|
|
34
|
+
| `fp-profile` | User info/profile block | [components-display.md](components-display.md) |
|
|
35
|
+
|
|
36
|
+
### Table-cell components (`t-*`) — used in f-table `headers`
|
|
37
|
+
|
|
38
|
+
`t-html` · `t-label` · `t-num` · `t-time` · `t-boolean` · `t-check` (toggle) · `t-text` · `t-select` · `t-combobox` · `t-menu` (context menu) · `t-link` · `t-button` — details: [ui-table-cell-patterns.md](ui-table-cell-patterns.md).
|
|
39
|
+
|
|
40
|
+
> `header-bar` and `f-window` are internal, runtime-built (header menu, `openWindow` window) — never place them directly in `controls`.
|
|
41
|
+
|
|
42
|
+
## Components needing JS/CSS imports
|
|
43
|
+
|
|
44
|
+
**When adding or removing one of these, run `fui import list` and add/remove the matching imports.**
|
|
45
|
+
|
|
46
|
+
| Component | Required imports |
|
|
47
|
+
| ----------------- | -------------------------------------------------------------------------------------------------------- |
|
|
48
|
+
| `f-pdfmake` | `/include/pdfmake/pdfmake.min.js` · `/include/pdfmake/print.min.js` · `/include/pdfmake/vfs_fonts.js` |
|
|
49
|
+
| `f-file-upload` | `/include/plupload-2.3.7/plupload.full.min.js` |
|
|
50
|
+
| `f-image-update` | `/include/vue-advanced-cropper/index.umd.js` · `/include/vue-advanced-cropper/style.css` |
|
|
51
|
+
| `f-qrcode` | `/include/qrcode/qrcode.min.js` |
|
|
52
|
+
| `f-qrcode-reader` | `/include/qrcode/qrcode-reader.min.js` |
|
|
53
|
+
| `f-echart` | `/include/chart/echarts.min.js` · `/include/chart/fechart.js` (**in this order**) — Apache ECharts |
|
|
54
|
+
| `f-sheet` | `/include/ag-grid/ag-grid-community.min.js` · `/include/ag-grid/fsheet.js` (**in this order**) — AG Grid |
|
|
55
|
+
| `f-editor` | `/include/ckeditor5/build/ckeditor.js` |
|
|
56
|
+
| `f-editor-dialog` | `/include/ckeditor5/build/ckeditor.js` |
|
|
57
|
+
|
|
58
|
+
> **Bundled, NO import needed:** `v-*` (Vuetify) · `f-table` · `f-dialog` · `f-button` ·
|
|
59
|
+
> `f-date` · `f-time` · `f-search` · `f-menu` · `f-radiobox` · `f-box` · `f-label` · `f-header` ·
|
|
60
|
+
> `f-title` · `f-slider` · `f-excel-reader` — and any component not in the table above.
|
|
61
|
+
|
|
62
|
+
**Workflow when adding a component with imports** (see `fui import --help`):
|
|
63
|
+
|
|
64
|
+
1. `fui import list` (project + module) — check existing imports
|
|
65
|
+
2. Missing → `fui import new` for each file
|
|
66
|
+
3. Removing a component → check whether the import is still used elsewhere; if not → `fui import delete`
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Quick selection rules
|
|
71
|
+
|
|
72
|
+
- `f-button` over `v-btn` when you need FUI actions (CALL, hotkey, form validation)
|
|
73
|
+
- `f-date` / `f-time` over `v-text-field` + manual date parsing
|
|
74
|
+
- `f-search` over `v-autocomplete` for API search
|
|
75
|
+
- `f-menu` over `v-menu + v-btn` for dropdown actions
|
|
76
|
+
- `f-table` over `v-data-table` for EVERY table — CRUD and view-only (readonly = omit `:update-api`)
|
|
77
|
+
- `f-echart` for every chart type (bar, line, pie, gauge, scatter, radar, sankey, tree, map...)
|
|
78
|
+
- Dialogs: `f-table` `ctrl-update` for inline CRUD; standalone dialog → hand-build `v-dialog` per the standard anatomy (`fui design read fui` §4 + `ui-dialog-patterns.md`); `f-dialog` only for very small forms
|