2023年2月

我在较早之前的随笔《
基于MVC4+EasyUI的Web开发框架形成之旅--附件上传组件uploadify的使用
》Web框架介绍中介绍了基于Uploadify的文件上传操作,免费版本用的是Jquery+Flash实现文件的上传处理,HTML5收费版本的没有试过。随着Flash逐渐退出整个环境,很多浏览器都可能不再支持Flash的了,因此在更新原有EasyUI整个框架的时候,我们对框架全面进行了完善,包括替换了这个Uploadify的文件上传模块,使用EasyUI内置的FileBox控件,结合Jquery的Ajax操作,我们就可以实现了单文件或者多文件的上传操作了。本篇随笔就主要基于这个主题,介绍基于FileBox控件实现文件上传处理,包括前端页面代码和后端的C#处理代码的操作。

1、FileBox控件的使用

FileBox控件原理上是一个TextBox控件和隐藏的一个File控件,因此对于TextBox控件的变化时间onchange依旧可以使用,而由于其隐藏了File控件,那么需要根据DOM规则获取其下面的File控件,从而可以获得文件列表进行Ajax的上传操作,同时由于File文件是一个特殊的内容,我们在上传的时候,使用了JS的FormData对象容器来进行承载文件信息和其他信息,从而能够顺利把文件及其他内容信息一并通过AJax调用,上传到后台去处理。

按照官方的示例,其实就是定义一个HTML元素,并通过脚本方式初始化对应的样式和事件即可,如下所示。

<inputclass="easyui-filebox"style="width:300px">
//初始化脚本
$('#fb').filebox({
buttonText:
'选择文件',
buttonAlign:
'left'})

这样就可以了。

我们来看看我的实际案例,一般需要上传附件的地方,包括导入Excel文件的记录,或者上传文件作为记录的附件,前者一般是单文件的方式,一次导入一个Excel文件,并加载Excel进行展示,如下所示。

文件控件的部分代码如下所示

        <divid="tb"style="padding:5px;height:auto">
        <!-------------------------------搜索框----------------------------------->
            <fieldset>
                <legend>Excel导入操作</legend>
                <formid="ffSearch"method="post"enctype="multipart/form-data">
                    <divtitle="Excel导入操作"style="padding: 5px"data-options="iconCls:'icon-key'">
                        <inputclass="easyui-validatebox"type="hidden"id="AttachGUID"name="AttachGUID" />
                        <br/>
                        <inputclass="easyui-filebox"id="file_upload"style="width:300px" />
                    </div>
                </form>
                <divid="div_files"></div>
            </fieldset>
                
        <!-------------------------------详细信息展示表格----------------------------------->
        <tableid="grid"title="用户操作"data-options="iconCls:'icon-view'">            
        </table>
    </div>

和前面Demo的定义类似,这里只是声明了一个FileBox控件,主要还是通过JS代码来进行初始化,和相关的控制。

    <scripttype="text/javascript">$(function() {//添加对话框,上传控件初始化
$('#file_upload').filebox({
buttonText:
'选择文件',//按钮文本 buttonAlign:'right',//按钮对齐 //multiple: true, //是否多文件方式 //accept: "image/gif,image/bmp,image/jpeg,image/jpg,image/png", //指定文件类型 onChange:function(e) {
UploadFile(
this,"file_upload","AttachGUID","div_files");//上传处理 }
});
});
</script>

通过JS的控制,就可以较好的呈现文件上传的控件了,比不用EasyUI的样式好看很多。

通过onchange的控制,可以对文件操作及时进行响应,如我这里选择文件后,马上进行上传,并进行数据的读取和显示在Datagrid里面。

文件上传的JS逻辑代码如下所示,全部贴出来供参考。

        //上传文件操作
        functionUploadFile(_obj, file_ctrlname, guid_ctrlname, div_files) {var value = $("#" + file_ctrlname).filebox('getValue');var files = $("#" + file_ctrlname).next().find('input[type=file]')[0].files;//console.log(files);

            //传入this参数,也可以用这个获取文件
            //var files = $(_obj).context.ownerDocument.activeElement.files;
            //console.log(files);
            
            var guid = $("#" +guid_ctrlname).val();if (value && files[0]) {
                //构建一个FormData存储复杂对象
                var formData = newFormData();
formData.append(
"folder", '数据导入文件');
formData.append(
"guid", guid);
formData.append(
'Filedata', files[0]);//默认的文件数据名为“Filedata” $.ajax({
url:
'/FileUpload/Upload', //单文件上传 type: 'POST',
processData:
false,
contentType:
false,
data: formData,
success:
function(json) {//转义JSON为对象 var data =$.parseJSON(json);//提示用户Excel格式是否正常,如果正常加载数据 ShowUpFiles(guid, div_files);

$.ajax({
url:
'/BillDetail_Cust/CheckExcelColumns?guid=' +guid,
type:
'get',
dataType:
'json',
success:
function(data) {if(data.Success) {
InitGrid();
//重新刷新表格数据 showTips("文件已上传,数据加载完毕!");
}
else{
showTips(
"上传的Excel文件检查不通过。请根据页面右上角的Excel模板格式进行数据录入。");
}
}
});
},
error:
function(xhr, status, error) {
$.messager.alert(
"提示", "操作失败"); //xhr.responseText }
});
}
}

我们这里使用了FormData来存储文件复杂对象。

var formData = new FormData();

通过查询控件的子DOM对象,我们获得File控件对象,并获取控件里面的Files属性。

var files = $("#" + file_ctrlname).next().find('input[type=file]')[0].files;

文件上传后,我们在文件框下面显示已经上传的文件列表,如下代码所示。

ShowUpFiles(guid, div_files);

文件上传到服务器后,我们通过接口获取对应的Excel文件,并转换为JSON列表返回,供页面绑定数据展示。

InitGrid(); //重新刷新表格数据

上传文件展示列表信息,整体界面效果如下所示。

单击删除,弹出确认删除对话框后移除文件即可。

上面通过限定,使用了一个文件,以及可以指定上传文件的格式等,如果我们使用多文件上传,客户端只需要设置Multiple为True即可,后端也不需要调整接口 ,使用同一个接口进行处理即可。

多文件的上传场景,一般是某个记录需要上传文件的情况下,在录入或者编辑界面提供文件上传处理,如下所示。

我们这里可以选择多个文件进行一并上传,初始化fileBox控件的时候,只需要指定multiple为true即可。

            //添加对话框,上传控件初始化
            $('#file_upload').filebox({
buttonText:
'选择文件', //按钮文本 buttonAlign: 'right', //按钮对齐 multiple: true,//accept: "image/gif,image/bmp,image/jpeg,image/jpg,image/png", //指定文件类型 onChange: function(e) {
UploadFile(
this, "file_upload", "Attachment_GUID", "div_files");//上传处理 }
});

