12451 b553c4e519 feat: API 统一 + 设备详情补全 + H5 导航优化
- API 路由统一: 移除 /api/h5/,统一 /api/pms/ 入口
- 二进制改名: api-server -> pms-api(与服务名一致)
- nginx: 合并 /api/ + /pms/api/ 为 /api/pms/
- 新增 docs/API.md 接口文档
- 后端: CabinetDetail 增加 rssi/pow_fail/address/soc 等字段
- Redis 管道批量读取仓体实时数据(SOC/电压/电流/功率)
- DB: 所有 TIMESTAMP 列改为 DATETIME(修复类型不匹配)
- 用户管理: 新增创建时间/修改时间列,状态改用 Switch
- 角色管理/用户管理: 操作按钮改为图标
- PC 设备详情: 基本信息补全 RSSI/ICCID/电源/地址
- PC 卡片: 通道统计改用 Lucide 图标,颜色统一
- PC 二维码: canvas 渲染,含编号文字,支持右键下载
- H5: 按 uni-app 规范修正导航(navigateTo 替代 reLaunch)
- H5: 详情页加返回导航条
- H5: 扫码支持摄像头(jsQR)+ ?id= 参数
- H5: 底部 tabBar 在所有页面常显
- H5: 首页统计改为 6 项(设备/通道/空闲/充电/充满/故障)
- H5: 下拉刷新 / 上拉加载
- 产品首页: 添加业务联系
- 文档: 清理过时文件,补充缺失规范章节
2026-07-04 04:03:02 +08:00

4.2 KiB

title, impact, impactDescription, type, tags
title impact impactDescription type tags
Check for Null/Undefined Before Accessing Properties in v-if MEDIUM Accessing properties on null/undefined causes runtime errors and crashes capability
vue3
conditional-rendering
v-if
null-check
defensive-programming

Check for Null/Undefined Before Accessing Properties in v-if

Impact: MEDIUM - Accessing properties on null or undefined objects in v-if conditions causes "Cannot read property of undefined" runtime errors. This commonly occurs when data is loaded asynchronously or when optional object properties are accessed without null checks.

Task Checklist

  • Always check that an object exists before accessing its properties
  • Use optional chaining (?.) in Vue 3 templates for cleaner null checks
  • Consider using computed properties for complex conditional logic
  • Handle loading states explicitly rather than relying on undefined checks

Incorrect:

<!-- WRONG: Accessing property before checking object exists -->
<template>
  <div v-if="user.isAdmin">
    Admin Panel
  </div>
  <!-- Error if user is null/undefined: Cannot read property 'isAdmin' of undefined -->
</template>
<!-- WRONG: Nested property access without checks -->
<template>
  <div v-if="order.customer.address.city === 'NYC'">
    Local delivery available
  </div>
  <!-- Error if any level is undefined -->
</template>
<!-- WRONG: Array access without existence check -->
<template>
  <div v-if="items[0].name === 'Featured'">
    {{ items[0].description }}
  </div>
  <!-- Error if items is empty array -->
</template>

Correct:

<!-- CORRECT: Check object exists first with && -->
<template>
  <div v-if="user && user.isAdmin">
    Admin Panel
  </div>
</template>
<!-- CORRECT: Optional chaining (Vue 3) -->
<template>
  <div v-if="user?.isAdmin">
    Admin Panel
  </div>
</template>
<!-- CORRECT: Optional chaining for nested properties -->
<template>
  <div v-if="order?.customer?.address?.city === 'NYC'">
    Local delivery available
  </div>
</template>
<!-- CORRECT: Array length check before access -->
<template>
  <div v-if="items.length > 0 && items[0].name === 'Featured'">
    {{ items[0].description }}
  </div>

  <!-- Or with optional chaining -->
  <div v-if="items?.[0]?.name === 'Featured'">
    {{ items[0].description }}
  </div>
</template>
<!-- CORRECT: Explicit loading state handling -->
<template>
  <div v-if="isLoading">Loading...</div>
  <div v-else-if="error">Error: {{ error.message }}</div>
  <div v-else-if="user">
    <h1>Welcome, {{ user.name }}</h1>
    <div v-if="user.isAdmin">Admin Panel</div>
  </div>
  <div v-else>No user data</div>
</template>

Using Computed Properties for Complex Checks

// CORRECT: Move complex checks to computed properties
<script setup>
import { computed } from 'vue'

const props = defineProps(['user', 'permissions'])

const canAccessAdmin = computed(() => {
  return props.user?.isAdmin &&
         props.permissions?.includes('admin_panel') &&
         !props.user?.isDisabled
})

const userDisplayName = computed(() => {
  return props.user?.profile?.displayName ||
         props.user?.name ||
         'Anonymous'
})
</script>

<template>
  <div v-if="canAccessAdmin">
    Admin Panel
  </div>
  <span>{{ userDisplayName }}</span>
</template>

Common Async Data Pattern

// CORRECT: Handle async data loading properly
<script setup>
import { ref, onMounted } from 'vue'

const user = ref(null)
const isLoading = ref(true)
const error = ref(null)

onMounted(async () => {
  try {
    user.value = await fetchUser()
  } catch (e) {
    error.value = e
  } finally {
    isLoading.value = false
  }
})
</script>

<template>
  <div v-if="isLoading">Loading user...</div>
  <div v-else-if="error">Failed to load user</div>
  <div v-else-if="user">
    <!-- Safe to access user properties here -->
    <h1>{{ user.name }}</h1>
    <p v-if="user.bio">{{ user.bio }}</p>
  </div>
</template>

Reference