03-数据字典模块开发

一、数据字典介绍

1、介绍

何为数据字典?
数据字典就是管理系统常用的分类数据或者一些固定数据,例如:省市区三级联动数据、民族数据、行业数据、学历数据等,由于该系统大量使用这种数据,所以我们要做一个数据管理方便管理系统数据,一般系统基本都会做数据管理。

一句话就是数据以官方为准,我们需要按照官方发布的数据进行维护数据,其他时间数据是固定的

2、预期效果

在这里插入图片描述

3、数据库字典字段设计

在这里插入图片描述

parent_id:
上级id,通过id与parent_id构建上下级关系,例如:我们要获取所有行业数据,那么只需要查询parent_id=20000的数据
name:名称,
例如:填写用户信息,我们要select标签选择民族,“汉族”就是数据字典的名称
value:值,
例如:填写用户信息,我们要select标签选择民族,“1”(汉族的标识)就是数据字典的值
dict_code:
编码,编码是我们自定义的,全局唯一,例如:我们要获取行业数据,我们可以通过parent_id获取,但是parent_id是不确定的,所以我们可以根据编码来获取行业数据

4、数据接口分析

数据字典是树形展示,由于数据比较多,我们使用树形数据与el-ui的懒加载方式,获取数据和展示数据,其他就是数据的增删改查,只有当我们点击显示下一级数据时,我们才会加载下一级的数据。

二、数据字典服务端开发

1、搭建service_cmn模块来完成数据字典的操作

搭建过程还是和service_hosp一样的,创建一个普通的maven工程,选择父工程为service模块,修改我们pom配置文件,创建我们的启动类,创建对应的配置文件,添加对应的配置信息

2、数据字典列表的获取

(1)分析

因为我们的前端使用el-ui 搭建字典列表的树形结构,我们不需要做过多的开发,对前端使用的组件需求数据分析可以知道,我们需要根据前端传递过来的id值来获取对应的此id下的子节点,而且还需一个特别的属性hasChildren=?

(2)model对象创建

注意:hasChildren属性我们是可以存在数据库的,只是前端树形组件需要这个数据,我们加上注解 @TableField(exist = false)表示数据库可以不存在此字段

@Data
@ApiModel(description = "数据字典")
@TableName("dict")
public class Dict {

    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "id")
    private Long id;

    @ApiModelProperty(value = "创建时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField("create_time")
    private Date createTime;

    @ApiModelProperty(value = "更新时间")
    @TableField("update_time")
    private Date updateTime;

    @ApiModelProperty(value = "逻辑删除(1:已删除,0:未删除)")
    @TableLogic
    @TableField("is_deleted")
    private Integer isDeleted;

    @ApiModelProperty(value = "其他参数")
    @TableField(exist = false)
    private Map<String, Object> param = new HashMap<>();

    @ApiModelProperty(value = "上级id")
    @TableField("parent_id")
    private Long parentId;

    @ApiModelProperty(value = "名称")
    @TableField("name")
    private String name;

    @ApiModelProperty(value = "值")
    @TableField("value")
    private String value;

    @ApiModelProperty(value = "编码")
    @TableField("dict_code")
    private String dictCode;

    @ApiModelProperty(value = "是否包含子节点")
    @TableField(exist = false)
    private boolean hasChildren;
//   exist = false 意思是数据库可以不存在此字段

}

(3)mapper接口

创建mapper接口,继承BaseMapper

@Repository
public interface DictMapper extends BaseMapper<Dict> {
}

(4)service层

创建接口,继承mp提供的IService接口

public interface DictService extends IService<Dict> {
    List<Dict> findChildData(Long id);

    void exportDictData(HttpServletResponse response);

    void importDictData(MultipartFile file);
}

创建实现service接口实现类继承mp的ServiceImpl

@Service
public class DictServiceImpl extends ServiceImpl<DictMapper, Dict> implements DictService {
 /**
     * 添加数据到缓存中
     *
     * @param id
     * @return
     */
    @Override
    @Cacheable(value = "dict", keyGenerator = "keyGenerator")
    public List<Dict> findChildData(Long id) {
        // 构造条件
        QueryWrapper<Dict> wrapper = new QueryWrapper<>();
        wrapper.eq("parent_id", id);
        // 查询数据
        List<Dict> dicts = baseMapper.selectList(wrapper);
        // 修改是否存在子节点数据值
        for (Dict dict : dicts) {
            dict.setHasChildren(isChildren(dict.getId()));
        }
        return dicts;
    }
}

(5)controller

@Api(tags = "数据字典模块")
@RestController
@RequestMapping("/admin/cmn/dict")
@CrossOrigin
public class DictController {
    @Autowired
    private DictService dictService;
    /**
     * 根据数据id查询子数据列表
     *
     * @param id 数据Id
     * @return
     */
    @ApiOperation("根据数据id查询子数据列表")
    @GetMapping("/findChildData/{id}")
    public Result findChildData(@PathVariable("id") Long id) {
        List<Dict> dicts = dictService.findChildData(id);
        return Result.ok(dicts);
    }
}

3、字典的导出工程实现

