Vue3 + TypeScript 零售库存管理系统 - 扫码、库存追踪、报表完整实现

欢迎加入开源鸿蒙PC社区:
https://harmonypc.csdn.net/

项目 Git 仓库:
https://atomgit.com/liboqian/harmonyOs_shop

基于 Vue3 Composition API + TypeScript 构建的零售库存桌面端后台管理系统,支持商品管理、出入库操作、供应商管理、库存预警、统计报表等核心功能,采用 localStorage 实现数据持久化,可打包为 HarmonyOS 应用。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一、项目背景

零售行业的库存管理是企业运营的核心环节。传统的手工记账方式效率低下、容易出错,无法满足现代零售企业的需求。

1.1 业务痛点

  • 库存数据不准确,账面库存与实际库存差异大
  • 商品出入库记录混乱,追溯困难
  • 缺货和积压现象频发,影响销售和资金周转
  • 供应商管理分散,采购决策缺乏数据支撑
  • 销售统计滞后,无法实时掌握经营状况

1.2 系统功能

模块核心功能说明
商品管理增删改查、搜索、分类筛选支持 SKU、条形码、多标签管理
出入库记录入库、出库、退货、盘点调整完整记录库存变动历史
库存预警缺货预警、低库存提醒、积压报警自动检测库存状态
供应商管理供应商信息、合作关系维护关联商品与供应商
统计报表分类库存、状态分布、低库存排行多维度数据分析

1.3 技术特点

  • 响应式 UI:清晰的表格与卡片布局,数据一目了然
  • 库存状态自动判定:根据最低/最高库存自动标识状态
  • 完整出入库追踪:每次变动都记录操作人、原因、时间
  • 实时预警提醒:顶部预警栏实时展示库存异常
  • 数据持久化:localStorage 存储,刷新不丢失
  • HarmonyOS 兼容:可打包为鸿蒙应用部署

二、技术栈

2.1 核心技术

技术版本说明
Vue3^3.4.0前端框架,使用 Composition API
TypeScript^5.3.0类型安全的 JavaScript 超集
Vue Router^4.6.4Vue 官方路由管理
Vite^5.0.0极速构建工具
HarmonyOSAPI 11+鸿蒙操作系统

2.2 项目配置

{
  "name": "retail-inventory-system",
  "version": "1.0.0",
  "description": "零售库存管理系统 - 扫码、库存追踪、报表",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  }
}

💡 Vite 5 相比 Webpack 有 10-100 倍的构建速度提升,非常适合快速迭代开发。


三、项目架构

3.1 目录结构

vue-app/
├── src/
│   ├── types/
│   │   └── retail.ts              # 类型定义
│   ├── services/
│   │   └── RetailService.ts       # 业务逻辑层
│   ├── components/
│   │   └── RetailPanel.vue        # 主组件
│   ├── views/
│   │   └── RetailView.vue         # 视图组件
│   ├── router/
│   │   └── index.ts               # 路由配置
│   ├── styles/
│   │   └── global.css             # 全局样式
│   ├── App.vue                    # 根组件
│   └── main.ts                    # 入口文件
├── index.html                     # HTML 模板
├── package.json                   # 项目配置
├── vite.config.ts                 # Vite 配置
└── tsconfig.json                  # TypeScript 配置

3.2 架构分层

┌─────────────────────────────────────────────┐
│                  View 层                     │
│              RetailView.vue                  │
├─────────────────────────────────────────────┤
│                Component 层                  │
│             RetailPanel.vue                  │
│  ┌──────────┬──────────┬────────┬────────┐  │
│  │ 商品管理 │ 出入库记录│ 供应商 │ 统计报表│  │
│  └──────────┴──────────┴────────┴────────┘  │
├─────────────────────────────────────────────┤
│                Service 层                    │
│             RetailService.ts                │
│  ┌──────────┬──────────┬────────┬────────┐  │
│  │ 商品管理 │ 出入库管理│ 供应商 │ 报表统计│  │
│  └──────────┴──────────┴────────┴────────┘  │
├─────────────────────────────────────────────┤
│                Type 层                       │
│                retail.ts                    │
│  Product | StockMovement | Supplier | Alert │
├─────────────────────────────────────────────┤
│              数据存储层                       │
│              localStorage                    │
└─────────────────────────────────────────────┘

