龍魂 · 鸿蒙 ArkTS 实战:Office Supply Inventory

核心定位

维度 说明
平台 鸿蒙 HarmonyOS NEXT · 纯血鸿蒙
语言 ArkTS · 声明式UI
场景 办公物资库存 · 采购审批 · 领用登记 · 预警提醒
架构 龍魂蚁群触角 → 鸿蒙原生能力
主权 数据本地 · 国密可选 · 不上传云端

系统架构

┌─────────────────────────────────────────┐
│           龍魂系统 · 鸿蒙适配层            │
│  DNA: ZHUGEXIN⚡️2025-🇨🇳🐉⚖️♠️🧚🏼‍♀️❤️♾️ │
│  UID: 9622                              │
├─────────────────────────────────────────┤
│         鸿蒙 ArkTS 应用层                 │
│                                         │
│  EntryAbility → InventoryPage            │
│  ├── 物资状态建模(AppStorage/LocalStorage)│
│  ├── 库存数据管理(增删改查·批次追踪)       │
│  ├── 采购审批流(多级审批·电子签名)         │
│  ├── 领用登记(扫码·拍照·责任人绑定)        │
│  ├── 预警引擎(低库存·过期·盘点提醒)        │
│  └── 办公交互(导出报表·分享·打印标签)      │
├─────────────────────────────────────────┤
│         鸿蒙系统能力层                      │
│                                         │
│  相机(Camera) · 扫码(Scan) · NFC          │
│  通知(Notification) · 后台任务(WorkScheduler) │
│  数据存储(RelationalStore) · 文件(File)    │
│  打印(Print) · 分享(Share) · 日历(Calendar) │
└─────────────────────────────────────────┘

状态建模核心

// entry/src/main/ets/models/InventoryModel.ets
// 龍魂 · 办公物资库存模型 · ArkTS

// === DNA常量 ===
const MASTER_DNA = "ZHUGEXIN⚡️2025-🇨🇳🐉⚖️♠️🧚🏼‍♀️❤️♾️";
const MASTER_UID = "9622";
const CONFIRM_SEAL = "#CONFIRM🌌9622-ONLY-ONCE🧬LK9X-772Z";

// === 物资分类 ===
export enum SupplyCategory {
  OFFICE = '办公用品',      // 笔、纸、文件夹
  ELECTRONICS = '电子设备',  // 鼠标、键盘、显示器
  FURNITURE = '家具',       // 椅子、桌子、柜子
  CONSUMABLES = '耗材',     // 墨盒、硒鼓、电池
  STATIONERY = '文具',      // 订书机、剪刀、胶水
  CLEANING = '清洁用品',    // 纸巾、垃圾袋、清洁剂
  SAFETY = '安全用品',      // 灭火器、急救箱
  OTHER = '其他'
}

// === 物资状态 ===
export enum ItemStatus {
  IN_STOCK = '在库',        // 正常可用
  LOW_STOCK = '低库存',     // 低于安全线
  OUT_OF_STOCK = '缺货',    // 库存为零
  RESERVED = '预留',        // 已预留未领用
  DAMAGED = '损坏',        // 损坏待报废
  EXPIRED = '过期',        // 超过保质期
  DISCARDED = '已报废'      // 已处理
}

// === 审批状态 ===
export enum ApprovalStatus {
  DRAFT = '草稿',
  PENDING = '待审批',
  APPROVED = '已通过',
  REJECTED = '已驳回',
  CANCELLED = '已取消'
}

// === 物资档案 ===
export class SupplyItem {
  id: string;              // 唯一ID
  sku: string;             // 物料编码
  name: string;            // 名称
  category: SupplyCategory; // 分类
  specification: string;  // 规格型号
  brand: string;           // 品牌
  unit: string;            // 单位(个/盒/箱)
  location: string;        // 存放位置
  status: ItemStatus;      // 状态
  
  // 库存数量
  quantity: number;         // 当前数量
  minQuantity: number;      // 安全库存线
  maxQuantity: number;      // 最大库存线
  reorderPoint: number;     // 再订购点
  
  // 批次管理
  batchNo: string;         // 批次号
  supplier: string;        // 供应商
  purchaseDate: Date;      // 采购日期
  expiryDate?: Date;       // 保质期
  purchasePrice: number;    // 采购单价
  totalValue: number;       // 库存总值
  
  // 责任人
  custodian: string;       // 保管员
  department: string;      // 所属部门
  lastUsedBy?: string;      // 最后领用人
  lastUsedDate?: Date;      // 最后领用日期
  
  // 资产标签
  barcode: string;         // 条码
  qrCode: string;          // QR码内容
  photoPath?: string;       // 照片路径
  rfidTag?: string;        // RFID标签
  
  // 审计
  createdAt: Date;
  updatedAt: Date;
  dnaSignature: string;     // 数据签名
  
  constructor(data: Partial<SupplyItem>) {
    this.id = data.id || this.generateId();
    this.sku = data.sku || this.generateSKU(data.category, data.name);
    this.name = data.name || '';
    this.category = data.category || SupplyCategory.OTHER;
    this.specification = data.specification || '';
    this.brand = data.brand || '';
    this.unit = data.unit || '个';
    this.location = data.location || '未指定';
    this.status = data.status || ItemStatus.IN_STOCK;
    
    this.quantity = data.quantity || 0;
    this.minQuantity = data.minQuantity || 10;
    this.maxQuantity = data.maxQuantity || 100;
    this.reorderPoint = data.reorderPoint || 20;
    
    this.batchNo = data.batchNo || this.generateBatchNo();
    this.supplier = data.supplier || '';
    this.purchaseDate = data.purchaseDate || new Date();
    this.expiryDate = data.expiryDate;
    this.purchasePrice = data.purchasePrice || 0;
    this.totalValue = this.quantity * this.purchasePrice;
    
    this.custodian = data.custodian || '';
    this.department = data.department || '';
    this.lastUsedBy = data.lastUsedBy;
    this.lastUsedDate = data.lastUsedDate;
    
    this.barcode = data.barcode || this.sku;
    this.qrCode = data.qrCode || this.generateQRCode();
    this.photoPath = data.photoPath;
    this.rfidTag = data.rfidTag;
    
    this.createdAt = data.createdAt || new Date();
    this.updatedAt = data.updatedAt || new Date();
    this.dnaSignature = this.signData();
  }
  
