疯狂的狮子li
2020-06-01 5f866906d1d14b2b3f2123a49270458c12fecf7e
Merge branch 'master' of https://gitee.com/y_project/RuoYi-Vue

 Conflicts:
 ruoyi-ui/package.json
 ruoyi-ui/src/api/system/config.js
 ruoyi-ui/src/api/system/dict/data.js
 ruoyi-ui/src/api/system/dict/type.js
 ruoyi-ui/src/assets/styles/element-variables.scss
 ruoyi-ui/src/components/SvgIcon/index.vue
 ruoyi-ui/src/layout/components/Sidebar/Link.vue
 ruoyi-ui/src/layout/components/Sidebar/index.vue
 ruoyi-ui/src/layout/components/TagsView/ScrollPane.vue
 ruoyi-ui/src/layout/components/TagsView/index.vue
 ruoyi-ui/src/views/monitor/job/index.vue
 ruoyi-ui/src/views/system/config/index.vue
 ruoyi-ui/src/views/system/dept/index.vue
 ruoyi-ui/src/views/system/dict/data.vue
 ruoyi-ui/src/views/system/dict/index.vue
 ruoyi-ui/src/views/system/menu/index.vue
 ruoyi-ui/src/views/system/notice/index.vue
 ruoyi-ui/src/views/system/post/index.vue
 ruoyi-ui/src/views/system/role/index.vue
 ruoyi-ui/src/views/system/user/index.vue
 ruoyi-ui/src/views/system/user/profile/resetPwd.vue
 ruoyi-ui/src/views/system/user/profile/userAvatar.vue
 ruoyi-ui/src/views/system/user/profile/userInfo.vue
 ruoyi-ui/vue.config.js
 ruoyi/pom.xml
 ruoyi/src/main/java/com/ruoyi/common/constant/Constants.java
 ruoyi/src/main/java/com/ruoyi/common/utils/StringUtils.java
 ruoyi/src/main/java/com/ruoyi/project/system/controller/SysConfigController.java
 ruoyi/src/main/java/com/ruoyi/project/system/controller/SysDictDataController.java
 ruoyi/src/main/java/com/ruoyi/project/system/controller/SysDictTypeController.java
 ruoyi/src/main/java/com/ruoyi/project/system/service/ISysConfigService.java
 ruoyi/src/main/java/com/ruoyi/project/system/service/ISysDictDataService.java
 ruoyi/src/main/java/com/ruoyi/project/system/service/ISysDictTypeService.java
 ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysConfigServiceImpl.java
 ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysDictDataServiceImpl.java
 ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysDictTypeServiceImpl.java
 ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysMenuServiceImpl.java
 ruoyi/src/main/resources/application.yml
 ruoyi/src/main/resources/vm/sql/sql.vm
 ruoyi/src/main/resources/vm/vue/index.vue.vm
已修改42个文件
已添加1个文件
1223 ■■■■■ 文件已修改
README.md 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/package.json 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/api/system/config.js 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/api/system/dict/data.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/api/system/dict/type.js 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/assets/styles/element-variables.scss 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/components/SvgIcon/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/layout/components/Sidebar/Link.vue 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/layout/components/Sidebar/index.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/layout/components/TagsView/ScrollPane.vue 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/layout/components/TagsView/index.vue 577 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/monitor/job/index.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/config/index.vue 25 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/dept/index.vue 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/dict/data.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/dict/index.vue 25 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/menu/index.vue 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/notice/index.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/post/index.vue 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/role/index.vue 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/user/index.vue 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/user/profile/resetPwd.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/user/profile/userAvatar.vue 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/user/profile/userInfo.vue 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/vue.config.js 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi/pom.xml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi/src/main/java/com/ruoyi/common/constant/Constants.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi/src/main/java/com/ruoyi/common/utils/DictUtils.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi/src/main/java/com/ruoyi/common/utils/StringUtils.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi/src/main/java/com/ruoyi/project/system/controller/SysConfigController.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi/src/main/java/com/ruoyi/project/system/controller/SysDictDataController.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi/src/main/java/com/ruoyi/project/system/controller/SysDictTypeController.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi/src/main/java/com/ruoyi/project/system/service/ISysConfigService.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi/src/main/java/com/ruoyi/project/system/service/ISysDictDataService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi/src/main/java/com/ruoyi/project/system/service/ISysDictTypeService.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysConfigServiceImpl.java 87 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysDictDataServiceImpl.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysDictTypeServiceImpl.java 90 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysMenuServiceImpl.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi/src/main/resources/application.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi/src/main/resources/vm/sql/sql.vm 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi/src/main/resources/vm/vue/index-tree.vue.vm 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi/src/main/resources/vm/vue/index.vue.vm 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
README.md
@@ -12,6 +12,7 @@
* æƒé™è®¤è¯ä½¿ç”¨Jwt,支持多终端认证系统。
* æ”¯æŒåŠ è½½åŠ¨æ€æƒé™èœå•ï¼Œå¤šæ–¹å¼è½»æ¾æƒé™æŽ§åˆ¶ã€‚
* é«˜æ•ˆçŽ‡å¼€å‘ï¼Œä½¿ç”¨ä»£ç ç”Ÿæˆå™¨å¯ä»¥ä¸€é”®ç”Ÿæˆå‰åŽç«¯ä»£ç ã€‚
* æä¾›äº†ä¸€ä¸ªOracle版本[RuoYi-Vue-Oracle](https://github.com/yangzongzhuan/RuoYi-Vue-Oracle),保持同步更新。
* æ„Ÿè°¢[Vue-Element-Admin](https://github.com/PanJiaChen/vue-element-admin),[eladmin-web](https://gitee.com/elunez/eladmin-web?_from=gitee_search)。
* ä¸åˆ†ç¦»ç‰ˆæœ¬ï¼Œè¯·ç§»æ­¥[RuoYi](https://gitee.com/y_project/RuoYi),微服务版本,请移步[RuoYi-Cloud](https://gitee.com/y_project/RuoYi-Cloud)
* é˜¿é‡Œäº‘优惠券:[点我进入](https://www.aliyun.com/minisite/goods?userCode=brki8iof&share_source=copy_link),腾讯云优惠券:[点我领取](https://cloud.tencent.com/redirect.php?redirect=1025&cps_key=198c8df2ed259157187173bc7f4f32fd&from=console)  
ruoyi-ui/package.json
@@ -1,11 +1,11 @@
{
  "name": "ruoyi",
  "version": "2.2.0",
  "version": "2.3.0",
  "description": "若依管理系统",
  "author": "若依",
  "license": "MIT",
  "scripts": {
    "dev": "vue-cli-service serve",
    "dev": "vue-cli-service serve --open",
    "build:prod": "vue-cli-service build",
    "build:stage": "vue-cli-service build --mode staging",
    "preview": "node build/index.js --preview",
ruoyi-ui/src/api/system/config.js
@@ -51,6 +51,14 @@
  })
}
// æ¸…理参数缓存
export function clearCache() {
  return request({
    url: '/system/config/clearCache',
    method: 'delete'
  })
}
// å¯¼å‡ºå‚æ•°
export function exportConfig(query) {
  return request({
@@ -58,4 +66,4 @@
    method: 'get',
    params: query
  })
}
}
ruoyi-ui/src/api/system/dict/data.js
@@ -20,7 +20,7 @@
// æ ¹æ®å­—典类型查询字典数据信息
export function getDicts(dictType) {
  return request({
    url: '/system/dict/data/dictType/' + dictType,
    url: '/system/dict/data/type/' + dictType,
    method: 'get'
  })
}
ruoyi-ui/src/api/system/dict/type.js
@@ -43,6 +43,14 @@
  })
}
// æ¸…理参数缓存
export function clearCache() {
  return request({
    url: '/system/dict/type/clearCache',
    method: 'delete'
  })
}
// å¯¼å‡ºå­—典类型
export function exportType(query) {
  return request({
@@ -58,4 +66,4 @@
    url: '/system/dict/type/optionselect',
    method: 'get'
  })
}
}
ruoyi-ui/src/assets/styles/element-variables.scss
@@ -6,7 +6,7 @@
/* theme color */
$--color-primary: #1890ff;
$--color-success: #13ce66;
$--color-warning: #FFBA00;
$--color-warning: #ffba00;
$--color-danger: #ff4949;
// $--color-info: #1E1E1E;
ruoyi-ui/src/components/SvgIcon/index.vue
@@ -1,7 +1,7 @@
<template>
  <div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners" />
  <svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
    <use :href="iconName" />
    <use :xlink:href="iconName" />
  </svg>