四、TypeScript 类型定义

4.1 枚举类型

// 商品分类
export type ProductCategory = 
  | 'electronics'  // 电子产品
  | 'clothing'     // 服装鞋帽
  | 'food'         // 食品饮料
  | 'home'         // 家居用品
  | 'beauty'       // 美妆护肤
  | 'sports'       // 运动户外
  | 'books'        // 图书文具
  | 'toys'         // 玩具童车

// 库存状态
export type StockStatus = 
  | 'normal'        // 正常
  | 'low'           // 库存不足
  | 'out_of_stock'  // 缺货
  | 'overstock'     // 库存积压

// 出入库类型
export type MovementType = 
  | 'inbound'     // 入库
  | 'outbound'    // 出库
  | 'return'      // 退货
  | 'transfer'    // 调拨
  | 'adjustment'  // 盘点调整

// 供应商状态
export type SupplierStatus = 'active' | 'inactive' | 'suspended'

4.2 核心接口

// 商品接口
export interface Product {
  id: string              // 唯一标识
  name: string            // 商品名称
  sku: string             // SKU 编码
  barcode: string         // 条形码(支持扫码)
  category: ProductCategory  // 分类
  price: number           // 售价
  cost: number            // 成本价
  stockQuantity: number   // 当前库存
  minStock: number        // 最低库存
  maxStock: number        // 最高库存
  supplierId: string      // 供应商ID
  location: string        // 货位(如 A01-01)
  description: string     // 描述
  tags: string[]          // 标签
  createdAt: number       // 创建时间
  updatedAt: number       // 更新时间
}

// 出入库记录接口
export interface StockMovement {
  id: string           // 唯一标识
  productId: string    // 商品ID
  productName: string  // 商品名称
  type: MovementType   // 操作类型
  quantity: number     // 变动数量(正数为入库,负数为出库)
  beforeQuantity: number  // 变动前库存
  afterQuantity: number   // 变动后库存
  reason: string       // 原因
  operator: string     // 操作人
  createdAt: number    // 操作时间
}

// 供应商接口
export interface Supplier {
  id: string
  name: string
  contact: string
  phone: string
  email: string
  address: string
  status: SupplierStatus
  products: number      // 供应商品数
  totalOrders: number   // 合作订单数
  createdAt: number
}

// 库存预警接口
export interface StockAlert {
  id: string
  productId: string
  productName: string
  sku: string
  type: StockStatus     // 预警类型
  currentStock: number  // 当前库存
  threshold: number     // 阈值
  message: string       // 预警消息
  createdAt: number
}

// 仪表盘统计
export interface DashboardStats {
  totalProducts: number      // 商品总数
  totalStockValue: number    // 库存总值
  lowStockCount: number      // 库存不足数
  outOfStockCount: number    // 缺货数
  todayMovements: number     // 今日出入库数
  totalSuppliers: number     // 供应商数
}

4.3 配置常量

// 分类配置
export const CATEGORY_CONFIG: Record<ProductCategory, { label: string; color: string }> = {
  electronics: { label: '电子产品', color: '#3b82f6' },
  clothing: { label: '服装鞋帽', color: '#ec4899' },
  food: { label: '食品饮料', color: '#f59e0b' },
  home: { label: '家居用品', color: '#10b981' },
  beauty: { label: '美妆护肤', color: '#8b5cf6' },
  sports: { label: '运动户外', color: '#06b6d4' },
  books: { label: '图书文具', color: '#6366f1' },
  toys: { label: '玩具童车', color: '#ef4444' },
}

// 库存状态配置
export const STOCK_STATUS_CONFIG: Record<StockStatus, { label: string; color: string; bg: string }> = {
  normal: { label: '正常', color: '#16a34a', bg: '#dcfce7' },
  low: { label: '库存不足', color: '#d97706', bg: '#fef3c7' },
  out_of_stock: { label: '缺货', color: '#dc2626', bg: '#fee2e2' },
  overstock: { label: '库存积压', color: '#2563eb', bg: '#dbeafe' },
}

