个人编制软件展示

PSI - Purchase Sale Inventory 进销存软件

PSI进销存系统移动端App开发

移动端需求分析

移动端是进销存系统的重要组成部分,需要满足以下核心场景:

场景 功能需求 技术要点
扫码出入库 扫描条码/二维码,快速完成出入库 相机扫码、离线缓存
库存查询 实时查询库存、库存预警 数据同步、缓存策略
订单管理 创建订单、订单审批、状态跟踪 表单验证、离线提交
数据报表 销售报表、库存报表查看 图表展示、数据导出
消息通知 订单提醒、库存预警、审批通知 推送通知、实时通讯

技术选型

移动端开发的技术方案对比:

App架构设计

PSI移动端采用模块化架构:

// App模块结构
const appModules = {
  // 核心模块
  core: {
    auth: '认证模块',
    network: '网络请求',
    storage: '本地存储',
    cache: '缓存管理'
  },

  // 业务模块
  business: {
    warehouse: '仓储管理',
    purchase: '采购管理',
    sales: '销售管理',
    inventory: '库存管理',
    report: '报表中心'
  },

  // 公共模块
  common: {
    scanner: '扫码功能',
    camera: '相机拍照',
    location: '定位服务',
    push: '消息推送'
  }
};

// 页面路由配置
const routes = {
  '/': 'HomePage',
  '/login': 'LoginPage',
  '/dashboard': 'DashboardPage',
  '/warehouse/in': 'WarehouseInPage',
  '/warehouse/out': 'WarehouseOutPage',
  '/warehouse/scan': 'ScannerPage',
  '/order/create': 'OrderCreatePage',
  '/order/list': 'OrderListPage',
  '/inventory/check': 'InventoryCheckPage',
  '/report/sales': 'SalesReportPage'
};

扫码功能实现

扫码是进销存系统的核心功能:

// 扫码功能实现
class ScannerService {
  constructor() {
    this.scanner = null;
  }

  // 初始化扫码器
  async init() {
    if (this.scanner) return;

    const { BarcodeScanner } = require('@capacitor-community/barcode-scanner');
    this.scanner = BarcodeScanner;
  }

  // 检查相机权限
  async checkPermission() {
    const permission = await this.scanner.checkPermission();
    return permission.granted;
  }

  // 请求相机权限
  async requestPermission() {
    const permission = await this.scanner.requestPermission();
    return permission.granted;
  }

  // 启动扫码
  async startScan(options = {}) {
    const hasPermission = await this.checkPermission();
    if (!hasPermission) {
      const granted = await this.requestPermission();
      if (!granted) throw new Error('相机权限被拒绝');
    }

    return new Promise((resolve, reject) => {
      this.scanner.startScan({
        targetedFormats: ['QR_CODE', 'CODE_128', 'EAN_13'],
        ...options
      }).then(result => {
        if (result.hasContent) {
          resolve(result.content);
        } else {
          reject(new Error('未扫描到内容'));
        }
      }).catch(reject);
    });
  }

  // 停止扫码
  async stopScan() {
    await this.scanner.stopScan();
  }

  // 解析条码内容
  parseBarcode(content) {
    // 判断条码类型
    if (content.startsWith('P:')) {
      // 产品条码
      return { type: 'product', code: content.substring(2) };
    } else if (content.startsWith('O:')) {
      // 订单条码
      return { type: 'order', code: content.substring(2) };
    } else if (content.startsWith('L:')) {
      // 库位条码
      return { type: 'location', code: content.substring(2) };
    } else {
      // 通用条码
      return { type: 'unknown', code: content };
    }
  }
}

// 扫码出入库页面逻辑
class WarehouseInPage {
  constructor() {
    this.scanner = new ScannerService();
    this.items = [];
  }

  async handleScan() {
    try {
      const content = await this.scanner.startScan();
      const parsed = this.scanner.parseBarcode(content);

      if (parsed.type === 'product') {
        // 查询产品信息
        const product = await this.queryProduct(parsed.code);
        this.addItem(product);
      } else {
        this.showError('无效的产品条码');
      }
    } catch (error) {
      this.showError(error.message);
    }
  }

  async addItem(product) {
    // 检查是否已存在
    const existing = this.items.find(item => item.productId === product.id);
    if (existing) {
      existing.quantity++;
    } else {
      this.items.push({
        productId: product.id,
        productCode: product.code,
        productName: product.name,
        quantity: 1,
        location: ''
      });
    }
    this.render();
  }
}

