KingFusion驾驶舱-产能信息模块开发实战

本文基于真实项目------一个智能生产线MES系统的驾驶舱开发过程整理,记录了从表结构分析、业务逻辑梳理到前后端代码实现的全过程。


一、背景

近期在做一个MES项目,驾驶舱界面需要展示工厂的产能信息,包含3张ECharts图表:

图表 类型 展示内容
年度产量趋势 折线图 1~12月成品产量变化
近7日产量趋势 折线图 最近7天每日成品产量
年度产量达成 柱状图 计划产量 vs 实际产量

平台是KingFusion(亚控科技的MES低代码平台),采用前后端分离架构:

  • 前端:KingFusion页面设计器 + ECharts图表控件

  • 后端:请求式计算脚本(类似API接口)


二、数据库表结构分析

最开始我以为只需要两张表,但随着分析的深入,最终涉及4张表。

2.1 主计划列表(PM_MaiorPlanData)

这张表记录的是生产计划,每个订单对应一条主计划记录。

字段 说明
c_MasterPlanOrder 主计划编号(主键)
i_Num 计划数量
d_DeliverDate 交货日期
i_ProductState 生产状态(0执行中/1待执行/2已完成)

用途:提供年度计划产量数据,用于图表3的"计划产量"柱状图。

2.2 工单列表(PM_WorkOrderData)

主计划拆分为多个工单,每个工序对应一个工单。

字段 说明
c_WorkOrder 工单号(主键)
c_MasterPlanOrder 关联主计划编号
c_MaterialCode 物料编码(关键关联字段)
c_WorkOrderType 工单类型(0半成品/1成品/2外协)

用途:桥梁表,将报工记录与物料档案关联起来。

2.3 报工记录表(PM_WorkReportRecord)

这是产能信息的核心数据表,每个工序完工后都会产生一条报工记录。

字段 说明
c_WorkReportCode 报工单号(主键)
c_WorkOrder 工单号(关联PM_WorkOrderData)
d_Date 报工日期
i_Num 报工数量(实际产量)
c_Line 设备/线体

2.4 物料档案信息表(AA_Basic_MaterialFileTable)

这张表定义了每个物料的分类属性,是最关键的过滤依据。

字段 说明
c_MaterialCode 物料编码(主键)
c_MaterialClassification 物料分类(原料/半成品/成品)
c_MaterialName 物料名称

三、核心业务逻辑------为什么要加"成品过滤"?

这是开发过程中最关键的发现,也是这篇文章最有价值的部分。

问题场景

一开始我直接统计所有报工记录的 i_Num,结果数据明显偏大。经过分析发现:

一个完整的防护门生产,需要经过多个工序:下料→门框焊接→门扇焊接→组装→喷涂→检验。

每个工序都要报工,但只有最后一个"组装"工序的报工才代表成品产出。

门框、门扇等中间工序的报工,对应的是半成品,不是成品。

如果不做过滤,驾驶舱显示的"产量"会把门框、门扇、面板等所有工序的报工都算进去,数据就失真了。

解决方案

通过三表JOIN + 物料分类过滤,只统计物料分类为"成品"的报工记录:

bash 复制代码
PM_WorkReportRecord (报工记录)
    ↓ c_WorkOrder JOIN
PM_WorkOrderData (工单列表)
    ↓ c_MaterialCode JOIN
AA_Basic_MaterialFileTable (物料档案)
    ↓ WHERE c_MaterialClassification = '成品'

四、后端实现(请求式计算脚本)

在后端计算工程中,我创建了一个请求式计算脚本 PM_cockpit,用 switch-case 结构区分不同接口。

case 1:年度产量趋势

sql 复制代码
SELECT
    EXTRACT(MONTH FROM wrr."d_Date") AS month,
    COALESCE(SUM(wrr."i_Num"), 0) AS actual_qty
FROM "PM_WorkReportRecord" wrr
JOIN "PM_WorkOrderData" wod ON wrr."c_WorkOrder" = wod."c_WorkOrder"
JOIN "AA_Basic_MaterialFileTable" mft ON wod."c_MaterialCode" = mft."c_MaterialCode"
WHERE mft."c_MaterialClassification" = '成品'
  AND wrr."d_Date" >= '2026-01-01' AND wrr."d_Date" < '2027-01-01'
  AND wrr."c_WorkOrder" IS NOT NULL AND wrr."c_WorkOrder" != 'null'
GROUP BY month ORDER BY month;

case 2:近7日产量趋势

sql 复制代码
SELECT
    wrr."d_Date" AS date,
    COALESCE(SUM(wrr."i_Num"), 0) AS actual_qty