// 出入库类型配置
export const MOVEMENT_TYPE_CONFIG: Record<MovementType, { label: string; color: string; icon: string }> = {
  inbound: { label: '入库', color: '#16a34a', icon: '📥' },
  outbound: { label: '出库', color: '#ef4444', icon: '📤' },
  return: { label: '退货', color: '#f59e0b', icon: '🔄' },
  transfer: { label: '调拨', color: '#3b82f6', icon: '↔️' },
  adjustment: { label: '盘点调整', color: '#8b5cf6', icon: '📋' },
}

五、服务层实现

5.1 服务类初始化

const STORAGE_KEYS = {
  products: 'retail_products',
  movements: 'retail_movements',
  suppliers: 'retail_suppliers',
  lastUpdateDate: 'retail_last_update_date',
}

class _RetailService {
  private products: Product[] = []
  private movements: StockMovement[] = []
  private suppliers: Supplier[] = []

  constructor() {
    this.loadFromStorage()
    if (this.products.length === 0) {
      this.initDemoData()
    }
  }

  private loadFromStorage(): void {
    try {
      const products = localStorage.getItem(STORAGE_KEYS.products)
      const movements = localStorage.getItem(STORAGE_KEYS.movements)
      const supps = localStorage.getItem(STORAGE_KEYS.suppliers)
      if (products) this.products = JSON.parse(products)
      if (movements) this.movements = JSON.parse(movements)
      if (supps) this.suppliers = JSON.parse(supps)
    } catch {
      this.products = []
      this.movements = []
      this.suppliers = []
    }
  }

  private saveToStorage(): void {
    localStorage.setItem(STORAGE_KEYS.products, JSON.stringify(this.products))
    localStorage.setItem(STORAGE_KEYS.movements, JSON.stringify(this.movements))
    localStorage.setItem(STORAGE_KEYS.suppliers, JSON.stringify(this.suppliers))
  }
}

5.2 商品管理

getProducts(): Product[] {
  return [...this.products]
}

searchProducts(keyword: string): Product[] {
  const lower = keyword.toLowerCase()
  return this.products.filter(p =>
    p.name.toLowerCase().includes(lower) ||
    p.sku.toLowerCase().includes(lower) ||
    p.barcode.includes(lower) ||
    p.description.toLowerCase().includes(lower) ||
    p.tags.some(t => t.toLowerCase().includes(lower))
  )
}

addProduct(product: Omit<Product, 'id' | 'createdAt' | 'updatedAt'>): Product {
  const now = Date.now()
  const newProduct: Product = {
    ...product,
    id: generateId(),
    createdAt: now,
    updatedAt: now,
  }
  this.products.push(newProduct)
  // 自动记录入库
  this.recordMovement(newProduct.id, newProduct.name, 'inbound', 
    product.stockQuantity, 0, product.stockQuantity, '新品入库', '系统')
  this.saveToStorage()
  return newProduct
}

5.3 出入库操作(核心)

出入库操作是库存管理的核心功能,每次操作都会自动记录变动历史:

adjustStock(productId: string, quantity: number, type: MovementType, reason: string, operator: string): boolean {
  const product = this.products.find(p => p.id === productId)
  if (!product) return false

  const beforeQty = product.stockQuantity
  const afterQty = beforeQty + quantity
  if (afterQty < 0) return false  // 不能负库存

  product.stockQuantity = afterQty
  product.updatedAt = Date.now()

  // 记录出入库流水
  this.recordMovement(productId, product.name, type, quantity, beforeQty, afterQty, reason, operator)
  this.saveToStorage()
  return true
}

recordMovement(productId: string, productName: string, type: MovementType, 
    quantity: number, beforeQty: number, afterQty: number, reason: string, operator: string): void {
  this.movements.push({
    id: generateId(),
    productId,
    productName,
    type,
    quantity,
    beforeQuantity: beforeQty,
    afterQuantity: afterQty,
    reason,
    operator,
    createdAt: Date.now(),
  })
}

出入库流程图