</template>
ruoyi-ui/src/layout/components/Sidebar/Link.vue
@@ -1,7 +1,5 @@
<template>
  <!-- eslint-disable vue/require-component-is -->
  <component v-bind="linkProps(to)">
  <component :is="type" v-bind="linkProps(to)">
    <slot />
  </component>
</template>
@@ -16,19 +14,28 @@
      required: true
    }
  },
  computed: {
    isExternal() {
      return isExternal(this.to)
    },
    type() {
      if (this.isExternal) {
        return 'a'
      }
      return 'router-link'
    }
  },
  methods: {
    linkProps(url) {
      if (isExternal(url)) {
    linkProps(to) {
      if (this.isExternal) {
        return {
          is: 'a',
          href: url,
          href: to,
          target: '_blank',
          rel: 'noopener'
        }
      }
      return {
        is: 'router-link',
        to: url
        to: to
      }
    }
  }
ruoyi-ui/src/layout/components/Sidebar/index.vue
@@ -13,8 +13,8 @@
                mode="vertical"
            >
                <sidebar-item
                    v-for="route in permission_routes"
                    :key="route.path"
                    v-for="(route, index) in permission_routes"
                    :key="route.path  + index"
                    :item="route"
                    :base-path="route.path"
                />
ruoyi-ui/src/layout/components/TagsView/ScrollPane.vue
@@ -19,12 +19,21 @@
      return this.$refs.scrollContainer.$refs.wrap
    }
  },
  mounted() {
    this.scrollWrapper.addEventListener('scroll', this.emitScroll, true)
  },
  beforeDestroy() {
    this.scrollWrapper.removeEventListener('scroll', this.emitScroll)
  },
  methods: {
    handleScroll(e) {
      const eventDelta = e.wheelDelta || -e.deltaY * 40
      const $scrollWrapper = this.scrollWrapper
      $scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4
    },
    emitScroll() {
      this.$emit('scroll')
    },
    moveToTarget(currentTag) {
      const $container = this.$refs.scrollContainer.$el
      const $containerWidth = $container.offsetWidth
ruoyi-ui/src/layout/components/TagsView/index.vue
@@ -1,318 +1,303 @@
<template>
    <div id="tags-view-container" class="tags-view-container">
        <scroll-pane ref="scrollPane" class="tags-view-wrapper">
            <router-link
                v-for="tag in visitedViews"
                ref="tag"
                :key="tag.path"
                :class="isActive(tag)?'active':''"
                :to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
                tag="span"
                class="tags-view-item"
                :style="activeStyle(tag)"
                @click.middle.native="!isAffix(tag)?closeSelectedTag(tag):''"
                @contextmenu.prevent.native="openMenu(tag,$event)"
            >
                {{ tag.title }}
                <span
                    v-if="!isAffix(tag)"
                    class="el-icon-close"
                    @click.prevent.stop="closeSelectedTag(tag)"
                />
            </router-link>
        </scroll-pane>
        <ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu">
            <li @click="refreshSelectedTag(selectedTag)">刷新页面</li>
            <li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">关闭当前</li>
            <li @click="closeOthersTags">关闭其他</li>
            <li @click="closeAllTags(selectedTag)">关闭所有</li>
        </ul>
    </div>
  <div id="tags-view-container" class="tags-view-container">
    <scroll-pane ref="scrollPane" class="tags-view-wrapper" @scroll="handleScroll">
      <router-link
        v-for="tag in visitedViews"
        ref="tag"
        :key="tag.path"
        :class="isActive(tag)?'active':''"
        :to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
        tag="span"
        class="tags-view-item"
        :style="activeStyle(tag)"
        @click.middle.native="!isAffix(tag)?closeSelectedTag(tag):''"
        @contextmenu.prevent.native="openMenu(tag,$event)"
      >
        {{ tag.title }}
        <span v-if="!isAffix(tag)" class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)" />
      </router-link>
    </scroll-pane>
    <ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu">
      <li @click="refreshSelectedTag(selectedTag)">刷新页面</li>
      <li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">关闭当前</li>
      <li @click="closeOthersTags">关闭其他</li>
      <li @click="closeAllTags(selectedTag)">关闭所有</li>
    </ul>
  </div>
</template>
<script>
import ScrollPane from "./ScrollPane";
import path from "path";
import ScrollPane from './ScrollPane'
import path from 'path'
export default {
    components: { ScrollPane },
    data() {
        return {
            visible: false,
            top: 0,
            left: 0,
            selectedTag: {},
            affixTags: []
        };
    },
    computed: {
        visitedViews() {
            return this.$store.state.tagsView.visitedViews;
        },
        routes() {
            return this.$store.state.permission.routes;
        },
        theme() {
            return this.$store.state.settings.theme;
        }
    },
    watch: {
        $route() {
            this.addTags();
            this.moveToCurrentTag();
        },
        visible(value) {
            if (value) {
                document.body.addEventListener("click", this.closeMenu);
            } else {
                document.body.removeEventListener("click", this.closeMenu);
            }
        }
    },
    mounted() {
        this.initTags();
        this.addTags();
    },
    methods: {
        isActive(route) {
            return route.path === this.$route.path;
        },
        activeStyle(tag) {
            if (!this.isActive(tag)) return {};
            return {
                "background-color": this.theme,
                "border-color": this.theme
            };
        },
        isAffix(tag) {
            return tag.meta && tag.meta.affix;
        },
        filterAffixTags(routes, basePath = "/") {
            let tags = [];
            routes.forEach(route => {
                if (route.meta && route.meta.affix) {
                    const tagPath = path.resolve(basePath, route.path);
                    tags.push({
                        fullPath: tagPath,
                        path: tagPath,
                        name: route.name,
                        meta: { ...route.meta }
                    });
                }
                if (route.children) {
                    const tempTags = this.filterAffixTags(
                        route.children,
                        route.path
                    );
                    if (tempTags.length >= 1) {
                        tags = [...tags, ...tempTags];
                    }
                }
            });
            return tags;
        },
        initTags() {
            const affixTags = (this.affixTags = this.filterAffixTags(
                this.routes
            ));
            for (const tag of affixTags) {
                // Must have tag name
                if (tag.name) {
                    this.$store.dispatch("tagsView/addVisitedView", tag);
                }
            }
        },
        addTags() {
            const { name } = this.$route;
            if (name) {
                this.$store.dispatch("tagsView/addView", this.$route);
            }
            return false;
        },
        moveToCurrentTag() {
            const tags = this.$refs.tag;
            this.$nextTick(() => {
                for (const tag of tags) {
                    if (tag.to.path === this.$route.path) {
                        this.$refs.scrollPane.moveToTarget(tag);
                        // when query is different then update
                        if (tag.to.fullPath !== this.$route.fullPath) {
                            this.$store.dispatch(
                                "tagsView/updateVisitedView",
                                this.$route
                            );
                        }
                        break;
                    }
                }
            });
        },
        refreshSelectedTag(view) {
            this.$store.dispatch("tagsView/delCachedView", view).then(() => {
                const { fullPath } = view;
                this.$nextTick(() => {
                    this.$router.replace({
                        path: "/redirect" + fullPath
                    });
                });
            });
        },
        closeSelectedTag(view) {
            this.$store
                .dispatch("tagsView/delView", view)
                .then(({ visitedViews }) => {
                    if (this.isActive(view)) {
                        this.toLastView(visitedViews, view);
                    }
                });
        },
        closeOthersTags() {
            this.$router.push(this.selectedTag);
            this.$store
                .dispatch("tagsView/delOthersViews", this.selectedTag)
                .then(() => {
                    this.moveToCurrentTag();
                });
        },
        closeAllTags(view) {
            this.$store
                .dispatch("tagsView/delAllViews")
                .then(({ visitedViews }) => {
                    if (this.affixTags.some(tag => tag.path === view.path)) {
                        return;
                    }
                    this.toLastView(visitedViews, view);
                });
        },
        toLastView(visitedViews, view) {
            const latestView = visitedViews.slice(-1)[0];
            if (latestView) {
                this.$router.push(latestView.fullPath);
            } else {
                // now the default is to redirect to the home page if there is no tags-view,
                // you can adjust it according to your needs.
                if (view.name === "Dashboard") {
                    // to reload home page
                    this.$router.replace({ path: "/redirect" + view.fullPath });
                } else {
                    this.$router.push("/");
                }
            }
        },
        openMenu(tag, e) {
            const menuMinWidth = 105;
            const offsetLeft = this.$el.getBoundingClientRect().left; // container margin left
            const offsetWidth = this.$el.offsetWidth; // container width
            const maxLeft = offsetWidth - menuMinWidth; // left boundary
            const left = e.clientX - offsetLeft + 15; // 15: margin right
            if (left > maxLeft) {
                this.left = maxLeft;
            } else {
                this.left = left;
            }
            this.top = e.clientY;
            this.visible = true;
            this.selectedTag = tag;
        },
        closeMenu() {
            this.visible = false;
        }
  components: { ScrollPane },
  data() {
    return {
      visible: false,
      top: 0,
      left: 0,
      selectedTag: {},
      affixTags: []
    }
};
  },
  computed: {
    visitedViews() {
      return this.$store.state.tagsView.visitedViews
    },
    routes() {
      return this.$store.state.permission.routes
    },
    theme() {
      return this.$store.state.settings.theme;
    }
  },
  watch: {
    $route() {
      this.addTags()
      this.moveToCurrentTag()
    },
    visible(value) {
      if (value) {
        document.body.addEventListener('click', this.closeMenu)
      } else {
        document.body.removeEventListener('click', this.closeMenu)
      }
    }
  },
  mounted() {
    this.initTags()
    this.addTags()
  },
  methods: {
    isActive(route) {
      return route.path === this.$route.path
    },
    activeStyle(tag) {
      if (!this.isActive(tag)) return {};
      return {
        "background-color": this.theme,
        "border-color": this.theme
      };
    },
    isAffix(tag) {
      return tag.meta && tag.meta.affix
    },
    filterAffixTags(routes, basePath = '/') {
      let tags = []
      routes.forEach(route => {
        if (route.meta && route.meta.affix) {
          const tagPath = path.resolve(basePath, route.path)
          tags.push({
            fullPath: tagPath,
            path: tagPath,
            name: route.name,
            meta: { ...route.meta }
          })
        }
        if (route.children) {
          const tempTags = this.filterAffixTags(route.children, route.path)
          if (tempTags.length >= 1) {
            tags = [...tags, ...tempTags]
          }
        }
      })
      return tags
    },
    initTags() {
      const affixTags = this.affixTags = this.filterAffixTags(this.routes)
      for (const tag of affixTags) {
        // Must have tag name
        if (tag.name) {
          this.$store.dispatch('tagsView/addVisitedView', tag)
        }
      }
    },
    addTags() {
      const { name } = this.$route
      if (name) {
        this.$store.dispatch('tagsView/addView', this.$route)
      }
      return false
    },
    moveToCurrentTag() {
      const tags = this.$refs.tag
      this.$nextTick(() => {
        for (const tag of tags) {
          if (tag.to.path === this.$route.path) {
            this.$refs.scrollPane.moveToTarget(tag)
            // when query is different then update
            if (tag.to.fullPath !== this.$route.fullPath) {
              this.$store.dispatch('tagsView/updateVisitedView', this.$route)
            }
            break
          }
        }
      })
    },
    refreshSelectedTag(view) {
      this.$store.dispatch('tagsView/delCachedView', view).then(() => {
        const { fullPath } = view
        this.$nextTick(() => {
          this.$router.replace({
            path: '/redirect' + fullPath
          })
        })
      })
    },
    closeSelectedTag(view) {
      this.$store.dispatch('tagsView/delView', view).then(({ visitedViews }) => {
        if (this.isActive(view)) {
          this.toLastView(visitedViews, view)
        }
      })
    },
    closeOthersTags() {
      this.$router.push(this.selectedTag)
      this.$store.dispatch('tagsView/delOthersViews', this.selectedTag).then(() => {
        this.moveToCurrentTag()
      })
    },
    closeAllTags(view) {
      this.$store.dispatch('tagsView/delAllViews').then(({ visitedViews }) => {
        if (this.affixTags.some(tag => tag.path === view.path)) {
          return
        }
        this.toLastView(visitedViews, view)
      })
    },
    toLastView(visitedViews, view) {
      const latestView = visitedViews.slice(-1)[0]
      if (latestView) {
        this.$router.push(latestView.fullPath)
      } else {
        // now the default is to redirect to the home page if there is no tags-view,
        // you can adjust it according to your needs.
        if (view.name === 'Dashboard') {
          // to reload home page
          this.$router.replace({ path: '/redirect' + view.fullPath })
        } else {
          this.$router.push('/')
        }
      }
    },
    openMenu(tag, e) {
      const menuMinWidth = 105
      const offsetLeft = this.$el.getBoundingClientRect().left // container margin left
      const offsetWidth = this.$el.offsetWidth // container width
      const maxLeft = offsetWidth - menuMinWidth // left boundary
      const left = e.clientX - offsetLeft + 15 // 15: margin right
      if (left > maxLeft) {
        this.left = maxLeft
      } else {
        this.left = left
      }
      this.top = e.clientY
      this.visible = true
      this.selectedTag = tag
    },
    closeMenu() {
      this.visible = false
    },
    handleScroll() {
      this.closeMenu()
    }
  }
}
</script>
<style lang="scss" scoped>
.tags-view-container {
    height: 34px;
    width: 100%;
  height: 34px;
  width: 100%;
  background: #fff;
  border-bottom: 1px solid #d8dce5;
  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04);
  .tags-view-wrapper {
    .tags-view-item {
      display: inline-block;
      position: relative;
      cursor: pointer;
      height: 26px;
      line-height: 26px;
      border: 1px solid #d8dce5;
      color: #495060;
      background: #fff;
      padding: 0 8px;
      font-size: 12px;
      margin-left: 5px;
      margin-top: 4px;
      &:first-of-type {
        margin-left: 15px;
      }
      &:last-of-type {
        margin-right: 15px;
      }
      &.active {
        background-color: #42b983;
        color: #fff;
        border-color: #42b983;
        &::before {
          content: '';
          background: #fff;
          display: inline-block;
          width: 8px;
          height: 8px;
          border-radius: 50%;
          position: relative;
          margin-right: 2px;
        }
      }
    }
  }
  .contextmenu {
    margin: 0;
    background: #fff;
    border-bottom: 1px solid #d8dce5;
    box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);
    .tags-view-wrapper {
        .tags-view-item {
            display: inline-block;
            position: relative;
            cursor: pointer;
            height: 26px;
            line-height: 26px;
            border: 1px solid #d8dce5;
            color: #495060;
            background: #fff;
            padding: 0 8px;
            font-size: 12px;
            margin-left: 5px;
            margin-top: 4px;
            &:first-of-type {
                margin-left: 15px;
            }
            &:last-of-type {
                margin-right: 15px;
            }
            &.active {
                background-color: #42b983;
                color: #fff;
                border-color: #42b983;
                &::before {
                    content: "";
                    background: #fff;
                    display: inline-block;
                    width: 8px;
                    height: 8px;
                    border-radius: 50%;
                    position: relative;
                    margin-right: 2px;
                }
            }
        }
    z-index: 3000;
    position: absolute;
    list-style-type: none;
    padding: 5px 0;
    border-radius: 4px;
    font-size: 12px;
    font-weight: 400;
    color: #333;
    box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);
    li {
      margin: 0;
      padding: 7px 16px;
      cursor: pointer;
      &:hover {
        background: #eee;
      }
    }
    .contextmenu {
        margin: 0;
        background: #fff;
        z-index: 3000;
        position: absolute;
        list-style-type: none;
        padding: 5px 0;
        border-radius: 4px;
        font-size: 12px;
        font-weight: 400;
        color: #333;
        box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);
        li {
            margin: 0;
            padding: 7px 16px;
            cursor: pointer;
            &:hover {
                background: #eee;
            }
        }
    }
  }
}
</style>
<style lang="scss">
//reset element css of el-icon-close
.tags-view-wrapper {
    .tags-view-item {
        .el-icon-close {
            width: 16px;
            height: 16px;
            vertical-align: 2px;
            border-radius: 50%;
            text-align: center;
            transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
            transform-origin: 100% 50%;
            &:before {
                transform: scale(0.6);
                display: inline-block;
                vertical-align: -3px;
            }
            &:hover {
                background-color: #b4bccc;
                color: #fff;
            }
        }
  .tags-view-item {
    .el-icon-close {
      width: 16px;
      height: 16px;
      vertical-align: 2px;
      border-radius: 50%;
      text-align: center;
      transition: all .3s cubic-bezier(.645, .045, .355, 1);
      transform-origin: 100% 50%;
      &:before {
        transform: scale(.6);
        display: inline-block;
        vertical-align: -3px;
      }
      &:hover {
        background-color: #b4bccc;
        color: #fff;
      }
    }
  }
}
</style>
ruoyi-ui/src/views/monitor/job/index.vue
@@ -438,8 +438,6 @@
                this.msgSuccess("修改成功");
                this.open = false;
                this.getList();
              } else {
                this.msgError(response.msg);
              }
            });
          } else {
@@ -448,8 +446,6 @@
                this.msgSuccess("新增成功");
                this.open = false;
                this.getList();
              } else {
                this.msgError(response.msg);
              }
            });
          }
