使用了 el-upload 的插件,但是一直都是报 404 ,在这个过程中,请注意您使用的 request header 中的 content-type 不同的类型需要的 NodeJS 的 request 解析方式不一样。在 Node 服务端接到用户的数据之后,就需要使用 fs 模块把图片解析成可以保存的文件流,存入磁盘中或者调用后台接口保存。调试了一个小时之后成功通过。把代码留下来,方便日后进行查询。

Vue 层
-------------------------------------
<el-upload class="avatar-uploader" accept="image/jpeg" action="agency/uploadPicForAgency" :data="{agencyId:'1',picFlag:'1'}" :multiple="false" :show-file-list="false" :on-success="handleAvatarSuccess" :before-upload="beforeBackgroundAvatarUpload">
  <img v-if="backgroundUrl" :src="backgroundUrl" width="100%" height="100%" class="avatar">
  <i v-else="" class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>

express 层
-----------------------------------
/**
 * @api {post} agency/uploadPicForAgency 代理商图片上传接口,返回图片存入服务器的URL
 * @apiName agency/uploadPicForAgency
 * @apiGroup agency
 * 
 * @apiParam   {FileObject}   imgInfo    图片文件对象
 * @apiParam   {String}       agencyId   代理商ID
 * @apiParam   {String}       picFlag    图片标记(1-登录页logo图,2-登录页背景图,3-内容页logo图)
 * 
 * @apiParamExample  {type} Request-Example:
 * 传统的FORM表单提交
 * Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryzJ4Ys0x4h70Z8heR
 * 
 * ------WebKitFormBoundaryzJ4Ys0x4h70Z8heR
    Content-Disposition: form-data; name="agencyId"

    000192
    ------WebKitFormBoundaryzJ4Ys0x4h70Z8heR
    Content-Disposition: form-data; name="picFlag"

    1
    ------WebKitFormBoundaryzJ4Ys0x4h70Z8heR
    Content-Disposition: form-data; name="file"; filename="Intel-logo.jpg"
    Content-Type: image/jpeg


    ------WebKitFormBoundaryzJ4Ys0x4h70Z8heR--
 * 
 * @apiSuccessExample {Object} Success-Response:
 * {
 *    message: ""
 *    success: true
 *    data: {
 *      imageStr: "/9j/4AAQSkZJRgABAgAAZABkAAD/7AARRHVja3kAAQAEAAAAXA.............."
        uploadPath: "agency/2019/05/54e4c76df1db41bbbca80c9a7ef16c5f.jpg"
      }
 * }
 */
router.post('/uploadPicForAgency', function (req, res, next) {
  const methodName = 'MAgencyPlatformDataAPI.uploadPicForAgency';
  var form = new formidable.IncomingForm();

  form.encoding = 'utf-8'; //设置编辑
  form.keepExtensions = true; //保留后缀
  form.maxFieldsSize = 2 * 1024 * 1024; //设置单文件大小限制
  form.parse(req, function (err, fields, files) {
    const agencyId = fields.agencyId;
    const picFlag = fields.picFlag;
    var imageBuf = fs.readFileSync(files.file.path);
    var imagebase = imageBuf.toString("base64");
    var imgInfo = {
      imgName: files.file.name,
      imgStr: imagebase,
    }
    util.JSONRPC(methodName, [imgInfo,agencyId,picFlag], function (data) {
      res.json({
        success: true,
        data: {
          uploadPath: data,
          imageStr: imagebase
        },
        message: ''
      })
    }, res);
  })
});

剛好在使用CSS中發現,Google Font的服務一直無法使用。之前國内有 360 的替代方案。後來數字公司的服務就下綫了。導致用戶訪問速度一直非常慢。在網上搜過資料原來很多提供靜態資源,CDN服務的公司都相繼下綫了自己的產品。經過谷歌大法,通過一個個的嘗試最後確定 fonts.loli.net 的業務仍然可以使用。

测试效果如图:


当鼠标划过的时候,需要显示更多的信息,因为GridPanel表格显示区域不够,所以把部分信息移动到 Tip 中显示。代码如下:

