AJ 组件库之通用数据字典 DataDict
需求分析
数据字典(Data Dictionary)是一种用户可以访问的记录数据库和应用程序元数据的目录。数据字典在系统也是必备的组件,对此我们想想要怎么做:
- 基本功能:Key/Value 方式的存储结构,外加其他属性如”说明“,尽可能设计为通用一点
- 树状结构,也就是说带分类功能的。换句话说,这个数据字典也是一个分类表(数据库层面就是加一个
parentId
字段,当然其他 逻辑较为复杂,而且也不是pId
这唯一一种树的存储方法) - 可以租户/门户隔离,也就是多个门户或者租户。
基本设计
表设计如下。
CREATE TABLE `sys_datadict` (`id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '主键 id,自增',`name` VARCHAR(20) NULL DEFAULT NULL COMMENT '名称、自定义编码、相当于 key。可选的' COLLATE 'utf8mb4_bin',`value` VARCHAR(50) NOT NULL COMMENT '值' COLLATE 'utf8mb4_bin',`content` VARCHAR(200) NULL DEFAULT NULL COMMENT '简介、描述' COLLATE 'utf8mb4_bin',`parentId` INT(11) NULL DEFAULT NULL COMMENT '父 id',`type` INT(11) NULL DEFAULT NULL COMMENT '类型 id',`sortNo` TINYINT(4) NULL DEFAULT NULL COMMENT '顺序、序号',`stat` TINYINT(4) NULL DEFAULT NULL COMMENT '数据字典:状态',`uid` BIGINT(20) NULL DEFAULT NULL COMMENT '唯一 id,通过 uuid 生成不重复 id',`tenantId` INT(11) NOT NULL DEFAULT '0' COMMENT '租户 id。0 = 不设租户',`createByUser` INT(11) NULL DEFAULT NULL COMMENT '创建者 id',`createDate` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`updateDate` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',PRIMARY KEY (`id`) USING BTREE,INDEX `tenantId` (`tenantId`) USING BTREE
)
COMMENT='数据字典'
COLLATE='utf8mb4_bin';
Java Bean:
import java.util.Date;import com.ajaxjs.framework.IBaseModel;/*** 数据字典 * * @author Frank Cheung<sp42@qq.com>* @date 2021-11-07*/
public class DataDict implements IBaseModel {/*** 主键 id,自增*/private Long id;/*** 名称、自定义编码、相当于 key。可选的*/private String name;/*** 值*/private String value;/*** 简介、描述*/private String desc;/*** 父 id*/private Long parentId;/*** 类型 id*/private Long type;/*** 顺序、序号*/private Integer sortNo;/*** 数据字典:状态*/private Integer stat;/*** 唯一 id,通过 uuid 生成不重复 id*/private Long uid;/*** 租户 id。0 = 不设租户*/private Long tenantId;/*** 创建者 id*/private Long createByUser;/*** 创建时间*/private Date createDate;/*** 修改时间*/private Date updateDate;public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getValue() {return value;}public void setValue(String value) {this.value = value;}public String getDesc() {return desc;}public void setDesc(String desc) {this.desc = desc;}public Long getParentId() {return parentId;}public void setParentId(Long parentId) {this.parentId = parentId;}public Long getType() {return type;}public void setType(Long type) {this.type = type;}public Integer getSortNo() {return sortNo;}public void setSortNo(Integer sortNo) {this.sortNo = sortNo;}public Integer getStat() {return stat;}public void setStat(Integer stat) {this.stat = stat;}public Long getUid() {return uid;}public void setUid(Long uid) {this.uid = uid;}public Long getTenantId() {return tenantId;}public void setTenantId(Long tenantId) {this.tenantId = tenantId;}public Long getCreateByUser() {return createByUser;}public void setCreateByUser(Long createByUser) {this.createByUser = createByUser;}public Date getCreateDate() {return createDate;}public void setCreateDate(Date createDate) {this.createDate = createDate;}public Date getUpdateDate() {return updateDate;}public void setUpdateDate(Date updateDate) {this.updateDate = updateDate;}
}
界面:
UI 设计也是大头问题,我们后面再讲。
实现难点
树状结构存储
如何在关系型数据库里面存储树状结构一直是需要思考的问题,尤其对于数据结构掌握不好的同学来说,更是难点。这个知识点我前期探索过,参考博文《SQL 双亲节点查找所有子节点》。现在回过头来看,结合大家的一些经验,决定这样子做。
- MySQL 还是老老实实用 parentId 的存储方法
- MySQL 8.0 虽然支持 CTE 递归,但老版本就无能为力了。对此,比较简单省力的方式是,直接上 MySQL 函数。
MySQL 函数为 getAllChildren
,源码如下:
CREATE DEFINER=`root`@`%` FUNCTION `getAllChildren`(`rootId` INT,`tableName` VARCHAR(50)
)
RETURNS varchar(4000) CHARSET latin1BEGINDECLARE P_TEMP VARCHAR(1000);DECLARE C_TEMP VARCHAR(1000);SET P_TEMP = '0';SET C_TEMP = CAST(rootId AS CHAR);WHILE C_TEMP IS NOT NULL DOSET P_TEMP = CONCAT(P_TEMP, ',', C_TEMP);IF tableName = 'sys_datadict' THENSELECT GROUP_CONCAT(id) INTO C_TEMP FROM sys_datadict WHERE FIND_IN_SET(parentId, C_TEMP) > 0; ELSEIF tableName = 'sys_datadict1' THENSELECT GROUP_CONCAT(id) INTO C_TEMP FROM sys_datadict WHERE FIND_IN_SET(parentId, C_TEMP) > 0; ELSE SET C_TEMP = NULL;END IF; -- if结束END WHILE; RETURN P_TEMP;
END
调用例子:
SELECT * FROM sys_datadict WHERE FIND_IN_SET(id, getAllChildren(2, 'sys_datadict')) ORDER BY parentId ASC
关键原理是 MySQL 的 find_in_set
函数,顺带学习一下。当然了——换了别的数据库就不是这样子做,通通要改。
getAllChildren()
第一个参数为 INT
,是父亲 id,第二个参数是表名,——为什么有表名呢?因为 MySQL 函数不支持动态传入表名作为参数,于是只能写死,因为当初我想这个函数支持多张表而不是一张表写一个函数的。不过我采取代价相对较低的”写死“,在函数里面写死表名,通过 IF ELSE
判断,便可达到支持多张表的目的。
调用上述 SQL 成功返回如下。
获取节点深度
传入节点 id,返回其深度(depth)。深度即表示节点在树中位于第几层次。
需要一个存储过程和函数来完成。如果你要拿去用,可能要改下 表名、字段名或者出参/入参的类型。
CREATE DEFINER=`root`@`%` PROCEDURE `countLayerHelper`(IN id INT, INOUT counter INT)
BEGINIF EXISTS (SELECT 1 FROM sys_datadict o WHERE o.id = id) THENBEGINDECLARE pid INT DEFAULT 0;DECLARE done BOOLEAN DEFAULT FALSE;DECLARE _cursor CURSOR FOR SELECT o.parentId FROM sys_datadict o WHERE o.id = id;DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;SET counter = counter + 1;OPEN _cursor;read_loop: LOOPFETCH _cursor INTO pid;IF done THEN LEAVE read_loop;END IF;CALL countLayerHelper(pid, counter);END LOOP;CLOSE _cursor;END;END IF;
END
另外需要一个函数来调用存储过程。
CREATE DEFINER=`root`@`%` FUNCTION `countLayer`(id INT) RETURNS int(11)
BEGINDECLARE counter INT DEFAULT 0;SET max_sp_recursion_depth = 255;CALL countLayerHelper(id, counter);RETURN counter - 1;
END
用户调用:
SELECT countLayer(3) AS depth
参考出处。
树节点数组如何转换为 JSON 树?
数据库返回的是一维数组,如何转换为带层次的 JSON 树呢?首先声明一个树节点的类型。
/*** 树节点*/
type TreeMap = {id: number;/*** 父 id,输入时候必填*/parentId: number;/*** 子节点,生成 Tree 之后出现*/children?: TreeMap[];
}
输入(JSON 起码要有 id
、parentId
字段):
{"result":[{"stat":null,"updateDate":"2022-03-28 18:12:55","type":null,"content":null,"parentId":9,"sortNo":null,"uid":null,"name":null,"tenantId":0,"id":14,"value":"广州新闻","createByUser":null,"createDate":"2022-03-28 18:12:26"}, {"stat":null,"updateDate":"2022-02-26 21:38:28","type":null,"content":null,"parentId":5,"sortNo":null,"uid":null,"name":null,"tenantId":0,"id":6,"value":"香港新闻","createByUser":null,"createDate":"2022-02-26 21:38:28"}, {"stat":null,"updateDate":"2022-03-28 15:20:43","type":null,"content":null,"parentId":5,"sortNo":null,"uid":null,"name":null,"tenantId":0,"id":7,"value":"澳门新闻","createByUser":null,"createDate":"2022-03-28 15:20:43"}, {"stat":null,"updateDate":"2022-03-28 15:20:55","type":null,"content":null,"parentId":5,"sortNo":null,"uid":null,"name":null,"tenantId":0,"id":8,"value":"台湾新闻","createByUser":null,"createDate":"2022-03-28 15:20:55"}, {"stat":null,"updateDate":"2022-03-28 15:22:06","type":null,"content":null,"parentId":4,"sortNo":null,"uid":null,"name":null,"tenantId":0,"id":12,"value":"美国新闻","createByUser":null,"createDate":"2022-03-28 15:22:06"}, {"stat":null,"updateDate":"2022-03-28 15:22:14","type":null,"content":null,"parentId":4,"sortNo":null,"uid":null,"name":null,"tenantId":0,"id":13,"value":"俄罗斯新闻","createByUser":null,"createDate":"2022-03-28 15:22:14"}, {"stat":null,"updateDate":"2022-03-28 15:21:08","type":null,"content":null,"parentId":3,"sortNo":null,"uid":null,"name":null,"tenantId":0,"id":9,"value":"广东新闻","createByUser":null,"createDate":"2022-03-28 15:21:05"}, {"stat":null,"updateDate":"2022-03-28 15:21:23","type":null,"content":null,"parentId":3,"sortNo":null,"uid":null,"name":null,"tenantId":0,"id":10,"value":"湖南新闻","createByUser":null,"createDate":"2022-03-28 15:21:23"}, {"stat":null,"updateDate":"2021-11-06 23:58:11","type":null,"content":null,"parentId":2,"sortNo":null,"uid":null,"name":null,"tenantId":0,"id":3,"value":"国内新闻","createByUser":null,"createDate":"2021-11-06 23:58:11"}, {"stat":null,"updateDate":"2021-11-06 23:58:25","type":null,"content":null,"parentId":2,"sortNo":null,"uid":null,"name":null,"tenantId":0,"id":4,"value":"国际新闻","createByUser":null,"createDate":"2021-11-06 23:58:25"}, {"stat":null,"updateDate":"2021-11-06 23:58:52","type":null,"content":null,"parentId":2,"sortNo":null,"uid":null,"name":null,"tenantId":0,"id":5,"value":"港澳台新闻","createByUser":null,"createDate":"2021-11-06 23:58:52"}, {"stat":null,"updateDate":"2021-11-06 23:57:25","type":null,"content":null,"parentId":1,"sortNo":null,"uid":null,"name":null,"tenantId":0,"id":2,"value":"新闻","createByUser":null,"createDate":"2021-11-06 23:57:25"}]}
转换为(多了 children
,表示下一级的节点集合):
[{"stat": null,"updateDate": "2021-11-06 23:58:11","type": null,"content": null,"parentId": 2,"sortNo": null,"uid": null,"name": null,"tenantId": 0,"id": 3,"value": "国内新闻","createByUser": null,"createDate": "2021-11-06 23:58:11","children": [{"stat": null,"updateDate": "2022-03-28 15:21:08","type": null,"content": null,"parentId": 3,"sortNo": null,"uid": null,"name": null,"tenantId": 0,"id": 9,"value": "广东新闻","createByUser": null,"createDate": "2022-03-28 15:21:05","children": [{"stat": null,"updateDate": "2022-03-28 18:12:55","type": null,"content": null,"parentId": 9,"sortNo": null,"uid": null,"name": null,"tenantId": 0,"id": 14,"value": "广州新闻","createByUser": null,"createDate": "2022-03-28 18:12:26","children": []}]}, {"stat": null,"updateDate": "2022-03-28 15:21:23","type": null,"content": null,"parentId": 3,"sortNo": null,"uid": null,"name": null,"tenantId": 0,"id": 10,"value": "湖南新闻","createByUser": null,"createDate": "2022-03-28 15:21:23","children": []}]
}, {"stat": null,"updateDate": "2021-11-06 23:58:25","type": null,"content": null,"parentId": 2,"sortNo": null,"uid": null,"name": null,"tenantId": 0,"id": 4,"value": "国际新闻","createByUser": null,"createDate": "2021-11-06 23:58:25","children": [{"stat": null,"updateDate": "2022-03-28 15:22:06","type": null,"content": null,"parentId": 4,"sortNo": null,"uid": null,"name": null,"tenantId": 0,"id": 12,"value": "美国新闻","createByUser": null,"createDate": "2022-03-28 15:22:06","children": []}, {"stat": null,"updateDate": "2022-03-28 15:22:14","type": null,"content": null,"parentId": 4,"sortNo": null,"uid": null,"name": null,"tenantId": 0,"id": 13,"value": "俄罗斯新闻","createByUser": null,"createDate": "2022-03-28 15:22:14","children": []}]
}, {"stat": null,"updateDate": "2021-11-06 23:58:52","type": null,"content": null,"parentId": 2,"sortNo": null,"uid": null,"name": null,"tenantId": 0,"id": 5,"value": "港澳台新闻","createByUser": null,"createDate": "2021-11-06 23:58:52","children": [{"stat": null,"updateDate": "2022-02-26 21:38:28","type": null,"content": null,"parentId": 5,"sortNo": null,"uid": null,"name": null,"tenantId": 0,"id": 6,"value": "香港新闻","createByUser": null,"createDate": "2022-02-26 21:38:28","children": []}, {"stat": null,"updateDate": "2022-03-28 15:20:43","type": null,"content": null,"parentId": 5,"sortNo": null,"uid": null,"name": null,"tenantId": 0,"id": 7,"value": "澳门新闻","createByUser": null,"createDate": "2022-03-28 15:20:43","children": []}, {"stat": null,"updateDate": "2022-03-28 15:20:55","type": null,"content": null,"parentId": 5,"sortNo": null,"uid": null,"name": null,"tenantId": 0,"id": 8,"value": "台湾新闻","createByUser": null,"createDate": "2022-03-28 15:20:55","children": []}]
}]
使用递归是一个方法,而且异常简洁、直观:
/*** 数组转成树* * @param list 数组* @param parentId 顶级父 id* @returns */
function tranListToTreeData(list: TreeMap[], parentId: number): TreeMap[] {// 先找到所有的根节点let tranList: TreeMap[] = list.filter((it: TreeMap) => it.parentId === parentId);// 传入 id list进行递归 在筛选出 他的父级 是一个数组tranList.forEach((itm: TreeMap) => itm.children = tranListToTreeData(list, itm.id));return tranList;
}
另外一个方法是这样的,建立一个 Map<id, TreeMap>
,然后相当于建立“链表”连接起来,——这个思路也异常简单,而且比递归的方法少了出栈入栈的消耗,效率更高。
/*** 数组转成树* * @param list 数组* @returns JSON Tree*/
function tranListToTreeData(list: TreeMap[]): TreeMap[] {let treeList: TreeMap[] = [];// 最终要产出的树状数据的数组let map = {}; // 所有项都使用对象存储起来list.forEach((item: TreeMap) => { // 建立一个映射关系:通过 id 快速找到对应的元素if (!item.children)item.children = [];map[item.id] = item;});list.forEach((item: TreeMap) => {// 对于每一个元素来说,先找它的上级// 如果能找到,说明它有上级,则要把它添加到上级的 children 中去// 如果找不到,说明它没有上级,直接添加到 treeListlet parent = map[item.parentId];if (parent)// 如果存在则表示 item 不是最顶层的数据parent.children.push(item);elsetreeList.push(item);// 如果不存在 则是顶层数据});return treeList;
}
AJ 组件库之通用数据字典 DataDict相关推荐
- 浅尝 | 从 0 到 1 Vue 组件库封装
写在前面 对于目前框架为王的时代,前端可能很大一部分时间,都是在开发相关的页面组件,而有句话说得好,没有哪个前端不想拥有一个属于自己的,为自己量身定制的组件库,那么本文就为大家整理一下: 如何从 0 ...
- web元件库、axure元件库、通用元件库、常用web组件、常用表单、框架、数据表单、导航栏、边框、图标、列表、日期时间选择器、评分组件、穿梭框、输入框、步骤条、图表组件、数据可视化、后台模板、时间轴
web元件库.axure元件库.通用元件库.常用web组件.常用表单.框架.数据表单.导航栏.边框.图标.列表.日期时间选择器.评分组件.穿梭框.输入框.步骤条.图表组件.数据可视化.后台模板.时间轴 ...
- 移动端通用元件库+app通用元件库+数据展示+操作反馈+通用模板+数据录入+列表页+表单页+详情页+通用版布局+移动端手机模板+业务组件+反馈组件+展示组件+表单组件+导航组件
移动端通用元件库+app通用元件库+数据展示+操作反馈+通用模板+数据录入+列表页+表单页+详情页+通用版布局+移动端手机模板+业务组件+反馈组件+展示组件+表单组件+导航组件 原型展示及下载地址:h ...
- 国产web端开源ui组件-通用前端ui界面组件库
写个序吧: 随着工作的不断深入,越来越发现很多好的前端开源项目都来自于国外,国产的开源项目很多时候面临叫好不叫座甚至有很多就消失不见了.开源和创新,不仅仅是需要我们的参与同样也需要我们不断的传播,因此 ...
- Axure RP大数据BI可视化大屏原型模板及通用组件库
对于做大数据交互原型设计时,需要做到很多背景很科技背景,数据统计汇总组件.Axure RP大数据可视化大屏原型模板及通用组件库主要结构由大屏模板.登录界面.入口界面.初始框架.图表组件.设计组件等六大 ...
- GMU(Global Mobile UI)是百度前端通用组开发的移动端组件库
GMU(Global Mobile UI)是百度前端通用组开发的移动端组件库,具有代码体积小.简单.易用等特点,组件内部处理了很多移动端的bug,覆盖机型广,能大大减少开发交互型组件的工作量,非常适合 ...
- 全端通用快速开发UI组件库UnifyUi大更新,Unify Ui是基于uni-app的全端(vue/nvue)组件库
本次进行整个框架的ui组件结构,添加详细注释.优化部分组件,新增 暗黑模式 是不是有种剁手的感觉?点击 unifyui 时刻关注动态, unifyui 团队即将公开招募协作者共同完善unifyui! ...
- .NET 开源免费图表组件库,Winform,WPF 通用
大家好, 我是等天黑, 今天给大家介绍一个功能完善, 性能强悍的图表组件库 ScottPlot, 当我第一次在 github 上看到这个库, 我看不懂,但我大受震撼, 这么好的项目当然要分享出来了. ...
- 使用vue加svg实现流程图代码_京东风格的移动端Vue组件库NutUI2.0来啦
移动端 Vue 组件库 NutUI 自发布以来受到了广泛的关注.据不完全统计,目前至少有30多个京东的 web 项目正在使用 NutUI . 经过一段时间紧锣密鼓的开发,近期,京东正式发布了 NutU ...
最新文章
- 各大知名企业的Research展示
- linux中时间戳与date的互转
- 重磅!神策数据游戏行业解决方案全面上线,速来围观
- GridView空记录时显示Header和Footer
- MySQL 5.6.20-enterprise-commercial的参数文件位置问题
- hashchange
- 苹果手机还原网络设置会怎样_如果你的苹果手机信号不好!要记得这样设置,让你信号轻松满格...
- 初次汇编程序 masm5
- 漂亮得不像触控板,全新 Magic Trackpad 2
- 戴尔服务器密码忘记,进入PE清除密码
- 已知六条边的边长,求四面体体积
- GooglTest GoogleMock 实践感想三 死亡测试初步(1)
- 大学计算机课英语心得体会,【大学计算机课程总结12篇】_大学计算机课程总结范文大全_2021年大学计算机课程总结_东城教研...
- Android studio Android源码开发环境搭建
- STM32L4超低功耗特性概述
- 教你用安卓手机以及在Linux下连接校园联通无线宽带
- 免费的NBA历史得分榜接口
- 7. RabbitMQ 高级
- java 将.amr音频文件转换为.mp3或.wav文件
- 计算机绘图中特征建模的概念,科大机械CADCAM习题集..doc
热门文章
- 复旦计算机考研复试要口试吗,复试的正确打开方式|复旦篇
- 北邮计算机复试读英语,简单的说一下北邮的复试过程吧
- 搜索其他计算机共享的打印机,打印机共享后其他电脑搜索不到_共享打印机查找不到_打印机品牌排行榜...
- BOM浏览器对象模型(Browser Object Model)
- unity 实现3d模型渐隐(修改材质透明度)
- 小米vr一体机html,小米VR一体机体验:一款让你大开眼界的产品
- STM32F030F4P6-提高ADC采集精度
- 【自制】csdn自定义模块栏目 个性化 【美化个人简介】
- 毕业设计-一种基于 MATLAB 的指纹识别方法
- geetest php,GitHub - lilwil/geetest: Geetest For ThinkPHP5