文件的上传和前面操作类似,只不过,我们这次添加了多个文件。

    //构建一个FormData存储复杂对象
    var formData = newFormData();
formData.append(
"folder", '政策法规');
formData.append(
"guid", guid);for (var i = 0; i < files.length; i++) {
formData.append(
'Filedata', files[i]);//注意:默认的文件数据名为“Filedata” }

最后也是统一通过ajax进行条用处理

$.ajax({
url:
'/FileUpload/Upload',
type:
'POST',
processData:
false,
contentType:
false,
data: formData,
success:
function(json) {ShowUpFiles(guid, div_files);//完成后更新已上传的文件列表 showTips("上传完毕!"); //提示完成 },
error:
function(xhr, status, error) {
$.messager.alert(
"提示", "操作失败"); //xhr.responseText }
});

以上就是使用EasyUI的FileBox控件进行处理的前端界面部分。

2、文件上传的后端代码处理

前面小节介绍了控件前端的使用,包括HTML代码的定义和JS的初始化,以及使用AJax异步方式上传文件并展示出来等操作。

文件的上传成功,其实也是需要后端的接口处理,需要对文件进行存储,和数据库信息的更新等操作。

前面我们通过AJax操作,在前端通过JS的方式传输FormData的内容。

$.ajax({
url:
'/FileUpload/Upload',
type:
'POST',
processData:
false,
contentType:
false,
data: formData,
success:
function(json) {//转义JSON为对象 //var data = $.parseJSON(json); ShowUpFiles(guid, div_files);//完成后更新已上传的文件列表 showTips("上传完毕!"); //提示完成 },
error:
function(xhr, status, error) {
$.messager.alert(
"提示", "操作失败"); //xhr.responseText }
});

其中/FileUpload/Upload就是处理的路径地址,我们来看看MVC里面的FileUploadController对应方法Upload即可。

先来看看定义和主要处理文件的部分内容。

我们通过HttpContext.Request.Files 获得多个文件的集合,并进行处理即可。

文件的处理,我们先读取文件内容到对象里面,然后使用了业务类进行统一的存储文件和数据库信息即可。

文件上传成功后,我们返回对应的地址给前端使用(如果需要的话),完整的上传处理文件的后端代码如下所示。

        /// <summary>
        ///多文件上传处理/// </summary>
        /// <param name="guid">附件组GUID</param>
        /// <param name="folder">指定的上传目录</param>
        /// <returns></returns>
[AcceptVerbs(HttpVerbs.Post)]public ActionResult Upload(string guid, stringfolder)
{
//如果需要修改字段显示,则参考下面代码处理 dynamic obj = newExpandoObject();
List
<string> urls = new List<string>();var result = newCommonResult();
HttpFileCollectionBase files
=HttpContext.Request.Files;if (files != null)
{
int i = 0;foreach (string key infiles.Keys)
{
try{#region MyRegionHttpPostedFileBase fileData= files[i++];if (fileData != null)
{
HttpContext.Request.ContentEncoding
= Encoding.GetEncoding("UTF-8");
HttpContext.Response.ContentEncoding
= Encoding.GetEncoding("UTF-8");
HttpContext.Response.Charset
= "UTF-8";string fileName = Path.GetFileName(fileData.FileName); //原始文件名称 string fileExtension = Path.GetExtension(fileName); //文件扩展名 FileUploadInfo info= newFileUploadInfo();
info.FileData
=ReadFileBytes(fileData);if (info.FileData != null)
{
info.FileSize
=info.FileData.Length;
}
info.Category
=folder;
info.FileName
=fileName;
info.FileExtend
=fileExtension;
info.AttachmentGUID
=guid;

info.AddTime
=DateTime.Now;
info.Editor
= CurrentUser.Name;//登录人//info.Owner_ID = OwerId;//所属主表记录ID result= BLLFactory<FileUpload>.Instance.Upload(info);if (!result.Success)
{
LogTextHelper.Error(
"上传文件失败:" +result.ErrorMessage);
}
else{//返回具体路径地址 string serverRealPath =info.BasePath.UriCombine(info.SavePath);if (!Path.IsPathRooted(info.BasePath) && !info.BasePath.StartsWith("http://") && !info.BasePath.StartsWith("https://"))
{
//如果是相对目录,加上当前程序的目录才能定位文件地址 var url =HttpContext.Request.Url;var baseurl = url.AbsoluteUri.Replace(url.PathAndQuery, "");
serverRealPath
= baseurl.UriCombine(serverRealPath).Replace('\\', '/');
}
urls.Add(serverRealPath);
}
}
#endregion}catch(Exception ex)
{
result.ErrorMessage
=ex.Message;
LogTextHelper.Error(ex);
}
}
obj.urls
=urls;
}
else{
result.ErrorMessage
= "fileData对象为空";
}
var newResult = new { Success = result.Success, ErrorMessage = result.ErrorMessage, urls =urls };returnToJsonContent(newResult);
}

以上就是使用EasyUI的控件FileBox的前端处理和后端C#代码,从而实现了文件的AJax处理操作,实现较好的体验和功能实现。

完整代码都已全部贴出,方便供参考学习。

在我较早的一篇随笔《
在DevExpress程序中使用TeeList控件以及节点查询的处理
》中,介绍了在树形列表TreeList控件上面,利用SearchControl实现节点的模糊查询过滤操作,效果还是非常不错的,TreeList功能比较强大,界面也相对比微软内置的Winform的TreeView控件美观不少。后来在一次偶然过程中,发现TreeList控件本身就可以打开头部过滤输入,实现节点的快速过滤,不过过滤是完全匹配方式,和我们常规的模糊匹配思路不一样,本篇随笔介绍对TreeList控件常规过滤方式的改进,支持内置输入过滤框中实现节点的模糊查询过滤操作。

1、利用SearchControl实现节点的模糊查询过滤回顾

我在随笔《
在DevExpress程序中使用TeeList控件以及节点查询的处理
》中介绍了整个开发的步骤和思路,最终的实现效果如下所示。

上面那个界面是之前案例的效果,新版本目前已经把不匹配的过滤掉了,最终效果如下所示。

如果我们忽略树列表的初始化操作和绑定数据源的部分,那么实现这个功能,主要的步骤就是在TreeList控件上面增加一个SearchControl的控件,并通代码初始化事件处理即可,如下代码所示。

/// <summary>
///实现树节点的过滤查询/// </summary>
private voidInitSearchControl()
{
this.searchControl1.Client = this.tree;this.tree.FilterNode += (object sender, DevExpress.XtraTreeList.FilterNodeEventArgs e) =>{if (tree.DataSource == null)return;string nodeText = e.Node.GetDisplayText("Name");//参数填写FieldName if (string.IsNullOrWhiteSpace(nodeText))return;bool isExist = nodeText.IndexOf(searchControl1.Text, StringComparison.OrdinalIgnoreCase) >= 0;if(isExist)
{
var node =e.Node.ParentNode;while (node != null)
{
if (!node.Visible)
{
node.Visible
= true;
node
=node.ParentNode;
}
else break;
}
}
e.Node.Visible
=isExist;
e.Handled
= true;
};
}

虽然实现的效果非常不错,不过麻烦的地方就是需要自己添加一个控件,并处理好布局和控件的初始化代码,稍显麻烦一些。

下面的处理方式就是打开内置的过滤输入框,并通过代码的方式实现模糊查询的方式过滤操作。

2、利用TreeList控件内置过滤方式实现模糊查询过滤

上面介绍了一种扩展方式实现节点的模糊查询,我们也可以利用TreeList控件本身具有的节点过滤框控件来实现TreeList控件节点的过滤。

在TreeList控件设计模式下,打开并设置 OptionsView.ShowAutoFilterRow、OptionsBehavior.EnableFiltering为True就可以看到树形控件顶部增加一个内置的控件显示了,如下界面所示。

也可以通过代码方式进行打开。

   this.treeList1.OptionsView.ShowAutoFilterRow = true;//显示过滤行
   this.treeList1.OptionsBehavior.EnableFiltering = true;//开启过滤功能

可以看到顶部多了一个输入框,默认的事件就支持对节点的过滤操作,不过是节点名称完全匹配,类似Equal的处理方式,这个和我们常规需要的操作方式不同,那么就需要进行一定的改进了。

TreeList控件提供ColumnFilterChanged事件进行自定义的过滤操作,我们在其中实现我们想要的部分匹配,也就是模糊查询方式的处理即可。

            this.treeList1.ColumnFilterChanged += (s, e) => //自定义过滤事件
{var tree =(TreeList)s;if (tree != null && tree.ActiveEditor != null)
{
string newKey =tree.ActiveEditor.EditValue.ToString();
tree.FilterNodes();
var operation = new FilterNodeOperation(newKey ?? "");
tree.NodesIterator.DoOperation(operation);
}
};

其中FilterNodeOperation 是我们自定义的一个过滤节点操作对象,具体的定义代码如下所示。

    /// <summary>
    ///过滤节点操作类/// </summary>
    classFilterNodeOperation : TreeListOperation
{
stringpattern;public FilterNodeOperation(string _pattern) { pattern =_pattern; }public override voidExecute(TreeListNode node)
{
if(NodeContainsPattern(node, pattern))
{
node.Visible
= true;if (node.ParentNode != null)
{
node.ParentNode.Visible
= true;
}
}
else{
node.Visible
= false;
}
}
/// <summary> ///模糊包含模式/// </summary> bool NodeContainsPattern(TreeListNode node, stringpattern)
{
foreach (TreeListColumn col innode.TreeList.Columns)
{
if(node.GetValue(col).ToString().ToUpper().Contains(pattern.ToUpper()))
{
return true;
}
}
return false;
}
}

根据上面的操作,做了一个过滤节点的Demo界面。

常规没有过滤的界面效果如下所示。

TreeList节点过滤后,界面效果如下所示。

对比之前的SearchControl实现的效果,这里面没有文本的高亮显示,不过显示也还是比较直观。

整个TreeList的控件处理代码如下所示,贴出供大家参考学习。

        /// <summary>
        ///初始化树/// </summary>
        private voidInitTree()
{
this.treeList1.OptionsBehavior.Editable = false;this.treeList1.OptionsView.EnableAppearanceOddRow = true;this.treeList1.OptionsView.EnableAppearanceEvenRow = true;this.treeList1.OptionsView.RowImagesShowMode = RowImagesShowMode.InCell;//紧凑型图标 this.treeList1.ExpandAll();//列过滤处理 this.treeList1.OptionsView.ShowAutoFilterRow = true;//显示过滤行 this.treeList1.OptionsBehavior.EnableFiltering = true;//开启过滤功能 this.treeList1.ColumnFilterChanged += (s, e) => //自定义过滤事件 {var tree =(TreeList)s;if (tree != null && tree.ActiveEditor != null)
{
string newKey =tree.ActiveEditor.EditValue.ToString();
tree.FilterNodes();
var operation = new FilterNodeOperation(newKey ?? "");
tree.NodesIterator.DoOperation(operation);
}
};
//初始化树节点选择事件 this.treeList1.FocusedNodeChanged += (s, e) =>{this.FocusedNodeChanged(s, e);
};
}
private void FocusedNodeChanged(objects, FocusedNodeChangedEventArgs e)
{
var tree =(TreeList)s;if (tree != null && tree.FocusedNode != null)
{
//选中节点文本 var text = tree.FocusedNode.GetDisplayText(0);switch(text)
{
case "采血":
ChildWinManagement.LoadMdiForm(
this, typeof(FrmOrganization));break;default:break;
}
}
}

这些就是Winform中一些界面处理的技巧,DevExpress界面控件的功能还是非常不错的,我们基类一些界面的处理技巧,可以极大提高用户的体验效果,同时提高我们软件的内在价值。

在很多时候,我们做一些非常规化的界面的时候,往往需要创建一些用户控件,在其中绘制好一些基础的界面块,作为后续重复使用的一个单元,用户控件同时也可以封装处理一些简单的逻辑。在开发Winform各种类型项目,我都时不时需要定制一些特殊的用户控件,以方便在界面模块中反复使用。我们一般是在自定义的用户控件里面,添加各种各样的界面控件元素,或者封装一些特殊的函数处理共外部调用等。本篇随笔主要介绍基于DevExpress的Winform开发经验,介绍一个类似看板信息的用户控件,并在TabelLayout和StackLayout布局控件中进行展示。

1、用户控件界面的处理

在偶尔的一个场合下,看到一个牙医管家的软件界面做的非常不错,其中有一个预约列表的界面感觉非常好,如下界面所示。

其中它的每个用户信息列表里面,都是一个综合信息的展示,非常直观,估计应该是用户自定义控件做的。

在其中里面,有不同的字体,各式图标,以及内容的信息展示, 这个我在DevExpress的列表控件里面,没有看到可以如此定义列表内容的,在DevExpress的GridView里面有一个看板模板的定义有点接近,但是试了一下,可调性不好,于是放弃寻求其他接近方案,玩遍DevExpress的控件后,发现最好的方式应该是自定义用户控件的方式来解决这个界面问题。

花了一点时间,制作了一个用户控件,在其中添加一个LayoutControl方便控制布局,添加一些标签以及设置了一些图标,得到下图所示。

左侧的颜色条由于使用Group控件,因此宽度暂时无法调整,如果介意大小,我们可以在其中在创建一个LayoutControl,然后在其中方式内容即可。

我们改变布局,然后添加一个颜色块,得到类似界面如下所示。

我们来定义一下用户控件的源码部分,修改其中源码,增加对应的属性,方便动态设置用户控件的相关属性,如颜色块,项目背景色,以及绑定的对象信息等内容。

然后在界面加载完毕后,设置对应的信息和颜色信息,如下代码所示。

        /// <summary>
        ///窗口加载事件/// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void UserItemControl_Load(objectsender, EventArgs e)
{
BindData();
RefreshColor();
}
/// <summary> ///根据用户定义信息,显示不同的内容/// </summary> private voidBindData()
{
if(this.UserItemInfo != null)
{
var info = this.UserItemInfo;this.lblCustomerName.Text =info.CustomerName;this.lblMobile.Text =info.Mobile;this.lblReceiver.Text =info.Receiver;this.lblRecordDate.Text =info.RecordDate;this.lblRecordNo.Text =info.RecordNo;this.lblDealType.Text = "";this.lblStar.ImageOptions.ImageIndex =GetStarImageIndex(info.Stars);if (!info.IsVip)
{
this.lblVip.Visibility =LayoutVisibility.Never;
}
}
}
/// <summary> ///刷新背景色/// </summary> private voidRefreshColor()
{
if (ItemBlockColor !=Color.Empty)
{
this.itemColor.AppearanceItemCaption.BackColor =ItemBlockColor;
}
if (ItemBackColor !=Color.Empty)
{
layoutControl1.BackColor
=ItemBackColor;
}
}

但我们鼠标浮动在项目上或者离开的时候,或者单击某项的时候,我们变换下颜色,方便区分显示。

        private void layoutControl1_MouseLeave(objectsender, EventArgs e)
{
if (!this.IsSelected)
{
this.layoutControl1.ResetBackColor();
}
}
private void layoutControl1_MouseEnter(objectsender, EventArgs e)
{
if (!this.IsSelected)this.layoutControl1.BackColor = Color.FromArgb(192, 255, 192);
}
private void layoutControl1_Click(objectsender, EventArgs e)
{
this.IsSelected = true;
OnItemClick
?.Invoke(this, e);
}

完成这些后,我们需要在窗体上对内容进行初始化。

最后我们看看界面的效果如下所示

或者下面这样的界面布局。

如果嫌弃边框红色不好看,我们 可以修改边框为灰色调一点的,这样总体看起来效果如下所示。

得到和我们最终需要的界面很接近了。

一般除了悬浮鼠标颜色变化外,控件单击后,我们会设置不同的背景色,以示区分。

        /// <summary>
        ///是否选中节点/// </summary>
        public boolIsSelected
{
get{returnm_IsSelected;
}
set{
m_IsSelected
=value;this.ItemBackColor = value ? Color.FromArgb(255, 255, 192) : Color.Transparent;this.RefreshColor();
}
}

一般列表界面中,我们除了支持鼠标移动、单击变色的效果外,我们还希望支持通过键盘箭头上下键进行上下浏览项目。我们如果需要使用键盘的按键,需要设置窗体的KeyPreview属性为True,

然后跟踪按键的事件即可,如下所示。

            this.KeyPreview = true;this.KeyUp += FrmKanBan_KeyUp;

按键事件捕捉处理如下所示,主要就是判断选中的用户控件,并对面板的子控件的选中效果进行处理。

        private void FrmKanBan_KeyUp(objectsender, KeyEventArgs e)
{
//单击鼠标或者切换按键,会触发用户控件获得selectItem对象,可以进行箭头上下移动 if (selectItem != null)
{
var panel =selectItem.Parent;if (panel != null)
{
//获取操作项的索引值 int oldIndex =panel.Controls.IndexOf(selectItem);if (e.KeyCode ==Keys.Up)
{
if (oldIndex > 0)
{
//通过序号获得新的控件,并单击它触发选择事件 var newCtrl = panel.Controls[oldIndex - 1];
Item_OnItemClick(newCtrl,
newEventArgs());
}
}
else if (e.KeyCode ==Keys.Down)
{
if (oldIndex < (panel.Controls.Count - 1))
{
//通过序号获得新的控件,并单击它触发选择事件 var newCtrl = panel.Controls[oldIndex + 1];
Item_OnItemClick(newCtrl,
newEventArgs());
}
}
}
}
}

界面中用户控件的切换选中效果,需要先清空之前所有的选择,然后在设置新的选中控件,所以还需要对控件触发单击事件进行处理,如下所示。

        /// <summary>
        ///选中的用户控件对象实例/// </summary>
        UserItemControl selectItem = null;/// <summary>
        ///单击用户控件,触发清除所有标记后,再次设置选中的项目标记/// </summary>
        private void Item_OnItemClick(objectsender, EventArgs e)
{
//清空所有控件的选中标记 var panel =(PanelControl)((Control)sender).Parent;foreach (Control ctrl inpanel.Controls)
{
var item = ctrl asUserItemControl;if(item != null)
{
item.IsSelected
= false;
}
}
//设置选中控件 selectItem =((UserItemControl)sender);
selectItem.IsSelected
= true;this.Text = selectItem.UserItemInfo.CustomerName + "-选中";//如果在面板中遮挡,移动滚动条,可以查看到完整用户控件界面 panel.ScrollControlIntoView(selectItem);
}

如下效果所示。

2、TabelLayout和StackLayout布局控件的介绍和使用

我们在做Winform开发的时候,一般知道,微软传统Winform的布局提供两个控件,FlowLayoutPanel和TableLayoutPanel,一个是流式布局,一个是表格布局,各有各的用处。流式布局主要就是按照顺序挨个放置控件,表格布局主要按照表格的定义的行列单元格,严格放置控件,表格单元格控制强度更大,而且控件具有拉伸效果。

对于DevExpress,我们一般还是倾向于采用它提供给的控件来做界面,可以很好融合它的皮肤效果,相对于Winform传统两个布局控件,DevExpress提供了两个封装效果相当的控件布局StackPanel和 TablePanel,他们的效果实现大同效果,不过调用的接口不同。

对于两个控件,我们希望里面的内容自动出现滚动条,那么设置属性AutoScroll 为True即可,如下代码所示。

panel.AutoScroll = true;

而StackPanel另外需要LayoutDirection,也就是控件顺序展现的方式,如下代码所示。

panel.LayoutDirection = StackPanelLayoutDirection.TopDown;

使用StackPanel面板来测试展示用户控件列表的界面代码如下所示。

        /// <summary>
        ///使用StackPanel对用户控件布局进行处理/// </summary>
        private voidInitPanel(StackPanel panel)
{
panel.AutoScroll
= true;//面板自动出现滚动条 panel.LayoutDirection = StackPanelLayoutDirection.TopDown;//从上往下展示 panel.Controls.Clear();//清空界面 var list = GetInfoList(); //获取用户控件绑定的对象信息列表 for (int i = 0; i < list.Count; i++)
{
//定义用户控件实例 var item = newUserItemControl();
item.UserItemInfo
= list[i];//绑定对象信息 item.Anchor= AnchorStyles.Top | AnchorStyles.Left |AnchorStyles.Right;
item.ItemBlockColor
= colors[i %10]; //变化颜色 item.OnItemClick += Item_OnItemClick;//触发选中事件 panel.Controls.Add(item);
}
}

对于表格布局TablePanel控件来说,使用初始化控件的方式也差不多,不过有个别地方注意即可。

        /// <summary>
        ///使用TablePanel对用户控件布局进行处理/// </summary>
        private voidInitPanel(TablePanel panel)
{
panel.AutoScroll
= true;//面板自动出现滚动条 panel.Controls.Clear();//清空界面 panel.Rows.Clear();//清空行 panel.Columns.Clear();//清空列//添加列定义(增加一个列即可) panel.Columns.Add(newTablePanelColumn(TablePanelEntityStyle.Relative, 55F));var list = GetInfoList(); //获取用户控件绑定的对象信息列表 for (int i = 0; i < list.Count; i++)
{
//定义行信息 panel.Rows.Add(newTablePanelRow(TablePanelEntityStyle.AutoSize, 100F));//定义用户控件实例 var item = newUserItemControl();
item.UserItemInfo
=list[i];//定义拉伸效果 item.Anchor = AnchorStyles.Top | AnchorStyles.Left |AnchorStyles.Right;
item.ItemBlockColor
= colors[i % 10]; //变化颜色 item.OnItemClick += Item_OnItemClick;//触发选中事件//先添加控件到面板集合中 panel.Controls.Add(item);//设置控件的单元格位置 panel.SetCell(item, i, 0);
}
//添加多一行,确保布局 panel.Rows.Add(newTablePanelRow(TablePanelEntityStyle.AutoSize, 100F));
}

添加控件的时候,需要注意下面的代码,才能正常展示控件信息,否则无法看到用户控件。

                //先添加控件到面板集合中
panel.Controls.Add(item);//设置控件的单元格位置
                panel.SetCell(item, i, 0);

最后对比下效果,左边是TablePanel,右边是StackPanel展现出来的效果。

以上就是界面如何在DevExpress开发中使用各种用户控件进行用户控件的创建、以及实现鼠标进入、移出、单击的不同效果,以及实现键盘上下箭头按键的事件选中效果,并介绍DevExpress中两个布局控件TabelPanel和StackPanel的正常使用,达到展示控件信息的作用。

微信门户应用管理系统,采用基于
MVC + Bootstrap + Ajax + Enterprise Library
的技术路线,界面层采用Boostrap + Metronic组合的前端框架,数据访问层支持Oracle、SQLServer、MySQL、PostgreSQL等数据库。框架以MVC5,C#4.5作为开发基础,能够部署在任何.NET服务器上,建议部署到阿里云服务器。

1、环境准备

本框架采用C#开发,Web管理端采用基于
MVC + Bootstrap + Ajax + Enterprise Library
的技术路线的架构,需要使用VS2017/2019进行开发,默认数据库采用SQL Server,可以安装SQL Server 2008R2及以上。

微信公众号开发,一般需要进行相关的网页页面调试,请下载微信提供的《微信开发者工具》,下载地址为:
https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html

框架开发一般还需要使用到代码生成工具,工具可以从我公司的官网上下载(
http://www.iqidi.com/Download/Database2SharpSetup.rar
)。

2、模块说明

整个框架项目包含了微信公众号、企业微信、小程序三个部分内容,其中三者之间有一些相同的部分抽象出来放在一个公共的项目里面,然后公众号、企业微信、小程序共享一个后台管理系统,这个后台系统主要就是实现账号信息的配置、和微信后台事件的对接、以及权限和系统数据的管理维护等功能。

使用VS打开项目源码后,解决方案的工程如下所示。

下面是对上面项目工程进行说明,方便理解其中各个模块的意义。

编号

名称

说明

01_Framework 目录里面的项目,是框架的基础内容,包括权限、字典、附件模块的管理。

1

Security

权限管理模块业务层。

2

Dictionary

字典管理模块业务层。

3

Attachment

附件管理模块业务层

4

Other

其他系统内容,如图标、产品、测试用户等基础信息

02_Common目录里面的项目,是公众号/企业微信/小程序公用的部分,如账号配置、基础消息对象类,也可以是Web后台管理所需的模块内容。

1

WHC.Common.API

提供一些第三方模块的封装,如邮件处理、短信处理以及一些第三方的应用扩展模块等。

2

WHC.Common.Cache

基于CacheManager的接口处理的缓存接口,可以实现SystemRuntimeCache、Redis等缓存的整合。

3

WHC.Common.Data

提供常用模块,需要对数据库处理的模块的封装,包括一些如账号、菜单、事件、消息内容等公用的数据存储。

4

WHC.Common.Entity

提供常用模块里面设计到的实体类信息。包括公众号和企业微信都共同用到的对象类。

5

WHC.Common.Handler

多个IOC方式的模块,可以在所需系统注册使用。

自定义接口包含有测试处理接口、企业微信消息推送接口、设备信息二维码扫码接口、短信和邮件发送接口。

外部的插件业务接口包括:百度的地理位置解析接口、电影院信息查询、天气信息查询、交通信息查询、旅游信息查询等。

03_Weixin 目录里面的项目,是微信公众号API相关的内容。

1

TestWeixin

是对公众号的API封装的项目模块进行测试。

2

WHC.Weixin.API

针对微信公众号API的单独封装。

3

WHC.Weixin.Data

结合数据库存储内容,对微信公众号模块的管理。这个模块包含对WHC.Weixin.API的使用。

04_Corp目录里面的项目,是对企业微信API的封装和对数据库管理模块的封装。

1

TestCorp

是对企业微信的API封装的项目模块进行测试。

2

WHC.Corp.API

针对微信企业微信API的单独封装。

3

WHC.Corp.Data

结合数据库存储内容,对企业微信模块的管理。这个模块包含对WHC.Corp.API的使用。

05_SmallApp目录里面的项目,是对小程序API的封装和对数据库管理模块的封装。

1

TestSmallApp

是对小程序的API封装的项目模块进行测试。

2

WHC.SmallApp.API

针对小程序API的单独封装。

3

WHC.SmallApp.Data

结合数据库存储内容,对小程序模块的管理。这个模块包含对WHC. SmallApp.API的使用。

06_UI 是微信后台管理系统部分内容,包括后台管理系统及企业微信的管理客户端程序。

1

WHC.Weixin.BootstrapUI

微信管理后台,包括对公众号和企业微信的统一管理,实现各个模块数据的同步和查看管理操作。

2

CorpManagement

基于C/S的Winform企业微信管理客户端,可以方便管理企业微信的通讯录的组织机构、标签、人员信息,以及文本、图片、语音、视频等消息的发送功能。

07_His 是扩展的项目部分

1

WHC.His.Data

包含对医疗设备维修项目、处方审核项目等扩展项目的模块管理。

其他模块

1

公用类库

WHC.Framework.Commons

WHC.Framework.ControlUtil

提供日常各种开发操作的辅助类库。

WHC.Framework.Commons项目是对常规.NET共用类库的封装使用,已达到统一、高效、方便的目的。

WHC.Framework.ControlUtil项目是对第三方组件的封装使用,以及对整个框架各层抽象类的定义和封装。

2

分页类

WHC.Pager.Entity

这个是独立定义的一个分页参数对象,方便框架里面对数据进行分页处理。

4

参数配置管理

SettingsProvider.Net

提供了一个第三方的配置信息的管理类。这个是在GitHub上的一款参数配置组件,能够基于普通配置文件、ProgramData目录文件、独立存储区文件等方式的配置文件存储,它主要是基于Json格式进行的配置保存,因此我们还可以把它存储到数据库

3、模块关系

开发模块划分包括:微信公众号、企业微信、小程序几个部分

这几个部分又有一些基础一样的内容,抽取出来作为公共的项目。公众号、企业微信、小程序公共部分内容如下所示。

公众号、企业微信、小程序几个分类,它们包含了数据存储的模块内容,也包括了对应分类的微信API操作部分。数据管理模块和API模块调用关系如下所示。

微信界面项目,同样是这几个部分的组合。微信界面部分是前面模块组件的综合使用,在微信应用里面,一般需要使用
80端口和微信服务器做交互,而这个同时往往也是我们项目的端口地址。

另外,微信框架还包括基础类库支持部分内容,包括权限管理、字典管理、附件管理等基础内容。

微信开发,我们首先需要利用我们的语言(这里是利用C#语言),为所有用到的API接口实现进一步的封装,方便使用,微信API模块包含的内容很多,其中公众号接口封装包括内容如下所示。

企业微信的API接口包含内容如下所示。

我们在WHC.Weixin.Data模块里面,定义了包含公众号的消息分派处理接口,这个分派接口是对接收来自微信服务器的各种消息事件进行响应;另外该模块还包含一些常规的数据存储,如关注用户、菜单、文章内容等方面数据的存储,如下所示。

同样我们参考微信公众号的做法,也是建立一个数据存储管理的项目,作为微信消息事件的处理入口,同时也管理存储一些必须的数据,包括需要同步的用户、标签、部门等数据。

4、微信公众号配置

不管是微信公众号,还是企业微信,都需要申请账号才能使用,并且最好通过认证,这样才能具有更多的接口权限使用。

微信公众号的注册申请参考文章:
http://www.cnblogs.com/wuhuacong/p/3613826.html

输入账号密码,登录微信公众号后台
https://mp.weixin.qq.com
,然后扫码确认登陆,在左侧【开发】【基本配置】中获取对应的账号配置参数。

在【修改配置】中进行修改内容,如下界面所示。

然后启动微信管理后台系统(一般启动,本地地址为
http://localhost:32973/Login/Index
),如果部署在服务器后,请通过域名进行访问即可。

登陆后台系统后,在【微信管理】【微信账号配置】管理界面里面进行维护对应账号信息,如下界面所示。

修改或者增加相关的公众号账号信息。(根据微信公众号参数,录入红色方框的内容)

再次回到刚才后台的界面,修改对应的URL,提交参数,如提示【提交成功】即可正确完成配置。

5、企业微信配置

微信企业微信的注册申请参考文章:
http://www.cnblogs.com/wuhuacong/p/3991735.html

企业微信后台登录地址为:
https://work.weixin.qq.com
,扫码登录进行获取对应的参数,找到【我的企业】【企业信息】界面,如下界面所示。

然后滚动到底部,找到对应的企业ID,如下界面所示,这个就是后面填写需要用到的CorpID。

再次切换到【应用管理】【应用】选项卡里面。

在应用底部,找到【创建应用】按钮,创建一个新的应用;或者单击进入现有的应用进行查看参数。

查看创建了的应用信息,如下界面所示。

记录下前面的CorpID、CorpSecret、加密Token、加密秘钥等参数。

然后启动Web管理后台,登录后台系统后,在【微信管理】【微信账号配置】管理界面里面进行维护对应账号信息,如下界面所示。

在账号管理界面里面,选择企业微信,修改或者增加相关的账号信息。

再次回到企业微信后台,录入上面获得的接收URL,然后保存成功即可,如下界面所示。

在上篇随笔《
微信门户开发框架-使用指导说明书
》中主要介绍了微信公众号和企业微信的模块说明以及各个模块之间的关系、账号配置以及如何和微信开发框架结合起来使用的内容,由于篇幅局限,因此本篇随笔继续这个方面拓展介绍,介绍基于微信框架的开发过程,包括微信账号菜单的管理,H5页面的开发知识,以及如何整合框架实现公众号应用和企业微信应用的快速开发过程。

微信公众号和企业微信,主要就是利用微信后台管理系统的入口,创建对应应用H5页面。本章节主要介绍基于微信框架的基础上,开发对应的H5页面过程,从而实现微信公众号或者企业应用的菜单对接或者事件对接处理。

1、微信开发者工具

微信公众号或者企业微信开发,一般需要进行相关的网页页面调试,请下载微信提供的《微信开发者工具》,下载地址为:

https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html

软件启动后即可扫码进入,例如我们对其中一个页面进行调试,界面如下所示。

一般情况下,我们的H5页面需要进行重定向获取用户的信息,因此也需要在公众号后台允许该工具重定向获取对应的用户信息,因此需要在后台绑定账号。

然后在下面界面中绑定开发者账号即可。

2、微信H5页面及JSDK开发

在我们开发微信页面的时候,需要大量用到了各种呈现的效果,一般可以使用
Boostrap的效果来设计不同的页面,不过微信团队也提供很多这方面的资源,包括
JSSDK的接口,以及
Weui的页面样式和相关功能页面,给我们提供了很大的便利。

1) JSSDK

微信JS-SDK是
微信公众平台
面向网页开发者提供的基于微信内的网页开发工具包。通过使用微信JS-SDK,网页开发者可借助微信高效地使用拍照、选图、语音、位置等手机系统的能力,同时可以直接使用微信分享、扫一扫、卡券、支付等微信特有的能力,为微信用户提供更优质的网页体验。

目前JSSDK支持的接口分类包括下面几类:基础接口、分享接口、图像接口、音频接口、智能接口、设备信息、地理位置、摇一摇周边、界面操作、微信扫一扫、微信小店、微信卡券、微信支付,随着微信功能的全部整合,估计更多的接口会陆续开放出来。

2

WeUI

Jquery WeUI

WeUI
是一套同微信原生视觉体验一致的基础样式库,由微信官方设计团队为微信内网页开发量身设计,可以令用户的使用感知更加统一。在微信网页开发中使用 WeUI,有如下优势:

  • 同微信客户端一致的视觉效果,令所有微信用户都能更容易地使用你的网站
  • 便捷获取快速使用,降低开发和设计成本
  • 微信设计团队精心打造,清晰明确,简洁大方

该样式库目前包含 button、cell、dialog、progress、toast、article、icon 等各式元素,已经在
GitHub
上开源。访问
http://weui.github.io/weui/
或微信扫码即可预览。

jQuery WeUI
中使用的是官方 WeUI 的 CSS 代码,并提供了 jQuery/Zepto 版本的 API 实现。JQuery WeUI相对于在官方WeUI的基础上做了一些功能扩展,已丰富界面设计和相关功能,因此我们可以考虑直接基于JQuery  WeUI的基础上进行页面开发即可。下载地址:
http://jqweui.com/

在微信框架里面的一些案例中,都利用了We UI样式来进行很多微信H5页面的功能设计,我们主要利用JQuery WeUI库来实现相对应的一些控件处理功能。

3、微信框架中H5控制器关系

我们知道,H5页面也是基于Asp.net的开发模式,因此H5页面的入口也是MVC的控制器。为了实现简单、高效的原则,我们一般采用了一定层次的控制器继承关系,因此MVC的控制器继承关系需要了解一下。

在解决方案控制器目录如下所示。

这些控制器的继承关系如下所示,继承能够极大程度利用基类的公用函数,减少子类的代码臃肿。

通过上面的继承关系,我们可以看到公众号和企业微信部分,有一个基类控制器是实现两者共同的功能的。然后再次分支,分为公众号控制器其或者企业微信控制器,这两个对于用户来说也是一个基类,我们一般在这个基础上创建自己的子类控制器,以便实现差异化的功能。针对公众号和企业号的处理,它们的接口对象不同,因此处理代码也是不同的,这个即使他们分开的原因。

例如,基于上面的继承关系,如果我们需要创建一个新的公众号模块“ABC”,那么我们只需要继承自BaseJSSDKController,创建一个ABCController控制器,在里面实现自己的业务控制器接口即可;对于企业微信也一样,如果我们要创建一个新的企业应用“XYZ”,那么我们只需要继承自BaseQySDKController,创建一个XYZController控制器即可。

4、账号微信菜单管理

然后启动微信管理后台系统(一般启动,本地地址为
http://localhost:32973/Login/Index
),如果部署在服务器后,请通过域名进行访问即可。

进入对应的【微信菜单】模块功能,如下所示。

由于不同账号的菜单不一样,因此注意从右上角选择之前创建好的公众号账号.

默认框架提供了我们标准的一些菜单,如果选择您新建的账号,可能菜单栏是空白的。

注意:
为了方便,我们一般允许多个菜单分组存在,但只有一个是启用的(黑色),其他分组是禁用的(红色),提交菜单到微信,也只是提交当前启用的菜单分组内的菜单列表。

如果是空白的,请先创建一个菜单分组,然后需要单击【进入菜单维护】进行菜单的创建操作。

菜单可以新增,然后指定对应的类型即可,如是Click类型,必须指定事件;如果是View类型,必须指定URL。菜单是树形列表,你可以在某个菜单下面创建子菜单。菜单的文字大小、菜单项目等都有限制,必须遵循微信指定的规则创建。如果菜单设置为不可见,那么该项菜单及其下面的菜单列表都不会提交到微信服务器。菜单排序是用于确定菜单列表的顺序位置的。

常见的一些菜单列表如下所示。

菜单的URL我们要注意填写,例如对于
http://www.iqidi.com/h5/Myinfo
,这个地址前面部分是域名,h5代表是H5Controller,MyInfo代表H5Controller控制器或其基类里面的一个方法入口。这个地址也可以在方法里面进行重定向,从而可以获取用户的openid等身份信息。

菜单维护好后,我们可以通过【提交菜单到微信】功能,发布我们编辑好的菜单列表。

如果发布成功,那么我们公众号的菜单就会及时更新;如果有错误信息,请检查菜单文字的大小、URL格式和大小、菜单项目的数量、事件代码是否填写等是否符合微信的要求。

5、微信公众号开发

公众号功能的开发,除了前面我们在后台创建账号,并录入账号的账号名、AppID、AppSecret、Token、URL、EncodingAESKey并在公众号配置成功后,我们只是完成了第一步和微信服务器的对接工作。接下来需要基于这个账号下面创建对应的菜单,以及实现菜单对应的H5页面控制器和视图代码的处理。这两个步骤都做好了,我们就可以着手对应公账号H5页面的功能开发了。

每开发一个应用功能,我们都需要根据我们的业务场景,设计好对应的数据库表,然后根据业务表删除对应的框架代码BLL/DAL/IDAL/Entity等这些,增量复制到框架对应位置即可,这个和我们开发其他应用的模式一样。下面我们以处方审核项目为例进行介绍。

1)
数据库设计

我们根据业务需要,创建对应的业务表即可。这里涉及一个药品表和一个处方记录表即可,如下截图所示是基于PowerDesigner工具进行的设计视图。表的设计,一般来说需要有一个ID字段,并作为主键,其他根据情况处理即可。

设计好数据库表后,利用工具提供的SQL生成脚本,获取脚本,在数据库上执行即可创建对应的表。

2)
生成框架代码

确保生成对应的数据库表后,我们就可以利用代码生成工具Database2Sharp(代码生成工具下载地址:
http://www.iqidi.com/database2sharp.htm
)进行代码的生成了。

我们这里生成代码,只需要选择“【代码生成】【EnterpriseLibrary代码生成】”即可。

代码生成后把生成的文件复制到VS项目中,然后编译测试是否有错误,有错误则修正即可。

3)
H5
后台代码处理

接下来我们就需要创建一个对应业务的H5控制器了,如果我们业务为ABC,那么你可以定义一个ABCController,继承自公众号控制器 BaseJSSDKController即可。

我处方管理页面则是定义H5Controller控制器名称。

它的控制器定义代码如下所示。

在H5页面里面,如果需要获取用户的OpenID,则需要重定向才能获取,框架里面封装了一个方法AuthOpenId可以实现重定向获取,我们使用如下代码即可。

一旦有了用户的OpenID,就可以通过后台接口获取对应的订阅用户或者系统用户信息(用户关注的时候,订阅用户记录就会写入数据库里面;或者系统用户表绑定对应的OpenID,可以和系统用户关联),如下代码所示是根据OpenID获取绑定的用户记录。

如果我们需要在后台管理系统中绑定用户,那么我们可以在系统用户管理中进行处理,如下界面所示。

在H5页面数据保存的时候,后台需要接受来自页面的数据集合,然后逐一取出进行处理即可,常规的数据可以通过FormCollection进行处理,如下代码所示。

对于记录的保存,一般新增和编辑保存处理都是一个方法,需要我们根据它传过来的ID是否为空判断是新增还是编辑的,如下代码所示。

对于前端需要列表数据,一般通过MVC返回对应的JSON对象给前端使用即可,如下是分页列表展示数据的函数。

5)
H5
页面代码处理