@@ -485,4 +481,4 @@
    }
  }
};
</script>
</script>
ruoyi-ui/src/views/system/config/index.vue
@@ -88,6 +88,15 @@
          v-hasPermi="['system:config:export']"
        >导出</el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="danger"
          icon="el-icon-refresh"
          size="mini"
          @click="handleClearCache"
          v-hasPermi="['system:config:remove']"
        >清理缓存</el-button>
      </el-col>
    </el-row>
    <el-table v-loading="loading" :data="configList" @selection-change="handleSelectionChange">
@@ -165,7 +174,7 @@
</template>
<script>
import { listConfig, getConfig, delConfig, addConfig, updateConfig, exportConfig } from "@/api/system/config";
import { listConfig, getConfig, delConfig, addConfig, updateConfig, exportConfig, clearCache } from "@/api/system/config";
export default {
  name: "Config",
@@ -296,8 +305,6 @@
                this.msgSuccess("修改成功");
                this.open = false;
                this.getList();
              } else {
                this.msgError(response.msg);
              }
            });
          } else {
@@ -306,8 +313,6 @@
                this.msgSuccess("新增成功");
                this.open = false;
                this.getList();
              } else {
                this.msgError(response.msg);
              }
            });
          }
@@ -340,7 +345,15 @@
        }).then(response => {
          this.download(response.msg);
        }).catch(function() {});
    },
    /** æ¸…理缓存按钮操作 */
    handleClearCache() {
      clearCache().then(response => {
        if (response.code === 200) {
          this.msgSuccess("清理成功");
        }
      });
    }
  }
};
</script>
</script>
ruoyi-ui/src/views/system/dept/index.vue
@@ -56,17 +56,17 @@
      </el-table-column>
      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
        <template slot-scope="scope">
          <el-button
            size="mini"
            type="text"
            icon="el-icon-edit"
          <el-button
            size="mini"
            type="text"
            icon="el-icon-edit"
            @click="handleUpdate(scope.row)"
            v-hasPermi="['system:dept:edit']"
          >修改</el-button>
          <el-button
            size="mini"
            type="text"
            icon="el-icon-plus"
          <el-button
            size="mini"
            type="text"
            icon="el-icon-plus"
            @click="handleAdd(scope.row)"
            v-hasPermi="['system:dept:add']"
          >新增</el-button>
