在 Vue element-ui 中 from 表单常用的正则可以对此封装一次,方便表单验证使用,搜集了一批常用的正则,出处:
https://www.cnblogs.com/zqifa/p/regex-1.html

1.校验数字的表达式

  1. 数字:^[0-9]*$
  2. n位的数字:^\d{n}$
  3. 至少n位的数字:^\d{n,}$
  4. m-n位的数字:^\d{m,n}$
  5. 零和非零开头的数字:^(0|[1-9][0-9]*)$
  6. 非零开头的最多带两位小数的数字:^([1-9][0-9]*)+(.[0-9]{1,2})?$
  7. 带1-2位小数的正数或负数:^(-)?\d+(.\d{1,2})?$
  8. 正数、负数、和小数:^(-|+)?\d+(.\d+)?$
  9. 有两位小数的正实数:^[0-9]+(.[0-9]{2})?$
  10. 有1~3位小数的正实数:^[0-9]+(.[0-9]{1,3})?$
  11. 非零的正整数:^[1-9]\d$ 或 ^([1-9][0-9]){1,3}$ 或 ^+?[1-9][0-9]*$
  12. 非零的负整数:^-[1-9][]0-9″$ 或 ^-[1-9]\d$
  13. 非负整数:^\d+$ 或 ^[1-9]\d*|0$
  14. 非正整数:^-[1-9]\d*|0$ 或 ^((-\d+)|(0+))$
  15. 非负浮点数:^\d+(.\d+)?$ 或 ^[1-9]\d.\d|0.\d[1-9]\d|0?.0+|0$
  16. 非正浮点数:^((-\d+(.\d+)?)|(0+(.0+)?))$ 或 ^(-([1-9]\d.\d|0.\d[1-9]\d))|0?.0+|0$
  17. 正浮点数:^[1-9]\d.\d|0.\d[1-9]\d$ 或 ^(([0-9]+.[0-9][1-9][0-9])|([0-9][1-9][0-9].[0-9]+)|([0-9][1-9][0-9]))$
  18. 负浮点数:^-([1-9]\d.\d|0.\d[1-9]\d)$ 或 ^(-(([0-9]+.[0-9][1-9][0-9])|([0-9][1-9][0-9].[0-9]+)|([0-9][1-9][0-9])))$
  19. 浮点数:^(-?\d+)(.\d+)?$ 或 ^-?([1-9]\d.\d|0.\d[1-9]\d|0?.0+|0)$

2.校验字符的表达式

  1. 汉字:^[\u4e00-\u9fa5]{0,}$
  2. 英文和数字:^[A-Za-z0-9]+$ 或 ^[A-Za-z0-9]{4,40}$
  3. 长度为3-20的所有字符:^.{3,20}$
  4. 由26个英文字母组成的字符串:^[A-Za-z]+$
  5. 由26个大写英文字母组成的字符串:^[A-Z]+$
  6. 由26个小写英文字母组成的字符串:^[a-z]+$
  7. 由数字和26个英文字母组成的字符串:^[A-Za-z0-9]+$
  8. 由数字、26个英文字母或者下划线组成的字符串:^\w+$ 或 ^\w{3,20}$
  9. 中文、英文、数字包括下划线:^[\u4E00-\u9FA5A-Za-z0-9_]+$
  10. 中文、英文、数字但不包括下划线等符号:^[\u4E00-\u9FA5A-Za-z0-9]+$ 或 ^[\u4E00-\u9FA5A-Za-z0-9]{2,20}$
  11. 可以输入含有^%&』,;=?$\」等字符:[^%&』,;=?$\x22]+
  12. 禁止输入含有~的字符:[^~\x22]+