H5页面一般是基于某个MVC控制器下进行的视图页面处理,一旦我们创建某个MVC控制器,VS就会创建一个对应的视图目录,如下是H5Controller控制器下的视图页面。

页面模块说明如下所示。

//通用模块的内容

BindFail.cshtml --- 微信公众号绑定失败处理

BindSuccess.cshtml --- 微信公众号绑定成功处理

Info.cshtml --- 显示一般信息的页面

JSLogin.cshtml --- 使用微信公众号扫码登陆页面

LoginError.cshtml --- 公众号扫码登陆错误页面

Test.cshtml --- 测试页面

Report.cshtml --- 报表测试展示

//医疗设备维修项目页面

DeviceAdd.cshtml --- 设备添加页面

DeviceCheck.cshtml --- 设备检查页面

DeviceInfo.cshtml --- 设备信息页面

DeviceInventory.cshtml --- 设备盘点页面

DeviceList.cshtml --- 设备列表页面

DeviceMaintain.cshtml --- 设备预防性维护页面

Measure.cshtml --- 设备度量页面

Repair.cshtml --- 设备维修工单页面

RepairDeal.cshtml --- 设备微信工单详细页面

//医疗处方审核项目页面

DrugInfo.cshtml --- 药品信息页面

DrugInquiry.cshtml --- 问诊信息页面

