Element表格和表单字典转换(静态和动态)
使用vue、element对form和table做字典映射。非常简单方便。还附带了和springboot集成的接口动态方式
一、定义静态字典
1. 新建字典文件
首先在util文件夹新建一个dict.js
const dictList={
statusList: [
{
id:0,name:'未使用'},
{
id:1,name:'已绑定'},
{
id:2,name:'已使用'},
{
id:3,name:'禁用'},
{
id:4,name:'作废'},
],
boolList: [
{
id:0,name:'禁用'},
{
id:1,name:'启用'}
]
}
export default dictList
2. 新建字典工具类
在util文件夹新建getDict.js文件
import dictList from '@/utils/dict'
/**获取静态字典值 **/
export function getDict(data, element, dictName) {
const col = element.property
const dictArr = dictList[dictName]
if (!dictArr) return
for (let item of dictArr) {
if (item.id === data[col]) {
return item.name
}
}
}
/**获取静态字典列表 **/
export function getDictList (dictName) {
return dictList[dictName]
}
3. 挂载到Vue上
找到项目main.js,新增如下代码
import {
getDict,getDictList} from '@/utils/getDict'
Vue.prototype.$getDict=getDict
Vue.prototype.$getDictList=getDictList
4. 页面使用
1. 表格中使用
表格中根据字典的key获取字典的name
<el-table>
<el-table-column label="状态" prop="status" :formatter="(row, column) => $getDict(row, column, 'statusList')" />
</el-table>
2. 表单中使用
表单中要通过下拉框的形式让用户选择字典
首先在data中声明变量
data() {
return {
statusList:this.$getDictList('statusList')
}
}
然后在form中使用下拉框
<el-form>
<el-form-item label="状态" prop="status">
<el-select v-model="form.status" style="width: 100%" placeholder="请选择状态">
<el-option
v-for="item in statusList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-form>
二、定义动态字典
这里使用springboot、mysql生成字典后台接口
1. 新建数据库
字段 | 类型 | 长度 | 注释 |
---|---|---|---|
id | int | 11 | 主键 |
parent_id | int | 11 | 父主键 |
dict_code | varchar | 255 | 字典码 |
dict_key | int | 255 | 字典值 |
dict_value | varchar | 255 | 字典名称 |
remark | varchar | 255 | 字典备注 |
is_deleted | int | 2 | 是否已删除,0:正常,1:已删除 |
CREATE TABLE `dict` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`parent_id` int(11) NULL DEFAULT 0 COMMENT '父主键',
`dict_code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '字典码',
`dict_key` int(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '字典值',
`dict_value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '字典名称',
`remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '字典备注',
`is_deleted` int(2) NULL DEFAULT 0 COMMENT '是否已删除,0:正常,1:已删除',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1174 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '系统字典表' ROW_FORMAT = DYNAMIC;
SET FOREIGN_KEY_CHECKS = 1;
2. 生成Springboot接口
实体类Dict.java
@Entity
@Table(name = "dict")
@Data
public class Dict{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@ApiModelProperty(value = "主键" , example = "1")
private Integer id;
@ApiModelProperty(value = "父主键" , example = "1")
private Integer pid;
@ApiModelProperty(value = "字典码" , example = "1")
private String code;
@ApiModelProperty(value = "字典值" , example = "1")
private Integer skey;
@ApiModelProperty(value = "字典名称" , example = "1")
private String svalue;
@ApiModelProperty(value = "备注" , example = "1")
private String remark;
@ApiModelProperty(value = "是否删除" , example = "1")
private Integer isdelete;
}
dao层
public interface DictDao extends JpaRepository<Dict, Integer> , JpaSpecificationExecutor<Dict>{
List<Dict> findAllByPid(Integer pid);
void deleteByPid(Integer pid);
@Query(value="select * from dict where code=:code and isdelete=0 and pid!=0"
,nativeQuery=true)
List<Dict> getDictByCode(@Param("code") String code);
}
service层
@Service
public class DictService{
@Autowired
private DictDao dictDao;
public ResponseModel<Page<Dict>> page(String keywords, Pageable page) {
Page<Dict> result = dictDao.findAll(new Specification<Dict>() {
@Override
public Predicate toPredicate(Root<Dict> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
List<Predicate> list = new ArrayList<Predicate>();
list.add(cb.equal(root.get("pid").as(Integer.class), 0));
if (!StringUtils.isEmpty(keywords)) {
//字典码
list.add(cb.like(root.get("code").as(String.class), "%" + keywords + "%"));
//字典名称
list.add(cb.like(root.get("svalue").as(String.class), "%" + keywords + "%"));
//备注
list.add(cb.like(root.get("remark").as(String.class), "%" + keywords + "%"));
}
Predicate[] p = new Predicate[list.size()];
return cb.or(list.toArray(p));
}
}, page);
return ResponseModel.success(result);
}
public ResponseModel<Dict> get(Integer id) {
return ResponseModel.success(dictDao.findById(id).get());
}
public ResponseModel<List<Dict>> findList() {
return ResponseModel.success(dictDao.findAll());
}
@Transactional
public ResponseModel<Dict> save(Dict dict) {
if(dict.getId()!=null){
Dict dictOld = dictDao.findById(dict.getId()).orElse(null);
if(dictOld!=null){
UpdateUtil.copyNullProperties(dict,dictOld);
return ResponseModel.success(dictDao.save(dictOld));
}
}
return ResponseModel.success(dictDao.save(dict));
}
@Transactional
public ResponseModel delete(String ids) {
dictDao.deleteAllById(ConvertUtil.strToIntList(ids));
return ResponseModel.success("删除成功");
}
@Transactional
public ResponseModel saveBatch(List<Dict> dicts){
if(dicts.size()>0){
dictDao.deleteByPid(dicts.get(0).getPid());
dictDao.flush();
dictDao.saveAll(dicts);
}
return ResponseModel.success("保存成功");
}
public ResponseModel<List<Dict>> findAllByPid(Integer pid){
return ResponseModel.success(dictDao.findAllByPid(pid));
}
public ResponseModel<Map<String,List<Dict>>> getDictByCode(String codes){
String[] codeArr = codes.split(",");
Map<String,List<Dict>> result = new HashMap<>();
for(int i=0;i<codeArr.length;i++){
result.put(codeArr[i],dictDao.getDictByCode(codeArr[i]));
}
return ResponseModel.success(result);
}
}
Controller层
@RequestMapping(value = "/dict")
@RestController
@Api(value="接口",tags={
"Dict()-增删改查;导入导出"})
public class DictController extends CommonController{
@Autowired
private DictService dictService;
@ApiOperation(value = "获取分页数据" ,notes = "获取分页数据" )
@ApiImplicitParams({
@ApiImplicitParam(name = "keywords" ,value = "搜索关键字" , required = false, dataType = "String")
})
@PostMapping("/page")
public ResponseModel<Page<Dict>> page(String keywords, Pageable page) {
return dictService.page(keywords, page);
}
@ApiOperation(value = "获取列表数据" ,notes = "获取列表数据" )
@GetMapping("/findAll")
public ResponseModel<List<Dict>> findAll(){
return dictService.findList();
}
@ApiOperation(value = "获取单条数据对象" ,notes = "获取单条数据对象")
@ApiImplicitParams({
@ApiImplicitParam(paramType = "query",name = "dictid" ,value = "dictID" , required = true, dataType = "Integer")
})
@GetMapping("/{id}")
public ResponseModel<Dict> get(@PathVariable Integer id) {
return dictService.get(id);
}
@ApiOperation(value = "保存单条数据", notes = "保存单条数据,id列为空则为新增,不为空则为修改")
@ApiImplicitParams({
@ApiImplicitParam(name = "Dict",value = "dict",required = false,dataType = "Dict")
})
@PostMapping("/save")
public ResponseModel<Dict> save(Dict dict) {
return dictService.save(dict);
}
@ApiOperation(value = "删除", notes = "删除" )
@ApiImplicitParams({
@ApiImplicitParam(name = "dictids", value = "DictID", required = true, dataType = "String")
})
@RequestMapping(value = "/delete" ,method = {
RequestMethod.DELETE})
public ResponseModel delete(String dictids) {
return dictService.delete(dictids);
}
@ApiOperation(value = "批量保存数据", notes = "批量保存数据,先删除,再保存")
@ApiImplicitParams({
@ApiImplicitParam(name = "Dict",value = "dict",required = false,dataType = "Arrays")
})
@PostMapping("/saveBatch")
public ResponseModel<Dict> saveBatch(@RequestBody List<Dict> dicts) {
return dictService.saveBatch(dicts);
}
@ApiOperation(value = "根据Pid查询字典", notes = "根据Pid查询字典")
@ApiImplicitParams({
@ApiImplicitParam(name = "pid",value = "pid",required = true,dataType = "Integer")
})
@PostMapping("/dictDetail")
public ResponseModel<List<Dict>> findByPid(Integer pid) {
return dictService.findAllByPid(pid);
}
@ApiOperation(value = "根据code获取字典列表", notes = "根据code获取字典列表,入参为多个code字符串")
@ApiImplicitParams({
@ApiImplicitParam(name = "csode",value = "csode",required = true,dataType = "String")
})
@PostMapping("/getDictCode")
public ResponseModel<Map<String,List<Dict>>> findByCode(String codes) {
return dictService.getDictByCode(codes);
}
}
3. vue字典管理页面
api接口dictapi.js
import request from '@/utils/request'
//查询分页
export function getPage(page) {
return request({
url: '/dict/page',
method: 'post',
data: page
})
}
//根据ID查找
export function find(id) {
return request({
url: '/dict/'+id,
method: 'get',
})
}
//批量删除
export function deleteModles(ids) {
return request({
url: '/dict/delete?dictids='+ ids,
method: 'delete',
})
}
//保存或更新
export function save(data) {
return request({
url: '/dict/save',
method: 'post',
data: data
})
}
export function saveAll(data) {
return request({
url: '/dict/saveBatch',
method: 'post',
data: data,
notForm: true
})
}
export function dictDetail(data) {
return request({
url: '/dict/dictDetail',
method: 'post',
data: data,
})
}
export function getDictCode(code) {
return request({
url: '/dict/getDictCode',
method: 'post',
data: {
codes:code},
})
}
vue页面dictManage.vue
<template>
<div class="app-container">
<div>
<el-row :gutter="0" class="baseMargin">
<el-col :span="18">
<el-input v-model="page.keywords" style="width: 100%" @keyup.enter.native="initPage" placeholder="请输入关键字搜索" size="small"></el-input>
</el-col>
<el-col :span="6" style="text-align: center">
<el-button size="small" type="primary" icon="el-icon-search" @click="initPage">查询</el-button>
<el-button size="small" type="primary" icon="el-icon-refresh-right" @click="resetTable">重置</el-button>
</el-col>
</el-row>
</div>
<div class="baseMargin">
<el-button
type="success"
icon="el-icon-plus"
@click="handleDialog(null)"
size="medium"
plain
>新增</el-button>
</div>
<el-table
v-loading="listLoading"
:data="data"
ref="modelTable"
element-loading-text="Loading"
border
fit
size="small"
@sort-change = "handleSortChange"
highlight-current-row
>
<el-table-column
type="selection"
width="55">
</el-table-column>
<el-table-column align="center" label="序号" width="50">
<template slot-scope="scope">
{
{ scope.$index +1}}
</template>
</el-table-column>
<el-table-column label="字典码" prop="code">
<template slot-scope="scope">
{
{ scope.row.code }}
</template>
</el-table-column>
<el-table-column label="字典名称" prop="svalue">
<template slot-scope="scope">
{
{ scope.row.svalue }}
</template>
</el-table-column>
<el-table-column label="备注" prop="remark">
<template slot-scope="scope">
{
{ scope.row.remark }}
</template>
</el-table-column>
<el-table-column label="操作" header-align="center" align="center">
<template slot-scope="scope">
<el-button
icon="el-icon-edit"
@click="handleDialog(scope.row.id)"
size="mini"
plain
type="warning"
>修改</el-button>
<el-button
icon="el-icon-s-tools"
@click="openSetting(scope.row.id,scope.row.code,scope.row.svalue)"
size="mini"
plain
type="primary"
>配置</el-button >
</template>
</el-table-column>
</el-table>
<el-pagination
style="text-align: right"
:current-page="page.page"
:page-sizes="[10, 50, 100, 500]"
:page-size="page.size"
layout="total, sizes, prev, pager, next, jumper"
:total="page.total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
<el-dialog
:title="dialog.title"
:visible.sync="dialog.show"
:close-on-click-modal = "false"
width="50%">
<el-form :model="form" :rules="rules" ref="modelForm" label-width="100px">
<el-form-item v-show="false"label="主键" prop="id">
<el-input v-model="form.id"></el-input>
</el-form-item>
<el-form-item label="字典码" prop="code">
<el-input v-model="form.code" disabled></el-input>
</el-form-item>
<el-form-item label="字典名称" prop="svalue">
<el-input v-model="form.svalue"></el-input>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button size="small" @click="dialog.show = false">取 消</el-button>
<el-button size="small" type="primary" :loading="formLoading" @click="submitDialog">确 定</el-button>
</span>
</el-dialog>
<el-dialog
:title="dialogDetail.title"
:visible.sync="dialogDetail.show"
:close-on-click-modal = "false"
width="40%">
<div style="display: flex;justify-content: flex-end">
<el-button type="primary" @click="addDictItem">新增</el-button>
</div>
<el-divider></el-divider>
<el-row :gutter="20" style="font-size: 16px;font-weight: bolder;text-align: center;">
<el-col :span="12">
字典名称
</el-col>
<el-col :span="4">
状态
</el-col>
<el-col :span="6">操作</el-col>
</el-row>
<el-divider></el-divider>
<el-row :gutter="20" v-for="(item,index) in dialogDetail.data" :key="index" style="height: 60px;display: flex;align-items: center">
<el-col :span="12">
<el-input v-model="item.svalue" style="width: 100%" placeholder="请输入字典值" size="small"></el-input>
</el-col>
<el-col :span="4" style="display: flex;justify-content: center">
<el-switch
v-model="item.isdelete"
active-color="#13ce66"
:active-value="0"
:inactive-value="1"
inactive-color="#ff4949">
</el-switch>
</el-col>
<el-col :span="6">
<div style="display: flex;justify-content: space-around;align-items: center">
<el-button type="primary" plain icon="el-icon-caret-top" circle @click="changeUp(index)"></el-button>
<el-button type="primary" plain icon="el-icon-caret-bottom" circle @click="changeDown(index)"></el-button>
<el-button type="danger" plain icon="el-icon-delete-solid" circle @click="deleteDictItem(index)"></el-button>
</div>
</el-col>
</el-row>
<span slot="footer" class="dialog-footer">
<el-button size="small" @click="dialogDetail.show = false">取 消</el-button>
<el-button size="small" type="primary" :loading="formLoading" @click="submitDetailDialog">确 定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { getPage,find,deleteModles,save,saveAll,dictDetail } from '@/api/dictapi'
export default {
data() {
return {
data: [],
listLoading: true,
formLoading: false,
page: {
size: 10,
page: 1,
total: 0,
column:'',
order:'',
keywords:'',
},
dialog: {
show: false,
title: '查看',
},
dialogDetail:{
title:'',
pid:'',
show:false,
code:'',
data:[]
},
form:{
id: '',
pid: 0,
code: '',
svalue: '',
remark: '',
isdelete: 0,
},
roleList:[],
rules: {
code: [{ required: true, message: '请输入字典码', trigger: 'blur' }],
skey: [{ required: true, message: '请输入字典值', trigger: 'blur' }],
svalue: [{ required: true, message: '请输入字典名称', trigger: 'blur' }],
}
}
},
created() {
this.initPage();
},
methods: {
changeUp(index){
if(index==0){
this.$message('已经到最顶部了');
return;
}
let temp = this.dialogDetail.data[index-1];
this.$set(this.dialogDetail.data, index-1, this.dialogDetail.data[index])
this.$set(this.dialogDetail.data, index, temp)
},
changeDown(index){
if(index==(this.dialogDetail.data.length-1)){
this.$message('已经到最底部了');
return;
}
let temp = this.dialogDetail.data[index+1];
this.$set(this.dialogDetail.data, index+1, this.dialogDetail.data[index])
this.$set(this.dialogDetail.data, index, temp)
},
submitDetailDialog(){
for(let i=0;i<this.dialogDetail.data.length;i++){
if(this.dialogDetail.data[i].svalue==''){
this.$message({
type: 'success',
message: '第'+(i+1)+'条数据不能为空!'
});
return;
}
this.dialogDetail.data[i].skey=(i+1);
this.dialogDetail.data[i].code= this.dialogDetail.code;
this.dialogDetail.data[i].pid= this.dialogDetail.pid;
}
saveAll(this.dialogDetail.data).then(response => {
this.$message({
type: 'success',
message: '操作成功!'
});
this.dialogDetail.show = false;
this.initPage();
});
},
addDictItem(){
this.dialogDetail.data.push(
{'svalue':'','isdelete':0}
)
},
deleteDictItem(index){
this.dialogDetail.data.splice(index,1);
},
openSetting(id,code,name){
this.dialogDetail.title = name+'-字典配置';
this.dialogDetail.show = true;
this.dialogDetail.code = code;
this.dialogDetail.pid = id;
dictDetail({'pid':this.dialogDetail.pid }).then(response => {
this.dialogDetail.data = response;
})
},
handleDialog(id){
if(id){
find(id).then(response => {
delete response.createtime;
this.form = response;
this.dialog.title = '修改';
});
}else{
this.dialog.show = true;
this.dialog.title = '新增';
}
this.dialog.show = true;
this.resetForm();
},
submitDialog(){
this.formLoading = true;
this.$refs.modelForm.validate((valid) => {
if (valid) {
save(this.form).then(response => {
this.$message({
type: 'success',
message: '操作成功!'
});
this.formLoading = false;
this.dialog.show = false;
this.initPage();
});
} else {
this.formLoading = false;
return false;
}
});
},
deleteAll(){
let currentSelect = this.$refs.modelTable.selection;
if(currentSelect.length>0){
this.$confirm('此操作将永久删除这'+currentSelect.length+'条数据, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
deleteModles(this.$getIds(currentSelect)).then(response => {
this.$message({
type: 'success',
message: '删除成功!'
});
this.initPage();
});
})
}else{
this.$message({
message: '请最少选择一条数据~',
type: 'warning'
});
}
},
//查询
resetTable(){
this.page.keywords = ''
this.initPage();
},
resetForm(){
this.$nextTick(()=>{
this.$refs.modelForm.resetFields()
this.$refs.modelForm.clearValidate()
})
},
handleSortChange(val){
this.page.column = val.prop;
if(val.order!=null){
if(val.order=='descending'){
this.page.order = 'desc'
}else{
this.page.order = 'asc'
}
}else{
this.page.column = '';
this.page.order = '';
}
this.initPage();
},
handleSizeChange(val) {
this.page.size = val;
this.initPage()
},
handleCurrentChange(val) {
this.page.page = val;
this.initPage()
},
initPage() {
this.listLoading = true;
getPage(this.$copyPage(this.page)).then(response => {
this.page.total = response.totalElements;
this.data = response.content;
this.listLoading = false
});
}
}
}
</script>
4. 页面效果
这样一个字典的增删改查就完成了
5. vue前端中的处理
5.1 完善获取字典的工具类
geDict.js最终版如下:
import dictList from '@/utils/dict'
/**获取静态字典值 **/
export function getDict(data, element, dictName) {
const col = element.property
const dictArr = dictList[dictName]
if (!dictArr) return
for (let item of dictArr) {
if (item.id === data[col]) {
return item.name
}
}
}
/**获取静态字典列表 **/
export function getDictList (dictName) {
return dictList[dictName]
}
/**获取静态字典值 **/
export function getDictDb(val,dictArr){
if(val>=0 && dictArr && dictArr.length>0){
let temp = dictArr.filter(item => item.skey == val)
if(temp.length==1){
return temp[0].svalue
}
}
}
5.2 vue挂载方法
找到main.js添加如下代码
import {
getDict,getDictList,getDictDb} from '@/utils/getDict'
Vue.prototype.$getDict=getDict
Vue.prototype.$getDictList=getDictList
Vue.prototype.$getDictDb=getDictDb
5.3 页面使用
首先引入api,然后在method中调用后端api接口获取字典列表。
<script>
import { getDictCode } from '@/api/dict'
export default {
data() {
return {
list:{
goods_type:[],
goods_factory:[],
goods_spec:[],
product_type:[]
}
}
},
created() {
//初始化获取所有字典
this.initList()
},
methods: {
initList(){
getDictCode('goods_type,goods_factory,goods_spec,product_type').then(response => {
this.list = response
});
}
}
5.4 表格中使用
<el-table>
<el-table-column
type="selection"
width="55">
</el-table-column>
<el-table-column label="商品类型" prop="typeid">
<template slot-scope="scope">
{
{$getDictDb(scope.row.typeid,list.goods_type)}}
</template>
</el-table-column>
<el-table-column label="生产厂家" prop="factory">
<template slot-scope="scope">
{
{$getDictDb(scope.row.factory,list.goods_factory)}}
</template>
</el-table-column>
<el-table-column label="产品类型" prop="producttype">
<template slot-scope="scope">
{
{$getDictDb(scope.row.producttype,list.product_type)}}
</template>
</el-table-column>
<el-table-column label="商品规格" prop="specs">
<template slot-scope="scope">
{
{$getDictDb(scope.row.specs,list.goods_spec)}}
</template>
</el-table-column>
</el-table>
5.5 表单中使用
<el-form :model="form" :rules="rules" ref="modelForm" label-width="100px">
<el-form-item v-show="false" label="商品ID" prop="id">
<el-input v-model="form.id"></el-input>
</el-form-item>
<el-form-item label="商品类型" prop="typeid">
<el-select v-model="form.typeid" style="width: 100%" placeholder="请选择所属产品">
<el-option
v-for="item in list.goods_type"
:key="item.skey"
:label="item.svalue"
:value="item.skey"
/>
</el-select>
<el-form-item label="生产厂家" prop="factory">
<el-select v-model="form.factory" style="width: 100%" placeholder="请选择生产厂家">
<el-option
v-for="item in list.goods_factory"
:key="item.skey"
:label="item.svalue"
:value="item.skey"
/>
</el-select>
</el-form-item>
<el-form-item label="产品类型" prop="producttype">
<el-select v-model="form.producttype" style="width: 100%" placeholder="请选择产品类型">
<el-option
v-for="item in list.product_type"
:key="item.skey"
:label="item.svalue"
:value="item.skey"
/>
</el-select>
</el-form-item>
<el-form-item label="商品规格" prop="specs">
<el-select v-model="form.specs" style="width: 100%" placeholder="请选择商品规格">
<el-option
v-for="item in list.goods_spec"
:key="item.skey"
:label="item.svalue"
:value="item.skey"
/>
</el-select>
</el-form-item>
</el-form>