store: {
  type: 'EduCartoonStore',
  autoLoad: false,
  listeners: {
      'load': function() {
          // 得到当前的Gird
          var grid = Ext.getCmp('eduCartoonListGrid');
          var view = grid.getView();
          // 创建一个 Tip
          var tip = Ext.create('Ext.tip.ToolTip', {
              target: view.el,
              delegate: view.itemSelector,
              trackMouse: true,
              renderTo: Ext.getBody(),
              listeners: {
                  // 显示Tip
                  beforeshow: function updateTipBody(tip) {
                      var record = view.getRecord(tip.triggerElement);
                      var state = '';
                      var createTime = '';
                      var packageTime = '-';
                      var showHtml = '';
                      // 状态
                      if (record.data.status === 0) {
                        state = "<span style='color:red'>待审核</span>";
                      } else if (record.data.status === 1) {
                        state = "<span style='color:green'>已审核</span>";
                      } else if (record.data.status === -1) {
                        state = "<span style='color:grey'>已关闭</span>";
                      }
                      // 创建时间
                      createTime = formatDate(record.data.ctime);
                      // 打包时间
                      if (record.data.zipGenerateTime !== undefined) {
                        packageTime = formatDate(record.data.zipGenerateTime)
                      }
                      showHtml += '<p><b>ID编号</b>:' + record.data.id + '</p>';
                      showHtml += '<p><b>状态</b>:' + state + '</p>';
                      showHtml += '<p><b>创建时间</b>:' + createTime + '</p>';
                      showHtml += '<p><b>打包时间</b>:' + packageTime + '</p>';
                      tip.update(showHtml);
                  }
              }
          });
      }
  }
}

场景是这样的,用户编辑完文本之后,上传了一张图片。而这个时候并没有让编辑器重新获得焦点。就会导致提交的图片为:themes/default/images/spacer.gif 。原因是上传完成图片之后,数据正确返回但是并没有动态绑定到图片的 src 属性中。这个时候需要手动设置一次富文本编辑框的值,触发 change 事件。修改代码如下:

ueditor.all.js  24518行

function callback(){
try{
	var link, json, loader,
	body = (iframe.contentDocument || iframe.contentWindow.document).body,
	result = body.innerText || body.textContent || '';
	json = (new Function("return " + result))();
	link = me.options.imageUrlPrefix + json.url;
	if(json.state == 'SUCCESS' && json.url) {
		loader = me.document.getElementById(loadingId);
		loader.setAttribute('src', link);
		loader.setAttribute('_src', link);
		loader.setAttribute('title', json.title || '');
		loader.setAttribute('alt', json.original || '');
		loader.removeAttribute('id');
		domUtils.removeClasses(loader, 'loadingclass');
		me.execCommand('inserthtml', '');   // 就是这一行,记住了!!!
	} else {
		showErrorLoader && showErrorLoader(json.state);
	}
}catch(er){
	showErrorLoader && showErrorLoader(me.getLang('simpleupload.loadError'));
}
form.reset();
domUtils.un(iframe, 'load', callback);
}

修改源码后就可以解决这个问题啦。

iPhone设备

物理分辨率是硬件所支持的,逻辑分辨率是软件可以达到的。

代数 设备 操作系统 逻辑分辨率(point) 物理分辨率(pixel) 屏幕尺寸(对角线长度) 缩放因子
iPhone
第一代 iPhone 2G iOS 1 320 x 480 480 x 320 3.5寸 1x
第二代 iPhone 3 iOS 2 320 x 480 480 x 320 3.5寸 1x
第三代 iPhone 3GS iOS 3 320 x 480 480 x 320 3.5寸 1x
第四代 iPhone 4 iOS 4 320 x 480 960 × 640 3.5寸 2x
第五代 iPhone 4S iOS 5 320 x 480 960 × 640 3.5寸 2x
第六代 iPhone 5 iOS 6 320 x 568 1136 x 640 4.0寸 2x
第七代 iPhone 5S/5C iOS 7 320 x 568 1136 x 640 4.0寸 2x
第八代 iPhone 6 iOS 8 375 x 667 1334 x 750 4.7寸 2x
第八代 iPhone 6 Plus iOS 8 414 x 736 2208 x 1242 (1920×1080) 5.5寸 3x
第九代 iPhone 6S iOS 9 375 x 667 1334 x 750 4.7寸 2x
第九代 iPhone 6S Plus iOS 9 414 x 736 2208 x 1242 (1920×1080) 5.5寸 3x
iPhone SE iOS 9 320 x 568 1136 x 640 4寸 2x
第十代 iPhone 7 iOS 10 375 x 667 1334 x 750 4.7寸 2x
第十代 iPhone 7 Plus iOS 10 414 x 736 2208 x 1242 (1920×1080) 5.5寸 3x

