UniMaxV5 系列开发培训 - 单表开发

背景

本期视频讲解的是怎么去开发单表的流程。以学生表为例,其中学生表字段为姓名(name)、性别 (sex)、年龄 (age)、班级 (className)、电话 (phone)。性别是一个选择框(0,1 分别代表男和女),其余全是文本框;在表格里面多了一个分数(score)属性,它是学生表里面没有的字段,用来去突出pass:true的作用。在表格 grid 数据的展示是和实体息息相关,通过实体字段一 一映射,如果实体里没有这个字段是会报错,所以需要使用 pass 去忽略次字段。在 form 表单里面对姓名这个字段做了个远程校验姓名不能为空的判断。只有满足校验结果才能去请求后台。

开发流程

1、首先在目录下创建自己的 vue 文件

image.png

2、在菜单定义里面去添加次页面,url 路径就是你创建文件的路径。

注意:路径从一级菜单开始,后面的“ .vue”,不需要,斜杆是“/”。

例子:我的 student.vue 文件在 系统管理 / 菜单设置下。所以我的路径就是 umasysmanage/menuSettings/student

3、角色管理添加权限
4、开始写 student.vue 页面

注意:你在里面添加的变量必须要在 data()里面的 return 里面定义,否则报错。添加的事件必须在 methods:{} 里面定义(样例如下)

样式模板

