个人编制软件展示

PSI - Purchase Sale Inventory 进销存软件

PSI进销存系统低代码平台设计

引言

低代码平台通过可视化配置方式,让用户无需编写代码即可快速构建业务应用。本文将介绍 PSI 进销存系统中低代码平台的设计与实现,帮助用户实现个性化的业务需求。

低代码平台架构

低代码平台核心组件:

组件 功能 技术实现
可视化设计器 拖拽式页面构建 React/Vue 表单设计器
表单引擎 动态表单渲染与验证 JSON Schema 解析
流程引擎 可视化流程编排 BPMN 2.0 实现
规则引擎 业务规则配置 Drools 表达式

可视化表单设计器

实现拖拽式表单构建:

// 表单设计器核心组件
class FormDesigner {
  constructor(container) {
    this.container = container;
    this.components = [];
    this.selectedComponent = null;

    // 初始化组件面板
    this.initComponentPanel();

    // 初始化画布
    this.initCanvas();

    // 初始化属性编辑器
    this.initPropertyPanel();
  }

  // 可用组件类型
  getComponentTypes() {
    return [
      { type: 'input', label: '单行文本', icon: 'text' },
      { type: 'textarea', label: '多行文本', icon: 'align-left' },
      { type: 'number', label: '数字', icon: 'hashtag' },
      { type: 'select', label: '下拉选择', icon: 'chevron-down' },
      { type: 'radio', label: '单选框', icon: 'circle' },
      { type: 'checkbox', label: '复选框', icon: 'check-square' },
      { type: 'date', label: '日期', icon: 'calendar' },
      { type: 'datetime', label: '日期时间', icon: 'clock' },
      { type: 'file', label: '文件上传', icon: 'upload' },
      { type: 'table', label: '数据表格', icon: 'table' }
    ];
  }

  // 从面板拖拽组件到画布
  addComponent(type, position) {
    const component = {
      id: this.generateId(),
      type: type,
      props: this.getDefaultProps(type),
      children: []
    };

    this.components.push(component);
    this.renderCanvas();

    return component;
  }

  // 获取组件默认属性
  getDefaultProps(type) {
    const defaults = {
      input: {
        label: '文本框',
        placeholder: '请输入',
        required: false,
        maxLength: 100,
        validation: []
      },
      number: {
        label: '数字',
        placeholder: '请输入数字',
        required: false,
        min: 0,
        max: 999999,
        precision: 2
      },
      select: {
        label: '下拉选择',
        required: false,
        options: [
          { label: '选项一', value: '1' },
          { label: '选项二', value: '2' }
        ],
        remote: false,
        api: ''
      }
    };

    return defaults[type] || {};
  }

  // 渲染画布
  renderCanvas() {
    this.canvas.innerHTML = '';

    this.components.forEach((comp, index) => {
      const el = this.createComponentElement(comp);
      el.dataset.index = index;

      // 绑定选择事件
      el.addEventListener('click', (e) => {
        e.stopPropagation();
        this.selectComponent(index);
      });

      // 拖拽排序
      el.setAttribute('draggable', true);
      el.addEventListener('dragstart', (e) => {
        e.dataTransfer.setData('index', index);
      });

      this.canvas.appendChild(el);
    });
  }

