目标一:能够完成admin端的登录功能
目标二:能够清晰通用后端的好处
目标三:能够完成通用后端的代码开发
目标四:熟悉前端的开发流程
在父工程下创建模块heima-leadnews-admin
(1)拷贝多环境配置文件:
maven_dev.properties
maven_prod.properties
maven_test.properties
(2)从其他微服务中靠谱pom文件中的依赖信息
(3)拷贝配置文件
application.properties
server.port=${port.admin}
spring.application.name=${sn.admin}
log4j2.xml
(4)创建包结构com.heima.admin 并在当前包下创建引导类
(5)在包com.heima.admin.config中引入配置:jackson、security、mysql
参考标准 | 请参考通用接口规范 |
---|---|
接口名称 | /login/in |
请求DTO | com.heima.model.admin.pojos.AdUser |
响应DTO | 返回map{token:xxx,user:{…}} |
(1)admin用户实体类com.heima.model.admin.pojos.AdUser
@Data
public class AdUser {private Long id;private String name;private String password;private String salt;private String nickname;private String image;private String phone;private Short status;private String email;private Date loginTime;private Date createdTime;
}
(2)创建com.heima.model.mappers.admin.AdUserMapper接口
public interface AdUserMapper {AdUser selectByName(String name);
}
(3)AdUserMapper.xml
id, name, password, salt, nickname, image, phone, status, email, login_time, created_time
(1)定义com.heima.admin.service.UserLoginService
public interface UserLoginService {ResponseResult login(AdUser user);
}
(2)实现类UserLoginServiceImpl实现类
@Service
@SuppressWarnings("all")
public class UserLoginServiceImpl implements UserLoginService {@Autowiredprivate AdUserMapper adUserMapper;@Overridepublic ResponseResult login(AdUser user) {if (StringUtils.isEmpty(user.getName())&&StringUtils.isEmpty(user.getPassword())) {return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE,"用户名和密码不能为空");}AdUser adUser = adUserMapper.selectByName(user.getName());if(adUser==null){return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"用户不存在");}else{if(user.getPassword().equalsIgnoreCase(adUser.getPassword())){Map map = new HashMap<>();adUser.setPassword("");adUser.setSalt("");map.put("token", AppJwtUtil.getToken(adUser));map.put("user",adUser);return ResponseResult.okResult(map);}else{return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PASSWORD_ERROR);}}}
}
(3)定义接口api : com.heima.admin.apis.LoginControllerApi
public interface LoginControllerApi{public ResponseResult login(AdUser user);
}
(4)定义controller:com.heima.admin.controller.v1.LoginController
@RestController
@RequestMapping("/login")
public class LoginController{@Autowiredprivate UserLoginService userLoginService ;@RequestMapping("/in")public ResponseResult login(@RequestBody AdUser user){return userLoginService.login(user);}}
导入当天资料文件中的前端项目
在src/constants/api.js中定义常量映射到后端请求地址
export const API_USERAUTH = '/login/in' //用户认证
在src/api/login.js中定义请求方法,在请求成功之后,需要把后台返回的token数据写入本地缓存
import request from '@/utils/request'
import {setUser} from '@/utils/store'
import { API_GETPHONECODE , API_USERAUTH , API_CAPTCHAS } from '@/constants/api'export function loginByUsername(name,password) {const data = {name,password}return request({url: API_USERAUTH,method: 'post',data}).then(result => {if(result['code']==0){let temp = result.datasetUser({name:temp.user.name,photo:null,token:temp.token}) //设置用户的个人数据}return result})
}
在src/router.js中asyncRouterMap对象的children数组中增加以下改动,以满足全局自动记录路由的功能:
设置登录为起始路由
export const asyncRouterMap = [{path: "/",component: Layout,redirect:'/login', //默认子路由name:'mainIndex',children:[{path:'/index',component: () => import('@/views/dashboard/index.vue'),}]},{path: '/login',component: () => import('@/views/login/index.vue'),},{path: '*',component: () => import('@/views/404.vue'),}
]
var myRouter = new Router({routes: asyncRouterMap
})
export default myRouter
后台管理系统大部分功能都是对数据进行查改删,少部分功能还有增加功能。在企业开发过程中,常常封装通用的接口来处理此类功能,以提高开发效率,但其封装过程会涉及以下几个关键点需要注意处理:
权限的控制(访问、数据、操作)
信息安全控制(SQL注入的问题)
对于部分功能需求归纳总结为以下功能点:
新增:能够通用插入授权的数据表数据
修改:能够通用修改指定的字段数据
删除:能够通用删除指定条件的数据
列表:能够通用加载不同数据表的数据,并支持条件查询、分页等
在通用功能中,对于以上功能可做前后置增强业务处理
在通用功能中,可控制不同数据表的操作权限
【持久层】
【业务层】
定义操作权限类,控制数据表的基本操作权限(是否允许CRUD),每次操作之前需要检查此表
每次操作之前检查当前登录人员是否有此功能操作权限(此步要与RBAC进行基础,此例不做实现)
参数校验(比如更新时需要查询条件)
参数合法性校验(放置SQL注入)
前置处理逻辑调用(前置结果可影响程序的继续执行,此例作为练习内容,在此步实现)
数据持久化操作
后置处理逻辑调用(后置结果可影响返回结果)
返回结果
通用操作功能,主要实现CRUD的功能:
列表接口:用户加载数据表分页数据,并提供字段搜索功能
删除接口:用于删除满足条件的某行数据(考虑安全,每次操作只能删除一条数据)
修改和新增接口:用于新增和修改数据表数据
CommonTableEnum:类用于定义哪些数据表可支持通用操作,及其开发的CRUD功能
CommonWhereDto:类用于接收前端传入的修改字段或者查询条件参数
CommonDto:类用于接收前端传入的接口参数(包括CRUD接口的参数)
CommonDao:持久层动态SQL拼接实现
CommonServiceimpl:类用于实现CRUD的通用逻辑处理
CommonController:实现通用接口的控制层定义
BaseCommonFilter:定义后置处理的接口方法(后置增加类的BeanName定义为CommonTableEnum名称即可)
创建com.heima.model.admin.enums.CommonTableEnum类,基本权限的控制定义为枚举类,在后台管理系统中,涉及以下几个功能,要使用到相关数据表,可用通过操作接口实现:
频道/敏感词管理:对平台上的频道进行CRUD操作,因此需要对AD_CHANNEL/
AD_SENSITIVE表允许CRUD操作
爬虫文章审核和自媒体文章审核功能:需要加载人工审核的列表数据,并修改审核状态,所以需要开通CL_NEWS/WM_NEWS的list和修改权限
@Getter
public enum CommonTableEnum {AD_CHANNEL("*",true,true,true,true),AD_SENSITIVE("*",true,true,true,true),// APP用户端AP_ARTICLE("*",true,false,false,false),AP_ARTICLE_CONFIG("*",true,false,true,false),AP_USER("*",true,false,true,false),CL_NEWS("*",true,false,true,false),WM_NEWS("*",true,false,true,false);String filed;boolean list;//开启列表权限?boolean add;//开启增加权限?boolean update;//开启修改权限?boolean delete;//开启删除权限?CommonTableEnum(String filed,boolean list,boolean add,boolean update,boolean delete){this.filed = filed;this.list = list;this.add = add;this.update = update;this.delete = delete;}
}
创建com.heima.model.admin.dtos.CommonWhereDto类,条件封装了操作的字段filed、条件的操作类型(eq、like)、字段值value。
@Data
public class CommonWhereDto { private String filed; private String type="eq"; private String value; }
创建com.heima.model.admin.dtos.CommonDto类,封装分页、操作模式、操作对象、查询条件、修改字段等参数信息。
@Data
public class CommonDto { private Integer size;
private Integer page;
// 操作模式add 新增,edit编辑
private String model;
// 操作的对象
private CommonTableEnum name;
// 查询的条件
private List where;
// 修改的字段
private List sets; }
创建com.heima.admin.service.impl.commfilter.BaseCommonFilter接口,用于定义后置处理的接口约束和公用默认方法。此定义可增加和扩展通用操作不用数据表的业务功能。
/*** 通用过滤器的过滤类*/
public interface BaseCommonFilter {void doListAfter(AdUser user,CommonDto dto);void doUpdateAfter(AdUser user,CommonDto dto);void doInsertAfter(AdUser user, CommonDto dto);void doDeleteAfter(AdUser user, CommonDto dto);/*** 获取更新字段里面的值* @param field* @param dto* @return*/default CommonWhereDto findUpdateValue(String field,CommonDto dto){if(dto!=null){for (CommonWhereDto cw : dto.getSets()){if(field.equals(cw.getFiled())){return cw;}}}return null;}/*** 获取查询字段里面的值* @param field* @param dto* @return*/default CommonWhereDto findWhereValue(String field,CommonDto dto){if(dto!=null){for (CommonWhereDto cw : dto.getWhere()){if(field.equals(cw.getFiled())){return cw;}}}return null;}}
创建com.heima.admin.dao.CommonDao类,使用注解方式提供通用的列表查询方法和增删改功能,并使用$拼接字符串方式,生成动态的SQL语句。
/*** 如果在mycat分库分表的情况下,可以提供多个方法支持不同分片算法的数据CRUD,这里演示较为常用的查询,既非复合分片的CRUD实现*/
@Mapper
public interface CommonDao {@Select("select * from ${tableName} limit #{start},#{size}")@ResultType(HashMap.class)List list(@Param("tableName") String tableName,@Param("start") int start,@Param("size") int size);@Select("select count(*) from ${tableName} ")@ResultType(Integer.class)int listCount(@Param("tableName") String tableName);@Select("select * from ${tableName} where 1=1 ${where} limit #{start},#{size}")@ResultType(HashMap.class)List listForWhere(@Param("tableName") String tableName,@Param("where") String where,@Param("start") int start, @Param("size") int size);@Select("select count(*) from ${tableName} where 1=1 ${where}")@ResultType(Integer.class)int listCountForWhere(@Param("tableName") String tableName,@Param("where") String where);@Update("update ${tableName} set ${sets} where 1=1 ${where}")@ResultType(Integer.class)int update(@Param("tableName") String tableName,@Param("where") String where,@Param("sets") String sets);@Insert("insert into ${tableName} (${fileds}) values (${values})")@ResultType(Integer.class)int insert(@Param("tableName") String tableName,@Param("fileds") String fileds,@Param("values") String values);@Delete("delete from ${tableName} where 1=1 ${where} limit 1")@ResultType(Integer.class)int delete(@Param("tableName") String tableName,@Param("where") String where);
}
创建通用的service接口
public interface CommonService {/*** 加载通用的数据列表* @param dto* @return*/ResponseResult list(CommonDto dto);/*** 修改通用的数据列表* @param dto* @return*/ResponseResult update(CommonDto dto);/*** 删除通用的数据列表* @param dto* @return*/ResponseResult delete(CommonDto dto);}
创建com.heima.admin.service.impl.CommonServiceImpl 类,实现通用CRUD的业务处理。
【辅助方法】
parseValue:方法过滤和替换引起SQL注入的关键字符;注filed和value都需要过滤
doFilter:用于依据name查找对应后置增加Bean,如果查得,则执行对应增强的后置处理
getWhere:拼接where条件字符串,支持like、between、=等条件查询
getSets:拼接修改的set语句的值
getInsertSql:拼接新增Sql的字段和值得字符串
【接口方法】
delete:方法必须有查询条件,删除成功之后执行doFilter方法后置处理
update:方法通过mode参数判别是新增还是修改,并检查对应的必要参数后调用对应方法
addData:判断权限后,调用数据插入方法,插入成功之后再调用后置处理
updateData:判断权限后,调用更新方法,修改成功后,再调用后置处理方法
list:方法判断权限后,计算分页参数和查询条件,并获取列表和总的及数,最后调用后置方法
/*** 通用操作类*/
@Service
public class CommonServiceImpl implements CommonService {@AutowiredCommonDao commonDao;@AutowiredApplicationContext context;/*** 删除通用的数据列表* @param dto* @return*/public ResponseResult delete(CommonDto dto){String where = getWhere(dto);String tableName =dto.getName().name().toLowerCase();if(!dto.getName().isDelete()){return ResponseResult.errorResult(AppHttpCodeEnum.NO_OPERATOR_AUTH);}if(StringUtils.isEmpty(where)){return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"删除条件不合法");}int temp = commonDao.delete(tableName,where);if(temp>0){doFilter(dto,"delete");}return ResponseResult.okResult(temp);}/*** 修改通用的数据列表* @param dto* @return*/public ResponseResult update(CommonDto dto){String model = dto.getModel();String where = getWhere(dto);String tableName =dto.getName().name().toLowerCase();if("add".equals(model)){if(StringUtils.isNotEmpty(where)){return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"新增数据不能设置条件");}else {return addData(dto, tableName);}}else {if(StringUtils.isEmpty(where)){return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"修改条件不能为空");}else {return updateData(dto, tableName, where);}}}/*** 插入一条数据* @param dto* @return*/private ResponseResult addData(CommonDto dto,String tableName){String[] sql = getInsertSql(dto);if(!dto.getName().isAdd()){return ResponseResult.errorResult(AppHttpCodeEnum.NO_OPERATOR_AUTH);}if(sql==null){return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"传入的参数值不能为空");}int temp =commonDao.insert(tableName,sql[0],sql[1]);if(temp>0){doFilter(dto,"add");}return ResponseResult.okResult(temp);}/*** 更新一条数据* @param dto* @return*/private ResponseResult updateData(CommonDto dto,String tableName,String where){String sets = getSets(dto);if(!dto.getName().isUpdate()){return ResponseResult.errorResult(AppHttpCodeEnum.NO_OPERATOR_AUTH);}if(StringUtils.isEmpty(sets)){return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"修改的参数值不能为空");}int temp = commonDao.update(tableName,where,sets);if(temp>0){doFilter(dto,"update");}return ResponseResult.okResult(temp);}/*** 通用列表加载方法* @param dto* @return*/public ResponseResult list(CommonDto dto){if(!dto.getName().isList()){return ResponseResult.errorResult(AppHttpCodeEnum.NO_OPERATOR_AUTH);}String where = getWhere(dto);String tableName =dto.getName().name().toLowerCase();List> list = null;int total = 0;int start = (dto.getPage()-1)*dto.getSize();if(start<-1)start=0;if(StringUtils.isEmpty(where)){list = commonDao.list(tableName,start,dto.getSize());total = commonDao.listCount(tableName);}else{list = commonDao.listForWhere(tableName,where,start,dto.getSize());total = commonDao.listCountForWhere(tableName,where);}Map map = Maps.newHashMap();map.put("list",list);map.put("total",total);doFilter(dto,"list");return ResponseResult.okResult(map);}/*** 拼接查询条件* @param dto* @return*/private String getWhere(CommonDto dto){StringBuffer where = new StringBuffer();if(dto.getWhere()!=null){dto.getWhere().stream().forEach(w->{// 字段不为空,并且字段和值不能相等(防止凭借真条件)if(StringUtils.isNotEmpty(w.getFiled())&&StringUtils.isNotEmpty(w.getValue())&&!w.getFiled().equalsIgnoreCase(w.getValue())) {String tempF = parseValue(w.getFiled());String tempV = parseValue(w.getValue());if(!tempF.matches("\\d*")&&!tempF.equalsIgnoreCase(tempV)) {if ("eq".equals(w.getType())) {where.append(" and ").append(tempF).append("=\'").append(tempV).append("\'");}if ("like".equals(w.getType())) {where.append(" and ").append(tempF).append(" like \'%").append(tempV).append("%\'");}if ("between".equals(w.getType())) {String temp[] = tempV.split(",");where.append(" and ").append(tempF).append(temp[0]).append(" and ").append(temp[1]);}}}});}return where.toString();}/*** 拼接修改条件* @param dto* @return*/private String getSets(CommonDto dto){StringBuffer sets = new StringBuffer();AtomicInteger count = new AtomicInteger();if(dto.getSets()!=null){dto.getSets().stream().forEach(w->{if(StringUtils.isEmpty(w.getValue())){count.incrementAndGet();}else {String tempF = parseValue(w.getFiled());String tempV = parseValue(w.getValue());if(!tempF.matches("\\d*")&&!tempF.equalsIgnoreCase(tempV)) {if (sets.length() > 0) {sets.append(",");}sets.append(tempF).append("=\'").append(tempV).append("\'");}}});}if(count.get()>0){return null;}return sets.toString();}/*** 拼接插入字符串* @param dto* @return*/private String[] getInsertSql(CommonDto dto){StringBuffer fileds = new StringBuffer();StringBuffer values = new StringBuffer();AtomicInteger count = new AtomicInteger();if(dto.getSets()!=null){dto.getSets().stream().forEach(w->{if(StringUtils.isEmpty(w.getValue())){count.incrementAndGet();}else {String tempF = parseValue(w.getFiled());String tempV = parseValue(w.getValue());if(!tempF.matches("\\d*")&&!tempF.equalsIgnoreCase(tempV)) {if (fileds.length() > 0) {fileds.append(",");values.append(",");}fileds.append(tempF);values.append("\'").append(tempV).append("\'");}}});}if(count.get()>0){return null;}return new String[]{fileds.toString(),values.toString()};}/*** SQL 单引号('),分号(;) 和 注释符号(--)* @param value* @return*/public String parseValue(String value){if(StringUtils.isNotEmpty(value)){return value.replaceAll(".*([';#%]+|(--)+).*", "");}return value;}private void doFilter(CommonDto dto,String name){try{BaseCommonFilter baseCommonFilter = findFilter(dto);if(baseCommonFilter!=null){AdUser adUser = AdminThreadLocalUtils.getUser();if("insert".equals(name)){baseCommonFilter.doInsertAfter(adUser,dto);}if("update".equals(name)){baseCommonFilter.doUpdateAfter(adUser,dto);}if("list".equals(name)){baseCommonFilter.doListAfter(adUser,dto);}if("delete".equals(name)){baseCommonFilter.doDeleteAfter(adUser,dto);}}}catch (Exception e){e.printStackTrace();}}private BaseCommonFilter findFilter(CommonDto dto){String name = dto.getName().name();if(context.containsBean(name)) {return context.getBean(name, BaseCommonFilter.class);}return null;}
}
创建com.heima.admin.controller.v1.CommonController类,实现Service的调用。
@RestController
@RequestMapping("/api/v1/admin/common")
public class CommonController{@Autowiredprivate CommonService commonService;@PostMapping("/list")public ResponseResult list(@RequestBody CommonDto dto) {return commonService.list(dto);}@PostMapping("/update")public ResponseResult update(@RequestBody CommonDto dto) {return commonService.update(dto);}@PostMapping("/delete")public ResponseResult delete(@RequestBody CommonDto dto) {return commonService.delete(dto);}}
后端已经有通用的接口了,前端相关功能页面也可提供通用的页面与对应页面进行对接,达到前后端通用性,都能提升开发效率的目的。
管理系统的页面布局和内容都较为单一和重复,基本可抽象为以下部分:
搜索栏:一个或多个输入搜索框,用于过滤列表中的数据
插入栏:是新增数据的操作入口
编辑窗:对数据的编辑和新增内容提供操作窗口(一般由新增、编辑等功能触发弹出)
列表列:控制重要信息的输出
行操作:不同列表数据行操作是不同的,比如有的编辑,有的数据则无编辑功能按钮
前端实现的难点在于模型的定义,通过模型可控制编辑窗的项和值、列表列的显示、搜索栏的输入项和值。行操作通用性不强,一般作为不同页面的特殊内容定义。在本案例中通过定义一个fileds模型来同时控制列表项的显示和编辑框的项和输入值。控制搜索栏的功能感兴趣的同学可自行实现。
定义fileds数组模型,包括是否在列表中显示、是否在编辑中显示、其值方法、值条件范围
在列表显示时,检查此模型是否设置对应列的显示
在编辑框显示时,检查此模型是否设置对应的字段开启了编辑功能
定义params模式,用于组装请求到后端接口的列表数据
定义Editor组件用于处理数据新增或编辑的处理
定义SearchTool组件用于处理搜索栏的渲染
定义SearchResult组件用于处理列表数据的渲染
创建文件srccomponents/CommEditor.vue
外部传入的参数项:
title:编辑框的名称
fileds:通用字段列表模型
table:操作的数据表代表字符
submitSuccess:新增或修改操作成功后回调上层的方法
属性项:
dialogFormVisible:用于控制dialog的显示
model:用户标识当前的操作是新增还是修改
disable:控制按钮的差异显示
formLabelWidth:控制表单label的宽度
entry:数据对应,用于初始化表单数据
form:用于生成表单字段的绑定值
rules:用于生成表单的验证规则
props: ['title', 'fileds','table','submitSuccess'],
data() {return {disable:false,model:'add',dialogFormVisible:false,formLabelWidth: '80px',entry:{},form: {},rules:{}}
}
VIEW实现了input、number、select、radio四种输入类型定义,其中select、radio的可选值从fileds的options、radios字段中定义。
{{opt.label}} 关 闭 取 消 确 定
通用编辑框有三种态:新增、修改、查看,因此有三个入口方法与此对应,另外除此之后,还有对传入参数解析、表单数据提交验证、数据接口提交等方法。
add:新增接口
edit:编辑数据
view:查看入口
refresh:依据当前参数,计算出rules、form
submit:验证表单数据,并组装接口参数
submitToBack:提交接口数据,并依据返回接口回调submitSuccess方法
methods:{add : function(){this.dialogFormVisible=truethis.entry = {}this.model = 'add'this.refresh()},edit : function(item){this.dialogFormVisible=truethis.entry = itemthis.model = 'edit'this.refresh()},view : function(item){this.disable=truethis.dialogFormVisible=truethis.entry = itemthis.model = 'view'this.refresh()},getTitle : function(){return (this.model=='add'?'新增':(this.model=='view'?'查看':'修改'))+' - '+this.title},refresh : function(){// 初始化数据for(let i=0;ilet tmp = this.fileds[i]if(tmp.rule){this.rules[tmp.name]=tmp.rule}// 是否有修改值entry,否则使用默认值let val = tmp.valueif(this.entry&&this.entry[tmp.name]!=undefined){val = this.entry[tmp.name]}if(typeof val == 'boolean'){if(val) val =1;else val = 0;}this.$set(this.form,tmp.name,val)}},submit : function(){this.$refs['commForm'].validate((valid) => {if (valid) {let sets = []for(let k in this.form){if(this.form[k]){// 排除时间的修改if(this.model=='add'||(this.model=='edit'&&k.indexOf("_time")==-1)){sets.push({filed:k,value:this.form[k]})}}}let param = {model:this.model,name:this.table,where:[{filed:'id',type:'eq',value:this.entry.id}],sets:sets}this.submitToBack(param)} else {return false;}});},async submitToBack(param){let res = await updateData(param)if(res.code==0){this.dialogFormVisible=falsethis.submitSuccess()this.$message({type:'success',message:this.getTitle()+'操作成功!'});}else{this.$message({type:'error',message:res.error_message});}}
}
创建文件src/views/content_media/components/SearchTool.vue
外部传入参数:
changeParam:点击搜索按钮时调用的参数搜索方法
addData:新增按钮点击时调用的方法
Model属性:(依据不同功能可实现多个属性字段扩展)
props:["changeParam","addData"],
data() {return {name: ''}
}
搜 索
VM较为简单,拼接好对应参数之后调用changeParam方法即可。
queryData() {let params = {filed:'title',type:'like',value: this.name}this.changeParam(params)
}
创建文件src/views/content_media/components/SearchTool.vue
外部传入参数较多,核心的参数如下:
list:列表的数据
fileds:列表项定义
内部属性:
listPage:定义分页
id:定义id查询条件
props: ['host','list','fileds','table','pageSize','total','changePage','changeStatus','editData','viewData'],
data() {return {listPage:{currentPage:1},id: {filed: 'id',type:'eq',value:''}}
}
VIEW包括分页条、数据项、操作按钮等内容。实现的思路是通过两次循环实现页面渲染,先循环list,再循环fileds渲染出每行的数据输出,并依据fileds.type进行输出值转换,比如转化成radio的lable值、时间则格式化等细节。
{{`共找到${total}条符合条件的内容`}} {{rs.label}} {{rs.label}} {{rs.label}} {{rs.label}} {{rs.label}} {{rs.label}} {{rs.label}} {{rs.label}} -1">{{ dateFormat(scope.row[item.name]) }} {{ scope.row[item.name] }} 查看拒绝通过
此组件VM中主要实现行操作的功能,因此此部分实现通用性要依据不同功能而定,但思路可借鉴如下:自媒体人工审核操作,行操作有通过、拒绝、查看三个操作按钮
通过或拒绝:operateForDisable组装请求参数,然后调用后台通用修改接口,操作成功之后调用changeStatus上层通知方法
operateForView:方法则是查看数据方法,直接通过父组件传递调用通用编辑框中的view方法
async operateForDisable(id,status,index) {this.id.value = id;let params = {name:this.table,where:[this.id],sets:[{filed:'status',value:status}]}let res = await updateData(params)if(res.code==0){this.changeStatus(index,status);this.$message({type:'success',message:'操作成功!'});}else{this.$message({type:'error',message:res.errorMessage});}
},
operateForView(item) {this.viewData(item)
}
创建文件src/views/content_media/index.vue
params:用于定义列表数据加载的参数,where用于设置查询条件,base定义不可更改的基本条件
list:用于存储后端返回的列表数据
fileds:用于定义功能操作表的字段信息,主要属性如下:
list:定义是否在列表中显示
label:定义字段中文名称
name:定义字段物理表名称
type:定义字段值的输入类型
placeholder:定义字段默认输入提示
value:定义字段默认值
radios:定义字段选项值
data() {return {params:{name:'WM_NEWS',page:1,size:10,base:[{type:'eq',filed:'status',value:'3'}],where:[]},total:0,host:'',list:[],fileds:[{list:true,label:'标题',name:'title',type:'input',placeholder:'请输入标题',rule:[{ required: true, message: '请输入标题', trigger: 'blur' },{ min: 10, max:20,message: '标题在10~50个字符', trigger: 'blur' }]},{list:true,label:'作者',name:'author_name',type:'input'},{list:true,label:'类型',name:'type',type:'radio',value:0,radios:[{value:0,label:'无图'},{value:1,label:'单图'},{value:2,label:'多图'}]},{list:true,label:'标签',name:'label',type:'input'},{list:true,label:'定时时间',name:'publish_time',type:'input'},{list:true,label:'创建时间',name:'created_time',type:'hidden',value:DateUtil.format13HH(new Date().getTime())},{list:true,label:'提交时间',name:'submited_time',type:'hidden',value:DateUtil.format13HH(new Date().getTime())}]}
}
VIEW实现既是引入三个组件,并传入对应参数即可。
在VM中,主要实现以下方法,以串联几个组件之后的调用:
viewData:方法提供查看数据的串联调用
addData:提供新增按钮的串联调用
submitSuccess:提供保存和修改之后数据重新加载
changeParam:提供搜索条件变化后搜索功能的串联调用
changePage:提供分页按钮点击之后串联调用
loadData:加载数据列表数据
methods: {// 编辑数据viewData : function(item){this.$refs['editor'].view(item)},// 新增数据addData : function(item){this.$refs['editor'].add()},// 新增或者修改后的操作方法submitSuccess:function(){this.loadData()},changeStatus:function(index,status){this.loadData()},changeParam :function(e){this.params.page=1this.params.where=ethis.loadData()},changePage :function(e){this.params.page=e.pagethis.loadData()},async loadData() {this.params.where=this.params.base.concat(this.params.where)let res = await loadList({...this.params});if (res.code == 0) {this.list = res.data.listthis.host = res.hostthis.total = res.data.total //总记录数} else {this.$message({type: 'error', message: res.error_message})}}
}
通用功能在企业实际开发中,基本良好的通用性以及扩展性,且能提高开发效率。因此是比较常见的玩法。但由于通用操作存在一定的风险,因此在使用时建议做好操作日志以及数据日志的备份处理。
在本案例中实现了核心流程功能实现,但在企业中还需要实现更多的细节处理,感兴趣的同学可抽实现进行优化,并在之后企业中应用。
【后端优化】
集成RBAC权限的进一步权限操作检查
集成行数据权限检查,进一步防范数据操作
设置前置处理接口,并可依据前置处理结果影响后续执行和返回结果
返回后置处理结果,并可返回结果
如果使用了Mycat,则需要优化动态SQL的节点选择注解,以提升性能
操作日志记录(建议必须加上)
【前端优化】
定义的fileds项能支持更多的表单类型,以及表单选择项可远程动态加载
通过fileds可自动控制搜索栏输出
通过fileds可控制行操作内容输出
提供一套通用页面可完成支持不同功能的自动渲染
后台需提供配置获取接口
对于差异化的功能可归纳前端定义,或者配置中支持动态脚本
submitSuccess:提供保存和修改之后数据重新加载
changeParam:提供搜索条件变化后搜索功能的串联调用
changePage:提供分页按钮点击之后串联调用
loadData:加载数据列表数据
methods: {// 编辑数据viewData : function(item){this.$refs['editor'].view(item)},// 新增数据addData : function(item){this.$refs['editor'].add()},// 新增或者修改后的操作方法submitSuccess:function(){this.loadData()},changeStatus:function(index,status){this.loadData()},changeParam :function(e){this.params.page=1this.params.where=ethis.loadData()},changePage :function(e){this.params.page=e.pagethis.loadData()},async loadData() {this.params.where=this.params.base.concat(this.params.where)let res = await loadList({...this.params});if (res.code == 0) {this.list = res.data.listthis.host = res.hostthis.total = res.data.total //总记录数} else {this.$message({type: 'error', message: res.error_message})}}
}
通用功能在企业实际开发中,基本良好的通用性以及扩展性,且能提高开发效率。因此是比较常见的玩法。但由于通用操作存在一定的风险,因此在使用时建议做好操作日志以及数据日志的备份处理。
在本案例中实现了核心流程功能实现,但在企业中还需要实现更多的细节处理,感兴趣的同学可抽实现进行优化,并在之后企业中应用。
【后端优化】
集成RBAC权限的进一步权限操作检查
集成行数据权限检查,进一步防范数据操作
设置前置处理接口,并可依据前置处理结果影响后续执行和返回结果
返回后置处理结果,并可返回结果
如果使用了Mycat,则需要优化动态SQL的节点选择注解,以提升性能
操作日志记录(建议必须加上)
【前端优化】
定义的fileds项能支持更多的表单类型,以及表单选择项可远程动态加载
通过fileds可自动控制搜索栏输出
通过fileds可控制行操作内容输出
提供一套通用页面可完成支持不同功能的自动渲染
后台需提供配置获取接口
对于差异化的功能可归纳前端定义,或者配置中支持动态脚本