<template> <eh-layout> <div slot="center"> <div slot="center"> </div> </div> </eh-layout> </template> <script> import qs from "qs"; export default { name: "student", data() { return { }; }, created() { }, mounted() {}, methods: { // 方法名称 展示样例, 在<template>里面定义的方法在methods里面必须有,但在methods里面的有的不一定要在<template>里面写 getMenthod(){}, }, }; </script> <style lang="scss" scoped></style>

注意 : name 属性的值必须和你的页面名字一样。

5、添加 eh-tabs 插件(用法如 EhTabs 平台多标签页组件)

必须

  • :tabs.sync="tabs" 是添加要显示的标签页,

  • v-model=“tabsName” 是默认选择的标签页

  • @after-close=“close” 标签页关闭事件

注意:

  • ref 在大多数情况你是必须加的,除非你不需要去自动关闭标签页。如果你一开始标签页只有学生信息这个标签页,你可以这样定义 tabs

tabs: [{ name: "student-select", title: "学生信息" }],

  • tabs 里面对象的 name 的值 必须和你slot的值对应

本次示例是显示三个标签页,默认选中学生信息标签页

<template> <eh-layout> <div slot="center"> <eh-tabs :tabs.sync="tabs" ref="myTabs" v-model="tabsName" @after-close="close"> <template slot="student-select"> </template> </eh-tabs> </div> </eh-layout> </template> <script> import qs from "qs"; export default { name: "student", data() { return { tabsName : "student-select", tabs: [ { name: "student-select", title: "学生信息" }, { name: "student-cess1", title: "测试1" }, { name: "student-cess2", title: "测试1" } ], otherParam : {}, }; }, }; </script>

图形展示

image.png

添加 eh-grid 表格(用法如 Eh-Grid 表格组件)

有五个必填属性

  • id :关联了查询模板

  • ref: 用来拿取表格数据

  • grid :表格展示相关的属性配置

  • dataSource : 表格数据获取相关的属性配置

  • options:表格的其他的一些属性配置

注意:

  • 如果表格想添加按钮你可以使用去添加。在这个里面去添加自己想要的按钮。

  • 对于添加的按钮 class=“changeButton” 是必须添加的,对于除了 eh-grid 里面的按钮 你还需要在外面的 div 上添加class=“item-button”

  • 对于 刷新、导出、查询按钮是不需要自己定义的,插件已经自定义好。但对于刷新的事件你自己还是需要写的,对于一些特别的比如加上了 **“row-key”: “id”属性的你还需要去使用clearSelection()** 方法,如果想要去隐藏这三个按钮,可以去看下面文档。

  • 对于 导出 有抛出一个方法,你可以通过ref直接去调用它

相关示例如下:

<template> <eh-layout> <div slot="center"> <div slot="center"> <eh-tabs :tabs.sync="tabs" ref="myTabs" v-model="tabsName" @after-close="close"> <template slot="student-select"> <eh-layout> <div slot="center"> <eh-grid :grid="gridOptions.grid" id="student" ref="student" :dataSource="gridOptions.dataSource" :options="gridOptions.options" @selection-change="handleSelectionChange" @cell-click="cellclick" > <template slot="toolbar"> <el-button class="changeButton" @click="refresh">自己刷新</el-button> <el-button class="changeButton" @click="addOrEdit('add')">新增</el-button> <el-button class="changeButton" @click="addOrEdit('edit')">修改</el-button> <el-button class="changeButton" @click="del(rwoData)">删除</el-button> <!-- <el-button class="changeButton" @click="exportExcel">导出</el-button> --> </template> </eh-grid> </div> </eh-layout> </template> <template slot="student-cess1"> </template> <template slot="student-cess2"> </template> </eh-tabs> </div> </div> </eh-layout> </template> <script> import qs from "qs"; ​ const gridOptions = { dataSource: { bean: "studentController", method: "select", entity: "com.epichust.entity.Student", filters: [ { field: "name", compare: "eq,cn", data: "", type: "text", }, { field: "sex", compare: "eq,ne", data: "", type: "select", options : { keyName : "label", valueName : "value", selectOptions :[ {label : '男', value : "0"}, {label : '女', value : "1"}, ] } }, ], }, grid: { // sortButton: true, title: "学生信息", description: "这里可以填写一些学生信息的描述", stripe: true, border: true, selectMax: 1,// 单选 export: true,// 导出 // refreshBtn: false, "row-key": "id", // // sortButton: true,//手动上下排序按钮 manualSort:true,//拖拽表格行排序 columns: [ { prop: "id", label: "id", visible: false, exportHideen : true, // 导出隐藏 width : "120px", }, { label: "姓名", prop: "name", // width : "120px", sortable: true }, { label: "性别", prop: "sex", // width : "120px", formatter : (row,cel) => { return row.sex == 0 ? '男' : "女" } }, { prop: "age", label: "年龄", width: "200px", sortable : true }, { label: "班级", prop: "className", width : "120px", }, { prop: "phone", label: "电话", width: "200px", }, { prop: "score", label: "分数", width: "200px", pass : true }, ], defaultSort: { prop: "name", order: "ascending" }, pagination: { pageSize: 10, }, }, options: { // 是否显示编辑过滤 // showFilterTemplate: false, // usetotal: false,//带总量查询 extraParamFields: { // consition: '8a5aya54a5t4t43453thf4' }, }, }; export default { name: "student", components: { studentAddOrEdit }, data() { return { tabsName : "student-select", gridOptions: gridOptions, rwoData: [], tabs: [ { name: "student-select", title: "学生信息" }, { name: "student-cess1", title: "测试1" }, { name: "student-cess2", title: "测试1" } ], otherParam : {}, }; }, }; </script>

相关图形展示

image.png

6、添加事件和表单文件的引入

注意:

  • 事件与事件之间以 **“,”分割,不然报错。而且必须写在methods里面,对于有特定需求的方法,例如想页面一开始就加载的方法,你可以写在mounted** 里面

  • 对于想要获取表格的所有数据,这里提供了两种方法:

    • 通过ref 去获取:this.$refs.ref 的名字.data

    • 通过dataSource获取: this.gridOptions.dataSource

      提示:建议通过第一种方式获取,第二种方式获取数据是有限制的,它的 dataSource 必须是”数组“形式的,而且它不是双向绑定的,如果对表格的值有修改,它将不会是最新数据,所以特别注意,一定要通过 ref 去拿值。

新增和修改:

本次案例使用的是插件的形式,也可以直接在页面内去定义二个新增或修改,然后在里面写上 form 表单。

这里主要讲解插件形式的。如果你想让新增或修改可重复利用,你可以去把他们写在一个页面里面,然后在主页面里面去引入。

使用方式:

    • 1、引入表单的 vue 文件。案列的表单文件在同级别所以是 import studentAddOrEdit[1] from “./studentAddOrEdit”;

    • 2、组件注册。**components: {studentAddOrEdit}这里的 studentAddOrEdit 和你上面引入“[1]”** 的名字是一样的.

    • 3、使用

参数的传递:

如果是使用的插件的形式,就将面临一个问题,我改如何去传递我想要参数到表单的页面呢。你可以去 data 里面去声明一个变量(otherParam),然后 ====,最后在你要去打开这个页面的位置去给这个变量赋值,推荐使用对象形式。==赋值的时候要注意这个值是不是你想要的值,是否多次打开会混淆,所以希望你先置空==. 关于为什么这里要给 ==param==,请看 == 表单文件 == 这个章节。

特别说明:为什么下方的新增 / 修改写了 slot 这个属性,因为要把新增修改放入到 tabs 标签页中,如果你不写,到时候页面会加载不出来。如果你只是想简单的引入,不是放在 tabs 下面,你可以不用写 slot。

<template> <eh-layout> <div slot="center"> <div slot="center"> <eh-tabs :tabs.sync="tabs" ref="myTabs" v-model="tabsName" @after-close="close"> <template slot="student-select"> <eh-layout> <div slot="center"> <eh-grid :grid="gridOptions.grid" id="student" ref="student" :dataSource="gridOptions.dataSource" :options="gridOptions.options" @selection-change="handleSelectionChange" @cell-click="cellclick" > <template slot="toolbar"> <el-button class="changeButton" @click="refresh">自己刷新</el-button> <el-button class="changeButton" @click="addOrEdit('add')">新增</el-button> <el-button class="changeButton" @click="addOrEdit('edit')">修改</el-button> <el-button class="changeButton" @click="del(rwoData)">删除</el-button> <!-- <el-button class="changeButton" @click="exportExcel">导出</el-button> --> </template> </eh-grid> </div> </eh-layout> </template> <template slot="student-cess1"> </template> <template slot="student-cess2"> </template> <!-- 新增/修改 --> <studentAddOrEdit slot="student-add" :param="otherParam" @tabmoved="remove"></studentAddOrEdit> <studentAddOrEdit slot="student-edit" :param="otherParam" @tabmoved="remove"></studentAddOrEdit> </eh-tabs> </div> </div> </eh-layout> </template> <script> import qs from "qs"; import { getFormDataByObject } from "@/util/index.js"; import studentAddOrEdit  from "./studentAddOrEdit"; ​ const gridOptions = { dataSource: { bean: "studentController", method: "select", entity: "com.epichust.entity.Student", filters: [ { field: "name", compare: "eq,cn", data: "", type: "text", }, { field: "sex", compare: "eq,ne", data: "", type: "select", options : { keyName : "label", valueName : "value", selectOptions :[ {label : '男', value : "0"}, {label : '女', value : "1"}, ] } }, ], }, grid: { // sortButton: true, title: "学生信息", description: "这里可以填写一些学生信息的描述", stripe: true, border: true, selectMax: 1,// 单选 export: true,// 导出 // refreshBtn: false, "row-key": "id", // // sortButton: true,//手动上下排序按钮 manualSort:true,//拖拽表格行排序 columns: [ { prop: "id", label: "id", visible: false, exportHideen : true, // 导出隐藏 width : "120px", }, { label: "姓名", prop: "name", // width : "120px", sortable: true // 排序 }, { label: "性别", prop: "sex", // width : "120px", formatter : (row,cel) => { return row.sex == 0 ? '男' : "女" } }, { prop: "age", label: "年龄", width: "200px", sortable : true }, { label: "班级", prop: "className", width : "120px", }, { prop: "phone", label: "电话", width: "200px", }, { prop: "score", label: "分数", width: "200px", pass : true }, ], // 默认排序 defaultSort: { prop: "name", order: "ascending" }, pagination: { pageSize: 10, //页面大小 }, }, options: { // 是否显示编辑过滤 // showFilterTemplate: false, // usetotal: false,//带总量查询 extraParamFields: { // consition: '8a5aya54a5t4t43453thf4' //额外参数 }, }, }; export default { name: "student", components: { studentAddOrEdit }, data() { return { tabsName : "student-select", gridOptions: gridOptions, rwoData: [], tabs: [ { name: "student-select", title: "学生信息" }, { name: "student-cess1", title: "测试1" }, { name: "student-cess2", title: "测试1" } ], otherParam : {}, }; }, created() { }, mounted() {}, methods: { //选择某一行数据 handleSelectionChange(val) { console.info(val) this.rwoData = val; //获取表格数据 1 建议使用1这种方式。 console.info(this.$refs.student.data) //获取表格数据 2 //   this.gridOptions.dataSource }, ​ cellclick(row, column, cell, event){ }, // 关闭 tabs标签 close(name){ //   this.refresh() console.info(name) ​ }, remove(name){ if(name == this.tabsName){ this.$refs.myTabs.removeTab(name) //对于有需要添加/修改保存完之后需要添加主页标签的,可以去添加this.tabsName = 'student-select' this.tabsName = 'student-select' } }, //刷新表格 refresh() { //刷新数据 this.$refs.student.bindGrid(); //添加了row-key 在需要加clearSelection()方法 this.$refs.student.clearSelection() }, // 跳转新增修改 addOrEdit(val) { // let data; if (val == "add") { this.otherParam = {} this.tabs.push({ name: "student-add", title: "新增" }); }else{ if(this.rwoData.length != 1){ this.$message({ message: "请选择一行数据进行修改!", type: "error", }); return false; } this.otherParam = this.rwoData[0] this.tabs.push({ name: "student-edit", title: "修改" }); } }, exportExcel(){ this.$refs.student.openExport(); }, }, }; </script> <style lang="scss" scoped></style>
7、表单文件

特别注意:关于表单文件,对于 == 显示 == 的文本框大于 ==6== 个的,请使用页面方式,如果小于等于 6 个,请使用弹框。参照用户管理界面的 == 新增和修改 ==

1、整个表单文件全部都是表单组件组成,里面包含一些属性希望不要忘记加上。

  • model

  • rules 验证规则

  • class=“form-container form-inline” 样式

  • label-position=“top” 标签位置

  • :inline=“true”

  • ref 关系到后面保存验证规则

2、因为是要当作插件使用的,所以需要接受组件传来的值,所以定义了props: [“param”], 相当与 data 里面的定义的一样,是本页面内全局并且页面加载的时候就存在。

注意:

  • 按钮上 div 的 class=“item-button” 不能省略,请都带上,否则按钮与其它页面会不起,导致有落差感。

  • 关于验证规则: 你可以使用 elementUI 本来就有的规则,也可以自己去定义。这里使用的是自定义的规则去请求了后台

  • 而且特别注意一点的是使用了验证规则,你的 prop 属性的值不能省略,不然验证规则不会生效,也不能写错,和你的属性对应。

  • 关于重置,虽然 elementUI 提供了重置事件,但是本人建议不要轻易使用,自己去写重置,可以像我下面那样写。它的重置非常不方便,而且会出很严重问题。慎用!!!

  • 关于保存按钮,或者你不想让一个按钮被连续点多次,请在 上加上指令 v-throttle,默认一秒钟

样例代码

<template> <el-form :model="formData" :rules="formDataRules" ref="formDataForm" class="form-container form-inline" label-position="top" :inline="true" > <div class="item-button"> <el-button class="changeButton" v-throttle @click="save('formDataForm')">保存</el-button> <el-button class="changeButton" @click="resetForm('formDataForm')">重置</el-button> </div><el-form-item prop="mode" label="mode" hidden> <el-input v-model="formData.mode"></el-input> </el-form-item> <el-form-item prop="id" label="id" hidden> <el-input v-model="formData.id"></el-input> </el-form-item> <el-form-item prop="createId" label="创建人" hidden> <el-input v-model="formData.createId"></el-input> </el-form-item> <el-form-item prop="createDate" label="创建时间" hidden> <el-input v-model="formData.createDate"></el-input> </el-form-item> <el-form-item prop="isActive" label="是否激活" hidden> <el-input v-model="formData.isActive"></el-input> </el-form-item> <el-form-item prop="isDelete" label="是否删除" hidden> <el-input v-model="formData.isDelete"></el-input> </el-form-item> <el-form-item prop="name" label="姓名" class="is-required"> <el-input v-model="formData.name"></el-input> </el-form-item> <el-form-item prop="sex" label="性别" > <!-- <el-input v-model="formData.sex"></el-input> --> <el-select v-model="formData.sex" placeholder="--请选择--"> <el-option v-for="item in sexList" :key="item.key" :label="item.value" :value="item.key" ></el-option> </el-select> </el-form-item> <el-form-item prop="age" label="年龄" > <el-input v-model="formData.age"></el-input> </el-form-item> <el-form-item prop="className" label="班级" > <el-input v-model="formData.className"></el-input> </el-form-item> <el-form-item prop="phone" label="电话"> <el-input  v-model="formData.phone"></el-input> </el-form-item> </el-form> </template> <script> import qs from "qs"; import { formatTree } from "@/util/index.js"; ​ export default { name: "studentAddOrEdit", props: ["param"], data() { let self = this; let checkCode = (rule, value, callback) => { { let resultPromise = self.getCodeResult(value); resultPromise.then((res) => { if (res.type === "error") { callback(new Error(res.data)); } else { callback(); } }); } }; return { formDefaultData: { id: "", createId: "", createDate: "", isActive: "0", isDelete: "0", name: "", sex : "", age : "", className : "", phone: "", }, //form 表单 formData: { id: "", createId: "", createDate: "", isActive: "0", isDelete: "0", name: "", sex : "", age : "", className : "", phone: "", }, formDataRules: { name: [{ validator: checkCode, trigger: "blur" }], }, sexList: [{key : '0', value : "男"},{key : '1', value : "女"}], mode : "" }; }, created() { // console.info("id", this.param); }, mounted() { let self = this; //赋值 let formData = self.formData; for (var key in formData) { formData[key] = this.param[key]; } if(this.param.id){ this.mode = 'edit' }else{ this.mode = 'add' } //重置 this.formDefaultData = Object.assign({}, this.formData); this.resetForm('formDataForm') }, methods: { //保存 save(formName) { let self = this; self.$refs[formName].validate((valid) => { if (valid) { let formData = Object.assign({}, self.formData); ​ //去除空的值 for (var key in formData) { if (formData[key] === "" || formData[key] == null) { delete formData[key]; } else { formData[key] = formData[key].toString(); } } ​ let param = { form: JSON.stringify(formData), }; ​ //请求 self.$http.post("studentController!saveform.m", qs.stringify(param)).then((res) => { if (res.code != 500) { this.$message({ message: res.data, type: res.type, }); if (res.type == "success") { //关闭 tabs self.$emit("tabmoved", "student-" + self.mode); } } }); } else { this.$message({ message: "请完善数据", type: "error", }); return false; } }); }, //重置 resetForm(formName) { let self = this; this.$nextTick(() => { self.formData = Object.assign({}, this.formDefaultData); self.$refs[formName].clearValidate(); }); }, // code 校验 async getCodeResult(value) { let self = this; ​ let data = qs.stringify({ id: this.formData.id, name: value, }); let res = await self.$http.post("studentController!getCode.m", data); return res; }, }, }; </script>

样例展示

image.png

后台返回数据格式

1、统一以Map 集合形式返回。

2、对于提示消息,返回两个属性,data(提示的信息) 和 type(类型:消息的类型)

3、对于返回的实体并且复杂的,请使用 **FastJsonUtil.format(object)** 转一下。

提示:前端对后台的消息或者异常提示做过处理,例如 this.returnData.toSuccess(“保存成功!”);,前端可以直接获取到,不需要自己去以 Map 返回。

姓名的一个不为空验证,返回示例

public void getCode() { Map<String,Object> map = new HashMap<>(); String id = this.getPageData().getParams().get("id"); String name = this.getPageData().getParams().get("name"); if((name!= "" && name != null)) { map.put("data", "可以使用"); map.put("type", "success"); }else { map.put("data", "名字不能为空"); map.put("type", "error"); } this.returnData.toParam(map); }

平台组件

EhTabs 平台多标签页组件

属性
  • v-model 当前 tab 标签的 name。通过赋值可切换标签显示页。
事件
  • after-close (Function) 在每次关闭标签页后 调用这个传入方法。默认参数 tabName:当前关闭的标签名。
方法
  • removeTab 调用此方法会直接移除一个标签和它的内容,并修改传入的 tabs 属性值。

Eh-Grid 表格组件

注意 :id 必须

属性
dataSource (Object | Array 必填)

表格数据获取相关的属性配置

注意:如果需要查询模板,就需要添加 filters 属性,不然到时候查询模板查询字段会是空

dataSource: { // formdata参数:控制器名 (string 必填) bean: "mbbBdMrlController", // formdata参数:方法名 (string 必填) method: "select", // formdata参数:entity (string 必填) entity: "com.epichust.entity.MbbBdMrl", // 使用此方法代替bean与method作为请求方法(string 可选) params: { id : 123 }, // 配置过滤选项 (Array<Object>) filters: [ { field: "id", // 字段名 compare: "cn,ge", // 比较方式(多个以逗号分隔) data: "", // 值 //type: "text" // 输入框类型(暂时忽略) }, { field: "code", compare: "cn,eq", data: "", } ], // 是否首次自动加载数据 loadDataOnFirst: true, }
grid (Object 必填)

表格展示相关的属性配置

  • 单选 selectMax: 1

  • 导出 export: true

  • 刷新 refreshBtn: false

  • 手动上下排序按钮 sortButton: true

  • 手动拖拽表格行 进行排序 manualSort:true

  • 扩展表格和上移下移 需要使用到此属性 "row-key": “id”

  • 隐藏列 visible: false

  • 忽略此列 pass : true

  • 排序 sortable : true

  • 格式转换 formatter

  • 导出时隐藏字段 exportHideen : true

注意:

  • columns 对象的 width 属性必须给。不然会出现样式不齐问题

  • 配置了拖拽和上下排序功能需要添加 "row-key": “id”

  • 添加了 "row-key": "id" 之后的表格刷新,需要使用到 clearSelection 方法

options (Object 非必填)

表格的其他的一些属性配置

options: { // 是否显示编辑过滤 showFilterTemplate: true, // 表格请求formdata参数 带总量查询 usetotal: true, // 额外参数字段:其中的属性会带入请求方法的 formdata中。 // 重要:如果要使用这个功能,需事先把 extraParamFields 定义好,哪怕是个空对象 {} extraParamFields: { // consition: '8a5aya54a5t4t43453thf4' } }

注意:

  • usetotal:true 或 false 有很大的区别,当为 false 时,不管你有多少条数据,当你的页面数为 10 的时候就只会查询 10 条数据

  • extraParamFields:如果需要传额外参数,就需要定义好,哪怕是个空对象 {}

事件
  • selection-change (Function) 获取 当前选择行的数据

  • cell-click (Function) 每次单击单元格会触发此回调,默认回调参数:row, column, cell, event

  • data-change (Function) 每次表格数据查询数据后调用

方法
  • doSearch / bindGrid (Function : void) 触发表格重新加载数据的操作

  • clearSelection (Function : void) 清除表格选中状态

  • openExport (Function : void) 打开导出 excel 的模态框

组件之间的方法调用


主传子: 通过 **$refs**

可以通过$refs 去调用组件的方法 this.$refs.ref的名字.方法名称

子传父:通过 $emit

this.$emit("tabmoved", "student-" + self.mode); //第一个参数 tabmoved 是 组件定义的事件名, 第二个参数 "student-" + self.mode 是 事件传的参数,可以是多个参数