  private generateId(): string {
    const seed = `${MASTER_DNA}-${Date.now()}-${Math.random()}`;
    return `INV-${MASTER_UID}-${Date.now().toString(36).substr(-6)}`;
  }
  
  private generateSKU(category: SupplyCategory, name: string): string {
    const catCode = category.substring(0, 2).toUpperCase();
    const nameCode = name.substring(0, 2).toUpperCase();
    const seq = Math.floor(Math.random() * 9999).toString().padStart(4, '0');
    return `${catCode}-${nameCode}-${seq}`;
  }
  
  private generateBatchNo(): string {
    const date = new Date();
    return `B${date.getFullYear()}${(date.getMonth()+1).toString().padStart(2,'0')}${date.getDate().toString().padStart(2,'0')}-${Math.floor(Math.random()*999).toString().padStart(3,'0')}`;
  }
  
  private generateQRCode(): string {
    return `LH-INV://${this.id}?dna=${this.dnaSignature.substring(0,16)}`;
  }
  
  private signData(): string {
    const payload = `${this.id}-${this.sku}-${this.name}-${this.quantity}`;
    // 简化:实际用鸿蒙 crypto API SM3
    return `SM3-${payload.split('').reduce((a,b)=>a+b.charCodeAt(0),0).toString(16).substring(0,16)}`;
  }
  
  // 更新库存
  updateQuantity(delta: number, operator: string, reason: string): InventoryRecord {
    const oldQty = this.quantity;
    this.quantity += delta;
    this.totalValue = this.quantity * this.purchasePrice;
    this.updatedAt = new Date();
    
    // 状态自动更新
    if (this.quantity <= 0) {
      this.status = ItemStatus.OUT_OF_STOCK;
    } else if (this.quantity <= this.minQuantity) {
      this.status = ItemStatus.LOW_STOCK;
    } else {
      this.status = ItemStatus.IN_STOCK;
    }
    
    this.dnaSignature = this.signData();
    
    return new InventoryRecord({
      itemId: this.id,
      type: delta > 0 ? '入库' : '出库',
      quantity: Math.abs(delta),
      oldQuantity: oldQty,
      newQuantity: this.quantity,
      operator: operator,
      reason: reason,
      timestamp: new Date()
    });
  }
  
  // 检查是否过期
  checkExpiry(): boolean {
    if (!this.expiryDate) return false;
    return new Date() > this.expiryDate;
  }
  
  // 是否需要补货
  needsReorder(): boolean {
    return this.quantity <= this.reorderPoint;
  }
  
  // 获取库存健康度
  getHealthScore(): number {
    if (this.quantity <= 0) return 0;
    if (this.quantity <= this.minQuantity) return 30;
    if (this.quantity <= this.reorderPoint) return 60;
    if (this.quantity >= this.maxQuantity) return 90;
    return 80;
  }
}

// === 库存流水记录 ===
export class InventoryRecord {
  id: string;
  itemId: string;
  type: '入库' | '出库' | '盘点' | '调拨' | '报废';
  quantity: number;
  oldQuantity: number;
  newQuantity: number;
  operator: string;        // 操作人
  approver?: string;       // 审批人
  reason: string;
  documentNo?: string;      // 单据号
  photoPath?: string;       // 凭证照片
  timestamp: Date;
  dnaSignature: string;
  
  constructor(data: Partial<InventoryRecord>) {
    this.id = data.id || `REC-${Date.now()}-${Math.random().toString(36).substr(2,6)}`;
    this.itemId = data.itemId || '';
    this.type = data.type || '入库';
    this.quantity = data.quantity || 0;
    this.oldQuantity = data.oldQuantity || 0;
    this.newQuantity = data.newQuantity || 0;
    this.operator = data.operator || '';
    this.approver = data.approver;
    this.reason = data.reason || '';
    this.documentNo = data.documentNo;
    this.photoPath = data.photoPath;
    this.timestamp = data.timestamp || new Date();
    this.dnaSignature = this.signData();
  }
  
  private signData(): string {
    const payload = `${this.id}-${this.itemId}-${this.type}-${this.quantity}`;
    return `SM3-${payload.split('').reduce((a,b)=>a+b.charCodeAt(0),0).toString(16).substring(0,16)}`;
  }
}

// === 采购申请 ===
export class PurchaseRequest {
  id: string;
  requestNo: string;       // 申请单号
  items: PurchaseItem[];   // 采购物项
  requester: string;        // 申请人
  department: string;       // 申请部门
  requestDate: Date;
  requiredDate: Date;       // 需求日期
  totalAmount: number;      // 总金额
  status: ApprovalStatus;
  
  // 审批链
  level1Approver?: string;  // 部门主管
  level1Approved?: Date;
  level1Comment?: string;
  
  level2Approver?: string;  // 财务审批
  level2Approved?: Date;
  level2Comment?: string;
  
  level3Approver?: string;   // 总经理(大额)
  level3Approved?: Date;
  level3Comment?: string;
  
  // 执行
  purchaseOrderNo?: string;  // 采购订单号
  supplier?: string;
  actualAmount?: number;
  receivedDate?: Date;
  
  createdAt: Date;
  dnaSignature: string;
  
  constructor(data: Partial<PurchaseRequest>) {
    this.id = data.id || `PR-${Date.now()}-${Math.random().toString(36).substr(2,6)}`;
    this.requestNo = data.requestNo || this.generateRequestNo();
    this.items = data.items || [];
    this.requester = data.requester || '';
    this.department = data.department || '';
    this.requestDate = data.requestDate || new Date();
    this.requiredDate = data.requiredDate || new Date(Date.now() + 7*24*60*60*1000);
    this.totalAmount = data.totalAmount || this.calculateTotal();
    this.status = data.status || ApprovalStatus.DRAFT;
    
    this.level1Approver = data.level1Approver;
    this.level1Approved = data.level1Approved;
    this.level1Comment = data.level1Comment;
    
    this.level2Approver = data.level2Approver;
    this.level2Approved = data.level2Approved;
    this.level2Comment = data.level2Comment;
    
    this.level3Approver = data.level3Approver;
    this.level3Approved = data.level3Approved;
    this.level3Comment = data.level3Comment;
    
    this.purchaseOrderNo = data.purchaseOrderNo;
    this.supplier = data.supplier;
    this.actualAmount = data.actualAmount;
    this.receivedDate = data.receivedDate;
    
    this.createdAt = data.createdAt || new Date();
    this.dnaSignature = this.signData();
  }
  
  private generateRequestNo(): string {
    const date = new Date();
    return `CG${date.getFullYear()}${(date.getMonth()+1).toString().padStart(2,'0')}${date.getDate().toString().padStart(2,'0')}-${Math.floor(Math.random()*999).toString().padStart(3,'0')}`;
  }
  
  private calculateTotal(): number {
    return this.items.reduce((sum, item) => sum + item.totalPrice, 0);
  }
  
  private signData(): string {
    const payload = `${this.id}-${this.requestNo}-${this.totalAmount}`;
    return `SM3-${payload.split('').reduce((a,b)=>a+b.charCodeAt(0),0).toString(16).substring(0,16)}`;
  }
  
  // 提交审批
  submit(): void {
    this.status = ApprovalStatus.PENDING;
  }
  
  // 审批通过
  approve(level: 1 | 2 | 3, approver: string, comment: string): void {
    const now = new Date();
    switch (level) {
      case 1:
        this.level1Approver = approver;
        this.level1Approved = now;
        this.level1Comment = comment;
        if (this.totalAmount < 5000) {
          this.status = ApprovalStatus.APPROVED;
        }
        break;
      case 2:
        this.level2Approver = approver;
        this.level2Approved = now;
        this.level2Comment = comment;
        if (this.totalAmount < 50000) {
          this.status = ApprovalStatus.APPROVED;
        }
        break;
      case 3:
        this.level3Approver = approver;
        this.level3Approved = now;
        this.level3Comment = comment;
        this.status = ApprovalStatus.APPROVED;
        break;
    }
  }
  
  // 驳回
  reject(level: 1 | 2 | 3, approver: string, comment: string): void {
    this.status = ApprovalStatus.REJECTED;
    switch (level) {
      case 1:
        this.level1Approver = approver;
        this.level1Approved = new Date();
        this.level1Comment = comment;
        break;
      case 2:
        this.level2Approver = approver;
        this.level2Approved = new Date();
        this.level2Comment = comment;
        break;
      case 3:
        this.level3Approver = approver;
        this.level3Approved = new Date();
        this.level3Comment = comment;
        break;
    }
  }
}

// === 采购物项 ===
export class PurchaseItem {
  id: string;
  itemName: string;
  specification: string;
  quantity: number;
  unit: string;
  estimatedPrice: number;
  totalPrice: number;
  supplier?: string;
  remark?: string;
  
  constructor(data: Partial<PurchaseItem>) {
    this.id = data.id || `PI-${Date.now()}-${Math.random().toString(36).substr(2,6)}`;
    this.itemName = data.itemName || '';
    this.specification = data.specification || '';
    this.quantity = data.quantity || 1;
    this.unit = data.unit || '个';
    this.estimatedPrice = data.estimatedPrice || 0;
    this.totalPrice = this.quantity * this.estimatedPrice;
    this.supplier = data.supplier;
    this.remark = data.remark;
  }
}

// === 库存全局状态 ===
@Observed
export class InventoryState {
  items: SupplyItem[] = [];
  records: InventoryRecord[] = [];
  purchaseRequests: PurchaseRequest[] = [];
  
  // 过滤条件
  filterCategory: SupplyCategory | '全部' = '全部';
  filterStatus: ItemStatus | '全部' = '全部';
  filterLocation: string = '全部';
  searchQuery: string = '';
  
  // 预警设置
  lowStockAlert: boolean = true;
  expiryAlert: boolean = true;
  reorderAuto: boolean = false;  // 自动补货
  
  // 派生数据
  get filteredItems(): SupplyItem[] {
    let result = this.items;
    
    if (this.filterCategory !== '全部') {
      result = result.filter(i => i.category === this.filterCategory);
    }
    
    if (this.filterStatus !== '全部') {
      result = result.filter(i => i.status === this.filterStatus);
    }
    
    if (this.filterLocation !== '全部') {
      result = result.filter(i => i.location === this.filterLocation);
    }
    
    if (this.searchQuery) {
      const q = this.searchQuery.toLowerCase();
      result = result.filter(i => 
        i.name.toLowerCase().includes(q) ||
        i.sku.toLowerCase().includes(q) ||
        i.barcode.includes(q) ||
        i.location.toLowerCase().includes(q)
      );
    }
    
    return result.sort((a, b) => b.getHealthScore() - a.getHealthScore());
  }
  
