Summer000 发表于 2024-1-10 21:16

Python 自研审批流引擎,可根据封装的审批引擎接口对接各类表单!!

本帖最后由 Summer000 于 2024-1-10 21:29 编辑

         这两个月给部门使用Django框架开发了一套内部人员使用的工作平台,想着可能很快就结束了,临到结束,领导要求增加两个调整申请审批单,需要有审批流,并且附带会签。可以自定义审批节点,本来想着找个开源的审批流引擎,发现市场上都是Activiti、Airflow、Zope之类的审批流引擎,引用起来还比较麻烦,不能很好的通过接口直接调用,比较烦人,索性自己开发了一套审批流引擎,暂且叫它MapFlow吧,为啥是Map呢,主要是配置很灵活,开箱即用。
      好了,言归正传,开始讲思路与功能点:
      首先,我需要使用一个类似于绘制前端动态图的方式绘制一张审批流程图,我需要保存当前绘制审批流程图的相关点位,对当前节点进行分类,是属于开始结束类还是步骤类,以及使用不同代码区分当前步骤节点,便于后期封装接口,这个后期再讲,上图:


呃,图片咋贴出来这么chou,先将就着看吧。。。
整个审批流引擎一共使用了12张表,包括两个完全不同的申请单,两个审批流审核,审批节点记录表,审批记录表等;
这个代码粘贴的太丑了,为啥不能直接上小方块呢?
    function init() {
      var $ = go.GraphObject.make;// for conciseness in defining template
myDiagram =
            $(go.Diagram, "myDiagramDiv",// must name or refer to the DIV HTML element
                {
                  "LinkDrawn": showLinkLabel,// this DiagramEvent listener is defined below
                  "LinkRelinked": showLinkLabel,
                  "undoManager.isEnabled": true// enable undo & redo
                });

      myDiagram.addDiagramListener("Modified", function(e) {
            var button = document.getElementById("SaveButton");
            if (button) button.disabled = !myDiagram.isModified;
            var idx = document.title.indexOf("*");
            if (myDiagram.isModified) {
                if (idx < 0) document.title += "*";
            } else {
                if (idx >= 0) document.title = document.title.substr(0, idx);
            }
      });

      function nodeStyle() {
            return [
                new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
                {
                  locationSpot: go.Spot.Center
                }
            ];
      }

      function makePort(name, align, spot, output, input) {
            var horizontal = align.equals(go.Spot.Top) || align.equals(go.Spot.Bottom);
            return $(go.Shape,
                {
                  fill: "transparent",// changed to a color in the mouseEnter event handler
                  strokeWidth: 0,// no stroke
                  width: horizontal ? NaN : 8,// if not stretching horizontally, just 8 wide
                  height: !horizontal ? NaN : 8,// if not stretching vertically, just 8 tall
                  alignment: align,// align the port on the main Shape
                  stretch: (horizontal ? go.GraphObject.Horizontal : go.GraphObject.Vertical),
                  portId: name,// declare this object to be a "port"
                  fromSpot: spot,// declare where links may connect at this port
                  fromLinkable: output,// declare whether the user may draw links from here
                  toSpot: spot,// declare where links may connect at this port
                  toLinkable: input,// declare whether the user may draw links to here
                  cursor: "pointer",// show a different cursor to indicate potential link point
                  mouseEnter: function(e, port) {// the PORT argument will be this Shape
                        if (!e.diagram.isReadOnly) port.fill = "rgba(255,0,255,0.5)";
                  },
                  mouseLeave: function(e, port) {
                        port.fill = "transparent";
                  }
                });
      }

      function textStyle() {
            return {
                font: "bold 11pt Lato, Helvetica, Arial, sans-serif",
                stroke: "#F8F8F8"
            }
      }

      function makeButton(text, action, visiblePredicate) {
            return $("ContextMenuButton",
                $(go.TextBlock, text),
                { click: action },
                visiblePredicate ? new go.Binding("visible", "", function(o, e) { return o.diagram ? visiblePredicate(o, e) : false; }).ofObject() : {});
      }

      var myContextMenu =$("ContextMenu",
            makeButton("✰配置✰",
                function(e, obj) {// OBJ is this Button
                  var contextmenu = obj.part;// the Button is in the context menu Adornment
                  var part = contextmenu.adornedPart;// the adornedPart is the Part that the context menu adorns
                  if (part instanceof go.Link) alert(linkInfo(part.data));
                  else if (part instanceof go.Group) alert(groupInfo(contextmenu));
                  else nodeInfo(part.data);
                }),


function isInArray(arr,value){
            for(var i = 0; i < arr.length; i++){
                if(value === arr){
                  return true;
                }
            }
            return false;
      }

      function nodeInfo(d) {
            var re_url = "{{url_data|safe}}";
            var pro_id = "{{process_id|safe}}";
            var node_key = JSON.parse('{{chart_exist|safe}}');
            if (isInArray(node_key,d.key) == true){
                layui.use('layer', function(){
                  parent.layer.open({
                        type: 2 //Page层类型
                        ,area: ['40%', '70%']
                        ,title: '节点配置'
                        ,shade: 0.6 //遮罩透明度
                        ,anim: 4 //0-6的动画形式,-1不开启
                        ,offset: '8%'
                        ,content:['/Flow/flow_chart_setting.html/'] + "?node_key=" + d.key + "&node_name=" + d.text + "&type=" + re_url + "&process_id=" + pro_id
                  });
                })
            }
            else {
                layui.use('layer', function(){
                  parent.layer.msg('请先保存节点数据!', {icon: 2});
                })
            }
      }

      myDiagram.nodeTemplateMap.add("",// the default category
            $(go.Node, "Table", nodeStyle(),
                { contextMenu: myContextMenu },
                $(go.Panel, "Auto",
                  $(go.Shape, "Rectangle",
                        { fill: "#282c34", stroke: "#00A9C9", strokeWidth: 3.5 },
                        new go.Binding("figure", "figure")),
                  $(go.TextBlock, textStyle(),
                        {
                            margin: 8,
                            maxSize: new go.Size(160, NaN),
                            wrap: go.TextBlock.WrapFit,
                            editable: true
                        },
                        new go.Binding("text").makeTwoWay())
                ),
                makePort("T", go.Spot.Top, go.Spot.TopSide, false, true),
                makePort("L", go.Spot.Left, go.Spot.LeftSide, true, true),
                makePort("R", go.Spot.Right, go.Spot.RightSide, true, true),
                makePort("B", go.Spot.Bottom, go.Spot.BottomSide, true, false)
            ));

      myDiagram.nodeTemplateMap.add("Conditional",
            $(go.Node, "Table", nodeStyle(),
                { contextMenu: myContextMenu },
                $(go.Panel, "Auto",
                  $(go.Shape, "Diamond",
                        { fill: "#282c34", stroke: "#00A9C9", strokeWidth: 3.5 },
                        new go.Binding("figure", "figure")),
                  $(go.TextBlock, textStyle(),
                        {
                            margin: 8,
                            maxSize: new go.Size(160, NaN),
                            wrap: go.TextBlock.WrapFit,
                            editable: true
                        },
                        new go.Binding("text").makeTwoWay())
                ),
                makePort("T", go.Spot.Top, go.Spot.Top, false, true),
                makePort("L", go.Spot.Left, go.Spot.Left, true, true),
                makePort("R", go.Spot.Right, go.Spot.Right, true, true),
                makePort("B", go.Spot.Bottom, go.Spot.Bottom, true, false)
            ));

      myDiagram.nodeTemplateMap.add("Start",
            $(go.Node, "Table", nodeStyle(),
                // { contextMenu: myContextMenu },
                $(go.Panel, "Spot",
                  $(go.Shape, "Circle",
                        { desiredSize: new go.Size(70, 70), fill: "#282c34", stroke: "#09d3ac", strokeWidth: 3.5 }),
                  $(go.TextBlock, "Start", textStyle(),
                        new go.Binding("text"))
                ),
                makePort("L", go.Spot.Left, go.Spot.Left, true, false),
                makePort("R", go.Spot.Right, go.Spot.Right, true, false),
                makePort("B", go.Spot.Bottom, go.Spot.Bottom, true, false)
            ));

      myDiagram.nodeTemplateMap.add("End",
            $(go.Node, "Table", nodeStyle(),
                // { contextMenu: myContextMenu },
                $(go.Panel, "Spot",
                  $(go.Shape, "Circle",
                        { desiredSize: new go.Size(60, 60), fill: "#282c34", stroke: "#DC3C00", strokeWidth: 3.5 }),
                  $(go.TextBlock, "End", textStyle(),
                        new go.Binding("text"))
                ),
                makePort("T", go.Spot.Top, go.Spot.Top, false, true),
                makePort("L", go.Spot.Left, go.Spot.Left, false, true),
                makePort("R", go.Spot.Right, go.Spot.Right, false, true)
            ));

      go.Shape.defineFigureGenerator("File", function(shape, w, h) {
            var geo = new go.Geometry();
            var fig = new go.PathFigure(0, 0, true); // starting point
            geo.add(fig);
            fig.add(new go.PathSegment(go.PathSegment.Line, .75 * w, 0));
            fig.add(new go.PathSegment(go.PathSegment.Line, w, .25 * h));
            fig.add(new go.PathSegment(go.PathSegment.Line, w, h));
            fig.add(new go.PathSegment(go.PathSegment.Line, 0, h).close());
            var fig2 = new go.PathFigure(.75 * w, 0, false);
            geo.add(fig2);
            // The Fold
            fig2.add(new go.PathSegment(go.PathSegment.Line, .75 * w, .25 * h));
            fig2.add(new go.PathSegment(go.PathSegment.Line, w, .25 * h));
            geo.spot1 = new go.Spot(0, .25);
            geo.spot2 = go.Spot.BottomRight;
            return geo;
      });

      myDiagram.nodeTemplateMap.add("Comment",
            $(go.Node, "Auto", nodeStyle(),
                { contextMenu: myContextMenu },
                $(go.Shape, "File",
                  { fill: "#282c34", stroke: "#DEE0A3", strokeWidth: 3 }),
                $(go.TextBlock, textStyle(),
                  {
                        margin: 8,
                        maxSize: new go.Size(200, NaN),
                        wrap: go.TextBlock.WrapFit,
                        textAlign: "center",
                        editable: true
                  },
                  new go.Binding("text").makeTwoWay())
            ));


      myDiagram.linkTemplate =
            $(go.Link,// the whole link panel
                {
                  routing: go.Link.AvoidsNodes,
                  curve: go.Link.JumpOver,
                  corner: 5, toShortLength: 4,
                  relinkableFrom: true,
                  relinkableTo: true,
                  reshapable: true,
                  resegmentable: true,
                  mouseEnter: function(e, link) { link.findObject("HIGHLIGHT").stroke = "rgba(30,144,255,0.2)"; },
                  mouseLeave: function(e, link) { link.findObject("HIGHLIGHT").stroke = "transparent"; },
                  selectionAdorned: false
                },
                new go.Binding("points").makeTwoWay(),
                $(go.Shape,// the highlight shape, normally transparent
                  { isPanelMain: true, strokeWidth: 8, stroke: "transparent", name: "HIGHLIGHT" }),
                $(go.Shape,// the link path shape
                  { isPanelMain: true, stroke: "gray", strokeWidth: 2 },
                  new go.Binding("stroke", "isSelected", function(sel) { return sel ? "dodgerblue" : "gray"; }).ofObject()),
                $(go.Shape,// the arrowhead
                  { toArrow: "standard", strokeWidth: 0, fill: "gray" }),
                $(go.Panel, "Auto",// the link label, normally not visible
                  { visible: false, name: "LABEL", segmentIndex: 2, segmentFraction: 0.5 },
                  new go.Binding("visible", "visible").makeTwoWay(),
                  $(go.Shape, "RoundedRectangle",// the label shape
                        { fill: "#F8F8F8", strokeWidth: 0 }),
                  $(go.TextBlock, "Yes",// the label
                        {
                            textAlign: "center",
                            font: "10pt helvetica, arial, sans-serif",
                            stroke: "#333333",
                            editable: true
                        },
                        new go.Binding("text").makeTwoWay())
                )
            );

      function showLinkLabel(e) {
            var label = e.subject.findObject("LABEL");
            if (label !== null) label.visible = (e.subject.fromNode.data.category === "Conditional");
      }

      myDiagram.toolManager.linkingTool.temporaryLink.routing = go.Link.Orthogonal;
      myDiagram.toolManager.relinkingTool.temporaryLink.routing = go.Link.Orthogonal;

      load();// load an initial diagram from some JSON text

      myPalette =
            $(go.Palette, "myPaletteDiv",// must name or refer to the DIV HTML element
                {
                  "animationManager.initialAnimationStyle": go.AnimationManager.None,
                  "InitialAnimationStarting": animateFadeDown, // Instead, animate with this function

                  nodeTemplateMap: myDiagram.nodeTemplateMap,// share the templates used by myDiagram
                  model: new go.GraphLinksModel([// specify the contents of the Palette
                        { category: "Start", text: "开始" },
                        { text: "步骤" },
                        { category: "Conditional", text: "判断" },
                        { category: "End", text: "结束" },
                  ])
                });

      function animateFadeDown(e) {
            var diagram = e.diagram;
            var animation = new go.Animation();
            animation.isViewportUnconstrained = true;
            animation.easing = go.Animation.EaseOutExpo;
            animation.duration = 900;
            animation.add(diagram, 'position', diagram.position.copy().offset(0, 200), diagram.position);
            animation.add(diagram, 'opacity', 0, 1);
            animation.start();
      }
    }

知心 发表于 2024-1-11 00:37

Summer000 发表于 2024-1-10 21:24
咱论坛代码不好粘贴呀

myDiagram =
    $(go.Diagram, "myDiagramDiv",// must name or refer to the DIV HTML element
      {
            "LinkDrawn": showLinkLabel,// this DiagramEvent listener is defined below
            "LinkRelinked": showLinkLabel,
            "undoManager.isEnabled": true// enable undo & redo
      });

myDiagram.addDiagramListener("Modified", function(e) {
    var button = document.getElementById("SaveButton");
    if (button) button.disabled = !myDiagram.isModified;
    var idx = document.title.indexOf("*");
    if (myDiagram.isModified) {
      if (idx < 0) document.title += "*";
    } else {
      if (idx >= 0) document.title = document.title.substr(0, idx);
    }
});
这还可以吧

青蛙考拉 发表于 2024-1-10 21:43

File "<frozen importlib._bootstrap_external>", line 936, in exec_module
File "<frozen importlib._bootstrap_external>", line 1074, in get_code
File "<frozen importlib._bootstrap_external>", line 1004, in source_to_code
File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
File "C:\Users\1\PycharmProjects\djangoProject\djangoProject\urls.py", line 1
    function init() {
IndentationError: unexpected indent
你确定你做的程序能跑?

Summer000 发表于 2024-1-10 21:24

咱论坛代码不好粘贴呀{:1_926:}

blindcat 发表于 2024-1-11 07:50

学习学习,感谢分享

milu1123 发表于 2024-1-11 08:47

老大。有成品嘛?

15126819695 发表于 2024-1-11 08:51

主要是学习你的项目数据结构的啦   这个东西那几家收费的目前比较有优势

Summer000 发表于 2024-1-11 09:10

青蛙考拉 发表于 2024-1-10 21:43
File "", line 936, in exec_module
File "", line 1074, in get_code
File "", line 1004, in sou ...

必须能跑,代码贴的不全,抽空我会都放上去,

Summer000 发表于 2024-1-11 09:11

milu1123 发表于 2024-1-11 08:47
老大。有成品嘛?

必须有!!一种是直接内部调用审批流的,一种是对接系统封装的外部接口。

weilai8023 发表于 2024-1-11 09:12

大佬,有应用程序成品吗
页: [1] 2 3
查看完整版本: Python 自研审批流引擎,可根据封装的审批引擎接口对接各类表单!!