Files
zcglxt/COMPONENT_USAGE_GUIDE.md
Claude e48975f9d5 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>
2026-01-25 00:26:33 +08:00

16 KiB
Raw Blame History

资产管理系统 - 组件使用文档

目录

  1. 批量导入组件
  2. 批量导出组件
  3. 扫码查询组件
  4. 资产分配组件
  5. 维修管理组件
  6. 统计报表组件

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

  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 - 操作成功触发

表单字段

{
  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