zhennann

zhennann 查看完整档案

填写现居城市  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 该用户太懒什么也没留下

个人动态

zhennann 发布了文章 · 6月23日

CabloyJS微信模块、企业微信模块已出齐

前言

Cabloy-企业微信模块完成时,加上之前已完成的Cabloy-微信模块,关于在CabloyJS中与微信/企业微信对接的任务已经完成了。这些模块的目标就是,只需填入各类服务的参数,就可以直接进入具体的业务开发,从而达到开箱即用的效果

背景分析

1. 账户体系

微信/企业微信的开发,有诸多的坑,而最大的坑就是账户体系

  • 首先,微信企业微信是不同的账户体系
  • 微信中,openid是识别用户的唯一标识,如果开通了微信开放平台,那么就可以通过unionid把散落在微信公共号微信小程序等不同服务的openid关联起来,标识同一个用户。那么,如何把openid与系统中的用户系统对接起来?如果开通了unionid,又如何对接?unionid开通之前的旧账户是否也可以平滑迁移?
  • 企业微信中,通过userid来识别每个企业成员。与微信不同的是,企业微信通过部门树来管理成员,从而为资源授权和分配提供组织依据。那么,我们在进行系统初始化的第一步就是要把企业的部门和成员同步到系统中,并实现双向查找功能

2. 杂项

除了纷繁复杂的账户体系对接任务,接下来我们还要面对一些随时开发随时遇到的小问题。因为微信/企业微信开发涉及到的场景、概念和术语有很多,往往需要通读官方文档。而官方文档对有些概念的表述语焉不详,惜墨如金,于是不得不写代码来验证一些猜测,然后再回头重温文档。当这一遍走完把项目完成后,如果有一个新项目,很可能还要再走一遍。这里仅仅列举一些经常出现的问题:

  • JSON与XML:微信公共号的消息推送系统采用XML作为数据格式,而小程序既要支持XML也要支持JSON。同时又有明文模式、兼容模式、安全模式之别。
  • openid/unionid与openId/unionId:在进行账号登录时,微信公共号返回的是openid/unionid,而小程序返回的openId/unionId。如果稍不留意,就掉在坑里了
  • 企业微信支持不同的应用:内置的应用、第三方应用、自建应用、关联的小程序等等。这些应用如何进行架构,提供统一便捷的API调用模型
  • 企业微信小程序本体是微信小程序,然后关联到企业微信成为一个企业微信应用。只有明白了这些概念和关系,才能准确的进行对接

核心目标

1、常见微信API SDK的问题

不同的开发语言,都能找到对应的微信API SDK。但这些微信API SDK往往是把官方提供的http api进行了一个语言层面的封装,很少走得更远的。比如,通过微信API SDK,我们可以很方便的获取AccessToken,可以获取User信息,但是如何把获取到的User与系统对接,如何处理openid和unionid之间的关联,仍然需要我们自己设计和开发

再比如,通过微信API SDK可以很方便的获取企业微信的部门和成员。但是如何与系统中的角色和用户对接,并支持不同的场景和登录方式,仍然有大量的工作需要做

2. 开箱即用

而CabloyJS的微信/企业微信模块的核心目标就是提供开箱即用的效果。只需配置好参数,所有的对接工作全部自动完成,使我们一步跨过纷繁的细节,直接进入具体的业务开发当中。当然,CabloyJS的微信/企业微信模块是完全开源的,我们仍然可以从中清晰地看到这些细节,以及处理的方式

为什么CabloyJS可以做到开箱即用的效果?就是因为CabloyJS是全栈NodeJS框架,可以把前端组件后端服务数据存储与访问等等,有机的结合在一起

比如,CabloyJS模块通过这些前后端的配合,直接实现了企业微信中部门与用户的同步工作,甚至还通过CabloyJS底层提供的SocketIO机制实现了前端同步进度的实时显示

contacts-sync-zhcn

两大亮点

基于CabloyJS全栈业务开发框架本身提供的特性,使得Cabloy-企业微信模块具有以下两个显著的亮点:

1. PC、Mobile自适应

许多企业微信应用存在这样一个问题:在Mobile端只能使用一部分功能,其他更多功能则需要登录PC系统进行操作

