ymy 2 жил өмнө
parent
commit
e1eded3d6b

+ 34 - 0
package-lock.json

@@ -9,6 +9,7 @@
       "version": "0.1.0",
       "dependencies": {
         "axios": "^1.3.5",
+        "clipboard": "^2.0.11",
         "core-js": "^3.8.3",
         "element-ui": "^2.15.13",
         "file-saver": "^2.0.5",
@@ -4239,6 +4240,16 @@
         "node": ">=6"
       }
     },
+    "node_modules/clipboard": {
+      "version": "2.0.11",
+      "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.11.tgz",
+      "integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==",
+      "dependencies": {
+        "good-listener": "^1.2.2",
+        "select": "^1.1.2",
+        "tiny-emitter": "^2.0.0"
+      }
+    },
     "node_modules/clipboardy": {
       "version": "2.3.0",
       "resolved": "https://registry.npmmirror.com/clipboardy/-/clipboardy-2.3.0.tgz",
@@ -5174,6 +5185,11 @@
         "node": ">=0.4.0"
       }
     },
+    "node_modules/delegate": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
+      "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
+    },
     "node_modules/depd": {
       "version": "2.0.0",
       "resolved": "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz",
@@ -6588,6 +6604,14 @@
         "node": ">=10"
       }
     },
+    "node_modules/good-listener": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
+      "integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==",
+      "dependencies": {
+        "delegate": "^3.1.2"
+      }
+    },
     "node_modules/gopd": {
       "version": "1.0.1",
       "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.0.1.tgz",
@@ -10922,6 +10946,11 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/select": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
+      "integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA=="
+    },
     "node_modules/select-hose": {
       "version": "2.0.0",
       "resolved": "https://registry.npmmirror.com/select-hose/-/select-hose-2.0.0.tgz",
@@ -12404,6 +12433,11 @@
         "node": ">=0.6.0"
       }
     },
