# 资产管理系统 - 组件使用文档
## 目录
1. [批量导入组件](#批量导入组件)
2. [批量导出组件](#批量导出组件)
3. [扫码查询组件](#扫码查询组件)
4. [资产分配组件](#资产分配组件)
5. [维修管理组件](#维修管理组件)
6. [统计报表组件](#统计报表组件)
---
## 1. 批量导入组件
### 组件信息
- **路径**: `src/views/assets/components/BatchImportDialog.vue`
- **名称**: `BatchImportDialog`
- **功能**: 批量导入资产数据
### Props
| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| modelValue | boolean | - | 对话框显示状态 |
### Events
| 事件名 | 参数 | 说明 |
|--------|------|------|
| update:modelValue | (value: boolean) | 显示状态变化 |
| success | - | 导入成功触发 |
### 使用示例
```vue
批量导入
```
### 功能说明
#### 三步导入流程
**步骤1: 上传文件**
- 支持拖拽上传
- 支持 .xlsx 和 .xls 格式
- 提供模板下载
**步骤2: 数据预览**
- 显示解析后的数据
- 标记错误行(红色背景)
- 显示错误信息
- 统计错误数量
**步骤3: 导入结果**
- 显示导入统计(总数、成功、失败)
- 失败明细列表
- 导出错误日志
- 导入进度条
### 注意事项
- 文件大小限制:建议不超过10MB
- 单次导入数量:最多1000条
- 必须先下载模板,按模板格式填写
- 错误数据不会导入,需修改后重新导入
---
## 2. 批量导出组件
### 组件信息
- **路径**: `src/views/assets/components/BatchExportDialog.vue`
- **名称**: `BatchExportDialog`
- **功能**: 批量导出资产数据
### Props
| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| modelValue | boolean | - | 对话框显示状态 |
### Events
| 事件名 | 参数 | 说明 |
|--------|------|------|
| update:modelValue | (value: boolean) | 显示状态变化 |
### 使用示例
```vue
批量导出
```
### 功能说明
#### 导出字段选择
可选择的字段:
- 资产编码(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
查询
```
#### 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
扫码查询
```
### 注意事项
- 摄像头访问需要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
选择资产
```
### 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
新建维修记录
```
---
## 6. 统计报表组件
### 组件信息
- **路径**: `src/views/assets/StatisticsDashboard.vue`
- **名称**: `StatisticsDashboard`
- **功能**: 资产统计和可视化
### 主要功能
#### 1. 统计卡片
```vue
```
卡片类型:
- 资产总数(紫色)
- 在用资产(绿色)
- 维修中(橙色)
- 待报废(红色)
#### 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
统计报表
```
### 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
取消
确定
```
### 表单验证模式
```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
上传文件
```
### 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