操作请求 → 查找商品 → 计算新库存 → 检查合法性 → 更新库存 → 记录流水 → 保存

5.4 库存预警

getStockAlerts(): StockAlert[] {
  const alerts: StockAlert[] = []
  this.products.forEach(product => {
    if (product.stockQuantity === 0) {
      alerts.push({
        id: generateId(),
        productId: product.id,
        productName: product.name,
        sku: product.sku,
        type: 'out_of_stock',
        currentStock: 0,
        threshold: product.minStock,
        message: `${product.name} 已缺货`,
        createdAt: Date.now(),
      })
    } else if (product.stockQuantity < product.minStock) {
      alerts.push({
        type: 'low',
        message: `${product.name} 库存不足,当前 ${product.stockQuantity},最低 ${product.minStock}`,
        // ... 其他字段
      })
    } else if (product.stockQuantity > product.maxStock) {
      alerts.push({
        type: 'overstock',
        message: `${product.name} 库存积压,当前 ${product.stockQuantity},最高 ${product.maxStock}`,
        // ... 其他字段
      })
    }
  })
  return alerts
}

getStockStatus(product: Product): StockStatus {
  if (product.stockQuantity === 0) return 'out_of_stock'
  if (product.stockQuantity < product.minStock) return 'low'
  if (product.stockQuantity > product.maxStock) return 'overstock'
  return 'normal'
}

5.5 统计分析

getStats(): DashboardStats {
  const totalProducts = this.products.length
  const totalStockValue = this.products.reduce((sum, p) => sum + p.cost * p.stockQuantity, 0)
  const lowStockCount = this.products.filter(p => p.stockQuantity > 0 && p.stockQuantity < p.minStock).length
  const outOfStockCount = this.products.filter(p => p.stockQuantity === 0).length

  const today = new Date()
  today.setHours(0, 0, 0, 0)
  const todayMovements = this.movements.filter(m => m.createdAt >= today.getTime()).length

  return { totalProducts, totalStockValue, lowStockCount, outOfStockCount, todayMovements, totalSuppliers: this.suppliers.length }
}

getCategoryStats(): Record<string, { label: string; count: number; value: number; color: string }> {
  const stats = {}
  this.products.forEach(p => {
    if (!stats[p.category]) {
      stats[p.category] = {
        label: CATEGORY_CONFIG[p.category].label,
        count: 0, value: 0, color: CATEGORY_CONFIG[p.category].color,
      }
    }
    stats[p.category].count++
    stats[p.category].value += p.cost * p.stockQuantity
  })
  return stats
}

六、核心功能实现

6.1 商品管理模块

商品列表以表格形式展示,支持分类筛选和多条件搜索。

<div class="products-table">
  <table>
    <thead>
      <tr>
        <th>商品信息</th>
        <th>SKU</th>
        <th>分类</th>
        <th>库存</th>
        <th>成本价</th>
        <th>售价</th>
        <th>库存状态</th>
        <th>货位</th>
        <th>操作</th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="product in filteredProducts" :key="product.id">
        <td class="product-info">
          <div class="product-name">{{ product.name }}</div>
          <div class="product-desc">{{ product.description.slice(0, 20) }}...</div>
        </td>
        <td><code>{{ product.sku }}</code></td>
        <td>
          <span class="tag" :style="{ backgroundColor: CATEGORY_CONFIG[product.category].color + '33' }">
            {{ CATEGORY_CONFIG[product.category].label }}
          </span>
        </td>
        <td>
          <span class="stock-qty" :class="`stock-${getStockStatus(product)}`">
            {{ product.stockQuantity }}
          </span>
        </td>
        <td>¥{{ product.cost }}</td>
        <td class="price">¥{{ product.price }}</td>
        <td>
          <span class="status-badge" :style="{ backgroundColor: STOCK_STATUS_CONFIG[getStockStatus(product)].bg }">
            {{ STOCK_STATUS_CONFIG[getStockStatus(product)].label }}
          </span>
        </td>
        <td>{{ product.location }}</td>
        <td>
          <button class="btn btn-xs btn-success" @click="showStockModal(product)">📦 出入库</button>
          <button class="btn btn-xs btn-danger" @click="deleteProduct(product.id)">🗑️</button>
        </td>
      </tr>
    </tbody>
  </table>
