Formdesign Plugins

是百度编辑器Ueditor第三方扩展,本文会详细讲述如何利用Ueditor开发Formdesign。
当然你也可以使用Umeditor或其它编辑器。

基础功能使用纯 Javascript 编写,兼容:IE7++、Chrome、Firefox 等主流浏览器,IE6请看这里

表单设计器思路
表单设计器思路

请先下载扩展,建议下载完整带示例包,如果你下载独立扩展包,那你还需要另外下载Ueditor

  1. 下载扩展

安装

解压后,这个目录便是表单设计器扩展的全部
独立下载的用户,同样 copy 到 Ueditor 这个目录即安装完成。

./js
--|ueditor
----|Formdesign        把扩展放到Ueditor下
--------|bootstrap
--------|leipi.Formdesign.v4.js
--------|checkbox.html
--------|...
----|dialogs
----|ueditor.all.js
----|...
      

配置说明

Formdesign Plugins 的配置目前只有一个,toolleipi:true它可以在编辑器的toolbars显示表单设计器的图标

建议新建虚拟站点来验证,否则部分功能可能异常,正常访问地址如: http://localhost/Formdesign/index.html
如果你显示的页面和官方一样并且没有脚本报错,表示安装成功。官方演示

index.html

<script id="myFormdesign" type="text/plain" style="widht:100%">
    这里是Ueditor Formdesign 内容
</script>

<script type="text/javascript" charset="utf-8" src="js/ueditor/ueditor.config.js"></script>
<script type="text/javascript" charset="utf-8" src="js/ueditor/ueditor.all.js"></script>
<script type="text/javascript" charset="utf-8" src="js/ueditor/lang/zh-cn/zh-cn.js"></script>
<!--Fromdesign扩展--->
<script type="text/javascript" charset="utf-8" src="js/ueditor/Formdesign/leipi.Formdesign.v4.js"></script>
//实例一个Ueditor
var leipiEditor = UE.getEditor('myFormdesign',{
        toolleipi:true,//是否在toolbars显示,表单设计器的图标 
       //toolbars:[['FullScreen', 'Source']],//这里是工具拦
       textarea: 'design_content',//编辑器的表单名称   
        //更多其他参数,请参考ueditor.config.js中的配置项
});

我们是完全参照Ueditor官方扩展标准编写的扩展,为了更好的兼容性,请大家也务必效仿。

添加扩展

以最简单的checkbox控件来分析,这段代码在 leipi.Formdesign.v4.js 中。
我们开发新的扩展时也可以复制这部分代码作为基础进行修改,以此来提高效率。


UE.plugins['checkbox'] = function () {
    var me = this,thePlugins = 'checkbox';
    me.commands[thePlugins] = {
        execCommand:function () {
            var dialog = new UE.ui.Dialog({
                //弹出模式以iframe方式打开的控件配置页面 URL
                iframeUrl:this.options.UEDITOR_HOME_URL + UE.leipiFormdesignUrl+'/checkbox.html',
                name:thePlugins,
                editor:this,
                title: '复选框',//弹出框标题
                cssRules:"width:600px;height:200px;",
                buttons:[//弹出框按钮集
                {
                    className:'edui-okbutton',
                    label:'确定',
                    onclick:function () {
                        dialog.close(true);
                    }
                },
                {
                    className:'edui-cancelbutton',
                    label:'取消',
                    onclick:function () {
                        dialog.close(false);
                    }
                }]
            });
            dialog.render();
            dialog.open();
        }
    };
    var popup = new baidu.editor.ui.Popup( {
        editor:this,
        content: '',
        className: 'edui-bubble',
        _edittext: function () {
              baidu.editor.plugins[thePlugins].editdom = popup.anchorEl;
              me.execCommand(thePlugins);
              this.hide();
        },
        _delete:function(){
            if( window.confirm('确认删除该控件吗?') ) {
                baidu.editor.dom.domUtils.remove(this.anchorEl,false);
            }
            this.hide();
        }
    } );
    popup.render();
    //绑定鼠标经过控件
    me.addListener( 'mouseover', function( t, evt ) {
        evt = evt || window.event;
        var el = evt.target || evt.srcElement;
        var leipiPlugins = el.getAttribute('leipiplugins');
        if ( /input/ig.test( el.tagName ) && leipiPlugins==thePlugins) {
            var html = popup.formatHtml(
                '<nobr>复选框: <span onclick=$$._edittext() class="edui-clickable">编辑</span>  <span onclick=$$._delete() class="edui-clickable">删除</span></nobr>' );
            if ( html ) {
                popup.getDom( 'content' ).innerHTML = html;
                popup.anchorEl = el;
                popup.showAnchor( popup.anchorEl );
            } else {
                popup.hide();
            }
        }
    });
};

