目录
关于 GooFlow
GooFlow 一个基于 Jquery/FontAwesome 的流程图/架构图画图插件,本文介绍的是基于JS的一个版本,能够适用于大部分支持H5的浏览器。通过创建画布,我们可以在其上通过工具栏绘制想要绘制的流程图/框/线等,如下图所示:
图中包括操作工具栏(顶部)、绘图工具栏(左侧)和绘制画布区域,左上角显示的是当前流程的名称。 具体的操作我们不再详述,本文将介绍通过改造后的 GooFlow 简化版本,结合数据库满足实际的开发需求。
功能需求
我们需要实现一个审批流程,在关键流程节点可能会添加审批人信息,通过GoolFlow绘制并显示,则更加友好和直观, 基本需要实现如下功能:
1、改造 GooFlow JS 程序,简化绘制工具栏。
2、设计相关数据库数据表保存流程图设计相关信息、节点明细信息等。
3、改造 GooFlow 操作工具栏,满足实际应用。
范例运行环境
操作系统: Windows Server 2019 DataCenter
GooFlow 版本:GooFlow V1 JS 版
数据库:Microsoft SQL Server 2016
.net版本: .netFramework4.7.1 或以上
开发工具:VS2019 C#
设计数据表
流程项目表
cc_flow 表记录项目信息,主要说明见下表:
序号 | 字段名 | 类型 | 说明 |
---|---|---|---|
1 | cid | uniqueidentifier | 记录唯一标识 |
2 | flowName | nvarchar(50) | 流程项目名称 |
3 | nodeCount | int | 节点总个数 |
4 | desript | nvarchar(50) | 项目描述 |
5 | flowJSON | nvarchar(MAX) | 流程全部 JSON 数据 |
6 | sys_insuser | nvarchar(100) | 创建信息用户名 |
7 | sys_instime | datetime | 创建时间 |
8 | sys_upduser | nvarchar(100) | 最后修改信息用户名 |
9 | sys_updtime | datetime | 修改时间 |
创建脚本如下:
CREATE TABLE [dbo].[cc_flow](
[cid] [uniqueidentifier] ROWGUIDCOL NOT NULL,
[flowName] [nvarchar](50) NOT NULL,
[nodeCount] [int] NULL,
[desript] [nvarchar](100) NULL,
[flowJSON] [nvarchar](max) NULL,
[sys_insuser] [nvarchar](100) NULL,
[sys_instime] [datetime] NULL,
[sys_upduser] [nvarchar](100) NULL,
[sys_updtime] [datetime] NULL,
CONSTRAINT [PK_cc_flow] PRIMARY KEY CLUSTERED
(
[cid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY],
CONSTRAINT [IX_cc_flow] UNIQUE NONCLUSTERED
(
[flowName] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[cc_flow] ADD CONSTRAINT [DF_cc_flow_cid] DEFAULT (newid()) FOR [cid]
GO
流程项目节点明细表
cc_flowNodes 表记录了项目绘制节点的所有信息明细情况,主要说明见下表:
序号 | 字段名 | 类型 | 说明 |
---|---|---|---|
1 | cid | uniqueidentifier | 记录唯一标识 |
2 | nodeId | nvarchar(50) | 节点ID |
3 | nodeName | nvarchar(50) | 节点流程名称 |
4 | desript | nvarchar(100) | 节点流程描述 |
5 | parentNodeId | nvarchar(50) | 父节点ID |
6 | projectCid | uniqueidentifier | 流程项目ID,所属的项目唯一标识 |
7 | sortid | int | 节点排序 |
8 | sys_insuser | nvarchar(100) | 创建信息用户名 |
9 | sys_instime | datetime | 创建时间 |
10 | sys_upduser | nvarchar(100) | 最后修改信息用户名 |
11 | sys_updtime | datetime | 修改时间 |
创建脚本如下:
CREATE TABLE [dbo].[cc_flowNodes](
[cid] [uniqueidentifier] NOT NULL,
[nodeId] [nvarchar](50) NOT NULL,
[nodeName] [nvarchar](50) NOT NULL,
[desript] [nvarchar](100) NULL,
[parentNodeId] [nvarchar](50) NULL,
[projectCid] [uniqueidentifier] NOT NULL,
[sortid] [int] NULL,
[sys_insuser] [nvarchar](100) NULL,
[sys_instime] [datetime] NULL,
[sys_upduser] [nvarchar](100) NULL,
[sys_updtime] [datetime] NULL
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[cc_flowNodes] ADD CONSTRAINT [DF_cc_flowNodes_cid] DEFAULT (newid()) FOR [cid]
GO
流程项目节点审批人表
cc_flowNodeAccounts 表记录了项目关键节点的参与审批人情况,主要说明见下表:
序号 | 字段名 | 类型 | 说明 |
---|---|---|---|
1 | cid | uniqueidentifier | 记录唯一标识 |
2 | nodeId | nvarchar(50) | 节点ID |
3 | AccountCid | uniqueidentifier | 所属的人员信息ID |
4 | desript | nvarchar(100) | 描述,可以记录诸如姓名相关的一些信息 |
5 | role | nvarchar(50) | 审批人权限(如决策、抄送) |
6 | projectCid | uniqueidentifier | 流程项目ID,所属的项目唯一标识 |
7 | sys_insuser | nvarchar(100) | 创建信息用户名 |
8 | sys_instime | datetime | 创建时间 |
9 | sys_upduser | nvarchar(100) | 最后修改信息用户名 |
10 | sys_updtime | datetime | 修改时间 |
创建脚本如下:
CREATE TABLE [dbo].[cc_flowNodeAccounts](
[cid] [uniqueidentifier] NOT NULL,
[projectCid] [uniqueidentifier] NOT NULL,
[AccountCid] [uniqueidentifier] NOT NULL,
[descript] [nvarchar](100) NULL,
[nodeId] [nvarchar](50) NOT NULL,
[role] [nvarchar](50) NULL,
[sys_insuser] [nvarchar](100) NULL,
[sys_instime] [datetime] NULL,
[sys_upduser] [nvarchar](100) NULL,
[sys_updtime] [datetime] NULL
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[cc_flowNodeAccounts] ADD CONSTRAINT [DF_cc_flowNodeBuss_cid] DEFAULT (newid()) FOR [cid]
GO
人员信息表
accounts 表审批人的基本个人情况,主要说明见下表:
序号 | 字段名 | 类型 | 说明 |
---|---|---|---|
1 | cid | uniqueidentifier | 记录唯一标识 |
2 | name | nvarchar(50) | 姓名 |
3 | nickName | nvarchar(50) | 昵称 |
创建脚本如下:
CREATE TABLE [dbo].[accounts](
[cid] [uniqueidentifier] ROWGUIDCOL NOT NULL,
[name] [nvarchar](50) NULL,
[nickname] [nvarchar](500) NULL,
CONSTRAINT [PK_wxmp_accounts] PRIMARY KEY CLUSTERED
(
[cid] ASC
)
)
GO
ALTER TABLE [dbo].[accounts] ADD CONSTRAINT [DF_wxmp_accounts_cid] DEFAULT (newid()) FOR [cid]
GO
示例代码
流程图主功能
本示例代码包含后端、前端及 JS 代码,代码如下:
<%@ Page Language="C#" AutoEventWireup="true" MaintainScrollPositionOnPostback="true" ValidateRequest="FALSE" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<script language="C#" runat="server">
public CosysJaneCommonAPI.CODAL dal = new CosysJaneCommonAPI.CODAL();
public DateTime dt = DateTime.Now;
void Page_load(Object sender, EventArgs e)
{
dal.RunAt = Form;
if (Page.IsPostBack == true) return;
dal.ConnKeyString = "JaneConnectionCloud";
ArrayList paras = new ArrayList();
paras.Clear();
paras.Add(new SqlParameter("a", "2"));
dal.simpledatalist("select cid,flowName from cc_flow order by flowName ", paras, "cid", "flowName",flowlist, true, "", "选择打开已创建流程");
}
protected void saveflow_Click(object sender, EventArgs e)
{
dal.ConnKeyString = "JaneConnectionCloud";
ArrayList paras = new ArrayList();
paras.Clear();
paras.Add(new SqlParameter("flowname", x_flowname.Text));
dal.RowsCount = 0;
dal.ErrorMessage = "";
dal.ExecDbScripts("insert into cc_flow (flowName) values(@flowname) ", paras);
if (dal.RowsCount > 0)
{
tip.Text = "创建成功!";
flowpanel.Visible = true;
}else
{
if (dal.ErrorMessage.IndexOf("重复键") != -1)
{
dal.RowsCount = 0;
dal.ErrorMessage = "";
paras.Clear();
paras.Add(new SqlParameter("flowname", x_flowname.Text));
object rv = dal.GetDataSet("select * from cc_flow where flowname=@flowname ", paras);
if (dal.RowsCount > 0)
{
DataSet ds = rv as DataSet;
DataTable dt = ds.Tables[0];
x_cid.Text = dt.Rows[0]["cid"].ToString();
x_flowJSON.Text = dt.Rows[0]["flowJSON"].ToString();
tip.Text = "打开成功!";
flowpanel.Visible = true;
}
else
{
tip.Text = dal.ErrorMessage;
flowpanel.Visible = false;
}
}
else
{
tip.Text = dal.ErrorMessage;
flowpanel.Visible = false;
}
}
}
protected void updflow_Click(object sender, EventArgs e)
{
dal.ConnKeyString = "JaneConnectionCloud";
ArrayList paras = new ArrayList();
Newtonsoft.Json.Linq.JObject jsonObj = Newtonsoft.Json.Linq.JObject.Parse(x_flowJSON.Text);
string nodelist = "";
foreach (Newtonsoft.Json.Linq.JProperty jt in jsonObj["nodes"])
{
nodelist+=jt.Name+",";
}
string cid = x_cid.Text;
dal.ConnKeyString = "JaneConnectionCloud";
paras.Clear();
paras.Add(new SqlParameter("cid", cid));
dal.RowsCount = 0;
dal.ErrorMessage = "";
dal.ExecDbScripts("delete cc_flowNodes where projectCid=@cid ", paras);
string[] la = nodelist.Split(',');
int added = 0;
for (int i = 0; i < la.GetLength(0); i++)
{
if (la[i] != "")
{
paras.Clear();
paras.Add(new SqlParameter("projectCid", cid));
paras.Add(new SqlParameter("parentNodeId", (i == 0 ? cid : la[i - 1])));
paras.Add(new SqlParameter("nodeId", la[i]));
paras.Add(new SqlParameter("nodeName", jsonObj["nodes"][la[i]]["name"].ToString()));
paras.Add(new SqlParameter("sortid", i + 1));
dal.RowsCount = 0;
dal.ErrorMessage = "";
dal.ExecDbScripts("insert into cc_flowNodes(projectCid,parentNodeId,nodeId,nodeName,sortid) values(@projectCid,@parentNodeId,@nodeId,@nodeName,@sortid) ", paras);
if (dal.RowsCount > 0)
{
added++;
}
}
}
paras.Clear();
paras.Add(new SqlParameter("cid", x_cid.Text));
paras.Add(new SqlParameter("flowName",x_flowname.Text));
paras.Add(new SqlParameter("flowJSON", x_flowJSON.Text));
dal.RowsCount = 0;
dal.ErrorMessage = "";
dal.ExecDbScripts("update cc_flow set flowName=@flowname,flowJSON=@flowJSON where cid=@cid ", paras);
if (dal.RowsCount > 0)
{
tip.Text = "更新成功!";
}
else
{
tip.Text = dal.ErrorMessage;
}
}
protected void flowlist_SelectedIndexChanged(object sender, EventArgs e)
{
if (flowlist.SelectedValue == "")
{
return;
}
dal.ConnKeyString = "JaneConnectionCloud";
ArrayList paras = new ArrayList();
dal.RowsCount = 0;
dal.ErrorMessage = "";
paras.Clear();
paras.Add(new SqlParameter("cid",flowlist.SelectedValue));
object rv = dal.GetDataSet("select * from cc_flow where cid=@cid ", paras);
if (dal.RowsCount > 0)
{
DataSet ds = rv as DataSet;
DataTable dt = ds.Tables[0];
x_cid.Text = dt.Rows[0]["cid"].ToString();
x_flowname.Text = dt.Rows[0]["flowName"].ToString();
x_flowJSON.Text = dt.Rows[0]["flowJSON"].ToString();
tip.Text = "打开成功!";
flowpanel.Visible = true;
}
else
{
tip.Text = dal.ErrorMessage;
flowpanel.Visible = false;
}
}
</script>
<!DOCTYPE html>
<html lang="cn" >
<head>
<meta charset="UTF-8">
<title></title>
<link rel="stylesheet" href="dist/GooFlow.min.css?v=10" />
<link rel="stylesheet" href="fontawesome/css/font-awesome.min.css" />
</head>
<script src="dist/jquery.min.js"></script>
<script src="dist/GooFlow.js?v=7"></script>
<body>
<form runat="server">
<div style=" display:flex">
<asp:TextBox ID="x_flowname" placeholder="输入项目名称" runat="server"></asp:TextBox>
<asp:Button ID="saveflow" Text="创建项目" runat="server" onclick="saveflow_Click"></asp:Button>
<asp:Button ID="updflow" Text="更新项目" style=" display:none" runat="server" onclick="updflow_Click" ></asp:Button>
<asp:TextBox ID="x_cid" placeholder="cid" style="display:none" ReadOnly="true" runat="server"></asp:TextBox>
<asp:DropDownList ID="flowlist" runat="server" AutoPostBack="true"
onselectedindexchanged="flowlist_SelectedIndexChanged"></asp:DropDownList>
</div>
<div style=" margin:10px; display:flex">
<asp:Label ID="tip" Text="这是一些提示信息..." runat="server"></asp:Label>
</div>
<iframe id="tframe" width="500" height="400" style=" display:none;z-index:99;position:absolute;" runat="server"></iframe>
<div id="flowpanel" visible="false" runat="server">
</div>
<div>
<asp:TextBox ID="x_flowJSON" Rows="10" style="display:none" width="100%" TextMode="MultiLine" runat="server"></asp:TextBox>
</div>
</form>
</body>
</html>
<script language="javascript" type="text/javascript">
var tframe=$('#tframe');
var curnode=null;
var flowpanel=null;
var sortBy = function (filed, rev, primer) {
rev = (rev) ? -1 : 1;
return function (a, b) {
a = a[filed];
b = b[filed];
if (primer && typeof (primer) != 'undefined') {
a = primer(a);
b = primer(b);
}else{
if(!isNaN(a) && !isNaN(a)){
a = Number(a);
b = Number(b);
}
}
if (a < b) { return rev * -1; }
if (a > b) { return rev * 1; }
return 1;
}
};
$(function(){
var options={initLabelText:"未命名",toolBtns:["task","chat","state"],toolBtnRemarks:{cursor:"选择指针",direct:"连线",task:"任务结点",chat:"决策结点",state:"状态结点",group:"组织区域框"},headBtns:["save","undo","redo","new","expand"],headBtnRemarks:['保存','撤销','重做','清空画布','结点设置']};
flowpanel = $('#flowpanel').createGooFlow(options);
var flowpanelData = {
areas: {
flowpanel_area_17: {
color: "yellow",
height: 300,
left: 40,
name: "部门一",
top: 30,
width: 150,
},
flowpanel_area_18: {
color: "blue",
height: 300,
left: 200,
name: "部门二",
top: 30,
width: 150,
},
flowpanel_area_19: {
color: "green",
height: 300,
left: 360,
name: "部门三",
top: 30,
width: 150,
},
flowpanel_area_20: {
color: "red",
height: 300,
left: 520,
name: "部门四",
top: 30,
width: 150,
},
},
lines: {
flowpanel_line_11: {
from: "flowpanel_node_1",
marked: false,
name: "",
to: "flowpanel_node_2",
type: "sl",
},
},
nodes: {
flowpanel_node_1: {
height: 24,
left: 70,
name: "节点流程1",
top: 66,
type: "chat",
width: 86,
sortid:1,
id:"flowpanel_node_1",
},
flowpanel_node_2: {
height: 24,
left: 240,
name: "节点流程2",
top: 132,
type: "state",
width: 86,
sortid:2,
id:"flowpanel_node_2",
},
}
};
flowpanel.loadData(flowpanelData);
if($('#x_flowJSON').val()!=""){
flowpanel.clearData();
flowpanel.loadData($.parseJSON($('#x_flowJSON').val()));
alert("加载流程图成功");
}
flowpanel.setTitle($('#x_flowname').val());
$('#flowpanel').css('backgroundColor','silver');
$('#flowpanel').css('borderColor','silver');
var obj = flowpanel.exportData();
flowpanel.onExpandClick=function(){
if(curnode==null) return;
var x=(curnode.offset().left+parseInt(curnode.css('width'),10))+'px';
var y=curnode.offset().top+'px';
tframe.css('left',x);
tframe.css('top',y);
tframe.attr('src','test_server.aspx?cid='+$('#x_cid').val()+'&nid='+curnode.selector.substring(1));
tframe.css('display',(tframe.css('display')=='none'?'':'none'));
}
flowpanel.onBtnSaveClick=function(){
$('#x_flowJSON').val(JSON.stringify(this.exportData()));
$('#updflow').click();
}
flowpanel.onItemFocus=function(id,type){
flowpanel.focusItem(id,false);
curnode=$("#"+id);
tframe.css('display','none');
}
});
</script>
设置审批人信息
页面名称为test_server.aspx,在流程图主功能的JS代码部分有体现, 本示例代码包含后端、前端及 JS 代码,代码如下:
<%@ Page Language="C#" AutoEventWireup="true" enableEventValidation="false" MaintainScrollPositionOnPostback="true" ValidateRequest="FALSE" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<script language="C#" runat="server">
public CosysJaneCommonAPI.CODAL dal = new CosysJaneCommonAPI.CODAL();
public DateTime dt = DateTime.Now;
void Page_load(Object sender, EventArgs e)
{
dal.RunAt = Form;
if (Page.IsPostBack == true) return;
ArrayList paras = new ArrayList();
dal.simpledatalist("select cid,nickname,name from accounts ", null, "cid", "name", sprlist, false, "", "");
dal.simpledatalist("select cid,'抄送' name from accounts ", null, "cid", "name", spr2list, false, "", "");
dal.RowsCount = 0;
dal.ErrorMessage = "";
string cid = Request.QueryString["cid"];
string nodeid = Request.QueryString["nid"];
paras.Clear();
paras.Add(new SqlParameter("cid", cid));
paras.Add(new SqlParameter("nodeid", nodeid));
object rv = dal.GetDataSet("select AccountCid,nodeId,role from cc_flowNodeAccounts where projectCid=@cid and nodeId=@nodeid", paras);
if (dal.RowsCount > 0)
{
DataTable dt = (rv as DataSet).Tables[0];
for (int i = 0; i < dt.Rows.Count; i++)
{
string accountid = dt.Rows[i]["AccountCid"].ToString();
string role = dt.Rows[i]["role"].ToString();
ListItem li = sprlist.Items.FindByValue(accountid);
if (li!=null)
{
li.Selected = true;
if(role=="抄送"){
spr2list.Items.FindByValue(accountid).Selected = true;
}
}
}
}
}
protected void saveflow_Click(object sender, EventArgs e)
{
string cid = Request.QueryString["cid"];
string nodeid = Request.QueryString["nid"];
ArrayList paras = new ArrayList();
paras.Clear();
paras.Add(new SqlParameter("cid", cid));
paras.Add(new SqlParameter("nodeid", nodeid));
dal.RowsCount = 0;
dal.ErrorMessage = "";
dal.ExecDbScripts("delete cc_flowNodeAccounts where projectCid=@cid and nodeId=@nodeid", paras);
for (int i = 0; i < sprlist.Items.Count; i++)
{
ListItem li = sprlist.Items[i];
if (li.Selected==true)
{
paras.Clear();
paras.Add(new SqlParameter("projectCid", cid));
paras.Add(new SqlParameter("AccountCid", li.Value));
paras.Add(new SqlParameter("nodeid", nodeid));
paras.Add(new SqlParameter("role", (spr2list.Items[i].Selected==true?"抄送":"决策") ));
paras.Add(new SqlParameter("descript", li.Text));
dal.RowsCount = 0;
dal.ErrorMessage = "";
dal.ExecDbScripts("insert into cc_flowNodeAccounts(projectCid,AccountCid,nodeId,role,descript) values(@projectCid,@AccountCid,@nodeid,@role,@descript) ", paras);
Response.Write(dal.ErrorMessage);
}
}
Alert(Form, "保存数据信息完成!");
}
protected void updflow_Click(object sender, EventArgs e)
{
ArrayList paras = new ArrayList();
paras.Clear();
paras.Add(new SqlParameter("cid", x_cid.Text));
}
protected void tooltab_SelectedIndexChanged(object sender, EventArgs e)
{
spr.Style["display"] = "none";
sortnode.Style["display"] = "none";
if (tooltab.SelectedValue == "0")
{
spr.Style["display"] = "";
}else if (tooltab.SelectedValue == "1")
{
sortnode.Style["display"] = "";
}
}
void SortRow(Object sender, EventArgs e)
{
Newtonsoft.Json.Linq.JObject jsonObj = Newtonsoft.Json.Linq.JObject.Parse(x_flowJSON.Text);
string cid = Request.QueryString["cid"];
ArrayList paras = new ArrayList();
paras.Clear();
paras.Add(new SqlParameter("cid", cid));
dal.RowsCount = 0;
dal.ErrorMessage = "";
dal.ExecDbScripts("delete cc_flowNodes where projectCid=@cid ", paras);
string[] la = sortstate1.Text.Split(',');
int added = 0;
for (int i = 0; i < la.GetLength(0); i++)
{
if (la[i] != "")
{
paras.Clear();
paras.Add(new SqlParameter("projectCid", cid));
paras.Add(new SqlParameter("parentNodeId",(i==0?cid:la[i-1])) );
paras.Add(new SqlParameter("nodeId", la[i]));
paras.Add(new SqlParameter("nodeName", jsonObj["nodes"][la[i]]["name"].ToString()));
paras.Add(new SqlParameter("sortid", i+1));
dal.RowsCount = 0;
dal.ErrorMessage = "";
dal.ExecDbScripts("insert into cc_flowNodes(projectCid,parentNodeId,nodeId,nodeName,sortid) values(@projectCid,@parentNodeId,@nodeId,@nodeName,@sortid) ", paras);
if (dal.RowsCount > 0)
{
added++;
}
}
}
paras.Clear();
paras.Add(new SqlParameter("cid", cid));
paras.Add(new SqlParameter("flowJSON", x_flowJSON.Text));
paras.Add(new SqlParameter("nodeCount", added));
dal.RowsCount = 0;
dal.ErrorMessage = "";
dal.ExecDbScripts("update cc_flow set flowJSON=@flowJSON,nodeCount=@nodeCount where cid=@cid ", paras);
if (dal.RowsCount > 0)
{
}
else
{
Alert(Form, "保存流程数据信息失败!");
return;
}
Alert(Form,"保存排序信息完毕!");
}
public void Alert(Control updatePanel, string msg)
{
msg = msg.Replace("\r\n", "").Replace("'", "\\'");
ScriptManager.RegisterClientScriptBlock(updatePanel, this.GetType(), "", "alert('" + msg + "')", true);
}
</script>
<!DOCTYPE html>
<html lang="cn" >
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body style=" background-color:Gray">
<form runat="server">
<div style=" display:flex">
<asp:RadioButtonList ID="tooltab" AutoPostBack="true" Font-Names="微软雅黑"
RepeatDirection="Horizontal" runat="server"
onselectedindexchanged="tooltab_SelectedIndexChanged" >
<asp:ListItem Value="0" Text="审批人" Selected="True"></asp:ListItem>
<asp:ListItem Value="1" Text="节点排序" ></asp:ListItem>
</asp:RadioButtonList>
</div>
<asp:Panel ID="spr" runat="server" Visible="true" BackColor="Silver" >
<div style=" display:flex">
<asp:CheckBoxList ID="sprlist" runat="server" Height="100px" ForeColor="White" Width="134px">
</asp:CheckBoxList>
<asp:CheckBoxList ID="spr2list" runat="server" Height="100px" BackColor="Gray" ForeColor="Silver" Width="134px">
</asp:CheckBoxList>
<asp:Button ID="updflow" Text="更新项目" style=" display:none" runat="server" onclick="updflow_Click" ></asp:Button>
<asp:TextBox ID="x_cid" style=" display:none" ReadOnly="true" runat="server"></asp:TextBox>
</div>
<div style=" display:flex">
<asp:Button ID="saveflow" style=" display:none1" Text="保存审批人" runat="server" onclick="saveflow_Click"></asp:Button>
</div>
</asp:Panel>
<asp:Panel ID="sortnode" runat="server" Visible="true" style="display:none" BackColor="Silver">
<table border="0" width="100%" cellspacing="0" cellpadding="0" id="table1">
<tr>
<td valign="top">
<div align="center">
<table border="0" width="94%" cellspacing="0" cellpadding="0" id="table2">
<tr>
<td align="left" valign="top">
<asp:listbox id="funclist" ondblclick="SetValues(this)"
style="border: 1px outset #6699FF; background-color: #FFFFFF;" Runat="server"
Height="328px" Width="405px" Font-Names="微软雅黑"/></td>
<td>
<p align="center">
<input id="moveUp" onclick="moveSelected(document.getElementById('funclist'), false)" type=button style="font-family:微软雅黑;" value="上移"/></p>
<p align="center">
<input id="moveDown" onclick="moveSelected(document.getElementById('funclist'), true)" type=button style="font-family:微软雅黑;" value="下移"/></p>
<p align="center"><input id="moveDown0" onclick="sortfunc()" type="button" value="保存" style="font-family:微软雅黑;"/></p></td>
</tr>
</table>
</div>
</td>
</tr>
</table>
</asp:Panel>
<asp:TextBox ID="sortstate" Text="" style="display:none" Runat=server/>
<asp:TextBox ID="sortstate1" Text="" style="display:none" Runat="server"/>
<asp:Button ID="sortbtn" Text="sort" OnClick="SortRow" style="display:none" Runat="server"/>
<asp:TextBox ID="x_flowJSON" Rows="10" style="display:none" width="100%" TextMode="MultiLine" runat="server"></asp:TextBox>
</form>
</body>
</html>
<script language="javascript" type="text/javascript">
form = document.forms[0];
function sortfunc() {
if (!confirm('您确认保存排序结果吗?')) {
return;
}
form.sortstate1.value = "";
for (var i = 0; i < form.funclist.length; i++) {
var _id = form.funclist.options[i].value;
flowpanel.$nodeData[_id].sortid = i;
form.sortstate1.value += _id + ",";
}
// return;
form.sortstate.value = "1";
var obj = flowpanel.exportData();
form.x_flowJSON.value = JSON.stringify(obj);
form.sortbtn.click();
}
function AddListBoxOption(obj, _text, _value) {
newOption = document.createElement("OPTION");
newOption.text = _text;
newOption.value = _value;
obj.options.add(newOption);
}
function moveSelected(select, down) {
if (select.selectedIndex != -1) {
if (down) {
if (select.selectedIndex != select.options.length - 1)
var i = select.selectedIndex + 1;
else
return;
}
else {
if (select.selectedIndex != 0)
var i = select.selectedIndex - 1;
else
return;
}
var swapOption = new Object();
swapOption.text = select.options[select.selectedIndex].text;
swapOption.value = select.options[select.selectedIndex].value;
swapOption.selected = select.options[select.selectedIndex].selected;
// swapOption.defaultSelected = select.options[select.selectedIndex].defaultSelected;
for (var property in swapOption)
select.options[select.selectedIndex][property] = select.options[i][property];
for (var property in swapOption)
select.options[i][property] = swapOption[property];
}
}
function jsonSort(array, field, reverse) {
//数组长度小于2 或 没有指定排序字段 或 不是json格式数据
if (array.length < 2 || !field || typeof array[0] !== "object") return array;
//数字类型排序
if (typeof array[0][field] === "number") {
array.sort(function (x, y) { return x[field] - y[field] });
}
//字符串类型排序
if (typeof array[0][field] === "string") {
array.sort(function (x, y) { return x[field].localeCompare(y[field]) });
}
//倒序
if (reverse) {
array.reverse();
}
return array;
}
var flowpanel = (window.parent.flowpanel);
window.onload = function () {
var obj = flowpanel.exportData();
form.x_flowJSON.value = JSON.stringify(obj);
var nodelist = document.getElementById('funclist');
nodelist.length = 0;
var sortid = 0;
var obj2 = new Array();
for (var i in obj.nodes) {
flowpanel.$nodeData[i].id = i;
obj2.push(flowpanel.$nodeData[i]);
}
var obj3 = jsonSort(obj2, 'sortid', false);
for (var j = 0; j < obj3.length;j++ ) {
flowpanel.$nodeData[obj3[j].id].sortid = sortid;
AddListBoxOption(nodelist, obj3[j].name, obj3[j].id);
sortid++;
}
}
</script>
运行结果演示
代码正常运行后如下图所示:
点击某一节点,点击控制栏最后的设置图标,会提供选择审批人的操作界面,如下图所示:
总结
关于 GooFlow 的引用,请下载我的资源:
https://download.csdn.net/download/michaelline/89601233
CosysJaneCommonAPI.CODAL 类的 dal.simpledatalist 方法需要在实际中自行改造,可参考我的文章:
《C# Web控件与数据感应之 ListControl 类》
CosysJaneCommonAPI.CODAL 类的 dal.GetDataSet 方法需要在实际中自行改造,可参考我的文章:
CosysJaneCommonAPI.CODAL 类的 dal.ExecDbScripts 方法需要在实际中自行改造,可参考我的文章:
代码这里仅供大家参考,欢迎大家评论指教!