DrugInquiry2.cshtml --- 问诊信息页面(使用另一种上传方式的处理)

DrugList.cshtml --- 药品列表页面

DrugShop.cshtml --- 药店信息页面

Prescription.cshtml --- 处方信息

PrescriptionDetail.cshtml --- 处方详细信息

PrescriptionMy.cshtml --- 本店处方页面

MyInfo.cshtml --- 我的信息

PersonalInfo.cshtml --- 个人信息

一般的H5页面文件头部如下所示,没有母版,指定对应的JQueryWeui样式等。

而其他页面所需的CSS样式,我们根据情况参考对应页面Style创建即可。

H5页面底部需要引入对应的JS文件以及编写对应的JS脚本代码函数。

H5页面的表单,参考Jquery WeUI的表单录入处理样式即可,如下所示。

最后可以一个类似下面的表单页面内容。

如对于一些弹出框的选择页面,如选择药品的页面,那么可以通过一个连接进行打开,如下效果所示。

页面代码如下所示。

对于输入框里面即时从服务端获取数据的操作,我们一般通过绑定文本控件的“input propertychange”事件进行响应的,通过获取服务端的JSON数据,然后组装层对应的界面HTML代码后,把它赋值给某个控件就可以实现内容的动态展示了,如下是药品查询并显示在界面上的代码。