3.特殊需求表达式

  1. Email地址:^\w+([-+.]\w+)@\w+([-.]\w+).\w+([-.]\w+)*$
  2. 域名:[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(/.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+/.?
  3. InternetURL:[a-zA-z]+://[^\s]* 或 ^http://([\w-]+.)+[\w-]+(/[\w-./?%&=]*)?$
  4. 手机号码(可根据目前国内收集号扩展前两位开头号码):^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$
  5. 电话号码(「XXX-XXXXXXX」、」XXXX-XXXXXXXX」、」XXX-XXXXXXX」、」XXX-XXXXXXXX」、」XXXXXXX」和」XXXXXXXX):^((\d{3,4}-)|\d{3.4}-)?\d{7,8}$
  6. 国内电话号码(0511-4405222、021-87888822):\d{3}-\d{8}|\d{4}-\d{7}
    7.15位身份证号:^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{2}$
    8.18位身份证号:^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$
  7. 帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9_]{4,15}$
  8. 密码(以字母开头,长度在6~18之间,只能包含字母、数字和下划线):^[a-zA-Z]\w{5,17}$
  9. 强密码(必须包含大小写字母和数字的组合,不能使用特殊字符,长度在8-10之间):^(?=.\d)(?=.[a-z])(?=.*[A-Z]).{8,10}$
  10. 日期格式:^\d{4}-\d{1,2}-\d{1,2}
  11. 一年的12个月(01~09和1~12):^(0?[1-9]|1[0-2])$
  12. 一个月的31天(01~09和1~31):^((0?[1-9])|((1|2)[0-9])|30|31)$
  13. 钱的输入格式:
    (1)有四种钱的表示形式我们可以接受:」10000.00″ 和 「10,000.00」, 和没有 「分」 的 「10000」 和 「10,000」:^[1-9][0-9]$ (2)这表示任意一个不以0开头的数字,但是,这也意味着一个字符」0″不通过,所以我们采用下面的形式:^(0|[1-9][0-9])$
    (3)一个0或者一个不以0开头的数字.我们还可以允许开头有一个负号:^(0|-?[1-9][0-9])$ (4)这表示一个0或者一个可能为负的开头不为0的数字.让用户以0开头好了.把负号的也去掉,因为钱总不能是负的吧.下面我们要加的是说明可能的小数部分:^[0-9]+(.[0-9]+)?$ (5)必须说明的是,小数点后面至少应该有1位数,所以」10.」是不通过的,但是 「10」 和 「10.2」 是通过的:^[0-9]+(.[0-9]{2})?$ (6)这样我们规定小数点后面必须有两位,如果你认为太苛刻了,可以这样:^[0-9]+(.[0-9]{1,2})?$ (7)这样就允许用户只写一位小数.下面我们该考虑数字中的逗号了,我们可以这样:^[0-9]{1,3}(,[0-9]{3})(.[0-9]{1,2})?$
    (8)1到3个数字,后面跟着任意个 逗号+3个数字,逗号成为可选,而不是必须:^([0-9]+|[0-9]{1,3}(,[0-9]{3})*)(.[0-9]{1,2})?$

备注:这就是最终结果了,别忘了」+」可以用」*」替代如果你觉得空字符串也可以接受的话(奇怪,为什么?)最后,别忘了在用函数时去掉去掉那个反斜杠,一般的错误都在这里

  1. xml文件:^([a-zA-Z]+-?)+[a-zA-Z0-9]+\.[x|X][m|M][l|L]$
  2. 中文字符的正则表达式:[\u4e00-\u9fa5]
  3. 双字节字符:^\x00-\xff
  4. 空白行的正则表达式:\n\s*\r (可以用来删除空白行)
  5. HTML标记的正则表达式:<(\S?)[^>]>.?|<.? /> (网上流传的版本太糟糕,上面这个也仅仅能部分,对于复杂的嵌套标记依旧无能为力)
  6. 首尾空白字符的正则表达式:^\s|\s$或(^\s)|(\s$) (可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等),非常有用的表达式)
  7. 腾讯QQ号:[1-9][0-9]{4,} (腾讯QQ号从10000开始)
  8. 中国邮政编码:[1-9]\d{5}(?!\d) (中国邮政编码为6位数字)
  9. IP地址:\d+.\d+.\d+.\d+ (提取IP地址时有用)
  10. IP地址:((?:(?:25[0-5]|2[0-4]\d|[01]?\d?\d)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d?\d))

使用了 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);
  })
});

React小书:http://huziketang.mangojuice.top/books/react/lesson1
React 开发者学习线路图(2018):https://www.css88.com/archives/10111
React中文文档:https://react.css88.com/
WebPack 4 教程,从零配置到生产发布:https://www.css88.com/archives/9436
React入门教程:https://react.docschina.org/tutorial/tutorial.html#%E8%AF%BE%E5%89%8D%E5%87%86%E5%A4%87
React Router 教程:https://react-guide.github.io/react-router-cn/docs/guides/basics/IndexRoutes.html

因为 Kaminari 使用的必须是 ActiveRecord 对象,一开始报了个错,半天搞不懂,回头自己重新查询了一下资料,其实官方的 Github 上就已经写好了。

把代码贴出来一下其实挺简单的:

def index
user_name
= params[:user_name]
tempSQL = "select o.*,u.user_name,u.nick_name,u.phone,f.title from
orders o, users u, flies f where o.user_id = u.id and o.goods_id = f.id"
if
!user_name.nil?
puts 'run here'
tempSQL += " and u.user_name = '#{user_name}'"
end
@orders
= Kaminari.paginate_array(Order.find_by_sql(tempSQL)).page(params[:page])
end

Kaminari.paginate_array 中已经就能够完成转换功能了,大家继续,玩得开心哦。

Electron的开发相对还是蛮简单的,看了一写简单的资料就能快速上手开发了。留下三篇相对全的资料,以备查询:
开发:https://juejin.im/post/5a6a91276fb9a01cbd58ce32
打包:https://juejin.im/entry/5805e39ad20309006854e58f
https://www.jianshu.com/p/1c2ad78df208
更新:https://segmentfault.com/a/1190000012904543

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

先不多说,上图:

JSDuck文档说明

初步使用,感觉还不错,因为其使用的运行环境是 Gem,所以需要安装 Ruby 相关的环境。安装可以参考链接:https://my.oschina.net/lsjcoder/blog/1583072

常用的命令表如下:

参数 含义
— 或 空 需要生成文档的文件或者目录,也可以是一个集合
–output 文档存放的目录
–config 配置文件路径
–encoding 文档编码方式
–exclude 不生成文档的目录或文件,可以是一个集合
–title 文档标题
–footer 文档标尾
–head-html 文档页面的head中需要添加的内容
–body-html 文档页面的body中需要添加的内容
–css 额外添加的样式​
–welcome 欢迎页面
–guides 向导配置
–examples 示例配置
–categories 分类配置
–images 图片路径配置
–tags 自定义标签
–examples-base-url 示例文件的基础路径
–help 查看命令帮助
–version 查看当前版本

随着 JavaScript 在越来越多的工程中扮演越来越重要的角色,文档的完善越来越重要,同时也开始约束 JSer 慢慢开始学习工程化,系统化的编写代码。开始自我改造吧。

测试效果如图:


当鼠标划过的时候,需要显示更多的信息,因为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);
}

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