  // 创建组件元素
  createComponentElement(component) {
    const el = document.createElement('div');
    el.className = 'form-component';
    el.style.marginBottom = '10px';

    let html = `
${component.props.label}`; if (component.props.required) { html += '*'; } html += '
'; switch (component.type) { case 'input': html += ``; break; case 'textarea': html += ``; break; case 'number': html += ``; break; case 'select': const options = component.props.options .map(o => ``) .join(''); html += ``; break; } el.innerHTML = html; return el; } // 选择组件 selectComponent(index) { this.components.forEach((comp, i) => { comp.selected = (i === index); }); this.renderCanvas(); this.renderPropertyPanel(this.components[index]); } // 导出表单配置 exportSchema() { return { version: '1.0', components: this.components.map(c => ({ type: c.type, props: c.props })) }; } } // 表单渲染器 - 根据配置渲染表单 class FormRenderer { constructor(schema, container) { this.schema = schema; this.container = container; this.values = {}; } // 渲染表单 render() { this.container.innerHTML = ''; this.schema.components.forEach(comp => { const wrapper = document.createElement('div'); wrapper.className = 'form-group'; const label = document.createElement('label'); label.textContent = comp.props.label; if (comp.props.required) { label.innerHTML += ' *'; } wrapper.appendChild(label); const input = this.createInput(comp); input.name = comp.props.field || comp.props.label; wrapper.appendChild(input); this.container.appendChild(wrapper); }); } // 创建输入控件 createInput(component) { const config = component.props; let element; switch (component.type) { case 'input': element = document.createElement('input'); element.type = 'text'; element.className = 'form-control'; element.placeholder = config.placeholder || ''; break; case 'number': element = document.createElement('input'); element.type = 'number'; element.className = 'form-control'; element.min = config.min || 0; element.max = config.max || 999999; break; case 'select': element = document.createElement('select'); element.className = 'form-control'; config.options.forEach(opt => { const option = document.createElement('option'); option.value = opt.value; option.textContent = opt.label; element.appendChild(option); }); break; // ... 其他类型处理 } // 绑定验证事件 if (config.required) { element.addEventListener('blur', () => { this.validateField(element, config); }); } // 绑定值变化事件 element.addEventListener('change', (e) => { this.values[element.name] = e.target.value; }); return element; } // 验证字段 validateField(element, config) { const value = element.value; if (config.required && !value) { this.showError(element, '此字段为必填项'); return false; } // 自定义验证规则 if (config.validation) { for (const rule of config.validation) { if (!rule.test(value)) { this.showError(element, rule.message); return false; } } } this.clearError(element); return true; } showError(element, message) { this.clearError(element); const error = document.createElement('div'); error.className = 'text-danger small'; error.textContent = message; element.parentNode.appendChild(error); element.classList.add('is-invalid'); } clearError(element) { element.classList.remove('is-invalid'); const error = element.parentNode.querySelector('.text-danger.small'); if (error) error.remove(); } // 获取表单值 getValues() { return { ...this.values }; } // 验证整个表单 validate() { let isValid = true; this.schema.components.forEach(comp => { const input = this.container.querySelector(`[name="${comp.props.field || comp.props.label}"]`); if (input) { if (!this.validateField(input, comp.props)) { isValid = false; } } }); return isValid; } }

工作流引擎设计

可视化流程编排实现:

// 工作流引擎核心实现
class WorkflowEngine {
  constructor() {
    this.processes = new Map();
    this.tasks = new Map();
  }

  // 定义流程
  defineProcess(definition) {
    const process = {
      id: definition.id,
      name: definition.name,
      nodes: definition.nodes,
      edges: definition.edges,
      variables: definition.variables || {}
    };

    this.processes.set(process.id, process);
    return process;
  }

  // 启动流程实例
  async startProcess(processId, initiator, variables = {}) {
    const process = this.processes.get(processId);
    if (!process) {
      throw new Error(`Process ${processId} not found`);
    }

    // 创建流程实例
    const instance = {
      id: this.generateId(),
      processId: processId,
      initiator: initiator,
      status: 'RUNNING',
      currentNode: this.getStartNode(process),
      variables: { ...process.variables, ...variables },
      startTime: new Date(),
      history: []
    };

    // 执行开始节点
    await this.executeNode(instance, instance.currentNode);

    return instance;
  }

  // 获取开始节点
  getStartNode(process) {
    return process.nodes.find(n => n.type === 'start');
  }

  // 执行节点
  async executeNode(instance, node) {
    switch (node.type) {
      case 'start':
        await this.executeStartNode(instance, node);
        break;
      case 'task':
        await this.executeTaskNode(instance, node);
        break;
      case 'gateway':
        await this.executeGatewayNode(instance, node);
        break;
      case 'end':
        await this.executeEndNode(instance, node);
        break;
    }
  }

  // 执行任务节点
  async executeTaskNode(instance, node) {
    // 创建任务
    const task = {
      id: this.generateId(),
      instanceId: instance.id,
      nodeId: node.id,
      nodeName: node.name,
      assignee: node.assignee || instance.initiator,
      status: 'PENDING',
      createTime: new Date()
    };

    this.tasks.set(task.id, task);

    // 记录历史
    instance.history.push({
      nodeId: node.id,
      nodeName: node.name,
      enterTime: new Date()
    });

    // 如果是用户任务,推送到待办
    if (node.taskType === 'user') {
      await this.createUserTask(task);
    } else {
      // 服务任务直接执行
      await this.executeServiceTask(instance, node);
      await this.completeTask(task.id);
    }
  }

  // 执行服务任务
  async executeServiceTask(instance, node) {
    if (node.service) {
      const service = this.getService(node.service.name);
      const result = await service.execute(instance.variables, node.service.params);
      instance.variables[node.service.outputVar] = result;
    }
  }

  // 执行网关节点
  async executeGatewayNode(instance, node) {
    if (node.gatewayType === 'exclusive') {
      // 单向网关 - 根据条件选择分支
      for (const edge of this.getOutgoingEdges(instance.processId, node.id)) {
        if (await this.evaluateCondition(instance, edge.condition)) {
          const nextNode = this.getNodeById(instance.processId, edge.target);
          await this.executeNode(instance, nextNode);
          return;
        }
      }
    } else if (node.gatewayType === 'parallel') {
      // 并行网关 - 执行所有分支
      const edges = this.getOutgoingEdges(instance.processId, node.id);
      await Promise.all(edges.map(async edge => {
        const nextNode = this.getNodeById(instance.processId, edge.target);
        await this.executeNode(instance, nextNode);
      }));
    }
  }

  // 完成任务
  async completeTask(taskId, variables = {}) {
    const task = this.tasks.get(taskId);
    task.status = 'COMPLETED';
    task.completeTime = new Date();
    task.variables = variables;

    // 查找流程实例
    const instance = await this.getInstanceById(task.instanceId);

    // 查找下一个节点
    const process = this.processes.get(instance.processId);
    const outgoingEdges = this.getOutgoingEdges(process.id, task.nodeId);

    if (outgoingEdges.length > 0) {
      const edge = outgoingEdges[0];
      const nextNode = this.getNodeById(process.id, edge.target);
      await this.executeNode(instance, nextNode);
    } else {
      // 流程结束
      await this.endProcess(instance);
    }
  }

  // 条件评估
  async evaluateCondition(instance, condition) {
    if (!condition) return true;

    // 简单表达式评估
    const vars = instance.variables;
    const expr = condition.replace(/\$\{(\w+)\}/g, (_, key) => {
      return typeof vars[key] === 'string' ? `'${vars[key]}'` : vars[key];
    });

    return eval(expr);
  }
}

// 流程设计器
class WorkflowDesigner {
  constructor(container) {
    this.container = container;
    this.nodes = [];
    this.edges = [];
  }

  // 添加节点
  addNode(node) {
    this.nodes.push(node);
    this.render();
  }

  // 连接节点
  connect(sourceId, targetId, condition) {
    this.edges.push({
      id: this.generateId(),
      source: sourceId,
      target: targetId,
      condition: condition
    });
    this.render();
  }

  // 导出流程定义
  exportDefinition() {
    return {
      id: this.generateId(),
      nodes: this.nodes,
      edges: this.edges
    };
  }
}

业务规则引擎

实现动态业务规则配置:

// 业务规则引擎
class RuleEngine {
  constructor() {
    this.rules = new Map();
  }

  // 定义规则
  defineRule(rule) {
    this.rules.set(rule.id, rule);
  }

  // 批量定义规则
  defineRules(rules) {
    rules.forEach(rule => this.defineRule(rule));
  }

  // 执行规则
  async execute(facts, ruleIds = null) {
    const rulesToExecute = ruleIds
      ? ruleIds.map(id => this.rules.get(id)).filter(Boolean)
      : Array.from(this.rules.values());

    const results = [];

    for (const rule of rulesToExecute) {
      // 评估条件
      if (await this.evaluateCondition(rule.condition, facts)) {
        // 执行动作
        const result = await this.executeActions(rule.actions, facts);
        results.push({ rule: rule.name, executed: true, result });
      }
    }

    return results;
  }

  // 评估条件
  async evaluateCondition(condition, facts) {
    if (!condition) return true;

    // 简化规则条件评估
    const factMap = this.buildFactMap(facts);

    return this.evalExpression(condition, factMap);
  }

  // 构建事实映射
  buildFactMap(facts) {
    return facts.reduce((map, fact) => {
      map[fact.field] = fact.value;
      return map;
    }, {});
  }

  // 执行动作
  async executeActions(actions, facts) {
    const results = [];

    for (const action of actions) {
      switch (action.type) {
        case 'set':
          this.setFieldValue(facts, action.field, action.value);
          results.push({ type: 'set', field: action.field, value: action.value });
          break;
        case 'calculate':
          const value = this.calculate(facts, action.expression);
          this.setFieldValue(facts, action.target, value);
          results.push({ type: 'calculate', result: value });
          break;
        case 'validate':
          const valid = this.validate(facts, action.rules);
          results.push({ type: 'validate', valid, message: action.message });
          break;
      }
    }

    return results;
  }

  // 计算表达式
  calculate(facts, expression) {
    const vars = this.buildFactMap(facts);
    const expr = expression.replace(/\$\{(\w+)\}/g, (_, key) => {
      return typeof vars[key] === 'string' ? `'${vars[key]}'` : vars[key];
    });
    return eval(expr);
  }
}

// 规则配置界面
class RuleConfig {
  constructor() {
    this.conditions = [];
    this.actions = [];
  }

  // 添加条件
  addCondition(field, operator, value) {
    this.conditions.push({ field, operator, value });
  }

  // 添加动作
  addAction(type, config) {
    this.actions.push({ type, ...config });
  }

  // 导出规则
  toRule() {
    return {
      id: this.generateId(),
      name: this.name,
      condition: this.buildCondition(),
      actions: this.actions
    };
  }

  buildCondition() {
    return this.conditions.map(c => {
      return `${c.field} ${c.operator} ${typeof c.value === 'string' ? `'${c.value}'` : c.value}`;
    }).join(' AND ');
  }
}

最佳实践建议

总结

低代码平台让 PSI 进销存系统更灵活:

通过低代码平台,用户可以快速构建符合业务需求的进销存应用。

← 下一篇:PSI进销存系统工作流引擎与审批流程设计