- 修复路由守卫:未登录时直接跳转,不显示提示信息 - 修复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>
16 KiB
16 KiB
资产管理系统 - 组件使用文档
目录
1. 批量导入组件
组件信息
- 路径:
src/views/assets/components/BatchImportDialog.vue - 名称:
BatchImportDialog - 功能: 批量导入资产数据
Props
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| modelValue | boolean | - | 对话框显示状态 |
Events
| 事件名 | 参数 | 说明 |
|---|---|---|
| update:modelValue | (value: boolean) | 显示状态变化 |
| success | - | 导入成功触发 |
使用示例
<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) | 显示状态变化 |
使用示例
<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. 相机扫码
// 启动相机
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. 手动输入
<el-input
v-model="inputCode"
placeholder="请输入资产编码"
@keyup.enter="handleManualSearch"
>
<template #append>
<el-button @click="handleManualSearch">查询</el-button>
</template>
</el-input>
3. 扫码历史
- 保存在 localStorage
- 最多保存20条
- 点击历史记录可快速查询
4. 扫码音效
// 使用Web Audio API
const playBeep = () => {
const audioContext = new AudioContext()
const oscillator = audioContext.createOscillator()
oscillator.frequency.value = 800
oscillator.start()
setTimeout(() => oscillator.stop(), 100)
}
使用示例
<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 | - | 操作成功触发 |
表单字段
{
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[]) | 确认选择 |
使用示例
<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
- 基本信息 - 分配单基本信息
- 资产明细 - 分配的资产列表
- 审批流程 - 审批历史时间轴
操作功能
- 审批(通过/拒绝)
- 执行(开始/完成)
- 查看审批历史
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 | - | 操作成功触发 |
表单字段
{
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: [] // 维修照片
}
使用示例
<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. 统计卡片
<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: 资产状态分布(饼图)
const statusPieOption = {
series: [{
type: 'pie',
radius: ['40%', '70%'], // 环形
data: [
{ value: 735, name: '在用' },
{ value: 580, name: '在库' },
{ value: 484, name: '维修中' },
{ value: 300, name: '待报废' }
]
}]
}
图表2: 资产类型分布(柱状图)
const typeBarOption = {
xAxis: { data: ['计算机', '打印机', '复印机', ...] },
series: [{
type: 'bar',
data: [326, 208, 156, ...]
}]
}
图表3: 资产价值趋势(折线图)
const valueTrendOption = {
xAxis: { data: ['1月', '2月', '3月', ...] },
yAxis: [
{ type: 'value', name: '数量' },
{ type: 'value', name: '价值(万元)' }
],
series: [
{ name: '资产数量', type: 'line' },
{ name: '资产价值', type: 'line', yAxisIndex: 1 }
]
}
图表4: 机构资产分布(树图)
const orgDistributionOption = {
series: [{
type: 'tree',
data: [
{
name: '广东省',
children: [
{ name: '广州市', children: [...] },
{ name: '深圳市', children: [...] }
]
}
]
}]
}
图表5: 维修统计(堆叠柱状图)
const maintenanceOption = {
series: [
{ name: '硬件故障', type: 'bar', stack: 'total' },
{ name: '软件故障', type: 'bar', stack: 'total' },
{ name: '其他', type: 'bar', stack: 'total' }
]
}
使用示例
<template>
<router-link to="/assets/statistics">
<el-button>统计报表</el-button>
</router-link>
</template>
ECharts按需引入
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
])
通用组件模式
对话框组件模式
所有对话框组件遵循统一的模式:
<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>
表单验证模式
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调用模式
const fetchData = async () => {
loading.value = true
try {
const data = await apiFunction(params)
// 处理数据
} catch (error) {
ElMessage.error('操作失败')
} finally {
loading.value = false
}
}
样式规范
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;
响应式断点
// 屏幕断点
$sm: 768px;
$md: 992px;
$lg: 1200px;
$xl: 1920px;
@media (max-width: $sm) {
// 小屏幕样式
}
常见问题
Q: 如何自定义表单验证?
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: 如何处理文件上传?
<el-upload
action="/api/upload"
:on-success="handleSuccess"
:on-error="handleError"
:before-upload="beforeUpload"
>
<el-button>上传文件</el-button>
</el-upload>
Q: 如何实现分页?
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