Merge remote-tracking branch 'origin/master'

pull/31/head
hetianchen 5 years ago
commit 5082b67722

@ -1,5 +1,7 @@
## 平台简介
若依是一套全部开源的快速开发平台,毫无保留给个人及企业免费使用。
* 采用前后端分离的模式,微服务版本前端(基于 [RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue))。
* 后端采用Spring Boot、Spring Cloud & Alibaba。
* 注册中心、配置中心选型Nacos权限认证使用Redis。

@ -812,7 +812,7 @@ public class ExcelUtil<T>
*/
private Object getValue(Object o, String name) throws Exception
{
if (StringUtils.isNotEmpty(name))
if (StringUtils.isNotNull(o) && StringUtils.isNotEmpty(name))
{
Class<?> clazz = o.getClass();
Field field = clazz.getDeclaredField(name);

@ -1,6 +1,9 @@
package com.ruoyi.common.log.aspect;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.aspectj.lang.JoinPoint;
@ -209,8 +212,31 @@ public class LogAspect
* @param o
* @return truefalse
*/
@SuppressWarnings("rawtypes")
public boolean isFilterObject(final Object o)
{
Class<?> clazz = o.getClass();
if (clazz.isArray())
{
return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
}
else if (Collection.class.isAssignableFrom(clazz))
{
Collection collection = (Collection) o;
for (Iterator iter = collection.iterator(); iter.hasNext();)
{
return iter.next() instanceof MultipartFile;
}
}
else if (Map.class.isAssignableFrom(clazz))
{
Map map = (Map) o;
for (Iterator iter = map.entrySet().iterator(); iter.hasNext();)
{
Map.Entry entry = (Map.Entry) iter.next();
return entry.getValue() instanceof MultipartFile;
}
}
return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse;
}
}

@ -1,11 +1,13 @@
package com.ruoyi.common.redis.service;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
@ -136,10 +138,15 @@ public class RedisService
* @param dataSet
* @return
*/
public <T> long setCacheSet(final String key, final Set<T> dataSet)
public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet)
{
Long count = redisTemplate.opsForSet().add(key, dataSet);
return count == null ? 0 : count;
BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
Iterator<T> it = dataSet.iterator();
while (it.hasNext())
{
setOperation.add(it.next());
}
return setOperation;
}
/**

@ -171,11 +171,13 @@ public class SysUserController extends BaseController
{
return AjaxResult.error("新增用户'" + user.getUserName() + "'失败,登录账号已存在");
}
else if (UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user)))
else if (StringUtils.isNotEmpty(user.getPhonenumber())
&& UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user)))
{
return AjaxResult.error("新增用户'" + user.getUserName() + "'失败,手机号码已存在");
}
else if (UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user)))
else if (StringUtils.isNotEmpty(user.getEmail())
&& UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user)))
{
return AjaxResult.error("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在");
}
@ -193,11 +195,13 @@ public class SysUserController extends BaseController
public AjaxResult edit(@Validated @RequestBody SysUser user)
{
userService.checkUserAllowed(user);
if (UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user)))
if (StringUtils.isNotEmpty(user.getPhonenumber())
&& UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user)))
{
return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
}
else if (UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user)))
else if (StringUtils.isNotEmpty(user.getEmail())
&& UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user)))
{
return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
}

@ -9,11 +9,7 @@
"build:prod": "vue-cli-service build",
"build:stage": "vue-cli-service build --mode staging",
"preview": "node build/index.js --preview",
"lint": "eslint --ext .js,.vue src",
"test:unit": "jest --clearCache && vue-cli-service test:unit",
"test:ci": "npm run lint && npm run test:unit",
"svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml",
"new": "plop"
"lint": "eslint --ext .js,.vue src"
},
"husky": {
"hooks": {
@ -41,59 +37,45 @@
},
"dependencies": {
"@riophae/vue-treeselect": "0.4.0",
"axios": "0.18.1",
"clipboard": "2.0.4",
"core-js": "3.6.5",
"echarts": "4.2.1",
"axios": "0.21.0",
"clipboard": "2.0.6",
"core-js": "3.8.1",
"echarts": "4.9.0",
"element-ui": "2.14.1",
"file-saver": "2.0.1",
"js-beautify": "1.10.2",
"fuse.js": "3.4.4",
"js-cookie": "2.2.0",
"file-saver": "2.0.4",
"fuse.js": "6.4.3",
"highlight.js": "10.4.1",
"js-beautify": "1.13.0",
"js-cookie": "2.2.1",
"jsencrypt": "3.0.0-rc.1",
"normalize.css": "7.0.0",
"nprogress": "0.2.0",
"path-to-regexp": "2.4.0",
"screenfull": "4.2.0",
"sortablejs": "1.8.4",
"vue": "2.6.10",
"vue-count-to": "1.0.13",
"path-to-regexp": "6.2.0",
"quill": "1.3.7",
"vue-cropper": "0.4.9",
"screenfull": "5.0.2",
"sortablejs": "1.10.2",
"vue": "2.6.12",
"vue-count-to": "1.0.13",
"vue-cropper": "0.5.5",
"vue-router": "3.4.9",
"vue-splitpane": "1.0.4",
"vuedraggable": "2.20.0",
"vuex": "3.1.0"
"vuedraggable": "2.24.3",
"vuex": "3.6.0"
},
"devDependencies": {
"@vue/cli-plugin-babel": "4.4.4",
"@vue/cli-plugin-eslint": "4.4.4",
"@vue/cli-plugin-unit-jest": "4.4.4",
"@vue/cli-service": "4.4.4",
"@vue/test-utils": "1.0.0-beta.29",
"autoprefixer": "9.5.1",
"@vue/cli-plugin-babel": "4.4.6",
"@vue/cli-plugin-eslint": "4.4.6",
"@vue/cli-service": "4.4.6",
"babel-eslint": "10.1.0",
"babel-jest": "23.6.0",
"babel-plugin-dynamic-import-node": "2.3.3",
"chalk": "2.4.2",
"chokidar": "2.1.5",
"chalk": "4.1.0",
"connect": "3.6.6",
"eslint": "6.7.2",
"eslint-plugin-vue": "6.2.2",
"html-webpack-plugin": "3.2.0",
"husky": "1.3.1",
"lint-staged": "8.1.5",
"mockjs": "1.0.1-beta3",
"plop": "2.3.0",
"runjs": "4.3.2",
"node-sass": "4.14.1",
"sass-loader": "8.0.2",
"script-ext-html-webpack-plugin": "2.1.3",
"script-loader": "0.7.2",
"serve-static": "1.13.2",
"svg-sprite-loader": "4.1.3",
"svgo": "1.2.0",
"vue-template-compiler": "2.6.10"
"eslint": "7.15.0",
"eslint-plugin-vue": "7.2.0",
"lint-staged": "10.5.3",
"sass": "1.30.0",
"runjs": "4.4.2",
"sass-loader": "10.1.0",
"script-ext-html-webpack-plugin": "2.1.5",
"svg-sprite-loader": "5.1.1",
"vue-template-compiler": "2.6.12"
},
"engines": {
"node": ">=8.9",

@ -0,0 +1,2 @@
User-agent: *
Disallow: /

@ -92,6 +92,15 @@
background-color: rgba(0, 0, 0, 0.06) !important;
}
}
& .theme-dark .nest-menu .el-submenu>.el-submenu__title,
& .theme-dark .el-submenu .el-menu-item {
background-color: $subMenuBg !important;
&:hover {
background-color: $subMenuHover !important;
}
}
}
.hideSidebar {

@ -12,7 +12,7 @@
class="header-search-select"
@change="change"
>
<el-option v-for="item in options" :key="item.path" :value="item" :label="item.title.join(' > ')" />
<el-option v-for="option in options" :key="option.item.path" :value="option.item" :label="option.item.title.join(' > ')" />
</el-select>
</div>
</template>
@ -167,7 +167,7 @@ export default {
display: inline-block;
vertical-align: middle;
/deep/ .el-input__inner {
::v-deep .el-input__inner {
border-radius: 0;
border: 0;
padding-left: 0;

@ -22,11 +22,8 @@ export default {
},
methods: {
click() {
if (!screenfull.enabled) {
this.$message({
message: 'you browser can not work',
type: 'warning'
})
if (!screenfull.isEnabled) {
this.$message({ message: '你的浏览器不支持全屏', type: 'warning' })
return false
}
screenfull.toggle()
@ -35,12 +32,12 @@ export default {
this.isFullscreen = screenfull.isFullscreen
},
init() {
if (screenfull.enabled) {
if (screenfull.isEnabled) {
screenfull.on('change', this.change)
}
},
destroy() {
if (screenfull.enabled) {
if (screenfull.isEnabled) {
screenfull.off('change', this.change)
}
}

@ -1,8 +1,7 @@
<!-- @author ruoyi 20201128 支持三级以上菜单缓存 -->
<template>
<section class="app-main">
<transition name="fade-transform" mode="out-in">
<keep-alive :max="20" :exclude="notCacheName">
<keep-alive :include="cachedViews">
<router-view :key="key" />
</keep-alive>
</transition>
@ -10,119 +9,17 @@
</template>
<script>
import Global from "@/layout/components/global.js";
export default {
name: 'AppMain',
computed: {
notCacheName() {
var visitedViews = this.$store.state.tagsView.visitedViews;
var noCacheViews = [];
Object.keys(visitedViews).some((index) => {
if (visitedViews[index].meta.noCache) {
noCacheViews.push(visitedViews[index].name);
}
});
return noCacheViews;
cachedViews() {
return this.$store.state.tagsView.cachedViews
},
key() {
return this.$route.path;
},
},
mounted() {
//
Global.$on("removeCache", (name, view) => {
this.removeCache(name, view);
});
},
methods: {
// keep-aliveVnode
getVnode() {
//
if (this.$children.length == 0) return false;
let vnode;
for (let item of this.$children) {
// datakeykeep-alivekeyrouter-viewkey
if (item.$vnode.data.key) {
vnode = item.$vnode;
break;
}
}
return vnode ? vnode : false;
},
// keep-alive
removeCache(name, view = {}) {
let vnode = this.getVnode();
if (!vnode) return false;
let componentInstance = vnode.parent.componentInstance;
// key
let keyStart = vnode.key.split("/")[0];
let thisKey = `${keyStart}${view.fullPath}`;
let regKey = `${keyStart}${view.path}`;
this[name]({ componentInstance, thisKey, regKey });
},
//
closeOthersTags({ componentInstance, thisKey }) {
Object.keys(componentInstance.cache).forEach((key, index) => {
if (key != thisKey) {
// (key)
if (componentInstance.cache[key]) {
componentInstance.cache[key].componentInstance.$destroy();
}
//
delete componentInstance.cache[key];
// keykey
componentInstance.keys.splice(index, 1);
}
});
},
//
closeAllTags({ componentInstance }) {
//
Object.keys(componentInstance.cache).forEach((key) => {
if (componentInstance.cache[key]) {
componentInstance.cache[key].componentInstance.$destroy();
}
});
//
componentInstance.cache = {};
// keykey
componentInstance.keys = [];
},
//
closeSelectedTag({ componentInstance, regKey }) {
let reg = new RegExp(`^${regKey}`);
Object.keys(componentInstance.cache).forEach((key, i) => {
if (reg.test(key)) {
//
if (componentInstance.cache[key]) {
componentInstance.cache[key].componentInstance.$destroy();
}
//
delete componentInstance.cache[key];
// keykey
componentInstance.keys.splice(i, 1);
}
});
},
//
refreshSelectedTag({ componentInstance, thisKey }) {
Object.keys(componentInstance.cache).forEach((key, index) => {
if (null != thisKey && key.replace("/redirect", "") == thisKey) {
// 1 (key)
if (componentInstance.cache[key]) {
componentInstance.cache[key].componentInstance.$destroy();
}
// 2
delete componentInstance.cache[key];
// 3 keykey
componentInstance.keys.splice(index, 1);
}
});
},
},
};
return this.$route.path
}
}
}
</script>
<style lang="scss" scoped>
@ -134,7 +31,7 @@ export default {
overflow: hidden;
}
.fixed-header + .app-main {
.fixed-header+.app-main {
padding-top: 50px;
}
@ -144,7 +41,7 @@ export default {
min-height: calc(100vh - 84px);
}
.fixed-header + .app-main {
.fixed-header+.app-main {
padding-top: 84px;
}
}

@ -13,7 +13,7 @@
mode="vertical"
>
<sidebar-item
v-for="(route, index) in permission_routes"
v-for="(route, index) in sidebarRouters"
:key="route.path + index"
:item="route"
:base-path="route.path"
@ -33,7 +33,7 @@ export default {
components: { SidebarItem, Logo },
computed: {
...mapState(["settings"]),
...mapGetters(["permission_routes", "sidebar"]),
...mapGetters(["sidebarRouters", "sidebar"]),
activeMenu() {
const route = this.$route;
const { meta, path } = route;

@ -82,7 +82,7 @@ export default {
position: relative;
overflow: hidden;
width: 100%;
/deep/ {
::v-deep {
.el-scrollbar__bar {
bottom: 0px;
}

@ -29,7 +29,6 @@
<script>
import ScrollPane from './ScrollPane'
import path from 'path'
import Global from "@/layout/components/global.js";
export default {
components: { ScrollPane },
@ -145,7 +144,6 @@ export default {
})
})
})
Global.$emit("removeCache", "refreshSelectedTag", this.selectedTag);
},
closeSelectedTag(view) {
this.$store.dispatch('tagsView/delView', view).then(({ visitedViews }) => {
@ -153,14 +151,12 @@ export default {
this.toLastView(visitedViews, view)
}
})
Global.$emit("removeCache", "closeSelectedTag", view);
},
closeOthersTags() {
this.$router.push(this.selectedTag)
this.$store.dispatch('tagsView/delOthersViews', this.selectedTag).then(() => {
this.moveToCurrentTag()
})
Global.$emit("removeCache", "closeOthersTags", this.selectedTag);
},
closeAllTags(view) {
this.$store.dispatch('tagsView/delAllViews').then(({ visitedViews }) => {
@ -169,7 +165,6 @@ export default {
}
this.toLastView(visitedViews, view)
})
Global.$emit("removeCache", "closeAllTags");
},
toLastView(visitedViews, view) {
const latestView = visitedViews.slice(-1)[0]

@ -2,8 +2,6 @@ import Vue from 'vue'
import Cookies from 'js-cookie'
import 'normalize.css/normalize.css' // a modern alternative to CSS resets
import Element from 'element-ui'
import './assets/styles/element-variables.scss'
@ -21,8 +19,11 @@ import { getDicts } from "@/api/system/dict/data";
import { getConfigKey } from "@/api/system/config";
import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, handleTree } from "@/utils/ruoyi";
import Pagination from "@/components/Pagination";
//自定义表格工具扩展
// 自定义表格工具扩展
import RightToolbar from "@/components/RightToolbar"
// 代码高亮插件
import hljs from 'highlight.js'
import 'highlight.js/styles/github-gist.css'
// 全局方法挂载
Vue.prototype.getDicts = getDicts
@ -52,6 +53,7 @@ Vue.component('Pagination', Pagination)
Vue.component('RightToolbar', RightToolbar)
Vue.use(permission)
Vue.use(hljs.vuePlugin);
/**
* If you don't want to use mock-server

@ -23,28 +23,18 @@ router.beforeEach((to, from, next) => {
// 拉取user_info
const roles = res.roles
store.dispatch('GenerateRoutes', { roles }).then(accessRoutes => {
// 测试 默认静态页面
// store.dispatch('permission/generateRoutes', { roles }).then(accessRoutes => {
// 根据roles权限生成可访问的路由表
router.addRoutes(accessRoutes) // 动态添加可访问路由表
next({ ...to, replace: true }) // hack方法 确保addRoutes已完成
})
})
.catch(err => {
store.dispatch('FedLogOut').then(() => {
}).catch(err => {
store.dispatch('LogOut').then(() => {
Message.error(err)
next({ path: '/' })
})
})
} else {
next()
// 没有动态改变权限的需求可直接next() 删除下方权限判断 ↓
// if (hasPermission(store.getters.roles, to.meta.roles)) {
// next()
// } else {
// next({ path: '/401', replace: true, query: { noGoBack: true }})
// }
// 可删 ↑
}
}
} else {

@ -10,6 +10,7 @@ const getters = {
introduction: state => state.user.introduction,
roles: state => state.user.roles,
permissions: state => state.user.permissions,
permission_routes: state => state.permission.routes
permission_routes: state => state.permission.routes,
sidebarRouters:state => state.permission.sidebarRouters,
}
export default getters

@ -6,13 +6,17 @@ import ParentView from '@/components/ParentView';
const permission = {
state: {
routes: [],
addRoutes: []
addRoutes: [],
sidebarRouters: []
},
mutations: {
SET_ROUTES: (state, routes) => {
state.addRoutes = routes
state.routes = constantRoutes.concat(routes)
}
},
SET_SIDEBAR_ROUTERS: (state, routers) => {
state.sidebarRouters = routers
},
},
actions: {
// 生成路由
@ -20,10 +24,14 @@ const permission = {
return new Promise(resolve => {
// 向后端请求路由数据
getRouters().then(res => {
const accessedRoutes = filterAsyncRouter(res.data)
accessedRoutes.push({ path: '*', redirect: '/404', hidden: true })
commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes)
const sdata = JSON.parse(JSON.stringify(res.data))
const rdata = JSON.parse(JSON.stringify(res.data))
const sidebarRoutes = filterAsyncRouter(sdata)
const rewriteRoutes = filterAsyncRouter(rdata, true)
rewriteRoutes.push({ path: '*', redirect: '/404', hidden: true })
commit('SET_ROUTES', rewriteRoutes)
commit('SET_SIDEBAR_ROUTERS', sidebarRoutes)
resolve(rewriteRoutes)
})
})
}
@ -31,8 +39,11 @@ const permission = {
}
// 遍历后台传来的路由字符串,转换为组件对象
function filterAsyncRouter(asyncRouterMap) {
function filterAsyncRouter(asyncRouterMap, isRewrite = false) {
return asyncRouterMap.filter(route => {
if (isRewrite && route.children) {
route.children = filterChildren(route.children)
}
if (route.component) {
// Layout ParentView 组件特殊处理
if (route.component === 'Layout') {
@ -44,14 +55,36 @@ function filterAsyncRouter(asyncRouterMap) {
}
}
if (route.children != null && route.children && route.children.length) {
route.children = filterAsyncRouter(route.children)
route.children = filterAsyncRouter(route.children, route, isRewrite)
}
return true
})
}
function filterChildren(childrenMap) {
var children = []
childrenMap.forEach((el, index) => {
if (el.children && el.children.length) {
if (el.component === 'ParentView') {
el.children.forEach(c => {
c.path = el.path + '/' + c.path
if (c.children && c.children.length) {
children = children.concat(filterChildren(c.children, c))
return
}
children.push(c)
})
childrenMap.splice(index, 1)
return
}
}
children = children.concat(el)
})
return children
}
export const loadView = (view) => { // 路由懒加载
return (resolve) => require([`@/views/${view}`], resolve)
return (resolve) => require([`@/views/${view}`], resolve)
}
export default permission

@ -158,7 +158,7 @@ export function handleTree(data, id, parentId, children, rootId) {
export function tansParams(params) {
let result = ''
Object.keys(params).forEach((key) => {
if (!Object.is(params[key], undefined) && !Object.is(params[key], null)) {
if (!Object.is(params[key], undefined) && !Object.is(params[key], null) && !Object.is(JSON.stringify(params[key]), '{}')) {
result += encodeURIComponent(key) + '=' + encodeURIComponent(params[key]) + '&'
}
})

@ -126,15 +126,12 @@ export default {
Cookies.remove("password");
Cookies.remove('rememberMe');
}
this.$store
.dispatch("Login", this.loginForm)
.then(() => {
this.$router.push({ path: this.redirect || "/" });
})
.catch(() => {
this.loading = false;
this.getCode();
});
this.$store.dispatch("Login", this.loginForm).then(() => {
this.$router.push({ path: this.redirect || "/" }).catch(()=>{});
}).catch(() => {
this.loading = false;
this.getCode();
});
}
});
}

@ -421,14 +421,10 @@ export default {
nickName: [
{ required: true, message: "用户昵称不能为空", trigger: "blur" }
],
deptId: [
{ required: true, message: "归属部门不能为空", trigger: "blur" }
],
password: [
{ required: true, message: "用户密码不能为空", trigger: "blur" }
],
email: [
{ required: true, message: "邮箱地址不能为空", trigger: "blur" },
{
type: "email",
message: "'请输入正确的邮箱地址",
@ -436,7 +432,6 @@ export default {
}
],
phonenumber: [
{ required: true, message: "手机号码不能为空", trigger: "blur" },
{
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
message: "请输入正确的手机号码",

@ -18,7 +18,6 @@
<script>
import { updateUserPwd } from "@/api/system/user";
import Global from "@/layout/components/global.js";
export default {
data() {
@ -65,7 +64,6 @@ export default {
});
},
close() {
Global.$emit("removeCache", "closeSelectedTag", this.$route);
this.$store.dispatch("tagsView/delView", this.$route);
this.$router.push({ path: "/index" });
}

@ -24,7 +24,6 @@
<script>
import { updateUserProfile } from "@/api/system/user";
import Global from "@/layout/components/global.js";
export default {
props: {
@ -69,7 +68,6 @@ export default {
});
},
close() {
Global.$emit("removeCache", "closeSelectedTag", this.$route);
this.$store.dispatch("tagsView/delView", this.$route);
this.$router.push({ path: "/index" });
}

@ -127,7 +127,6 @@
import { getGenTable, updateGenTable } from "@/api/tool/gen";
import { optionselect as getDictOptionselect } from "@/api/system/dict/type";
import { listMenu as getMenuTreeselect } from "@/api/system/menu";
import Global from "@/layout/components/global.js";
import basicInfoForm from "./basicInfoForm";
import genInfoForm from "./genInfoForm";
import Sortable from 'sortablejs'
@ -208,7 +207,6 @@ export default {
},
/** 关闭按钮 */
close() {
Global.$emit("removeCache", "closeSelectedTag", this.$route);
this.$store.dispatch("tagsView/delView", this.$route);
this.$router.push({ path: "/tool/gen", query: { t: Date.now()}})
}