如图所示:

这里写图片描述

Tips:

机型 屏幕宽高比
iPhone 5 320÷568=0.563
iPhone 6 375÷667=0.562
iPhone 6 Plus 414÷736=0.5625

最终发现iPhone5和6一个小秘密,它们的比例是不变的

iPod设备

设备 操作系统 物理分辨率(pixel) 屏幕尺寸(对角线长度) 缩放因子
iPod Touch 1 iOS 1 480 x 320 3.5寸 1x
iPod Touch 2 iOS 2 480 x 320 3.5寸 1x
iPod Touch 3 iOS 3 480 x 320 3.5寸 1x
iPod Touch 4 iOS 4 960 x 640 3.5寸 2x
iPod Touch 5 iOS 6 1136 x 640 4.0寸 2x
iPod Touch 6 iOS 8 1136 x 640 4.0寸 2x

iPad设备

设备 操作系统 物理分辨率(pixel) 屏幕尺寸(对角线长度) 缩放因子
iPad 1 iOS 3 1024 x 768 9.7寸 1x
iPad 2 iOS 4 1024 x 768 9.7寸 1x
iPad 3 (The New iPad) iOS 5 2048 x 1536 9.7寸 2x
iPad 4 iOS 6 2048 x 1536 9.7寸 2x
iPad Air iOS 7 2048 x 1536 9.7寸 2x
iPad Air 2 iOS 8 2048 x 1536 9.7寸 2x
iPad Pro iOS 9 2732 x 2048 12.9寸 3x
iPad mini iOS 6 1024 x 768 7.9寸 1x
iPad mini 2 iOS 7 2048 x 1536 7.9寸 2x
iPad mini 3 iOS 8 2048 x 1536 7.9寸 2x
iPad mini 4 iOS 9 2048 x 1536 7.9寸 2x