</div>

搜索功能说明

搜索维度字段说明
名称搜索product.name支持模糊匹配
SKU搜索product.sku精确匹配编码
条码搜索product.barcode支持扫码输入
描述搜索product.description描述内容匹配
标签搜索product.tags[]标签数组遍历

6.2 出入库记录

出入库记录展示所有库存变动历史,支持按类型筛选:

<div class="movements-list">
  <div v-for="mv in filteredMovements" :key="mv.id" class="movement-card">
    <div class="movement-header">
      <div>
        <span class="movement-icon">{{ MOVEMENT_TYPE_CONFIG[mv.type].icon }}</span>
        <span class="movement-type">{{ MOVEMENT_TYPE_CONFIG[mv.type].label }}</span>
        <span class="movement-product">{{ mv.productName }}</span>
      </div>
      <div class="movement-qty" :class="mv.quantity > 0 ? 'qty-in' : 'qty-out'">
        {{ mv.quantity > 0 ? '+' : '' }}{{ mv.quantity }}
      </div>
    </div>
    <div class="movement-details">
      <span>原因: {{ mv.reason }}</span>
      <span>操作人: {{ mv.operator }}</span>
      <span>库存: {{ mv.beforeQuantity }} → {{ mv.afterQuantity }}</span>
      <span class="movement-time">{{ formatTime(mv.createdAt) }}</span>
    </div>
  </div>
</div>

出入库类型说明

类型图标数量符号说明
入库📥正数 (+)采购入库、新品入库
出库📤负数 (-)销售出库、领用出库
退货🔄正数 (+)客户退货退库
调拨↔️可正可负仓库间调拨
盘点调整📋可正可负盘点差异调整

6.3 库存状态自动判定

系统根据 minStockmaxStock 自动判定库存状态:

stockQuantity = 0          → 缺货 (out_of_stock)
stockQuantity < minStock   → 库存不足 (low)
minStock ≤ stockQuantity ≤ maxStock → 正常 (normal)
stockQuantity > maxStock   → 库存积压 (overstock)

状态颜色标识

状态背景色文字色说明
正常#dcfce7#16a34a绿色,库存健康
不足#fef3c7#d97706橙色,需要补货
缺货#fee2e2#dc2626红色,紧急状态
积压#dbeafe#2563eb蓝色,需要促销

6.4 统计报表

统计报表提供四个维度的数据可视化:

<div class="reports-grid">
  <!-- 分类库存统计 -->
  <div class="report-section">
    <h3>分类库存统计</h3>
    <div class="category-stat-row" v-for="(data, cat) in categoryStats" :key="cat">
      <span class="cat-dot" :style="{ backgroundColor: data.color }"></span>
      <span class="cat-name">{{ data.label }}</span>
      <span class="cat-count">{{ data.count }}种</span>
      <div class="cat-bar">
        <div class="cat-bar-fill" :style="{ width: getCategoryPercentage(data.value) + '%' }"></div>
      </div>
      <span class="cat-value">¥{{ data.value.toLocaleString() }}</span>
    </div>
  </div>

  <!-- 库存状态分布 -->
  <div class="report-section">
    <h3>库存状态分布</h3>
    <div class="status-item" v-for="(config, status) in STOCK_STATUS_CONFIG" :key="status">
      <span class="status-label">{{ config.label }}</span>
      <span class="status-count">{{ getStockStatusCount(status) }}</span>
      <div class="status-bar">
        <div class="status-bar-fill" :style="{ width: getStockStatusPercentage(status) + '%' }"></div>
      </div>
    </div>
  </div>

  <!-- 低库存预警 TOP10 -->
  <div class="report-section">
    <h3>低库存预警 TOP10</h3>
    <div class="low-stock-row" v-for="(product, index) in lowStockProducts" :key="product.id">
      <span class="rank" :class="{ 'rank-top3': index < 3 }">{{ index + 1 }}</span>
      <span class="product-name">{{ product.name }}</span>
      <span class="stock-num">{{ product.stockQuantity }}</span>
      <span class="min-stock">最低: {{ product.minStock }}</span>
    </div>
  </div>

  <!-- 出入库趋势 -->
  <div class="report-section">
    <h3>出入库趋势</h3>
    <div class="trend-summary">
      <span class="trend-num">{{ totalInbound }}</span>  <!-- 入库总量 -->
      <span class="trend-num">{{ totalOutbound }}</span>  <!-- 出库总量 -->
      <span class="trend-num">{{ totalInbound - totalOutbound }}</span>  <!-- 净变化 -->
    </div>
  </div>