FROM "PM_WorkReportRecord" wrr
JOIN "PM_WorkOrderData" wod ON wrr."c_WorkOrder" = wod."c_WorkOrder"
JOIN "AA_Basic_MaterialFileTable" mft ON wod."c_MaterialCode" = mft."c_MaterialCode"
WHERE mft."c_MaterialClassification" = '成品'
  AND wrr."d_Date" >= CURRENT_DATE - 7
  AND wrr."c_WorkOrder" IS NOT NULL AND wrr."c_WorkOrder" != 'null'
GROUP BY wrr."d_Date" ORDER BY wrr."d_Date";

case 3:年度产量达成(计划 vs 实际)

这个case需要双路查询,分别返回计划产量和实际产量:

sql 复制代码
case 3://驾驶舱-产能信息-年度产量达成
    // 计划产量:从主计划表按月份汇总
    var sqlPlan =`SELECT
        EXTRACT(MONTH FROM "d_DeliverDate") AS month,
        COALESCE(SUM("i_Num"), 0) AS plan_qty
    FROM "PM_MaiorPlanData"
    WHERE "d_DeliverDate" >= '2026-01-01' AND "d_DeliverDate" < '2027-01-01'
    GROUP BY month ORDER BY month`;
 
    // 实际产量:三表JOIN + 成品过滤
    var sqlActual =`SELECT
        EXTRACT(MONTH FROM wrr."d_Date") AS month,
        COALESCE(SUM(wrr."i_Num"), 0) AS actual_qty
    FROM "PM_WorkReportRecord" wrr
    JOIN "PM_WorkOrderData" wod ON wrr."c_WorkOrder" = wod."c_WorkOrder"
    JOIN "AA_Basic_MaterialFileTable" mft ON wod."c_MaterialCode" = mft."c_MaterialCode"
    WHERE mft."c_MaterialClassification" = '成品'
      AND wrr."d_Date" >= '2026-01-01' AND wrr."d_Date" < '2027-01-01'
      AND wrr."c_WorkOrder" IS NOT NULL AND wrr."c_WorkOrder" != 'null'
    GROUP BY month ORDER BY month`;
 
    var resPlan=[], resActual=[];
    var retPlan = await SQLQuery('RFM_PM', sqlPlan, resPlan);
    var retActual = await SQLQuery('RFM_PM', sqlActual, resActual);
    OutParam.records = resPlan;      // 前端取 records
    OutParam.records1 = resActual;   // 前端取 records1
    OutParam.errorCode = retPlan;
    OutParam.errorCode1 = retActual;
    break;

注意:OutParam.records 和 OutParam.records1 分别返回两个数据集,前端通过 res.records 和 res.records1 分别接收。


五、前端实现(页面事件 + ECharts)

5.1 界面创建时------初始化变量

在驾驶舱页面加载时,通过翻转变量值来触发变量改变事件,实现图表数据的自动加载:

javascript 复制代码
//=============产能信息==============//
//近7日产量趋势图
$Variable.Day7Output=!$Variable.Day7Output;
//年度产量趋势
$Variable.AnnualOutput=!$Variable.AnnualOutput;
//年度产量达成情况
$Variable.AnnualOutputAchieve=!$Variable.AnnualOutputAchieve;

5.2 变量事件------调用后端 + 更新图表

以年度产量趋势为例,变量改变事件中通过 requestCalculationPost 调用后端,返回数据后更新 ECharts:

javascript 复制代码
//年度产量趋势
 
var M={ 'type': 1 };
 
requestCalculationPost($System.RFM_PM, 'PM_cockpit', M, function (res) {
    if(res.Code===0){
        if (res.errorCode === 0) {
            var resdata = res.records;
            var monthData = ['1','2','3','4','5','6','7','8','9','10','11','12'];
            var actualData = [0,0,0,0,0,0,0,0,0,0,0,0];
 
            for (var i = 0; i < resdata.length; i++) {
                var idx = parseInt(resdata[i].month) - 1;
                actualData[idx] = resdata[i].actual_qty;
            }
 
            option = {
                title: {
                    text: "年度产量趋势",
                    left: "center",
                    textStyle: { color: 'white', fontSize: 30 },
                    top: 11
                },
                xAxis: {
                    type: 'category',
                    boundaryGap: false,
                    data: monthData,
                    axisLine: { lineStyle: { color: 'white' } },
                    axisLabel: { textStyle: { color: 'white', fontSize: 20 } },
                    name: '月份',
                    nameTextStyle: { fontSize: 20 }
                },
                yAxis: {
                    axisLine: { lineStyle: { color: 'white' } },
                    axisLabel: { textStyle: { color: 'white', fontSize: 20 } },
                    splitLine: { show: false },
                    type: 'value',
                    name: '单位:(樘)'
                },
                series: [{
                    data: actualData,
                    type: 'line',
                    lineStyle: { color: 'green', width: 5 },
                    areaStyle: {},
                    label: { show: true, color: 'white', fontSize: 20 }
                }]
            };
            CustomCharts42.$Children.echart.setOption(option, true);
        } else {
            $Function.tip('error', '年度产量趋势查询失败!');
        }
    }
});