我们使用Alibaba的EasyExcel工具进行完成数据的导入和导出。

(1)导入依赖

因为只有service_cmn 使用此依赖,所以我们只需要把依赖导入到service_cmn 模块下

<dependencies>
        <dependency>
            <!--alibaba excel操作-->
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.1.1</version>
        </dependency>
    </dependencies>

(2)按照EasyExcel规范创建model类

为啥不直接使用dict实体类,反而又创建一个实例类呢,本着,不同的业务使用不同的实体类,解耦合,后期修改不影响其他实体类的效果

@Data
public class DictEeVo {

	@ExcelProperty(value = "id" ,index = 0)
	private Long id;

	@ExcelProperty(value = "上级id" ,index = 1)
	private Long parentId;

	@ExcelProperty(value = "名称" ,index = 2)
	private String name;

	@ExcelProperty(value = "值" ,index = 3)
	private String value;

	@ExcelProperty(value = "编码" ,index = 4)
	private String dictCode;

}

(3)导出字典数据接口开发

1、controller

因为导出是一个下载动作,我们需要报我们的数据数据导出到excel中,所以我们response对象来完成下载操作。

  /**
     * 实现下载功能,
     * 我们需要HttpServletResponse来完成下载功能
     * 使用流的形式返回给浏览器
     *
     * @param response
     */
    @ApiOperation("导出数据字典")
    @GetMapping("/exportData")
    public void exportData(HttpServletResponse response) {
        dictService.exportDictData(response);
    }
2、service

其实就是一个下载操作

  1. 完成下载信息的设置
  2. 读取数据
  3. 数据库数据转换为导出字典数据
  4. 使用EasyExcel完成写文件的操作
  5. 把文件写入到下载流中。