</div>

七、响应式数据管理

7.1 组合式 API

import { ref, computed, onMounted } from 'vue'

const activeTab = ref('products')
const selectedCategory = ref('all')
const selectedMovementType = ref('all')
const searchKeyword = ref('')
const showAddModal = ref(false)
const showStockModalData = ref<Product | null>(null)
const toastMessage = ref('')
const toastType = ref('success')

const products = ref<Product[]>([])
const movements = ref<StockMovement[]>([])
const suppliers = ref<any[]>([])
const alerts = ref<any[]>([])
const stats = ref({ totalProducts: 0, totalStockValue: 0, lowStockCount: 0, outOfStockCount: 0, todayMovements: 0, totalSuppliers: 0 })

7.2 计算属性

const filteredProducts = computed(() => {
  let result = products.value
  if (selectedCategory.value !== 'all') 
    result = result.filter(p => p.category === selectedCategory.value)
  if (searchKeyword.value) {
    const kw = searchKeyword.value.toLowerCase()
    result = result.filter(p =>
      p.name.toLowerCase().includes(kw) || p.sku.toLowerCase().includes(kw) ||
      p.barcode.includes(kw) || p.description.toLowerCase().includes(kw)
    )
  }
  return result
})

const lowStockProducts = computed(() =>
  [...products.value]
    .filter(p => p.stockQuantity <= p.minStock)
    .sort((a, b) => (a.stockQuantity / a.minStock) - (b.stockQuantity / b.minStock))
    .slice(0, 10)
)

const totalInbound = computed(() =>
  movements.value.filter(m => m.quantity > 0).reduce((sum, m) => sum + m.quantity, 0)
)

const totalOutbound = computed(() =>
  movements.value.filter(m => m.quantity < 0).reduce((sum, m) => sum + Math.abs(m.quantity), 0)
)

八、样式设计

8.1 库存数量样式

