Browse Source

Merge remote-tracking branch 'upstream/master'

“zyj” 3 năm trước cách đây
mục cha
commit
8a9acb3509

+ 1 - 11
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java

@@ -1,6 +1,5 @@
 package com.ruoyi.web.controller.system;
 
-import java.util.Iterator;
 import java.util.List;
 import org.apache.commons.lang3.ArrayUtils;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -54,16 +53,7 @@ public class SysDeptController extends BaseController
     public AjaxResult excludeChild(@PathVariable(value = "deptId", required = false) Long deptId)
     {
         List<SysDept> depts = deptService.selectDeptList(new SysDept());
-        Iterator<SysDept> it = depts.iterator();
-        while (it.hasNext())
-        {
-            SysDept d = (SysDept) it.next();
-            if (d.getDeptId().intValue() == deptId
-                    || ArrayUtils.contains(StringUtils.split(d.getAncestors(), ","), deptId + ""))
-            {
-                it.remove();
-            }
-        }
+        depts.removeIf(d -> d.getDeptId().intValue() == deptId || ArrayUtils.contains(StringUtils.split(d.getAncestors(), ","), deptId + ""));
         return AjaxResult.success(depts);
     }
 

+ 5 - 0
ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java

@@ -19,6 +19,11 @@ public class Constants
      */
     public static final String GBK = "GBK";
 
+    /**
+     * www主域
+     */
+    public static final String WWW = "www.";
+
     /**
      * http请求
      */

+ 1 - 1
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java

@@ -131,7 +131,7 @@ public class SysDictData extends BaseEntity
 
     public boolean getDefault()
     {
-        return UserConstants.YES.equals(this.isDefault) ? true : false;
+        return UserConstants.YES.equals(this.isDefault);
     }
 
     public String getIsDefault()

+ 3 - 15
ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java

@@ -124,9 +124,9 @@ public class RedisCache
      * @param collection 多个对象
      * @return
      */
-    public long deleteObject(final Collection collection)
+    public boolean deleteObject(final Collection collection)
     {
-        return redisTemplate.delete(collection);
+        return redisTemplate.delete(collection) > 0;
     }
 
     /**
@@ -231,18 +231,6 @@ public class RedisCache
         return opsForHash.get(key, hKey);
     }
 
-    /**
-     * 删除Hash中的数据
-     * 
-     * @param key
-     * @param hKey
-     */
-    public void delCacheMapValue(final String key, final String hKey)
-    {
-        HashOperations hashOperations = redisTemplate.opsForHash();
-        hashOperations.delete(key, hKey);
-    }
-
     /**
      * 获取多个Hash中的数据
      *
@@ -264,7 +252,7 @@ public class RedisCache
      */
     public boolean deleteCacheMapValue(final String key, final String hKey)
     {
-        return Boolean.TRUE.equals(redisTemplate.opsForHash().delete(key, hKey));
+        return redisTemplate.opsForHash().delete(key, hKey) > 0;
     }
 
     /**

+ 2 - 2
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java

@@ -529,7 +529,7 @@ public class SysMenuServiceImpl implements ISysMenuService
      */
     public String innerLinkReplaceEach(String path)
     {
-        return StringUtils.replaceEach(path, new String[] { Constants.HTTP, Constants.HTTPS },
-                new String[] { "", "" });
+        return StringUtils.replaceEach(path, new String[] { Constants.HTTP, Constants.HTTPS, Constants.WWW, "." },
+                new String[] { "", "", "", "/" });
     }
 }

+ 5 - 0
ruoyi-ui/src/assets/styles/transition.scss

@@ -12,11 +12,16 @@
 }
 
 /* fade-transform */
+.fade-transform--move,
 .fade-transform-leave-active,
 .fade-transform-enter-active {
   transition: all .5s;
 }
 