@@ -281,8 +281,6 @@
                this.msgSuccess("修改成功");
                this.open = false;
                this.getList();
              } else {
                this.msgError(response.msg);
              }
            });
          } else {
@@ -291,8 +289,6 @@
                this.msgSuccess("新增成功");
                this.open = false;
                this.getList();
              } else {
                this.msgError(response.msg);
              }
            });
          }
@@ -314,4 +310,4 @@
    }
  }
};
</script>
</script>
ruoyi-ui/src/views/system/dict/data.vue
@@ -305,8 +305,6 @@
                this.msgSuccess("修改成功");
                this.open = false;
                this.getList();
              } else {
                this.msgError(response.msg);
              }
            });
          } else {
@@ -315,8 +313,6 @@
                this.msgSuccess("新增成功");
                this.open = false;
                this.getList();
              } else {
                this.msgError(response.msg);
              }
            });
          }
@@ -352,4 +348,4 @@
    }
  }
};
</script>
</script>
ruoyi-ui/src/views/system/dict/index.vue
@@ -94,6 +94,15 @@
          v-hasPermi="['system:dict:export']"
        >导出</el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="danger"
          icon="el-icon-refresh"
          size="mini"
          @click="handleClearCache"
          v-hasPermi="['system:dict:remove']"
        >清理缓存</el-button>
      </el-col>
    </el-row>
    <el-table v-loading="loading" :data="typeList" @selection-change="handleSelectionChange">
