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

209 lines
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:
```vue
<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:
```typescript
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
3 months ago
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:
```typescript
const col = {
key: 'code',
title: 'Name',
dataKey: 'code',
width: 120, // minimum width required
3 months ago
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
```typescript
// ✅ 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
```vue
<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):
```typescript
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:**
```typescript
const count = ref(0);
// Template: {{ count }} ✓ (auto-unwrapped)
```
**Broken - Refs nested in objects:**
```typescript
const virtualizer = useVirtualRows(...);
// Template: {{ virtualizer.visibleRows }} ✗ (NOT auto-unwrapped)
```
**Fixed - Destructure refs for template use:**
```typescript
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