触发扩展

上面我们实例化了一个Ueditor为leipiEditor,触发扩展只需要调用Ueditor方法execCommand

leipiEditor.execCommand('checkbox');//执行扩展 UE.plugins['checkbox'] 弹出iframe dialog。

定义扩展属性

checkbox.html 为例


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>复选框</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1" >
    <meta name="generator" content="www.leipi.org" />
    <link rel="stylesheet" href="bootstrap/css/bootstrap.css">
    <!--[if lte IE 6]>
    <link rel="stylesheet" type="text/css" href="bootstrap/css/bootstrap-ie6.css">
    <![endif]-->
    <!--[if lte IE 7]>
    <link rel="stylesheet" type="text/css" href="bootstrap/css/ie.css">
    <![endif]-->
    <link rel="stylesheet" href="leipi.style.css">
    <script type="text/javascript" src="../dialogs/internal.js"></script>
    <script type="text/javascript">
function createElement(type, name)
{     
    var element = null;     
    try {        
        element = document.createElement('<'+type+' name="'+name+'">');     
    } catch (e) {}   
    if(element==null) {     
        element = document.createElement(type);     
        element.name = name;     
    } 
    return element;     
}
    </script>
</head>
<body>
<div class="content">
    <table class="table table-bordered table-striped table-hover">
     <tr>
        <th><span>控件名称</span><span class="label label-important">*</span></th>
        <th><span>选中状态</span></th>
    </tr>
    <tr>
        <td><input id="orgname" placeholder="必填项" type="text"/> </td>
        <td>
            <label class="radio"><input id="orgchecked0" checked="checked" name="checked" type="radio"> 不选中 </label>
            <label class="radio"><input id="orgchecked1" name="checked" type="radio"> 选中 </label>
        </td>
    </tr>
    </table>
</div>
<script type="text/javascript">
var oNode = null,thePlugins = 'checkbox';
window.onload = function() {
    if( UE.plugins[thePlugins].editdom ){
        oNode = UE.plugins[thePlugins].editdom;
        var gTitle=oNode.getAttribute('title').replace(/"/g,"\"");
        $G('orgname').value = gTitle;
        var checked = oNode.getAttribute('checked');
        checked ? $G('orgchecked1').checked = true : $G('orgchecked0').checked = true;
    }
}
dialog.oncancel = function () {
    if( UE.plugins[thePlugins].editdom ) {
        delete UE.plugins[thePlugins].editdom;
    }
};
dialog.onok = function (){
    if( $G('orgname').value == '') {
        alert('控件名称不能为空');
        return false;
    }
    var gTitle=$G('orgname').value.replace(/\"/g,""");
    if( !oNode ) {
        try {
            oNode = createElement('input','leipiNewField');
            oNode.setAttribute('title',gTitle);
            oNode.setAttribute('leipiPlugins',thePlugins );
            oNode.setAttribute('type','checkbox');
            if ($G('orgchecked1').checked) {
                oNode.setAttribute('checked','checked');
            } else {
                oNode.checked = false;
            }
            editor.execCommand('insertHtml',oNode.outerHTML);
            return true ;
        } catch ( e ) {
            try {
                editor.execCommand('error');
            } catch ( e ) {
                alert('控件异常,请到 [雷劈网] 反馈或寻求帮助!');
            }
            return false;
        }
    } else {
        oNode.setAttribute('title',gTitle);
        if ($G('orgchecked1').checked) {
            oNode.setAttribute('checked','checked');
        } else {
            oNode.removeAttribute('checked');
        }
        delete UE.plugins[thePlugins].editdom; 
        return true;
    }
};
</script>
</body>
</html>