+.fade-transform-leave-active {
+  position: absolute;
+}
+
 .fade-transform-enter {
   opacity: 0;
   transform: translateX(-30px);

+ 20 - 8
ruoyi-ui/src/components/FileUpload/index.vue

@@ -12,7 +12,7 @@
       :show-file-list="false"
       :headers="headers"
       class="upload-file-uploader"
-      ref="upload"
+      ref="fileUpload"
     >
       <!-- 上传按钮 -->
       <el-button size="mini" type="primary">选取文件</el-button>
@@ -151,14 +151,16 @@ export default {
       this.$modal.closeLoading()
     },
     // 上传成功回调
-    handleUploadSuccess(res) {
-      this.uploadList.push({ name: res.fileName, url: res.fileName });
-      if (this.uploadList.length === this.number) {
-        this.fileList = this.fileList.concat(this.uploadList);
-        this.uploadList = [];
-        this.number = 0;
-        this.$emit("input", this.listToString(this.fileList));
+    handleUploadSuccess(res, file) {
+      if (res.code === 200) {
+        this.uploadList.push({ name: res.fileName, url: res.fileName });
+        this.uploadedSuccessfully();
+      } else {
+        this.number--;
         this.$modal.closeLoading();
+        this.$modal.msgError(res.msg);
+        this.$refs.fileUpload.handleRemove(file);
+        this.uploadedSuccessfully();
       }
     },
     // 删除文件
@@ -166,6 +168,16 @@ export default {
       this.fileList.splice(index, 1);
       this.$emit("input", this.listToString(this.fileList));
     },
+    // 上传结束处理
+    uploadedSuccessfully() {
+      if (this.number > 0 && this.uploadList.length === this.number) {
+        this.fileList = this.fileList.concat(this.uploadList);
+        this.uploadList = [];
+        this.number = 0;
+        this.$emit("input", this.listToString(this.fileList));
+        this.$modal.closeLoading();
+      }
+    },
     // 获取文件名称
     getFileName(name) {
       if (name.lastIndexOf("/") > -1) {

+ 36 - 22
ruoyi-ui/src/components/ImageUpload/index.vue

@@ -9,8 +9,8 @@
       :limit="limit"
       :on-error="handleUploadError"
       :on-exceed="handleExceed"
-      name="file"
-      :on-remove="handleRemove"
+      ref="imageUpload"
+      :on-remove="handleDelete"
       :show-file-list="true"
       :headers="headers"
       :file-list="fileList"
@@ -117,25 +117,6 @@ export default {
     },
   },
   methods: {
-    // 删除图片
-    handleRemove(file, fileList) {
-      const findex = this.fileList.map(f => f.name).indexOf(file.name);
-      if(findex > -1) {
-        this.fileList.splice(findex, 1);
-        this.$emit("input", this.listToString(this.fileList));
-      }
-    },
-    // 上传成功回调
-    handleUploadSuccess(res) {
-      this.uploadList.push({ name: res.fileName, url: res.fileName });
-      if (this.uploadList.length === this.number) {
-        this.fileList = this.fileList.concat(this.uploadList);
-        this.uploadList = [];
-        this.number = 0;
-        this.$emit("input", this.listToString(this.fileList));
-        this.$modal.closeLoading();
-      }
-    },
     // 上传前loading加载
     handleBeforeUpload(file) {
       let isImg = false;
@@ -171,11 +152,42 @@ export default {
     handleExceed() {
       this.$modal.msgError(`上传文件数量不能超过 ${this.limit} 个!`);
     },
+    // 上传成功回调
+    handleUploadSuccess(res, file) {
+      if (res.code === 200) {
+        this.uploadList.push({ name: res.fileName, url: res.fileName });
+        this.uploadedSuccessfully();
+      } else {
+        this.number--;
+        this.$modal.closeLoading();
+        this.$modal.msgError(res.msg);
+        this.$refs.imageUpload.handleRemove(file);
+        this.uploadedSuccessfully();
+      }
+    },
+    // 删除图片
+    handleDelete(file) {
+      const findex = this.fileList.map(f => f.name).indexOf(file.name);
+      if(findex > -1) {
+        this.fileList.splice(findex, 1);
+        this.$emit("input", this.listToString(this.fileList));
+      }
+    },
     // 上传失败
     handleUploadError() {
       this.$modal.msgError("上传图片失败,请重试");
       this.$modal.closeLoading();
     },
+    // 上传结束处理
+    uploadedSuccessfully() {
+      if (this.number > 0 && this.uploadList.length === this.number) {
+        this.fileList = this.fileList.concat(this.uploadList);
+        this.uploadList = [];
+        this.number = 0;
+        this.$emit("input", this.listToString(this.fileList));
+        this.$modal.closeLoading();
+      }
+    },
     // 预览
     handlePictureCardPreview(file) {
       this.dialogImageUrl = file.url;
@@ -186,7 +198,9 @@ export default {
       let strs = "";
       separator = separator || ",";
       for (let i in list) {
-        strs += list[i].url.replace(this.baseUrl, "") + separator;
+        if (list[i].url) {
+          strs += list[i].url.replace(this.baseUrl, "") + separator;
+        }
       }
       return strs != '' ? strs.substr(0, strs.length - 1) : '';
     }

+ 7 - 3
ruoyi-ui/src/layout/components/AppMain.vue

@@ -2,15 +2,19 @@
   <section class="app-main">
     <transition name="fade-transform" mode="out-in">
       <keep-alive :include="cachedViews">
-        <router-view :key="key" />
+        <router-view v-if="!$route.meta.link" :key="key" />
       </keep-alive>
     </transition>
+    <iframe-toggle />
   </section>
 </template>
 
 <script>
+import iframeToggle from "./IframeToggle/index"
+
 export default {
   name: 'AppMain',
+  components: { iframeToggle },
   computed: {
     cachedViews() {
       return this.$store.state.tagsView.cachedViews
@@ -31,7 +35,7 @@ export default {
   overflow: hidden;
 }
 
-.fixed-header+.app-main {
+.fixed-header + .app-main {
   padding-top: 50px;
 }
 
@@ -41,7 +45,7 @@ export default {
     min-height: calc(100vh - 84px);
   }
 
-  .fixed-header+.app-main {
+  .fixed-header + .app-main {
     padding-top: 84px;
   }
 }

+ 24 - 0
ruoyi-ui/src/layout/components/IframeToggle/index.vue

@@ -0,0 +1,24 @@
+<template>
+  <transition-group name="fade-transform" mode="out-in">
+    <inner-link
+      v-for="(item, index) in iframeViews"
+      :key="item.path"
+      :iframeId="'iframe' + index"
+      v-show="$route.path === item.path"
+      :src="item.meta.link"
+    ></inner-link>
+  </transition-group>
+</template>
+
+<script>
+import InnerLink from "../InnerLink/index"
+
+export default {
+  components: { InnerLink },
+  computed: {
+    iframeViews() {
+      return this.$store.state.tagsView.iframeViews
+    }
+  }
+}
+</script>

+ 40 - 20
ruoyi-ui/src/layout/components/InnerLink/index.vue

@@ -1,27 +1,47 @@
+<template>
+  <div :style="'height:' + height" v-loading="loading" element-loading-text="正在加载页面,请稍候!">
+    <iframe
+      :id="iframeId"
+      style="width: 100%; height: 100%"
+      :src="src"
+      frameborder="no"
+    ></iframe>
+  </div>
+</template>
+
 <script>
 export default {
+  props: {
+    src: {
+      type: String,
+      default: "/"
+    },
+    iframeId: {
+      type: String
+    }
+  },
   data() {
-    return {};
+    return {
+      loading: false,
+      height: document.documentElement.clientHeight - 94.5 + "px;"
+    };
   },
-  render() {
-    const { $route: { meta: { link } }, } = this;
-    if ({ link }.link === "") {
-      return "404";
+  mounted() {
+    var _this = this;
+    const iframeId = ("#" + this.iframeId).replace(/\//g, "\\/");
+    const iframe = document.querySelector(iframeId);
+    // iframe页面loading控制
+    if (iframe.attachEvent) {
+      this.loading = true;
+      iframe.attachEvent("onload", function () {
+        _this.loading = false;
+      });
+    } else {
+      this.loading = true;
+      iframe.onload = function () {
+        _this.loading = false;
+      };
     }
-    let url = { link }.link;
-    const height = document.documentElement.clientHeight - 94.5 + "px";
-    const style = { height: height };
-
-    return (
-      <div style={style}>
-        <iframe
-          src={url}
-          frameborder="no"
-          style="width: 100%; height: 100%"
-          scrolling="auto"
-        ></iframe>
-      </div>
-    );
-  },
+  }
 };
 </script>

+ 6 - 0
ruoyi-ui/src/layout/components/TagsView/index.vue

@@ -133,6 +133,9 @@ export default {
       const { name } = this.$route
       if (name) {
         this.$store.dispatch('tagsView/addView', this.$route)
+        if (this.$route.meta.link) {
+          this.$store.dispatch('tagsView/addIframeView', this.$route)
+        }
       }
       return false
     },
@@ -153,6 +156,9 @@ export default {
     },
     refreshSelectedTag(view) {
       this.$tab.refreshPage(view);
+      if (this.$route.meta.link) {
+        this.$store.dispatch('tagsView/delIframeView', this.$route)
+      }
     },
     closeSelectedTag(view) {
       this.$tab.closePage(view).then(({ visitedViews }) => {

+ 33 - 12
ruoyi-ui/src/store/modules/tagsView.js

@@ -1,9 +1,18 @@
 const state = {
   visitedViews: [],
-  cachedViews: []
+  cachedViews: [],
+  iframeViews: []
 }
 
 const mutations = {
+  ADD_IFRAME_VIEW: (state, view) => {
+    if (state.iframeViews.some(v => v.path === view.path)) return
+    state.iframeViews.push(
+      Object.assign({}, view, {
+        title: view.meta.title || 'no-name'
+      })
+    )
+  },
   ADD_VISITED_VIEW: (state, view) => {
     if (state.visitedViews.some(v => v.path === view.path)) return
     state.visitedViews.push(
@@ -18,7 +27,6 @@ const mutations = {
       state.cachedViews.push(view.name)
     }
   },
-
   DEL_VISITED_VIEW: (state, view) => {
     for (const [i, v] of state.visitedViews.entries()) {
       if (v.path === view.path) {
@@ -26,6 +34,10 @@ const mutations = {
         break
       }
     }
+    state.iframeViews = state.iframeViews.filter(item => item.path !== view.path)
+  },
+  DEL_IFRAME_VIEW: (state, view) => {
+    state.iframeViews = state.iframeViews.filter(item => item.path !== view.path)
   },
   DEL_CACHED_VIEW: (state, view) => {
     const index = state.cachedViews.indexOf(view.name)
@@ -36,6 +48,7 @@ const mutations = {
     state.visitedViews = state.visitedViews.filter(v => {
       return v.meta.affix || v.path === view.path
     })
+    state.iframeViews = state.iframeViews.filter(item => item.path === view.path)
   },
   DEL_OTHERS_CACHED_VIEWS: (state, view) => {
     const index = state.cachedViews.indexOf(view.name)
@@ -45,16 +58,15 @@ const mutations = {
       state.cachedViews = []
     }
   },
-
   DEL_ALL_VISITED_VIEWS: state => {
     // keep affix tags
     const affixTags = state.visitedViews.filter(tag => tag.meta.affix)
     state.visitedViews = affixTags
+    state.iframeViews = []
   },
   DEL_ALL_CACHED_VIEWS: state => {
     state.cachedViews = []
   },
-
   UPDATE_VISITED_VIEW: (state, view) => {
     for (let v of state.visitedViews) {
       if (v.path === view.path) {
@@ -63,7 +75,6 @@ const mutations = {
       }
     }
   },
-  
   DEL_RIGHT_VIEWS: (state, view) => {
     const index = state.visitedViews.findIndex(v => v.path === view.path)
     if (index === -1) {
@@ -77,10 +88,13 @@ const mutations = {
       if (i > -1) {
         state.cachedViews.splice(i, 1)
       }
+      if(item.meta.link) {
+        const fi = state.iframeViews.findIndex(v => v.path === item.path)
+        state.iframeViews.splice(fi, 1)
+      }
       return false
     })
   },
-
   DEL_LEFT_VIEWS: (state, view) => {
     const index = state.visitedViews.findIndex(v => v.path === view.path)
     if (index === -1) {
@@ -94,6 +108,10 @@ const mutations = {
       if (i > -1) {
         state.cachedViews.splice(i, 1)
       }
+      if(item.meta.link) {
+        const fi = state.iframeViews.findIndex(v => v.path === item.path)
+        state.iframeViews.splice(fi, 1)
+      }
       return false
     })
   }
@@ -104,13 +122,15 @@ const actions = {
     dispatch('addVisitedView', view)
     dispatch('addCachedView', view)
   },
+  addIframeView({ commit }, view) {
+    commit('ADD_IFRAME_VIEW', view)
+  },
   addVisitedView({ commit }, view) {
     commit('ADD_VISITED_VIEW', view)
   },
   addCachedView({ commit }, view) {
     commit('ADD_CACHED_VIEW', view)
   },
-
   delView({ dispatch, state }, view) {
     return new Promise(resolve => {
       dispatch('delVisitedView', view)
@@ -127,13 +147,18 @@ const actions = {
       resolve([...state.visitedViews])
     })
   },
+  delIframeView({ commit, state }, view) {
+    return new Promise(resolve => {
+      commit('DEL_IFRAME_VIEW', view)
+      resolve([...state.iframeViews])
+    })
+  },
   delCachedView({ commit, state }, view) {
     return new Promise(resolve => {
       commit('DEL_CACHED_VIEW', view)
       resolve([...state.cachedViews])
     })
   },
-
   delOthersViews({ dispatch, state }, view) {
     return new Promise(resolve => {
       dispatch('delOthersVisitedViews', view)
@@ -156,7 +181,6 @@ const actions = {
       resolve([...state.cachedViews])
     })
   },
-
   delAllViews({ dispatch, state }, view) {
     return new Promise(resolve => {
       dispatch('delAllVisitedViews', view)
@@ -179,18 +203,15 @@ const actions = {
       resolve([...state.cachedViews])
     })
   },
-
   updateVisitedView({ commit }, view) {
     commit('UPDATE_VISITED_VIEW', view)
   },
-
   delRightTags({ commit }, view) {
     return new Promise(resolve => {
       commit('DEL_RIGHT_VIEWS', view)
       resolve([...state.visitedViews])
     })
   },
-
   delLeftTags({ commit }, view) {
     return new Promise(resolve => {
       commit('DEL_LEFT_VIEWS', view)

+ 7 - 7
ruoyi-ui/src/views/system/menu/index.vue

@@ -107,7 +107,7 @@
       <el-form ref="form" :model="form" :rules="rules" label-width="100px">
         <el-row>
           <el-col :span="24">
-            <el-form-item label="上级菜单">
+            <el-form-item label="上级菜单" prop="parentId">
               <treeselect
                 v-model="form.parentId"
                 :options="menuOptions"
@@ -159,7 +159,7 @@
             </el-form-item>
           </el-col>
           <el-col :span="12" v-if="form.menuType != 'F'">
-            <el-form-item>
+            <el-form-item prop="isFrame">
               <span slot="label">
                 <el-tooltip content="选择是外链则路由地址需要以`http(s)://`开头" placement="top">
                 <i class="el-icon-question"></i>
@@ -195,7 +195,7 @@
             </el-form-item>
           </el-col>
           <el-col :span="12" v-if="form.menuType != 'M'">
-            <el-form-item>
+            <el-form-item prop="perms">
               <el-input v-model="form.perms" placeholder="请输入权限标识" maxlength="100" />
               <span slot="label">
                 <el-tooltip content="控制器中定义的权限字符,如:@PreAuthorize(`@ss.hasPermi('system:user:list')`)" placement="top">
@@ -206,7 +206,7 @@
             </el-form-item>
           </el-col>
           <el-col :span="12" v-if="form.menuType == 'C'">
-            <el-form-item>
+            <el-form-item prop="query">
               <el-input v-model="form.query" placeholder="请输入路由参数" maxlength="255" />
               <span slot="label">
                 <el-tooltip content='访问路由的默认传递参数,如:`{"id": 1, "name": "ry"}`' placement="top">
@@ -217,7 +217,7 @@
             </el-form-item>
           </el-col>
           <el-col :span="12" v-if="form.menuType == 'C'">
-            <el-form-item>
+            <el-form-item prop="isCache">
               <span slot="label">
                 <el-tooltip content="选择是则会被`keep-alive`缓存,需要匹配组件的`name`和地址保持一致" placement="top">
                 <i class="el-icon-question"></i>
@@ -231,7 +231,7 @@
             </el-form-item>
           </el-col>
           <el-col :span="12" v-if="form.menuType != 'F'">
-            <el-form-item>
+            <el-form-item prop="visible">
               <span slot="label">
                 <el-tooltip content="选择隐藏则路由将不会出现在侧边栏,但仍然可以访问" placement="top">
                 <i class="el-icon-question"></i>
@@ -248,7 +248,7 @@
             </el-form-item>
           </el-col>
           <el-col :span="12" v-if="form.menuType != 'F'">
-            <el-form-item>
+            <el-form-item prop="status">
               <span slot="label">
                 <el-tooltip content="选择停用则路由将不会出现在侧边栏,也不能被访问" placement="top">
                 <i class="el-icon-question"></i>