.stock-qty { font-weight: 600; font-size: 16px; }
.stock-normal { color: #16a34a; }
.stock-low { color: #f59e0b; }
.stock-out_of_stock { color: #dc2626; }
.stock-overstock { color: #2563eb; }

8.2 出入库数量样式

.movement-qty { font-size: 20px; font-weight: 700; }
.qty-in { color: #16a34a; }   /* 绿色表示入库 */
.qty-out { color: #ef4444; }  /* 红色表示出库 */

8.3 预警栏样式

.alert-bar {
  background: #fef3c7;
  border-left: 4px solid #f59e0b;
  border-radius: 8px;
  padding: 15px;
  margin-bottom: 20px;
}

.alert-out_of_stock { color: #dc2626; font-weight: 500; }
.alert-low { color: #d97706; }
.alert-overstock { color: #2563eb; }

九、构建与部署

9.1 构建命令

# 清理缓存
Remove-Item -Recurse -Force "dist" -ErrorAction SilentlyContinue
Remove-Item -Recurse -Force ".hvigor" -ErrorAction SilentlyContinue

# 构建
npm run build

9.2 构建输出

✓ 37 modules transformed.
../dist/index.html                       0.63 kB │ gzip:  0.47 kB
../dist/assets/index-CBgsX6DZ.css        0.21 kB │ gzip:  0.19 kB
../dist/assets/RetailView-CYjZWCAP.css  10.33 kB │ gzip:  2.30 kB
../dist/assets/RetailView-BG0ml3g4.js   31.64 kB │ gzip: 10.10 kB
../dist/assets/index-QA81rV61.js        91.45 kB │ gzip: 35.85 kB
✓ built in 633ms

9.3 产物分析

文件大小Gzip 后说明
index.html0.63 KB0.47 KBHTML 入口
index.css0.21 KB0.19 KB全局样式
RetailView.css10.33 KB2.30 KB组件样式
RetailView.js31.64 KB10.10 KB业务逻辑
index.js91.45 KB35.85 KBVue + Router
总计134.26 KB48.91 KB-

十、核心亮点

10.1 出入库流水追踪

每次库存变动都会记录完整的流水信息:

interface StockMovement {
  productId: string        // 哪个商品
  type: MovementType       // 什么操作
  quantity: number         // 变动多少
  beforeQuantity: number   // 变动前是多少
  afterQuantity: number    // 变动后是多少
  reason: string           // 为什么变动
  operator: string         // 谁操作的
  createdAt: number        // 什么时候变的
}

设计优势

  • 完整追溯:可以追溯任意时间点的库存状态
  • 责任到人:每次操作都记录操作人
  • 原因明确:每次变动都有原因说明
  • 数据分析:支持按类型、时间、操作人等多维度分析

10.2 库存状态自动判定

系统自动根据阈值判定库存状态:

getStockStatus(product: Product): StockStatus {
  if (product.stockQuantity === 0) return 'out_of_stock'
  if (product.stockQuantity < product.minStock) return 'low'
  if (product.stockQuantity > product.maxStock) return 'overstock'
  return 'normal'
}

💡 当商品 minStock = 10maxStock = 100 时:库存为 5 显示"不足",库存为 0 显示"缺货",库存为 150 显示"积压"。

10.3 预警自动刷新

每次数据刷新时自动检测库存状态并生成预警:

function refreshData(): void {
  products.value = RetailService.getProducts()
  movements.value = RetailService.getMovements()
  suppliers.value = RetailService.getSuppliers()
  alerts.value = RetailService.getStockAlerts()   // 自动检测
  stats.value = RetailService.getStats()
}

十一、常见问题解答

11.1 如何实现扫码入库?

searchProducts 方法与扫描输入绑定即可:

// 扫描枪输入条形码后自动搜索
function onBarcodeScanned(barcode: string): void {
  const products = RetailService.searchProducts(barcode)
  if (products.length === 1) {
    showStockModal(products[0])
  }
}

11.2 如何对接真实后端?

修改 RetailService 中的方法,将 localStorage 操作替换为 API 调用:

async adjustStock(productId: string, quantity: number, type: string, reason: string, operator: string): Promise<boolean> {
  await fetch(`/api/stock/adjust`, {
    method: 'POST',
    body: JSON.stringify({ productId, quantity, type, reason, operator })
  })
  // 刷新数据
  return true
}

11.3 如何设置库存阈值?

在添加或编辑商品时设置 minStockmaxStock

商品类型建议 minStock建议 maxStock说明
热销品50500高周转商品
常规品20200正常周转
滞销品550低周转商品
季节性按需按需根据季节调整

十二、总结

本项目基于 Vue3 + TypeScript 实现了一个功能完整的零售库存后台管理系统,主要特点包括:

  1. 完整的商品管理:支持 SKU、条形码、多标签、货位管理
  2. 出入库全追踪:入库、出库、退货、调拨、盘点五种操作类型
  3. 库存状态自动判定:根据阈值自动标识正常/不足/缺货/积压
  4. 实时预警提醒:顶部预警栏实时展示库存异常商品
  5. 供应商管理:关联商品与供应商,统计合作数据
  6. 统计报表:分类库存、状态分布、低库存排行、出入库趋势
  7. 数据导出:支持 JSON 和 CSV 格式导出
  8. 类型安全:完整的 TypeScript 类型定义
  9. 响应式设计:Vue3 Composition API,逻辑清晰可维护

项目代码结构清晰,遵循分层架构设计,可直接打包为 HarmonyOS 应用部署到鸿蒙设备。


十三、参考资料


Logo

赋能鸿蒙PC开发者,共建全场景原生生态,共享一次开发多端部署创新价值。

更多推荐