# 资产管理系统 - 组件使用文档 ## 目录 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
{{ totalAssets }}
资产总数
``` 卡片类型: - 资产总数(紫色) - 在用资产(绿色) - 维修中(橙色) - 待报废(红色) #### 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