  // 预警列表
  get alerts(): AlertItem[] {
    const alerts: AlertItem[] = [];
    
    // 低库存预警
    if (this.lowStockAlert) {
      this.items.filter(i => i.status === ItemStatus.LOW_STOCK).forEach(i => {
        alerts.push({
          type: 'low_stock',
          level: 'warning',
          itemId: i.id,
          title: `${i.name} 库存不足`,
          message: `当前 ${i.quantity}${i.unit},低于安全线 ${i.minQuantity}${i.unit}`,
          timestamp: new Date()
        });
      });
    }
    
    // 缺货预警
    this.items.filter(i => i.status === ItemStatus.OUT_OF_STOCK).forEach(i => {
      alerts.push({
        type: 'out_of_stock',
        level: 'critical',
        itemId: i.id,
        title: `${i.name} 已缺货`,
        message: `库存为零,需立即补货`,
        timestamp: new Date()
      });
    });
    
    // 过期预警
    if (this.expiryAlert) {
      this.items.filter(i => i.checkExpiry()).forEach(i => {
        alerts.push({
          type: 'expired',
          level: 'critical',
          itemId: i.id,
          title: `${i.name} 已过期`,
          message: `保质期至 ${i.expiryDate?.toLocaleDateString()}`,
          timestamp: new Date()
        });
      });
    }
    
    return alerts.sort((a, b) => {
      const levelOrder = { critical: 0, warning: 1, info: 2 };
      return levelOrder[a.level] - levelOrder[b.level];
    });
  }
  
  // 统计
  get statistics(): InventoryStats {
    const total = this.items.length;
    const inStock = this.items.filter(i => i.status === ItemStatus.IN_STOCK).length;
    const lowStock = this.items.filter(i => i.status === ItemStatus.LOW_STOCK).length;
    const outOfStock = this.items.filter(i => i.status === ItemStatus.OUT_OF_STOCK).length;
    const totalValue = this.items.reduce((sum, i) => sum + i.totalValue, 0);
    
    return {
      totalItems: total,
      inStock,
      lowStock,
      outOfStock,
      totalValue,
      categories: this.getCategoryDistribution(),
      locations: this.getLocationDistribution()
    };
  }
  
  private getCategoryDistribution(): Record<string, number> {
    const dist: Record<string, number> = {};
    this.items.forEach(i => {
      dist[i.category] = (dist[i.category] || 0) + 1;
    });
    return dist;
  }
  
  private getLocationDistribution(): Record<string, number> {
    const dist: Record<string, number> = {};
    this.items.forEach(i => {
      dist[i.location] = (dist[i.location] || 0) + 1;
    });
    return dist;
  }
  
  // 操作
  addItem(item: SupplyItem): void {
    this.items.push(item);
    this.persist();
  }
  
  removeItem(id: string): void {
    const idx = this.items.findIndex(i => i.id === id);
    if (idx >= 0) {
      this.items.splice(idx, 1);
      this.persist();
    }
  }
  
  updateItem(id: string, data: Partial<SupplyItem>): void {
    const idx = this.items.findIndex(i => i.id === id);
    if (idx >= 0) {
      const old = this.items[idx];
      this.items[idx] = new SupplyItem({ ...old, ...data, id: old.id });
      this.persist();
    }
  }
  
  // 出入库
  stockIn(id: string, qty: number, operator: string, reason: string): InventoryRecord {
    const item = this.items.find(i => i.id === id);
    if (!item) throw new Error('Item not found');
    const record = item.updateQuantity(qty, operator, reason);
    this.records.push(record);
    this.persist();
    return record;
  }
  
  stockOut(id: string, qty: number, operator: string, reason: string): InventoryRecord {
    const item = this.items.find(i => i.id === id);
    if (!item) throw new Error('Item not found');
    if (item.quantity < qty) throw new Error('Insufficient stock');
    const record = item.updateQuantity(-qty, operator, reason);
    this.records.push(record);
    this.persist();
    return record;
  }
  
  // 采购申请
  addPurchaseRequest(req: PurchaseRequest): void {
    this.purchaseRequests.push(req);
    this.persist();
  }
  
  private persist(): void {
    AppStorage.setOrCreate('inventory_items', JSON.stringify(this.items));
    AppStorage.setOrCreate('inventory_records', JSON.stringify(this.records));
    AppStorage.setOrCreate('inventory_requests', JSON.stringify(this.purchaseRequests));
  }
  
  load(): void {
    const items = AppStorage.get<string>('inventory_items');
    if (items) {
      try {
        const parsed = JSON.parse(items);
        this.items = parsed.map((i: any) => new SupplyItem(i));
      } catch (e) {
        console.error('加载库存数据失败', e);
      }
    }
  }
}

// === 预警项 ===
export interface AlertItem {
  type: 'low_stock' | 'out_of_stock' | 'expired' | 'reorder' | 'pending_approval';
  level: 'critical' | 'warning' | 'info';
  itemId: string;
  title: string;
  message: string;
  timestamp: Date;
}

// === 统计 ===
export interface InventoryStats {
  totalItems: number;
  inStock: number;
  lowStock: number;
  outOfStock: number;
  totalValue: number;
  categories: Record<string, number>;
  locations: Record<string, number>;
}

// 全局状态
export const inventoryState = new InventoryState();
AppStorage.setOrCreate('inventoryState', inventoryState);

主页面实现

// entry/src/main/ets/pages/InventoryPage.ets
// 龍魂 · 办公物资库存主页面 · ArkTS

import { SupplyItem, SupplyCategory, ItemStatus, InventoryState, inventoryState, InventoryRecord, PurchaseRequest } from '../models/InventoryModel';
import { InventoryDetailDialog } from '../components/InventoryDetailDialog';
import { StockOperationDialog } from '../components/StockOperationDialog';
import { PurchaseRequestDialog } from '../components/PurchaseRequestDialog';
import { AlertPanel } from '../components/AlertPanel';
import { InventoryDatabase } from '../database/InventoryDatabase';