@ -1,320 +1,321 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="表名称" prop="tableName">
<el-input
v-model="queryParams.tableName"
placeholder="请输入表名称"
clearable
size="small"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="表描述" prop="tableComment">
<el-input
v-model="queryParams.tableComment"
placeholder="请输入表描述"
clearable
size="small"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="创建时间">
<el-date-picker
v-model="dateRange"
size="small"
style="width: 240px"
value-format="yyyy-MM-dd"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
icon="el-icon-download"
size="mini"
@click="handleGenTable"
v-hasPermi="['tool:gen:code']"
>生成</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="info"
icon="el-icon-upload"
size="mini"
@click="openImportTable"
v-hasPermi="['tool:gen:import']"
>导入</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
icon="el-icon-edit"
size="mini"
:disabled="single"
@click="handleEditTable"
v-hasPermi="['tool:gen:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['tool:gen:remove']"
>删除</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="tableList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column label="序号" type="index" width="50" align="center">
<template slot-scope="scope">
<span>{{(queryParams.pageNum - 1) * queryParams.pageSize + scope.$index + 1}}</span>
</template>
</el-table-column>
<el-table-column
label="表名称"
align="center"
prop="tableName"
:show-overflow-tooltip="true"
width="130"
/>
<el-table-column
label="表描述"
align="center"
prop="tableComment"
:show-overflow-tooltip="true"
width="130"
/>
<el-table-column
label="实体"
align="center"
prop="className"
:show-overflow-tooltip="true"
width="130"
/>
<el-table-column label="创建时间" align="center" prop="createTime" width="160" />
<el-table-column label="更新时间" align="center" prop="updateTime" width="160" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
type="text"
size="small"
icon="el-icon-view"
@click="handlePreview(scope.row)"
v-hasPermi="['tool:gen:preview']"
>预览</el-button>
<el-button
type="text"
size="small"
icon="el-icon-edit"
@click="handleEditTable(scope.row)"
v-hasPermi="['tool:gen:edit']"
>编辑</el-button>
<el-button
type="text"
size="small"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['tool:gen:remove']"
>删除</el-button>
<el-button
type="text"
size="small"
icon="el-icon-refresh"
@click="handleSynchDb(scope.row)"
v-hasPermi="['tool:gen:edit']"
>同步</el-button>
<el-button
type="text"
size="small"
icon="el-icon-download"
@click="handleGenTable(scope.row)"
v-hasPermi="['tool:gen:code']"
>生成代码</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 预览界面 -->
<el-dialog :title="preview.title" :visible.sync="preview.open" width="80%" top="5vh" append-to-body>
<el-tabs v-model="preview.activeName">
<el-tab-pane style="overflow-x: scroll;"
v-for="(value, key) in preview.data"
:label="key.substring(key.lastIndexOf('/')+1,key.indexOf('.vm'))"
:name="key.substring(key.lastIndexOf('/')+1,key.indexOf('.vm'))"
:key="key"
>
<pre>{{ value }}</pre>
</el-tab-pane>
</el-tabs>
</el-dialog>
<import-table ref="import" @ok="handleQuery" />
</div>
</template>
<script>
import { listTable, previewTable, delTable, genCode, synchDb } from "@/api/tool/gen";
import importTable from "./importTable";
import { downLoadZip } from "@/utils/zipdownload";
export default {
name: "Gen",
components: { importTable },
data() {
return {
//
loading: true,
//
uniqueId: "",
//
ids: [],
//
tableNames: [],
//
single: true,
//
multiple: true,
//
showSearch: true,
//
total: 0,
//
tableList: [],
//
dateRange: "",
//
queryParams: {
pageNum: 1,
pageSize: 10,
tableName: undefined,
tableComment: undefined
},
//
preview: {
open: false,
title: "代码预览",
data: {},
activeName: "domain.java"
}
};
},
created() {
this.getList();
},
activated() {
const time = this.$route.query.t;
if (time != null && time != this.uniqueId) {
this.uniqueId = time;
this.resetQuery();
}
},
methods: {
/** 查询表集合 */
getList() {
this.loading = true;
listTable(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
this.tableList = response.rows;
this.total = response.total;
this.loading = false;
}
);
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 生成代码操作 */
handleGenTable(row) {
const tableNames = row.tableName || this.tableNames;
if (tableNames == "") {
this.msgError("请选择要生成的数据");
return;
}
if(row.genType === "1") {
genCode(row.tableName).then(response => {
this.msgSuccess("成功生成到自定义路径:" + row.genPath);
});
} else {
downLoadZip("/code/gen/batchGenCode?tables=" + tableNames, "ruoyi");
}
},
/** 同步数据库操作 */
handleSynchDb(row) {
const tableName = row.tableName;
this.$confirm('确认要强制同步"' + tableName + '"表结构吗?', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(function() {
return synchDb(tableName);
}).then(() => {
this.msgSuccess("同步成功");
})
},
/** 打开导入表弹窗 */
openImportTable() {
this.$refs.import.show();
},
/** 重置按钮操作 */
resetQuery() {
this.dateRange = [];
this.resetForm("queryForm");
this.handleQuery();
},
/** 预览按钮 */
handlePreview(row) {
previewTable(row.tableId).then(response => {
this.preview.data = response.data;
this.preview.open = true;
});
},
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.tableId);
this.tableNames = selection.map(item => item.tableName);
this.single = selection.length != 1;
this.multiple = !selection.length;
},
/** 修改按钮操作 */
handleEditTable(row) {
const tableId = row.tableId || this.ids[0];
this.$router.push("/gen/edit/" + tableId);
},
/** 删除按钮操作 */
handleDelete(row) {
const tableIds = row.tableId || this.ids;
this.$confirm('是否确认删除表编号为"' + tableIds + '"的数据项?', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(function() {
return delTable(tableIds);
}).then(() => {
this.getList();
this.msgSuccess("删除成功");
})
}
}
};
</script>
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="表名称" prop="tableName">
<el-input
v-model="queryParams.tableName"
placeholder="请输入表名称"
clearable
size="small"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="表描述" prop="tableComment">
<el-input
v-model="queryParams.tableComment"
placeholder="请输入表描述"
clearable
size="small"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="创建时间">
<el-date-picker
v-model="dateRange"
size="small"
style="width: 240px"
value-format="yyyy-MM-dd"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
icon="el-icon-download"
size="mini"
@click="handleGenTable"
v-hasPermi="['tool:gen:code']"
>生成</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="info"
icon="el-icon-upload"
size="mini"
@click="openImportTable"
v-hasPermi="['tool:gen:import']"
>导入</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
icon="el-icon-edit"
size="mini"
:disabled="single"
@click="handleEditTable"
v-hasPermi="['tool:gen:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['tool:gen:remove']"
>删除</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="tableList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column label="序号" type="index" width="50" align="center">
<template slot-scope="scope">
<span>{{(queryParams.pageNum - 1) * queryParams.pageSize + scope.$index + 1}}</span>
</template>
</el-table-column>
<el-table-column
label="表名称"
align="center"
prop="tableName"
:show-overflow-tooltip="true"
width="120"
/>
<el-table-column
label="表描述"
align="center"
prop="tableComment"
:show-overflow-tooltip="true"
width="120"
/>
<el-table-column
label="实体"
align="center"
prop="className"
:show-overflow-tooltip="true"
width="120"
/>
<el-table-column label="创建时间" align="center" prop="createTime" width="160" />
<el-table-column label="更新时间" align="center" prop="updateTime" width="160" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
type="text"
size="small"
icon="el-icon-view"
@click="handlePreview(scope.row)"
v-hasPermi="['tool:gen:preview']"
>预览</el-button>
<el-button
type="text"
size="small"
icon="el-icon-edit"
@click="handleEditTable(scope.row)"
v-hasPermi="['tool:gen:edit']"
>编辑</el-button>
<el-button
type="text"
size="small"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['tool:gen:remove']"
>删除</el-button>
<el-button
type="text"
size="small"
icon="el-icon-refresh"
@click="handleSynchDb(scope.row)"
v-hasPermi="['tool:gen:edit']"
>同步</el-button>
<el-button
type="text"
size="small"
icon="el-icon-download"
@click="handleGenTable(scope.row)"
v-hasPermi="['tool:gen:code']"
>生成代码</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 预览界面 -->
<el-dialog :title="preview.title" :visible.sync="preview.open" width="80%" top="5vh" append-to-body>
<el-tabs v-model="preview.activeName">
<el-tab-pane
v-for="(value, key) in preview.data"
:label="key.substring(key.lastIndexOf('/')+1,key.indexOf('.vm'))"
:name="key.substring(key.lastIndexOf('/')+1,key.indexOf('.vm'))"
:key="key"
>
<highlightjs autodetect :code="value" />
</el-tab-pane>
</el-tabs>
</el-dialog>
<import-table ref="import" @ok="handleQuery" />
</div>
</template>
<script>
import { listTable, previewTable, delTable, genCode, synchDb } from "@/api/tool/gen";
import importTable from "./importTable";
import { downLoadZip } from "@/utils/zipdownload";
export default {
name: "Gen",
components: { importTable },
data() {
return {
//
loading: true,
//
uniqueId: "",
//
ids: [],
//
tableNames: [],
//
single: true,
//
multiple: true,
//
showSearch: true,
//
total: 0,
//
tableList: [],
//
dateRange: "",
//
queryParams: {
pageNum: 1,
pageSize: 10,
tableName: undefined,
tableComment: undefined
},
//
preview: {
open: false,
title: "代码预览",
data: {},
activeName: "domain.java"
}
};
},
created() {
this.getList();
},
activated() {
const time = this.$route.query.t;
if (time != null && time != this.uniqueId) {
this.uniqueId = time;
this.resetQuery();
}
},
methods: {
/** 查询表集合 */
getList() {
this.loading = true;
listTable(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
this.tableList = response.rows;
this.total = response.total;
this.loading = false;
}
);
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 生成代码操作 */
handleGenTable(row) {
const tableNames = row.tableName || this.tableNames;
if (tableNames == "") {
this.msgError("请选择要生成的数据");
return;
}
if(row.genType === "1") {
genCode(row.tableName).then(response => {
this.msgSuccess("成功生成到自定义路径:" + row.genPath);
});
} else {
downLoadZip("/code/gen/batchGenCode?tables=" + tableNames, "ruoyi");
}
},
/** 同步数据库操作 */
handleSynchDb(row) {
const tableName = row.tableName;
this.$confirm('确认要强制同步"' + tableName + '"表结构吗?', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(function() {
return synchDb(tableName);
}).then(() => {
this.msgSuccess("同步成功");
})
},
/** 打开导入表弹窗 */
openImportTable() {
this.$refs.import.show();
},
/** 重置按钮操作 */
resetQuery() {
this.dateRange = [];
this.resetForm("queryForm");
this.handleQuery();
},
/** 预览按钮 */
handlePreview(row) {
previewTable(row.tableId).then(response => {
this.preview.data = response.data;
this.preview.open = true;
});
},
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.tableId);
this.tableNames = selection.map(item => item.tableName);
this.single = selection.length != 1;
this.multiple = !selection.length;
},
/** 修改按钮操作 */
handleEditTable(row) {
const tableId = row.tableId || this.ids[0];
this.$router.push("/gen/edit/" + tableId);
},
/** 删除按钮操作 */
handleDelete(row) {
const tableIds = row.tableId || this.ids;
this.$confirm('是否确认删除表编号为"' + tableIds + '"的数据项?', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(function() {
return delTable(tableIds);
}).then(() => {
this.getList();
this.msgSuccess("删除成功");
})
}
}
};
</script>

@ -107,7 +107,11 @@ module.exports = {
}
}
})
config.optimization.runtimeChunk('single')
config.optimization.runtimeChunk('single'),
{
from: path.resolve(__dirname, './public/robots.txt'), //防爬虫文件
to: './', //到根目录下
}
}
)
}

Loading…
Cancel
Save