/**
     * 实现字典数据导出功能
     *
     * @param response
     */
    @Override
    public void exportDictData(HttpServletResponse response) {
        try {
            // 1、设置下载的信息
            // 1.1、再回传前,通过响应头告诉客户端返回的数据类型excel类型
            response.setContentType("application/vnd.ms-excel");
            // 1.2、设置下载的文件名字,且对中文进行utf-8编码
            String fileName = URLEncoder.encode("数据字典.xlsx", "UTF-8");
            // 1.3、设置响应头的信息,attachment(附件下载); fileName=(文件名)
            response.setHeader("Content-Disposition", "attachment; fileName=" + fileName);
            // 2、准备响应的数据流,使用response.getOutputStream(流数据);把流输出浏览器

            // 因为我们需要读取数据,封装数据,所以我们需要先对数据进行处理
            // 获取数据库字典信息
            List<Dict> dicts = baseMapper.selectList(null);
            // 创建写文件字典对象,vo上标注注解@ExcelProperty,专门用来导入和导出excel文件
            List<DictEeVo> dictEeVos = new ArrayList<>();
            // 把我们的dict对象,转换为dictEevo对象
            for (Dict dict : dicts) {
                // 创建需要转换的vo对象
                DictEeVo dictEeVo = new DictEeVo();
                // 调用BeanUtils.copyProperties转换,求实就是获取属性,设置属性的操作。
                BeanUtils.copyProperties(dict, dictEeVo);
                // 添加到集合中
                dictEeVos.add(dictEeVo);
            }

            // 3、把EasyExcel写文件写入到response.getOutputStream()中,直接响应给客户端
            EasyExcel.write(response.getOutputStream(), DictEeVo.class)
                    .sheet("dict").doWrite(dictEeVos);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

4、导入字典数据接口开发

其实就是上传Excel文件,使用EasyExcel读取文件(一行一行的读取数据),然后添加到数据库。但是想要使用EasyExcel读取Excel文件,需要我们使用按照EasyExcel规范实现监听器,来完成一行行的读取数据

(1)实现监听器(具体对读取数据的操作)

public class DictListener extends AnalysisEventListener<DictEeVo> {
    private DictMapper dictMapper;

    // 调用Mapper测试插入数据
    public DictListener(DictMapper dictMapper) {
        this.dictMapper = dictMapper;
    }

    /**
     * 监听读取excel文件
     * 一行一行的读取
     *
     * @param data
     * @param context
     */
    @Override
    public void invoke(DictEeVo data, AnalysisContext context) {
        Dict dict = new Dict();
        BeanUtils.copyProperties(data, dict);
        dictMapper.insert(dict);
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {

    }
}

(2)controller

/**
     * 导入字典数据
     *
     * @param file
     * @return
     */
    @PostMapping("/importData")
    public Result importData(@RequestPart MultipartFile file) {
        dictService.importDictData(file);
        return Result.ok();
    }

(3)service

监听器自动监听我们的读数据的操作。

/**
     * 导入字典数据
     * allEntries=true 刷新我们的缓存
     *
     * @param file
     */
    @Override
    @CacheEvict(value = "dict", allEntries = true)
    public void importDictData(MultipartFile file) {
        // 直接读取我们上长传文件的流,对读取的数据进行处理在监听器里面实现
        try {
            EasyExcel.read(file.getInputStream(), DictEeVo.class, new DictListener(baseMapper))
                    .sheet()
                    .doRead();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

三、前端模块开发

1、字典数据列表展示

(1)添加路由

因为这个路由只有一个子节点,所以我们需要显示父节点数据的话,需要我们指定一个属性alwaysShow: true,让他一直显示

  {
    path: '/cmn',
    component: Layout,
    redirect: '/cmn/list',
    name: '数据字典',
    meta: {title: '数据管理', icon: 'example'},
    // 表示总是显示这个路由信息,即使只有一个子标签
    alwaysShow: true,
    children: [
      {
        path: 'list',
        name: '数据字典列表',
        component: () => import('@/views/dict/list'),
        meta: {title: '数据字典', icon: 'table'}
      }
    ]
  },

(2)定义api

使用es6的模板字符串参数拼接

dictList(id) {
    return request({
      // 后端服务接口
      url: `/admin/cmn/dict/findChildData/${id}`,
      method: 'get'
    })
  }

(3)使用el-ui的树形结构组件

<template>
    <div class="app-container">
        <el-table
        :data="list"
        style="width: 100%"
        row-key="id"
        border
        lazy
        :load="getChildrens"
        :tree-props="{children: 'children', hasChildren: 'hasChildren'}">
            <el-table-column label="名称" width="230" align="left">
            <template slot-scope="scope">
            <span>{{ scope.row.name }}</span>
            </template>
            </el-table-column>

            <el-table-column label="编码" width="220">
            <template slot-scope="{row}">
                    {{ row.dictCode }}
            </template>
            </el-table-column>
            <el-table-column label="" width="230" align="left">
            <template slot-scope="scope">
            <span>{{ scope.row.value }}</span>
            </template>
            </el-table-column>
            <el-table-column label="创建时间" align="center">
            <template slot-scope="scope">
            <span>{{ scope.row.createTime }}</span>
            </template>
            </el-table-column>
        </el-table>
    </div>
</template>

(4)定义页面vue脚本

通过对el-ui树形组件的分析我们可以知道我们需要加载哪些数据,以及创建哪些方法,el-ui都是规定好的数据,以及绑定的事件函数等等。


<script>
import dict from '@/api/dict'

export default {
  // 定义变量和参数
  data() {
    return {
      list: [], // 数据字典列表数组
      dialogImportVisible: false // 控制上传文件弹出层是否弹出
    }
  },
  created() {
    // 获取根节点数据
    this.getDictList(1)
  },
  methods: {
    // 导入数据
    importData() {
      // 打开弹出层
      this.dialogImportVisible = true
    },
    // 上传成功调用的函数
    onUploadSuccess() {
      // 关闭弹出层
      this.dialogImportVisible = false
      // 刷新数据(即重新获取数据)
      this.getDictList(1)
    },
    // 获取字典列表结合
    getDictList(id) {
      dict.dictList(id)
        .then(response => {
          this.list = response.data
        })
    },
    // el-ui自动调用获取字节点数据
    // 参数啥都都是el-ui自动封装好的,我们不需要过多操作
    getChildrens(tree, treeNode, resolve) {
      // 获取该节点下的子节点数据
      dict.dictList(tree.id).then(response => {
        resolve(response.data)
      })
    }
  }
}
</script>

2、字典数据导出

(1)列表页面添加导出按钮使用a标签

直接使用a标签,直接直接访问我们后端接口获取下载数据。

<a href="http://localhost:8202/admin/cmn/dict/exportData" target="_blank">
          <el-button type="text"><i class="fa fa-plus"/> 导出</el-button>
        </a>

3、字典数据导入操作

(1)页面添加导入组件

<el-button type="text"@click="importData"><i class="fa fa-plus"/> 导入</el-button>

(2)添加弹出层友好提示(包括上传文件操作)

就是bootstart的拟态框效果,其中自动封装上传文件的操作。

<!-- 上传文件弹出层 el-ui 提供的
    :visible.sync="dialogImportVisible" 双向绑定dialogImportVisible=flase不展示,true展示
    -->
    <el-dialog title="导入" :visible.sync="dialogImportVisible" width="480px">
      <el-form label-position="right" label-width="170px">
        <el-form-item label="文件">
          <!-- 上传的核心代码 multiple=false不支持多文件上传,反之,on-success上传成功调用的函数-->
          <el-upload
            :multiple="false"
            :on-success="onUploadSuccess"
            :action="'http://localhost:8202/admin/cmn/dict/importData'"
            class="upload-demo">
            <!--提示信息-->
            <el-button size="small" type="primary">点击上传</el-button>
            <div slot="tip" class="el-upload__tip">只能上传xls文件,且不超过500kb</div>
          </el-upload>
        </el-form-item>

      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogImportVisible = false">
          取消
        </el-button>
      </div>
    </el-dialog>

创建弹出层需要的数据,以及绑定的函数

dialogImportVisible: false // 控制上传文件弹出层是否弹出

 // 导入数据
    importData() {
      // 打开弹出层
      this.dialogImportVisible = true
    },
    // 上传成功调用的函数
    onUploadSuccess() {
      // 关闭弹出层
      this.dialogImportVisible = false
      // 刷新数据(即重新获取数据)
      this.getDictList(1)
    },
  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值