精华内容
下载资源
问答
  • 可视化编辑

    2014-11-11 10:42:30
    基于Jquery实现的可视化编辑技术,可融入CMS
  • CmsEasy 可视化编辑商城系统也称企业网站程序,系统前台生成html、完全符合SEO、同时有在线客服、潜在客户跟踪、... CmsEasy可视化编辑商城系统采用拖放技术,具有实时书写和文本编辑功能; 无需代码,自由拖拽布局,适
  • CmsEasy可视化编辑商城系统采用拖放技术,具有实时书写和文本编辑功能; 无需代码,自由拖拽布局,适应所有设备。鼠标拖动,即可完成网站设计制作; 图片上传、编辑、排版即可预览设计效果,添加链接,打打字,动感...
  • 鼠标拖拽可视化编辑企业网站系统CmsEasy 简介 鼠标拖拽可视化编辑企业网站系统CmsEasy也称企业网站程序,系统前台生成html、完全符合SEO、同时有在线客服、潜在客户跟踪、便捷的企业网站管理、搜索引擎推广等功能。...
  • HTML可视化编辑

    2010-07-22 15:17:02
    KindEditor是一套开源的HTML可视化编辑器,主要用于让用户在网站上获得所见即所得编辑效果,兼容IE、Firefox、Chrome、Safari、Opera等主流浏览器。 KindEditor使用JavaScript编写,可以无缝的与Java、.NET、PHP、...
  • 数据大屏可视化编辑
  • 可视化编程平台应用方式-编辑控制

    可视化编程平台应用方式-编辑控制

    展开全文
  • Bootstrap 可视化编辑器summernote

    万次阅读 2017-08-12 12:37:36
    版权声明:本文出自沉默...Bootstrap 可视化HTML编辑器之summernote,用其官网上的介绍就是“Super Simple WYSIWYG editor”,不过在我看来,与bootstrap中文官网上提供的“bootstrap-wysiwyg”要更simple,更漂亮,
     
    

    目录(?)[+]

    Bootstrap 可视化HTML编辑器之summernote,用其官网上的介绍就是“Super Simple WYSIWYG editor”,不过在我看来,与bootstrap中文官网上提供的“bootstrap-wysiwyg”要更simple,更漂亮,更好用!

    虽然我之前尝试过使用bootstrap-wysiwyg,可参照Bootstrap wysiwyg富文本数据如何保存到mysql,但事后诸葛亮的经验告诉我,summernote绝对是更佳的富文本编辑器,这里对其工作team点三十二个赞!!!!!

    经过一天时间的探索,对summernote有所掌握,那么为了更广大前端爱好者提供便利,我将费劲一番心血来介绍一下summernote,超级福利啊。

    一、官方API和源码下载

    工欲善其事必先利其器,首先把summernote的源码拿到以及对应官方API告诉大家是首个任务!

    官网(demo和api) 
    github源码下载,注意下载开发版

    二、效果图

    效果图1 
    效果图3

    效果图2 
    效果图2

    效果图3 
    效果图1

    三、开讲内容

    大的方向为以下三个内容:

    1. summernote的页面布局(资源引入、初始参数)
    2. summernote从本地上传图片方法(前端onImageUpload方法、后端springMVC文件保存)
    3. summernote所在form表单的数据提交

    ①、summernote的页面布局

    <!DOCTYPE html>
    <html lang="zh-CN">
    <%@ include file="/components/common/taglib.jsp"%>
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
      <title>summernote - bs3fa4</title>
    
      <!-- include jquery -->
    <script type="text/javascript" src="${ctx}/components/jquery/jquery.js"></script>
    
      <!-- include libs stylesheets -->
     <link type="text/css" rel="stylesheet" href="${ctx}/components/bootstrap/css/bootstrap.css" />
    <script type="text/javascript" src="${ctx}/components/bootstrap/js/bootstrap.min.js"></script>
    
      <!-- include summernote -->
    <link type="text/css" rel="stylesheet" href="${ctx}/components/summernote/summernote.css" />
    <script type="text/javascript" src="${ctx}/components/summernote/summernote.js"></script>
    <script type="text/javascript" src="${ctx}/components/summernote/lang/summernote-zh-CN.js"></script>
    
      <script type="text/javascript">
        $('div.summernote').each(function() {
            var $this = $(this);
            var placeholder = $this.attr("placeholder") || '';
            var url = $this.attr("action") || '';
            $this.summernote({
                lang : 'zh-CN',
                placeholder : placeholder,
                minHeight : 300,
                dialogsFade : true,// Add fade effect on dialogs
                dialogsInBody : true,// Dialogs can be placed in body, not in
                // summernote.
                disableDragAndDrop : false,// default false You can disable drag
                // and drop
                callbacks : {
                    onImageUpload : function(files) {
                        var $files = $(files);
                        $files.each(function() {
                            var file = this;
                            var data = new FormData();
                            data.append("file", file);
    
                            $.ajax({
                                data : data,
                                type : "POST",
                                url : url,
                                cache : false,
                                contentType : false,
                                processData : false,
                                success : function(response) {
                                    var json = YUNM.jsonEval(response);
                                    YUNM.debug(json);
                                    YUNM.ajaxDone(json);
    
                                    if (json[YUNM.keys.statusCode] == YUNM.statusCode.ok) {
                                        // 文件不为空
                                        if (json[YUNM.keys.result]) {
                                            var imageUrl = json[YUNM.keys.result].completeSavePath;
                                            $this.summernote('insertImage', imageUrl, function($image) {
    
                                            });
                                        }
                                    }
    
                                },
                                error : YUNM.ajaxError
                            });
                        });
                    }
                }
            });
        });
      </script>
    </head>
    <body>
    <div class="container">
        <form class="form-horizontal required-validate" action="#" enctype="multipart/form-data" method="post" onsubmit="return iframeCallback(this, pageAjaxDone)">
        <div class="form-group">
            <label for="" class="col-md-2 control-label">项目封面</label>
            <div class="col-md-8 tl th">
                <input type="file" name="image" class="projectfile" value="${deal.image}"/>
                <p class="help-block">支持jpg、jpeg、png、gif格式,大小不超过2.0M</p>
            </div>
        </div>
    
          <div class="form-group">
            <label for="" class="col-md-2 control-label">项目详情</label>
            <div class="col-md-8">
                <div class="summernote" name="description" placeholder="请对项目进行详细的描述,使更多的人了解你的" action="${ctx}/file">${deal.description}</div>
            </div>
        </div>
        </form>
    </div>
    </body>
    </html>
    
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • <!DOCTYPE html>html5的标记是必须的,注意千万不能是<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">这种doctype,否则summernote的组件显示怪怪的,按钮的大小布局不一致,这里就不再上图了,但是千万注意!
    • bootstrap 的版本号最好为v3.3.5

    1、布局div

    <div class="summernote" name="description" placeholder="请对项目进行详细的描述,使更多的人了解你的" action="${ctx}/file">${deal.description}</div>
       
    • 1
    • 1

    相信你也看到了我为div加上的三个属性name、placeholder、action,那么我们来详细介绍一下三个属性的作用:

    1. name,为外层form表单提供summernote数据保存时的数据模型的属性名,和input标签的name属性作用一致,稍候在form提交的时候具体介绍。
    2. placeholder,很直白,为summernote提供初始状态的文本描述,当然还需要后续加工,div显然是不支持placeholder属性的。
    3. action,为图片上传提供后端接收地址,稍候在介绍图片上传onImageUpload会再次用到。

    另外${deal.description}其实你不需要太多关注,和textarea的赋值的用法一致,就是单纯的显示保存后的内容。

    2、summernote初始化

      $('div.summernote').each(function() {
            var $this = $(this);
            var placeholder = $this.attr("placeholder") || '';
            var url = $this.attr("action") || '';
            $this.summernote({
                lang : 'zh-CN',
                placeholder : placeholder,
                minHeight : 300,
                dialogsFade : true,// Add fade effect on dialogs
                dialogsInBody : true,// Dialogs can be placed in body, not in
                // summernote.
                disableDragAndDrop : false,// default false You can disable drag
                // and drop
            });
        });
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    使用jQuery获取到页面上的summernote,对其进行初始化,我们来详细介绍列出参数的用法(先不介绍图片上传的onImageUpload 方法)。

    1. lang ,指定语言为中文简体
    2. placeholder ,summernote初始化显示的内容。
    3. minHeight,最小高度为300,注意这里没有使用height,是有原因的,这里稍作解释,就不上图了。当使用height指定高度后,假如上传比height高的图片,summernote就不会自动调整高度,并且前文中“效果图3”中标出的红色区域会不贴着图片,而溢出到summernote外部。
    4. dialogsFade,增加summernote上弹出窗口滑进滑出的动态效果。
    5. dialogsInBody,这个属性也很关键,默认为false,字面上的意思是summernote的弹出框是否在body中(in嘛),设置为false时,dialog的式样会继承其上一级外部(如上文中的form-horizontal)容器式样,那么显示的效果就很别扭,这里也不再上图;那么设置为true时,就不会继承上一级外部div的属性啦,从属于body嘛。
    6. disableDragAndDrop,设置为false吧,有的时候拖拽会出点问题,你可实践。

    ②、summernote从本地上传图片方法

    1、前端onImageUpload方法

    假如问度娘如下的话:“onImageUpload方法怎么写?”,度娘大多会为你找到如下回答:

    
    $(\'.summernote\').summernote({
        height:300,
        onImageUpload: function(files, editor, welEditable) {
         sendFile(files[0],editor,welEditable);
        }
       });
     });
    
    function sendFile(file, editor, welEditable) {
        data = new FormData();
        data.append("file", file);
        url = "http://localhost/spichlerz/uploads";
        $.ajax({
            data: data,
            type: "POST",
            url: url,
            cache: false,
            contentType: false,
            processData: false,
            success: function (url) {
                editor.insertImage(welEditable, url);
            }
        });
    }
    </script>
    
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    以上资源来自于stackoverflow。

    但其实呢,summernote-develop版本的summernote已经不支持这种onImageUpload写法,那么如今的写法是什么样子呢?参照summernote的官网例子。

    onImageUpload

    Override image upload handler(default: base64 dataURL on IMG tag). You can upload image to server or AWS S3: more…

    // onImageUpload callback
    $('#summernote').summernote({
      callbacks: {
        onImageUpload: function(files) {
          // upload image to server and create imgNode...
          $summernote.summernote('insertNode', imgNode);
        }
      }
    });
    
    // summernote.image.upload
    $('#summernote').on('summernote.image.upload', function(we, files) {
      // upload image to server and create imgNode...
      $summernote.summernote('insertNode', imgNode);
    });
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    那么此时onImageUpload的具体写法呢?(后端为springMVC):

    callbacks : {
        // onImageUpload的参数为files,summernote支持选择多张图片
        onImageUpload : function(files) {
            var $files = $(files);
    
            // 通过each方法遍历每一个file
            $files.each(function() {
                var file = this;
                // FormData,新的form表单封装,具体可百度,但其实用法很简单,如下
                var data = new FormData();
    
                // 将文件加入到file中,后端可获得到参数名为“file”
                data.append("file", file);
    
                // ajax上传
                $.ajax({
                    data : data,
                    type : "POST",
                    url : url,// div上的action
                    cache : false,
                    contentType : false,
                    processData : false,
    
                    // 成功时调用方法,后端返回json数据
                    success : function(response) {
                        // 封装的eval方法,可百度
                        var json = YUNM.jsonEval(response);
    
                        // 控制台输出返回数据
                        YUNM.debug(json);
    
                        // 封装方法,主要是显示错误提示信息
                        YUNM.ajaxDone(json);
    
                        // 状态ok时
                        if (json[YUNM.keys.statusCode] == YUNM.statusCode.ok) {
                            // 文件不为空
                            if (json[YUNM.keys.result]) {
    
                                // 获取后台数据保存的图片完整路径
                                var imageUrl = json[YUNM.keys.result].completeSavePath;
    
                                // 插入到summernote
                                $this.summernote('insertImage', imageUrl, function($image) {
                                    // todo,后续可以对image对象增加新的css式样等等,这里默认
                                });
                            }
                        }
    
                    },
                    // ajax请求失败时处理
                    error : YUNM.ajaxError
                });
            });
        }
    }
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    注释当中加的很详细,这里把其他关联的代码一并贴出,仅供参照。

        debug : function(msg) {
            if (this._set.debug) {
                if (typeof (console) != "undefined")
                    console.log(msg);
                else
                    alert(msg);
            }
        },
    jsonEval : function(data) {
            try {
                if ($.type(data) == 'string')
                    return eval('(' + data + ')');
                else
                    return data;
            } catch (e) {
                return {};
            }
        },
        ajaxError : function(xhr, ajaxOptions, thrownError) {
            if (xhr.responseText) {
                $.showErr("<div>" + xhr.responseText + "</div>");
            } else {
                $.showErr("<div>Http status: " + xhr.status + " " + xhr.statusText + "</div>" + "<div>ajaxOptions: " + ajaxOptions + "</div>"
                        + "<div>thrownError: " + thrownError + "</div>");
            }
    
        },
        ajaxDone : function(json) {
            if (json[YUNM.keys.statusCode] == YUNM.statusCode.error) {
                if (json[YUNM.keys.message]) {
                    YUNM.debug(json[YUNM.keys.message]);
                    $.showErr(json[YUNM.keys.message]);
                }
    
            } else if (json[YUNM.keys.statusCode] == YUNM.statusCode.timeout) {
                YUNM.debug(json[YUNM.keys.message]);
                $.showErr(json[YUNM.keys.message] || YUNM.msg("sessionTimout"), YUNM.loadLogin);
            }
        },
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    2、后端springMVC文件保存

    2.1、为springMVC增加文件的配置
        <bean id="multipartResolver"
            class="org.springframework.web.multipart.commons.CommonsMultipartResolver" p:defaultEncoding="UTF-8">
            <property name="maxUploadSize" value="1024000000"></property>
        </bean>
    
    
    <mvc:annotation-driven conversion-service="conversionService" />
        <bean id="conversionService"
            class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
            <property name="converters">
                <list>
                    <!-- 这里使用string to date可以将dao在jsp到controller转换的时候直接将string格式的日期转换为date类型 -->
                    <bean class="com.honzh.common.plugin.StringToDateConverter" />
    <!--                为type为file类型的数据模型增加转换器 -->
                    <bean class="com.honzh.common.plugin.CommonsMultipartFileToString" />
                </list>
            </property>
        </bean>
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    这里就不做过多介绍了,可参照我之前写的SpringMVC之context-dispatcher.xml,了解基本的控制器

    2.2、FileController.java
    package com.honzh.spring.controller;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.apache.log4j.Logger;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    import com.honzh.common.base.UploadFile;
    import com.honzh.spring.service.FileService;
    
    @Controller
    @RequestMapping(value = "/file")
    public class FileController extends BaseController {
        private static Logger logger = Logger.getLogger(FileController.class);
    
        @Autowired
        private FileService fileService;
    
        @RequestMapping("")
        public void index(HttpServletRequest request, HttpServletResponse response) {
            logger.debug("获取上传文件...");
            try {
                UploadFile uploadFiles = fileService.saveFile(request);
    
                renderJsonDone(response, uploadFiles);
            } catch (Exception e) {
                logger.error(e.getMessage());
                logger.error(e.getMessage(), e);
                renderJsonError(response, "文件上传失败");
            }
        }
    
    }
    
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    2.3、FileService.java
    package com.honzh.spring.service;
    
    import java.io.IOException;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Random;
    
    import javax.servlet.http.HttpServletRequest;
    
    import org.apache.commons.io.FileUtils;
    import org.apache.log4j.Logger;
    import org.springframework.stereotype.Service;
    import org.springframework.web.multipart.MultipartFile;
    import org.springframework.web.multipart.MultipartHttpServletRequest;
    
    import com.honzh.common.Variables;
    import com.honzh.common.base.UploadFile;
    import com.honzh.common.util.DateUtil;
    
    @Service
    public class FileService {
        private static Logger logger = Logger.getLogger(FileService.class);
    
        public UploadFile saveFile(HttpServletRequest request) throws IOException {
            logger.debug("获取上传文件...");
    
            // 转换为文件类型的request
            MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
    
            // 获取对应file对象
            Map<String, MultipartFile> fileMap = multipartRequest.getFileMap();
            Iterator<String> fileIterator = multipartRequest.getFileNames();
    
            // 获取项目的相对路径(http://localhost:8080/file)
            String requestURL = request.getRequestURL().toString();
            String prePath = requestURL.substring(0, requestURL.indexOf(Variables.ctx));
    
            while (fileIterator.hasNext()) {
                String fileKey = fileIterator.next();
                logger.debug("文件名为:" + fileKey);
    
                // 获取对应文件
                MultipartFile multipartFile = fileMap.get(fileKey);
    
                if (multipartFile.getSize() != 0L) {
    
                    validateImage(multipartFile);
    
                    // 调用saveImage方法保存
                    UploadFile file = saveImage(multipartFile);
                    file.setPrePath(prePath);
    
                    return file;
                }
            }
    
            return null;
        }
    
        private UploadFile saveImage(MultipartFile image) throws IOException {
            String originalFilename = image.getOriginalFilename();
            logger.debug("文件原始名称为:" + originalFilename);
    
            String contentType = image.getContentType();
            String type = contentType.substring(contentType.indexOf("/") + 1);
            String fileName = DateUtil.getCurrentMillStr() + new Random().nextInt(100) + "." + type;
    
            // 封装了一个简单的file对象,增加了几个属性
            UploadFile file = new UploadFile(Variables.save_directory, fileName);
            file.setContentType(contentType);
            logger.debug("文件保存路径:" + file.getSaveDirectory());
    
            // 通过org.apache.commons.io.FileUtils的writeByteArrayToFile对图片进行保存
            FileUtils.writeByteArrayToFile(file.getFile(), image.getBytes());
    
            return file;
        }
    
        private void validateImage(MultipartFile image) {
        }
    }
    
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    2.4、UploadFile.java
    package com.honzh.common.base;
    
    import java.io.File;
    
    import com.honzh.common.Variables;
    
    public class UploadFile {
        private String saveDirectory;
        private String fileName;
        private String contentType;
        private String prePath;
        private String completeSavePath;
        private String relativeSavePath;
    
        public UploadFile(String saveDirectory, String filesystemName) {
            this.saveDirectory = saveDirectory;
            this.fileName = filesystemName;
        }
    
        public String getFileName() {
            return fileName;
        }
    
        public String getSaveDirectory() {
            return saveDirectory;
        }
    
        public String getContentType() {
            return contentType;
        }
    
        public void setContentType(String contentType) {
            this.contentType = contentType;
        }
    
        public String getPrePath() {
            if (prePath == null) {
                return "";
            }
            return prePath;
        }
    
        public void setPrePath(String prePath) {
            this.prePath = prePath;
            setCompleteSavePath(prePath + getRelativeSavePath());
        }
    
        public String getCompleteSavePath() {
            return completeSavePath;
        }
    
        public void setCompleteSavePath(String completeSavePath) {
            this.completeSavePath = completeSavePath;
        }
    
        public String getRelativeSavePath() {
            return relativeSavePath;
        }
    
        public void setRelativeSavePath(String relativeSavePath) {
            this.relativeSavePath = relativeSavePath;
        }
    
        public void setSaveDirectory(String saveDirectory) {
            this.saveDirectory = saveDirectory;
        }
    
        public void setFileName(String fileName) {
            this.fileName = fileName;
        }
    
        public File getFile() {
            if (getSaveDirectory() == null || getFileName() == null) {
                return null;
            } else {
                setRelativeSavePath(Variables.ctx + "/" + Variables.upload + "/" + getFileName());
                return new File(getSaveDirectory() + "/" + getFileName());
            }
        }
    }
    
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    后端文件保存方法也非常简单,懂Java的同学都可以看得懂,那么对于后端不使用springmvc的同学,你可以再找找方法。


    辛苦的介绍完前两节后,我们来一个动态图看一下效果吧! 
    这里写图片描述

    ③. summernote所在form表单的数据提交

    这里,我们再回顾一下summernote所在的form表单,其中还包含了一个普通file的input标签,也就是说,该form还需要上传一张项目封面。

    <form class="form-horizontal required-validate" action="#" enctype="multipart/form-data" method="post" onsubmit="return iframeCallback(this, pageAjaxDone)">
       
    • 1
    • 1

    先看一下form的属性:

    1. enctype:”multipart/form-data”,表明为文件类型的form保存
    2. iframeCallback方法,稍候详细介绍,主要是对有文件上传的form表单进行封装。

    1、iframeCallback

    function iframeCallback(form, callback) {
        YUNM.debug("带文件上传处理");
    
        var $form = $(form), $iframe = $("#callbackframe");
    
        var data = $form.data('bootstrapValidator');
        if (data) {
            if (!data.isValid()) {
                return false;
            }
        }
    
        // 富文本编辑器
        $("div.summernote", $form).each(function() {
            var $this = $(this);
    
            if (!$this.summernote('isEmpty')) {
                var editor = "<input type='hidden' name='" + $this.attr("name") + "' value='" + $this.summernote('code') + "' />";
                $form.append(editor);
            } else {
                $.showErr("请填写项目详情");
                return false;
            }
    
        });
    
        if ($iframe.size() == 0) {
            $iframe = $("<iframe id='callbackframe' name='callbackframe' src='about:blank' style='display:none'></iframe>").appendTo("body");
        }
        if (!form.ajax) {
            $form.append('<input type="hidden" name="ajax" value="1" />');
        }
        form.target = "callbackframe";
    
        _iframeResponse($iframe[0], callback || YUNM.ajaxDone);
    }
    function _iframeResponse(iframe, callback) {
        var $iframe = $(iframe), $document = $(document);
    
        $document.trigger("ajaxStart");
    
        $iframe.bind("load", function(event) {
            $iframe.unbind("load");
            $document.trigger("ajaxStop");
    
            if (iframe.src == "javascript:'%3Chtml%3E%3C/html%3E';" || // For
            // Safari
            iframe.src == "javascript:'<html></html>';") { // For FF, IE
                return;
            }
    
            var doc = iframe.contentDocument || iframe.document;
    
            // fixing Opera 9.26,10.00
            if (doc.readyState && doc.readyState != 'complete')
                return;
            // fixing Opera 9.64
            if (doc.body && doc.body.innerHTML == "false")
                return;
    
            var response;
    
            if (doc.XMLDocument) {
                // response is a xml document Internet Explorer property
                response = doc.XMLDocument;
            } else if (doc.body) {
                try {
                    response = $iframe.contents().find("body").text();
                    response = jQuery.parseJSON(response);
                } catch (e) { // response is html document or plain text
                    response = doc.body.innerHTML;
                }
            } else {
                // response is a xml document
                response = doc;
            }
    
            callback(response);
        });
    }
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80

    贴上全部代码以供参考,但是这里我们只讲以下部分:

    // 富文本编辑器
        $("div.summernote", $form).each(function() {
            var $this = $(this);
    
            if (!$this.summernote('isEmpty')) {
                var editor = "<input type='hidden' name='" + $this.attr("name") + "' value='" + $this.summernote('code') + "' />";
                $form.append(editor);
            } else {
                $.showErr("请填写项目详情");
                return false;
            }
    
        });
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 通过form获取到summernote对象$this 后,通过!$this.summernote('isEmpty')来判断用户是否对富文本编辑器有内容上的填写,保证不为空,为空时,就弹出提示信息。
    • $this.summernote('code')可获得summernote编辑器的html内容,将其封装到input对象中,name为前文中div提供的name,供后端使用。

    这里其他地方就不做多解释了,详细可参照Bootstrap wysiwyg富文本数据如何保存到mysql

    保存到数据库中是什么样子呢?

    <p><img src="http://localhost:8080/ymeng/upload/2016033117093076.jpeg" style=""></p><p><br></p><p>你好,有兴趣可以加入到沉默王二的群啊<br></p>
       
    • 1
    • 1

    页面效果为:

    这里写图片描述


    2、新版iframeCallback方法

    var $form = $(form), $iframe = $("#callbackframe");
    
    YUNM.debug("验证其他简单组件");
    
    var data = $form.data('bootstrapValidator');
    
    if (data) {
        if (!data.isValid()) {
            return false;
        }
    }
    
    // 富文本编辑器
    $("div.summernote", $form).each(function() {
        var $this = $(this);
    
        if ($this.summernote('isEmpty')) {
        } else {
            YUNM.debug($this.summernote('code'));
    
            // 使用base64对内容进行编码
            // 1.解决复制不闭合的html文档,保存后显示错乱的bug
            // 2.解决文本中特殊字符导致的bug
            var editor = "<input type='hidden' name='" + $this.attr("name") + "' value='" + $.base64.btoa($this.summernote('code')) + "' />";
            $form.append(editor);
        }
    });
    
    YUNM.debug("验证通过");
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    比对之前的代码,可以发现代码有两处发生了变化:

    1. 当summernote为空时,之前没有做在bootstrap的validator中,是因为还没有搞清楚summernote这种非input标签在validator中的使用,下面会做详细说明。
    2. 对summernote的内容加上了base64编码处理,这会有很多好处,稍候介绍。

    3、base64的使用方法

    js端我在Bootstrap wysiwyg富文本数据如何保存到mysql这篇文章中做了说明,此处不再说明。

    可能会有同学需要JavaScript端的base64编码,而需要在springMVC后端使用base64的解码,那么此处介绍一个jar包(Java Base64.jar),使用方法很简单,下载好jar包后,就可以使用如下方法解码:

    import it.sauronsoftware.base64.Base64;
    
    deal.setDescription(StringEscapeUtils.escapeHtml(Base64.decode(description, "utf-8")));
       
    • 1
    • 2
    • 3
    • 1
    • 2
    • 3
    1. 首先,base64的import如上,来自于javabase64.jar包。
    2. decode的编码前端js使用的utf-8,此处自然也用utf-8。
    3. 至于StringEscapeUtils类,也是一个非常实用的工具类,有兴趣的可详细关注一下(主要可以对html等等特殊标签进行转义)。

    4、summernote加入到bootstrap validator中

    <div class="form-group">
        <label for="" class="col-md-1 control-label">项目详情</label>
        <div class="col-md-10">
            <div class="summernote" name="description" data-bv-excluded="false" data-bv-notempty placeholder="请对项目进行详细的描述,使更多的人了解你的云梦"
                action="${ctx}/file">${deal.description}</div>
        </div>
    </div>
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. 注意data-bv-excluded=”false”(由于summernote使用了div作为form表单的呈现形式,非一般的input标签,所以此处要将该name=”description”的field标识为非excluded,默认的validator是不对“[‘:disabled’, ‘:hidden’, ‘:not(:visible)’]”三种标签做处理的,而summernote会默认作为disabled的一种,那么设置上data-bv-excluded=”false” 后,validator将会对summernote做非空的判断)、data-bv-notempty属性。
    2. 当然有了上述两个属性后,并不能保证validator的有效性,那么接下来,请继续看。
    onChange : function(contents, $editable) {
    
        if ($this.parents().length > 0) {
            var $form = $this.parents().find("form.required-validate", $p);
    
            if ($form.length > 0) {
                var data = $form.data('bootstrapValidator');
    
                YUNM.debug($this.summernote('isEmpty'));
                if ($this.summernote('isEmpty')) {
                    data.updateStatus($this.attr("name"), 'INVALID');
                } else {
                    data.updateStatus($this.attr("name"), 'VALID');
                }
            }
        }
    
    },
    onInit : function() {
        if ($this.parents().length > 0) {
            var $form = $this.parents().find("form.required-validate", $p);
    
            if ($form.length > 0) {
                var data = $form.data('bootstrapValidator');
    
                if (!$this.summernote('isEmpty')) {
                    data.updateStatus($this.attr("name"), 'VALID');
                }
            }
        }
    },
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    在summernote的callbacks中加入onChange 、onInit,当文本域发生变化、初始化时,对summernote在form中的验证字段进行状态的更新,validator中使用updateStatus方法。

      /**
       * Update all validating results of field
       *
       * @param {String|jQuery} field The field name or field element
       * @param {String} status The status. Can be 'NOT_VALIDATED', 'VALIDATING', 'INVALID' or 'VALID'
       * @param {String} [validatorName] The validator name. If null, the method updates validity result for all validators
       * @returns {BootstrapValidator}
       */
      updateStatus: function(field, status, validatorName) {
    
    OK,等补上以上两个内容后,整个summernote就完整了。
    
    展开全文
  • 页面可视化编辑器设计

    万次阅读 热门讨论 2017-09-04 16:38:37
    页面可视化编辑器   1.背景 a) 公司是一个垂直的药品电商平台主营B2B,初创平台需要大量的营销活动来拉动商业需求。 b) 本人来公司近一年,一直负责营销活动的研发工作,主要有商家促销体系、平台营销、运营...

     

     

    页面可视化编辑器

     

    1.背景

    a) 公司是一个垂直的药品电商平台主营B2B,初创平台需要大量的营销活动来拉动商业需求。

    b) 本人来公司近一年,一直负责营销活动的研发工作,主要有商家促销体系、平台营销、运营数据统计三大块。做了一个初步的统计,从今年2月到8月底累计上线的活动约200个,其中手工开发的页面占到120个左右,每个活动约2~4个页面(h5&pc),算下来几乎1天一个+活动。

    c) 其中包括和运营梳理活动方案,制定各种活动开发方案及实施细节,并和各个部门的研发同学沟通技术细节。

    d) 整体下来从运营活动发起、启动研发、活动上线、后期的数据统计,整个流程下来需要处理的事情非常多。

    e) 经常碰到的一个问题,运营提报活动,并且要求快速上线,研发同事面对的就是没日没夜的加班,通宵已成习惯。但我们做为研发人,不能容忍这种问题长期出现,这时迫不及待地需要一个平台化的系统来支持运营部门的活动需求,加速活动上线流程,降低研发成本。

    2.活动页面可视化编辑器

    a) 先上图

     

    主功能区

     

    主功能区

     

    b) 从工具栏选择相应的UI组件,通过属性编辑器编辑对应的属性并保存到数据库。

    c) 涉及到的后端领域模型如下

     

    领域模型

    d) 前端交互领域模型

     

    前端交互领域模型

     

     

    e) 整体逻辑架构

     

    逻辑架构

    f) 前端渲染时序

     

     

     

    g) 前端渲染实现思路

    i. 编译指令:angular自定义指令,用来加载可视化组件并绑定作用域及参数;

     

    ii. 可视化组件:为页面中展示的一个楼层,

    前端实现:为一个angular自定义指令,一个html,一个组件数据包括配置

    加载过程:由自定义的编译指令通过组件模板配置的html模板地址进行加载并绑定对应的组件配置属性

     

    iii. 组件模板:用来定义一个组件的构成,包括组件名称、唯一code标识、类型、组件所有的配置项

    前端实现:一个工具栏展示图片,一个html模板,一个自定义指令。

    加载过程:在配置页面初始化时展示在工具栏

     

    iv. 属性编辑器:用来为每个组件编辑对应的配置项,通过组件模板code对应到组件模板。

    前端实现:

    加载过程:在每个组件被选中的同时通过组件对应的组件模板code进行切换

    h) 实现思路-数据交互模型

     

    i) 技术栈

     

    j) 开发规范

     

    k) 系统架构

     

     

     

    3.自动产出活动统计数据

     【待续】

    4.未来的工作

     

     

     

     具体的活动页面demo:http://act.mypharma.com/activity/p/411.html

    http://act.mypharma.com/activity/p/4582.html

     很多组件还在规划和研发中,敬请期待

     

    文中图片ppt地址:http://download.csdn.net/download/philip502/9963680

     

    展开全文
  • 关键字: web组态编辑器集成,web组态页面集成,web可视化组件,web SCADA前端和后端,web可视化编辑器 web组态编辑器web图形可视化软件 web可视化编辑器,又称WEB组态可视化软件,即用户可以在web页面编辑器上直接搭建...

    关键字: web组态编辑器集成,web组态页面集成,web可视化组件,web SCADA前端和后端,web可视化编辑器

    web组态编辑器web图形可视化软件

    web可视化编辑器,又称WEB组态可视化软件,即用户可以在web页面编辑器上直接搭建出一个行业应用系统出来。web组态可视化编辑器能够运用在哪些场景中呢?

    web组态可视化编辑器为工程用户提供了二次开发的可能,不用编程、不用写代码,通过可视化界面即可生成web页面,开发出一个应用系统来,在电力、物联网、大数据平台有广泛的应用。

    一个好的应用系统,应该给用户提供业务自定义的工具。一个好的应用系统,应该采用图文并茂的方式将复杂的业务逻辑直观化、可视化。

    多比web组态可视化编辑器

    web组态软件是基于浏览器的应用,通过浏览器操作组态编辑器、浏览组态画面,实现工程管理、设备组态编辑以及设备组态运行三大功能。换句话说,web组态软件分编辑态和运行态。通过实现图元组态、可视化图表组态、数据库组态的配置与关联,网关采集数据的实时推送,实现基于Web服务的实时数据监控和可视化展示,服务端的分项目分权限的多用户访问等等。

    从用户操作与界面呈现的角度来说,WEB组态软件采用标准HTML5技术,基于B/S架构进行开发,支持WEB端跨平台呈现,在浏览器端完成便捷的人机交互,简单的拖拖拽拽即可完成web可视化页面的编排设计工作,让用户不需要编写计算机程序,不需要编程知识,就能通过拖拽和配置实现应用系统的功能。

    从软件架构来说,WEB组态软件包括前端和后端。

    ## 前端:主要实现图形可视化编辑,包括图元、图表等等。

    ## 后端:与数据联动,支持多数据源,包括数据库或设备实时采集等等。

    B/S结构的Web SCADA系统必须具备以下特征

    1,对不同控制对象均可方便组态 ,只是在算法组态时调用不同图形元素。

    2,控制人员用可视化语言进行“画图”编辑 ,鼠标连线。

    3,模块参数可动态改变。
    4,开放性 ,可接入用户自定义模块。
    5,与图形组态软件溶为一体 ,组成易用的图形仿真组态软件。
    6,支持图形化建模,自动化程度高,可维护性好;
    7,支持在线模块级和源代码级的调试;

    Q&A

    web组态是基于ActiveX实现的吗?

    Activex是由微软开发,所以在支持上,目前原生态支持的只有IE,其他浏览器想要支持activex, 需要额外做一些设置或安装补丁包。并且从2015年微软Edge浏览器不再支持ActiveX。

    web组态基于BS架构,采用html5和JavaScript,具有良好的集成性和技术前瞻性。

    web组态的图元库可以扩展吗?

    web组态支持图元自扩展,SVG格式,以支持图形缩放。

    多比组态可视化平台widget

    更多的web组态集成案例,见web SCADA

    总结:web组态编辑器可视化编辑器软件为工程用户提供了二次开发的可能,不用编程不用写代码,通过可视化界面即可生成web页面。web SCADA组态编辑器软件,经过数年的积累和行业用户的需求定制,包括完备的前端(图形可视化)和后端(与数据联动,数据库或实时采集)。web组态可视化平台基于B/S架构,采用html5和JavaScript,具有良好的集成性。

    展开全文
  • 最近基于Eclipse的SpringIDE包括了一个图形工作流的设计器来编辑可视化Spring工作流。SpringWebflow的一个关键方面就是能够模拟一个Web应用作为在工作流中的一系列步骤。一个流程定义了对用户事件作出反应的的用户...
  • TWaver可视化编辑器,经常化身为各种形式,出现在TWaver的demo、产品和各个项目中,帮助企业更好地进行IT可视化管理。今天我们就来深度八一八这个神秘的存在:TWaver的编辑器。
  • JSON数据可视化/JSONEditor,json可视化编辑能力的实现(以表单形式编辑json数据) 使用场景:提供可视化界面编辑json数据内容 技术栈:React/Mobx/Ant Design 特点: 弹性布局,提供大屏和小屏两种展示模式 支持...
  • KindEditor是一套开源的HTML可视化编辑器,主要用于让用户在网站上获得所见即所得编辑效果,兼容IE、Firefox、Chrome、Safari、Opera等主流浏览器。 KindEditor使用JavaScript编写,可以无缝的与Java、.NET、PHP、...
  •   KindEditor是一套开源的HTML可视化编辑器,主要用于让用户在网站上获得所见即所得编辑效果,兼容IE、Firefox、 Chrome、Safari、Opera等主流浏览器。 KindEditor使用JavaScript编写,可以无缝的于Java...
  • 可视化在线编辑器架构设计

    千次阅读 2018-04-12 10:41:45
    现在在线网页编辑器非常火,实现平台也五花八门,这次介绍可视化在线编辑器的设计理念、编辑器具体的功能实现,以及弹性数据流的设计思路。还附带了拓展插件的思考,核心功能规划与插件数据流的组织,帮助你站在更高...
  • Ananas Desktop:可定制的数据集成/分析工具,让非技术人员也能够编辑数据处理作业并按需可视化
  • 要求:对前端技术(JavasScript、HTML、CSS),对可视化技术(Canvas、WebGL)有浓厚的兴趣 基础不好的可培养,基础好的可共谋大事 感兴趣的给我发邮件:hr@servasoft.com ------------------------------------------...
  • Extjs3.2可视化编辑

    热门讨论 2010-05-20 21:06:44
    ExtJS是一个很不错的Ajax框架,可以用来开发带有华丽外观的富客户端应用,使得我们的b/s应用更加具有活力及生命力.ExtJS是一个用javascript编写,与后台技术无关的前端ajax框架.因此,可以把ExtJS用在.Net、Java、Php等...
  • 可视化编辑商城系统也称企业网站程序,系统前台生成html、完全符合SEO、同时有在线客服、潜在客户跟踪、便捷的企业网站管理、搜索引擎推广等功能。 采用拖放技术,具有实时书写和文本编辑功能; 无需代码,自由拖拽...
  • KindEditor是一套开源的HTML可视化编辑器,主要用于让用户在网站上获得所见即所得编辑效果,兼容IE、Firefox、Chrome、Safari、Opera等主流浏览器。 KindEditor使用JavaScript编写,可以无缝的于Java、.NET、PHP、...
  • 可视化交互技术

    千次阅读 2015-08-21 20:26:42
    1人机交互理论 人机交互领域涉及到多个领域的学科,包括计算机科学、系统设计、以及行为科学。...为了高效的在人与机器之间的任务进行转换,开发一种有效的用户交互,最小人需要完成的认知模型与 计算机
  • 数据可视化 (1)可视化 (1)可视化的含义 定义 可视化是一种使复杂信息能够容易和快速被人理解的手段,是一种聚焦在信息重要特征的信息压缩,是可以放大人类感知的图形化表示方法。 可视化为人类大脑与...
  • HTML编辑器-HTML网页表单可视化在线编辑器插件大全 ...比如网站后台提交内容,需要在线编辑(加粗、插入图片、颜色)等样式编辑,留言系统客户留言内容可视化编辑等需求。在源代码模式下可以插入自己写
  • TWaver可视化编辑器(二)3D编辑

    千次阅读 2017-08-11 16:17:38
    作为一款高效、轻量、自带编辑功能小组件,TWaver Java在电信网管界一炮而红,在各大运营商的OSS,BSS,NMS系统中随处可见。
  • 为了优化 OWL 本体的可视化编辑,我们开发了 GrOWL:一个用于 OWL 的浏览器和可视化编辑器,可以准确地可视化 OWL 本体的底层 DL 语义,同时避免冗长的 OWL 语法的困难。 在本文中,我们讨论了 GrOWL 可视化模型和...
  • 针对矿图可视化无法跨浏览器和跨终端、发布流程复杂等问题,并结合当前矿图生成和编辑的现状,提出了一种基于云渲染技术的跨浏览器、多终端矿图可视化系统实现方案。该方案主要是在云端渲染、切片、发布和在GIS前端...
  • Web前端可视化绘图软件编辑

    千次阅读 2020-05-22 14:10:46
    随着物联网、大数据等技术高速发展,我们逐步向数字化、可视化的人工智能(AI)时代的方向不断迈进。智能时代是工业 4.0 时代,我国工业领域正努力从“制造”迈向“智造”的新跨越。 正文:1.mxgraph: 介绍:开源...
  • 可视化实验通过所见即所得的在线配置,降低了运营方使用A/B Testing 的成本,本文就web端可视化实验实现简单分享下。 分析 整个可视化实验流程大体如下: 1. 创建可视化实验,填入目标url; 2. 打开目标页,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 63,724
精华内容 25,489
关键字:

编辑技术网站可视化