fix: 修复前端登录体验和API调用问题
- 修复路由守卫:未登录时直接跳转,不显示提示信息 - 修复API拦截器:401错误直接跳转,无需确认 - 移除不必要的ElMessageBox确认框 - 优化Token过期处理逻辑 - 修复文件管理API引入路径和URL前缀 - 修复调拨/回收管理API端点不匹配问题 - 修复通知管理API方法不匹配问题 - 统一系统配置API路径为单数形式 影响文件: - src/router/index.ts - src/api/request.ts - src/api/file.ts - src/api/index.ts 测试状态: - 前端构建通过 - 所有API路径已验证 - 登录流程测试通过 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
784
COMPONENT_USAGE_GUIDE.md
Normal file
784
COMPONENT_USAGE_GUIDE.md
Normal file
@@ -0,0 +1,784 @@
|
||||
# 资产管理系统 - 组件使用文档
|
||||
|
||||
## 目录
|
||||
|
||||
1. [批量导入组件](#批量导入组件)
|
||||
2. [批量导出组件](#批量导出组件)
|
||||
3. [扫码查询组件](#扫码查询组件)
|
||||
4. [资产分配组件](#资产分配组件)
|
||||
5. [维修管理组件](#维修管理组件)
|
||||
6. [统计报表组件](#统计报表组件)
|
||||
|
||||
---
|
||||
|
||||
## 1. 批量导入组件
|
||||
|
||||
### 组件信息
|
||||
- **路径**: `src/views/assets/components/BatchImportDialog.vue`
|
||||
- **名称**: `BatchImportDialog`
|
||||
- **功能**: 批量导入资产数据
|
||||
|
||||
### Props
|
||||
| 参数 | 类型 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| modelValue | boolean | - | 对话框显示状态 |
|
||||
|
||||
### Events
|
||||
| 事件名 | 参数 | 说明 |
|
||||
|--------|------|------|
|
||||
| update:modelValue | (value: boolean) | 显示状态变化 |
|
||||
| success | - | 导入成功触发 |
|
||||
|
||||
### 使用示例
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<el-button @click="handleImport">批量导入</el-button>
|
||||
|
||||
<BatchImportDialog
|
||||
v-model="importVisible"
|
||||
@success="handleImportSuccess"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import BatchImportDialog from '@/views/assets/components/BatchImportDialog.vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
const importVisible = ref(false)
|
||||
|
||||
const handleImport = () => {
|
||||
importVisible.value = true
|
||||
}
|
||||
|
||||
const handleImportSuccess = () => {
|
||||
ElMessage.success('导入成功')
|
||||
// 刷新列表
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
### 功能说明
|
||||
|
||||
#### 三步导入流程
|
||||
|
||||
**步骤1: 上传文件**
|
||||
- 支持拖拽上传
|
||||
- 支持 .xlsx 和 .xls 格式
|
||||
- 提供模板下载
|
||||
|
||||
**步骤2: 数据预览**
|
||||
- 显示解析后的数据
|
||||
- 标记错误行(红色背景)
|
||||
- 显示错误信息
|
||||
- 统计错误数量
|
||||
|
||||
**步骤3: 导入结果**
|
||||
- 显示导入统计(总数、成功、失败)
|
||||
- 失败明细列表
|
||||
- 导出错误日志
|
||||
- 导入进度条
|
||||
|
||||
### 注意事项
|
||||
|
||||
- 文件大小限制:建议不超过10MB
|
||||
- 单次导入数量:最多1000条
|
||||
- 必须先下载模板,按模板格式填写
|
||||
- 错误数据不会导入,需修改后重新导入
|
||||
|
||||
---
|
||||
|
||||
## 2. 批量导出组件
|
||||
|
||||
### 组件信息
|
||||
- **路径**: `src/views/assets/components/BatchExportDialog.vue`
|
||||
- **名称**: `BatchExportDialog`
|
||||
- **功能**: 批量导出资产数据
|
||||
|
||||
### Props
|
||||
| 参数 | 类型 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| modelValue | boolean | - | 对话框显示状态 |
|
||||
|
||||
### Events
|
||||
| 事件名 | 参数 | 说明 |
|
||||
|--------|------|------|
|
||||
| update:modelValue | (value: boolean) | 显示状态变化 |
|
||||
|
||||
### 使用示例
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<el-button @click="handleExport">批量导出</el-button>
|
||||
|
||||
<BatchExportDialog v-model="exportVisible" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import BatchExportDialog from '@/views/assets/components/BatchExportDialog.vue'
|
||||
|
||||
const exportVisible = ref(false)
|
||||
|
||||
const handleExport = () => {
|
||||
exportVisible.value = true
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
### 功能说明
|
||||
|
||||
#### 导出字段选择
|
||||
可选择的字段:
|
||||
- 资产编码(assetCode)
|
||||
- 资产名称(assetName)
|
||||
- 设备类型(deviceTypeName)
|
||||
- 品牌(brandName)
|
||||
- 型号(modelName)
|
||||
- 序列号(serialNumber)
|
||||
- 所属网点(orgName)
|
||||
- 位置(location)
|
||||
- 状态(status)
|
||||
- 采购日期(purchaseDate)
|
||||
- 采购价格(purchasePrice)
|
||||
- 保修截止(warrantyExpireDate)
|
||||
|
||||
#### 筛选条件
|
||||
- 设备类型
|
||||
- 所属网点
|
||||
- 资产状态
|
||||
- 关键词搜索
|
||||
|
||||
#### 导出格式
|
||||
- Excel (.xlsx)
|
||||
- CSV (.csv)
|
||||
|
||||
---
|
||||
|
||||
## 3. 扫码查询组件
|
||||
|
||||
### 组件信息
|
||||
- **路径**: `src/views/assets/AssetScan.vue`
|
||||
- **名称**: `AssetScan`
|
||||
- **功能**: 扫码查询资产
|
||||
|
||||
### 主要功能
|
||||
|
||||
#### 1. 相机扫码
|
||||
```typescript
|
||||
// 启动相机
|
||||
const startCamera = async () => {
|
||||
const stream = await navigator.mediaDevices.getUserMedia({
|
||||
video: { facingMode: 'environment' }
|
||||
})
|
||||
videoRef.value.srcObject = stream
|
||||
}
|
||||
|
||||
// 停止相机
|
||||
const stopCamera = () => {
|
||||
const stream = videoRef.value.srcObject
|
||||
stream.getTracks().forEach(track => track.stop())
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 手动输入
|
||||
```vue
|
||||
<el-input
|
||||
v-model="inputCode"
|
||||
placeholder="请输入资产编码"
|
||||
@keyup.enter="handleManualSearch"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="handleManualSearch">查询</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
```
|
||||
|
||||
#### 3. 扫码历史
|
||||
- 保存在 localStorage
|
||||
- 最多保存20条
|
||||
- 点击历史记录可快速查询
|
||||
|
||||
#### 4. 扫码音效
|
||||
```typescript
|
||||
// 使用Web Audio API
|
||||
const playBeep = () => {
|
||||
const audioContext = new AudioContext()
|
||||
const oscillator = audioContext.createOscillator()
|
||||
oscillator.frequency.value = 800
|
||||
oscillator.start()
|
||||
setTimeout(() => oscillator.stop(), 100)
|
||||
}
|
||||
```
|
||||
|
||||
### 使用示例
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<router-link to="/assets/scan">
|
||||
<el-button>扫码查询</el-button>
|
||||
</router-link>
|
||||
</template>
|
||||
```
|
||||
|
||||
### 注意事项
|
||||
|
||||
- 摄像头访问需要HTTPS或localhost
|
||||
- 需要授予摄像头权限
|
||||
- 二维码识别需集成 @zxing/library
|
||||
|
||||
---
|
||||
|
||||
## 4. 资产分配组件
|
||||
|
||||
### 4.1 分配单列表
|
||||
|
||||
**路径**: `src/views/allocation/AllocationList.vue`
|
||||
|
||||
#### 筛选条件
|
||||
- 单据类型(allocation/transfer/recovery/maintenance/scrap)
|
||||
- 审批状态(pending/approved/rejected/cancelled)
|
||||
- 执行状态(pending/executing/completed)
|
||||
- 关键词(单号/申请人)
|
||||
|
||||
#### 操作按钮
|
||||
- 新建分配单
|
||||
- 查看详情
|
||||
- 编辑(草稿状态)
|
||||
- 删除(草稿状态)
|
||||
- 提交审批
|
||||
- 审批(待审批状态)
|
||||
- 执行(已通过状态)
|
||||
|
||||
### 4.2 创建分配单对话框
|
||||
|
||||
**路径**: `src/views/allocation/components/CreateAllocationDialog.vue`
|
||||
|
||||
#### Props
|
||||
| 参数 | 类型 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| modelValue | boolean | - | 对话框显示状态 |
|
||||
| orderId | number \| null | null | 分配单ID(编辑时传入) |
|
||||
|
||||
#### Events
|
||||
| 事件名 | 参数 | 说明 |
|
||||
|--------|------|------|
|
||||
| update:modelValue | (value: boolean) | 显示状态变化 |
|
||||
| success | - | 操作成功触发 |
|
||||
|
||||
#### 表单字段
|
||||
```typescript
|
||||
{
|
||||
orderType: 'allocation', // 单据类型
|
||||
targetOrganizationId: 1, // 目标机构ID
|
||||
title: '分配单标题', // 标题
|
||||
assetIds: [1, 2, 3], // 资产ID列表
|
||||
remark: '备注信息' // 备注
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 资产选择器对话框
|
||||
|
||||
**路径**: `src/views/allocation/components/AssetSelectorDialog.vue`
|
||||
|
||||
#### Props
|
||||
| 参数 | 类型 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| modelValue | boolean | - | 对话框显示状态 |
|
||||
| excludeIds | number[] | [] | 排除的资产ID |
|
||||
|
||||
#### Events
|
||||
| 事件名 | 参数 | 说明 |
|
||||
|--------|------|------|
|
||||
| update:modelValue | (value: boolean) | 显示状态变化 |
|
||||
| confirm | (assets: any[]) | 确认选择 |
|
||||
|
||||
#### 使用示例
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<el-button @click="showSelector">选择资产</el-button>
|
||||
|
||||
<AssetSelectorDialog
|
||||
v-model="selectorVisible"
|
||||
:exclude-ids="selectedIds"
|
||||
@confirm="handleConfirm"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import AssetSelectorDialog from '@/views/allocation/components/AssetSelectorDialog.vue'
|
||||
|
||||
const selectorVisible = ref(false)
|
||||
const selectedIds = ref<number[]>([])
|
||||
|
||||
const showSelector = () => {
|
||||
selectorVisible.value = true
|
||||
}
|
||||
|
||||
const handleConfirm = (assets: any[]) => {
|
||||
console.log('已选择:', assets)
|
||||
selectedIds.value = assets.map(a => a.id)
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
### 4.4 分配单详情对话框
|
||||
|
||||
**路径**: `src/views/allocation/components/AllocationDetailDialog.vue`
|
||||
|
||||
#### Tabs
|
||||
1. **基本信息** - 分配单基本信息
|
||||
2. **资产明细** - 分配的资产列表
|
||||
3. **审批流程** - 审批历史时间轴
|
||||
|
||||
#### 操作功能
|
||||
- 审批(通过/拒绝)
|
||||
- 执行(开始/完成)
|
||||
- 查看审批历史
|
||||
|
||||
---
|
||||
|
||||
## 5. 维修管理组件
|
||||
|
||||
### 5.1 维修管理页面
|
||||
|
||||
**路径**: `src/views/assets/MaintenanceManagement.vue`
|
||||
|
||||
#### 筛选条件
|
||||
- 状态(待维修/维修中/已完成/已取消)
|
||||
- 优先级(低/中/高)
|
||||
- 关键词(资产名称/编码)
|
||||
|
||||
#### 操作按钮
|
||||
- 新建维修记录
|
||||
- 查看
|
||||
- 编辑(待维修状态)
|
||||
- 开始维修
|
||||
- 完成维修
|
||||
- 取消维修
|
||||
|
||||
### 5.2 维修记录对话框
|
||||
|
||||
**路径**: `src/views/assets/components/MaintenanceDialog.vue`
|
||||
|
||||
#### Props
|
||||
| 参数 | 类型 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| modelValue | boolean | - | 对话框显示状态 |
|
||||
| recordId | number \| null | null | 记录ID(编辑时传入) |
|
||||
| assetId | number \| null | null | 资产ID(预选) |
|
||||
|
||||
#### Events
|
||||
| 事件名 | 参数 | 说明 |
|
||||
|--------|------|------|
|
||||
| update:modelValue | (value: boolean) | 显示状态变化 |
|
||||
| success | - | 操作成功触发 |
|
||||
|
||||
#### 表单字段
|
||||
```typescript
|
||||
{
|
||||
assetId: 1, // 资产ID
|
||||
faultType: 'hardware', // 故障类型
|
||||
priority: 'medium', // 优先级
|
||||
maintenanceType: 'self_repair', // 维修类型
|
||||
faultDescription: '...', // 故障描述
|
||||
maintenancePersonnel: '张三', // 维修人员
|
||||
maintenanceCost: 500.00, // 维修费用
|
||||
startDate: '2025-01-24', // 开始日期
|
||||
endDate: '2025-01-25', // 结束日期
|
||||
remark: '备注', // 备注
|
||||
photos: [] // 维修照片
|
||||
}
|
||||
```
|
||||
|
||||
#### 使用示例
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<el-button @click="showMaintenance">新建维修记录</el-button>
|
||||
|
||||
<MaintenanceDialog
|
||||
v-model="maintenanceVisible"
|
||||
:asset-id="currentAssetId"
|
||||
@success="handleSuccess"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import MaintenanceDialog from '@/views/assets/components/MaintenanceDialog.vue'
|
||||
|
||||
const maintenanceVisible = ref(false)
|
||||
const currentAssetId = ref<number>(1)
|
||||
|
||||
const showMaintenance = () => {
|
||||
maintenanceVisible.value = true
|
||||
}
|
||||
|
||||
const handleSuccess = () => {
|
||||
console.log('维修记录已保存')
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 统计报表组件
|
||||
|
||||
### 组件信息
|
||||
- **路径**: `src/views/assets/StatisticsDashboard.vue`
|
||||
- **名称**: `StatisticsDashboard`
|
||||
- **功能**: 资产统计和可视化
|
||||
|
||||
### 主要功能
|
||||
|
||||
#### 1. 统计卡片
|
||||
```vue
|
||||
<el-card class="stat-card">
|
||||
<div class="stat-content">
|
||||
<div class="stat-icon total">
|
||||
<el-icon><Box /></el-icon>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">{{ totalAssets }}</div>
|
||||
<div class="stat-label">资产总数</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
```
|
||||
|
||||
卡片类型:
|
||||
- 资产总数(紫色)
|
||||
- 在用资产(绿色)
|
||||
- 维修中(橙色)
|
||||
- 待报废(红色)
|
||||
|
||||
#### 2. ECharts图表
|
||||
|
||||
**图表1: 资产状态分布(饼图)**
|
||||
```typescript
|
||||
const statusPieOption = {
|
||||
series: [{
|
||||
type: 'pie',
|
||||
radius: ['40%', '70%'], // 环形
|
||||
data: [
|
||||
{ value: 735, name: '在用' },
|
||||
{ value: 580, name: '在库' },
|
||||
{ value: 484, name: '维修中' },
|
||||
{ value: 300, name: '待报废' }
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
**图表2: 资产类型分布(柱状图)**
|
||||
```typescript
|
||||
const typeBarOption = {
|
||||
xAxis: { data: ['计算机', '打印机', '复印机', ...] },
|
||||
series: [{
|
||||
type: 'bar',
|
||||
data: [326, 208, 156, ...]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
**图表3: 资产价值趋势(折线图)**
|
||||
```typescript
|
||||
const valueTrendOption = {
|
||||
xAxis: { data: ['1月', '2月', '3月', ...] },
|
||||
yAxis: [
|
||||
{ type: 'value', name: '数量' },
|
||||
{ type: 'value', name: '价值(万元)' }
|
||||
],
|
||||
series: [
|
||||
{ name: '资产数量', type: 'line' },
|
||||
{ name: '资产价值', type: 'line', yAxisIndex: 1 }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**图表4: 机构资产分布(树图)**
|
||||
```typescript
|
||||
const orgDistributionOption = {
|
||||
series: [{
|
||||
type: 'tree',
|
||||
data: [
|
||||
{
|
||||
name: '广东省',
|
||||
children: [
|
||||
{ name: '广州市', children: [...] },
|
||||
{ name: '深圳市', children: [...] }
|
||||
]
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
**图表5: 维修统计(堆叠柱状图)**
|
||||
```typescript
|
||||
const maintenanceOption = {
|
||||
series: [
|
||||
{ name: '硬件故障', type: 'bar', stack: 'total' },
|
||||
{ name: '软件故障', type: 'bar', stack: 'total' },
|
||||
{ name: '其他', type: 'bar', stack: 'total' }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 使用示例
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<router-link to="/assets/statistics">
|
||||
<el-button>统计报表</el-button>
|
||||
</router-link>
|
||||
</template>
|
||||
```
|
||||
|
||||
### ECharts按需引入
|
||||
|
||||
```typescript
|
||||
import { use } from 'echarts/core'
|
||||
import { CanvasRenderer } from 'echarts/renderers'
|
||||
import { PieChart, BarChart, LineChart, TreeChart } from 'echarts/charts'
|
||||
import {
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
LegendComponent,
|
||||
GridComponent
|
||||
} from 'echarts/components'
|
||||
|
||||
use([
|
||||
CanvasRenderer,
|
||||
PieChart,
|
||||
BarChart,
|
||||
LineChart,
|
||||
TreeChart,
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
LegendComponent,
|
||||
GridComponent
|
||||
])
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 通用组件模式
|
||||
|
||||
### 对话框组件模式
|
||||
|
||||
所有对话框组件遵循统一的模式:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
:title="title"
|
||||
width="800px"
|
||||
:close-on-click-modal="false"
|
||||
@close="handleClose"
|
||||
>
|
||||
<!-- 内容 -->
|
||||
<el-form ref="formRef" :model="formData" :rules="formRules">
|
||||
<!-- 表单字段 -->
|
||||
</el-form>
|
||||
|
||||
<!-- 底部按钮 -->
|
||||
<template #footer>
|
||||
<el-button @click="handleClose">取消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
modelValue: boolean
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'update:modelValue', value: boolean): void
|
||||
(e: 'success'): void
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
const emit = defineEmits<Emits>()
|
||||
|
||||
const visible = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (val) => emit('update:modelValue', val)
|
||||
})
|
||||
|
||||
const handleClose = () => {
|
||||
visible.value = false
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
### 表单验证模式
|
||||
|
||||
```typescript
|
||||
const formRules = {
|
||||
fieldName: [
|
||||
{ required: true, message: '请输入', trigger: 'blur' },
|
||||
{ min: 2, max: 50, message: '长度在2-50个字符', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
const valid = await formRef.value?.validate().catch(() => false)
|
||||
if (!valid) return
|
||||
|
||||
// 提交逻辑
|
||||
}
|
||||
```
|
||||
|
||||
### API调用模式
|
||||
|
||||
```typescript
|
||||
const fetchData = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await apiFunction(params)
|
||||
// 处理数据
|
||||
} catch (error) {
|
||||
ElMessage.error('操作失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 样式规范
|
||||
|
||||
### SCSS变量
|
||||
|
||||
```scss
|
||||
// 主题色
|
||||
$primary-color: #409EFF;
|
||||
$success-color: #67C23A;
|
||||
$warning-color: #E6A23C;
|
||||
$danger-color: #F56C6C;
|
||||
$info-color: #909399;
|
||||
|
||||
// 文本色
|
||||
$text-primary: #303133;
|
||||
$text-regular: #606266;
|
||||
$text-secondary: #909399;
|
||||
|
||||
// 边框色
|
||||
$border-base: #DCDFE6;
|
||||
$border-light: #E4E7ED;
|
||||
$border-lighter: #EBEEF5;
|
||||
$border-extra-light: #F2F6FC;
|
||||
|
||||
// 背景色
|
||||
$bg-color: #F5F7FA;
|
||||
```
|
||||
|
||||
### 响应式断点
|
||||
|
||||
```scss
|
||||
// 屏幕断点
|
||||
$sm: 768px;
|
||||
$md: 992px;
|
||||
$lg: 1200px;
|
||||
$xl: 1920px;
|
||||
|
||||
@media (max-width: $sm) {
|
||||
// 小屏幕样式
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q: 如何自定义表单验证?
|
||||
|
||||
```typescript
|
||||
const customValidator = (rule: any, value: any, callback: any) => {
|
||||
if (!value) {
|
||||
callback(new Error('不能为空'))
|
||||
} else if (value.length < 6) {
|
||||
callback(new Error('长度不能少于6位'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
const formRules = {
|
||||
password: [
|
||||
{ validator: customValidator, trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Q: 如何处理文件上传?
|
||||
|
||||
```vue
|
||||
<el-upload
|
||||
action="/api/upload"
|
||||
:on-success="handleSuccess"
|
||||
:on-error="handleError"
|
||||
:before-upload="beforeUpload"
|
||||
>
|
||||
<el-button>上传文件</el-button>
|
||||
</el-upload>
|
||||
```
|
||||
|
||||
### Q: 如何实现分页?
|
||||
|
||||
```typescript
|
||||
import { usePagination } from '@/composables/usePagination'
|
||||
|
||||
const { pagination, resetPage, setTotal } = usePagination()
|
||||
|
||||
const fetchData = async () => {
|
||||
const data = await apiFunction({
|
||||
page: pagination.page,
|
||||
page_size: pagination.pageSize
|
||||
})
|
||||
setTotal(data.total)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### 1. 组件命名
|
||||
- 使用大驼峰命名
|
||||
- 文件名与组件名一致
|
||||
- 对话框以Dialog结尾
|
||||
|
||||
### 2. Props定义
|
||||
- 使用TypeScript接口
|
||||
- 提供默认值
|
||||
- 添加注释说明
|
||||
|
||||
### 3. 事件命名
|
||||
- 使用kebab-case
|
||||
- 事件名语义明确
|
||||
- 参数类型明确
|
||||
|
||||
### 4. 样式编写
|
||||
- 使用scoped避免污染
|
||||
- 使用SCSS变量
|
||||
- 遵循BEM命名
|
||||
|
||||
### 5. 性能优化
|
||||
- 合理使用computed
|
||||
- 避免不必要的watch
|
||||
- 按需引入组件
|
||||
|
||||
---
|
||||
|
||||
**更新时间**: 2025-01-24
|
||||
**版本**: v1.0.0
|
||||
Reference in New Issue
Block a user