# HUI-VUE-PRO 总体设计

# 1 背景

在新技术体系下,hui-vue 2.0 并不能满足所有的开发需求;系统公司产品要统一的还包括页面布局以及共性业务控件的通用性;在 hui-vue 2.0 之后,系统业务控件和布局控件也需要包含一次大的调整,来对接新规范。

hui-vue-pro 和 hui-vue 不同,需要的是跨部门之间的协作,每个人维护单个项目,并整合到 hui-vue-pro 中。项目中的控件需要独立打包和版本发布,但对于整个 hui-vue-pro 也需要统一的 api 展示,共同维护;包与包之前也可能存在相互依赖的关系;

如果说 hui-vue 可以使用与互联网等别的项目,那 hui-vue-pro 就是更适用与新技术体系的存在,他帮助开发人员解决更多细节的问题。同时也为不了解新技术体系的同事提供相关文档支持。

# 2 设计目标

  1. 软件设计
  2. 包管理设计
  3. 其他框架设计
  4. 布局设计
  5. 业务控件设计
  6. 其他设计(统一工具 API,校验规范等)

# 2.1 设计约束

  1. hui 设计规范 约束
  2. hui-vue 2.0 约束,必须基于 hui 之上
  3. hui-pro 设计规范 约束
  4. Layout 页面布局规范 约束
  5. 官网单个页面展开在 2s 以内
  6. 支持新浏览器选型

# 3 软件设计

# 3.1 整体设计方案选型

# 3.1.1 Monorepo 设计体系

# 3.1.1.1 hui-vue 模式存在的问题

当前大部分UI框架设计的 Webpack 配置都相对复杂,例如 ElementAnt Design VueMuse-UI以及 hui-vue 1.x 等 Vue 组件库。例如 hui-vue 1.x,为了实现业务层面的两种引入形式(完整引入按需引入),以及抛出一些可供业务层面通用的 utilsi18n 等,Webpack 配置非常复杂。

一般UI组件库的设计者将引入形式设计成 完整引入按需引入 两种形式:

  • 完整引入:开发成本低,针对一些大型业务或者对于构建体积不是特别注重的业务
  • 按需引入:开发成本高,开发的颗粒度相对精细,可以减少业务的构建体积以及SPA应用的首屏加载时间等

构建整个 hui-vue 1.x 组件库的脚本命令繁多,构建的代码之间互相还有引用关系,对于开发的引用路径也会产生一定的约束。因此设计类似于 hui-vue 1.x 的 UI 框架相对开发者而言需要一定的开发门槛(至少需要了解 Webpack 的整体构建机制才能在开发中注意对构建会有影响的模块、第三方库等引入方式)。

随着新技术体系的发展,hui-vue 2.0对 hui-vue 1.x 的框架设计进行了整体优化,采用了 Vue CLI 3库构建 能力简化了 Webpack 的配置,使得 hui-vue 2.0 的构建配置更加轻盈并提升了配置的可维护性。 同时在 Demo 演示网站的设计方面,hui-vue 2.0摒弃了 markdown-it 结合 vue-markdown-loader 实现 md 格式文档演示的方案(Webpack 配置复杂、扩展性差以及需要额外实现国际化功能等,具体可查看 hui-vue 2.0 的总体设计文档),采用了对 Vue 语法支持友好的 Vuepress(Vue 驱动的静态网站生成器)设计 hui 控件演示 Demo 的静态网站(采用 Vuepress 设计静态网站的优势详见 hui-vue 2.0 的总体设计文档)。

hui-vue-pro 将沿用 hui-vue 2.0 的整体框架设计,采用 Vuepress 设计演示 Demo 的静态网站,使用 Vue CLI 3 的库构建对组件进行构建处理(包含了 Webpack 4.x 版本以及 Babel 7.x 版本的特性)。但是 hui-vue-pro 的定位和 hui-vue 2.0 是不同的,是为了满足各个事业部对于复杂业务场景的需求(hui-vue 2.0 基础组件库不能满足的某些可复用的复杂业务场景,需要抽离成通用复杂组件进行设计,这些通用的复杂组件可以定位成 hui-vue-pro 的组件,当然也包括 hui-vue 2.0 基础组件库没有的布局组件)。因此 hui-vue-pro 会带来一系列新的问题:

  • 各个事业部可能会加入 hui-vue-pro 的组件库设计,如何规范和约束开发流程以及制定开发标准
  • 如何快速的发布版本从而解决各个事业部对于组件的依赖问题,但又要保证 hui-vue-pro 组件库的整体稳定性
  • 如何保证各个事业部对于 hui-vue-pro 组件开发的质量问题
  • 组件的数量在未来随着业务的增长会越来越多,如何解决引入体积变大的问题(类似于 hui-vue 2.0 的按需引入产生的开发成本,开发者甚至是脚手架更愿意整体引入组件库,如何解决完整引入体积过大的问题)

如果沿用 hui-vue 2.0 的所有框架设计是不能解决 hui-vue-pro 组件库所面临的上述问题,于是需要探索一种新的UI组件库设计思路。这期间讨论了N种方案,甚至包括让各个事业部自己设计符合各自业务的组件库,当然这会产生一系列新的问题:

  • 解耦各个事业部的组件库设计会导致各个事业部之间的协作减少,技术沟通减少,组件库质量参差不齐
  • 各个事业部的设计和开发成本增加,且需要开发人员专门维护各自的组件库设计
  • 各个事业部之间的UI设计可能不协调不统一
  • 如果组件库存在相似的情况,则造成了重复设计开发,浪费开发资源
# 3.1.1.2 hui-vue-pro 采用 Monorepo 设计体系

在一系列的讨论和探索之后,组内借鉴了 Vue CLI 3 以及 Babel 7.x 的结构设计提出了 Monorepo(Monorepo is a unified source code repository used by an organisation to host as much of its code as possible.)的概念,决定在 hui-vue-pro 的设计中预研出一种新的基于 Monorepo 的设计方案,这种设计方案将使得 hui-vue-pro 组件库产生以下特点:

  • 结构特点:各个事业部的组件开发将通过 hui-vue-pro 组件库设计规范进行约束,hui-vue-pro 作为容器将承接开发的 Eslint 校验规则、Stylelint 校验规则、Vue CLI 3 构建配置容器、代码提交说明规范、版本发布规范、演示 Demo 地址以及浏览器兼容性配置等,但各个事业部的组件都由各自进行开发、维护以及版本发布(在同一个项目仓库地址的项目目录下形成多 npm 包结构体系,每一个组件都是一个自成体系的npm包)。
  • 配置特点:如果需要进行构建处理,每个组件(单组件npm包)可单独进行构建配置,同时结合 Vue CLI 3 的库构建能力,对于简单UI组件的构建几乎可以做到 Webpack 零配置(需要特殊的 Loader处理除外),同时和 hui-vue 2.0 的构建相比,最大的优势在于各个组件之间的构建配置互不影响,都是基于 Vue CLI 3 默认 Webpack 配置的用户层 Webpack 再配置处理,同时单个组件所产生的 Webpack 也会变得非常简单,变得比 hui-vue 2.0 的构建配置更加简单和可维护(可理解为一套通用的 Webpack 配置会为每一个需要构建的组件单独执行一遍构建)。
  • 发布特点:单个组件可快速发布 PATCH 修复版本,从而不会像 hui-vue 那样在发布前需要确保组件库的所有组件的稳定性(hui-vue 由专门的发布人员进行整体组件的版本发布,hui-vue-pro 将有组件的维护者控制自己设计和维护的组件版本)。单个组件的发布一方面可以快速解决个事业部对于不同组件的依赖响应速度过慢问题(例如项目提测期间无法快速解决由 hui-vue-pro 造成的缺陷等),另外一方面不会对 hui-vue-pro 的其他组件造成影响(除非组件和组件之间还形成了特殊的依赖关系)。
  • 引入特点:采用 scope 包的形式发布,从而使得开发者在按需引入时可以快速区分组件的来源是 hui-vue 还是 hui-vue-pro 组件库。
  • 按需加载:由于采用单组件 scope 形式发布单 npm 包,因此 hui-vue-pro 的组件库设计不需要考虑 hui-vue 1.x 的多组件入口文件构建,也不需要对CSS样式进行 Gulp 多入口构建处理,天然形成了按需加载,开发者在引入 hui-vue-pro 的组件库时可以根据项目对于特定组件的需求进行指定引用,从而不会造成 hui-vue 整体引入的副作用(例如 hui-vue 的使用对于大部分在项目中不会使用的组件会进行构建处理,从而增加了项目整体的构建体积,造成资源浪费)。
  • 开发调试:对于新的按需加载引入方式,决定采用 hui-vue 2.0 的处理方式,对于没有特殊 Webpack 配置处理的组件将不再进行构建处理(如果有特殊 Loader 不构建需要在业务层面进行 Webpack 构建配置,增加了开发者对于组件库引入的开发成本),虽然这样造成了使用场景的局限性,但是对于采用海豚脚手架或者 Vue CLI 3 工具生成的项目而言(这是大多数项目的创建方式),无疑可以防止 Webpack 的二次构建,从而减少业务项目的构建体积,同时采用源码的引入方式非常方便组件开发者对于某些特殊业务场景产生问题的复现、调试与修复。

当然采用 Monorepo 体系进行组件库设计,也会产生一些新的问题:

  • 完整引入:将不再提供完整引入功能,从而增加了业务层面的单组件引入开发成本。
  • UMD导出:在 hui-vue 的组件设计中,将导出UMD模式的组件库,提供对于非模块化或者其他简单场景的开发需求,但是在 hui-vue-pro 中将不再提供 UMD 模式导出,限制了使用场景。
  • 开发成本:由于 hui-vue-pro 采用了 Monorepo 的设计体系,因此需要理解整个UI组件库的项目目录结构,并且需要使用 Lerna 配合 YarnWorkspace 特性进行页面的开发调试,需要了解这些特性,同时也需要制定新组件的开发规范和评审规范(组件是否可以纳入到 hui-vue-pro 组件库),并且对于组件的发布也需要了解 npm 的发布流程(组件开发者甚至都没有发布过 npm 包)。
  • 构建成本:由于构建时采用单组件独立构建的模式,在进行总体构建(总体发布版本时需要总体构建)时需要将所有的组件都构建一遍,随着需要构建的组件越来越多,可能会产生构建时间越来越长的问题。

Yarn 的 Workspace 是诸如 Lerna 这样的工具可以(并且正在)利用的底层机制。 它们将永远不会试图提供像 Lerna 那么高级的功能,但通过实现该解决方案的核心逻辑和 Yarn 内部的连接步骤,能够提供新的用法并提高性能。

# 3.1.2 Monorepo 管理工具

参考了 Vue CLI 3 以及 Babel 7.x 的 Monorepo 设计体系,采用 Lerna 配合 YarnWorkspace 作为组件库的体系实现工具。Lerna 是一个 Monorepo 管理工具,使所有的组件(npm 包)设计都集成在一个 Git 仓库里,同时可以利用 yarn 的 Workspace 特性,模拟发布的组件环境,从而使组件的开发和测试变得简单,不需要多次进行组件的发布测试(这里用 Lerna 进行 Vue CLI 插件开发也非常方便),同时 Lerna 还集成了版本发布工具,可以快速的对 UI 框架进行版本发布。Lerna 的特点如下:

  • 统一的 Eslint、单元测试、Webpack 构建以及发布流程(对于使用 Vue CLI 作为容器的优势)
  • 易于协调组件之间的修改(如果组件之间有依赖关系)
  • 单一的提 issues 的仓库地址(相对于各个事业部采用各自的组件库设计)
  • 模拟发布的 npm 包环境从而提升发布组件的版本稳定性(利用 Lerna 集成 Yarn 的 Workspace 特性)
  • 集成版本发布工具(自动检测需要发布的 npm 包)
  • 可用于设计和维护大型项目,例如 Babel、Vue CLI 等(组件数目增加时采用 hui-vue 的设计形式将变得难以管理和维护)

更多关于 Lerna 所带来的优势以及劣势,可查看 Why is Babel a monorepo?

# 3.1.3 构建工具版本选型

为了降低构建和维护的成本,hui-vue-pro 将和 hui-vue 2.0 保持一致的构建工具选型设计方案,将采用最新的 Webpack 4.x 版本进行库构建(Webpak 4.x 版本的理念是实现零配置,受到 Parcel 打包工具的启发,尽可能的让开发者降低项目的配置成本),选用 Webpack 4.x 将带来以下一些优势:

  • 引入构建模式(mode),为开发和生产环境提供不同的默认配置,极大的简化了 Webpack 配置
    • 开发模式:更利于浏览器调试(Tooling for in browser debugging)、更快的增量编译速度(Fast incremental compilation for a fast development cycle)以及更详细的报错消息(Useful error messages at runtime)
    • 生产模式:自动压缩构建输出的文件,更小的构建体积(Small output size)、更快速的运行时处理(Fast code at runtime)、去除无用的开发态代码(Omitting development-only code)、不会暴露源码以及源文件路径(Not exposing source code or file paths)、快速的静态资源输出(Easy to use output assets)、Scope hoisting 和 Tree-shaking
  • 插件优化:Webpack 4.x 在插件方面最大的改动是删除了 CommonsChunkPlugin 插件,使用内置的 optimization.splitChunksoptimization.runtimeChunk,默认会为构建执行代码切割(这个功能在 UI 组件的构建中没有作用,但是在海豚 2.x 的脚手架设计中会产生影响)
  • 代码构建优化:Webpack 4.x 版本升级对 Tree-shaking 进行了优化处理,将会在构建时去除未引用的代码(Dead branches are now removed by webpack itself),从而使得库在引用第三方 API 构建时会有较大幅度的构建体积缩减(持续跟踪版本升级的过程中,仍然会有更优的解决方案)

更多可参考Webpack官方的v4.0.0发布日志

Babel 转译方面,hui-vue-pro 将和 hui-vue 2.0 一样采用 Babel 7.x 版本,采用新版本最主要的原因是支持 polyfill 的按需加载,从而减少构建体积。Babel 7.x 带来的优势如下:

  • 可按运行环境动态配置 Babel
  • @babel/preset-env 插件集代替 babel-preset-es2015,并持续享受它带来的一系列优势(例如集成了babel-preset-es2015, babel-preset-es2016以及babel-preset-es2017、并且可以配合 browserslist 实现 polyfill的按需加载 等)
  • 速度提升、代码优化
  • 添加注解,方便 Uglify 等插件移除无用代码
  • 支持扩展原生内置元素 (Array、Error 等)
  • TypeScript 支持(未来对 hui-vue-pro 的 TypeScript 支持铺垫)
  • 支持 TC39 提案语法

更多可参考Babel官方的发布说明 Babel 7 Released

# 3.1.4 开发和构建容器选型

hui-vue-pro 采用 Monorepo 的设计体系,并为了和 hui-pro 2.0 保持同样的技术选型方案,将使用 Vue CLI 3 工具作为开发和构建的容器。Vue CLI 3 在 2.x 版本的基础上新增了很多功能,例如构建目标/库浏览器兼容性以及插件系统等。在 hui-vue-pro 中采用 Vue CLI 3 工具进行设计,将会带来一系列构建和开发的新特性。

在库构建方面:

  • 提升可维护性:无需关注 Webpack 4.x 的版本升级,Vue CLI 3 官方会维护 Webapck 版本
  • 减少 Webpack 配置:Vue CLI 3 默认提供了一套基础UI库的 Webpack 构建配置,同时允许外部通过 vue.config.js 文件更细粒度的控制其内部配置(内部 Webpack 配置通过 webpack-chain 维护),并且提供 vue inspect 查看最终UI库构建所需要的 Webpack 配置信息,可大大减少库构建的 Webpack 配置
  • 库构建:VUE CLI 3 的库构建默认支持构建 CommonJS 包和 UMD 包,并可提取库所需要的 CSS 文件
  • 默认的 Webpack 配置完善:Vue CLI 3 对于库构建的 Webpack 配置非常完善,包括缓存处理配置、静态资源构建优化配置、完善的CSS预处理器配置、Eslint 配置等
  • 浏览器兼容性处理:VUE CLI 3 集成了@babel/preset-env 和 autoprefixer,由于这两者同时支持 browserslist,因此对于 JavaScript 和 CSS 的构建可以做到更优的浏览器兼容性处理(JavaScript 的按需加载 polyfill 以及自动添加 CSS 浏览器前缀)

在开发方面:

# 3.1.5 静态演示网站设计选型

在 hui-vue 1.x 中,演示 Demo 采用了 markdown-it 结合 vue-markdown-loader 对 Demo 中 md 文件格式的 vue 语法进行解析,并使用 cooking watch 热启动从而实现 hui-vue 1.x 控件的开发态调试,而构建部署演示 Demo 的静态资源则需要使用 cooking build 命令构建,这就需要对演示 Demo 额外产生一套 Webpack 配置,并且配置还需要区分开发态和生产态,这使得配置变得非常复杂并且难以维护。

在 hui-vue-pro 的设计中,将摒弃 hui-vue 1.x 演示 Demo 的设计模式,选用对 Vue 语法支持友好的 Vuepress(Vue 驱动的静态网站生成器)设计 hui 控件的演示 Demo,采用 Vuepress 的优势如下:

  • 提升可维护性:无需关注 Webpack 4.x 的版本升级,Vuepress 官方会维护 Webapck 版本。
  • 减少 Webpack 配置:Vuepress 默认提供了一套 Webpack 配置,同时允许外部通过 config.js 文件更细粒度的控制其内部配。
  • 支持灵活的用户层 Webpack 配置,从而可实现 Eslint 热校验(修改代码自动校验 Eslint)。
  • 内置 Markdown 拓展:内置的 Markdown 配置可丰富演示文档展示的多样性。
  • 自定义 Demo 演示主题:Vue 驱动的自定义主题系统可以丰富 hui-vue-pro 演示 Demo 的交互性,演示 Demo 的交互扩展性可以随着版本的迭代更加灵活多变。
  • 天然支持多语言:在 hui-vue 1.x 中对于多语言的支持需要额外的设计,在 Vuepress 中可天然支持多语言。
  • 天然支持Demo搜索:支持 Algolia 搜索

# 3.2 框架设计

# 3.2.1 总体目录结构设计

hui-pro-vue 的目录结构设计如下:

.
├── .gitlab/                 		// Issues模板配置
├── bin/                 			// Node脚本
├── changelog/                 		// 升级日志
├── config/                 		// Webpack 配置
├── doc/                 			// 规范文档
├── docs/                 			// VuePress 静态网站设计
├── node_modules/         			// 容器依赖和 hui-pro-vue 的组件依赖
├── packages/                 		// workspaces(单 npm 包组件库)
├── preview/                 		// 布局调试页面
├── .browserslistrc  				// 目标浏览器配置
├── .cz-config.js   				// cz 定制化提交说明配置
├── .eslintignore   				// eslint 忽略配置
├── .eslintrc.js   					// eslint 规则配置
├── .gitignore   					// git 忽略配置
├── .gitlab-ci.yml   				// gitlab 持续集成配置
├── .lintstagedrc					// lint-staged 配置
├── .prettierrc						// 代码格式优化配置
├── .stylelintrc					// CSS 校验配置
├── babel.config.js					// vue cli 的 babel 配置
├── lerna.json						// lerna 配置
├── package.json					// vue cli 容器描述文件(容器不是 npm 包)
├── postcss.config.js				// postcss 配置
├── README.md						// 说明文档
└── vue.config.js					// 通用的组件构建配置文件

整体使用 Vue CLI 3 作为开发容器,包含了 Eslint、Webpack、Babel、Browserslist 等默认功能(通过 Vue CLI 3 的插件系统集成)。除此之外额外加入了 Lerna、Stylelint、Gitlab Issues 模板和 CI 集成、lint-staged(Git增量提交代码的Eslint检测)、cz(Git提交说明)、husky(Git钩子工具)等。

# 3.2.2 Lerna 设计

在 Vue CLI 3 容器的基础上,使用 lerna init 实现 hui-pro-vue 组件库的 Monorepo 结构,Lerna 工具在 lerna.json 中进行配置:

{
  "useWorkspaces": true,
  "npmClient": "yarn",
  "version": "1.4.0",
  "packages": [
    "packages/*"
  ]
}

配置说明:

  • useWorkspaces:使用 Yarn 的 Workspaces 特性,使项目中的 npm 包软链接到 node_modules 中,从而可以模拟实现 npm 包的真实发布情况,可以配合 Lerna 更好的管理依赖,从而利于开发、测试和版本发布
  • npmClient:由于使用了 Yarn 的 Workspaces 特性,因此需要使用 Yarn 作为依赖安装工具
  • version:Lerna 总体版本控制,所有 npm 包共用这个集中版本(作为 hui-pro-vue 的总体版本控制)
  • packages:将 packages 文件夹下的组件作为独立的 npm 包,可进行 PATCH 版本的独立发布

# 3.2.3 单个组件(npm 包)的结构设计

packages 目录放置各个事业部的可抽象复杂组件,这些复杂组件在 hui-vue-pro 组件库中的开发需要符合以下目录结构:

.
├──packages/                    // workspaces(单npm包组件库)
│   └── plan                    // plan组件,npm 包名为 @hui-pro/plan
│      ├── lang                 // 国际化 (可选)
│      │  ├── en_US.js          // 英文
│      │  └── zh_CN.js          // 中文
│      ├── lib                  // 构建目录(可选)
│      ├── node_modules         // 当前组件的依赖
│      ├── src                  // 源文件
│      ├── theme                // 样式
│      │  ├── ...
│      │  └── index.scss        // 样式入口文件
│      ├── ...                  // image/font/util 等文件夹(这里的 util 指内部不对外抛出)
│      ├── index.js             // 组件源文件入口文件
│      ├── package.json         // npm 包描述文件
│      ├── README.md		    // 说明文件
│      ├── babel.config.js      // babel 配置文件(可选)
│      └── vue.config.js        // Vue CLI3 配置文件(可选)

其中 package.json 是单个组件的包描述文件,包含了当前组件的依赖列表(例如第三方库、hui-vue-pro 的 @hui-pro/theme@hui-pro/locale 以及 @hui-pro/utils 等)、开发态依赖列表、开发作者、包入口文件、描述、包名称、构建脚本、以及版本等信息,如下所示:

{
  "name": "包名称",
  "version": "包版本",
  "description": "包描述",
  "main": "包入口文件",
  "author": "开发作者",
  "scripts": {构建脚本},
  "files": [npm包文件列表],
  "devDependencies": {开发态依赖},
  "dependencies": {包依赖}
}

files 如果不设置,将抛出当前 npm 包的所有文件。

其中的 vue.config.js 将继承外部 Vue CLI 3 的库构建的默认通用配置,只需要简单的针对当前组件增加一些额外特殊的配置即可。themelang 文件不进行构建处理,从而使得业务层对于当前组件的多语言和样式引入进行统一处理,同时约束成统一目录结构的最大好处在于后续的可扩展性,我们都知道组件的按需引入是会产生开发成本的,组件开发者需要知道业务层使用的特定组件从而一一引入它们,对单组件进行强约束可以在业务层面的引入减少出错的几率,同时也为了后期可以开发出引入 hui-vue-pro 组件的特定工具,从而减少开发成本。

从上面的结构可以发现,当前 npm 包就是一个小型的 hui-vue,只是 hui-vue 构建的是所有包含的基础组件,而当前npm包只是构建和发布当前单个组件,从而做到了各个事业部在同一套约束规范下的开发、构建以及发布的解耦。

关于单个组件的开发规范可查看新增控件流程

# 3.2.4 版本发布规范以及发布流程

hui-vue-pro 版本管理分为 Lerna 总体版本和 npm 包独立版本,总体版本采用 lerna publish 进行总体发布,此时会强行将各个 npm 包版本保持到和 Lerna 发布的版本同步(因此 Lerna 是进行统一 npm 包版本管理)。npm 包独立版本由各个组件的开发人员自行维护,通过 npm publish 自行发布 PATCH 版本(不允许发布 MINORMAJOR 版本)。

Lerna 总体版本

  • Lerna 总体版本在 lerna.jsonversion 字段说明
  • Lerna 发布版本不能是 PATCH 版本变动
  • Lerna 的 MINOR 版本变动 2 周 1 次
  • Lerna 正式发布版本的起始主版本号为 1(1.x.y)
  • Lerna 发布版本的同时会对项目进行打 tag,tag 和 Lerna 版本保持一致
  • Lerna 在整体框架有调整的情况下(例如变迁 Vue 3 等)需要发布 MAJOR 版本
  • Lerna 发布版本前需要通过所有的 npm 包的单元测试

通过 lerna publish 发布所有 npm 包的统一版本号,使所有 npm 包统一提升 MINOR 版本或 MAJOR 版本,通过 Leran 发布版本的同时会对 @hui-pro 整体进行打 tag。

单个 npm 包版本

  • 新增组件的起始版本需要和 Lerna 版本保持一致(不建议将不稳定的 0.x.y 版本的控件放入 @hui-pro)
  • 单个 npm 包的 MINOR 版本在任何时刻和 Lerna 发布的 MINOR 版本保持一致
  • 单个 npm 包可以快速发布 PATCH 版本,以保证快速修复问题(通过 npm publish 独立发布当前 npm 包的修订号)
  • 单个 npm 包有新功能应该等待 Lerna 发布新的MINOR版本时进行合并(除非这个新功能比较急切使用可以快速发布 PATCH 版本)
  • 单个 npm 包不允许有非兼容性更新(除非 Lerna升级 MAJOR 版本)
  • 多个 npm 包之间互相引用必须是同一个 MINORMAJOR 版本,以此来保证相互之间的兼容性
  • 单个 npm 包发布 PATCH 版本的频率各自开发者视情况而定
  • 单个 npm 包发布需要跑通单元测试

关于单个组件包的发布流程可查看 npm发布流程

# 3.2.5 Lerna 总体版本的发布方案

hui-pro 由于使用 Monorepo 机制,各个控件单独发布 PATCH 版本,因此在 gitlab 上并没有控件 PATCH 对应的 tags,这样就不利于做代码的回溯,因此需要制定 MINOR 版本发布策略:

  • 每两周发布一个 MINOR 版本,届时有改动的控件都会自动提升到最新的版本号。

发布版本使用命令:

lerna publish

他会创建一个新的 release,生成新的版本,执行 git commit/tag 并发布到 npm,以下是具体流程:

  1. 发布项目里的每个模块
  2. 执行 lerna updated 确定是否有控件需要发布版本
  3. 假如需要发布,给 lerna.json 版本号做自增
  4. 更新 package.json 里的版本号至最新
  5. 为新版本更新 dependencies
  6. 为新版本创建一个 git commit 和 tag`
  7. 发布更新项目到 npm
  8. 一次发布所有 packages,删除 lerna-temp tags 和增加 tagslatest

通过上面流程,可以给所有提交过代码或者升级过依赖的控件,升级到版本 v1.1.0,如果希望所有控件都升级版本,可以使用命令:

lerna publish --force-publish=*

# 3.2.6 代码提交规范

Git 每次提交代码,都要写 commit message,良好的 commit message 可以对项目管理维护起到积极的作用:

  • 提供更多的历史信息,方便快速浏览
  • 可以过滤某些 commit(比如文档改动),便于快速查找信息
  • 可以直接从 commit 生成 changelog

这里引入 commitizenconventional-changelog 插件来规范 commit message,并做到日志自动化生成,参考文章

为了生成的日志能够直观且有效,需要从两个维度对提交记录进行归类:标识scpoe

标识

每个提交记录都需要一个标识来区分提交代码的类型,表明这段代码提交的目的,以下是所有标识:

标识 描述
feat 新增一个功能
fix 修复一个 Bug
docs 文档变更(更新文档、Demo)
ci 发布控件版本
style 代码风格变更(不影响功能,解决代码冲突、eslint 校验修改等)
refactor 重构(即不是新增功能,也不是修改bug的代码变动)
chore 开发工具变动(框架改动、eslint规则变动等)
test 新增测试
perf 改善性能
revert 代码回退(如果当前 commit 用于撤销以前的 commit)

scpoe

每个提交记录除了标识,还需要定义各自的 scope,将记录分配到具体的控件下面,比如可能的 scope 有:

  • layout
  • page
  • pwd-input
  • 等等...

有了标识scope,还需要指定代码提交原则,让自动生成的日志有效:

  • 提交代码要谨慎,featfix 会自动生成 changelog,其他标识不会,因此选择 featfix 时要考虑是否是新增功能或者修复缺陷。
  • 提交代码要仔细,scpoe 请选择对应的控件,不要错选或者选择 empty/custom 等默认 scope,默认 scope 只有特殊情况下使用。
  • 提交代码要完整,保证一条提交记录搞定一个 feat 或修复一个 bug,一个功能完成度不够时不要提交,尽量不要出现一个功能多条提交记录的情况。
  • 提交记录要具体,对新增功能和修复的缺陷进行具体描述,不要仅说明修改某个控件的问题。
    • 错误例子:修复密码输入框缺陷
    • 正确例子:修复密码输入框密码强度无法正确显示的缺陷。
  • 提交代码要充分测试,控件要保证基本完成的情况下,在本地进行充分测试后提交,不要引入新的问题。

# 3.2.7 日志自动化生成方案

通过上一个章节我们已经有了良好的代码提交记录,现在可以使用 conventional-changelog 来自动生成 changelog

conventional-changelog -p angular -i changelog/CHANGELOG.md -s -r 0

通过这条指令,可以自动生成日志,包括以下五个部分:

  • New features(新增需求)
  • Bug fixes(修复缺陷)
  • Breaking changes(不兼容修改)
  • Performance Improvements(性能提升)
  • Reverts(代码回退)

每个部分都会罗列相关的 commit ,并且有指向这些 commit 的链接。

日志都生成了,但是这里发现一个问题,这份日志对 HUI 是有效的,但是对于 HUI-Pro 而言阅读就比较困难,HUI-Promonorepo 机制,我们希望达到的效果是每条提交记录能归类到对应的 scope 下面,因此我们需要额外的脚本对 changelog 进行处理。

日志处理脚本运行流程图:

日志

# 3.2.8 Eslint配置和Stylelint配置

该规范适用于使用vue、scss技术栈的项目。

# 3.2.8.1 eslint、prettier

核心插件:

相关级别:

  • eslint:recommended (推荐)
  • plugin:vue/recommended(推荐)

额外规则:

  • prefer-const:优先使用const
  • vue-libs/jsx-uses-vars:防止在JSX中使用的变量被错误地标记为未使用
# 相关资料:
# 3.2.8.2 stylelint

核心插件:

相关级别:

  • stylelint-config-standard(标准)
# 3.2.8.3 vscode 编辑器相关配置

相关插件:

自定义配置项:

{
    "vetur.validation.template": false,
    "prettier.eslintIntegration": true,
    "editor.formatOnSave": true,
    "editor.tabSize": 2,
    "eslint.autoFixOnSave": true,
    "eslint.validate": [
        "javascript",
        "javascriptreact",
        {
            "language": "vue",
            "autoFix": true
        }
    ],
    "stylelint.config": {
        "extends": "@hui/stylelint-scss"
    }
}

# 3.2.9 gitlab的CI/CD持续集成设计

CI/CD(Continuous Integration/Continuous Delivery, and Continuous Deployment)是为了软件交付管道以快速、自动化和可重复的方式从源代码生成发布版本。在频繁发布、自动化流程、重复性工作等方便都有着极大的作用。

为了减少发布时的重复繁琐工作,在hui-vue-pro中我们引入gitlabCI/CD,以减少维护人员的工作量。

由于在hui-vue-pro中发布采用了learn的一套解决方案,所以在发布流程这块我们就不再接入CI/CD,而是在发布之后,官方演示网站的构建和部署采用持续集成的方式。其主要流程分为:

  • 静态网站本地构建
  • 构建之后静态资源在ngnix服务的部署

# 3.2.10 vuepress 设计

Vuepress是一个由VueVue Routerwebpack驱动的单页应用,在项目中的主要应用是项目文档的静态展示。

# 3.2.10.1 目录结构

vuepress目录结构如下:

.
├── docs
│   ├── .vuepress
│   │   ├── components/
│   │   ├── link/
│   │   ├── public/
│   │   ├── router
│   │   │   └── index.js
│   │   ├── styles/
│   │   ├── config.js
│   │   └── enhanceApp.js
│   │ 
│   ├── zh/
│   ├── en/
│   ├── README.md
│ 
└── package.json

  • docs/.vuepress:用于存放全局的配置、组件、静态资源等。
  • docs/.vuepress/components:该目录中的 Vue 组件将会被自动注册为全局组件。
  • docs/.vuepress/link:用于存放头部菜单及侧边菜单项的配置文件。
  • docs/.vuepress/public:存放静态资源,如图片,图标等。
  • docs/.vuepress/router:存放路由配置相关的文件。
  • docs/.vuepress/styles:用于存放样式相关的文件。
  • docs/.vuepress/config.js:配置文件的入口文件。
  • docs/.vuepress/enhanceApp.js:客户端应用的增强。
  • docs/zh:用于存放中文版的文档文件。
  • docs/en:用于存放英文版的文档文件。
# 3.2.10.2 基本配置

package.json中,添加运行开发环境和打包的脚本。

{
  "script": {
    "dev": "vuepress dev docs",
    "build:docs": "vuepress build docs",
  }
}

docs/.vuepress/config.js,需要配置页面上的基本信息,主要包括启动地址、标题、head头部信息、主题信息、webpack配置等。

其中,主题配置中包含了中文文档和英文文档,里面包含头部和侧边栏导航的配置。

# 3.2.10.3 Markdown文档设计

vuepress中,所有的标题将会自动生成对应的侧边栏锚点链接。

在HUI-Pro中集成的控件和工具均需要编写对应的API文档。文档一般包含以下内容:

  • 开发信息。包括版本号、开发成员(可选)、设计成员(可选)、规范访问路径(可选)。
  • 安装方式。
  • 引入方式。
  • 控件/工具类的用法。需提供演示demo及其代码。
  • API接口列表。 开发人员访问文档的目的在于快速学习并掌握组件的使用,因此文档内容需要尽可能清晰的列出控件/工具类常见的用法,以精炼的代码来引导开发人员掌握。

# 4 布局设计

# 4.1 布局控件

基于基础控件与元素,进行组合和布局进行页面的搭建,用于信息的合理呈现,我们把它称为页面布局。布局控件就是用来进行页面元素的自由组合和排布,帮助前端工程师搭建页面。

布局分为主容器 Layout、内容区域 Content、顶部区域 Header、底部区域 Footer 以及侧边扩展区域 Aside

  • Layout: 主容器,用于承载内容和头尾部等区域,使用 flex 布局,可支持修改主轴方向为垂直和水平,进行布局时可嵌套多个主容器以达到效果。
  • Content: 内容区域,用于承载页面内容信息。
  • Header:顶部区域,用于承载内容区域顶部的信息,默认宽度 100%,高度可以自定义。
  • Footer:底部区域,用于承载内容区域底部的信息,默认宽度 100%,高度可以自定义。
  • Aside:侧边扩展区域,用于承载内容区域左右两侧的信息,默认高度 100%,宽度可以自定义。

# 4.2 典型页面控件

海康的企业级产品是一个庞大且复杂的体系,产品线范围很广,且行业特性不同,视觉规范组梳理并对最基础的典型页面进行指导,典型页面控件就是用于实现基础典型页面,保证页面的一致性和可复用性。

# 4.2.1 Page 页面

页面最外层容器,承载左侧导航栏和右侧内容区域,若不存在左侧导航栏也可以不使用 Page。

  • Page 控件需要支持自动展开和收起菜单功能,当浏览器窗口变大时,若页面宽度大于断点值,则菜单自动展开,当浏览器变小时,若页面宽度小于断点值,则菜单自动收起。

菜单自动展开收起流程图如下:

  • Page 控件需要能够设置菜单自动展开收起的页面宽度断点值

# 4.2.2 PageMenu 左侧导航栏

左侧导航栏,承载页面的跳转功能,点击导航项可以跳转到对应页面,左侧导航栏可以根据平台框架自行定义,也可以使用 el-menu 控件加载的默认导航。

  • 通过参数 menu 设置导航栏数据
  • 通过参数 menu-theme 设置导航栏主题为深色还是浅色
  • 通过参数 fold-widthunfold-width 设置菜单的展开和折叠宽度
  • 通过默认插槽可以实现左侧导航栏的自定义

# 4.2.3 PageContainer 页面容器

页面容器承载除左侧导航栏外所有的页面元素,在不使用 Page 和 PageMenu 的时候,使用 PageContainer 作为最外层容器。

  • 页面滚动时抛出事件 on-scroll

# 4.2.4 PageHeader 页面头部区域

页面头部区域承载面包屑、返回按钮等,用于展示当前页面在系统中所处的位置,并可以进行页面跳转等操作。

  • 通过参数 affix 设置页面头部是否开启图钉模式,图钉模式时页面头部区域将固定在页面顶部,不会随滚动条滚动而消失
  • 通过参数 breadcrumb 设置页面面包屑
  • 通过参数 title 设置页面标题
  • 通过参数 subtitle 设置页面副标题
  • 通过参数 return-icon 设置是否显示返回按钮
  • 通过参数 beforeReturn 可以控制点击返回按钮是否会触发返回页面事件
  • 通过参数 returnEvent 可以设置在点击返回按钮后触发的方法
  • 通过参数 returnRouter 设置点击返回按钮后跳转的路由

点击返回按钮后的流程图如下:

页面底部区域通常为操作区域,比如固定的表单提交操作栏等。

  • 通过参数 affix 设置页面头部是否开启图钉模式,图钉模式页面底部区域时将固定在页面底部
  • 通过参数 inner-width 设置使用默认 slot 时,操作区域所占的宽度
  • 通过参数 inner-center 设置使用默认 slot 时,操作区域里的内容是否居中显示
  • 通过参数 inner-style 设置使用默认 slot 时,操作区域里的内容样式

# 4.2.6 PageContent 主内容区域

主内容区域为主要信息承载区域,由于我们的产品页面信息承载量比较大,内容区域还会存在多层级信息,需要不同的内容控件及面板用于页面内部菜单导航及指引,为了保证复杂内容的信息层级有序,我们采用边框线的方式对页面进行分割。

  • 通过参数 flex 设置是否使用 flex 布局
  • 通过参数 align-center 设置使用 flex 布局时,水平方向是否居中
  • 通过参数 direction 设置使用 flex 布局时,内部是水平排列还是垂直排列,默认为'垂直排列'

# 4.2.7 PageSidebar 侧边栏

为了解决产品页面内容区域信息层级的复杂,我们把页面内部层级进行区分,将侧边栏通过分割线分割出来,用来承载二级导航,分为树形式和列表形式。

  • 通过参数 affix 设置侧边栏是否开启图钉模式
  • 通过参数 offset-top 设置图钉模式模式时,设置距离窗口顶部达到指定偏移量后触发
  • 通过参数 type 设置侧边栏类型
  • 通过参数 width 设置侧边栏宽度
  • 通过参数 inline-scroll 设置是否使用内置滚动条

# 4.2.8 PageAction 操作栏

操作栏承载页面上的操作按钮。

  • 通过参数 affix 设置操作栏是否开启图钉模式
  • 通过参数 offset-top 设置图钉模式模式时,设置距离窗口顶部达到指定偏移量后触发
  • 通过参数 search-icon 设置是否显示搜索图标
  • 通过参数 search-icon-tips 设置搜索图标提示信息
  • 通过参数 search-icon-active 设置搜索图标是否激活
  • 搜索框显示隐藏时抛出事件 search-collapse

# 4.2.9 PageSearch 搜索栏

搜索栏用于在大量数据中通过筛选,得到特定的数据。分为 PageSearch 和 PageSearchItem 两个控件。

PageSearch 搜索栏:

  • 通过参数 model 设置搜索表单数据对象
  • 通过参数 rules 设置搜索表单验证规则
  • 通过参数 label-position 设置表单域标签的位置
  • 通过参数 label-width 设置表单域标签的宽度
  • 通过参数 options 设置搜索栏每个断点的宽度值

PageSearchItem 搜索项:

  • 通过参数 prop 设置搜索表单域 model 字段
  • 通过参数 rules 设置搜索项验证规则
  • 通过参数 label 设置搜索项标签文本
  • 通过参数 label-width 设置搜索项标签的的宽度
  • 通过参数 required 设置搜索项是否必填
  • 通过参数 required-right 设置搜索项是否必填,且星号显示在文字右侧
  • 通过参数 show 设置搜索项高低频过滤
  • 通过参数 hidden 设置搜索项是否隐藏

# 4.2.10 PageTable 表格内容

  • 通过参数 header-affix 设置表格头部是否开启图钉模式
  • 通过参数 header-offset-top 设置表格头部开启固定模式时,距离窗口顶部达到指定偏移量后触发
  • 通过参数 scrollbar-affix 设置表格滚动条是否开启固定模式
  • 通过参数 scrollbar-offset-bottom 设置表格滚动条开启固定模式时,距离窗口底部达到指定偏移量后触发
  • 通过参数 pagination-affix 设置表格分页栏是否开启固定模式
  • 通过参数 pagination-offset-bottom 设置表格分页栏开启固定模式时,距离窗口底部达到指定偏移量后触发

# 4.2.11 PageGroup 分组

  • 通过参数 collapsable 设置分组是否可伸缩
  • 通过参数 expanded 设置分组可伸缩时,是否为展开状态
  • 通过参数 title 设置分组标题
  • 分组展开时抛出事件 open
  • 分组收起时抛出事件 close

# 4.2.12 PageButtonGroup 按钮分组

  • 通过参数 max-width 设置按钮组最大宽度
  • 按钮折叠时抛出事件 fold
  • 按钮展开时抛出事件 unfold

# 4.2.13 PageDetail 详情页

PageDetail 详情页:

  • 通过参数 single 设置详情项是否单列显示

PageDetailItem 详情项:

  • 通过参数 label 设置详情项标签
  • 通过参数 double-col 设置详情项是否占据两个单元格

# 5 业务控件设计

# 5.1 PwdInput 密码输入框

密码输入框是对普通输入框的一个增强,能够根据输入密码的复杂度实时提示当前密码的风险等级,以便用户能够及时知道自己所输入的密码强度。

主要是用于设置用户密码和设备密码等场景。

密码强度规则是根据公司的安全红线来制定的,具体的规则如下

  • 等级 0(风险密码):密码长度小于 8 位,或者只包含 4 类字符中的任意一类,或者密码与用户名一样,或者密码是用户名的倒写。
  • 等级 1(弱密码):包含两类字符,且组合为(数字+小写字母)或(数字+大写字母),且长度大于等于 8 位。
  • 等级 2(中密码):包含两类字符,且组合不能为(数字+小写字母)和(数字+大写字母),且长度大于等于 8 位。
  • 等级 3(强密码):包含三类字符及以上,且长度大于等于 8 位。

在某些业务场景需要对密码进行回显,但是为了密码的安全性,后台并不会把密码返回给前端,所以在回显密码的时候回默认采用自定义的某几个字符进行展示,所以在密码框中看到的密码,并不是真实的密码。这个“密码”可以通过echo-password来进行设置。其他的一些参数设置尽量与输入框控件保持一致。

# 5.2 TableTransfer 表格穿梭框

表格穿梭框是对hui的穿梭框的一个补充,其使用场景更加广泛,业务性也更加的强。为了应对不同的业务需要,表格穿梭框提供了 2 种展现形式:一种是两侧都是普通列表展示,另一种左侧是 2 层的树表格,右侧是普通列表。

该控件主要用于设备的选择、人员/组织的选择等场景。

# 5.2.1 控件设计实现

控件支持以下需求:

  1. 支持勾选的数据左右移动
  2. 支持列表的单选和全选
  3. 支持列表的搜索
  4. 支持已勾选数量和总数的统计
  5. 支持滚动点击加载更多
  6. 支持 2 层的树结构数据
  7. 支持与树结合,跨节点选择

由于需要支持上述第 1/4 点,所以控件的数据是一次性从后台获取,由客户端来处理搜索,加载更多等逻辑。

  • 左右两侧数据互斥的处理——同一份数据,只能出现在左侧或者右侧。

    在控件内部,拿到外部传入的数据之后,分别对左右侧数据进行缓存,每次移动时需要将缓存的数据与勾选的数据做 diff 处理,最终得到两侧需要展示的数据。如果左侧是树结构列表的情况下,则需要对数据做特殊处理,因为右移时只需要移动子节点数据,父节点数据不需要进行移动,如果是左移,则在做了 diff 处理之后,根据需要右移的子节点,在缓存的数据中取到其父节点,保证数据能够组装成树结构数据后在左侧列表进行展示。

  • 初次加载控件,对 dom 节点渲染数量做一定的限制,保证页面渲染不卡顿。

    因为采用的数据一次性获取,所以如果数据量过大时,页面渲染会出现卡顿,为了避免这种情况,对列表初次加载数量做一定限制,用于点击列表末尾加载更多后再次渲染“下一页”的数据。在两侧都是普通列表的情况下,只需要控制传入列表数据的数量即可,每次点击加载更多,向列表中 push 对应的“下一页”数据。如果是在左侧树列表的情况下,每页加载的数量是指父节点的数量,如果加载数量是子节点数量,那么就会存在一页数据种最后一个父节点下子节点数据不全的问题。确定父节点数量之后,根据父节点 id 在缓存数据种取出对应子节点,然后组成树结构的数据,传给树列表。

  • 与树配合使用,需要对跨节点的数据做特殊处理

    在一些业务场景下需要将不同区域或者组织下的数据选择到右侧列表,此时向右侧移动数据时,需要将当前树选中节点的 id 作为标识放到右移的数据中,这样在这些数据左移回来时,就可以根据这个字段并结合当前选中的树节点 id 来判断该数据是否应该显示在左侧列表中。如果是不涉及到回显的页面,控件内部已经将该逻辑封装处理了,但是如果存在回显时,则需要后台返回的数据中就要包含该字段。

# 5.2.2 常见问题

1. 从左侧移到右侧没有问题,但是从左侧移到右侧报错。

左侧存在组织树或者区域树时,穿梭框从左侧移到右侧时,内部会通过 belongArea(没有设置 belongField 的情况下)判断移动的节点是否属于当前左侧树选中的组织或者区域,如果属于才会显示在右侧的列表中,不属于的会移过去,但是不会显示。

右侧节点数据中需要包含该节点所属的区域或者组织的数组(一般情况下是节点的 id)

2. 需求中有包含下级的功能,在勾选包含下级的情况下,右侧数据移到左侧时,左侧没有显示移动的节点。

在这种情况下,右侧列表的数据中的所属组织或者区域的字段需要是一个包含该节点的所有父节点的数组,这样向左移动时,只要是移动节点中的所属组织或者区域的数据包含当前选中的组织或者区域,那么都能移过去并且显示。

3. 什么情况下不需要传入belongArea

如果只是单个穿梭框的情况下(没有回显的需求),可以不传入该字段。控件内部会默认将treeNodeId字段作为该字段的值。所以treeNodeId是必传字段,但是对具体的值没有什么要求。

4. 直接修改leftTableDaterightTableData值为什么没有生效。

为了不受对象引用造成影响,控件内部在接收到上面两个参数之后,会完全拷贝一份,所以如果想修改这两个参数需要重新赋值。不推荐利用对象的引用关系直接修改数据,这样会使数据的变化变的很难跟踪。

5. 为什么要前端做搜索。可以改成后台搜索吗?

控件中的一些交互,比如:全选,移到左侧之后,右侧需要消失等交互,如果是后台搜素就无法实现。

# 5.3 SyncTree 异步树

异步树模块主要用于解决树组件在数据量过大时,一次渲染所有数据节点导致浏览器卡顿甚至崩溃的情况。所谓异步即采用异步渲染子节点和分页渲染同级节点的方式,缓解一次渲染的压力。

在初始化树的时候,只会渲染根节点。然后,每第一次展开某节点的时候,会获取第一页子节点数据,如果数据过多超过一页时,当用户点击加载跟多的交互按钮时,加载第二页数据,以此类推。

同时,由于页面上存在过多的节点数据也会造成卡顿,为更好提示用户,在展示树时,节点数据超过某一预设值(如 1000)时,出现提示节点过多的提示信息;在搜索树时,获取的节点数超过某一预设值(如 500)时,出现提示节点过多的提示信息。

# 5.3.1 组件框架设计

异步树分为单选异步树和多选异步树,不过整体思路基本相同。

异步树设计了流程图

首先,异步树组件的模板内容需要包括搜索框、展示过多和搜索结果过多的两种横幅提示、HUI 的树组件。

异步树实现的核心就是分页渲染,而分页的关键在于树组件本身自带了属性lazy(是否开启懒加载)和load(加载函数),当开启树的懒加载渲染时,树每次展开父级节点会根据 load 函数返回的内容加载对应的节点数据。当数据量超过一页数量的时候,我们插入一个虚拟的节点,给改节点绑定点击事件,用于用户点击触发加载更多的逻辑。加载更多即就是加载第二页数据;同样,在下一页数据中,如果还没有全部加载完(还不是最后一页),就摧毁原虚拟节点,重新添加一个新的虚拟节点,并绑上和之前一样的数据。

虚拟节点和虽然具有节点的特性,它会在某一父节点下显示在最后一个节点,但是我们并不希望它有正常节点的一些功能,比较 node-click 事件。所以,虚拟节点有以下几点需要注意:

  1. 使用 render 函数渲染所有节点。这么做即可以给虚拟节点绑上特殊的点击函数,也能方便正常节点做一些节点渲染的处理,比如搜索关键字的高亮显示;
  2. 在树控件中传入 beforeClick 回调函数,其中判断,如果是虚拟节点,就不再执行节点点击事件而出发 node-click 事件。
  3. 给虚拟节点附上一些样式,来隐藏它作为节点的一些功能,比如图标、勾选框和悬浮高亮色。 另外,在每次加载入要渲染的数据的时候,记录显示的数据总量,当总量超过预设值(如 1000)时,将模板结构中,预先隐藏的横幅提示信息给显示出来。执行搜索的时候也是一样。

# 5.3.2 组件子模块划分

子模块名称 功能描述
单选异步树 支持单个选择树节点
多选异步树 支持多个勾选树节点,并支持抽取被配置了的节点
混合异步树 同时支持后台返回一层数据或者多层数据,结合前端分页和后端分页的所有情况

# 5.3.3 子模块流程设计

单选异步树

异步树设计了流程图

如上图所示,单选异步树需要将树组件的 show-checkbox 属性置为 false,并且,每次拿取分页数据都是从后台获取,数据信息均由后台给出。

在传给树控件的懒加载 load 函数中,执行的是加载第一页的逻辑。其中需要添加是否插入虚拟节点的判断。在虚拟节点绑定的点击事件中,要执行的是请求并加载第 N 页的逻辑。

多选异步树

异步树设计了流程图

多选异步树的总体流程和单选异步树差不多,区别在于多选是一次性从后台获取所有数据,该数据会被缓存在变量中,每次需要加载数据的时候,根据条件从总数据中去查找提取出来。另外,多选异步树涉及勾选和抽取两个功能,那么就会有初始化(被)勾选和初始化禁用的状态。需要在拿到数据以后,根据父节点和缓存的状态记录,同样设置一遍。

混合异步树

混合异步树在流程上和单选多选一样。它具有单选的特性,每次需要获取更多节点的时候,都会从后台去请求一遍,获取节点数据;它也具有多选的特性,每次拿到新的数据的时候,都会把它缓存起来,并且执行去重操作。然后每次需要进行数据查找或者数据操作的时候,都可以直接操作总数据。

# 5.4 TreeSelect 树选择

树选择类似下拉选择的选择控件,可选择的数据结构由树形控件组成,例如部门、地区等等。

  • 使用场景是表单数据输入、表格数据过滤。
  • 选项为多层级。展现层级关系,帮助用户定位选择。例如,省市区。
  • 支持对多组层级选项进行单个/多个选择。
  • 通过点击触发,不断展开层级。如关闭面板未进行页面刷新,不恢复之前展开的层级。
  • 树选择面板的高度,随选项的不断展开进行延伸,但有最大高度限制,超出最大高度时显示滚动条。横向最大宽度同选择框宽度,超出宽度显示横向滚动条。

# 5.4.1 组件框架设计

树选择是一个典型的复合型组件,是由多个基础组件组合在一起的特定场景组件。

首先它几乎具备树组件的大部分特性,并且组合了input输入框进行树节点的搜索过滤,其次需要和select选择器组件那样,通过用户的点击行为来触发下拉面板的展示,并且根据点击选择的数据不同,在主体框内联动显示对应的数据信息。 设计主体流程如下。

树选择设计了流程图

# 5.4.2 组件子模块划分

子模块名称 功能描述
单选树 支持单个选择树节点
多选树 支持多个勾选树节点

# 5.4.3 子模块流程设计

单选树

对多组层级选项进行单个选择。

  • 选项过多,增加搜索功能,用户快速检索目标。搜索支持即时或非即时搜索。
  • 单选树任一父级能够直接选择。
  • 树选择或级联选择,展开下拉列表定位到一级选项并展开子项。如使用动态加载选项时,展开下拉列表定位到一级选项但展开子项。
  • 支持选中选项时,选择器内展示选项所有上级。例如浙江省/杭州市/滨江区。

多选树

对多组层级选项进行多个选择。

  • 选项过多,增加搜索功能,用户快速检索目标。搜索支持即时或非即时搜索。
  • 多选项在选择框中,以标签呈现,标签交互参考多选下拉标签交互。删除标签时,标签对应的选项取消选中。
  • 特殊交互,选择父节点时,将选择框中父节点下的子节点标签合并为父节点标签。 其中,合并节点为整个方案最难实现的部分。

组件通过提供属性check-model来设置勾选模式,当模式为parent时候,支持当子节点全部勾选时,仅展示父节点。这就意味这,需要在每次勾选的时候,动态去检测节点的父子勾选状态,并将当前选择的数据进行整合处理。

# 5.5 AddFormItem 逐条添加

适用于需要重复添加多个或多组对象的场景,建议添加对象数量不超过20个(组)时使用。

逐条添加场景比较灵活多变,直接封装业务逻辑会导致控件的限制特别多,不能适应灵活多变的场景;所以目前主要是封装了布局的逻辑,而实际的业务逻辑由开发者自行处理,以保证灵活性。

# 5.5.1 组件框架设计

逐条添加控件针对具体场景,拆分出了四个控件,包括逐条添加按钮、单个项目添加、横向排布按组添加和纵向排布按组添加。

# 5.5.2 组件子模块划分

子模块名称 功能描述
逐条添加按钮 封装逐条添加按钮
单个项目添加 支持单个表单项的添加
横向排布按组添加 支持横向排布的一组表单项的添加
纵向排布按组添加 支持纵向排布的一组表单项的添加

# 5.5.3 子模块流程设计

逐条添加按钮(h-add-form-item-btn)

实现逐条添加按钮特有的样式和功能。

  • 按钮占满表单内容区域,与表单内容区域宽度一致。
  • 按钮上可以显示最大可添加条数和当前已添加条数。
  • 当前条数达到最大可添加条数时,按钮禁用。
  • 基于hui的button控件封装,且保留原控件的所有属性和事件,利用$attrs和$listeners透传。

单个项目添加(h-add-form-item-single)

单个表单项内容的样式封装,并提供内容插槽和操作按钮插槽,以便自定义操作按钮。

  • 基于hui的el-form-item控件封装,默认插槽接受自定义内容,可以是hui中的输入框和下拉框等控件。
  • 具名插槽operate可以传入针对该项内容的操作按钮。
  • 保留el-form-item所有原有属性和事件,利用$attrs和$listeners透传。

横向排布按组添加(h-add-form-item-row)

这里主要封装了在表格中的el-form-item样式,主要处理的是默认高度,这里直接将错误提示信息的高度预留出来,防止校验时出现错误提示信息而出现抖动。

  • 基于hui的el-form-item控件封装,默认插槽接受自定义内容,可以是hui中的输入框和下拉框等控件。
  • 保留el-form-item所有原有属性和事件,利用$attrs和$listeners透传。

纵向排布按组添加(h-add-form-item-col)

这里主要封装了一个布局控件,通过默认slot传入自定义内容(这里一般情况下都是表单),布局控件会把传入的内容展示为一组,且提供一个删除按钮,用于删除整组内容。

  • 纯布局控件,不依赖已有控件,只提供和默认插槽,用于传入多个el-form-item,并传入的多个el-form-item在样式上分成一组。
  • 支持通过属性配置是否显示删除按钮。
  • 删除按钮点击时,抛出事件通知外部控件。

# 5.6 ImgCropper 图片裁切

当对用户上传的图片比例有所限制,需要符合要求才能上传成功时,为了帮助用户更直观的进行上传操作,采取对用户上传的图片进行裁剪处理,使得上传的图片比例符合要求。常用于头像上传、特定比例的封面上传等。

# 5.6.1 控件基本业务逻辑

图片裁切控件接收一个图片url或base64,然后在调起裁切功能时,会以全屏弹窗形式展示待剪裁图片和指定大小的剪裁区域,然后用户可以缩放和拖拽预览图,点击确定后,控件会返回当前预览图的原始尺寸、缩放倍数、剪裁起始点、剪裁区域大小等参数。整体展示样式如图: 图片裁切示意图-1

# 5.6.2 设计要点

# 5.6.2.1 预览图拖拽的实现方式

这里在图片预览容器内展示的图片是可以拖拽的,实现拖拽的方案分如下步骤完成:

  • 鼠标左键按下:记录当前鼠标位置信息;监听鼠标移动事件;监听鼠标抬起事件;
  • 鼠标左键按下后开始移动:在移动事件中计算当前移动的偏移量,以此实时更新预览图片位置,达到图片跟随鼠标移动的效果。
  • 鼠标抬起:解除鼠标移动监听事件;解除鼠标抬起监听事件。

# 5.6.2.2 剪裁区域内容展示实现方式

这里在实现时,其实是三层视图叠加实现的效果。最底层是图片预览容器元素和预览图片;中间层是剪裁区域展示元素;最上层是用于监听各种操作事件的空元素。最上层元素无具体内容,但是它和预览图片的大小和位置实时同步,用于实现看起来是在操作预览图的效果。 图片裁切示意图-2

  • 最上层元素监听鼠标事件,包括移动和缩放。
  • 最上层将接收到的指令作用于预览图片上。
  • 最上层元素调整自身位置与尺寸和预览图一致。

# 5.7 MapPicker 标记地图点位

用于需要在地图上进行标记的场景,例如管辖区域下门店位置的标记。

# 5.7.1 控件基本业务逻辑

标记地图点位控件接收一个gais专题地图配置信息和一个坐标点信息,然后在渲染时,基于配置信息渲染地图,基于传入的坐标点信息在地图上绘制坐标点。如果没有传入坐标点信息,那么支持手动在地图上标记一个点,标记后返回坐标点信息。

# 5.7.2 设计要点

# 5.7.2.1 地图渲染实现方式

这里地图渲染基于openlayer和gais提供的gaismap包,在传入正确的gais专题地图配置后,即可渲染对应的地图,具体的地图渲染逻辑gaismap已经封装好了,无需额外处理。

# 5.7.2.2 标记点绘制的实现方式

  1. 地图绘制完成后,获取地图实例。
  2. 基于openlayer的api,在底图上增加图层
  3. 基于openlayer的api,在图层上增加要素
  4. 基于openlayer的api,在要素中存放一个标记点
  5. 设置标记点的样式和icon

# 5.7.2.3 标记点拖拽的实现方式

  1. 基于openlayer的PointerInteraction自定义鼠标按下、拖拽和抬起的事件。
  2. 自定义标记点上鼠标按下时的处理逻辑: 2.1 将当前标记点样式设置为半透明,用于标记移动前的位置。 2.2 克隆当前标记点,用于展示移动轨迹。
  3. 监听鼠标的drag事件,计算出原位置与当前位置的变化量,并将用于展示移动轨迹的标记点位置基于变化量实时更新。
  4. 监听鼠标的抬起事件,并作如下操作: 4.1 将原有标记点删除。 4.2 将用于展示移动轨迹的标记点定位到当前鼠标位置。 4.3 通过事件抛出当前标记点坐标。

# 5.7.2.4 可扩展性设计

  1. 标记点icon可自定义。
  2. 可通过指定方法获取要素实例,进行更深层次的标记点样式自定义。
  3. 可通过指定方法获取地图实例,进行更深层次的交互和功能自定义。

# 5.8 HolidayPicker 节假日设置

适用于对节假日、特殊日的设置等日历相关场景。

# 5.8.1 控件基本业务逻辑

节假日设置接收一个节假日日期列表,渲染时基于传入的节假日日期在展示当月日历的同时,将节假日日期标记出来。同时,支持在日历上修改节假日列表,同时,将修改后的节假日列表传出。

# 5.8.2 设计要点

# 5.8.2.1 日历渲染方式

  1. 内部维护一个数组,按照7*6的计算方式,即共计6行,每行7个,相当于是4个元素的数组,用于承接当前展示的日期。
  2. 计算当前年月,以及当前月份的天数以及该月第一天是周几。
  3. 基于第二步的计算值,将当月日期存入到第一步创建的数组中。
  4. 前后多出来的日期分别标记为上一月和下一月的日期。

# 5.8.2.2 可扩展型设计

  1. 日历中每个日期中展示的内容可自定义。
  2. 日历上方的年月变动通过事件通知到外部。
  3. 日历的宽度在指定范围内自适应,高度可配置。
  4. 周起始日可配置。

# 5.9 CascadeSelector 横向级联选择器

横向级联选择器是对hui的级联选择器的另一种交互体现,其使用场景更广,业务性也更加的强。 与hui的联级选择器相比:

  1. 其子节点数据会较多,一个面板展示数据更多,hui则需要滚动才能看到更多
  2. 其下拉面板大小比较固定,hui则在多个层级下,不断的向右扩展
  3. 其层级比较清晰,适用于不超过4个层级的场景。

总之在联级选项较多,且层级不超过4个层级的情况下使用横向级联选择器具有较好的交互体验,例如省市区/园区、楼栋、宿舍等。

图1.1 横向联级选择器示意图

# 5.9.1 控件设计实现

控件支持以下需求:

  1. 异步加载数据 (支持后端搜索)
  2. 同步加载数据(支持前端搜索)
  3. 只能选择最后一层级
  4. 可选择任意层级
  5. 支持字母过滤
  6. 支持默认选择

# 5.9.2 组件框架设计

框架设计流程图如下,总体设计可分为异步和同步两种模式,这两种模式主要的区别就是数据的获取方式以及搜索的方式。 其余只能选择最后一层级、可选择任意层级、支持字母过滤和支持默认选择都是相同的。

图1.2 横向联级选择器总体设计流程图

异步模式:初始化时,先向服务端获取组织第一层的数据并渲染,然后每次选择节点进入下一层级时,再向服务端获取下一层的数据并渲染, 并且每次获取的数据都在前端进行缓存(默认有缓存机制),相同的层级直接从缓存中获取(当然如果数据需要有实时性,那么也可配置不缓存,每次都会从服务端获取数据)。并且该模式下搜索只能搜索本层级的数据。

同步模式:初始化时,向服务端获取当前组织所有数据,并且默认只渲染第一层数据,每次选择节点进入下一层级时,直接从数据中获取当前层级的数据进行展示。并且该模式下搜索是在全局范围内搜索。

# 5.9.3 组件子模块划分

子模块名称 功能描述
异步横向联级选择器 支持异步获取数据,异步本级搜索
同步横向联级选择器 支持单次获取所有数据,前端全局搜索

# 5.9.3.1 子模块流程设计

异步横向联级选择器

异步加载数据的存在大数据情况下减轻服务器端的压力,每层数据都是通过向服务端获取。 搜索可分为字母过滤和搜索过滤,搜索的范围均为本层的数据。

字母过滤: 需要使用者配置,通过对数据增加一个字段type,以type字段区分 当前属于哪个字母(之所以在控件内部不做文字的解析,如“江陵路”属于“J”字母,是为了保证性能问题,js计算速度要比服务器端慢);字母过滤默认情况下展示全部,当选择对应字母时,才切换对应的数据。

搜索过滤: 是否支持搜索可动态配置,且每层数据中的搜索都可自由控制,互不影响,比如大部分场景是:省市组织是不需要搜索的,而区域需要搜索。这种就配置第一、二层无搜索,第三层可搜索。控件内部搜索按照名称进行过滤,且搜索的字段会高亮显示,当然复杂的业务需要搜索条件不仅仅是名称,那么可自定义过滤方法,那么控件会根据自定义方法进行过滤。

图1.3 异步横向联级选择器的设计流程图

同步横向联级选择器

图1.4 横向联级选择器搜索示意图

同步加载数据的存在是为了在数据量较小的情况下,可全局范围内搜索,并且直接选择展示对应数据。全局范围内的搜索将大大提升选择效率(异步情况下,需要选择3次节点、3次数据的获取,同步直接搜索江陵路选择即可)。但这种场景在数据量较大的情况下,会增加网络传输的压力和前端处理搜索数据的速度,当然渲染节点仍然是单层节点的渲染,所以渲染这层是没有影响。(当然在异步情况下也是能做成全局搜索的情况,但是这种情况对于接口的对接需要要求会更严格,需要事先约定好,但作为通用组件,这边需要更加灵活,因此同步加载会更加便捷。)

当然这边的搜索仍然会包含字母过滤,字母过滤的设计同上模块。这边主要设计全局搜索这一模块。 全局搜索输入框与下拉框集为一体,即可选择也可输入搜索。当选择时,下拉面板展示每层的数据。当搜索时会展示搜索的结果,如下图所示,会将所有的组织层级都显示出来,选择数据后,直接展示每层展示的结果,并切换所选择的最后一层节点的数据(比如选择到浙江/杭州市,那么就会在省tab面板选中浙江,在市tab面板选中杭州,并且输入框值也同步修改)。

在只可选择最后一层级和可选择任意层级的模式上搜索结果是不同的,在可选择最后一层级模式中,搜索的结果展示的都是到最后一层结果,而在选择任意层级的模式 中,就会展示所有结果。(比如搜索杭州,所有结果为浙江/杭州,浙江/杭州/xxx,那么在选择最后一层级模式就只展示浙江/杭州/xxx,而在选择任意层级则展示全部搜索结果)

图1.3 异步横向联级选择器的设计流程图

备注

面板tab是不可随意切换的,只有在选中节点后,才会放开后面层级的切换,比如(默认有省市区3层级tab, 默认不可直接切换到市tab和区tab;如果选中了浙江省 那么市tab将会开发切换,可在省和市来回切换,数据展示不变)

# 5.9.4 可拓展性设计

  1. tab字段可自定义
  2. 下拉面板高度可自定义
  3. 下拉面板内容栅格布局可自定义
  4. 可默认初始化数据(异步加载也可)
  5. 搜索条件可配置

# 5.9.6 对外提供的API

1.对外提供可选中节点方法

# 5.9.5 对外提供的可配项

  1. tab-list: tab分页数据,必选参数
  2. get-region-data: 异步获取数据回调函数
  3. data: 初始化数据(即第一层数据) 必选参数
  4. load-type: 加载方式,同步或异步
  5. is-only-select-last: 是否只能选择到最后一层
  6. is-only-show-last: 是否只展示最后一个选中的数据
  7. letter-filter: 是否有字母过滤
  8. placeholder: 输入框提示语
  9. default-option: 属性为name和id,内部通过id和name进行展示和标识的,展示为name对应的属性值
  10. cashe: 异步模式下是否需要缓存已加载过的数据
  11. can-search: 每层tab下是否支持搜索
  12. no-data-text: 暂无数据的提示语文案
  13. search-fn: 搜索自定义函数
  14. default-selected:是否默认选中节点,仅初始化有效
  15. after-data-complete-fn: 节点加载完后的回调函数
  16. base-content: 输入框中的前缀
  17. col-span: 内容区域栅格模式
  18. panel-height: 下拉面板高度

# 5.10 BatchSelector 批量选择

批量选择适用于批量选项较少的场景下使用,例如监控点控制在100个以内的场景。 优势是针对已选择和为选择的操作比较明确,简单表单设计。但不合适较多选项的场景。

图1.1 批量选择示意图

# 5.10.1 控件设计实现:

控件支持以下需求:

  1. 支持标签的展示形式
  2. 支持列表的展示形式
  3. 标签的宽度定宽和不定宽的模式
  4. 单个删除和批量删除

# 5.10.2 组件框架设计

框架设计流程图如下,总体可分为标签和列表模式。

标签模式是内部直接根据name字段展示内容,并把id值作为唯一标示(当然name和id字段可修改配置的)。单个删除时,通过id来删除对应的数据。清空时,会有2次清空数据的确认框,确定时才会真的清空数据,然后更新v-model数据。

列表模式是通过插槽的形式进行渲染,使用者可直接采用huitablePagination,单个删除的逻辑需要自己实现,清空数据则内部已经实现。

图1.2 批量选择设计图

# 5.10.3 控件内部基本逻辑

批量选择控件接受一个v-model值,用于绑定选择的数据进行展示。 当动态的变化v-model时,标签会自动渲染,并可操作单个和批量删除操作,且更新v-model数据。

# 5.10.4 控件可拓展性设计

  1. 可配置标签的长度
  2. 数据双向绑定
  3. 可自定义添加事件
  4. 可自定义设置选择上线

# 5.10.5 对外提供的事件

  1. 添加按钮点击事件

# 5.10.6 对外提供的可配项

  1. max-num: 最大数量
  2. label-width: 标签内容宽度
  3. default-params: 可配属性参数,用于修改默认name和id字段

# 5.11 MutliSelector 下拉搜索多选

下拉搜索多选是对hui的下拉选择器的一个扩展,该控件更具有广泛性。 与hui的下拉选择器相比:

  1. 可支持搜索
  2. 支持全选
  3. 支持选择的数据排序

总之该控件适用于下拉选择器对多个对象的选择操作,且需要使用搜索款速定位至目标对象的场景, 建议选择的对象数量不超过100时使用。

图1.1 下拉搜索多选示意图

# 5.11.1 控件设计实现

控件支持以下需求:

  1. 支持多选,且支持多选上限的限制
  2. 支持全选(与多选上限功能互斥)
  3. 支持搜索
  4. 支持数据排序

# 5.11.2 组件框架设计

框架设计流程图如下,总体设计可分为多选,全选以及多选上限模块。 多选是指下拉面板中可选择多个数据,并更新数据,而全选则直接选中全部的数据,多选上限指的是选择的最多个数。数据的排序指,选完数据后并关闭下拉面板后,对选择的数据进行置顶排序。

图1.2 下拉搜索多选设计图

# 5.11.3 控件的基本业务逻辑

控件接收一个下拉数据,用于渲染下拉列表。并且绑定一个v-model数据。当选择数据时,v-model会更新数据。此外当需要选中项置顶时,在下拉面板关闭时,进行列表重新排序,选中项置顶。

全选按钮,当支持全选功能时,控件内部渲染一个全选按钮,并且与下拉列表选中项联动,会有无选、半选和全选3种状态。

选中限制:当选择的个数达到上限时,其他选项将做置灰不可选择处理。

# 5.11.4 可拓展性设计

  1. 可配置是否全选
  2. 可配置选择的上限个数
  3. 可搜索过滤函数

# 5.12 SelectTreeOption 树对象选择

树对象选择器是hui tree组织树上单选和多选的另一种交互的体现。该使用范围更广,且更加具有业务性。 与hui tree单选和多选相比:

  1. 树对象选择器只选择挂载在组织上的资源(当然如果把组织也当资源放入,那么是另外的事情)
  2. 树对象选择器在切换树节点后,资源也会切换,资源的数据不会有巨大的增加,与组织树不断展开节点,数据量越来越多相比,性能上有了较大的改善
  3. 通过组织和资源分开搜索,提高了搜索性能

总之树对象选择适用于树下单个或多个对象的选择操作,可以是同步树,也可以是异步树;对象加载方式采用同步加载,建议选择的对象数量不超过500时使用,超过500的不建议使用。

图1.1 树对象选择示意图

# 5.12.1 控件设计实现

控件支持以下需求:

  1. 可配置,有无头部(已选择部分),底部(确定、取消按钮部分)
  2. 支持单选
  3. 支持多选(有上限限制、全选)
  4. 支持自定义选项内容
  5. 支持宽面板模式
  6. 支持仅使用下拉面板内容

# 5.12.2 组件框架设计

为了实现高可配置性,需要将控件按模块进行划分:

  1. 下拉框模块
  2. 下拉面板顶部
  3. 下拉面板内容区
  4. 下拉面板底部
  5. 自定义内容容器
# 下拉框模块

下拉框模块需要支持单选,多选的展示模式。并且需要支持可删除已选择的内容的功能

# 下拉面板顶部

下拉面板顶部只要展示‘已选择(1/5)’的内容和包含下级内容复现框。并且该内容具有很大的自由操作性。 因此将提供左右2侧的自定义内容插槽,供使用者另外拓展。而且默认展示的内容也可通过配置项去掉。

# 下拉面板内容区

自定义内容区需要定义一个控件,并全局注册。因为使用者使用该控件进行内容展示,而控件内部需要处理复选或单选的相关逻辑,因此在控件内部直接封装radio和checkbox并通过插槽来承载使用者的自定义内容。因此控件只需要处理如何选中的逻辑(控件在外部插槽插入已经脱离内容的控制),因此通过通信的形式来实现选中操作。当然这边还需使用者配合比如需要使用高亮控件等,配合搜索使用。

# 下拉面板底部

下拉面板底部有确定和取消按钮

如果当存在下拉面板时,选中的数据都是暂时的,必须确定才会真正更新数据v-model,如果点击取消或面板外的地方,则会还原上一个选中的状态。

如果不存在下拉面板时,则为实时选中实时更新数据,而且在单选时,选中数据后还会自动关闭下拉面板。而多选不会自动关闭。

# 5.12.3 可拓展性设计

  1. 可设置单选或多选模式
  2. 可自定义选项内容
  3. 可独立复用面板
  4. 可在头部插入自定义内容
  5. 可自定义搜索函数
  6. 可自由保留去除头部栏和底部栏
  7. 可动态配置显示内容

备注: 目前树对象选择设计还有缺点:默认对象加载是同步加载,这种 方式在ie下一次性加载那么多数据是性能问题的。目前只支持滚动加载。但单个数据滚动出所有数据还是会有同样的问题。目前没有做分页是设计师认为面板不需要使用,而且面板的内容空间不够大。后期如果需要修改,设计师在重新设计展示。

# 5.13 TimePicker 时分秒选择器

时分秒选择是对hui 时间选择器的一个拓展,目前hui时间选择只支持时分秒、时分的格式类型。而现在大部分业务场景都会需要精确到小时的场景。因此这是控件的拓展。

图1.1 时分秒选择器示意图

# 5.13.1 控件设计实现

控件支持以下需求:

  1. 支持时分秒
  2. 支持时分
  3. 支持时
  4. 支持自定义时间区间
  5. 支持效验时间可否相等配置

# 5.13.2 组件框架设计

总体设计可分为3种模式:时分秒、时分和时模式。 时分秒区间为00:00:00-23:59:59; 时分区间为00:00-23:59; 时区间为00:00-24:00,但统计中有部分场景是00:00-23:00,这种需求时,按照点位为统计区间,因此这边可支持配置时间区间,可灵活选择区间。

# 5.13.3 可拓展性设计

  1. 可自定义展示时间区间
  2. 可自定义效验规则
  3. 可配置format时间格式(HH/HH:mm/HH:mm:ss)

# 5.14 TimeQuarter 季度选择器

季度选择器也是对hui 时间选择器的一个拓展,目前hui时间选择只支持日、月、年的选择。而目前业务场景季度的选择也时分普遍。

图1.1 季度选择器示意图

# 5.14.1 控件设计实现

控件支持以下需求:

  1. 支持季度选择
  2. 支持不可选的配置

# 5.14.2 组件框架设计

该控件设计比较简单,就是选择一个年份和一个季度。数据同步更新即可。 这边数据类型可支持字符串和时间格式2种。选择的时间会通过计算转变为一个时间区间。 如2019年第一季度,那么抛出数据为['2019-01-01', '2019-03-31']

# 5.14.3 框架可拓展性设计

  1. 支持可选时间的配置

# 5.15 TimeSelector 时间选择器

时间选择器是一个多种时间的综合体:日、周、月、季度、年、自定义、特殊日等等组合而成。这种模式多用于图表类的查询。方便用户直接查看对应的时间。

时间选择器可分为2种模式:简易模式和复杂模式。 简易模式是由今日、昨日、近7天、近30天、自定义维度构成,可根据实际需求,自行对维度做删减。 复杂模式是有日、周、月、季度、年、自定义以及自行拓展时间维度沟通,可根据实际需求,自行对维度做删减。

时间选择器主要是将时分秒选择器,季度选择器以及hui 时间选择组合而成,其核心是自由切换时间维度时,可触发数据更新,数据格式都统一化处理,对外抛出具体的时间或事件区间,如果有颗粒度还会给出对应颗粒度。此外并在正向切换时,时间将做联动处理(比如从今日切到周,则会切到今日当前的周,以此类推)。

# 5.15.1 控件设计实现

控件支持以下需求:

  1. 支持简易模式
  2. 支持复杂模式
  3. 支持数据正向联动
  4. 支持时间维度的扩展
  5. 支持颗粒度的选择

# 5.15.2 组件框架设计

总体设计这边主要需要设计的就是简易模式和复杂模式2大类。

简易模式:该模块比较简单,今日、昨日、近7天、近30天的时间值都是确定不变的,而自定义时间需要使用者选择需要的时间,当切换时间维度时,只需要更新数据即可。 最近7天,最近30天,默认以今天为基准(含今天)往前推算7天、30天; 当选择的日期跨度由限制时,可在选择日期超出上限时给予错误提示;

复杂模式:日、周、月、季度、年、自定义维度都是使用者自己选择时间,当选择对应的时间后,会抛出对应的字符串格式的时间,如日:'2019-7-17', 周:['2019-07-15', '2019-07-21'],月: '2019-7', 季度:['2019-07-01', '2019-09-30'], 年:'2019', 自定义: ['开始时间', '结束时间'];

颗粒度的计算方式如下:

时间跨度不超过 7 天,按日统计;

时间跨度超过 7 天不超过 31 天,按日或周统计;

时间跨度 超过 31 天不超过 1年,按周、月或季度统计;

时间 跨度大于1 年,按月、季、年 统计;

当前颗粒度主要场景为自定义维度时,选择不同的时间范围,会有对应的时间颗粒度的下拉列表。

数据的正向联动设计:

Tab 切换至日,默认显示当天 24h 的数据;切换至周,自动显示当天所在周数据;切换至季,自动显示当天所在季度数据;切换至年,自动显示当天所在年数据;切换至自定义,需手动选择日期范围,选择后显示对应数据。

当前所有时间都做不可选择未来时间的限制。此外还有以下情况要特殊处理: 周: 当日所有的周是可选择的;月:当日所在的月是可选择的;季度:当日所在的季度是可选择;年:当日所在的年是可选择的。

# 5.15.3 控件拓展性设计

  1. 可自定义展示时间维度
  2. 数据双向绑定
  3. 可配置数据联动
  4. 可动态配置颗粒度
  5. 可拓展默认维度意外的配置
  6. 可动态拓展内容展示
  7. 可配置日的时间精确维度
  8. 可配置简要时间模式

# 5.15.4 对外提供的API

  1. 更新扩展时间维度的方法
  2. 重置时间方法
  3. 转变时间格式方法

# 5.15.5 对外提供的可配项

  1. time-labels: 按钮显示文本
  2. default-time: 初始化默认时间
  3. day-options: 日维度的时间限制条件
  4. week-options: 周维度的时间限制条件
  5. month-options: 月维度的时间限制条件
  6. quarter-options:季度维度的时间限制条件
  7. year-options: 年维度的时间限制条件
  8. custom-options: 自定义维度的时间限制条件
  9. granularity-list: 颗粒度列表
  10. granularity-type: 用于控制颗粒度展示的范围
  11. day-type: 日的精确到类型
  12. accurate-day-format: 精确时间格式
  13. type: 时间模式:简易或复杂
  14. 以及类型时分秒选择器配置、季度选择器配置和hui时间选择的配置

# 5.16 ImgCard 图片卡片布局

卡片页面布局是一种针对卡片类型的一个布局方式,主要采用了栅格布局。 目前卡片图片的比例分为16:9和1:1两种模式。

16:9模式中的栅格布局为: n ≤750px,采用col-md-12(2等分); 750<n ≤1216px, 采用col-lg-8(3等分); 1216<n ≤ 1680px,采用col-xl-6(4等分); n>1680px ,采用col-xxl-4(6等分)。

1:1模式中的栅格布局为: n ≤750px,采用col-md-8(3等分); 750<n ≤1216px, 采用col-lg-6(4等分); 1216<n ≤ 1680px,采用col-xl-4(6等分); n>1680 px,采用col-xxl-3(8等分);

此外除了默认的2种栅格模式,还对外提供自定义的可配置的栅格模式。 通过设计师来设计其他模式。

# 5.16.1 布局设计实现

控件支持以下需求:

  1. 16:9模式
  2. 1:1模式
  3. 自定义模式

# 5.16.2 布局设计

通过类型cardRatio来设置卡片的栅格布局模式; 通过customSpan用来设置自定义的栅格模式;

支持内置的卡片预设的样式,也可通过img-card-item容器来自定义展示内容。

# 5.16.3 可拓展性设计

  1. 可配置栅格模式
  2. 可配置图片展示的比例
  3. 可自定义卡片内容

# 6 工具类设计

# 6.1 前言

无论是任何语言,工具类库都是必不可少的。web 前端比较优秀的类库有 UnderscoreLodash。由于 ES6 的普及,以及未来 E7...,这些类库中一些优秀的工具方法,渐渐的被 ES 内置语法取代,慢慢的淡出了历史的舞台。但仍有很多工具方法,或因未被定为 ES 规范语法,或因需要它来兼容各大浏览器,被广泛需求。而仅因使用几个函数,引入几个第三方类库,又显得冗余。于是诞生了 @hui-pro/utils。

# 6.2 背景

@hui-pro/utils 前身是@sword/utils、dolphin_common、以及其他同事在日常工作中所总结出的一些优秀的工具方法。因@hui-pro 的诞生,将这些工具类合并,并统一开发规范。

# 6.3 设计约束

  1. 受命名约束,所有工具类命名必须唯一
  2. 项目中使用 es 包时需要对 node_modules/@hui-pro/utils 进行 babel 转义。
  3. 按需加载依赖第三方类库 babel-plugin-import

# 6.4 方案说明

开发:

src 目录为开发源码,按工具类型划分目录结构。

生产:

通过 npm run build,将有层级的 src 目录输出到 lib(commonjs)es(esm) 下,并平铺文件,入口main默认指向es

使用:

参考 使用教程

# 6.5 构建流程图

构建流程图

# 7 测试方案

# 7.1 方案说明

由于体系中的 ui 库要达成统一,hui 规范从 1.0 升级到 2.0;在 BS 端也做了统一的规范,规范内容包括,样式,基础控件,文案,布局;具体规范内容参考链接:http://uplus.hikvision.com.cn/#community/article?id=b20c1faa-b0dc-45f6-ad51-919ef7e8adaa

在 hui2.0 的技术实现上我们也做了一些调整,做了一些功能上的非兼容性更新;重新定义了浏览器支持的范围,去掉了一些 ie9 遗留的功能,删除了一些设计了不好的控件接口;

本文针对以上这些并结合项目做一次针对项目上的方案推荐,有接入的可以考虑参考本文做针对性的测试。

# 7.2 测试要点

# 7.2.1 浏览器支持策略改变

引入了browserslist规范,我们采用了默认的规则,Browserslist’s default browsers (> 0.5%, last 2 versions, Firefox ESR, not dead). 好处是与时俱进,并对一些超过 2 年官方不维护的浏览器不做支持(包括 ie10);更多细节可以参考之间的浏览器选型预研方案

# 7.2.2 控件和布局的非兼容性更新

这次样式更新的会比较多,最重要的字体变为 14px,字体图标绘制区域从 16px 改为 24px(展示内容由设计者定,一般都会留空隙);其他交互的变化改动参考http://iris.hikvision.com.cn/huidesign/develop/issues/14; 如果测试时间充裕可以再项目中针对一些页面对控件进行测试;在事业部中也会有单独的业务类控件,这个也需要单独进行做测试。

布局这次改动会比较多,多了很多典型页面,在事业部也会拓展很多典型页面;这块也需要详细测试。

# 7.3 测试方案推荐

# 7.3.1 浏览器测试方案

主流浏览器建议主测使用场景多于 0.5%的,如果存在多浏览器测试的话,尽量采用分 build 测试;如果存在一些部门还支持 ie10 的,我们 hui 只会支持 1 个小版本,而且是兼容性的,所以项目中只能测主功能,不测细节。ie10 和 11 的一些细节兼容性不再做维护。

跨版本不大的浏览器的建议只需要测试一个版本,比如 chrome 要支持 49 的可以单独测试;

# 7.3.2 布局和控件的测试方案

  • 典型页面测试:各个部门可以提炼出一些典型页面进行测试,不需要覆盖全部页面;典型页面我们有一个服务lego可以查到对应的典型页面,一些基础的控件也建议在典型页面中测试
  • 复杂控件测试:根据复杂控件找页面,单独对控件进行测试,复杂控件比如时间选择器,树等
  • 测试的主要内容为 ui 交互内容还原度,功能稳定与合理性

典型页面和业务控件,可以单独咨询项目对应的 UI 交互负责人

# 7.3.3 性能测试

在页面数据量不大,返回数据及时的情况下,基本页面尽量保持 2s 内加载完成;如果页面节点数量过大,另行计算时间;

# 8 修订记录

修改人 修改时间 说明
相霄 2019.2.14 创建文档
朱献康 2019.2.15 添加框架设计 3.1 整体设计方案选型、3.2.1 ~ 3.2.4
陈冠彬 2019.2.18 添加框架设计的 3.2.5 ~ 3.2.7
高明君 2019.1.18 添加框架设计 3.2.8 Eslint 配置和 Stylelint 配置
张鑫14 2019.2.18 添加框架设计 3.2.13 gitlab 的 CI/CD 持续集成设计
陈冠彬 2019.2.19 添加章节 4 布局设计
张鑫14 2019.2.20 添加业务控件设计 5.1 ~ 5.2
徐子龙 2019.2.20 添加业务控件设计 5.3 ~ 5.4
姜炎6 2019.2.21 添加业务控件设计 5.5 ~ 5.8
郑杰7 2019.2.22 添加业务控件设计 5.9 ~ 5.16
高明君 2019.2.22 添加章节 6 工具类设计
徐子龙 2019.2.22 添加 3.2.10 vuepress设计
郑杰7 2019.2.26 修改业务控件设计 5.9 ~ 5.16