技术选型
SpringBoot2.2.6+MySQL5.6+Vue2.0+ElementUI
数据库设计
考虑到不同的数据字典数据结构区别很小,除了在前台页面展示的列名和列的数量不一样以外,基本上都可以沿用同一种数据表结构,所以考虑使用两张表T_DICTIONARY和T_DICTIONARY_DETAIL,前者用来维护所有的数据字典表的表信息,包括表名、列名、标识、是否在用等,后者用来保存具体的某一个数据字典的数据,这里通过t_did关联前者的主键,用于区分该表的数据具体属于哪一张。后者的通用字段有五个,分别是col1到col5,根据需要从前到后存储数据字段的数据,而列名的显示通过T_DICTIONARY的字段t_col_name来保存,需要用户在录入数据的时候输入列名称,数据库存储的时候.用逗号隔开。
系统设计
系统采用前后端分离的模式,后台统一返回封装数据,前台对数据进行渲染,前台通过路由来进行数据表格页面的切换。
代码实现
后台
核心接口包括主表t_dictionary:
@PostMapping("/list")
public List<Dictionary> dictionaryListAll(){
QueryWrapper<Dictionary> queryWrapper = new QueryWrapper<>();
queryWrapper.orderByAsc("t_order");
return dictionaryService.list(queryWrapper);
}
@GetMapping("/{id}")
public Dictionary dictionaryOne(@PathVariable Long id){
return dictionaryService.getById(id);
}
@PostMapping("/add")
public boolean dictionaryAdd(@RequestBody Dictionary dictionary){
return dictionaryService.save(dictionary);
}
@PostMapping("/delete/{id}")
public boolean dictionaryDelete(@PathVariable Long id){
return dictionaryService.removeById(id);
}
@PostMapping("/update")
public boolean dictionaryUpdate(@RequestBody Dictionary dictionary){
return dictionaryService.updateById(dictionary);
}
以及其详情表的接口
@GetMapping("/query/{did}")
public List<DictionaryDetail> dictionaryDetailList(@PathVariable Long did){
QueryWrapper<DictionaryDetail> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("t_did",did);
return iDictionaryDetailService.list(queryWrapper);
}
@GetMapping("/{id}")
public DictionaryDetail dictionaryDetailOne(@PathVariable Long id){
return iDictionaryDetailService.getById(id);
}
@PostMapping("/update")
public boolean dictionaryDetailUpdate(@RequestBody DictionaryDetail dictionaryDetail){
return iDictionaryDetailService.updateById(dictionaryDetail);
}
@PostMapping("/add")
public boolean dictionaryDetailAdd(@RequestBody DictionaryDetail dictionaryDetail){
return iDictionaryDetailService.save(dictionaryDetail);
}
@PostMapping("/delete/{id}")
public boolean dictionaryDetailDelete(@PathVariable Long id){
return iDictionaryDetailService.removeById(id);
}
后台代码较少,主要提供两表的CRUD功能即可.
前台
前台主要包括两部分:左侧列表和右侧表格内容.左侧列表第一个菜单默认为对主表的管理[综合管理],点击后右侧显示主表的表格数据,添加数据后左侧列表中多出一个菜单,点击后即可对该[新增的数据字典表]进行数据维护.
Dictionary.vue 路由配置:
routes: [
{
path: '/',
name: '',
component: Dictionary,
redirect: "/all",
children: [
{path: '/all', component: All, name: "All",hidden: false},
{path: '/detail/:id', component: Detail, name: "Detail",hidden: true},
]
},
// all是主表对应的页面,detail则是各个字典表的页面,需要循环输出
]
<el-menu-item-group>
<template slot="title"></template>
<el-menu-item index="/all">
<span>综合管理</span>
</el-menu-item>
<el-menu-item v-for="(item,index) in navList" :index="'/detail/'+item.id" :key="index">
<span>{{item.dictionaryName}}</span>
</el-menu-item>
</el-menu-item-group>
All.vue 除了对t_dictionary的增上改查以外,在添加操作之后要对其父路由:Dictionary进行数据重新加载的操作. 在添加的回调里面,完成对Dictionary.vue的refresh方法调用
this.$emit('refresh', "");
另外由于设计的字段是通用字段,所以在添加数据的时候需要同时添加字典表的字段名称,用于前台显示.
dictionaryColName:[{ required: true, message: '请输入列名称(逗号隔开,最多支持五列)', trigger: 'blur' }],
Detail.vue 需要动态加载列和列名称
<el-table-column
v-for="(item,index) in col"
:key="index"
fixed
:prop="item.prop"
:label="item.name"
width="150">
</el-table-column>
data(){
return {
id : '',
col : [],
tableData: [],
dialogTableVisible: false,
dialogFormVisible: false,
formData:['','','','','','','','',''],
}
}
initTable:function(id){
this.axios({
method: 'get',
data: "",
url: 'api/dic/'+id,
}).then(result => {
var col_names = result.data.data.dictionaryColName.split(',');
var cols = [];
for(var i = 0;i<col_names.length;i++){
var obj = {};
obj.name = col_names[i];
obj.prop = 'col'+(i+1);
cols.push(obj);
}
this.col = cols;
this.initTableData(id);
}).catch(error => {
this.$message.error("网络异常");
})
},
initTableData:function(id){
this.axios({
method: 'get',
data: "",
url: 'api/dic/detail/query/'+id,
}).then(result => {
this.tableData = result.data.data;
}).catch(error => {
this.$message.error("网络异常");
})
},
同样的在添加页面修改页面都需要动态显示列和列名称.