核心编译类,以PHP为例,其它语言请效仿,主要以正则匹配数据

<?php
class Formdesign
{
    /*
    * 处理自定义设计表单模板
    * $fields  总字段数
    */
    public function  parse_form($template,$fields=0)
    {
        //获取标签  
        $preg =  "/<(img|input|textarea|select).*?(<\/select>|<\/textarea>|\/>)/s";
        //获取属性  修改为可变 的匹配
        $preg_attr ="/(\w+)=\"(.?|.+?)\"/s";
        preg_match_all($preg,$template,$temparr);
        ....
        ....
        
        $parse_form = array(
            'fields'=>$fields,//总字段数
            'template'=>$template,//编译后的html完整代码
            'parse'=>$template_parse,//替换后的html代码
            'data'=>$template_data,//提取出来的 字段信息
            'add_fields'=>$add_fields,//本次需要添加的字段
        );
        return $parse_form;
        
    }
   
    /*
    * 创建数据表, 通过fields自动计算字段增长
    * 表中存取数据利用 foreign_id 进行
    */
    public function parse_table($formid,$add_fields)
    {
        //1 分表创建
        //2 添加字段
        .....
    }
    
    /*
    * 编译字段
    * $controller = array(
    *   'user'=>array(
    *           'uid'=>9527,
    *           'real_name'=>'唐伯虎',
    *       ),
    *   'else'='其它要传进来的数据',
    * );
    */
    public function unparse_form($form,$form_data=array(),$controller=array())
    {
        //把 $template_parse  和 $template_data 进度数据处理,最终返回 完整HTML作显示
        ......
       
    }
    
    //抽出表单提交后 控件的值
    public function unparse_data($form,$post_data,$controller=array())
    {
        //用户填写完表单POST提交时,把自定义的字段,提取出来,开发者直接利用它来保存信息
        .....
    }
    
}?>

文档后期完善,请查看首页源代码示例


        /*
        Javascript 解析表单
        template 表单设计器里的Html内容
        fields 字段总数
        */
        parse_form:function(template,fields)
        

下载的每个示例中 db目录下leipi_formdesign.sql文件是Mysql数据库文件,其它数据库请参考



        一、表 leipi_form 中以下字段都是 parse_form 中解析出来的
        /* 
         *   content 建议只在编辑设计表单时用
         *  content_parse + (自身业务) + content_data = content 用户填写form 
         *
         *  content_data 可以使用 Json 格式,官网 php 中使用 serialize
        */
        `content` text NOT NULL COMMENT '表单原html模板未经处理的',
        `content_parse` text NOT NULL COMMENT '表单替换的模板 经过处理',
        `content_data` text NOT NULL COMMENT '表单中的字段数据',
        `fields` smallint(5) unsigned NOT NULL DEFAULT '0' COMMENT '字段总数',

        二、表 leipi_form_data_X 每个设计生成一张表,表字段根据表单控件添加,不用删除字段,防止冗余可以后期统一清理
        
        三、表 leipi_foreign_test 是一示例,这里 form_id 关联 leipi_form 的 id 以决定使用某张表单,用户填写好的表单数据保留到 data_X ,用 id 关联 X 

        

  1. 先在前端添加相应扩展或选项
  2. 修改 Formdesign.class.php 来达到最终效果。

如:在宏控件中添加一个【当前用户部门】 我们在macros.html加上<option value="sys_dept">当前用户部门,如 华府</option>然后修改phppublic function macros_parse()


public function macros_parse($data,$def_value='',$controller=array())
{
    switch($data['leipidata'])
    {
    ......
    //当前部门
    case 'sys_realname':
        if(!$def_value)
            $def_value = $controller['user']['dept'];
        $tpl = str_replace('{macros}',$def_value,$tpl);
        break;
    .....
    }
    ....
}