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.4 Vue 官方路由管理
Vite ^5.0.0 极速构建工具
HarmonyOS API 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.html 0.63 KB 0.47 KB HTML 入口
index.css 0.21 KB 0.19 KB 全局样式
RetailView.css 10.33 KB 2.30 KB 组件样式
RetailView.js 31.64 KB 10.10 KB 业务逻辑
index.js 91.45 KB 35.85 KB Vue + Router
总计 134.26 KB 48.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 说明
热销品 50 500 高周转商品
常规品 20 200 正常周转
滞销品 5 50 低周转商品
季节性 按需 按需 根据季节调整

十二、总结

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

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

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


十三、参考资料


Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