@Entry
@Component
struct InventoryPage {
  @StorageLink('inventoryState') inventoryState: InventoryState = inventoryState;
  @State private selectedTab: number = 0;
  @State private showAddDialog: boolean = false;
  @State private showStockDialog: boolean = false;
  @State private showPurchaseDialog: boolean = false;
  @State private editingItem: SupplyItem | null = null;
  @State private showSearch: boolean = false;
  @State private searchText: string = '';
  @State private showAlerts: boolean = false;
  @State private scanMode: boolean = false;
  
  aboutToAppear() {
    this.inventoryState.load();
    InventoryDatabase.init();
    this.scheduleAlerts();
  }
  
  build() {
    Column() {
      // === 顶部标题栏 ===
      this.HeaderBuilder()
      
      // === 预警横幅 ===
      if (this.inventoryState.alerts.length > 0 && !this.showAlerts) {
        this.AlertBannerBuilder()
      }
      
      // === 预警面板(展开) ===
      if (this.showAlerts) {
        AlertPanel({ alerts: this.inventoryState.alerts, onClose: () => {
          this.showAlerts = false;
        }})
      }
      
      // === 统计卡片 ===
      this.StatsBuilder()
      
      // === 标签切换 ===
      this.TabBuilder()
      
      // === 搜索栏 ===
      if (this.showSearch) {
        this.SearchBuilder()
      }
      
      // === 过滤工具栏 ===
      this.FilterBuilder()
      
      // === 物资列表 ===
      this.ItemListBuilder()
      
      // === 底部操作栏 ===
      this.FooterBuilder()
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#0a0a0a')
  }
  
  // === 标题栏 ===
  @Builder
  HeaderBuilder() {
    Row() {
      Text('🐉')
        .fontSize(28)
        .margin({ right: 8 })
      
      Column() {
        Text('龍魂库存管理')
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
          .fontColor('#c41e3a')
        
        Text(`UID:${MASTER_UID} · ${this.inventoryState.items.length}项物资`)
          .fontSize(12)
          .fontColor('#666')
      }
      .alignItems(HorizontalAlign.Start)
      
      Blank()
      
      // 预警按钮
      Badge({
        value: this.inventoryState.alerts.length.toString(),
        position: BadgePosition.RightTop,
        style: { badgeSize: 18, badgeColor: '#c41e3a' }
      }) {
        Button() {
          Text('🔔')
            .fontSize(20)
        }
        .type(ButtonType.Circle)
        .backgroundColor(this.inventoryState.alerts.length > 0 ? 'rgba(196,30,58,0.2)' : '#1a1a1a')
        .width(40)
        .height(40)
        .onClick(() => {
          this.showAlerts = !this.showAlerts;
        })
      }
      
      // 扫码按钮
      Button() {
        Text('📷')
          .fontSize(20)
      }
      .type(ButtonType.Circle)
      .backgroundColor('#1a1a1a')
      .width(40)
      .height(40)
      .onClick(() => {
        this.startScan();
      })
      
      // 搜索按钮
      Button() {
        Text(this.showSearch ? '✕' : '🔍')
          .fontSize(20)
      }
      .type(ButtonType.Circle)
      .backgroundColor('#1a1a1a')
      .width(40)
      .height(40)
      .onClick(() => {
        this.showSearch = !this.showSearch;
        if (!this.showSearch) {
          this.inventoryState.searchQuery = '';
        }
      })
      
      // 添加按钮
      Button() {
        Text('+')
          .fontSize(24)
          .fontColor('#fff')
      }
      .type(ButtonType.Circle)
      .backgroundColor('#c41e3a')
      .width(40)
      .height(40)
      .onClick(() => {
        this.editingItem = null;
        this.showAddDialog = true;
      })
    }
    .width('100%')
    .height(56)
    .padding({ left: 16, right: 16 })
    .backgroundColor('#1a1a1a')
    .border({ width: { bottom: 1 }, color: '#333' })
  }
  
  // === 预警横幅 ===
  @Builder
  AlertBannerBuilder() {
    Row() {
      Text('⚠️')
        .fontSize(24)
        .margin({ right: 8 })
      
      Column() {
        Text(`${this.inventoryState.alerts.length} 条预警待处理`)
          .fontSize(14)
          .fontWeight(FontWeight.Bold)
          .fontColor('#ffcc00')
        
        Text(this.inventoryState.alerts.slice(0, 2).map(a => a.title).join('、'))
          .fontSize(12)
          .fontColor('#ffcc00')
          .maxLines(1)
          .textOverflow({ overflow: TextOverflow.Ellipsis })
      }
      .alignItems(HorizontalAlign.Start)
      .layoutWeight(1)
      
      Button('查看')
        .fontSize(12)
        .fontColor('#0a0a0a')
        .backgroundColor('#ffcc00')
        .borderRadius(12)
        .padding({ left: 12, right: 12 })
        .onClick(() => {
          this.showAlerts = true;
        })
    }
    .width('100%')
    .padding(12)
    .backgroundColor('rgba(255,204,0,0.1)')
    .border({ width: { bottom: 1 }, color: 'rgba(255,204,0,0.3)' })
  }
  
  // === 统计卡片 ===
  @Builder
  StatsBuilder() {
    Row() {
      this.StatCard('总物资', this.inventoryState.statistics.totalItems.toString(), '#fff')
      this.StatCard('在库', this.inventoryState.statistics.inStock.toString(), '#00ff00')
      this.StatCard('低库存', this.inventoryState.statistics.lowStock.toString(), '#ffcc00')
      this.StatCard('缺货', this.inventoryState.statistics.outOfStock.toString(), '#ff0000')
      this.StatCard('总值', `¥${(this.inventoryState.statistics.totalValue/10000).toFixed(1)}`, '#c41e3a')
    }
    .width('100%')
    .padding({ left: 12, right: 12, top: 12, bottom: 12 })
    .justifyContent(FlexAlign.SpaceAround)
  }
  
  @Builder
  StatCard(label: string, value: string, color: string) {
    Column() {
      Text(value)
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .fontColor(color)
      
      Text(label)
        .fontSize(11)
        .fontColor('#888')
        .margin({ top: 4 })
    }
    .padding(8)
    .backgroundColor('#1a1a1a')
    .borderRadius(8)
    .width('18%')
  }
  
  // === 标签切换 ===
  @Builder
  TabBuilder() {
    Row() {
      ForEach([
        { label: '库存总览', icon: '📦' },
        { label: '出入库', icon: '📋' },
        { label: '采购审批', icon: '✅' },
        { label: '盘点', icon: '📊' }
      ], (item: {label: string, icon: string}, index: number) => {
        Column() {
          Text(`${item.icon} ${item.label}`)
            .fontSize(13)
            .fontWeight(this.selectedTab === index ? FontWeight.Bold : FontWeight.Normal)
            .fontColor(this.selectedTab === index ? '#c41e3a' : '#888')
          
          if (this.selectedTab === index) {
            Divider()
              .strokeWidth(2)
              .color('#c41e3a')
              .width(20)
              .margin({ top: 4 })
          }
        }
        .padding({ top: 12, bottom: 12, left: 16, right: 16 })
        .onClick(() => {
          this.selectedTab = index;
        })
      })
    }
    .width('100%')
    .justifyContent(FlexAlign.SpaceAround)
    .backgroundColor('#1a1a1a')
    .border({ width: { bottom: 1 }, color: '#333' })
  }
  
  // === 搜索栏 ===
  @Builder
  SearchBuilder() {
    Row() {
      TextInput({ placeholder: '搜索名称/编码/条码/位置...', text: $$this.searchText })
        .width('100%')
        .height(40)
        .backgroundColor('#2a2a2a')
        .fontColor('#fff')
        .placeholderColor('#666')
        .borderRadius(8)
        .padding({ left: 12, right: 12 })
        .onChange((value: string) => {
          this.inventoryState.searchQuery = value;
        })
    }
    .width('100%')
    .padding({ left: 16, right: 16, top: 8, bottom: 8 })
    .backgroundColor('#1a1a1a')
  }
  
  // === 过滤工具栏 ===
  @Builder
  FilterBuilder() {
    Row() {
      // 分类选择
      Select(Object.values(SupplyCategory).map(c => ({ value: c })))
        .selected(0)
        .fontColor('#ccc')
        .backgroundColor('#2a2a2a')
        .borderRadius(8)
        .padding({ left: 8, right: 8 })
        .onSelect((index: number) => {
          const cats = ['全部', ...Object.values(SupplyCategory)];
          this.inventoryState.filterCategory = cats[index] as any;
        })
      
      // 状态选择
      Select([{ value: '全部' }, ...Object.values(ItemStatus).map(s => ({ value: s }))])
        .selected(0)
        .fontColor('#ccc')
        .backgroundColor('#2a2a2a')
        .borderRadius(8)
        .padding({ left: 8, right: 8 })
        .margin({ left: 8 })
        .onSelect((index: number) => {
          const stats = ['全部', ...Object.values(ItemStatus)];
          this.inventoryState.filterStatus = stats[index] as any;
        })
      
      Blank()
      
      // 排序
      Select([{ value: '健康度↓' }, { value: '数量↓' }, { value: '价值↓' }, { value: '名称↑' }])
        .selected(0)
        .fontColor('#ccc')
        .backgroundColor('#2a2a2a')
        .borderRadius(8)
        .padding({ left: 8, right: 8 })
    }
    .width('100%')
    .height(48)
    .padding({ left: 16, right: 16 })
    .backgroundColor('#1a1a1a')
    .border({ width: { bottom: 1 }, color: '#333' })
  }
  
  // === 物资列表 ===
  @Builder
  ItemListBuilder() {
    List() {
      ForEach(this.inventoryState.filteredItems, (item: SupplyItem) => {
        ListItem() {
          this.ItemCardBuilder(item)
        }
        .swipeAction({ end: this.SwipeActionsBuilder(item) })
        .onClick(() => {
          this.editingItem = item;
          this.showAddDialog = true;
        })
      }, (item: SupplyItem) => item.id)
    }
    .width('100%')
    .layoutWeight(1)
    .divider({ strokeWidth: 1, color: '#222' })
    .scrollBar(BarState.Auto)
    .edgeEffect(EdgeEffect.Spring)
    .padding({ bottom: 16 })
  }
  
  // === 物资卡片 ===
  @Builder
  ItemCardBuilder(item: SupplyItem) {
    Row() {
      // 健康度指示
      Stack() {
        // 条码/QR占位
        Column() {
          Text(item.sku)
            .fontSize(10)
            .fontColor('#888')
            .fontFamily('monospace')
            .rotate({ angle: -90 })
        }
        .width(40)
        .height(40)
        .backgroundColor('#2a2a2a')
        .borderRadius(4)
        .justifyContent(FlexAlign.Center)
        
        // 状态角标
        Text(item.status === ItemStatus.OUT_OF_STOCK ? '缺' : 
             item.status === ItemStatus.LOW_STOCK ? '低' : 
             item.checkExpiry() ? '过' : '')
          .fontSize(9)
          .fontColor('#fff')
          .backgroundColor(
            item.status === ItemStatus.OUT_OF_STOCK ? '#ff0000' :
            item.status === ItemStatus.LOW_STOCK ? '#ffcc00' :
            item.checkExpiry() ? '#ff6600' : 'transparent'
          )
          .borderRadius(4)
          .padding({ left: 2, right: 2 })
          .position({ x: 28, y: -4 })
          .visibility(
            item.status === ItemStatus.OUT_OF_STOCK || 
            item.status === ItemStatus.LOW_STOCK || 
            item.checkExpiry() ? Visibility.Visible : Visibility.Hidden
          )
      }
      .width(40)
      .height(40)
      .margin({ right: 12 })
      
      // 信息
      Column() {
        Row() {
          Text(item.name)
            .fontSize(15)
            .fontWeight(FontWeight.Medium)
            .fontColor('#fff')
            .layoutWeight(1)
          
          Text(`¥${item.purchasePrice}`)
            .fontSize(12)
            .fontColor('#888')
        }
        .width('100%')
        
        Row() {
          Text(`${item.category} · ${item.specification}`)
            .fontSize(12)
            .fontColor('#888')
            .maxLines(1)
            .textOverflow({ overflow: TextOverflow.Ellipsis })
            .layoutWeight(1)
          
          // 库存数量
          Text(`${item.quantity}${item.unit}`)
            .fontSize(14)
            .fontWeight(FontWeight.Bold)
            .fontColor(
              item.status === ItemStatus.OUT_OF_STOCK ? '#ff0000' :
              item.status === ItemStatus.LOW_STOCK ? '#ffcc00' :
              '#00ff00'
            )
        }
        .width('100%')
        .margin({ top: 4 })
        
        Row() {
          Text(`📍${item.location}`)
            .fontSize(11)
            .fontColor('#666')
          
          Text(`👤${item.custodian}`)
            .fontSize(11)
            .fontColor('#666')
            .margin({ left: 8 })
          
          Blank()
          
          // 健康度条
          Stack() {
            Row()
              .width('100%')
              .height(4)
              .backgroundColor('#333')
              .borderRadius(2)
            
            Row()
              .width(`${item.getHealthScore()}%`)
              .height(4)
              .backgroundColor(
                item.getHealthScore() > 80 ? '#00ff00' :
                item.getHealthScore() > 50 ? '#ffcc00' :
                '#ff0000'
              )
              .borderRadius(2)
          }
          .width(60)
          .height(4)
        }
        .width('100%')
        .margin({ top: 6 })
      }
      .layoutWeight(1)
      .alignItems(HorizontalAlign.Start)
    }
    .width('100%')
    .padding(16)
    .backgroundColor(
      item.status === ItemStatus.OUT_OF_STOCK ? 'rgba(255,0,0,0.05)' :
      item.status === ItemStatus.LOW_STOCK ? 'rgba(255,204,0,0.05)' :
      '#0a0a0a'
    )
    .borderRadius(8)
    .margin({ left: 12, right: 12, top: 8 })
  }
  
  // === 滑动操作 ===
  @Builder
  SwipeActionsBuilder(item: SupplyItem) {
    Row() {
      Button() {
        Text('入库')
          .fontColor('#fff')
          .fontSize(12)
      }
      .type(ButtonType.Capsule)
      .backgroundColor('#00aa00')
      .width(60)
      .height('100%')
      .margin({ right: 4 })
      .onClick(() => {
        this.editingItem = item;
        this.showStockDialog = true;
      })
      
      Button() {
        Text('出库')
          .fontColor('#fff')
          .fontSize(12)
      }
      .type(ButtonType.Capsule)
      .backgroundColor('#c41e3a')
      .width(60)
      .height('100%')
      .margin({ right: 4 })
      .onClick(() => {
        this.editingItem = item;
        this.showStockDialog = true;
      })
      
      Button() {
        Text('删除')
          .fontColor('#fff')
          .fontSize(12)
      }
      .type(ButtonType.Capsule)
      .backgroundColor('#ff0000')
      .width(60)
      .height('100%')
      .onClick(() => {
        AlertDialog.show({
          title: '确认删除',
          message: `删除 ${item.name}`,
          primaryButton: {
            value: '删除',
            action: () => {
              this.inventoryState.removeItem(item.id);
            }
          },
          secondaryButton: {
            value: '取消',
            action: () => {}
          }
        });
      })
    }
    .width(200)
    .height('100%')
    .padding({ left: 4 })
  }
  
  // === 底部操作栏 ===
  @Builder
  FooterBuilder() {
    Row() {
      Button('📥 入库')
        .fontSize(13)
        .fontColor('#fff')
        .backgroundColor('#00aa00')
        .borderRadius(8)
        .padding({ left: 16, right: 16 })
        .onClick(() => {
          this.showStockDialog = true;
        })
      
      Button('📤 出库')
        .fontSize(13)
        .fontColor('#fff')
        .backgroundColor('#c41e3a')
        .borderRadius(8)
        .padding({ left: 16, right: 16 })
        .onClick(() => {
          this.showStockDialog = true;
        })
      
      Button('📝 采购')
        .fontSize(13)
        .fontColor('#fff')
        .backgroundColor('#0066cc')
        .borderRadius(8)
        .padding({ left: 16, right: 16 })
        .onClick(() => {
          this.showPurchaseDialog = true;
        })
      
      Button('📊 导出')
        .fontSize(13)
        .fontColor('#ccc')
        .backgroundColor('#1a1a1a')
        .border({ width: 1, color: '#444' })
        .borderRadius(8)
        .padding({ left: 16, right: 16 })
        .onClick(() => {
          this.exportInventory();
        })
    }
    .width('100%')
    .height(56)
    .padding({ left: 16, right: 16 })
    .backgroundColor('#1a1a1a')
    .border({ width: { top: 1 }, color: '#333' })
    .justifyContent(FlexAlign.SpaceAround)
  }
  
  // === 功能方法 ===
  private startScan() {
    // 调用鸿蒙扫码能力
    // 解析条码/QR获取物资信息
  }
  
  private scheduleAlerts() {
    // 设置定时检查
    // 低库存/过期提醒
  }
  
  private exportInventory() {
    // 导出Excel/CSV
    const csv = this.generateCSV();
    // 调用鸿蒙文件分享
  }
  
  private generateCSV(): string {
    const headers = '编码,名称,分类,规格,品牌,单位,数量,安全线,位置,状态,保管员,部门,采购价,总值,批次,供应商,采购日期,保质期\n';
    const rows = this.inventoryState.items.map(i => 
      `${i.sku},${i.name},${i.category},${i.specification},${i.brand},${i.unit},${i.quantity},${i.minQuantity},${i.location},${i.status},${i.custodian},${i.department},${i.purchasePrice},${i.totalValue},${i.batchNo},${i.supplier},${i.purchaseDate.toISOString()},${i.expiryDate?.toISOString() || ''}`
    ).join('\n');
    return headers + rows;
  }
}

// === 常量 ===
const MASTER_DNA = "ZHUGEXIN⚡️2025-🇨🇳🐉⚖️♠️🧚🏼‍♀️❤️♾️";
const MASTER_UID = "9622";

数据库持久化

// entry/src/main/ets/database/InventoryDatabase.ets
// 龍魂 · 库存数据库 · 关系型存储

import { relationalStore } from '@kit.ArkData';

const STORE_NAME = 'LongHunInventory.db';
const TABLE_ITEMS = 'supply_items';
const TABLE_RECORDS = 'inventory_records';
const TABLE_REQUESTS = 'purchase_requests';

export class InventoryDatabase {
  private static store: relationalStore.RdbStore | null = null;
  