@@ -173,7 +182,7 @@
</template>
<script>
import { listType, getType, delType, addType, updateType, exportType } from "@/api/system/dict/type";
import { listType, getType, delType, addType, updateType, exportType, clearCache } from "@/api/system/dict/type";
export default {
  name: "Dict",
@@ -300,8 +309,6 @@
                this.msgSuccess("修改成功");
                this.open = false;
                this.getList();
              } else {
                this.msgError(response.msg);
              }
            });
          } else {
@@ -310,8 +317,6 @@
                this.msgSuccess("新增成功");
                this.open = false;
                this.getList();
              } else {
                this.msgError(response.msg);
              }
            });
          }
@@ -344,7 +349,15 @@
        }).then(response => {
          this.download(response.msg);
        }).catch(function() {});
    },
    /** æ¸…理缓存按钮操作 */
    handleClearCache() {
      clearCache().then(response => {
        if (response.code === 200) {
          this.msgSuccess("清理成功");
        }
      });
    }
  }
};
</script>
</script>
ruoyi-ui/src/views/system/menu/index.vue
@@ -49,16 +49,16 @@
      </el-table-column>
      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
        <template slot-scope="scope">
          <el-button size="mini"
            type="text"
            icon="el-icon-edit"
          <el-button size="mini"
            type="text"
            icon="el-icon-edit"
            @click="handleUpdate(scope.row)"
            v-hasPermi="['system:menu:edit']"
          >修改</el-button>
          <el-button
            size="mini"
            type="text"
            icon="el-icon-plus"
          <el-button
            size="mini"
            type="text"
            icon="el-icon-plus"
            @click="handleAdd(scope.row)"
            v-hasPermi="['system:menu:add']"
          >新增</el-button>
@@ -340,8 +340,6 @@
                this.msgSuccess("修改成功");
                this.open = false;
                this.getList();
              } else {
                this.msgError(response.msg);
              }
            });
          } else {
@@ -350,8 +348,6 @@
                this.msgSuccess("新增成功");
                this.open = false;
                this.getList();
              } else {
                this.msgError(response.msg);
              }
            });
          }
@@ -373,4 +369,4 @@
    }
  }
};
</script>
</script>
ruoyi-ui/src/views/system/notice/index.vue
@@ -308,8 +308,6 @@
                this.msgSuccess("修改成功");
                this.open = false;
                this.getList();
              } else {
                this.msgError(response.msg);
              }
            });
          } else {
@@ -318,8 +316,6 @@
                this.msgSuccess("新增成功");
                this.open = false;
                this.getList();
              } else {
                this.msgError(response.msg);
              }
            });
          }
@@ -342,4 +338,4 @@
    }
  }
};
</script>
</script>
ruoyi-ui/src/views/system/post/index.vue
@@ -107,7 +107,7 @@
        </template>
      </el-table-column>
    </el-table>
    <pagination
      v-show="total>0"
      :total="total"
@@ -277,8 +277,6 @@
                this.msgSuccess("修改成功");
                this.open = false;
                this.getList();
              } else {
                this.msgError(response.msg);
              }
            });
          } else {
@@ -287,8 +285,6 @@
                this.msgSuccess("新增成功");
                this.open = false;
                this.getList();
              } else {
                this.msgError(response.msg);
              }
            });
          }
@@ -324,4 +320,4 @@
    }
  }
};
</script>
</script>
ruoyi-ui/src/views/system/role/index.vue
@@ -482,8 +482,6 @@
                this.msgSuccess("修改成功");
                this.open = false;
                this.getList();
              } else {
                this.msgError(response.msg);
              }
            });
          } else {
@@ -493,8 +491,6 @@
                this.msgSuccess("新增成功");
                this.open = false;
                this.getList();
              } else {
                this.msgError(response.msg);
              }
            });
          }
@@ -510,8 +506,6 @@
            this.msgSuccess("修改成功");
            this.openDataScope = false;
            this.getList();
          } else {
            this.msgError(response.msg);
          }
        });
      }
@@ -545,4 +539,4 @@
    }
  }
};
</script>
</script>
ruoyi-ui/src/views/system/user/index.vue
@@ -572,8 +572,6 @@
          resetUserPwd(row.userId, value).then(response => {
            if (response.code === 200) {
              this.msgSuccess("修改成功,新密码是:" + value);
            } else {
              this.msgError(response.msg);
            }
          });
        }).catch(() => {});
@@ -588,8 +586,6 @@
                this.msgSuccess("修改成功");
                this.open = false;
                this.getList();
              } else {
                this.msgError(response.msg);
              }
            });
          } else {
@@ -598,8 +594,6 @@
                this.msgSuccess("新增成功");
                this.open = false;
                this.getList();
              } else {
                this.msgError(response.msg);
              }
            });
          }
ruoyi-ui/src/views/system/user/profile/resetPwd.vue
@@ -59,8 +59,6 @@
            response => {
              if (response.code === 200) {
                this.msgSuccess("修改成功");
              } else {
                this.msgError(response.msg);
              }
            }
          );
ruoyi-ui/src/views/system/user/profile/userAvatar.vue
@@ -122,8 +122,6 @@
            this.open = false;
            this.options.img = process.env.VUE_APP_BASE_API + response.imgUrl;
            this.msgSuccess("修改成功");
          } else {
            this.msgError(response.msg);
          }
          this.$refs.cropper.clearCrop();
        });
@@ -135,4 +133,4 @@
    }
  }
};
</script>
</script>
ruoyi-ui/src/views/system/user/profile/userInfo.vue
@@ -2,7 +2,7 @@
  <el-form ref="form" :model="user" :rules="rules" label-width="80px">
    <el-form-item label="用户昵称" prop="nickName">
      <el-input v-model="user.nickName" />
    </el-form-item>
    </el-form-item>
    <el-form-item label="手机号码" prop="phonenumber">
      <el-input v-model="user.phonenumber" maxlength="11" />
    </el-form-item>
@@ -64,8 +64,6 @@
          updateUserProfile(this.user).then(response => {
            if (response.code === 200) {
              this.msgSuccess("修改成功");
            } else {
              this.msgError(response.msg);
            }
          });
        }