离线数据同步

移动端需要支持离线操作:

// 离线数据管理
class OfflineManager {
  constructor() {
    this.pendingOps = []; // 待同步操作
    this.db = null; // 本地数据库
  }

  // 初始化本地数据库
  async init() {
    const { SQLite } = require('@capacitor-community/sqlite');
    this.db = await SQLite.createDatabase({ name: 'psi.db' });
    await this.createTables();
  }

  // 创建离线表
  async createTables() {
    await this.db.execute(`
      CREATE TABLE IF NOT EXISTS offline_operations (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        type TEXT NOT NULL,
        table_name TEXT NOT NULL,
        data TEXT NOT NULL,
        created_at INTEGER NOT NULL,
        synced INTEGER DEFAULT 0
      )
    `);
  }

  // 保存离线操作
  async saveOperation(type, tableName, data) {
    const operation = {
      type,
      tableName,
      data: JSON.stringify(data),
      createdAt: Date.now(),
      synced: 0
    };

    await this.db.execute(
      'INSERT INTO offline_operations (type, table_name, data, created_at, synced) VALUES (?, ?, ?, ?, ?)',
      [operation.type, operation.tableName, operation.data, operation.createdAt, operation.synced]
    );

    // 尝试立即同步
    this.syncNow();
  }

  // 同步到服务器
  async syncNow() {
    const operations = await this.db.execute(
      'SELECT * FROM offline_operations WHERE synced = 0 ORDER BY created_at'
    );

    for (const op of operations.values) {
      try {
        await this.syncOperation(op);
        await this.db.execute(
          'UPDATE offline_operations SET synced = 1 WHERE id = ?',
          [op.id]
        );
      } catch (error) {
        console.error('同步失败:', error);
        // 重试逻辑
      }
    }
  }

  // 同步单个操作
  async syncOperation(operation) {
    const url = `/api/${operation.table_name}`;
    const method = operation.type === 'create' ? 'POST'
      : operation.type === 'update' ? 'PUT'
      : 'DELETE';

    const response = await fetch(url, {
      method,
      headers: { 'Content-Type': 'application/json' },
      body: operation.data
    });

    if (!response.ok) {
      throw new Error('Sync failed');
    }
  }

  // 定期同步
  startPeriodicSync(interval = 60000) {
    setInterval(() => this.syncNow(), interval);
  }
}

消息推送集成

实现消息推送功能:

// 消息推送服务
class PushService {
  constructor() {
    this.token = null;
    this.listeners = [];
  }

  // 初始化推送
  async init() {
    const { PushNotifications } = require('@capacitor/push-notifications');

    // 请求权限
    const result = await PushNotifications.requestPermission();
    if (result.granted) {
      // 注册设备
      await PushNotifications.register();
    }

    // 监听token
    PushNotifications.addListener('registration', (token) => {
      this.token = token.value;
      this.saveToken(token.value);
    });

    // 监听消息
    PushNotifications.addListener('pushNotificationReceived', (notification) => {
      this.handleNotification(notification);
    });

    // 监听点击
    PushNotifications.addListener('pushNotificationActionPerformed', (notification) => {
      this.handleAction(notification);
    });
  }

  // 处理通知
  handleNotification(notification) {
    const { title, body, data } = notification;

    // 根据通知类型处理
    switch (data.type) {
      case 'order_approve':
        // 跳转审批页面
        router.navigate('/order/approve');
        break;
      case 'inventory_alert':
        // 跳转库存预警
        router.navigate('/inventory/alert');
        break;
      case 'stock_out':
        // 跳转缺货提醒
        router.navigate('/inventory/out');
        break;
    }

    // 通知监听器
    this.listeners.forEach(listener => listener(notification));
  }

  // 添加监听器
  addListener(callback) {
    this.listeners.push(callback);
  }

  // 保存token到服务器
  async saveToken(token) {
    await api.post('/device/token', { token, platform: 'android' });
  }
}

性能优化

移动端性能优化策略:

总结

PSI移动端App开发采用跨平台方案,实现了一套代码同时支持iOS和Android。通过扫码、离线同步、消息推送等核心功能,满足了仓储、物流等场景的移动办公需求。

← 下一篇:PSI进销存系统多租户架构设计 上篇:PSI进销存系统移动端小程序开发实战 →