Commit 875339d5 authored by trumansdo's avatar trumansdo
Browse files

补完菜单项管理

parent fbc2862e
......@@ -29,4 +29,15 @@
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
</dependencies>
</project>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<classifier>exec</classifier>
</configuration>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
package com.ibeetl.admin.console.dao;
import java.util.List;
import org.beetl.sql.core.annotatoin.SqlResource;
import org.beetl.sql.core.engine.PageQuery;
import org.beetl.sql.core.mapper.BaseMapper;
......@@ -15,4 +16,6 @@ public interface MenuConsoleDao extends BaseMapper<CoreMenu> {
* @param query 查询条件
*/
void queryByCondtion(PageQuery query);
List<CoreMenu> selectMenuAndRelationFunction();
}
package com.ibeetl.admin.console.service;
import static com.ibeetl.admin.core.service.CorePlatformService.FUNCTION_TREE_CACHE;
import cn.hutool.core.collection.CollUtil;
import com.ibeetl.admin.console.dao.FunctionConsoleDao;
import com.ibeetl.admin.console.dao.RoleFunctionConsoleDao;
......@@ -24,6 +26,7 @@ import java.util.stream.Collectors;
import javax.validation.constraints.NotNull;
import org.beetl.sql.core.engine.PageQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
......@@ -52,13 +55,13 @@ public class FunctionConsoleService extends CoreBaseService<CoreFunction> {
/**
* @return 返回功能点构成的树结构
*/
@Cacheable(FUNCTION_TREE_CACHE)
public List<CoreFunction> getFuncTree() {
List<CoreFunction> coreFunctionList = functionConsoleDao.getSQLManager()
.lambdaQuery(CoreFunction.class)
.andEq(CoreFunction::getDelFlag,
DelFlagEnum.NORMAL).select();
System.out.println(coreFunctionList);
CoreFunction root = new CoreFunction();
root.setId(-1L);
......
package com.ibeetl.admin.console.service;
import static com.ibeetl.admin.core.service.CorePlatformService.MENU_FUNC_TREE_CACHE;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.ibeetl.admin.console.dao.MenuConsoleDao;
import com.ibeetl.admin.core.dao.CoreRoleMenuDao;
import com.ibeetl.admin.core.entity.CoreMenu;
......@@ -7,46 +11,59 @@ import com.ibeetl.admin.core.rbac.tree.MenuItem;
import com.ibeetl.admin.core.service.CoreBaseService;
import com.ibeetl.admin.core.service.CorePlatformService;
import com.ibeetl.admin.core.util.PlatformException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import javax.validation.constraints.NotNull;
import org.beetl.sql.core.engine.PageQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
@Service
@Transactional
public class MenuConsoleService extends CoreBaseService<CoreMenu> {
@Autowired MenuConsoleDao menuDao;
@Autowired CoreRoleMenuDao roleMenuDao;
@Autowired
MenuConsoleDao menuDao;
@Autowired CorePlatformService platformService;
@Autowired
CoreRoleMenuDao roleMenuDao;
@Autowired
CorePlatformService platformService;
public void queryByCondtion(PageQuery<CoreMenu> query) {
menuDao.queryByCondtion(query);
queryListAfter(query.getList());
}
public Long saveMenu(CoreMenu menu) {
CoreMenu query = new CoreMenu();
query.setCode(menu.getCode());
long queryCount = menuDao.templateCount(query);
if (queryCount > 0) {
throw new PlatformException("菜单编码已存在");
}
menu.setCreateTime(new Date());
menu.setParentMenuId(menu.getParent().getId());
menu.setFunctionId(menu.getRelationFunction().getId());
menuDao.insert(menu, true);
platformService.clearMenuCache();
return menu.getId();
}
public void deleteMenu(Long menuId) {
deleteMenuId(menuId);
}
public void batchDeleteMenuId(List<Long> menuIds) {
for (Long id : menuIds) {
deleteMenuId(id);
}
......@@ -54,17 +71,22 @@ public class MenuConsoleService extends CoreBaseService<CoreMenu> {
}
public void updateMenu(CoreMenu menu) {
menu.setParentMenuId(menu.getParent().getId());
menu.setFunctionId(menu.getRelationFunction().getId());
menuDao.updateById(menu);
platformService.clearMenuCache();
}
public CoreMenu getMenu(Long menuId) {
CoreMenu menu = menuDao.unique(menuId);
platformService.clearMenuCache();
return menu;
}
private void deleteMenuId(Long menuId) {
MenuItem root = platformService.buildMenu();
MenuItem fun = root.findChild(menuId);
List<MenuItem> all = fun.findAllItem();
......@@ -74,6 +96,7 @@ public class MenuConsoleService extends CoreBaseService<CoreMenu> {
}
private void realDeleteMenu(List<MenuItem> all) {
List<Long> ids = new ArrayList<>(all.size());
for (MenuItem item : all) {
ids.add(item.getId());
......@@ -82,4 +105,41 @@ public class MenuConsoleService extends CoreBaseService<CoreMenu> {
// 删除角色和菜单的关系
roleMenuDao.deleteRoleMenu(ids);
}
/**
* @return 菜单树,排序
*/
@Cacheable(MENU_FUNC_TREE_CACHE)
public List<CoreMenu> getMenusTree() {
List<CoreMenu> coreMenuList = menuDao.selectMenuAndRelationFunction();
System.out.println(coreMenuList);
CoreMenu root = new CoreMenu();
root.setId(0L);
buildTree(root, coreMenuList);
return root.getChildren();
}
/**
* 深度优先算法递归构建菜单树,根据seq排序
*/
private void buildTree(CoreMenu root, @NotNull List<CoreMenu> allNodes) {
if (CollUtil.isEmpty(allNodes)) {
return;
}
List<CoreMenu> childNodes =
allNodes.stream()
.filter(route -> ObjectUtil.equal(route.getParentMenuId(), root.getId()))
.sorted()
.collect(Collectors.toList());
root.setChildren(childNodes);
allNodes.removeAll(childNodes);
List<CoreMenu> rootChildrenList = root.getChildren();
for (CoreMenu coreFunction : rootChildrenList) {
buildTree(coreFunction, allNodes);
}
}
}
package com.ibeetl.admin.console.web;
import com.ibeetl.admin.console.service.MenuConsoleService;
import com.ibeetl.admin.core.annotation.Function;
import com.ibeetl.admin.core.annotation.RequestBodyPlus;
import com.ibeetl.admin.core.entity.CoreFunction;
import com.ibeetl.admin.core.entity.CoreMenu;
import com.ibeetl.admin.core.rbac.tree.MenuItem;
import com.ibeetl.admin.core.service.CorePlatformService;
import com.ibeetl.admin.core.util.ConvertUtil;
import com.ibeetl.admin.core.web.JsonResult;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.beetl.sql.core.engine.PageQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("menus")
@RestController
public class MenuElController {
@Autowired
MenuConsoleService menuConsoleService;
@Autowired
CorePlatformService platformService;
@GetMapping
@Function("menu.query")
public JsonResult<List<CoreMenu>> menusTree() {
List<CoreMenu> menuTreeItems = menuConsoleService.getMenusTree();
return JsonResult.success(menuTreeItems);
}
/**
* 添加
*
* @param menu
* @return
*/
@PostMapping
@Function("menu.save")
@ResponseBody
public JsonResult save(@Validated @RequestBody CoreMenu menu) {
Long id = menuConsoleService.saveMenu(menu);
return JsonResult.success(id);
}
/**
* 更新
*
* @param menu
* @return
*/
@PutMapping
@Function("menu.update")
@ResponseBody
public JsonResult update(@RequestBody CoreMenu menu) {
menuConsoleService.updateMenu(menu);
return JsonResult.success();
}
/**
* 批量删除
*
* @param ids 菜单id集合
* @return
*/
@DeleteMapping
@Function("menu.delete")
@ResponseBody
public JsonResult delete(@RequestBodyPlus("ids") ArrayList<Long> ids) {
menuConsoleService.batchDeleteMenuId(ids);
return JsonResult.success();
}
}
......@@ -10,7 +10,7 @@ user:
orgId: 1
# 文件操作的根目录配置 请根据各自计算机配置
localFile:
root: E:\code_workspace\temp_space\
root: /home/pi/java_dir/temp_space/
# --------beetl配置
beetl:
suffix: html
......@@ -60,7 +60,7 @@ spring:
baseDataSource:
driver-class-name: com.mysql.cj.jdbc.Driver
password: 123456
url: jdbc:mysql://127.0.0.1:3306/starter_vue?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&useSSL=false&useInformationSchema=true
url: jdbc:mysql://localhost:3306/starter_vue?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&useSSL=false&useInformationSchema=true
username: root
session:
store-type: none
......@@ -7,8 +7,8 @@
<!-- 日志文件目录、压缩文件目录、日志格式配置 -->
<properties>
<Property name="fileName">/Users/admin/Code/log</Property>
<Property name="fileGz">/Users/admin/Code/log/7z</Property>
<Property name="fileName">/home/pi/java_dir/admin/Code/log</Property>
<Property name="fileGz">/home/pi/java_dir/admin/Code/log/7z</Property>
<Property name="PID">????</Property>
<Property name="LOG_PATTERN">%clr{%d{DEFAULT}}{blue} %clr{%5level} %clr{${sys:PID}}{magenta}
%clr{---}{faint} %clr{[%.15t]}{faint} %clr{%-40.40c{1.}}{cyan} %clr{信息:%m}{magenta} %clr{%l}{faint} %n%xwEx
......
......@@ -30,5 +30,47 @@ queryByCondtion
order by m.id
@}
selectMenuAndRelationFunction
=======
* 查询菜单和其关联的功能点
```sql
select
cm.id , cm.name , cm.code , cm.function_id , cm.icon , cm.parent_menu_id ,
cm.seq , cm.type , cf.id func_id , cf.access_url func_access_url , cf.name func_name ,
cf.type func_type
from core_menu cm
join core_function cf on
cf.id = cm.function_id
and cm.del_flag = 0
and cf.del_flag = 0
```
@ mapping("MenuFunctionMapping");
MenuFunctionMapping
===
* 菜单功能点结果集映射
```javascript
var menu_func_mapping={
"id": "menu_func_map",
"mapping": {
"resultType": "com.ibeetl.admin.core.entity.CoreMenu",
"id": "id",
"name": "name",
"code": "code",
"functionId": "function_id",
"parentMenuId": "parent_menu_id",
"icon": "icon",
"seq": "seq",
"type": "type",
"relationFunction": {
"resultType": "com.ibeetl.admin.core.entity.CoreFunction",
"id": "func_id",
"name": "func_name",
"accessUrl": "func_access_url",
"type": "func_type"
}
}
};
```
\ No newline at end of file
......@@ -6,16 +6,17 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.ContentType;
import cn.hutool.system.SystemUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ibeetl.admin.core.util.PlatformException;
import com.ibeetl.admin.core.web.JsonResult;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
......@@ -25,11 +26,15 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.servlet.error.AbstractErrorController;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.util.MimeTypeUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.ModelAndView;
/**
......@@ -46,15 +51,19 @@ public class CustomErrorController extends AbstractErrorController {
private final DefaultErrorAttributes defaultErrorAttributes;
@Autowired ObjectMapper objectMapper;
@Autowired
ObjectMapper objectMapper;
public CustomErrorController(DefaultErrorAttributes defaultErrorAttributes) {
super(defaultErrorAttributes);
this.defaultErrorAttributes = defaultErrorAttributes;
}
@RequestMapping(ERROR_PATH)
public ModelAndView getErrorPath(HttpServletRequest request, HttpServletResponse response) {
public ModelAndView getErrorPath(HttpServletRequest request, HttpServletResponse response)
throws UnsupportedEncodingException {
Throwable cause = getRealException(request);
Map<String, Object> errorInfo = wrapErrorInfo(request);
// 后台打印日志信息方方便查错
......@@ -64,13 +73,42 @@ public class CustomErrorController extends AbstractErrorController {
return hanlderPlatformException(errorInfo, response);
} else if (cause instanceof ConstraintViolationException) {
return hanlderConstraintViolationException(errorInfo, request, response);
} else if (cause instanceof MethodArgumentNotValidException) {
return hanlderMethodArgumentNotValidException(errorInfo, request, response);
} else {
return hanlderGeneralException(errorInfo, response);
}
}
private ModelAndView hanlderMethodArgumentNotValidException(Map<String, Object> errorInfo,
HttpServletRequest request, HttpServletResponse response)
throws UnsupportedEncodingException {
ModelAndView modelAndView = handlerHtml(errorInfo);
if (modelAndView != null) {
modelAndView.addObject("errorMessage", "服务器内部错误,请联系管理员");
logger.error("方法参数校验失败,请查看上述详细信息");
} else {
if (Convert.toInt(errorInfo.get("status")).equals(HttpStatus.NOT_FOUND.value())) {
writeJson(response, JsonResult.http404(errorInfo.get("path")));
} else {
MethodArgumentNotValidException methodArgumentNotValidException =
(MethodArgumentNotValidException) getRealException(request);
BindingResult bindingResult = methodArgumentNotValidException.getBindingResult();
List<String> messages = CollUtil.<String>newArrayList();
List<FieldError> fieldErrors = bindingResult.getFieldErrors();
for (FieldError fieldError : fieldErrors) {
messages.add(fieldError.getDefaultMessage());
}
writeJson(response, JsonResult.fail(messages));
}
}
return modelAndView;
}
private ModelAndView hanlderGeneralException(
Map<String, Object> errorInfo, HttpServletResponse response) {
ModelAndView modelAndView = handlerHtml(errorInfo);
if (modelAndView != null) {
modelAndView.addObject("errorMessage", "服务器内部错误,请联系管理员");
......@@ -82,6 +120,7 @@ public class CustomErrorController extends AbstractErrorController {
private ModelAndView hanlderConstraintViolationException(
Map<String, Object> errorInfo, HttpServletRequest request, HttpServletResponse response) {
ModelAndView modelAndView = handlerHtml(errorInfo);
if (modelAndView != null) {
modelAndView.addObject("errorMessage", "服务器内部错误,请联系管理员");
......@@ -107,6 +146,7 @@ public class CustomErrorController extends AbstractErrorController {
private ModelAndView hanlderPlatformException(
Map<String, Object> errorInfo, HttpServletResponse response) {
ModelAndView modelAndView = handlerHtml(errorInfo);
if (modelAndView != null) {
modelAndView.addObject("errorMessage", errorInfo.get("message"));
......@@ -118,11 +158,9 @@ public class CustomErrorController extends AbstractErrorController {
/**
* 通用处理页面请求方法
*
* @param errorInfo
* @return
*/
protected ModelAndView handlerHtml(Map<String, Object> errorInfo) {
ModelAndView view = null;
if (!Convert.toBool(errorInfo.get("isAjax"))) {
view = new ModelAndView("/error.html");
......@@ -135,6 +173,7 @@ public class CustomErrorController extends AbstractErrorController {
}
protected void handlerAjax(Map<String, Object> errorInfo, HttpServletResponse response) {
if (Convert.toInt(errorInfo.get("status")).equals(HttpStatus.NOT_FOUND.value())) {
writeJson(response, JsonResult.http404(errorInfo.get("path")));
} else {
......@@ -144,26 +183,19 @@ public class CustomErrorController extends AbstractErrorController {
}
/**
* 提取errorAttributes 中的错误信息,包括:<br>
* timestamp:时间<br>
* status:http响应码<br>
* error:响应码的原因<br>
* exception:异常类名<br>
* errors:controller可能的校验错误对象集合<br>
* message:controller的错误信息<br>
* trace: 异常的堆栈信息<br>
* path:请求路径<br>
*
* @param request
* @return
* 提取errorAttributes 中的错误信息,包括:<br> timestamp:时间<br> status:http响应码<br> error:响应码的原因<br>
* exception:异常类名<br> errors:controller可能的校验错误对象集合<br> message:controller的错误信息<br> trace:
* 异常的堆栈信息<br> path:请求路径<br>
*/
protected Map<String, Object> wrapErrorInfo(HttpServletRequest request) {
Map<String, Object> errorAttributes = super.getErrorAttributes(request, true);
errorAttributes.put("isAjax", isJsonRequest(request));
return Collections.unmodifiableMap(errorAttributes);
}
protected void prettyLog(Map errorInfo) {
Object path = errorInfo.get("path");
Object status = errorInfo.get("status");
Object message = errorInfo.get("message");
......@@ -185,30 +217,25 @@ public class CustomErrorController extends AbstractErrorController {
/**
* json请求,要么是.json后缀的请求,要么是http请求报文中规定的json请求
*
* @param request
* @return
*/
protected boolean isJsonRequest(HttpServletRequest request) {
String requestUri = (String) request.getAttribute("javax.servlet.error.request_uri");
if (requestUri != null && requestUri.endsWith(".json")) {
return true;
} else {
return (request.getHeader("Accept").contains("application/json")
|| (request.getHeader("X-Requested-With") != null
&& request.getHeader("X-Requested-With").contains("XMLHttpRequest")));
&& request.getHeader("X-Requested-With").contains("XMLHttpRequest")));
}
}
/**
* json响应的输出流方式
*
* @param response
* @param error
*/
protected void writeJson(HttpServletResponse response, JsonResult error) {
response.addHeader(HttpHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON_VALUE);
response.addHeader(HttpHeaders.ACCEPT_CHARSET, CharsetUtil.UTF_8);
response.setContentType(ContentType.JSON.toString(CharsetUtil.CHARSET_UTF_8));
try {
response.getWriter().write(objectMapper.writeValueAsString(error));
} catch (IOException e) {
......@@ -218,22 +245,24 @@ public class CustomErrorController extends AbstractErrorController {
/**
* 获取真正的异常,而不是被tomcat等包装的异常
*
* @param request
* @return
*/
protected Throwable getRealException(HttpServletRequest request) {
Throwable error = (Throwable) request.getAttribute("javax.servlet.error.exception");
if (error != null) {
while (error instanceof ServletException && error.getCause() != null) {
error = error.getCause();
}
WebRequest webRequest = new ServletWebRequest(request);
Throwable error = (Throwable) webRequest
.getAttribute(DefaultErrorAttributes.class.getName() + ".ERROR",
RequestAttributes.SCOPE_REQUEST);
if (error == null) {
error = (Throwable) webRequest
.getAttribute("javax.servlet.error.exception", RequestAttributes.SCOPE_REQUEST);
}
return error;
}
@Override
public String getErrorPath() {
return ERROR_PATH;
}
}
......@@ -7,12 +7,15 @@ import static org.springframework.http.HttpMethod.POST;
import static org.springframework.http.HttpMethod.PUT;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.CharsetUtil;
import com.ibeetl.admin.core.conf.springmvc.convert.DateConditionalGenericConverter;
import com.ibeetl.admin.core.conf.springmvc.convert.StringToDictTypeEnumConverterFactory;
import com.ibeetl.admin.core.conf.springmvc.interceptor.HttpRequestInterceptor;
import com.ibeetl.admin.core.conf.springmvc.interceptor.SessionInterceptor;
import com.ibeetl.admin.core.conf.springmvc.resolve.RequestBodyPlusProcessor;
import com.ibeetl.admin.core.service.CoreUserService;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
......@@ -24,41 +27,56 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.format.FormatterRegistry;
import org.springframework.format.datetime.DateFormatter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
/** 切勿在此配置类中向SpringMVC中添加bean。 也就是不要 @Bean这类方法。 会出现无法ServletContext注入null,因为父接口的原因 */
/**
* 切勿在此配置类中向SpringMVC中添加bean。 也就是不要 @Bean这类方法。 会出现无法ServletContext注入null,因为父接口的原因
*/
@Configuration
public class SpringWebMvcConfigurer implements WebMvcConfigurer, InitializingBean {
public static final String DEFAULT_APP_NAME = "开发平台";
/** 系统名称,可以在application.properties中配置 app.name=xxx */
/**
* 系统名称,可以在application.properties中配置 app.name=xxx
*/
// @Value("${app.name}")
// String appName;
private String mvcTestPath;
@Autowired private Environment env;
@Autowired
private Environment env;
@Autowired private CoreUserService userService;
@Autowired
private CoreUserService userService;
@Autowired private BeetlGroupUtilConfiguration beetlGroupUtilConfiguration;
@Autowired
private BeetlGroupUtilConfiguration beetlGroupUtilConfiguration;
@Autowired private GroupTemplate groupTemplate;
@Autowired
private GroupTemplate groupTemplate;
@Autowired private RequestMappingHandlerAdapter adapter;
@Autowired
private RequestMappingHandlerAdapter adapter;
/**
* 添加拦截器
*
* @param registry 拦截器的注册器
* @param registry
* 拦截器的注册器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new HttpRequestInterceptor()).addPathPatterns("/**");
registry
.addInterceptor(new SessionInterceptor(userService))
......@@ -66,13 +84,33 @@ public class SpringWebMvcConfigurer implements WebMvcConfigurer, InitializingBea
.addPathPatterns("/**");
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
for (HttpMessageConverter<?> converter : converters) {
if (converter instanceof MappingJackson2HttpMessageConverter) {
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = (MappingJackson2HttpMessageConverter) converter;
mappingJackson2HttpMessageConverter.setDefaultCharset(CharsetUtil.CHARSET_UTF_8);
List<MediaType> supportedMediaTypes = mappingJackson2HttpMessageConverter.getSupportedMediaTypes();
List<MediaType> customMediaTypes = CollUtil.newArrayList(supportedMediaTypes);
customMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
customMediaTypes.add(MediaType.parseMediaType("text/html;charset=UTF-8"));
customMediaTypes.add(MediaType.parseMediaType("text/plain;charset=UTF-8"));
customMediaTypes.add(MediaType.parseMediaType("application/xml;charset=UTF-8"));
mappingJackson2HttpMessageConverter.setSupportedMediaTypes(customMediaTypes);
}
}
}
/**
* 增加跨域映射
*
* @param registry 跨域映射注册器
* @param registry
* 跨域映射注册器
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry
.addMapping("/**")
.allowedOrigins("*")
......@@ -81,15 +119,16 @@ public class SpringWebMvcConfigurer implements WebMvcConfigurer, InitializingBea
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {}
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
}
/**
* SpringMVC的请求响应消息的转换格式器
*
* @param registry
*/
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addFormatter(new DateFormatter("yyyy-MM-dd HH:mm:ss"));
registry.addFormatter(new DateFormatter("yyyy-MM-dd"));
/*converter 在json传参时无效*/
......@@ -99,6 +138,7 @@ public class SpringWebMvcConfigurer implements WebMvcConfigurer, InitializingBea
@Override
public void afterPropertiesSet() {
this.mvcTestPath = env.getProperty("mvc.test.path");
Map<String, Object> var = new HashMap<>(5);
String appName = env.getProperty("app.name");
......@@ -113,4 +153,5 @@ public class SpringWebMvcConfigurer implements WebMvcConfigurer, InitializingBea
argumentResolvers.addAll(adapter.getArgumentResolvers());
adapter.setArgumentResolvers(argumentResolvers);
}
}
package com.ibeetl.admin.core.entity;
import com.ibeetl.admin.core.annotation.Dict;
import com.ibeetl.admin.core.util.ValidateConfig;
import com.ibeetl.admin.core.util.enums.CoreDictType;
import java.util.Date;
import java.util.List;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.beetl.sql.core.annotatoin.AutoID;
import org.beetl.sql.core.annotatoin.SeqID;
import org.beetl.sql.core.annotatoin.UpdateIgnore;
import com.ibeetl.admin.core.annotation.Dict;
import com.ibeetl.admin.core.util.ValidateConfig;
import com.ibeetl.admin.core.util.enums.CoreDictType;
/** 系统菜单 */
public class CoreMenu extends BaseEntity {
/**
* 系统菜单
*/
@NoArgsConstructor
@Data
public class CoreMenu extends BaseEntity implements Comparable {
public static final String TYPE_SYSTEM = "MENU_S";
public static final String TYPE_NAV = "MENU_N";
public static final String TYPE_MENUITEM = "MENU_M";
@NotNull(message = "ID不能为空", groups = ValidateConfig.UPDATE.class)
......@@ -26,7 +32,8 @@ public class CoreMenu extends BaseEntity {
protected Long id;
// 创建时间
@UpdateIgnore protected Date createTime;
@UpdateIgnore
protected Date createTime;
// 菜单代码
@NotBlank(message = "菜单代码不能为空", groups = ValidateConfig.ADD.class)
......@@ -35,6 +42,8 @@ public class CoreMenu extends BaseEntity {
// 功能id
private Long functionId;
private CoreFunction relationFunction;
// 类型 /*1 系统 2 导航 3 菜单项(与功能点有关)*/
@NotNull(message = "菜单类型不能为空")
@Dict(type = CoreDictType.MENU_TYPE)
......@@ -49,83 +58,35 @@ public class CoreMenu extends BaseEntity {
private Long parentMenuId;
// 排序
@NotNull(message = "排序不能为空")
private Integer seq;
private Integer seq = Integer.MAX_VALUE;
// 图标
private String icon;
public CoreMenu() {}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
private String icon = "menu";
public Long getFunctionId() {
return functionId;
}
public void setFunctionId(Long functionId) {
this.functionId = functionId;
}
private CoreMenu parent;
public String getType() {
return type;
}
private List<CoreMenu> children;
public void setType(String type) {
this.type = type;
}
private short delFlag;
public String getName() {
return name;
}
@Override
public int compareTo(Object o) {
public void setName(String name) {
this.name = name;
if (!(o instanceof CoreMenu)) {
throw new IllegalArgumentException("给定的对象不是同一个类型");
}
if (o == null) {
throw new NullPointerException("给定的对象为NULL");
}
CoreMenu other = (CoreMenu) o;
// <0 =0 >0
if (this.seq == null) {
this.seq = Integer.MAX_VALUE;
}
if (other.seq == null) {
other.seq = Integer.MAX_VALUE;
}
return this.seq - other.seq;
}
public Long getParentMenuId() {
return parentMenuId;
}
public void setParentMenuId(Long parentMenuId) {
this.parentMenuId = parentMenuId;
}
public Integer getSeq() {
return seq;
}
public void setSeq(Integer seq) {
this.seq = seq;
}
public String getIcon() {
return icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
}
......@@ -4,19 +4,6 @@ import static com.ibeetl.admin.core.util.HttpRequestLocal.ACCESS_CURRENT_ORG;
import static com.ibeetl.admin.core.util.HttpRequestLocal.ACCESS_CURRENT_USER;
import static com.ibeetl.admin.core.util.HttpRequestLocal.ACCESS_USER_ORGS;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.PostConstruct;
import org.beetl.sql.core.SQLManager;
import org.beetl.sql.core.engine.SQLPlaceholderST;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import com.ibeetl.admin.core.dao.CoreFunctionDao;
import com.ibeetl.admin.core.dao.CoreMenuDao;
import com.ibeetl.admin.core.dao.CoreOrgDao;
......@@ -40,6 +27,17 @@ import com.ibeetl.admin.core.util.PlatformException;
import com.ibeetl.admin.core.util.beetl.DataAccessFunction;
import com.ibeetl.admin.core.util.beetl.NextDayFunction;
import com.ibeetl.admin.core.util.enums.DelFlagEnum;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import javax.annotation.PostConstruct;
import org.beetl.sql.core.SQLManager;
import org.beetl.sql.core.engine.SQLPlaceholderST;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
/**
* 系统平台功能访问入口,所有方法应该支持缓存或者快速访问
......@@ -51,53 +49,85 @@ public class CorePlatformService {
// 菜单树,组织机构树,功能树缓存标记
public static final String MENU_TREE_CACHE = "cache:core:menuTree";
/**
* 菜单与其关联功能点树
*/
public static final String MENU_FUNC_TREE_CACHE = "cache:core:menuFuncTree";
public static final String ORG_TREE_CACHE = "cache:core:orgTree";
public static final String ORG_CACHE_TREE_CHILDREN = "cache:core:orgTreeChildrens";
public static final String ORG_CACHE_TREE_LIST = "cache:core:orgTreeList";
public static final String FUNCTION_TREE_CACHE = "cache:core:functionTree";
// 字典列表
public static final String DICT_CACHE_TREE_CHILDREN = "cache:core:dictTreeChildrens";
public static final String DICT_CACHE_TREE_LIST = "cache:core:dictTreeList";
public static final String DICT_CACHE_TYPE = "cache:core:dictType";
public static final String DICT_CACHE_VALUE = "cache:core:dictValue";
public static final String DICT_CACHE_SAME_LEVEL = "cache:core:ditcSameLevel";
public static final String DICT_CACHE_CHILDREN = "cache:core:dictChildren";
public static final String USER_FUNCTION_ACCESS_CACHE = "cache:core:userFunctionAccess";
public static final String USER_FUNCTION_CHIDREN_CACHE = "ccache:core:functionChildren";
public static final String FUNCTION_CACHE = "cache:core:function";
public static final String USER_DATA_ACCESS_CACHE = "cache:core:userDataAccess";
public static final String USER_MENU_CACHE = "cache:core:userMenu";
public static final String ACCESS_SUPPER_ADMIN = "admin";
@Autowired HttpRequestLocal httpRequestLocal;
@Autowired
HttpRequestLocal httpRequestLocal;
@Autowired
CoreRoleFunctionDao roleFunctionDao;
@Autowired CoreRoleFunctionDao roleFunctionDao;
@Autowired
CoreRoleMenuDao sysRoleMenuDao;
@Autowired CoreRoleMenuDao sysRoleMenuDao;
@Autowired
CoreOrgDao sysOrgDao;
@Autowired CoreOrgDao sysOrgDao;
@Autowired
CoreRoleFunctionDao sysRoleFunctionDao;
@Autowired CoreRoleFunctionDao sysRoleFunctionDao;
@Autowired
CoreMenuDao sysMenuDao;
@Autowired CoreMenuDao sysMenuDao;
@Autowired
CoreUserDao sysUserDao;
@Autowired CoreUserDao sysUserDao;
@Autowired
CoreFunctionDao sysFunctionDao;
@Autowired CoreFunctionDao sysFunctionDao;
@Autowired
SQLManager sqlManager;
@Autowired SQLManager sqlManager;
@Autowired
DataAccessFunction dataAccessFunction;
@Autowired DataAccessFunction dataAccessFunction;
@Autowired
CorePlatformService self;
@Autowired CorePlatformService self;
@Autowired DataAccessFactory dataAccessFactory;
@Autowired
DataAccessFactory dataAccessFactory;
@PostConstruct
@SuppressWarnings("unchecked")
public void init() {
SQLPlaceholderST.textFunList.add("function");
// sql语句里带有此函数来判断数据权限
sqlManager.getBeetl().getGroupTemplate().registerFunction("function", dataAccessFunction);
......@@ -105,51 +135,59 @@ public class CorePlatformService {
}
public CoreUser getCurrentUser() {
checkSession();
CoreUser user = (CoreUser) httpRequestLocal.getSessionValue(ACCESS_CURRENT_USER);
CoreUser user = (CoreUser) HttpRequestLocal.getSessionValue(ACCESS_CURRENT_USER);
return user;
}
public void changeOrg(Long orgId) {
List<CoreOrg> orgs = this.getCurrentOrgs();
for (CoreOrg org : orgs) {
if (org.getId().equals(orgId)) {
httpRequestLocal.setSessionValue(ACCESS_CURRENT_ORG, org);
HttpRequestLocal.setSessionValue(ACCESS_CURRENT_ORG, org);
}
}
}
public Long getCurrentOrgId() {
checkSession();
CoreOrg org = (CoreOrg) httpRequestLocal.getSessionValue(ACCESS_CURRENT_ORG);
CoreOrg org = (CoreOrg) HttpRequestLocal.getSessionValue(ACCESS_CURRENT_ORG);
return org.getId();
}
public CoreOrg getCurrentOrg() {
checkSession();
CoreOrg org = (CoreOrg) httpRequestLocal.getSessionValue(ACCESS_CURRENT_ORG);
CoreOrg org = (CoreOrg) HttpRequestLocal.getSessionValue(ACCESS_CURRENT_ORG);
return org;
}
public List<CoreOrg> getCurrentOrgs() {
List<CoreOrg> orgs = (List<CoreOrg>) httpRequestLocal.getSessionValue(ACCESS_USER_ORGS);
List<CoreOrg> orgs = (List<CoreOrg>) HttpRequestLocal.getSessionValue(ACCESS_USER_ORGS);
return orgs;
}
protected void checkSession() {
CoreOrg org = (CoreOrg) httpRequestLocal.getSessionValue(ACCESS_CURRENT_ORG);
CoreOrg org = (CoreOrg) HttpRequestLocal.getSessionValue(ACCESS_CURRENT_ORG);
if (org == null) {
throw new PlatformException("会话过期,重新登录");
}
}
public void setLoginUser(CoreUser user, CoreOrg currentOrg, List<CoreOrg> orgs) {
httpRequestLocal.setSessionValue(ACCESS_CURRENT_USER, user);
httpRequestLocal.setSessionValue(ACCESS_CURRENT_ORG, currentOrg);
httpRequestLocal.setSessionValue(ACCESS_USER_ORGS, orgs);
HttpRequestLocal.setSessionValue(ACCESS_CURRENT_USER, user);
HttpRequestLocal.setSessionValue(ACCESS_CURRENT_ORG, currentOrg);
HttpRequestLocal.setSessionValue(ACCESS_USER_ORGS, orgs);
}
public MenuItem getMenuItem(long userId, long orgId) {
CoreUser user = this.sysUserDao.unique(userId);
if (this.isSupperAdmin(user)) {
return self.buildMenu();
......@@ -160,7 +198,8 @@ public class CorePlatformService {
return menu;
}
public OrgItem getUserOrgTree() {
public OrgItem getUserOrgTree() {
if (this.isCurrentSupperAdmin()) {
OrgItem root = self.buildOrg();
return root;
......@@ -189,33 +228,29 @@ public class CorePlatformService {
/**
* 判断用户是否是超级管理员
*
* @param user
* @return
*/
public boolean isSupperAdmin(CoreUser user) {
return user.getCode().startsWith(ACCESS_SUPPER_ADMIN);
}
public boolean isCurrentSupperAdmin() {
CoreUser user = this.getCurrentUser();
return isSupperAdmin(user);
}
public boolean isAllowUserName(String name) {
return !name.startsWith(ACCESS_SUPPER_ADMIN);
}
/**
* 获取用户在指定功能点的数据权限配置,如果没有,返回空集合
*
* @param userId
* @param orgId
* @param fucntionCode
* @return
*/
@Cacheable(USER_DATA_ACCESS_CACHE)
public List<CoreRoleFunction> getRoleFunction(Long userId, Long orgId, String fucntionCode) {
List<CoreRoleFunction> list = sysRoleFunctionDao.getRoleFunction(userId, orgId, fucntionCode);
return list;
}
......@@ -223,14 +258,14 @@ public class CorePlatformService {
/**
* 当前用户是否能访问功能,用于后台功能验证,functionCode 目前只支持二级域名方式,不支持更多级别
*
* @param functionCode "user.add","user"
* @return
* @param functionCode
* "user.add","user"
*/
@Cacheable(USER_FUNCTION_ACCESS_CACHE)
public boolean canAcessFunction(Long userId, Long orgId, String functionCode) {
CoreUser user = getCurrentUser();
if (user.getId() == userId && isSupperAdmin(user)) {
if (Objects.equals(user.getId(), userId) && isSupperAdmin(user)) {
return true;
}
String str = functionCode;
......@@ -246,13 +281,12 @@ public class CorePlatformService {
/**
* 当前功能的子功能,如果有,则页面需要做按钮级别的过滤
*
* @param userId
* @param orgId
* @param parentFunction 菜单对应的function
* @return
* @param parentFunction
* 菜单对应的function
*/
@Cacheable(USER_FUNCTION_CHIDREN_CACHE)
public List<String> getChildrenFunction(Long userId, Long orgId, String parentFunction) {
CoreFunction template = new CoreFunction();
template.setCode(parentFunction);
List<CoreFunction> list = sysFunctionDao.template(template);
......@@ -266,23 +300,19 @@ public class CorePlatformService {
/**
* 查询当前用户有用的菜单项目,可以在随后验证是否能显示某项菜单
*
* @return
*/
@Cacheable(USER_MENU_CACHE)
public Set<Long> getCurrentMenuIds(Long userId, Long orgId) {
List<Long> list = sysRoleMenuDao.queryMenuByUser(userId, orgId);
return new HashSet<Long>(list);
}
/**
* 验证菜单是否能被显示
*
* @param item
* @param allows
* @return
*/
public boolean canShowMenu(CoreUser user, MenuItem item, Set<Long> allows) {
if (isSupperAdmin(user)) {
return true;
}
......@@ -291,6 +321,7 @@ public class CorePlatformService {
@Cacheable(MENU_TREE_CACHE)
public MenuItem buildMenu() {
List<CoreMenu> list = sysMenuDao.allMenuWithURL();
return MenuBuildUtil.buildMenuTree(list);
}
......@@ -310,13 +341,13 @@ public class CorePlatformService {
@Cacheable(FUNCTION_TREE_CACHE)
public FunctionItem buildFunction() {
List<CoreFunction> list = sysFunctionDao.all();
return FunctionBuildUtil.buildOrgTree(list);
}
/**
* 用户信息被管理员修改,重置会话,让用户操作重新登录
*
* @param name
*/
public void restUserSession(String name) {
// TODO
......@@ -324,13 +355,14 @@ public class CorePlatformService {
@CacheEvict(
cacheNames = {
FUNCTION_CACHE,
FUNCTION_TREE_CACHE, /*功能点本身缓存*/
MENU_TREE_CACHE,
USER_MENU_CACHE, /*功能点关联菜单缓存*/
USER_FUNCTION_ACCESS_CACHE,
USER_FUNCTION_CHIDREN_CACHE,
USER_DATA_ACCESS_CACHE, /*功能点相关权限缓存*/
FUNCTION_CACHE,
FUNCTION_TREE_CACHE, /*功能点本身缓存*/
MENU_TREE_CACHE,
MENU_FUNC_TREE_CACHE,
USER_MENU_CACHE, /*功能点关联菜单缓存*/
USER_FUNCTION_ACCESS_CACHE,
USER_FUNCTION_CHIDREN_CACHE,
USER_DATA_ACCESS_CACHE, /*功能点相关权限缓存*/
},
allEntries = true)
public void clearFunctionCache() {
......@@ -338,7 +370,8 @@ public class CorePlatformService {
}
@CacheEvict(
cacheNames = {CorePlatformService.MENU_TREE_CACHE, CorePlatformService.USER_MENU_CACHE},
cacheNames = {MENU_TREE_CACHE, MENU_FUNC_TREE_CACHE,
USER_MENU_CACHE},
allEntries = true)
public void clearMenuCache() {
// 没有做任何事情,交给spring cache来处理了
......@@ -346,25 +379,28 @@ public class CorePlatformService {
@CacheEvict(
cacheNames = {
CorePlatformService.DICT_CACHE_CHILDREN,
CorePlatformService.DICT_CACHE_SAME_LEVEL,
CorePlatformService.DICT_CACHE_TYPE,
CorePlatformService.DICT_CACHE_VALUE
CorePlatformService.DICT_CACHE_CHILDREN,
CorePlatformService.DICT_CACHE_SAME_LEVEL,
CorePlatformService.DICT_CACHE_TYPE,
CorePlatformService.DICT_CACHE_VALUE
},
allEntries = true)
public void clearDictCache() {}
public void clearDictCache() {
}
@CacheEvict(
cacheNames = {CorePlatformService.ORG_TREE_CACHE},
allEntries = true)
public void clearOrgCache() {}
public void clearOrgCache() {
}
/**
* 得到类型为系统的菜单,通常就是根菜单下面
*
* @return
*/
public List<MenuItem> getSysMenu() {
MenuItem root = buildMenu();
List<MenuItem> list = root.getChildren();
for (MenuItem item : list) {
......@@ -377,13 +413,12 @@ public class CorePlatformService {
/**
* 得到菜单的子菜单
*
* @param menuId
* @return
*/
public List<MenuItem> getChildMenu(Long menuId) {
MenuItem root = buildMenu();
List<MenuItem> list = root.findChild(menuId).getChildren();
return list;
}
}
......@@ -6,7 +6,8 @@ ENV = 'development'
# 仅限开发环境,生产环境要用NGINX仅限代理
VUE_APP_BASE_API = '/dev-api'
# VUE_APP_SERVER_HOST = 'http://127.0.0.1:9527/mock'
VUE_APP_SERVER_HOST = 'http://127.0.0.1:8080'
VUE_APP_SERVER_HOST = 'http://127.0.0.1:8080'
#VUE_APP_SERVER_HOST = 'http://192.168.1.177:8080'
# vue-cli uses the VUE_CLI_BABEL_TRANSPILE_MODULES environment variable,
# to control whether the babel-plugin-dynamic-import-node plugin is enabled.
......
1、路由由登录后从后端动态生成发送给前端使用addRoutes添加到路由表中
\ No newline at end of file
1、路由由登录后从后端动态生成发送给前端使用addRoutes添加到路由表中
2、不能滥用key属性,更多是同一个父元素下的子元素每一个都要有独特的key,或者你需要一个完整的生命周期
key是vue区分新旧node时对比的关键
/*
* @Author: 一日看尽长安花
* @since: 2020-05-31 14:38:23
* @LastEditTime: 2020-06-13 12:42:08
* @LastEditors: 一日看尽长安花
* @Description:
*/
import request from '@/utils/request';
/**
* 功能点管理的数据
* @param {*} params
*/
export function menus(params) {
return request({
url: '/menus',
method: 'get',
params
});
}
/**
* 功能点管理的数据
* @param {*} params
*/
export function createMenuItem(params) {
return request({
url: '/menus',
method: 'post',
params
});
}
/**
* 功能点管理的数据
* @param {*} params
*/
export function updateMenuItem(params) {
return request({
url: '/menus',
method: 'put',
params
});
}
/**
* 功能点管理的数据
* @param {*} params
*/
export function delMenuItemsByParent(params) {
return request({
url: '/menus',
method: 'delete',
params
});
}
<!--
* @Author: 一日看尽长安花
* @since: 2020-06-07 14:41:55
* @LastEditTime: 2020-06-07 14:45:23
* @LastEditTime: 2020-06-14 18:55:44
* @LastEditors: 一日看尽长安花
* @Description:
-->
<template><span></span></template>
<script>
export default {
name: 'Refresh',
......
<svg t="1592122095549" class="icon" viewBox="0 0 1024 1024" version="1.1"
xmlns="http://www.w3.org/2000/svg" p-id="1126"
xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200">
<path d="M864 0h-152c-88.224 0-160 71.776-160 160v152c0 88.224 71.776 160 160 160h152c88.224 0 160-71.776 160-160V160c0-88.224-71.776-160-160-160z m80 312c0 44.112-35.888 80-80 80h-152c-44.112 0-80-35.888-80-80V160c0-44.112 35.888-80 80-80h152c44.112 0 80 35.888 80 80v152zM312 0H160C71.776 0 0 71.776 0 160v152c0 88.224 71.776 160 160 160h152c88.224 0 160-71.776 160-160V160c0-88.224-71.776-160-160-160z m80 312c0 44.112-35.888 80-80 80H160c-44.112 0-80-35.888-80-80V160c0-44.112 35.888-80 80-80h152c44.112 0 80 35.888 80 80v152zM312 552H160c-88.224 0-160 71.776-160 160v152c0 88.224 71.776 160 160 160h152c88.224 0 160-71.776 160-160v-152c0-88.224-71.776-160-160-160z m80 312c0 44.112-35.888 80-80 80H160c-44.112 0-80-35.888-80-80v-152c0-44.112 35.888-80 80-80h152c44.112 0 80 35.888 80 80v152zM984 824c-22.092 0-40 17.908-40 40 0 44.112-35.888 80-80 80h-152c-44.112 0-80-35.888-80-80v-152c0-44.112 35.888-80 80-80h152a80.06 80.06 0 0 1 73.35 47.992c8.85 20.242 32.436 29.472 52.676 20.624 20.242-8.852 29.474-32.436 20.624-52.676C985.164 589.658 927.6 552 864 552h-152c-88.224 0-160 71.776-160 160v152c0 88.224 71.776 160 160 160h152c88.224 0 160-71.776 160-160 0-22.092-17.908-40-40-40z" p-id="1127"></path>
</svg>
<!--
* @Author: 一日看尽长安花
* @since: 2020-05-30 12:53:38
* @LastEditTime: 2020-06-06 18:44:44
* @LastEditTime: 2020-06-14 15:23:59
* @LastEditors: 一日看尽长安花
* @Description:
-->
<template>
<el-dialog
id="sel-func-dialog"
:key="Math.random()"
title="功能点选择"
:visible="visible"
@update:visible="$emit('update:visible', $event)"
>
<el-input
v-model="filterText"
placeholder="输入关键字进行过滤"
@input="filterInput"
>
</el-input>
<el-input v-model="filterText" placeholder="输入关键字进行过滤"> </el-input>
<el-tree
:key="Math.random()"
ref="tree"
:data="treeData"
node-key="id"
......@@ -82,10 +75,12 @@ export default {
filterText: ''
};
},
watch: {
filterText(nv, ov) {
this.$refs.tree.filter(nv);
}
},
methods: {
filterInput(value) {
this.$refs.tree.filter(value);
},
filterNode(value, data) {
if (!value) return true;
return data.name.indexOf(value) !== -1;
......
<!--
* @Author: 一日看尽长安花
* @since: 2020-05-30 12:53:38
* @LastEditTime: 2020-06-14 18:58:00
* @LastEditors: 一日看尽长安花
* @Description:
-->
<template>
<!-- vue实例外创建 -->
<div id="menu-manager" class="sp-transfer_editor--two">
<div class="sp-side_panel--left">
<el-input v-model="filterText" placeholder="输入关键字进行过滤">
</el-input>
<el-tree
key="treeKey"
ref="tree"
:data="treeData"
node-key="id"
default-expand-all
:expand-on-click-node="false"
:filter-node-method="filterNode"
>
<template v-slot="{ node: node, data: data }">
<div :class="['sp-tree_node', 'sp-tree_node_type--' + data.type]">
<span>{{ data.name }}</span>
<span>
<el-button
plain
type="primary"
size="mini"
@click="appendNode(node, data)"
>
添加
</el-button>
<el-button
v-if="data.id !== 0"
plain
type="primary"
size="mini"
@click="editNode(node, data)"
>
编辑
</el-button>
<el-button
v-if="data.id !== 0"
plain
type="primary"
size="mini"
@click="removeNode(node, data)"
>
删除
</el-button>
</span>
</div>
</template>
</el-tree>
</div>
<div class="sp-side_panel--right">
<el-form
key="formKey"
ref="nodeForm"
:rules="rules"
:model="formModel"
label-width="80px"
>
<el-form-item label="功能名" prop="name">
<el-input
v-model="formModel.name"
placeholder="请输入菜单名称"
></el-input>
</el-form-item>
<el-form-item label="菜单代码" prop="code">
<el-input
v-model="formModel.code"
placeholder="请输入系统唯一菜单代码"
></el-input>
</el-form-item>
<el-form-item label="菜单地址" prop="relation_function">
<el-input
v-model="formModel.relation_function.access_url"
placeholder="请选择关联功能点"
readonly
@focus="openSelectRelationFunctionLayer"
></el-input>
</el-form-item>
<el-form-item label="菜单图标">
<el-input
v-model="formModel.icon"
placeholder="请输入已有的svg图标,询问前端"
></el-input>
</el-form-item>
<el-form-item label="菜单序号">
<el-input
v-model="formModel.seq"
placeholder="请输入本级中的菜单序号"
></el-input>
</el-form-item>
<el-form-item label="父菜单" prop="parent">
<el-input
v-model="formModel.parent.name"
placeholder="点击选择上一级菜单"
readonly
@focus="openSelectParentNodeLayer"
></el-input>
</el-form-item>
<el-form-item label="菜单类型" prop="type">
<el-select v-model="formModel.type" placeholder="请选择菜单类型">
<el-option label="系统" value="MENU_S"></el-option>
<el-option label="导航" value="MENU_N"></el-option>
<el-option label="菜单" value="MENU_M"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="saveNode">保存</el-button>
<el-button>取消</el-button>
</el-form-item>
</el-form>
</div>
<sel-func-dialog
v-model="formModel"
:per-level-label="['relation_function']"
:visible.sync="selFuncDialogVisible"
:tree-data="funcTreeData"
>
</sel-func-dialog>
<sel-menu-dialog
v-model="formModel"
:per-level-label="['parent']"
:visible.sync="selParentDialogVisible"
:tree-data="treeData"
>
</sel-menu-dialog>
</div>
</template>
<script>
/** 菜单管理 */
import {
menus,
createMenuItem,
updateMenuItem,
delMenuItemsByParent
} from '@/api/menu';
import { funcs } from '@/api/func';
import SelFuncDialog from '@/views/functions/select_dialog';
import SelMenuDialog from './select_dialog';
export default {
name: 'MenuMange',
components: { SelFuncDialog, SelMenuDialog },
props: {},
data() {
return {
filterText: '',
treeData: [],
funcTreeData: [],
formModel: {
parent: {
id: undefined,
name: undefined
},
relation_function: {
id: undefined,
name: undefined
}
},
actType: 'create',
selFuncDialogVisible: false,
selParentDialogVisible: false,
rules: {
name: { required: true, message: '请输入名称', trigger: 'blur' },
code: { required: true, message: '请输入代码点', trigger: 'blur' },
relation_function: {
type: 'object',
required: true,
fields: {
access_url: {
type: 'string',
message: '请选择关联功能点',
required: true
}
}
},
parent: {
type: 'object',
required: true,
fields: {
name: { type: 'string', message: '请选择父功能', required: true }
}
},
type: {
type: 'string',
required: true,
message: '请选择访问类型',
trigger: 'change'
}
}
};
},
watch: {
filterText(val) {
this.$refs.tree.filter(val);
}
},
mounted() {
menus().then(res => {
const { code, message, data } = { ...res };
const vmNode = [
{
id: 0,
name: '平台',
children: data
}
];
this.treeData = vmNode;
});
funcs().then(res => {
const { code, message, data } = { ...res };
const vmNode = [
{
id: -1,
name: '平台',
children: data
}
];
this.funcTreeData = vmNode;
});
},
methods: {
filterNode(value, data) {
if (!value) return true;
return data.name.indexOf(value) !== -1;
},
appendNode(node, data) {
this.formModel = {
parent: {
id: undefined,
name: undefined
},
relation_function: {
id: undefined,
name: undefined
}
};
this.formModel.parent = data;
this.actType = 'create';
},
editNode(node, data) {
this.formModel = data;
this.formModel.parent = node.parent.data;
this.actType = 'editor';
},
removeNode(node, data) {
const ids = this.getTreeBranchIds(node);
this.$confirm('此操作将永久删除该菜单及其子菜单, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
.then(() => {
delMenuItemsByParent({ ids: ids }).then(response => {
const { code, message, data } = { ...response };
const loading = this.$loading({
lock: true,
text: 'Loading',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
const _that = this;
this.$message({
message: '删除成功',
type: 'success',
onClose: function(_instance) {
_that.$router.replace('/refresh');
loading.close();
}
});
});
})
.catch(() => {});
},
getTreeBranchIds(node) {
let ids = [];
ids.push(node.data.id);
const _children = node.childNodes;
for (let i = 0; i < _children.length; i++) {
const child = _children[i];
let _child_ids = this.getTreeBranchIds(child);
ids = ids.concat(_child_ids);
}
return ids;
},
saveNode() {
const _that = this;
_that.$refs.nodeForm.validate(valid => {
if (!valid) {
_that.$message.error('数据请填写完成');
return false;
}
// 解除循环引用
delete _that.formModel.parent.children;
if (_that.actType === 'create') {
_that.formModel.parent_menu_id = _that.formModel.parent.id;
createMenuItem(_that.formModel).then(response => {
const { code, message, data } = { ...response };
const _that = this;
const loading = this.$loading({
lock: true,
text: 'Loading',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
this.$message({
message: '创建菜单成功',
type: 'success',
onClose: function(_instance) {
// todo 可以用组件Mixin改写,或者直接挂载到Router原型上
_that.$router.replace('/refresh');
loading.close();
}
});
});
} else {
updateMenuItem(_that.formModel).then(response => {
const { code, message, data } = { ...response };
const _that = this;
const loading = this.$loading({
lock: true,
text: 'Loading',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
this.$message({
message: '更新菜单成功',
type: 'success',
onClose: function(_instance) {
// todo 可以用组件Mixin改写,或者直接挂载到Router原型上
_that.$router.replace('/refresh');
loading.close();
}
});
});
}
});
},
openSelectParentNodeLayer() {
this.selParentDialogVisible = true;
},
openSelectRelationFunctionLayer() {
this.selFuncDialogVisible = true;
}
}
};
</script>
<style lang="scss" scoped>
.sp-transfer_editor--two {
display: flex;
justify-content: space-between;
align-items: stretch;
margin: 10px;
min-height: 84vh;
max-height: 84vh;
}
%sp-side_panel--common {
flex-grow: 1;
flex-shrink: 1;
flex-basis: 0%;
box-shadow: 2px 2px 6px 1px #aaa;
padding: 10px;
margin: 0 10px;
}
.sp-side_panel--left {
@extend %sp-side_panel--common;
overflow-y: scroll;
}
.sp-side_panel--right {
@extend %sp-side_panel--common;
}
.sp-tree_node {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
padding-left: 6px;
border-radius: 5px;
}
%sp-tree_node_type {
box-shadow: 1px 1px 1px 1px #e8f4ff;
}
.sp-tree_node_type--MENU_S {
@extend %sp-tree_node_type;
background: linear-gradient(to right, #ff03035e, transparent);
}
.sp-tree_node_type--MENU_N {
@extend %sp-tree_node_type;
background: linear-gradient(to right, #fff5035e, transparent);
}
.sp-tree_node_type--MENU_M {
@extend %sp-tree_node_type;
background: linear-gradient(to right, #033cff5e, transparent);
}
</style>
<style lang="scss">
.el-tree-node {
margin-top: 5px;
}
</style>
<!--
* @Author: 一日看尽长安花
* @since: 2020-05-30 12:53:38
* @LastEditTime: 2020-06-14 15:24:13
* @LastEditors: 一日看尽长安花
* @Description:
-->
<template>
<el-dialog
id="sel-menu-dialog"
title="菜单选择"
:visible="visible"
@update:visible="$emit('update:visible', $event)"
>
<el-input v-model="filterText" placeholder="输入关键字进行过滤"> </el-input>
<el-tree
ref="tree"
:data="treeData"
node-key="id"
:label="label"
:show-checkbox="true"
default-expand-all
:check-strictly="true"
:expand-on-click-node="false"
:filter-node-method="filterNode"
>
<template v-slot="{ node: node, data: data }">
<div :class="['sp-tree_node', 'sp-tree_node_type--' + data.type]">
<span>{{ data.name }}</span>
</div>
</template>
</el-tree>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="saveSelect">确 定</el-button>
</div>
</el-dialog>
</template>
<script>
export default {
name: 'SelMenuDialog',
components: {},
model: {
prop: 'value',
event: 'updateValue'
},
props: {
visible: {
type: Boolean,
default: false
},
treeData: {
type: Array,
default: function() {
return [];
}
},
label: {
type: String,
default: 'name'
},
perLevelLabel: {
type: Array,
required: true
},
value: {
type: Object,
required: true
}
},
data() {
return {
filterText: ''
};
},
watch: {
filterText(nv) {
this.$refs.tree.filter(nv);
}
},
methods: {
filterNode(value, data) {
if (!value) return true;
return data.name.indexOf(value) !== -1;
},
saveSelect() {
// 包括半选节点
const selNodes = this.$refs.tree.getCheckedNodes(false, true);
if (selNodes && selNodes.length === 0) {
this.$emit('update:visible', false);
}
if (selNodes && selNodes.length !== 1) {
this.$message({
message: '只能选择一个节点',
type: 'warning'
});
return;
}
let _node = this.$refs.tree.getNode(selNodes[0]);
let resObj = {};
for (let i = this.perLevelLabel.length - 1; i >= 0; i--) {
const _label = this.perLevelLabel[i];
this.$lodash.set(resObj, _label, _node.data);
_node = _node.parent;
}
const updateValue = this.$lodash.assignIn({}, this.value, resObj);
this.$emit('updateValue', updateValue);
this.$emit('update:visible', false);
}
}
};
</script>
<style lang="scss" scoped>
.sp-tree_node {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
padding-left: 6px;
border-radius: 5px;
}
%sp-tree_node_type {
box-shadow: 1px 1px 1px 1px #e8f4ff;
}
.sp-tree_node_type--MENU_S {
@extend %sp-tree_node_type;
background: linear-gradient(to right, #ff03035e, transparent);
}
.sp-tree_node_type--MENU_N {
@extend %sp-tree_node_type;
background: linear-gradient(to right, #fff5035e, transparent);
}
.sp-tree_node_type--MENU_M {
@extend %sp-tree_node_type;
background: linear-gradient(to right, #033cff5e, transparent);
}
</style>
<style lang="scss">
.el-dialog {
height: 90%;
overflow-y: scroll;
top: -10vh;
margin-bottom: -5vh;
}
.el-tree-node {
margin-top: 5px;
}
</style>
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment