基于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.
 
 
 
 

7.8 KiB

noob-components

Project Overview

A Vue 3 component library built with TypeScript and Vite, providing reusable UI components and composables for management applications. The library extends Element Plus with custom components, layouts, and utilities.

Technology Stack

  • Framework: Vue 3 (Composition API)
  • Language: TypeScript
  • Build Tool: Vite
  • Base UI Library: Element Plus
  • Styling: SCSS with scoped styles
  • State Management: Vuex (for consuming applications)
  • Internationalization: vue3-i18n
  • Package Manager: npm/bun

Project Structure

noob-components/
├── packages/
│   ├── base/          # Base UI components
│   │   ├── data/      # Data display components (ListTable, etc.)
│   │   ├── item/      # Form input components (Input, Select, DateTime, etc.)
│   │   └── layout/    # Layout components
│   ├── manage/        # Management-specific components
│   │   ├── common/    # Common components (login forms, etc.)
│   │   └── router/    # Router layout components (index.vue, zhuBeiDong.vue)
│   └── app.ts         # Main export file
├── plugs/
│   ├── composables/   # Vue composables
│   │   ├── useListTable.ts    # Table data management
│   │   ├── useModifyForm.ts   # Form modification logic
│   │   └── ...
│   ├── http/          # HTTP utilities and API client
│   ├── element/       # Element Plus utilities
│   └── utils/         # General utilities
├── dist/              # Build output
│   ├── noob-mengyxu.js
│   ├── noob-mengyxu.css
│   └── style.css
└── vite.config.ts     # Vite configuration

Key Components

Router Layouts (packages/manage/router/)

Two main layout components for management applications:

  • index.vue: Standard layout with horizontal/vertical menu support
  • zhuBeiDong.vue: Alternative layout with specific styling

Both components:

  • Provide app structure with header, aside, and main content areas
  • Support theme switching (via state.style)
  • Support size adjustments (via state.size)
  • Handle user authentication checks
  • Use scoped CSS with :deep() for child component styling

Login Components (packages/manage/common/)

  • login2.vue: Login form with vertical layout (labels above inputs)

Data Components (packages/base/data/)

  • list-table.vue: Table component with pagination, sorting, filtering
    • Uses TypeScript interface-based props
    • Integrates with useListTable composable
    • Supports Element Plus table features

Form Components (packages/base/item/)

  • input.vue: Text input wrapper
  • select.vue: Select dropdown wrapper
  • datetime.vue: Date/time picker wrapper
  • modify-form.vue: Form modification component

All form components:

  • Support .full class for 100% width
  • Have default constrained width via .form-item
  • Use scoped styles

Key Composables

useListTable (plugs/composables/useListTable.ts)

Manages table data with pagination and filtering.

Important implementation details:

  • Uses deepCopy(toRaw(example)) to avoid infinite reactivity loops
  • Automatically manages pagination params (page, size)
  • Supports both array and paginated response ({ data: [], total: N })
  • Has disableAutoQuery option to disable automatic queries

Interface:

interface TableColumn {
  code: string;           // Property path (supports nested like "task.name")
  name?: string;          // Display name
  i18n?: string;          // i18n key
  type?: string;          // Column type
  width?: string | number;
  fixed?: boolean | 'left' | 'right';
  align?: 'left' | 'center' | 'right';
  slot?: boolean;         // Use custom slot
  dict?: string;          // Dictionary for value mapping
  timestamp?: boolean;    // Format as timestamp
  filesize?: boolean;     // Format as file size
}

useModifyForm (plugs/composables/useModifyForm.ts)

Manages form state for add/edit operations.

Styling Conventions

CSS Scoping

  • Router components (index.vue, zhuBeiDong.vue):

    • Split into unscoped (for body) and scoped blocks
    • Use :deep() for child components from library
    • Example: :deep(.head-icon), :deep(.el-input)
  • Form components (input.vue, select.vue, etc.):

    • Use scoped styles
    • Support .full class to override width

CSS Variables Binding

Components use v-bind() to bind Vuex state to CSS:

color: v-bind("state.style.color");
background-color: v-bind("state.style.bodyBg");
font-size: v-bind("state.size.fontSize");
height: v-bind("state.size.headHeight");

State Structure

state.style = {
  color: string;
  bodyBg: string;
  headBg: string;
  itemBg: string;
  selectionBg: string;
  selectionColor: string;
}

state.size = {
  fontSize: string;
  headHeight: string;
  asideWidth: string;
  mainHeight: string;
  tableHeight: string;
  headIconSize: string;
  size: 'small' | 'default' | 'large';
}

Build Configuration

Library Build (vite.config.ts)

  • Entry: packages/app.ts
  • Output: dist/noob-mengyxu.js and dist/noob-mengyxu.css
  • CSS is extracted separately (not injected into JS)
  • Vue is externalized (peer dependency)

Development Workflow

  1. Local development with linking:

    bun link
    cd ../s6a_manage
    bun link noob-mengyxu
    
  2. Important: When using bun link, JS/templates load from source but CSS loads from dist/

    • After CSS changes, must rebuild: npm run build:lib

Common Issues and Solutions

Issue: CSS Conflicts from Multiple Router Components

Problem: Multiple unscoped CSS rules causing variable conflicts

Solution:

  • Use scoped styles with :deep() for child elements
  • Split into unscoped (body only) and scoped blocks

Issue: Infinite Loop in useListTable with deepCopy

Problem: deepCopy(example) inside watchEffect establishes reactive dependencies

Solution: Use deepCopy(toRaw(example)) for deep non-reactive clone

Issue: Form Input Width Issues

Problem: .form-item class collision between components

Solution:

  • Add .full { width: 100%; } to base components
  • Remove conflicting styles from router components
  • Use :deep() when needed to style library components

Issue: Header Icons Not Styled

Problem: After scoping, library components don't have scoped attribute

Solution: Change .head-icon to :deep(.head-icon) in router components

TypeScript Conventions

  • Use interface-based prop definitions: defineProps<Props>()
  • Use withDefaults() for default values
  • Prefer undefined over null for optional props (simplifies types)
  • Use () => [] or () => ({}) for array/object defaults

Exports

Main export file: packages/app.ts

Exports are organized into modules:

  • Element: Element Plus utilities
  • NoobHead: Header components
  • NoobInput, NoobButton, etc.: Form components
  • ListTable, ModifyForm: Data components
  • Api: HTTP client
  • Styles, Size: Theme utilities
  • Composables: useListTable, useModifyForm, etc.

Recent Changes

  1. TypeScript Conversion: Converted list-table.vue to interface-based props
  2. CSS Scoping: Fixed conflicts by scoping router component styles
  3. Reactivity Fix: Changed to deepCopy(toRaw(example)) in useListTable
  4. Form Layout: Fixed login form to use vertical layout (labels above inputs)
  5. Pagination: Fixed handling of paginated responses { data: [], total: N }

Dependencies

Key dependencies:

  • vue: ^3.x
  • element-plus: UI library
  • vue3-i18n: Internationalization
  • vuex: State management (for consumers)
  • axios: HTTP client
  • js-md5: Password hashing
  • @vueuse/core: Vue utilities
  • lodash-es: Utility functions