5.3 近7日产量趋势------动态日期

前端用 getLastSevenDays() 函数动态生成近7天的日期数组,与后端返回的数据做映射:

javascript 复制代码
var dates = getLastSevenDays();
 
function getLastSevenDays() {
  const dates = [];
  for (let i = 6; i >= 0; i--) {
    const date = new Date();
    date.setDate(date.getDate() - i);
    const month = String(date.getMonth() + 1).padStart(2, '0');
    const day = String(date.getDate()).padStart(2, '0');
    dates.push(`${month}-${day}`);
  }
  return dates;
}

5.4 年度产量达成------双柱对比

这个图表有两个柱子系列,用不同颜色区分:

javascript 复制代码
series: [
    {
        name: '计划产量',
        type: 'bar',
        data: planQty,
        itemStyle: { color: '#FFD400' },  // 黄色
        barWidth: '20'
    },
    {
        name: '实际产量',
        type: 'bar',
        data: actualQty,
        itemStyle: { color: '#2b80ff' },  // 蓝色
        barWidth: '16'
    }
]

六、踩坑总结

坑1:成品过滤------最大的坑

一开始只统计了报工记录表,发现数据偏大。后来才意识到报工记录包含了所有工序的数据,必须通过物料档案表过滤出"成品"。

坑2:数据源名称

一开始以为不同数据库的数据源名称不同,实际上所有表在同一个数据库,不需要跨库查询,一个 JOIN 就能解决。

坑3:ECharts组件ID

驾驶舱JSON中图表的 name 属性(如 CustomCharts42)就是前端JS中引用的组件ID,代码中

CustomCharts42.$Children.echart.setOption() 必须与设计器中的名称一致。


七、完整数据流图

javascript 复制代码
PM_WorkReportRecord (报工记录)
    │ c_WorkOrder
    ▼
PM_WorkOrderData (工单列表)
    │ c_MaterialCode
    ▼
AA_Basic_MaterialFileTable (物料档案)
    │ WHERE c_MaterialClassification = '成品'
    ▼
GROUP BY month/date → SUM(i_Num) → actual_qty
    │
    ├─→ 年度产量趋势 (CustomCharts42)
    ├─→ 近7日产量趋势 (CustomCharts43)
    └─→ 年度产量达成 (CustomCharts41)

八、总结

KingFusion驾驶舱开发的核心思路:

  1. 数据源分析:先理清业务数据在哪些表中,字段含义是什么

  2. 业务逻辑梳理:产量统计的关键不是"怎么统计",而是"统计什么"------成品/半成品/原料的区分是MES系统的核心业务逻辑

  3. 前后端分离:后端请求式计算负责SQL查询,前端变量事件负责数据展示,通过 requestCalculationPost 通信

  4. ECharts渲染:用 setOption 动态更新图表,数据从后端返回后填充到option中


本文基于某智能生产线MES项目实战经验整理,所用平台为亚控KingFusion。

如果你也在做KingFusion相关的MES项目,欢迎交流讨论。

相关推荐
小弥儿4 小时前
GPT Image 2 提示词库,把散落的生图提示词变成工程资产
开发语言·javascript·gpt·学习
楠楠子呀4 小时前
独立站聊天转化全链路:从二维码引流到数据复盘
java·javascript·数据仓库·python·c#·自动化·etl
智商偏低4 小时前
bindFramebuffer
前端·javascript·html
BioRunYiXue5 小时前
RACE技术全攻略:从全长克隆到靶基因验证
java·javascript·网络·人工智能·科技·算法·eclipse
IMPYLH5 小时前
HTML 的 <ins> 元素
前端·javascript·html
晨米酱5 小时前
Umi Mock 如何从文件声明变成 HTTP 响应
前端·javascript·设计
01_ice5 小时前
前端学习JS(3)
前端·javascript·学习
Hilaku6 小时前
外包前端和大厂前端,写的代码到底有什么本质区别?
前端·javascript·程序员
YHHLAI6 小时前
React `useState` 深入浅出
前端·javascript·react.js