+    "node_modules/tiny-emitter": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
+      "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
+    },
     "node_modules/to-fast-properties": {
       "version": "2.0.0",
       "resolved": "https://registry.npmmirror.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz",

+ 1 - 0
package.json

@@ -9,6 +9,7 @@
   },
   "dependencies": {
     "axios": "^1.3.5",
+    "clipboard": "^2.0.11",
     "core-js": "^3.8.3",
     "element-ui": "^2.15.13",
     "file-saver": "^2.0.5",

+ 5 - 0
src/assets/styles/index.scss

@@ -5,6 +5,11 @@
 @import './sidebar.scss';
 @import './btn.scss';
 
+html,body,div,button,input,i,span,ul,li,a,dl,dt,dd{
+  padding: 0;
+  margin: 0;
+}
+
 body {
   height: 100%;
   -moz-osx-font-smoothing: grayscale;

+ 2 - 0
src/benyun/components/byTable/byTable.vue

@@ -30,6 +30,7 @@
             <span class="col-action" 
               v-for="(pluginsItem,_index) of item.plugins" 
               :key="'plu'+_index" 
+              v-hasPermi="[item.audit]" 
               @click="pluginClick(pluginsItem,row)">
               <i v-if="pluginsItem.icon" :class="pluginsItem.icon" ></i>
               {{ pluginsItem.name }}
@@ -104,6 +105,7 @@ export default class ByTable extends VueViews {
     if(this.propConfig){
       this.setConfig(this.propConfig)
     }
+    console.log('aaaaa',this.config)
   }
   mounted(){
     this.$nextTick(()=>{

+ 47 - 12
src/benyun/components/byTool/byTool.vue

@@ -1,13 +1,27 @@
 <template>
   <div class="tool">
     <div class="tool-left">
-      <el-button v-for="(item,index) of showTools" :icon="item.icon" :key="index" size="small" @click="clickBtn(item)">{{ item.name }}</el-button>
+      <el-button 
+        v-for="(item,index) of showTools" 
+        :icon="item.icon" 
+        :key="index" 
+        size="small" 
+        v-hasPermi="[item.audit]" 
+        @click="clickBtn(item)">
+        {{ item.name }}
+      </el-button>
       <slot name="tool-left" />
     </div>
-    <slot name="tool-right" />
-    <!-- <div class="tool-right">
-      
-    </div> -->
+    
+    <div class="tool-right">
+      <el-tooltip class="item" effect="dark" :content="!openSearch ? '隐藏搜索' : '显示搜索'" placement="top">
+        <el-button :icon="openSearch?'el-icon-search':'el-icon-zoom-out'" size="small" circle v-if="propTools.search" @click="toggleSearch"></el-button>
+      </el-tooltip>
+      <el-tooltip class="item" effect="dark" content="刷新" placement="top">
+        <el-button icon="el-icon-refresh" size="small" circle v-if="propTools.refresh" @click="refresh"></el-button>
+      </el-tooltip>
+      <slot name="tool-right" />
+    </div>
   </div>
 </template>
 
@@ -17,18 +31,27 @@ import { Component, Prop, Vue, Mixins } from 'vue-property-decorator'
   // components: { logDrawer }
 })
 export default class GmTools extends Vue {
+  openSearch:boolean = true
+
   @Prop()
   propConfig:any
 
   @Prop()
   customTools:any;
 
+  get audit(){
+    return this.propConfig?.audit ? this.propConfig.audit : {}
+  }
+  get propTools(){
+    return this.propConfig?.tools ? this.propConfig.tools : {}
+  }
+
   tools:Array<any>=[
-    { name: '新增', icon: 'el-icon-plus', clickName: 'onAdd', _class: 'onAdd' },
-    { name: '修改', icon: 'el-icon-edit', clickName: 'onUpdate', _class: 'onUpdate' },
-    { name: '删除', icon: 'el-icon-delete', clickName: 'onDelete', _class: 'onDelete' },
-    { name: '导出', icon: 'el-icon-download', clickName: 'onExport', _class: 'onExport' },
-    { name: '刷新', icon: 'el-icon-refresh', clickName: 'onRefresh', _class: 'onRefresh' }
+    { name: '新增', icon: 'el-icon-plus', clickName: 'onAdd', _class: 'add' },
+    { name: '修改', icon: 'el-icon-edit', clickName: 'onUpdate', _class: 'edit' },
+    { name: '删除', icon: 'el-icon-delete', clickName: 'onDelete', _class: 'delete' },
+    { name: '导出', icon: 'el-icon-download', clickName: 'onExport', _class: 'export' },
+    { name: '刷新', icon: 'el-icon-refresh', clickName: 'onRefresh', _class: 'refresh' }
   ]
   showTools:Array<any>=[]
 
@@ -45,16 +68,28 @@ export default class GmTools extends Vue {
     this.showTools = [];
     for(const item of this.tools){
       if(data[item._class]){
-        this.showTools.push(item)
+        let obj:any = (this as any).$lodash.cloneDeep(item);
+        if(this.audit){
+          obj.audit = this.audit[item._class]
+        }
+        this.showTools.push(obj)
       }
     }
   }
-
+  //自定义工具栏按钮设置
   setCustomTools(tools:Array<any>){
     for(const item of tools){
       this.showTools.push(item)
     }
   }
+  //点击刷新按钮
+  refresh(){
+    this.$emit('onRefresh')
+  }
+  //点击搜索
+  toggleSearch(){
+    this.$emit('searchHandle')
+  }
 
   clickBtn(item:any){
     if(item?.event?.click){

+ 64 - 0
src/directive/dialog/drag.ts

@@ -0,0 +1,64 @@
+/**
+* v-dialogDrag 弹窗拖拽
+* Copyright (c) 2019 young
+*/
+
+export default {
+  bind(el:any, binding:any, vnode:any, oldVnode:any) {
+    const value = binding.value
+    if (value == false) return
+    // 获取拖拽内容头部
+    const dialogHeaderEl = el.querySelector('.el-dialog__header');
+    const dragDom = el.querySelector('.el-dialog');
+    dialogHeaderEl.style.cursor = 'move';
+    // 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
+    const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null);
+    dragDom.style.position = 'absolute';
+    dragDom.style.marginTop = 0;
+    let width = dragDom.style.width;
+    if (width.includes('%')) {
+      width = +document.body.clientWidth * (+width.replace(/\%/g, '') / 100);
+    } else {
+      width = +width.replace(/\px/g, '');
+    }
+    dragDom.style.left = `${(document.body.clientWidth - width) / 2}px`;
+    // 鼠标按下事件
+    dialogHeaderEl.onmousedown = (e:any) => {
+      // 鼠标按下,计算当前元素距离可视区的距离 (鼠标点击位置距离可视窗口的距离)
+      const disX = e.clientX - dialogHeaderEl.offsetLeft;
+      const disY = e.clientY - dialogHeaderEl.offsetTop;
+
+      // 获取到的值带px 正则匹配替换
+      let styL:any, styT:any;
+
+      // 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
+      if (sty.left.includes('%')) {
+        styL = +document.body.clientWidth * (+sty.left.replace(/\%/g, '') / 100);
+        styT = +document.body.clientHeight * (+sty.top.replace(/\%/g, '') / 100);
+      } else {
+        styL = +sty.left.replace(/\px/g, '');
+        styT = +sty.top.replace(/\px/g, '');
+      };
+
+      // 鼠标拖拽事件
+      document.onmousemove = function (e) {
+        // 通过事件委托,计算移动的距离 (开始拖拽至结束拖拽的距离)
+        const l = e.clientX - disX;
+        const t = e.clientY - disY;
+
+        let finallyL = l + styL
+        let finallyT = t + styT
+
+        // 移动当前元素
+        dragDom.style.left = `${finallyL}px`;
+        dragDom.style.top = `${finallyT}px`;
+
+      };
+
+      document.onmouseup = function (e) {
+        document.onmousemove = null;
+        document.onmouseup = null;
+      };
+    }
+  }
+};

+ 34 - 0
src/directive/dialog/dragHeight.ts

@@ -0,0 +1,34 @@
+/**
+* v-dialogDragWidth 可拖动弹窗高度(右下角)
+* Copyright (c) 2019 young
+*/
+
+export default {
+    bind(el:any) {
+        const dragDom:any = el.querySelector('.el-dialog');
+        let lineEl:any = document.createElement('div');
+        lineEl.style = 'width: 6px; background: inherit; height: 10px; position: absolute; right: 0; bottom: 0; margin: auto; z-index: 1; cursor: nwse-resize;';
+        lineEl.addEventListener('mousedown',
+            function(e:any) {
+                // 鼠标按下,计算当前元素距离可视区的距离
+                const disX = e.clientX - el.offsetLeft;
+                const disY = e.clientY - el.offsetTop;
+                // 当前宽度 高度
+                const curWidth = dragDom.offsetWidth;
+                const curHeight = dragDom.offsetHeight;
+                document.onmousemove = function(e) {
+                    e.preventDefault(); // 移动时禁用默认事件
+                    // 通过事件委托,计算移动的距离
+                    const xl = e.clientX - disX;
+                    const yl = e.clientY - disY
+                    dragDom.style.width = `${curWidth + xl}px`;
+                    dragDom.style.height = `${curHeight + yl}px`;
+                };
+                document.onmouseup = function(e) {
+                    document.onmousemove = null;
+                    document.onmouseup = null;
+                };
+            }, false);
+        dragDom.appendChild(lineEl);
+    }
+}

+ 30 - 0
src/directive/dialog/dragWidth.ts

@@ -0,0 +1,30 @@
+/**
+* v-dialogDragWidth 可拖动弹窗宽度(右侧边)
+* Copyright (c) 2019 young
+*/
+
+export default {
+    bind(el:any) {
+        const dragDom = el.querySelector('.el-dialog');
+        let lineEl:any = document.createElement('div');
+        lineEl.style = 'width: 5px; background: inherit; height: 80%; position: absolute; right: 0; top: 0; bottom: 0; margin: auto; z-index: 1; cursor: w-resize;';
+        lineEl.addEventListener('mousedown',
+            function (e:any) {
+                // 鼠标按下,计算当前元素距离可视区的距离
+                const disX = e.clientX - el.offsetLeft;
+                // 当前宽度
+                const curWidth = dragDom.offsetWidth;
+                document.onmousemove = function (e) {
+                    e.preventDefault(); // 移动时禁用默认事件
+                    // 通过事件委托,计算移动的距离
+                    const l = e.clientX - disX;
+                    dragDom.style.width = `${curWidth + l}px`;
+                };
+                document.onmouseup = function (e) {
+                    document.onmousemove = null;
+                    document.onmouseup = null;
+                };
+            }, false);
+        dragDom.appendChild(lineEl);
+    }
+}

+ 23 - 0
src/directive/index.ts

@@ -0,0 +1,23 @@
+import hasRole from './permission/hasRole'
+import hasPermi from './permission/hasPermi'
+import dialogDrag from './dialog/drag'
+import dialogDragWidth from './dialog/dragWidth'
+import dialogDragHeight from './dialog/dragHeight'
+import clipboard from './module/clipboard'
+
+const install = function(Vue:any) {
+  Vue.directive('hasRole', hasRole)
+  Vue.directive('hasPermi', hasPermi)
+  Vue.directive('clipboard', clipboard)
+  Vue.directive('dialogDrag', dialogDrag)
+  Vue.directive('dialogDragWidth', dialogDragWidth)
+  Vue.directive('dialogDragHeight', dialogDragHeight)
+
+  if (window.Vue) {
+    (window as any)['hasRole'] = hasRole;
+    (window as any)['hasPermi'] = hasPermi
+    Vue.use(install); // eslint-disable-line
+  }
+}
+
+export default install

+ 55 - 0
src/directive/module/clipboard.ts

@@ -0,0 +1,55 @@
+/**
+* v-clipboard 文字复制剪贴
+* Copyright (c) 2021 young
+*/
+
+import Clipboard from 'clipboard'
+
+export default {
+  bind(el:any, binding:any, vnode:any) {
+    switch (binding.arg) {
+      case 'success':
+        el._vClipBoard_success = binding.value;
+        break;
+      case 'error':
+        el._vClipBoard_error = binding.value;
+        break;
+      default: {
+        const clipboard = new Clipboard(el, {
+          text: () => binding.value,
+          action: () => binding.arg === 'cut' ? 'cut' : 'copy'
+        });
+        clipboard.on('success', (e:any) => {
+          const callback = el._vClipBoard_success;
+          callback && callback(e);
+        });
+        clipboard.on('error', (e:any) => {
+          const callback = el._vClipBoard_error;
+          callback && callback(e);
+        });
+        el._vClipBoard = clipboard;
+      }
+    }
+  },
+  update(el:any, binding:any) {
+    if (binding.arg === 'success') {
+      el._vClipBoard_success = binding.value;
+    } else if (binding.arg === 'error') {
+      el._vClipBoard_error = binding.value;
+    } else {
+      el._vClipBoard.text = function () { return binding.value; };
+      el._vClipBoard.action = () => binding.arg === 'cut' ? 'cut' : 'copy';
+    }
+  },
+  unbind(el:any, binding:any) {
+    if (!el._vClipboard) return
+    if (binding.arg === 'success') {
+      delete el._vClipBoard_success;
+    } else if (binding.arg === 'error') {
+      delete el._vClipBoard_error;
+    } else {
+      el._vClipBoard.destroy();
+      delete el._vClipBoard;
+    }
+  }
+}

+ 28 - 0
src/directive/permission/hasPermi.ts

@@ -0,0 +1,28 @@
+ /**
+ * v-hasPermi 操作权限处理
+ * Copyright (c) 2019 young
+ */
+
+ import store from '@/store'
+
+ export default {
+  inserted(el:any, binding:any, vnode:any) {
+    const { value } = binding
+    const all_permission = "*:*:*";
+    const permissions = store.getters && store.getters.permissions
+
+    if (value && value instanceof Array && value.length > 0) {
+      const permissionFlag = value
+
+      const hasPermissions = permissions.some((permission:any) => {
+        return all_permission === permission || permissionFlag.includes(permission)
+      })
+
+      if (!hasPermissions) {
+        el.parentNode && el.parentNode.removeChild(el)
+      }
+    } else {
+      throw new Error(`请设置操作权限标签值`)
+    }
+  }
+}

+ 28 - 0
src/directive/permission/hasRole.ts

@@ -0,0 +1,28 @@
+ /**
+ * v-hasRole 角色权限处理
+ * Copyright (c) 2019 young
+ */
+
+ import store from '@/store'
+
+ export default {
+  inserted(el:any, binding:any, vnode:any) {
+    const { value } = binding
+    const super_admin = "admin";
+    const roles = store.getters && store.getters.roles
+
+    if (value && value instanceof Array && value.length > 0) {
+      const roleFlag = value
+
+      const hasRole = roles.some((role:any) => {
+        return super_admin === role || roleFlag.includes(role)
+      })
+
+      if (!hasRole) {
+        el.parentNode && el.parentNode.removeChild(el)
+      }
+    } else {
+      throw new Error(`请设置角色权限标签值"`)
+    }
+  }
+}

+ 3 - 1
src/layout/components/AppMain.vue

@@ -30,9 +30,10 @@ export default {
 .app-main {
   /* 50= navbar  50  */
   min-height: calc(100vh - 50px);
+  // height: calc(100vh - 50px);
   width: 100%;
   position: relative;
-  overflow: hidden;
+  overflow-y: hidden;;
 }
 
 .fixed-header+.app-main {
@@ -43,6 +44,7 @@ export default {
   .app-main {
     /* 84 = navbar + tags-view = 50 + 34 */
     min-height: calc(100vh - 84px);
+    // height: calc(100vh - 84px);
   }
 
   .fixed-header+.app-main {

+ 3 - 0
src/main.ts

@@ -8,6 +8,7 @@ import {download} from '@/benyun/utils/request'
 import Cookies from 'js-cookie'
 import lodash from 'lodash'
 import plugins from '@/benyun/plugins'
+import directive from './directive' // directive
 
 import '@/assets/styles/index.scss' // global css
 import '@/assets/styles/benyuntech.scss' // young css
@@ -37,6 +38,8 @@ Vue.use(Element, {
 Vue.use(Column).use(Table);
 Vue.use(gmComponent);
 Vue.use(plugins);
+Vue.use(directive);
+
 Vue.config.productionTip = false
 
 // 全局方法挂载

+ 0 - 17
src/store/index2.ts

@@ -1,17 +0,0 @@
-import Vue from 'vue'
-import Vuex from 'vuex'
-
-Vue.use(Vuex)
-
-export default new Vuex.Store({
-  state: {
-  },
-  getters: {
-  },
-  mutations: {
-  },
-  actions: {
-  },
-  modules: {
-  }
-})

+ 1 - 1
src/views/audit/collaborationLog/index.vue

@@ -1,3 +1,3 @@
 <template>
-  <div>123</div>
+  <div style="height:1200px">123</div>
 </template>

+ 12 - 6
src/views/demo/form.vue

@@ -108,16 +108,22 @@ export default class DemoForm extends Vue {
 
   toolConfig={
     tools:{
-      onAdd:true,
-      onUpdate:true,
-      onDelete:true,
-      onExport:true,
-      onRefresh:true
+      add:true,
+      edit:true,
+      delete:true,
+      export:true,
+      search:true
+    },
+    audit:{
+      add:'audit:collaborationLog:add',
+      edit:'audit:collaborationLog:edit',
+      delete:'audit:collaborationLog:remove',
+      export:'audit:collaborationLog:export'
     }
   }
   customTools:Array<any>=[
     {
-      name: '新增123', icon: 'el-icon-plus', event:{
+      name: '新增123', icon: 'el-icon-plus', audit:[''], event:{
         click:()=>{
           console.log('新增123')
         }