ruoyi-ui/vue.config.js
@@ -83,12 +83,6 @@
      .end()
    config
      // https://webpack.js.org/configuration/devtool/#development
      .when(process.env.NODE_ENV === 'development',
        config => config.devtool('cheap-source-map')
      )
    config
      .when(process.env.NODE_ENV !== 'development',
        config => {
          config
ruoyi/pom.xml
@@ -5,7 +5,7 @@
    <groupId>com.ruoyi</groupId>
    <artifactId>ruoyi</artifactId>
    <version>2.2.0</version>
    <version>2.3.0</version>
    <packaging>jar</packaging>
    <name>ruoyi</name>
@@ -25,7 +25,7 @@
        <java.version>1.8</java.version>
        <mybatis.spring.boot.starter.version>1.3.2</mybatis.spring.boot.starter.version>
        <pagehelper.spring.boot.starter.version>1.2.5</pagehelper.spring.boot.starter.version>
        <fastjson.version>1.2.68</fastjson.version>
        <fastjson.version>1.2.70</fastjson.version>
        <druid.version>1.1.14</druid.version>
        <commons.io.version>2.5</commons.io.version>
        <commons.fileupload.version>1.3.3</commons.fileupload.version>
ruoyi/src/main/java/com/ruoyi/common/constant/Constants.java
@@ -110,6 +110,16 @@
    public static final String JWT_AUTHORITIES = "authorities";
    /**
     * å‚数管理 cache key
     */
    public static final String SYS_CONFIG_KEY = "sys_config:";
    /**
     * å­—典管理 cache key
     */
    public static final String SYS_DICT_KEY = "sys_dict:";
    /**
     * èµ„源映射路径 å‰ç¼€
     */
    public static final String RESOURCE_PREFIX = "/profile";
ruoyi/src/main/java/com/ruoyi/common/utils/DictUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,64 @@
package com.ruoyi.common.utils;
import java.util.Collection;
import java.util.List;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.framework.redis.RedisCache;
import com.ruoyi.project.system.domain.SysDictData;
/**
 * å­—典工具类
 *
 * @author ruoyi
 */
public class DictUtils
{
    /**
     * è®¾ç½®å­—典缓存
     *
     * @param key å‚æ•°é”®
     * @param dictDatas å­—典数据列表
     */
    public static void setDictCache(String key, List<SysDictData> dictDatas)
    {
        SpringUtils.getBean(RedisCache.class).setCacheObject(getCacheKey(key), dictDatas);
    }
    /**
     * èŽ·å–å­—å…¸ç¼“å­˜
     *
     * @param key å‚æ•°é”®
     * @return dictDatas å­—典数据列表
     */
    public static List<SysDictData> getDictCache(String key)
    {
        Object cacheObj = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key));
        if (StringUtils.isNotNull(cacheObj))
        {
            List<SysDictData> DictDatas = StringUtils.cast(cacheObj);
            return DictDatas;
        }
        return null;
    }
    /**
     * æ¸…空字典缓存
     */
    public static void clearDictCache()
    {
        Collection<String> keys = SpringUtils.getBean(RedisCache.class).keys(Constants.SYS_DICT_KEY + "*");
        SpringUtils.getBean(RedisCache.class).deleteObject(keys);
    }
    /**
     * è®¾ç½®cache key
     *
     * @param configKey å‚æ•°é”®
     * @return ç¼“存键key
     */
    public static String getCacheKey(String configKey)
    {
        return Constants.SYS_DICT_KEY + configKey;
    }
}
ruoyi/src/main/java/com/ruoyi/common/utils/StringUtils.java
@@ -450,4 +450,10 @@
        }
        return sb.toString();
    }
    @SuppressWarnings("unchecked")
    public static <T> T cast(Object obj)
    {
        return (T) obj;
    }
}
ruoyi/src/main/java/com/ruoyi/project/system/controller/SysConfigController.java
@@ -118,4 +118,16 @@
    {
        return toAjax(configService.deleteConfigByIds(configIds));
    }
    /**
     * æ¸…空缓存
     */
    @PreAuthorize("@ss.hasPermi('system:config:remove')")
    @Log(title = "参数管理", businessType = BusinessType.CLEAN)
    @DeleteMapping("/clearCache")
    public AjaxResult clearCache()
    {
        configService.clearCache();
        return AjaxResult.success();
    }
}
ruoyi/src/main/java/com/ruoyi/project/system/controller/SysDictDataController.java
@@ -21,6 +21,7 @@
import com.ruoyi.framework.web.page.TableDataInfo;
import com.ruoyi.project.system.domain.SysDictData;
import com.ruoyi.project.system.service.ISysDictDataService;
import com.ruoyi.project.system.service.ISysDictTypeService;
/**
 * æ•°æ®å­—典信息
@@ -33,6 +34,9 @@
{
    @Autowired
    private ISysDictDataService dictDataService;
    @Autowired
    private ISysDictTypeService dictTypeService;
    @PreAuthorize("@ss.hasPermi('system:dict:list')")
    @GetMapping("/list")
@@ -66,10 +70,10 @@
    /**
     * æ ¹æ®å­—典类型查询字典数据信息
     */
    @GetMapping(value = "/dictType/{dictType}")
    @GetMapping(value = "/type/{dictType}")
    public AjaxResult dictType(@PathVariable String dictType)
    {
        return AjaxResult.success(dictDataService.selectDictDataByType(dictType));
        return AjaxResult.success(dictTypeService.selectDictDataByType(dictType));
    }
    /**
ruoyi/src/main/java/com/ruoyi/project/system/controller/SysDictTypeController.java
@@ -108,6 +108,18 @@
    }
    /**
     * æ¸…空缓存
     */
    @PreAuthorize("@ss.hasPermi('system:dict:remove')")
    @Log(title = "字典类型", businessType = BusinessType.CLEAN)
    @DeleteMapping("/clearCache")
    public AjaxResult clearCache()
    {
        dictTypeService.clearCache();
        return AjaxResult.success();
    }
    /**
     * èŽ·å–å­—å…¸é€‰æ‹©æ¡†åˆ—è¡¨
     */
    @GetMapping("/optionselect")
