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 进销存系统更灵活:
- 快速交付:可视化配置减少开发工作量
- 灵活定制:满足个性化业务需求
- 降低门槛:无需编程经验即可配置
- 易于维护:配置化方式便于后续调整
通过低代码平台,用户可以快速构建符合业务需求的进销存应用。