基于vue3.0和element-plus的组件库
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.
 
 
 
 

18 KiB

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:

<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:

<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:

<!-- 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:

<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:

<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:

<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:

<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:

<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:

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:

<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:

<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:

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:

<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:

<!-- 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:

<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:

<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:

const { state } = useStore();
// state.style - colors, backgrounds
// state.size - sizes, paddings, fonts

Example in SCSS:

color: v-bind('state.style.color');
background-color: v-bind('state.style.primaryBg');

i18n Integration

Components use vue3-i18n for translations:

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:

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:

// 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

// 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

    <!-- Bad -->
    <div>{{ state.style.color }}</div>
    
    <!-- Good -->
    <div :style="{ color: themeColor }">text</div>
    
  2. Do not use any for typed props - Define proper interfaces

    // Bad
    prop: any;
    
    // Good
    interface TableColumn {
      code: string;
      name?: string;
    }
    
  3. Do not mix Element Plus imports - Use the component registry

    // 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