而CabloyJS框架下的Cabloy-企业微信允许所有的业务功能同时支持PC和Mobile使用。同时又能保证以下两点:

    1. 通过角色权限系统,控制不同用户使用不同功能
    2. 前端页面采用异步加载策略,从而适应大型项目的开发
    • Mobile端效果

    mobile-zhcn

    • PC端效果

    pc-zhcn

    2. 数据孤岛

    企业从不同服务提供商采购不同的企业微信应用,必然导致数据孤岛的出现,而且这些数据散存在不同服务商的后台,缺乏数据联动与共享机制

    而CabloyJS框架本身就是基于业务模块构建的。企业自建的模块或者使用第三方的模块,都汇集在一个CabloyJS项目之中,并进行私有部署,从而从根本上解决了数据孤岛的问题,不仅能实现数据联动与共享,也可以更灵活的进行数据采集、处理和分析

    归根结底一句话,数据和程序都掌握在自己的手中

    特性

    基于CabloyJS全栈框架提供的便利性和灵活性,Cabloy-企业微信主要有如下特性:

    1. 一站式整合

    当前整合了企业微信自建应用企业微信小程序的接口,具体如下:

    场景名称说明
    自建应用消息推送系统自动完成接口对接,并对用户进行认证
    自建应用网页登录自动跳转微信登录,并对用户进行认证
    自建应用网页JSSDK自动注入JSSDK,并自动完成配置
    小程序后台登录接口自动完成接口对接,并对用户进行认证
    小程序前端SDK提供SDK,便于企业微信小程序前端直接访问CabloyJS后端API

    2. 开箱即用

    只需配置好企业微信账号参数,所有接口自动完成对接

    3. 多小程序支持

    企业微信可以关联多个企业微信小程序,因此,模块也提供了多小程序支持

    4. 多站点支持

    通过CabloyJS提供的多实例特性,可以实现多站点支持,比如为不同的企业提供企业微信服务。请参见:EggBornJS:多实例

    参考链接

    查看原文

    赞 0 收藏 0 评论 0

    zhennann 发布了文章 · 6月4日

    CabloyJS V3.2.0支持Socket IO

    CabloyJS v3.2.0引入了Socket IO,并且实现了统一的在线推送离线推送机制

    效果演示

    1. IM

    用户向系统发送一条消息,系统通过websocket在线通道向用户推送一条回复

    im

    2. 进度条

    系统通过websocket在线通道向前端实时推送任务的进度

    progress

    项目配置

    升级到该版本,请更新以下项目配置:

    1. 增加redis连接信息

    请依次修改测试环境开发环境生产环境的配置,这里以开发环境为例

    {project}/src/backend/config/config.local.js

      // redis
      ...
      const __redisConnectionDefaultIO = Object.assign({}, __redisConnectionDefault, {
        keyPrefix: `io_${appInfo.name}:`,
      });
    
      config.redisConnection = {
        ...
        io: __redisConnectionDefaultIO,
      };
    
      config.redis = {
        clients: {
          redlock: config.redisConnection.default,
          ...
          io: config.redisConnection.io,
        },
      };

    2. Nginx配置

    在Nginx配置中添加/socket.io/的转向

    {project}/docker-compose/config/nginx/conf.d/nginx.conf

      ...
      location /socket.io/ {
        proxy_http_version 1.1;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Host $server_name;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $http_host;
        proxy_set_header X-NginX-Proxy true;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_pass http://$node_ip:$node_port$request_uri;
        proxy_redirect off;
        proxy_buffer_size 64k;
        proxy_buffers   4 32k;
        proxy_busy_buffers_size 64k;
      }
    查看原文

    赞 0 收藏 0 评论 0

    zhennann 发布了文章 · 5月2日

    CabloyJS - GitHub Readme

    简体中文 | [English](https://community.cabloy.com/articles/cabloy-github-readme.html)
    
    # CabloyJS
    
    CabloyJS是一款顶级NodeJS全栈业务开发框架, 基于KoaJS + EggJS + VueJS + Framework7
    
    [![NPM version][npm-image]][npm-url]
    [![David deps][david-image]][david-url]
    [![NPM download][download-image]][download-url]
    [![Join the chat at https://gitter.im/cabloy/main](https://badges.gitter.im/cabloy/main.svg)](https://gitter.im/cabloy/main?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
    
    [npm-image]: https://img.shields.io/npm/v/cabloy.svg?style=flat-square
    [npm-url]: https://npmjs.org/package/cabloy
    [david-image]: https://img.shields.io/david/zhennann/cabloy.svg?style=flat-square
    [david-url]: https://david-dm.org/zhennann/cabloy
    [download-image]: https://img.shields.io/npm/dm/cabloy.svg?style=flat-square
    [download-url]: https://npmjs.org/package/cabloy
    
    ## 文档
    
    - [官网 && 文档](https://cabloy.com)
    
    ## 在线演示
    
    |网站类型|网站链接|
    |--|--|
    |管理系统(PC布局)|[https://admin.cabloy.com](https://admin.cabloy.com)|
    |管理系统(Mobile布局)|![cabloy-demo-qrcode](https://admin.cabloy.com/api/a/file/file/download/f4f26271d3134ff88a993f8d6374ea9d.png)|
    |||
    |博客|[https://zhennann.com](https://zhennann.com)|
    |技术文档(英文)|[https://cabloy.com/index.html](https://cabloy.com/index.html)|
    |技术文档(中文)|[https://cabloy.com/zh-cn/index.html](https://cabloy.com/zh-cn/index.html)|
    |社区(英文)|[https://community.cabloy.com/index.html](https://community.cabloy.com/index.html)|
    |社区(中文)|[https://community.cabloy.com/zh-cn/index.html](https://community.cabloy.com/zh-cn/index.html)|
    |Cabloy商店(英文)|[https://store.cabloy.com/index.html](https://store.cabloy.com/index.html)|
    |Cabloy商店(中文)|[https://store.cabloy.com/zh-cn/index.html](https://store.cabloy.com/zh-cn/index.html)|
    
    ## 资源
    
    - [CabloyJS 商店](https://store.cabloy.com)
    - [CabloyJS 社区](https://community.cabloy.com)
    - [CabloyJS Awesome](./docs/awesome.zh-CN.md)
    
    ### 文章
    
    - [一文读懂NodeJS全栈开发利器:CabloyJS(万字长文)](https://community.cabloy.com/zh-cn/articles/known-cabloyjs.html)
    
    ### 视频
    
    - [网易免费课程 - CabloyJS全栈业务开发实战](https://study.163.com/course/courseMain.htm?courseId=1209403891)
    
    ## 截图
    
    ### 1. 自适应布局
    
    #### 1.1 PC布局
    
    ![layoutpc-zhcn](https://admin.cabloy.com/api/a/file/file/download/162577be0a914a87bf4eb7233b2baccf.gif)
    
    #### 1.2 Mobile布局
    
    ![layoutmobile-zhcn](https://admin.cabloy.com/api/a/file/file/download/ba364be52fe94a838bff69b4e3be8c25.gif)
    
    ### 2. 国际化
    
    ![i18n](https://admin.cabloy.com/api/a/file/file/download/412fb208b3c2476595b85483da03d996.gif)
    
    ### 3. 主题
    
    ![theme-zhcn](https://admin.cabloy.com/api/a/file/file/download/d0874ebb68d84bc58f9cd1cb7c78a7d1.gif)
    
    ### 4. 拖拽
    
    #### 4.1 移动
    
    ![move-zhcn](https://admin.cabloy.com/api/a/file/file/download/cd3448052b4940cdb6eb6149d477bd54.gif)
    
    #### 4.2 调整尺寸
    
    ![resize-zhcn](https://admin.cabloy.com/api/a/file/file/download/3ce656778fda4433aabaa73ef48e0402.gif)
    
    ### 5. 仪表盘
    
    #### 5.1 如何使用仪表盘
    
    ![use-zhcn](https://admin.cabloy.com/api/a/file/file/download/223b45ad67fb41ef94463310f416f44f.gif)
    
    #### 5.2 如何创建新的仪表盘
    
    ![profile-zhcn](https://admin.cabloy.com/api/a/file/file/download/6b7bcf62d4704a85a155ed26f4761157.gif)
    
    ### 6. PC布局
    
    #### 6.1 头部按钮
    
    ![header-zhcn](https://admin.cabloy.com/api/a/file/file/download/fa05f8e237ff4507ac565fdf1b851b9f.gif)
    
    #### 6.2 左边栏
    
    ![sidebar-left-zhcn](https://admin.cabloy.com/api/a/file/file/download/b4933a97a5b3454a925f3bec32919229.gif)
    
    #### 6.3 右边栏
    
    ![sidebar-right-zhcn](https://admin.cabloy.com/api/a/file/file/download/93336dcd875b489e959ce8823d8ac082.gif)
    
    #### 6.4 状态栏(左)
    
    ![statusbar-left-zhcn](https://admin.cabloy.com/api/a/file/file/download/b99c2feb281e45eb95bb95360f446699.gif)
    
    #### 6.5 状态栏(右)
    
    ![statusbar-right-zhcn](https://admin.cabloy.com/api/a/file/file/download/7b9492e010ea4aaf963d3bd283e5da9e.gif)
    
    ## 定位
    
    CabloyJS是面向`中小开发团队`和`接单侠`开发的NodeJS全栈业务快速开发框架,支持全场景业务开发,省时、省力
    
    ### 中小开发团队或接单侠所面临的困境
    
    1. `多场景需求`:PC、Android、IOS、微信、钉钉,等等
    2. `多种要素平衡`:技术、人才、时间、质量 -> 成本、收益
    
    客户的潜在需求日益多变,因此开发场景日益碎片化,不同的场景又面临着不同的技术选择,从而又决定着人员的配备,`技术选择`与`人员配置`影响着项目开发的`时间`与`质量`,从而又最终体现在`成本`与`收益`的平衡上
    
    ### CabloyJS应对之法
    
    1. 采用`pc = mobile + pad`的独特页面风格,把移动端的开发体验与用户操控体验带入pc端,一套代码适应全场景需求
    2. CabloyJS不仅是`技术框架`,更是`业务框架`,将`用户管理`、`角色管理`、`权限管理`等诸多功能特性沉淀成核心模块,从而为快速业务开发提供强有力的支撑
    3. 彻底的`前后端分离`体系,从而实现`前端灵活多变,后端不变应万变`,使整个CabloyJS架构具有很强的灵活性和延展性
    
    ## 理念
    
    > 既可快速开发,又可灵活定制
    
    为了实现此理念,CabloyJS内置开发了大量核心模块,使您可以在最短的时间内架构一个完整的Web项目。比如,当您新建一个Web项目时,就已经具备完整的用户登录与认证系统,也具有验证码功能,同时也具备`用户管理`、`角色管理`、`权限管理`等功能
    
    此外,这些内置模块提供了灵活的定制特性,您也可以开发全新的模块来替换内置模块,从而实现系统的定制化
    
    ## 风格
    
    CabloyJS决不重复制造轮子,而是在业界流行技术框架基础之上做的`继承再创新`,比如前端基于`VueJS + Framework7`,后端基于`KoaJS + EggJS`
    
    有了这些底层框架的强大支撑,CabloyJS就可以放开手脚在业务层面做大量的创新与沉淀,从而将`NodeJS全栈开发`的体验带入一个新的层面
    
    因此,CabloyJS的风格就是`别具一格`,一旦用上就会`爱不释手`,因为`顺畅而愉悦`的全栈开发体验本来就应该是这样
    
    ## 亮点与痛点
    
    ### 1. 亮点:pc = mobile + pad
    
    CabloyJS最大的亮点是:通过`pc=mobile+pad`的模式,把mobile场景的`操控体验`和`开发模式`带⼊pc场景。既显著减少了代码开发量,提升了开发效率,⼜保持了用户操控体验的⼀致性
    
    ### 2. 痛点:全场景业务开发
    
    CabloyJS最大的痛点是:通过模块化的架构设计,可以快速开发全场景业务
    
    |场景|前端|后端|
    |--|--|--|
    | PC:Web | CabloyJS前端 |CabloyJS后端|
    | PC:Exe | CabloyJS前端 + Electron |CabloyJS后端|
    | Mobile:IOS | CabloyJS前端 + Cordova |CabloyJS后端|
    | Mobile:Android | CabloyJS前端 + Cordova |CabloyJS后端|
    |微信公共号| CabloyJS前端 + 微信API |CabloyJS后端|
    |企业微信| CabloyJS前端 + 微信API |CabloyJS后端|
    | 钉钉 | CabloyJS前端 + 钉钉API |CabloyJS后端|
    | Slack | CabloyJS前端 + Slack API |CabloyJS后端|
    | 小程序:微信、支付宝、百度等 |小程序框架|CabloyJS后端|
    
    * 后端:由于完整的前后端分离设计,只需开发一套CabloyJS后端代码即可
    * 前端:所有可基于H5的场景,只需开发一套CabloyJS前端代码即可
    
    ## CabloyJS的开发历程
    
    CabloyJS从2016年启动开发,主要历经两个开发阶段:
    
    ### 1. 第一阶段:EggBornJS
    
    EggBornJS关注的核心就是`模块化`与`模块隔离`,并以此实现一套完整的全栈开发框架
    
    比如模块`egg-born-front`是框架前端的核心模块,模块`egg-born-backend`是框架后端的核心模块,模块`egg-born`是框架的命令行工具,用于创建项目骨架
    
    这也是为什么所有业务模块都是以`egg-born-module-`为命名前缀的原因
    
    ### 2. 第二阶段:CabloyJS
    
    EggBornJS只是一个基础的全栈开发框架,如果要进行业务开发,还需要考虑许多与业务相关的支撑特性,如:`用户管理`、`角色管理`、`权限管理`、`菜单管理`、`参数设置管理`、`表单验证`、`登录机制`,等等。特别是在前后端分离的场景下,对`权限管理`的要求就提升到一个更高的水平
    
    CabloyJS在EggBornJS的基础上,提供了一套核心业务模块,从而实现了一系列业务支撑特性,并将这些特性进行有机的组合,形成完整而灵活的上层生态架构,从而支持具体的业务开发进程
    
    ## CabloyJS架构图
    
    ![cabloy](https://admin.cabloy.com/api/a/file/file/download/856010163ead499b8d23454c9b5bbd45.png)
    
    ## 特性
    
    ## EggBornJS特性
    
    - **前后端分离**:前后端分离开发,深度解耦
    - **业务模块化**:`页面组件`和`业务逻辑`按模块组织
    - **加载方式灵活**:模块既可`异步加载`,也可`同步加载`
    - **模块高度内聚**:模块包括`前端页面组件`和`后端业务逻辑`
    - **参数配置灵活**:模块中的前后端可以单独进行`参数配置`
    - **国际化**:模块中的前后端均支持独立的`国际化`
    - **模块隔离**:模块的`页面、数据、逻辑、路由、配置`等元素均进行了`命名空间隔离`处理,避免模块之间的变量污染与冲突
    - **超级易用的事务处理**:只需在路由记录上配置一个参数,即可完美实现数据库的`事务处理`
    - **渐进式开发**:由于模块的高度内聚,可以将业务以模块的形式沉淀,在多个项目中重复使用,既可贡献到npm开源社区,也可部署到公司内部私有npm仓库
    
    > 有了EggBornJS,从此可复用的不仅仅是组件,还有业务模块
    
    ## CabloyJS特性
    
    - **移动优先,完美的PC布局适配:pc = mobile + pad**
    - **统一的原子数据管理**
    - **统一的用户角色权限管理**
    - **对多种运行环境的精细支持**
    - **支持多域名多实例运行**
    - **灵活的测试驱动开发**
    - **内置众多核心模块,提供大量核心特性**
    
    > 有了CabloyJS,您就可以快速开发各类业务应用
    
    ## 信念
    
    > 凡是可以用JavaScript来写的应用,最终都会用JavaScript来写 | Atwood定律
    
    相信,Javascript的深度探索者都会被这句名言激发,共同努力,为Javascript生态添砖加瓦,构建更繁荣的应用生态
    
    CabloyJS正是对这一名言的探索之作。CabloyJS不重复造轮子,而是采用业界最新的开源技术,进行全栈开发的最佳组合
    
    欢迎您也加入CabloyJS的社区生态,一起促进Javascript的繁荣与应用
    
    ## 名称的由来
    
    ### 1. EggBorn
    
    这个名称的由来比较简单,因为有了Egg(后端框架),所以就有了EggBorn。有一部动画片叫《天书奇谭》,里面的萌主就叫“蛋生”,我很喜欢看(不小心暴露了年龄😅)
    
    ### 2. Cabloy
    
    Cabloy来自蓝精灵的魔法咒语,拼对了Cabloy这个单词就会有神奇的效果。同样,CabloyJS是有关化学的魔法,基于模块的组合与生化反应,您将实现您想要的任何东西
    
    ## License
    
    [LGPL](./LICENSE)
    查看原文

    赞 0 收藏 0 评论 0

    zhennann 发布了文章 · 5月1日

    CabloyJS v3.1.0支持集群及更多 🎉

    在抗疫期间,CabloyJS v3.1.0设计并开发了大量特性,并且所有相关文档已集齐。强烈建议大家试用,拍砖

    特性 - 后端核心

    • 集群: 集群现在已经成为CabloyJS的一等公民。也就是说,CabloyJS项目随时做好准备,以便部署到集群环境中

      • Redis: 集群基于Redis
      • Queue: 基于bottleneck & bullmq重构了支持集群的队列
      • Schedule: 基于队列重构了支持集群的定时任务
      • Broadcast: 基于Redis重构了支持集群的广播
      • Cache: 基于Redis重构了支持集群的缓存
      • Startup: 重构了启动项
      • Docker Compose: 在项目根目录有一个docker-compose.yml配置文件。如果您已经安装好了docker compose环境,那么可以一键启动CabloyJS所有的服务,包括Redis、MySQL、Nginx、以及CabloyJS后端服务
    • Module Monkey: 使用CabloyJS提供的Module Monkey机制,使我们可以像猴子🐒一样,很轻易的对模块的某些组件进行替换

    特性 - 前端核心

    特性 - 业务模块

    查看原文

    赞 0 收藏 0 评论 0

    zhennann 发布了文章 · 5月1日

    高度灵活可定制的PC布局:头部按钮、左边栏、右边栏、状态栏

    什么是自适应布局

    CabloyJS提供了一套布局管理器,实现自适应布局

    关于自适应布局的概念,强烈建议先阅读以下两篇文章:

    1. 自适应布局:pc = mobile + pad
    2. 自适应布局:视图尺寸

    什么是PC布局

    CabloyJS内置了一套Mobile布局PC布局。其中,PC布局提供了更丰富的布局元素和交互体验。

    PC布局是由模块a-layoutpc实现的,如果对底层的实现机制感兴趣,可以直接查看模块a-layoutpc的源码,这里重点说明PC布局的风格,以及如何定制,如何进行二次开发

    演示

    1. 头部按钮

    header-zhcn

    2. 左边栏

    sidebar-left-zhcn

    3. 右边栏

    sidebar-right-zhcn

    4. 状态栏(左)

    statusbar-left-zhcn

    5. 状态栏(右)

    statusbar-right-zhcn

    查看原文

    赞 0 收藏 0 评论 0

    zhennann 发布了文章 · 5月1日

    一个支持数据绑定与数据联动的Dashboard

    什么是仪表盘

    仪表盘是不同部件的组合,可以在一个页面集中显示各类信息,方便用户集中查看信息、并快速处理业务

    关于制作部件,请参见:制作部件

    CabloyJS仪表盘的特点

    1. 更灵活的自适应能力,可以针对mobile/pad/pc自动调整布局
    2. 支持拖放调整部件的次序
    3. 支持拖放调整部件的宽度
    4. 支持部件之间数据的联动,从而让部件的数据流动起来
    5. 支持部件组,从而支持部件横向纵向之间的布局
    6. 用户可以定制仪表盘
    7. 用户可以添加更多仪表盘,并进行便捷的切换

    仪表盘演示

    1. 如何使用仪表盘

    use-zhcn

    2. 如何创建仪表盘

    profile-zhcn

    查看原文

    赞 0 收藏 0 评论 0

    zhennann 发布了文章 · 4月26日

    Cabloy-CMS中区块的开发与效果

    关于区块

    Cabloy-CMS引入了区块的概念,通过区块可以快速向文章添加各种类型的内容,比如:插入一个地图子页面、插入一首音乐,等等

    Cabloy-CMS中的区块可以类比于Wordpress古腾堡编辑器中的区块。古腾堡编辑器以区块编辑为主线,而Cabloy-CMS仍以Markdown编辑为主线,然后根据需要向文章添加合适的区块。具体效果如何,欢迎大家拍砖

    Cabloy-CMS通过模块cms-pluginblock提供了几个内置的区块,我们还可以使用第三方区块,或者自己开发区块

    使用演示

    cms-block-zhcn

    查看原文

    赞 0 收藏 0 评论 0

    zhennann 发布了文章 · 4月24日

    一个全新的Vue拖拽特性实现:“调整尺寸”部分

    关于拖拽

    CabloyJS提供了完备的拖拽特性,可以实现移动调整尺寸两大类功能,这里对调整尺寸的开发进行阐述

    关于移动的开发,请参见:拖拽:移动

    演示

    resize-zhcn

    开发步骤

    下面以模块test-party为例,说明拖拽(调整尺寸)的开发步骤

    完整源码请参见文件src/module/test-party/front/src/kitchen-sink/pages/dragdrop/dragdropResize.vue,这里只说明开发要点

    1. v-eb-dragdrop

    通过directive v-eb-dragdrop向需要实现调整尺寸的DOM元素附加拖拽特性

    更多情况下,我们并不是拖拽DOM元素本身,而是拖拽与DOM元素相对应的手柄元素
        <div class="test-dragdrop-resize-element" :style="{width:width+'px',height:height+'px'}">
          <span class="resize-handler-col" v-eb-dragdrop="getDragdropContext('col')"></span>
          <span class="resize-handler-row" v-eb-dragdrop="getDragdropContext('row')"></span>
        </div>
        getDragdropContext(resizeDirection) {
          return {
            scene: this.dragdropScene,
            resizable: true,
            resizeDirection,
            onDragStart: this.onDragStart,
            onDragMove: this.onDragMove,
            onDragEnd: this.onDragEnd,
          };
        },

    我们向v-eb-dragdrop传入一个拖拽Context对象,具体参数如下:

    名称说明
    scene应用场景,用于隔离不同的拖拽元素组,通常我们使用便捷方法Vue.prototype.$meta.util.nextId('dragdrop')创建一个唯一值
    resizable标明此拖拽是用于调整尺寸
    resizeDirection拖拽方向,col/row
    onDragStart当启动拖拽时激发
    onDragMove当拖动时激发
    onDragEnd当拖拽行为结束时激发

    拖拽事件

    1. onDragStart

    当启动拖拽时激发。我们可以通过此事件返回一个tooltip信息,进行友好的提示

        onDragStart({ $el, context }) {
          const isRow = context.resizeDirection === 'row';
          const size = this.$view.sizeExtent;
          const tooltip = isRow ? this.height : this.width;
          return { size, tooltip };
        },
    • 参数
    名称说明
    $el拖拽手柄元素
    context拖拽Context对象
    • 返回值
    名称说明
    size当前拖拽元素所属容器的尺寸,当拖动时便于准确计算偏移量的百分比。如果不关心移动的百分比信息,size可以返回null
    tooltip拖拽元素的提示信息

    2. onDragMove

    当拖动时激发

        onDragMove({ $el, context, diff }) {
          const isRow = context.resizeDirection === 'row';
          if (!isRow) {
            let diffAbs = parseInt(diff.abs.x);
            if (diffAbs === 0) return;
            this.width += diffAbs;
            const tooltip = this.width;
            return { eaten: true, tooltip };
          } else {
            let diffAbs = parseInt(diff.abs.y);
            if (diffAbs === 0) return;
            this.height += diffAbs;
            const tooltip = this.height;
            return { eaten: true, tooltip };
          }
        },
    • 参数
    名称说明
    $el拖拽手柄元素
    context拖拽Context对象
    diff拖动时的偏移量
    diff.abs偏移量的绝对值表示
    diff.percent偏移量的百分比表示

    关于diff.percent:

    1. 在Grid布局中,往往通过百分比来布局DOM元素。如果给这些DOM元素启用拖放特性来调整尺寸,那么调整的尺寸也将是百分比。可以参考仪表盘部件的拖放实现
    2. 如果要得到准确的diff.percent信息,必须在事件onDragStart中返回Grid布局容器的size信息
    • 返回值
    名称说明
    eaten如果当前传入的diff偏移量有效,就设置eaten:true,从而重新计算新的diff偏移量
    tooltip拖拽元素的提示信息

    关于eaten:

    • 在Grid布局中,往往通过百分比来布局DOM元素。而这些百分比不是连续值。因此,需要拖动一定的像素才认为是一个有效的百分比变更。这时,我们就需要返回eaten:false,告知系统当前的偏移量需要累积,直到一个认可的偏移量出现,然后再返回eaten:true

    3. onDragEnd

    当拖拽行为结束时激发。如果需要执行清理工作,可以响应此事件

        onDragEnd({ $el, context }) {
          // do nothing
        },
    • 参数
    名称说明
    $el拖拽手柄元素
    context拖拽Context对象
    查看原文

    赞 1 收藏 0 评论 0

    zhennann 发布了文章 · 4月24日

    一个全新的Vue拖拽特性实现:“移动”部分

    关于拖拽

    CabloyJS提供了完备的拖拽特性,可以实现移动调整尺寸两大类功能,这里对移动的开发进行阐述

    关于调整尺寸的开发,请参见:拖拽:调整尺寸

    演示

    move-zhcn

    开发步骤

    下面以模块test-party为例,说明拖拽(移动)的开发步骤

    完整源码请参见文件src/module/test-party/front/src/kitchen-sink/pages/dragdrop/dragdropMove.vue,这里只说明开发要点

    1. v-eb-dragdrop

    通过directive v-eb-dragdrop向需要实现移动的DOM元素附加拖拽特性

      <f7-list class="test-dragdrop-move-list">
        <eb-list-item v-for="(item,index) of items" :key="item" :title="item" :badge="getBadge(item,index)" v-eb-dragdrop="getDragdropContext(item)">
        </eb-list-item>
      </f7-list>
        getDragdropContext(item) {
          return {
            scene: this.dragdropScene,
            item,
            onDragElement: this.onDragElement,
            onDragStart: this.onDragStart,
            onDropElement: this.onDropElement,
            onDropLeave: this.onDropLeave,
            onDropEnter: this.onDropEnter,
            onDragDone: this.onDragDone,
            onDragEnd: this.onDragEnd,
          }
        },

    我们向v-eb-dragdrop传入一个拖拽Context对象,具体参数如下:

    名称说明
    scene应用场景,用于隔离不同的拖拽元素组,通常我们使用便捷方法Vue.prototype.$meta.util.nextId('dragdrop')创建一个唯一值
    item与当前拖拽元素相关的自定义值
    onDragElement当初始化拖拽特性时激发
    onDragStart当启动拖拽时激发
    onDropElement返回当前拖拽目标元素
    onDropLeave当鼠标移出拖拽目标元素时激发
    onDropEnter当鼠标移入拖拽目标元素时激发
    onDragDone当一个有效的拖拽行为完成时激发
    onDragEnd当拖拽行为结束时激发

    2. 拖拽样式

    当鼠标移入一个有效的拖拽目标元素时,会自动给这个DOM元素添加一个data属性data-dragdrop-drop。我们可以通过CSS样式来高亮显示当前拖拽目标元素

    .test-dragdrop-move-list {
      li {
        &[data-dragdrop-drop] {
          background: rgba(128, 128, 128, 0.5);
        }
      }
    }

    更完整的data属性清单如下:

    名称说明
    data-dragdrop-element可拖拽元素
    data-dragdrop-drag当前拖拽源元素
    data-dragdrop-drop当前拖拽目标元素

    拖拽事件

    1. onDragElement

    当初始化拖拽特性时激发,如果拖拽手柄与拖拽源元素不同,可通过此事件返回拖拽手柄对应的拖拽源元素

        onDragElement({ $el, context }) {
          // return undefined or
          return dragElement;
        },
    • 参数
    名称说明
    $el拖拽手柄元素
    context拖拽Context对象
    • 返回值
    名称说明
    undefined如果拖拽手柄与拖拽源元素相同,可以返回undefined,或者不必响应此事件
    dragElement返回与拖拽手柄对应的拖拽源元素

    2. onDragStart

    当启动拖拽时激发。我们可以通过此事件返回一个tooltip信息,进行友好的提示

        onDragStart({ $el, context, dragElement }) {
          const indexDrag = this.__getItemIndex(context.item);
          this.indexDragIndex = indexDrag;
          const tooltip = context.item;
          return { tooltip };
        },
    • 参数
    名称说明
    $el拖拽手柄元素
    context拖拽Context对象
    dragElement拖拽源元素,有可能与$el不同
    • 返回值
    名称说明
    tooltip拖拽源元素的提示信息

    3. onDropElement

    返回当前拖拽目标元素。可以基于DOM元素之间的位置关系来判断当前元素是否可以作为拖拽目标

        onDropElement({ $el, context, dragElement, dragContext }) {
          const indexDrop = this.__getItemIndex(context.item);
          const indexDrag = this.__getItemIndex(dragContext.item);
          if (indexDrop === indexDrag || indexDrop == indexDrag + 1) return null;
          // dropElement
          const dropElement = $el;
          // tooltip
          const tooltip = context.item;
          // ok
          return { dropElement, tooltip };
        },
    • 参数
    名称说明
    $el拖拽目标的手柄元素
    context拖拽目标的Context对象
    dragElement拖拽源元素
    dragContext拖拽源的Context对象
    • 返回值
    名称说明
    null如果当前元素不可作为拖拽目标,就返回null
    dropElement当前拖拽目标元素
    tooltip当前拖拽目标元素的提示信息

    4. onDropLeave

    当鼠标移出拖拽目标元素时激发

    当鼠标移出拖拽目标元素时,系统会自动移除DOM元素中的data属性data-dragdrop-drop。因此,一般而言,可以通过CSS样式来切换拖拽目标的高亮显示。我们仍然可以通过此事件定制格外的行为

        onDropLeave({ $el, context, dropElement }) {
          this.indexDropIndex = -1;
        },
    • 参数
    名称说明
    $el拖拽目标的手柄元素
    context拖拽目标的Context对象
    dropElement拖拽目标元素

    5. onDropEnter

    当鼠标移入拖拽目标元素时激发

    当鼠标移入拖拽目标元素时,系统会自动向DOM元素添加data属性data-dragdrop-drop。因此,一般而言,可以通过CSS样式来切换拖拽目标的高亮显示。我们仍然可以通过此事件定制格外的行为

        onDropEnter({ $el, context, dropElement }) {
          const indexDrop = this.__getItemIndex(context.item);
          this.indexDropIndex = indexDrop;
        },
    • 参数
    名称说明
    $el拖拽目标的手柄元素
    context拖拽目标的Context对象
    dropElement拖拽目标元素

    6. onDragDone

    当一个有效的拖拽行为完成时激发

        onDragDone({ $el, context, dragElement, dropElement, dropContext }) {
          const indexDrag = this.__getItemIndex(context.item);
          this.items.splice(indexDrag, 1);
          const indexDrop = this.__getItemIndex(dropContext.item);
          this.items.splice(indexDrop, 0, context.item);
        },
    • 参数
    名称说明
    $el拖拽源的手柄元素
    context拖拽源的Context对象
    dragElement拖拽源元素
    dropElement拖拽目标元素
    dropContext拖拽目标的Context对象

    7. onDragEnd

    当拖拽行为结束时激发。如果需要执行清理工作,可以响应此事件

        onDragEnd({ $el, context, dragElement }) {
          this.indexDragIndex = -1;
        },
    • 参数
    名称说明
    $el拖拽源的手柄元素
    context拖拽源的Context对象
    dragElement拖拽源元素
    查看原文

    赞 1 收藏 0 评论 0

    zhennann 发布了文章 · 2月7日

    NPM Error:gyp: No Xcode or CLT version detected!

    问题

    最近在macOS Catalina中使用npm安装模块,经常会出现如下错误:

    
    > node-gyp rebuild
    
    No receipt for 'com.apple.pkg.CLTools_Executables' found at '/'.
    
    No receipt for 'com.apple.pkg.DeveloperToolsCLILeo' found at '/'.
    
    No receipt for 'com.apple.pkg.DeveloperToolsCLI' found at '/'.
    
    gyp: No Xcode or CLT version detected!
    gyp ERR! configure error 
    gyp ERR! stack Error: `gyp` failed with exit code: 1
    gyp ERR! stack     at ChildProcess.onCpExit (/usr/local/lib/node_modules/npm/node_modules/node-gyp/lib/configure.js:351:16)
    gyp ERR! stack     at ChildProcess.emit (events.js:210:5)
    gyp ERR! stack     at Process.ChildProcess._handle.onexit (internal/child_process.js:272:12)
    gyp ERR! System Darwin 19.3.0
    gyp ERR! command "/usr/local/bin/node" "/usr/local/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "rebuild"
    gyp ERR! cwd /Users/yangjian/Documents/temp/test001/node_modules/fsevents
    gyp ERR! node -v v12.13.0
    gyp ERR! node-gyp -v v5.0.5
    gyp ERR! not ok 
    • 截图如下

    1

    解决方案

    1. 尝试用如下命令进行修复

    $ xcode-select --install

    系统提示如下信息:

    xcode-select: error: command line tools are already installed, use "Software Update" to install updates

    而事实上并没有所谓的"Software Update"可以更新

    2. 正确姿势

    一筹莫展之际,找到如下解决方案:

    $ sudo rm -rf $(xcode-select -print-path)
    $ xcode-select --install

    请参见:

    查看原文

    赞 1 收藏 1 评论 0

    认证与成就

    • 获得 10 次点赞
    • 获得 0 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 0 枚铜徽章

    擅长技能
    编辑

    (゚∀゚ )
    暂时没有

    开源项目 & 著作
    编辑

    (゚∀゚ )
    暂时没有

    注册于 2018-07-17
    个人主页被 263 人浏览