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

5.4 KiB

Component Guidelines

How components are built in this project.


Overview

This project uses Vue 3 with Element Plus components. Components follow a consistent pattern for props, slots, and composition.


Element Plus el-table-v2 Usage

IMPORTANT: el-table-v2 has a different API than el-table. Key differences:

Auto-resizing with ElAutoResizer

el-table-v2 does NOT have an autosize prop. Use ElAutoResizer wrapper instead:

<el-auto-resizer>
  <template #default="{ height, width }">
    <el-table-v2
      :columns="columns"
      :data="data"
      :width="width"
      :height="height"
      :row-height="50"
      :header-height="50"
    />
  </template>
</el-auto-resizer>

Note: Parent container of ElAutoResizer must have a fixed height (e.g., height: 100% or explicit pixel value).

el-table-v2 Required Props

el-table-v2 requires both width and height as mandatory props - they cannot be omitted.

Slot-to-CellRenderer Conversion

el-table-v2 uses cellRenderer functions instead of Vue slots. To render parent slots:

import { useSlots, renderSlot } from 'vue';

const slots = useSlots();

// In column definition:
col.cellRenderer = ({ cellData, rowData }) => {
  if (slots[slotName]) {
    return renderSlot(slots, slotName, { row: rowData });
  }
  return h('span', {}, formattedValue);
};

Column Flex Distribution

For auto-distributed column widths (no explicit width), keep a minimum basis and use flex factors greater than 1 so columns can absorb remaining row space after scrollbar width and fixed columns are accounted for:

const col = {
  key: 'code',
  title: 'Name',
  dataKey: 'code',
  width: 120,        // minimum width required
  flexGrow: 1.2,     // expands to fill available space
  flexShrink: 1.2,   // shrinks proportionally when space is tight
  align: 'center',
};

Import Path Conventions

Correct Import Patterns

// ✅ CORRECT: Use relative paths for internal modules
import { clearAndAssign, deepCopy } from "../util/objectUtil";
import { ElAutoResizer, ElTableV2 } from "element-plus";

// ❌ WRONG: noob-mengyxu/utils does not exist
import { clearAndAssign } from "noob-mengyxu/utils";

Rule: For utility functions within the project, always use relative paths (../util/, ./). The noob-mengyxu namespace is only for exporting packages.


Component Structure

<template>
  <div class="component-name">
    <!-- Markup -->
  </div>
</template>

<script lang="ts" setup>
import { ref, computed } from 'vue';

// Props definition
interface Props {
  data?: any;
  columns?: TableColumn[];
}

const prop = withDefaults(defineProps<Props>(), {
  data: () => [],
  columns: () => [],
});

const emit = defineEmits(['query', 'change']);
</script>

<style lang="scss" scoped>
/* Scoped styles */
</style>

Props Conventions

  1. Use withDefaults(defineProps<Props>()) for optional props with defaults
  2. Always provide default values for array/object props
  3. Use TypeScript interfaces for complex prop types

Common Mistakes

1. Timestamp Formatting Assumptions

The formatStamp function expects Unix timestamps in seconds (not milliseconds):

const formatStamp = (value: any) => {
  const date = new Date(value * 1000); // expects seconds
  // ...
};

When generating test data, use Math.floor(Date.now() / 1000) or Date.now() / 1000.

2. Element Plus Version Mismatch

Element Plus el-table (v1) and el-table-v2 (virtualized) have completely different APIs:

  • v1: uses prop for columns, slots for cell rendering
  • v2: uses columns prop, cellRenderer functions, separate ElAutoResizer

3. Forgetting Required Props

When using new Element Plus components, verify required props - they will cause runtime errors if omitted.

4. Vue 3 Template Ref Auto-Unwrap Gotchas

Vue 3 <script setup> auto-unwraps refs in templates, but with important limitations:

Works - Top-level refs:

const count = ref(0);
// Template: {{ count }} ✓ (auto-unwrapped)

Broken - Refs nested in objects:

const virtualizer = useVirtualRows(...);
// Template: {{ virtualizer.visibleRows }} ✗ (NOT auto-unwrapped)

Fixed - Destructure refs for template use:

const virtualizer = useVirtualRows(...);
const { visibleRows, totalHeight } = virtualizer;
// Template: {{ visibleRows }} ✓ (auto-unwrapped top-level ref)

TypeScript also fails when refs are nested in objects - TypeScript sees virtualizer.visibleRows as type ComputedRef | true | VirtualRow[] instead of VirtualRow[].

Rule: Always destructure refs from composable return objects when they will be used directly in templates.

5. Virtual Scrolling Overscan with Small Datasets

Virtual scrolling with overscan may render ALL rows even when scrolled if:

  • Total row heights < viewport height + (overscan × average row height)
  • Dataset is small (e.g., 10 rows with 60px each = 600px total)

With overscan=5 and 10 rows (600px total) and viewport 300px:

  • Overscan renders rows -5 to +5 from viewport edge
  • This covers all 10 rows regardless of scroll position

This is NOT a bug - it's expected behavior. Virtual scrolling only culls rows when the dataset exceeds viewport + overscan capacity.


Accessibility

  • Use semantic HTML elements
  • Ensure keyboard navigation works
  • Add ARIA labels where necessary