ruoyi/src/main/java/com/ruoyi/project/system/service/ISysConfigService.java
@@ -51,14 +51,6 @@
    public int updateConfig(SysConfig config);
    /**
     * åˆ é™¤å‚数配置信息
     *
     * @param configId å‚æ•°ID
     * @return ç»“æžœ
     */
    public int deleteConfigById(Long configId);
    /**
     * æ‰¹é‡åˆ é™¤å‚数信息
     * 
     * @param configIds éœ€è¦åˆ é™¤çš„参数ID
@@ -67,6 +59,11 @@
    public int deleteConfigByIds(Long[] configIds);
    /**
     * æ¸…空缓存数据
     */
    public void clearCache();
    /**
     * æ ¡éªŒå‚数键名是否唯一
     * 
     * @param config å‚数信息
ruoyi/src/main/java/com/ruoyi/project/system/service/ISysDictDataService.java
@@ -19,14 +19,6 @@
    public List<SysDictData> selectDictDataList(SysDictData dictData);
    /**
     * æ ¹æ®å­—典类型查询字典数据
     *
     * @param dictType å­—典类型
     * @return å­—典数据集合信息
     */
    public List<SysDictData> selectDictDataByType(String dictType);
    /**
     * æ ¹æ®å­—典类型和字典键值查询字典数据信息
     * 
     * @param dictType å­—典类型
@@ -42,14 +34,6 @@
     * @return å­—典数据
     */
    public SysDictData selectDictDataById(Long dictCode);
    /**
     * é€šè¿‡å­—å…¸ID删除字典数据信息
     *
     * @param dictCode å­—典数据ID
     * @return ç»“æžœ
     */
    public int deleteDictDataById(Long dictCode);
    /**
     * æ‰¹é‡åˆ é™¤å­—典数据信息
ruoyi/src/main/java/com/ruoyi/project/system/service/ISysDictTypeService.java
@@ -1,6 +1,7 @@
package com.ruoyi.project.system.service;
import java.util.List;
import com.ruoyi.project.system.domain.SysDictData;
import com.ruoyi.project.system.domain.SysDictType;
/**
@@ -26,6 +27,14 @@
    public List<SysDictType> selectDictTypeAll();
    /**
     * æ ¹æ®å­—典类型查询字典数据
     *
     * @param dictType å­—典类型
     * @return å­—典数据集合信息
     */
    public List<SysDictData> selectDictDataByType(String dictType);
    /**
     * æ ¹æ®å­—典类型ID查询信息
     * 
     * @param dictId å­—典类型ID
@@ -42,14 +51,6 @@
    public SysDictType selectDictTypeByType(String dictType);
    /**
     * é€šè¿‡å­—å…¸ID删除字典信息
     *
     * @param dictId å­—å…¸ID
     * @return ç»“æžœ
     */
    public int deleteDictTypeById(Long dictId);
    /**
     * æ‰¹é‡åˆ é™¤å­—典信息
     * 
     * @param dictIds éœ€è¦åˆ é™¤çš„å­—å…¸ID
@@ -58,6 +59,11 @@
    public int deleteDictTypeByIds(Long[] dictIds);
    /**
     * æ¸…空缓存数据
     */
    public void clearCache();
    /**
     * æ–°å¢žä¿å­˜å­—典类型信息
     * 
     * @param dictType å­—典类型信息
ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysConfigServiceImpl.java
@@ -1,10 +1,15 @@
package com.ruoyi.project.system.service.impl;
import java.util.Collection;
import java.util.List;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.redis.RedisCache;
import com.ruoyi.project.system.domain.SysConfig;
import com.ruoyi.project.system.mapper.SysConfigMapper;
import com.ruoyi.project.system.service.ISysConfigService;
@@ -19,6 +24,22 @@
{
    @Autowired
    private SysConfigMapper configMapper;
    @Autowired
    private RedisCache redisCache;
    /**
     * é¡¹ç›®å¯åŠ¨æ—¶ï¼Œåˆå§‹åŒ–å‚æ•°åˆ°ç¼“å­˜
     */
    @PostConstruct
    public void init()
    {
        List<SysConfig> configsList = configMapper.selectConfigList(new SysConfig());
        for (SysConfig config : configsList)
        {
            redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue());
        }
    }
    /**
     * æŸ¥è¯¢å‚数配置信息
@@ -43,10 +64,20 @@
    @Override
    public String selectConfigByKey(String configKey)
    {
        String configValue = Convert.toStr(redisCache.getCacheObject(getCacheKey(configKey)));
        if (StringUtils.isNotEmpty(configValue))
        {
            return configValue;
        }
        SysConfig config = new SysConfig();
        config.setConfigKey(configKey);
        SysConfig retConfig = configMapper.selectConfig(config);
        return StringUtils.isNotNull(retConfig) ? retConfig.getConfigValue() : "";
        if (StringUtils.isNotNull(retConfig))
        {
            redisCache.setCacheObject(getCacheKey(configKey), retConfig.getConfigValue());
            return retConfig.getConfigValue();
        }
        return StringUtils.EMPTY;
    }
    /**
@@ -70,7 +101,12 @@
    @Override
    public int insertConfig(SysConfig config)
    {
        return configMapper.insertConfig(config);
        int row = configMapper.insertConfig(config);
        if (row > 0)
        {
            redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue());
        }
        return row;
    }
    /**
@@ -82,19 +118,12 @@
    @Override
    public int updateConfig(SysConfig config)
    {
        return configMapper.updateConfig(config);
    }
    /**
     * åˆ é™¤å‚数配置信息
     *
     * @param configId å‚æ•°ID
     * @return ç»“æžœ
     */
    @Override
    public int deleteConfigById(Long configId)
    {
        return configMapper.deleteConfigById(configId);
        int row = configMapper.updateConfig(config);
        if (row > 0)
        {
            redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue());
        }
        return row;
    }
    /**
@@ -106,7 +135,22 @@
    @Override
    public int deleteConfigByIds(Long[] configIds)
    {
        return configMapper.deleteConfigByIds(configIds);
        int count = configMapper.deleteConfigByIds(configIds);
        if (count > 0)
        {
            Collection<String> keys = redisCache.keys(Constants.SYS_CONFIG_KEY + "*");
            redisCache.deleteObject(keys);
        }
        return count;
    }
    /**
     * æ¸…空缓存数据
     */
    public void clearCache()
    {
        Collection<String> keys = redisCache.keys(Constants.SYS_CONFIG_KEY + "*");
        redisCache.deleteObject(keys);
    }
    /**
@@ -126,4 +170,15 @@
        }
        return UserConstants.UNIQUE;
    }
    /**
     * è®¾ç½®cache key
     *
     * @param configKey å‚æ•°é”®
     * @return ç¼“存键key
     */
    private String getCacheKey(String configKey)
    {
        return Constants.SYS_CONFIG_KEY + configKey;
    }
}
ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysDictDataServiceImpl.java
@@ -3,6 +3,7 @@
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.common.utils.DictUtils;
import com.ruoyi.project.system.domain.SysDictData;
import com.ruoyi.project.system.mapper.SysDictDataMapper;
import com.ruoyi.project.system.service.ISysDictDataService;
@@ -31,18 +32,6 @@
    }
    /**
     * æ ¹æ®å­—典类型查询字典数据
     *
     * @param dictType å­—典类型
     * @return å­—典数据集合信息
     */
    @Override
    public List<SysDictData> selectDictDataByType(String dictType)
    {
        return dictDataMapper.selectDictDataByType(dictType);
    }
    /**
     * æ ¹æ®å­—典类型和字典键值查询字典数据信息
     * 
     * @param dictType å­—典类型
@@ -68,18 +57,6 @@
    }
    /**
     * é€šè¿‡å­—å…¸ID删除字典数据信息
     *
     * @param dictCode å­—典数据ID
     * @return ç»“æžœ
     */
    @Override
    public int deleteDictDataById(Long dictCode)
    {
        return dictDataMapper.deleteDictDataById(dictCode);
    }
    /**
     * æ‰¹é‡åˆ é™¤å­—典数据信息
     * 
     * @param dictCodes éœ€è¦åˆ é™¤çš„字典数据ID
@@ -87,7 +64,12 @@
     */
    public int deleteDictDataByIds(Long[] dictCodes)
    {
        return dictDataMapper.deleteDictDataByIds(dictCodes);
        int row = dictDataMapper.deleteDictDataByIds(dictCodes);
        if (row > 0)
        {
            DictUtils.clearDictCache();
        }
        return row;
    }
    /**
@@ -99,7 +81,12 @@
    @Override
    public int insertDictData(SysDictData dictData)
    {
        return dictDataMapper.insertDictData(dictData);
        int row = dictDataMapper.insertDictData(dictData);
        if (row > 0)
        {
            DictUtils.clearDictCache();
        }
        return row;
    }
    /**
@@ -111,6 +98,11 @@
    @Override
    public int updateDictData(SysDictData dictData)
    {
        return dictDataMapper.updateDictData(dictData);
        int row = dictDataMapper.updateDictData(dictData);
        if (row > 0)
        {
            DictUtils.clearDictCache();
        }
        return row;
    }
}
ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysDictTypeServiceImpl.java
@@ -1,11 +1,15 @@
package com.ruoyi.project.system.service.impl;
import java.util.List;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.utils.DictUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.project.system.domain.SysDictData;
import com.ruoyi.project.system.domain.SysDictType;
import com.ruoyi.project.system.mapper.SysDictDataMapper;
import com.ruoyi.project.system.mapper.SysDictTypeMapper;
@@ -24,6 +28,20 @@
    @Autowired
    private SysDictDataMapper dictDataMapper;
    /**
     * é¡¹ç›®å¯åŠ¨æ—¶ï¼Œåˆå§‹åŒ–å­—å…¸åˆ°ç¼“å­˜
     */
    @PostConstruct
    public void init()
    {
        List<SysDictType> dictTypeList = dictTypeMapper.selectDictTypeAll();
        for (SysDictType dictType : dictTypeList)
        {
            List<SysDictData> dictDatas = dictDataMapper.selectDictDataByType(dictType.getDictType());
            DictUtils.setDictCache(dictType.getDictType(), dictDatas);
        }
    }
    /**
     * æ ¹æ®æ¡ä»¶åˆ†é¡µæŸ¥è¯¢å­—典类型
@@ -46,6 +64,29 @@
    public List<SysDictType> selectDictTypeAll()
    {
        return dictTypeMapper.selectDictTypeAll();
    }
    /**
     * æ ¹æ®å­—典类型查询字典数据
     *
     * @param dictType å­—典类型
     * @return å­—典数据集合信息
     */
    @Override
    public List<SysDictData> selectDictDataByType(String dictType)
    {
        List<SysDictData> dictDatas = DictUtils.getDictCache(dictType);
        if (StringUtils.isNotNull(dictDatas))
        {
            return dictDatas;
        }
        dictDatas = dictDataMapper.selectDictDataByType(dictType);
        if (StringUtils.isNotNull(dictDatas))
        {
            DictUtils.setDictCache(dictType, dictDatas);
            return dictDatas;
        }
        return null;
    }
    /**
@@ -72,18 +113,6 @@
    }
    /**
     * é€šè¿‡å­—å…¸ID删除字典信息
     *
     * @param dictId å­—å…¸ID
     * @return ç»“æžœ
     */
    @Override
    public int deleteDictTypeById(Long dictId)
    {
        return dictTypeMapper.deleteDictTypeById(dictId);
    }
    /**
     * æ‰¹é‡åˆ é™¤å­—典类型信息
     * 
     * @param dictIds éœ€è¦åˆ é™¤çš„å­—å…¸ID
@@ -91,7 +120,28 @@
     */
    public int deleteDictTypeByIds(Long[] dictIds)
    {
        return dictTypeMapper.deleteDictTypeByIds(dictIds);
        for (Long dictId : dictIds)
        {
            SysDictType dictType = selectDictTypeById(dictId);
            if (dictDataMapper.countDictDataByType(dictType.getDictType()) > 0)
            {
                throw new CustomException(String.format("%1$s已分配,不能删除", dictType.getDictName()));
            }
        }
        int count = dictTypeMapper.deleteDictTypeByIds(dictIds);
        if (count > 0)
        {
            DictUtils.clearDictCache();
        }
        return count;
    }
    /**
     * æ¸…空缓存数据
     */
    public void clearCache()
    {
        DictUtils.clearDictCache();
    }
    /**
@@ -103,7 +153,12 @@
    @Override
    public int insertDictType(SysDictType dictType)
    {
        return dictTypeMapper.insertDictType(dictType);
        int row = dictTypeMapper.insertDictType(dictType);
        if (row > 0)
        {
            DictUtils.clearDictCache();
        }
        return row;
    }
    /**
@@ -118,7 +173,12 @@
    {
        SysDictType oldDict = dictTypeMapper.selectDictTypeById(dictType.getDictId());
        dictDataMapper.updateDictDataType(oldDict.getDictType(), dictType.getDictType());
        return dictTypeMapper.updateDictType(dictType);
        int row = dictTypeMapper.updateDictType(dictType);
        if (row > 0)
        {
            DictUtils.clearDictCache();
        }
        return row;
    }
    /**
ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysMenuServiceImpl.java
@@ -140,7 +140,7 @@
        {
            RouterVo router = new RouterVo();
            router.setHidden("1".equals(menu.getVisible()));
            router.setName(StringUtils.capitalize(menu.getPath()));
            router.setName(getRouteName(menu));
            router.setPath(getRouterPath(menu));
            router.setComponent(getComponent(menu));
            router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon()));
@@ -300,6 +300,23 @@
    }
    /**
     * èŽ·å–è·¯ç”±åç§°
     *
     * @param menu èœå•信息
     * @return è·¯ç”±åç§°
     */
    public String getRouteName(SysMenu menu)
    {
        String routerName = StringUtils.capitalize(menu.getPath());
        // éžå¤–链并且是一级目录(类型为目录)
        if (isMeunFrame(menu))
        {
            routerName = StringUtils.EMPTY;
        }
        return routerName;
    }
    /**
     * èŽ·å–è·¯ç”±åœ°å€
     * 
     * @param menu èœå•信息
ruoyi/src/main/resources/application.yml
@@ -3,7 +3,7 @@
  # åç§°
  name: RuoYi
  # ç‰ˆæœ¬
  version: 2.2.0
  version: 2.3.0
  # ç‰ˆæƒå¹´ä»½
  copyrightYear: 2019
  # å®žä¾‹æ¼”示开关
ruoyi/src/main/resources/vm/sql/sql.vm
@@ -6,17 +6,17 @@
SELECT @parentId := LAST_INSERT_ID();
-- æŒ‰é’® SQL
insert into sys_menu  (menu_name, parent_id, order_num, path, component, is_frame, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
values('${functionName}查询', @parentId, '1',  '#', '', 1,  'F', '0',  '0', '${permissionPrefix}:query',        '#', 'admin', '2018-03-01', 'ry', '2018-03-01', '');
insert into sys_menu  (menu_name, parent_id, order_num, path, component, is_frame, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
values('${functionName}新增', @parentId, '2',  '#', '', 1,  'F', '0',  '0', '${permissionPrefix}:add',          '#', 'admin', '2018-03-01', 'ry', '2018-03-01', '');
insert into sys_menu  (menu_name, parent_id, order_num, path, component, is_frame, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
values('${functionName}修改', @parentId, '3',  '#', '', 1,  'F', '0',  '0', '${permissionPrefix}:edit',         '#', 'admin', '2018-03-01', 'ry', '2018-03-01', '');
insert into sys_menu  (menu_name, parent_id, order_num, path, component, is_frame, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
values('${functionName}删除', @parentId, '4',  '#', '', 1,  'F', '0',  '0', '${permissionPrefix}:remove',       '#', 'admin', '2018-03-01', 'ry', '2018-03-01', '');
insert into sys_menu  (menu_name, parent_id, order_num, path, component, is_frame, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
values('${functionName}导出', @parentId, '5',  '#', '', 1,  'F', '0',  '0', '${permissionPrefix}:export',       '#', 'admin', '2018-03-01', 'ry', '2018-03-01', '');
ruoyi/src/main/resources/vm/vue/index-tree.vue.vm
@@ -375,8 +375,6 @@
                this.msgSuccess("修改成功");
                this.open = false;
                this.getList();
              } else {
                this.msgError(response.msg);
              }
            });
          } else {
@@ -385,8 +383,6 @@
                this.msgSuccess("新增成功");
                this.open = false;
                this.getList();
              } else {
                this.msgError(response.msg);
              }
            });
          }
ruoyi/src/main/resources/vm/vue/index.vue.vm
@@ -392,8 +392,6 @@
                this.msgSuccess("修改成功");
                this.open = false;
                this.getList();
              } else {
                this.msgError(response.msg);
              }
            });
          } else {
@@ -402,8 +400,6 @@
                this.msgSuccess("新增成功");
                this.open = false;
                this.getList();
              } else {
                this.msgError(response.msg);
              }
            });
          }