forked from mengyxu/noob-components
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
687 lines
18 KiB
687 lines
18 KiB
|
3 months ago
|
# Base Components
|
||
|
|
|
||
|
|
> Base Vue 3 components built on Element Plus.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Overview
|
||
|
|
|
||
|
|
The `packages/base/` directory provides foundational Vue 3 components that wrap Element Plus components with theme integration, i18n support, and consistent prop patterns.
|
||
|
|
|
||
|
|
### Architecture
|
||
|
|
|
||
|
|
```
|
||
|
|
packages/base/
|
||
|
|
├── index.ts # Exports all base components
|
||
|
|
├── item/ # Basic UI elements
|
||
|
|
│ ├── tag.vue
|
||
|
|
│ ├── button.vue
|
||
|
|
│ ├── select.vue
|
||
|
|
│ ├── input.vue
|
||
|
|
│ ├── datetime.vue
|
||
|
|
│ ├── light-box.vue
|
||
|
|
│ ├── buttonWithTooltip.vue
|
||
|
|
│ ├── confirmCancel.vue
|
||
|
|
│ ├── tzDatePicker.vue
|
||
|
|
│ ├── tzDateTime.vue
|
||
|
|
│ └── ws-monitor-toggle.vue
|
||
|
|
└── data/ # Data-driven components
|
||
|
|
├── search-row.vue
|
||
|
|
├── list-table.vue
|
||
|
|
├── listTableDialog.vue
|
||
|
|
├── infomation.vue
|
||
|
|
├── modify-form.vue
|
||
|
|
├── descriptions.vue
|
||
|
|
└── table-action.vue
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Item Components
|
||
|
|
|
||
|
|
### NoobTag
|
||
|
|
|
||
|
|
Wraps `el-tag` with theme integration.
|
||
|
|
|
||
|
|
**File**: `packages/base/item/tag.vue`
|
||
|
|
|
||
|
|
**Props**:
|
||
|
|
| Prop | Type | Default | Description |
|
||
|
|
|------|------|---------|-------------|
|
||
|
|
| `type` | `string` | `null` | Element Plus tag type |
|
||
|
|
| `color` | `string` | `null` | Custom color |
|
||
|
|
| `closable` | `boolean` | `false` | Show close button |
|
||
|
|
| `round` | `boolean` | `false` | Round style |
|
||
|
|
| `effect` | `string` | `'light'` | Tag effect (light/dark/plain) |
|
||
|
|
|
||
|
|
**Usage**:
|
||
|
|
```vue
|
||
|
|
<NoobTag type="success">Active</NoobTag>
|
||
|
|
<NoobTag color="#387dff">Custom</NoobTag>
|
||
|
|
<NoobTag closable round>Removable</NoobTag>
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### NoobButton
|
||
|
|
|
||
|
|
Wraps `el-button` with theme integration and slot content detection.
|
||
|
|
|
||
|
|
**File**: `packages/base/item/button.vue`
|
||
|
|
|
||
|
|
**Props**:
|
||
|
|
| Prop | Type | Default | Description |
|
||
|
|
|------|------|---------|-------------|
|
||
|
|
| `type` | `string` | `null` | Button type (primary/success/warning/danger/info) |
|
||
|
|
| `icon` | `string` | `null` | Icon name |
|
||
|
|
| `disabled` | `boolean` | `false` | Disabled state |
|
||
|
|
| `plain` | `boolean` | `false` | Plain style |
|
||
|
|
| `round` | `boolean` | `false` | Round style |
|
||
|
|
| `circle` | `boolean` | `false` | Circle style (icon-only) |
|
||
|
|
| `loading` | `boolean` | `false` | Loading state |
|
||
|
|
| `text` | `boolean` | `false` | Text button |
|
||
|
|
| `link` | `boolean` | `false` | Link button |
|
||
|
|
|
||
|
|
**Usage**:
|
||
|
|
```vue
|
||
|
|
<NoobButton type="primary" icon="Plus">Add</NoobButton>
|
||
|
|
<NoobButton type="danger" icon="Delete" circle />
|
||
|
|
<NoobButton loading @click="handleSave">Saving...</NoobButton>
|
||
|
|
```
|
||
|
|
|
||
|
|
**Note**: Automatically detects if slot content exists before rendering, handling empty slots in circle buttons.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### NoobSelect
|
||
|
|
|
||
|
|
Wraps `el-select` with Vuex dict integration and width management.
|
||
|
|
|
||
|
|
**File**: `packages/base/item/select.vue`
|
||
|
|
|
||
|
|
**Props**:
|
||
|
|
| Prop | Type | Default | Description |
|
||
|
|
|------|------|---------|-------------|
|
||
|
|
| `modelValue` | `any` | `-` | v-model value |
|
||
|
|
| `placeholder` | `string` | `i18n: rule.pleaseSelect` | Placeholder text |
|
||
|
|
| `disabled` | `boolean` | `false` | Disabled state |
|
||
|
|
| `clearable` | `boolean` | `true` | Show clear button |
|
||
|
|
| `filterable` | `boolean` | `true` | Enable search |
|
||
|
|
| `full` | `boolean` | `false` | 100% width |
|
||
|
|
| `width` | `number` | `-` | Fixed width in px |
|
||
|
|
| `dict` | `string` | `-` | Vuex dict key for options |
|
||
|
|
| `stateProp` | `string` | `-` | Vuex state property for options |
|
||
|
|
| `maxValue` | `number` | `-` | Generate 1-N options |
|
||
|
|
| `valueKey` | `string` | `'key'` | Option value key |
|
||
|
|
| `labelKey` | `string` | `'value'` | Option label key |
|
||
|
|
| `remote` | `boolean` | `false` | Enable remote search |
|
||
|
|
| `remoteMethod` | `function` | `null` | Remote search function |
|
||
|
|
|
||
|
|
**Usage**:
|
||
|
|
```vue
|
||
|
|
<!-- Dict-based options -->
|
||
|
|
<NoobSelect v-model="form.status" dict="userStatus" />
|
||
|
|
|
||
|
|
<!-- Array options from Vuex state -->
|
||
|
|
<NoobSelect v-model="form.type" stateProp="userTypes" valueKey="id" labelKey="name" />
|
||
|
|
|
||
|
|
<!-- Numeric range options -->
|
||
|
|
<NoobSelect v-model="form.count" :maxValue="10" />
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### NoobInput
|
||
|
|
|
||
|
|
Wraps `el-input` with width management and label prepend support.
|
||
|
|
|
||
|
|
**File**: `packages/base/item/input.vue`
|
||
|
|
|
||
|
|
**Props**:
|
||
|
|
| Prop | Type | Default | Description |
|
||
|
|
|------|------|---------|-------------|
|
||
|
|
| `modelValue` | `any` | `-` | v-model value |
|
||
|
|
| `label` | `string` | `null` | Prepend label |
|
||
|
|
| `placeholder` | `string` | `i18n: rule.pleaseEnter` | Placeholder text |
|
||
|
|
| `type` | `string` | `null` | Input type |
|
||
|
|
| `disabled` | `boolean` | `false` | Disabled state |
|
||
|
|
| `clearable` | `boolean` | `true` | Show clear button |
|
||
|
|
| `full` | `boolean` | `false` | 100% width |
|
||
|
|
| `width` | `number` | `-` | Fixed width in px |
|
||
|
|
|
||
|
|
**Usage**:
|
||
|
|
```vue
|
||
|
|
<NoobInput v-model="form.name" label="Name" />
|
||
|
|
<NoobInput v-model="form.note" type="textarea" :rows="3" />
|
||
|
|
<NoobInput v-model="form.search" full placeholder="Search..." />
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### ConfirmCancel
|
||
|
|
|
||
|
|
Confirmation button pair with i18n text.
|
||
|
|
|
||
|
|
**File**: `packages/base/item/confirmCancel.vue`
|
||
|
|
|
||
|
|
**Events**:
|
||
|
|
| Event | Payload | Description |
|
||
|
|
|-------|---------|-------------|
|
||
|
|
| `confirm` | `void` | Confirm button clicked |
|
||
|
|
| `cancel` | `void` | Cancel button clicked |
|
||
|
|
|
||
|
|
**Usage**:
|
||
|
|
```vue
|
||
|
|
<ConfirmCancel @confirm="handleConfirm" @cancel="handleCancel" />
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### ButtonWithTooltip
|
||
|
|
|
||
|
|
Wraps `NoobButton` with tooltip disabled state handling.
|
||
|
|
|
||
|
|
**File**: `packages/base/item/buttonWithTooltip.vue`
|
||
|
|
|
||
|
|
**Props**:
|
||
|
|
| Prop | Type | Default | Description |
|
||
|
|
|------|------|---------|-------------|
|
||
|
|
| `tooltip` | `string` | `-` | Tooltip content |
|
||
|
|
| `disabled` | `boolean` | `-` | Button disabled state |
|
||
|
|
|
||
|
|
**Usage**:
|
||
|
|
```vue
|
||
|
|
<ButtonWithTooltip tooltip="Requires admin access" :disabled="!isAdmin">
|
||
|
|
Delete
|
||
|
|
</ButtonWithTooltip>
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### WsMonitorToggle
|
||
|
|
|
||
|
|
WebSocket subscription monitor with automatic retry logic.
|
||
|
|
|
||
|
|
**File**: `packages/base/item/ws-monitor-toggle.vue`
|
||
|
|
|
||
|
|
**Props**:
|
||
|
|
| Prop | Type | Default | Description |
|
||
|
|
|------|------|---------|-------------|
|
||
|
|
| `topics` | `string[]` | **required** | Topics to monitor |
|
||
|
|
| `statusEndpoint` | `string` | `'/public/ws/topics'` | Status check endpoint |
|
||
|
|
| `interval` | `number` | `3000` | Retry interval (ms) |
|
||
|
|
| `maxRetries` | `number` | `10` | Max connection attempts |
|
||
|
|
| `connect` | `object/function` | `-` | Subscribe command or function |
|
||
|
|
| `disconnect` | `object/function` | `-` | Unsubscribe command or function |
|
||
|
|
| `disabled` | `boolean` | `false` | Disable toggle |
|
||
|
|
| `labelConnected` | `string` | `'ws.autorefresh.enabled'` | Connected label i18n key |
|
||
|
|
| `labelConnecting` | `string` | `'ws.autorefresh.enabling'` | Connecting label i18n key |
|
||
|
|
| `labelDisconnected` | `string` | `'ws.autorefresh.disabled'` | Disconnected label i18n key |
|
||
|
|
| `labelError` | `string` | `'ws.autorefresh.error'` | Error label i18n key |
|
||
|
|
|
||
|
|
**Events**:
|
||
|
|
| Event | Payload | Description |
|
||
|
|
|-------|---------|-------------|
|
||
|
|
| `status-change` | `boolean` | Connection status changed |
|
||
|
|
| `error` | `Error` | Error occurred |
|
||
|
|
|
||
|
|
**States**: `disconnected` | `connecting` | `connected` | `error`
|
||
|
|
|
||
|
|
**Usage**:
|
||
|
|
```vue
|
||
|
|
<WsMonitorToggle
|
||
|
|
:topics="['user:notifications', 'system:alerts']"
|
||
|
|
:connect="{ action: 'subscribe', topics: topics }"
|
||
|
|
:disconnect="{ action: 'unsubscribe', topics: topics }"
|
||
|
|
@status-change="onStatusChange"
|
||
|
|
/>
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Data Components
|
||
|
|
|
||
|
|
### SearchRow
|
||
|
|
|
||
|
|
Search form row with standard action buttons.
|
||
|
|
|
||
|
|
**File**: `packages/base/data/search-row.vue`
|
||
|
|
|
||
|
|
**Props**:
|
||
|
|
| Prop | Type | Default | Description |
|
||
|
|
|------|------|---------|-------------|
|
||
|
|
| `title` | `string` | `null` | Row title |
|
||
|
|
| `fresh` | `boolean` | `true` | Show refresh button |
|
||
|
|
| `add` | `boolean` | `true` | Show add button |
|
||
|
|
| `del` | `boolean` | `true` | Show delete button |
|
||
|
|
| `query` | `boolean` | `true` | Show query button |
|
||
|
|
|
||
|
|
**Slots**:
|
||
|
|
| Slot | Description |
|
||
|
|
|------|-------------|
|
||
|
|
| `left` | Extra buttons on left side |
|
||
|
|
| `default` | Search form fields |
|
||
|
|
|
||
|
|
**Events**:
|
||
|
|
| Event | Description |
|
||
|
|
|-------|-------------|
|
||
|
|
| `query` | Query button clicked |
|
||
|
|
| `add` | Add button clicked |
|
||
|
|
| `delete` | Delete button clicked |
|
||
|
|
|
||
|
|
**Usage**:
|
||
|
|
```vue
|
||
|
|
<SearchRow title="User Management" @query="fetchData" @add="openAddDialog">
|
||
|
|
<template #left>
|
||
|
|
<NoobButton @click="exportData">Export</NoobButton>
|
||
|
|
</template>
|
||
|
|
<NoobInput v-model="searchForm.name" label="Name" />
|
||
|
|
<NoobSelect v-model="searchForm.status" dict="userStatus" />
|
||
|
|
</SearchRow>
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### ListTable
|
||
|
|
|
||
|
|
Data table with pagination and formatting support.
|
||
|
|
|
||
|
|
**File**: `packages/base/data/list-table.vue`
|
||
|
|
|
||
|
|
**Props**:
|
||
|
|
| Prop | Type | Default | Description |
|
||
|
|
|------|------|---------|-------------|
|
||
|
|
| `data` | `any[]/PageResponse` | `[]` | Table data or paginated response |
|
||
|
|
| `props` | `TableColumn[]` | `[]` | Column definitions |
|
||
|
|
| `page` | `boolean` | `false` | Enable pagination |
|
||
|
|
| `height` | `number/string` | `-` | Table height |
|
||
|
|
| `maxHeight` | `number/string` | `-` | Table max height |
|
||
|
|
| `example` | `object` | `{}` | Pagination state ({ page, size }) |
|
||
|
|
| `rowKey` | `string` | `-` | Row key for tree/lazy |
|
||
|
|
| `selectKey` | `string` | `-` | Key for auto-selection |
|
||
|
|
| `treeProps` | `object` | `-` | Tree table configuration |
|
||
|
|
| `lazy` | `boolean` | `false` | Lazy loading |
|
||
|
|
| `border` | `boolean` | `false` | Show border |
|
||
|
|
|
||
|
|
**TableColumn Interface**:
|
||
|
|
```typescript
|
||
|
|
interface TableColumn {
|
||
|
|
code: string; // Property name
|
||
|
|
name?: string; // Column label (used if i18n not set)
|
||
|
|
i18n?: string; // i18n key for label
|
||
|
|
type?: string; // 'selection' | 'index' | 'expand'
|
||
|
|
width?: number; // Column width
|
||
|
|
fixed?: string; // 'left' | 'right'
|
||
|
|
align?: string; // 'left' | 'center' | 'right'
|
||
|
|
slot?: boolean; // Enable slot rendering
|
||
|
|
dict?: string; // Dict key for value mapping
|
||
|
|
timestamp?: boolean; // Format as timestamp
|
||
|
|
filesize?: boolean; // Format as file size
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Events**:
|
||
|
|
| Event | Payload | Description |
|
||
|
|
|-------|---------|-------------|
|
||
|
|
| `query` | `void` | Refresh data |
|
||
|
|
| `selection-change` | `selection[]` | Selection changed |
|
||
|
|
| `row-click` | `row` | Row clicked |
|
||
|
|
| `cell-click` | `row, column, cell, event` | Cell clicked |
|
||
|
|
|
||
|
|
**Usage**:
|
||
|
|
```vue
|
||
|
|
<ListTable
|
||
|
|
:data="tableData"
|
||
|
|
:props="columns"
|
||
|
|
page
|
||
|
|
:example="pagination"
|
||
|
|
row-key="id"
|
||
|
|
@query="fetchData"
|
||
|
|
@selection-change="onSelect"
|
||
|
|
>
|
||
|
|
<template #status="{ row }">
|
||
|
|
<NoobTag :type="row.active ? 'success' : 'info'">
|
||
|
|
{{ row.active ? 'Active' : 'Inactive' }}
|
||
|
|
</NoobTag>
|
||
|
|
</template>
|
||
|
|
</ListTable>
|
||
|
|
|
||
|
|
<script setup>
|
||
|
|
const columns = [
|
||
|
|
{ code: 'id', name: 'ID', width: 80 },
|
||
|
|
{ code: 'name', name: 'Name' },
|
||
|
|
{ code: 'status', slot: true },
|
||
|
|
{ code: 'createdAt', timestamp: true },
|
||
|
|
{ code: 'size', filesize: true }
|
||
|
|
];
|
||
|
|
</script>
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### ListTableDialog
|
||
|
|
|
||
|
|
List table with multi-select in a dialog.
|
||
|
|
|
||
|
|
**File**: `packages/base/data/listTableDialog.vue`
|
||
|
|
|
||
|
|
**Props** (from `ListTableProps`):
|
||
|
|
| Prop | Type | Default | Description |
|
||
|
|
|------|------|---------|-------------|
|
||
|
|
| `props` | `TableColumn[]` | `-` | Column definitions |
|
||
|
|
| `rowKey` | `string` | `-` | Row key for selection |
|
||
|
|
| `useDicts` | `string[]` | `-` | Dict keys to load |
|
||
|
|
| `initialPage` | `number` | `1` | Initial page |
|
||
|
|
| `initialPageSize` | `number` | `-` | Initial page size |
|
||
|
|
| `initExample` | `object` | `-` | Initial query params |
|
||
|
|
|
||
|
|
**Events**:
|
||
|
|
| Event | Payload | Description |
|
||
|
|
|-------|---------|-------------|
|
||
|
|
| `query` | `(handler, example)` | Query handler (async) |
|
||
|
|
| `confirm` | `(handler, rows)` | Confirm handler (async) |
|
||
|
|
| `close` | `(handler)` | Close handler (async) |
|
||
|
|
|
||
|
|
**Usage**:
|
||
|
|
```vue
|
||
|
|
<ListTableDialog
|
||
|
|
:props="columns"
|
||
|
|
row-key="id"
|
||
|
|
:useDicts="['userStatus']"
|
||
|
|
@query="handleQuery"
|
||
|
|
@confirm="handleConfirm"
|
||
|
|
@close="closeDialog"
|
||
|
|
>
|
||
|
|
<template #name="{ row }">
|
||
|
|
<strong>{{ row.name }}</strong>
|
||
|
|
</template>
|
||
|
|
</ListTableDialog>
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### ModifyForm
|
||
|
|
|
||
|
|
Dynamic form for create/edit operations.
|
||
|
|
|
||
|
|
**File**: `packages/base/data/modify-form.vue`
|
||
|
|
|
||
|
|
**Props**:
|
||
|
|
| Prop | Type | Default | Description |
|
||
|
|
|------|------|---------|-------------|
|
||
|
|
| `width` | `number` | `80` | Label width |
|
||
|
|
| `param` | `object` | `{}` | Form data |
|
||
|
|
| `rules` | `object` | `{}` | Validation rules |
|
||
|
|
| `class` | `string` | `''` | Form class |
|
||
|
|
| `type` | `string` | `null` | Form type |
|
||
|
|
| `modify` | `boolean` | `false` | Edit mode (hides noModify fields) |
|
||
|
|
| `items` | `FormItem[]` | `[]` | Form field definitions |
|
||
|
|
| `confirm` | `string/boolean` | `undefined` | Confirm button config |
|
||
|
|
| `cancel` | `string/boolean` | `undefined` | Cancel button config |
|
||
|
|
|
||
|
|
**FormItem Interface**:
|
||
|
|
```typescript
|
||
|
|
interface FormItem {
|
||
|
|
code: string; // Field key
|
||
|
|
name?: string; // Label (used if i18n not set)
|
||
|
|
i18n?: string; // i18n key for label
|
||
|
|
dict?: string; // Dict for select
|
||
|
|
maxValue?: number; // Max value for number select
|
||
|
|
date?: boolean; // Render as date picker
|
||
|
|
formater?: string; // Date format string
|
||
|
|
slot?: boolean; // Enable slot rendering
|
||
|
|
type?: string; // Input type (textarea, etc.)
|
||
|
|
rows?: number; // Textarea rows
|
||
|
|
disabled?: boolean; // Disabled state
|
||
|
|
noModify?: boolean; // Hidden in edit mode
|
||
|
|
readonly?: boolean; // Readonly state
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Events**:
|
||
|
|
| Event | Description |
|
||
|
|
|-------|-------------|
|
||
|
|
| `confirm` | Form validated and submitted |
|
||
|
|
| `cancel` | Cancel clicked |
|
||
|
|
|
||
|
|
**Exposed Methods**:
|
||
|
|
| Method | Description |
|
||
|
|
|--------|-------------|
|
||
|
|
| `clearValidate` | Clear validation state |
|
||
|
|
|
||
|
|
**Usage**:
|
||
|
|
```vue
|
||
|
|
<ModifyForm
|
||
|
|
ref="formRef"
|
||
|
|
:param="formData"
|
||
|
|
:items="formFields"
|
||
|
|
:rules="formRules"
|
||
|
|
:modify="isEdit"
|
||
|
|
@confirm="handleSubmit"
|
||
|
|
@cancel="handleCancel"
|
||
|
|
/>
|
||
|
|
|
||
|
|
<script setup>
|
||
|
|
const formFields = [
|
||
|
|
{ code: 'name', name: 'Username', type: 'input' },
|
||
|
|
{ code: 'status', dict: 'userStatus' },
|
||
|
|
{ code: 'birthDate', date: true, formater: 'YYYY-MM-DD' },
|
||
|
|
{ code: 'remark', type: 'textarea', rows: 3 },
|
||
|
|
{ code: 'slot_field', slot: true }
|
||
|
|
];
|
||
|
|
</script>
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Descriptions
|
||
|
|
|
||
|
|
Key-value display component based on `el-descriptions`.
|
||
|
|
|
||
|
|
**File**: `packages/base/data/descriptions.vue`
|
||
|
|
|
||
|
|
**Props**:
|
||
|
|
| Prop | Type | Default | Description |
|
||
|
|
|------|------|---------|-------------|
|
||
|
|
| `data` | `object/array/string` | `0` | Display data |
|
||
|
|
| `title` | `string` | `null` | Descriptions title |
|
||
|
|
| `border` | `boolean` | `true` | Show border |
|
||
|
|
|
||
|
|
**Usage**:
|
||
|
|
```vue
|
||
|
|
<!-- Object key-value -->
|
||
|
|
<Descriptions :data="{ name: 'John', age: 30 }" title="User Info" />
|
||
|
|
|
||
|
|
<!-- Array of {key, value} -->
|
||
|
|
<Descriptions :data="[{ key: 'Email', value: 'john@example.com' }]" />
|
||
|
|
|
||
|
|
<!-- Single value -->
|
||
|
|
<Descriptions :data="totalCount" title="Statistics" />
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### TableAction
|
||
|
|
|
||
|
|
Row action buttons (edit/delete/add) with tooltips.
|
||
|
|
|
||
|
|
**File**: `packages/base/data/table-action.vue`
|
||
|
|
|
||
|
|
**Props**:
|
||
|
|
| Prop | Type | Default | Description |
|
||
|
|
|------|------|---------|-------------|
|
||
|
|
| `add` | `boolean` | `false` | Show add button |
|
||
|
|
| `modify` | `boolean` | `true` | Show edit button |
|
||
|
|
| `del` | `boolean` | `true` | Show delete button |
|
||
|
|
|
||
|
|
**Slots**:
|
||
|
|
| Slot | Description |
|
||
|
|
|------|-------------|
|
||
|
|
| `left` | Extra content before actions |
|
||
|
|
| `default` | Extra buttons after actions |
|
||
|
|
|
||
|
|
**Events**:
|
||
|
|
| Event | Description |
|
||
|
|
|-------|-------------|
|
||
|
|
| `modify` | Edit button clicked |
|
||
|
|
| `del` | Delete button clicked |
|
||
|
|
| `add` | Add button clicked |
|
||
|
|
|
||
|
|
**Usage**:
|
||
|
|
```vue
|
||
|
|
<TableAction @modify="handleEdit" @del="handleDelete" @add="handleAdd">
|
||
|
|
<template #left>
|
||
|
|
<NoobButton size="small">Custom</NoobButton>
|
||
|
|
</template>
|
||
|
|
</TableAction>
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Infomation
|
||
|
|
|
||
|
|
Card component for displaying single metrics/stats.
|
||
|
|
|
||
|
|
**File**: `packages/base/data/infomation.vue`
|
||
|
|
|
||
|
|
**Props**:
|
||
|
|
| Prop | Type | Default | Description |
|
||
|
|
|------|------|---------|-------------|
|
||
|
|
| `num` | `number` | `0` | Numeric value |
|
||
|
|
| `icon` | `string` | `''` | Element Plus icon name |
|
||
|
|
| `title` | `string` | `''` | Card title |
|
||
|
|
| `center` | `boolean` | `false` | Center alignment |
|
||
|
|
|
||
|
|
**Usage**:
|
||
|
|
```vue
|
||
|
|
<Infomation :num="152" icon="User" title="Total Users" />
|
||
|
|
<Infomation :num="89.5" icon="TrendCharts" title="Conversion Rate" center />
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Common Patterns
|
||
|
|
|
||
|
|
### Theme Integration
|
||
|
|
|
||
|
|
All components use Vuex store for theme values:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const { state } = useStore();
|
||
|
|
// state.style - colors, backgrounds
|
||
|
|
// state.size - sizes, paddings, fonts
|
||
|
|
```
|
||
|
|
|
||
|
|
Example in SCSS:
|
||
|
|
```scss
|
||
|
|
color: v-bind('state.style.color');
|
||
|
|
background-color: v-bind('state.style.primaryBg');
|
||
|
|
```
|
||
|
|
|
||
|
|
### i18n Integration
|
||
|
|
|
||
|
|
Components use `vue3-i18n` for translations:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const { t } = useI18n();
|
||
|
|
// t('key') for translation
|
||
|
|
```
|
||
|
|
|
||
|
|
Common i18n keys:
|
||
|
|
- `base.add` - Add button
|
||
|
|
- `base.delete` - Delete button
|
||
|
|
- `base.select` - Select/Search button
|
||
|
|
- `base.confirm` - Confirm button
|
||
|
|
- `base.cancel` - Cancel button
|
||
|
|
- `rule.pleaseEnter` - Enter placeholder
|
||
|
|
- `rule.pleaseSelect` - Select placeholder
|
||
|
|
|
||
|
|
### v-model Pattern
|
||
|
|
|
||
|
|
Components use Vue 3 `defineProps` with `defineEmits`:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const prop = defineProps({
|
||
|
|
modelValue: null,
|
||
|
|
});
|
||
|
|
const emit = defineEmits(["update:modelValue"]);
|
||
|
|
|
||
|
|
// Sync internal value to prop
|
||
|
|
watch(myValue, (n) => emit('update:modelValue', n));
|
||
|
|
```
|
||
|
|
|
||
|
|
### Width Management
|
||
|
|
|
||
|
|
Item components support multiple width modes:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Fixed width from prop
|
||
|
|
width.value = prop.width + 'px';
|
||
|
|
|
||
|
|
// Or from Vuex store
|
||
|
|
width.value = state.size.searchWidth;
|
||
|
|
|
||
|
|
// Full width
|
||
|
|
&.full { width: 100%; }
|
||
|
|
```
|
||
|
|
|
||
|
|
### Props Definition Pattern
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// With defaults
|
||
|
|
const prop = defineProps({
|
||
|
|
modelValue: null,
|
||
|
|
disabled: {
|
||
|
|
type: Boolean,
|
||
|
|
default: false,
|
||
|
|
},
|
||
|
|
});
|
||
|
|
|
||
|
|
// TypeScript interface (preferred for new components)
|
||
|
|
interface Props {
|
||
|
|
title: string;
|
||
|
|
items: TableColumn[];
|
||
|
|
}
|
||
|
|
withDefaults(defineProps<Props>(), {
|
||
|
|
items: () => [],
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Anti-Patterns
|
||
|
|
|
||
|
|
1. **Do not access Vuex store directly in templates** - Use computed properties
|
||
|
|
```vue
|
||
|
|
<!-- Bad -->
|
||
|
|
<div>{{ state.style.color }}</div>
|
||
|
|
|
||
|
|
<!-- Good -->
|
||
|
|
<div :style="{ color: themeColor }">text</div>
|
||
|
|
```
|
||
|
|
|
||
|
|
2. **Do not use `any` for typed props** - Define proper interfaces
|
||
|
|
```typescript
|
||
|
|
// Bad
|
||
|
|
prop: any;
|
||
|
|
|
||
|
|
// Good
|
||
|
|
interface TableColumn {
|
||
|
|
code: string;
|
||
|
|
name?: string;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
3. **Do not mix Element Plus imports** - Use the component registry
|
||
|
|
```typescript
|
||
|
|
// Avoid
|
||
|
|
import { ElButton } from 'element-plus';
|
||
|
|
|
||
|
|
// Prefer (through noob-mengyxu)
|
||
|
|
import { NoobButton } from 'noob-mengyxu';
|
||
|
|
```
|
||
|
|
|
||
|
|
4. **Avoid prop drilling** - Use provide/inject or Vuex for deep component trees
|
||
|
|
|
||
|
|
5. **Do not forget to emit `update:modelValue`** - For v-model to work properly
|