上面代码通过绑定input的propertychange事件,根据URL地址获取对应的JSON数据,然后遍历集合进行构建HTML内容,最后赋值HTML给对应控件,实现界面内容的实时变化。

6、
企业微信开发

企业微信的H5页面开发和公众号的开发类似,都需要先在后台管理系统中创建对应账号(注意一个企业微信下不同的应用需要创建不同的账号),然后在对应账号下面创建好对应的菜单资源,并同步到微信上即可更新应用的菜单。具体的账号配置参考前面小节关于【企业微信配置】说明。

同样,每开发一个应用功能,我们都需要根据我们的业务场景,设计好对应的数据库表,然后根据业务表删除对应的框架代码BLL/DAL/IDAL/Entity等这些,增量复制到框架对应位置即可,这个和我们开发其他应用的模式一样。

1)
数据库设计

步骤和公众号类似,此处忽略。

2)
生成框架代码

步骤和公众号类似,此处忽略。

3)
H5
后台代码处理

前面我们介绍过H5控制器类的继承关系,如下所示。这里我们以资产管理项目的企业微信控制器为例介绍。

QyH5Controller的定义代码只需要继承自BaseQySDKController即可。

和公众号有些不一样,企业微信的用户身份获得的是user_id的内容,这个是对应企业微信的标识的。我们也可以通过重定向页面获得用户身份user_id的内容,如下代码所示。

一旦有了用户的user_id,就可以通过后台接口获取对应的系统用户信息(系统用户表绑定对应的user_id,可以和系统用户关联),如下代码所示是根据user_id获取绑定的用户记录。

BLLFactory<User>.Instance.FindByCorpUserId(userId);

如果我们需要在后台管理系统中绑定用户,那么我们可以在系统用户管理中进行处理,如下界面所示。

一般情况下面,对于多个参数的情况,我们也是可以通过FormCollection的集合获取,如下函数定义所示。

对于前端需要列表数据,一般通过MVC返回对应的JSON对象给前端使用即可,如下是分页列表展示数据的函数。

4)
H5
页面代码处理

和公众号类似,创建对应企业应用H5控制器后,也同样会创建对应的视图目录,如我们创建了QyH5Controller控制器,那么对应有QyH5的视图目录,我们创建一些我们需要的视图文件页面,配合QyH5控制器里面的对应入口,如下所示。

企业微信的应用和公众号的H5页面应用本质上没有差异,只需要自己指定对应的样式,创建好对应的HTML代码即可。

同样在H5页面底部引入对应的JS文件,编写对应的JS处理函数来处理页面逻辑。