  static async init(): Promise<void> {
    const config: relationalStore.StoreConfig = {
      name: STORE_NAME,
      securityLevel: relationalStore.SecurityLevel.S1,
      encrypt: true
    };
    
    this.store = await relationalStore.getRdbStore(getContext(), config);
    
    // 创建物资表
    await this.store.executeSql(`
      CREATE TABLE IF NOT EXISTS ${TABLE_ITEMS} (
        id TEXT PRIMARY KEY,
        sku TEXT NOT NULL UNIQUE,
        name TEXT NOT NULL,
        category TEXT,
        specification TEXT,
        brand TEXT,
        unit TEXT,
        location TEXT,
        status TEXT,
        quantity INTEGER,
        min_quantity INTEGER,
        max_quantity INTEGER,
        reorder_point INTEGER,
        batch_no TEXT,
        supplier TEXT,
        purchase_date INTEGER,
        expiry_date INTEGER,
        purchase_price REAL,
        total_value REAL,
        custodian TEXT,
        department TEXT,
        barcode TEXT,
        qr_code TEXT,
        photo_path TEXT,
        rfid_tag TEXT,
        created_at INTEGER,
        updated_at INTEGER,
        dna_signature TEXT
      )
    `);
    
    // 创建流水表
    await this.store.executeSql(`
      CREATE TABLE IF NOT EXISTS ${TABLE_RECORDS} (
        id TEXT PRIMARY KEY,
        item_id TEXT,
        type TEXT,
        quantity INTEGER,
        old_quantity INTEGER,
        new_quantity INTEGER,
        operator TEXT,
        approver TEXT,
        reason TEXT,
        document_no TEXT,
        photo_path TEXT,
        timestamp INTEGER,
        dna_signature TEXT
      )
    `);
    
    // 创建采购申请表
    await this.store.executeSql(`
      CREATE TABLE IF NOT EXISTS ${TABLE_REQUESTS} (
        id TEXT PRIMARY KEY,
        request_no TEXT UNIQUE,
        requester TEXT,
        department TEXT,
        request_date INTEGER,
        required_date INTEGER,
        total_amount REAL,
        status TEXT,
        level1_approver TEXT,
        level1_approved INTEGER,
        level1_comment TEXT,
        level2_approver TEXT,
        level2_approved INTEGER,
        level2_comment TEXT,
        level3_approver TEXT,
        level3_approved INTEGER,
        level3_comment TEXT,
        purchase_order_no TEXT,
        supplier TEXT,
        actual_amount REAL,
        received_date INTEGER,
        created_at INTEGER,
        dna_signature TEXT
      )
    `);
  }
}

模块清单

文件 路径 说明
数据模型 entry/src/main/ets/models/InventoryModel.ets SupplyItem/InventoryRecord/PurchaseRequest
主页面 entry/src/main/ets/pages/InventoryPage.ets 完整UI
数据库 entry/src/main/ets/database/InventoryDatabase.ets 关系型存储
详情弹窗 entry/src/main/ets/components/InventoryDetailDialog.ets 物资详情
出入库弹窗 entry/src/main/ets/components/StockOperationDialog.ets 扫码/拍照/登记
采购弹窗 entry/src/main/ets/components/PurchaseRequestDialog.ets 多级审批
预警面板 entry/src/main/ets/components/AlertPanel.ets 预警列表

鸿蒙特性使用

特性 用途 API
ArkTS声明式UI 界面构建 @Component @Entry
状态管理 数据响应 @State @Observed @StorageLink
关系型数据库 本地加密存储 relationalStore
扫码 物资识别 scanBarcode
相机 凭证拍照 cameraPicker
NFC RFID读取 tagSession
通知 预警提醒 notificationManager
打印 标签打印 print
分享 导出报表 share

龍魂标识

位置 内容
应用名称 龍魂库存管理
标题栏 🐉 龍魂库存管理
底部标识 来自龍魂库存管理 UID9622
数据签名 SM3-哈希
DNA ZHUGEXIN⚡️2025-🇨🇳🐉⚖️♠️🧚🏼‍♀️❤️♾️

🐉 龍魂 · 鸿蒙 ArkTS 实战:Office Supply Inventory 交付完成
DNA: ZHUGEXIN⚡️2025-🇨🇳🐉⚖️♠️🧚🏼‍♀️❤️♾️
UID: 9622
确认码: #CONFIRM🌌9622-ONLY-ONCE🧬LK9X-772Z
时间: 2026-07-14 06:06
模块: 7核心文件
特性: 状态建模 · 批次追踪 · 采购审批 · 扫码领用 · 预警引擎 · 导出报表

Logo

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

更多推荐