(经常需要查询的设备像素比:转载自 http://blog.csdn.net/scorpio_27/article/details/52297643)

前端日常工作是什么?拿到PSD图,然后开切!首先我们建立一个app的文件夹,然后在app下,我们建立img文件夹,接下来是 css …… 项目终于搞定之后,我们要使用在线压缩工具把样式表进行合并压缩,把所有的大尺寸图片重新导入Ps,保存为 web 格式,然后调整质量 。。。。。。

你就没想过偷懒的办法么?你就是这么把时间耗在没有意义的简单机械的活儿中么?

或者,很多前端都已经开始使用Grunt这样的工具了,自动压缩、自动打包、文件合并、Less,Sass 编译、文件监控、本地文件服务器调试环境。不错,基于Grunt另外推出了非常多的 generate ,类似 angularJS , Ember , Backbone, webapp, bootstrap 等等等等,但是 Grunt 的学习曲线也是非常陡的。虽然能够通过一些通用脚手架来搭建前端工程作为基础架构。一旦需要修改配置,则是异常痛苦。

gulp 的功效和 Grunt 也是异曲同工、插件也是异常丰富。

通过 Grunt 与 gulp 的对比。我个人还是比较喜欢 gulp 的一些操作模式。不管是从词法、编程语义的理解程度来说。gulp 都是优于 Grunt 。官网的介绍谈到了文件流、以及处理速度。因为没有现成的案例,所以不能妄自评论。下面就浅谈一下两者之间的使用细节。

这里引用?http://markgoodyear.com/2014/01/getting-started-with-gulp/ 中的例子:

Grunt

sass: {
  dist: {
    options: {
      style: 'expanded'
    },
    files: {
      'dist/assets/css/main.css': 'src/styles/main.scss',
    }
  }
},

autoprefixer: {
  dist: {
    options: {
      browsers: [
        'last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4'
      ]
    },
    src: 'dist/assets/css/main.css',
    dest: 'dist/assets/css/main.css'
  }
},

grunt.registerTask('styles', ['sass', 'autoprefixer']);

Gulp

gulp.task('sass', function() {
  return gulp.src('src/styles/main.scss')
    .pipe(sass({ style: 'compressed' }))
    .pipe(autoprefixer('last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4'))
    .pipe(gulp.dest('dist/assets/css'))
});

不管从代码还是从阅读性上来说,Gulp 不是更加优越一些嘛?从代码的输入和输出,以及事件的操作顺序一幕了然,完全不用在复杂的GruntFile中间去找各种各样的变量表达式。妈妈再也不用担心我的学习!

那么我们来一个简单的Case吧。

首先确保你安装了NodeJS。

然后运行Continue reading

偶尔翻出来以前写的一些小东西。做应用开发久了,久而久之一开始就很注意框架的选择。但是最初写Javascript那种alert逐步调试。写一些通用的工具类的时间仿佛离我很远了。翻了下以前的一些代码,把他贴出来。就权当是复习下吧。

/**
 * author:      可乐加糖
 * description: 呵呵,不知道写什么好,随便吧。
 */

(function (window) {
    //基础类库
    var cola = {
        get_node: function (elem_id) {
            if (elem_id.constructor != String) {
                throw new Error("参数传递错误");
                return false;
            }
            return document.getElementById(elem_id) || null;
        },
        prev: function (elem) {
            do {
                elem = elem.previousElementSibling
            } while (elem && elem.nodeType != 1);
            return elem
        },
        next: function (elem) {
            do {
                elem = elem.nextSibling;
            } while (elem && elem.nodeType != 1);
            return elem
        },
        first_node: function (elem) {
            elem = elem.firstChild;
            return elem && elem.nodeType != 1 ? this.next(elem) : elem;
        },
        last: function (elem) {
            elem = elem.lastChild;
            return elem && elem.nodeType != 1 ? prev(elem) : elem;
        },
        parent: function (elem, num) {
            num = num || 1;
            for (var i = 0; i < num; i++) {
                if (elem != null)
                    elem = elem.parentNode;
                return elem;
            }
        },
        remove: function (elem) {
          if(elem)
            elem.parentNode.removeChild(elem);
        },
        empty: function (elem) {
            while(elem.firstChild)
                elem.removeChild(elem.firstChild)
        }
    }
    window.cola = cola;
})(window)

这是最开始写的一个二级下拉导航条的代码

document.getElementsByClassName = function(clsName){
    if(typeof(clsName)!="string" || clsName.constructor != String){
        throw new Error("className必须是字符串");
        return false;
    }
    var nodes = document.getElementsByTagName("*");
    var nodeArr = new Array();
    for(var i= 0,icount = nodes.length;i<icount;i++){
        if(nodes[i].className == clsName)
            nodeArr.push(nodes[i]);
    }
    return nodeArr;
}
window.onload = function(){
    var nodes = document.getElementsByClassName("has-child");
    for(var i= 0,icount = nodes.length;i<icount;i++){
        nodes[i].onmouseover = function(){
            var child_menu = this.getElementsByTagName("ul");
            child_menu[0].style.display = "block";
        }
        nodes[i].onmouseout = function () {
            var child_menu = this.getElementsByTagName("ul");
            child_menu[0].style.display = "none";
        }
    }
}

还有获取元素的样式表,当初也是在IE下调试了很久才搞定。

function getStyle(elem,name){
    if(elem.style[name])
        return elem.style[name];
    else if(elem.currentStyle)
        return elem.currentStyle[name];
    else if(document.defaultView && document.defaultView.getComputedStyle){
        name = name.replace(/([A-Z])/g,"-$1");
        name = name.toLowerCase();
        var s = document.defaultView.getComputedStyle(elem,"");
        return s && s.getPropertyValue(name);
    }
    else
        return null;
}