瀏覽代碼

修改账户绑定

zhuxinbo 5 年之前
父節點
當前提交
3db70c9b53
共有 50 個文件被更改,包括 20563 次插入17 次删除
  1. 二進制
      public/logo.png
  2. 9 1
      src/api/api.js
  3. 二進制
      src/assets/daiban.png
  4. 二進制
      src/assets/duban.png
  5. 二進制
      src/assets/guaz.png
  6. 二進制
      src/assets/logo-white.png
  7. 二進制
      src/assets/nodata.png
  8. 二進制
      src/assets/zaiban.png
  9. 73 0
      src/cas/sso.js
  10. 61 0
      src/components/chart/IndexBar.vue
  11. 240 0
      src/components/jeecg/JCategorySelect.vue
  12. 65 0
      src/components/jeecg/JCron.vue
  13. 95 0
      src/components/jeecg/JInput.vue
  14. 928 0
      src/components/jeecg/modal/JCronModal.vue
  15. 315 0
      src/components/jeecgbiz/JSelectBizComponent/JSelectBizComponentModal.vue
  16. 36 0
      src/components/jeecgbiz/JSelectBizComponent/README.md
  17. 158 0
      src/components/jeecgbiz/JSelectBizComponent/index.vue
  18. 37 0
      src/components/jeecgbiz/JSelectPosition.vue
  19. 38 0
      src/components/jeecgbiz/JSelectRole.vue
  20. 14 6
      src/router/index.js
  21. 107 0
      src/utils/LunarFullCalendarUtil.js
  22. 40 0
      src/utils/util.js
  23. 519 0
      src/views/dashboard/IndexBdc.vue
  24. 269 0
      src/views/dashboard/IndexChart.vue
  25. 372 0
      src/views/dashboard/IndexTask.vue
  26. 91 0
      src/views/jeecg/TableTotal.vue
  27. 275 0
      src/views/jeecg/modules/JEditableTable/DefaultTable.vue
  28. 70 0
      src/views/jeecg/modules/JEditableTable/ReadOnlyTable.vue
  29. 129 0
      src/views/jeecg/modules/JEditableTable/ThreeLinkage.vue
  30. 63 3
      src/views/modules/advertiser/UserAllocationList.vue
  31. 31 6
      src/views/modules/advertiser/modules/UserAllocationModal.vue
  32. 1 1
      src/views/modules/kuaishouapp/account/newMould.vue
  33. 388 0
      src/views/modules/online/cgform/OnlCgformCopyList.vue
  34. 478 0
      src/views/modules/online/cgform/OnlCgformHeadList.vue
  35. 820 0
      src/views/modules/online/cgform/auto/OnlCgformAutoList.vue
  36. 713 0
      src/views/modules/online/cgform/auto/OnlCgformTreeList.vue
  37. 268 0
      src/views/modules/online/cgform/util/TableUtils.js
  38. 256 0
      src/views/modules/online/cgreport/OnlCgreportHeadList.vue
  39. 293 0
      src/views/modules/online/cgreport/auto/OnlCgreportAutoList.vue
  40. 164 0
      src/views/modules/oss/OSSFileList.vue
  41. 31 0
      src/views/system/AddressList.vue
  42. 125 0
      src/views/system/DictDeleteList.vue
  43. 198 0
      src/views/system/NewPermissionList.vue
  44. 187 0
      src/views/system/SysFillRuleList.vue
  45. 178 0
      src/views/system/SysPositionList.vue
  46. 98 0
      src/views/system/modules/AddressListLeft.vue
  47. 198 0
      src/views/system/modules/AddressListRight.vue
  48. 158 0
      src/views/system/modules/SysFillRuleModal.vue
  49. 180 0
      src/views/system/modules/SysPositionModal.vue
  50. 11794 0
      yarn.lock

二進制
public/logo.png


+ 9 - 1
src/api/api.js

@@ -91,7 +91,12 @@ const queryUserByDepId = (params) => getAction("/sys/user/queryUserByDepId", par
 const queryUserRoleMap = (params) => getAction("/sys/user/queryUserRoleMap", params);
 // 重复校验
 const duplicateCheck = (params) => getAction("/sys/duplicate/check", params);
+// 加载分类字典
+const loadCategoryData = (params)=>getAction("/sys/category/loadAllData",params);
+/*update_begin author:wuxianquan date:20190908 for:添加查询一级菜单和子菜单查询api */
+const getSystemMenuList = (params)=>getAction("/sys/permission/getSystemMenuList",params);
 
+const getSystemSubmenu = (params)=>getAction("/sys/permission/getSystemSubmenu",params);
 export {
   // imgView,
   // doMian,
@@ -139,7 +144,10 @@ export {
   duplicateCheck,
   queryTreeListForRole,
   getIndustryList,
-  getIndustryTreeList
+  getIndustryTreeList,
+  loadCategoryData,
+  getSystemMenuList,
+  getSystemSubmenu
 
 }
 

二進制
src/assets/daiban.png


二進制
src/assets/duban.png


二進制
src/assets/guaz.png


二進制
src/assets/logo-white.png


二進制
src/assets/nodata.png


二進制
src/assets/zaiban.png


+ 73 - 0
src/cas/sso.js

@@ -0,0 +1,73 @@
+import Vue from 'vue'
+import { ACCESS_TOKEN } from "@/store/mutation-types"
+import store from '@/store'
+/**
+ * 单点登录
+ */
+const init = (callback) => {
+  console.log("-------单点登录开始-------");
+  let token = Vue.ls.get(ACCESS_TOKEN);
+  let st = getUrlParam("ticket");
+  var sevice = "http://"+window.location.host+"/";
+  if(token){
+    loginSuccess(callback);
+  }else{
+    if(st){
+      validateSt(st,sevice,callback);
+    }else{
+      var serviceUrl = encodeURIComponent(sevice);
+      window.location.href = window._CONFIG['casPrefixUrl']+"/login?service="+serviceUrl;
+    }
+  }
+  console.log("-------单点登录结束-------");
+};
+const SSO = {
+  init: init
+};
+
+function getUrlParam(paraName) {
+  var url = document.location.toString();
+  var arrObj = url.split("?");
+
+  if (arrObj.length > 1) {
+    var arrPara = arrObj[1].split("&");
+    var arr;
+
+    for (var i = 0; i < arrPara.length; i++) {
+      arr = arrPara[i].split("=");
+
+      if (arr != null && arr[0] == paraName) {
+        return arr[1];
+      }
+    }
+    return "";
+  }
+  else {
+    return "";
+  }
+}
+
+function validateSt(ticket,service,callback){
+  let params = {
+    ticket: ticket,
+    service:service
+  };
+  store.dispatch('ValidateLogin',params).then(res => {
+    //this.departConfirm(res)
+    if(res.success){
+      loginSuccess(callback);
+    }else{
+      var sevice = "http://"+window.location.host+"/";
+      var serviceUrl = encodeURIComponent(sevice);
+      window.location.href = window._CONFIG['casPrefixUrl']+"/login?service="+serviceUrl;
+    }
+  }).catch((err) => {
+    console.log(err);
+    //that.requestFailed(err);
+  });
+}
+
+function loginSuccess (callback) {
+  callback();
+}
+export default SSO;

+ 61 - 0
src/components/chart/IndexBar.vue

@@ -0,0 +1,61 @@
+<template>
+  <div :style="{ padding: '0 0 32px 32px' }">
+    <h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
+    <v-chart
+      height="254"
+      :data="datasource"
+      :forceFit="true"
+      :padding="['auto', 'auto', '40', '50']">
+      <v-tooltip />
+      <v-axis />
+      <v-bar position="x*y"/>
+    </v-chart>
+  </div>
+</template>
+
+<script>
+
+  const data = []
+  for (let i = 0; i < 12; i += 1) {
+    data.push({
+      x: `${i + 1}月`,
+      y: Math.floor(Math.random() * 1000) + 200
+    })
+  }
+  const tooltip = [
+    'x*y',
+    (x, y) => ({
+      name: x,
+      value: y
+    })
+  ]
+  const scale = [{
+    dataKey: 'x',
+    min: 2
+  }, {
+    dataKey: 'y',
+    title: '时间',
+    min: 1,
+    max: 22
+  }]
+
+  export default {
+    name: "Bar",
+    props: {
+      title: {
+        type: String,
+        default: ''
+      }
+    },
+    mounted(){
+      this.datasource = data
+    },
+    data () {
+      return {
+        datasource:[],
+        scale,
+        tooltip
+      }
+    }
+  }
+</script>

+ 240 - 0
src/components/jeecg/JCategorySelect.vue

@@ -0,0 +1,240 @@
+<template>
+  <a-tree-select
+    allowClear
+    labelInValue
+    style="width: 100%"
+    :disabled="disabled"
+    :dropdownStyle="{ maxHeight: '400px', overflow: 'auto' }"
+    :placeholder="placeholder"
+    :loadData="asyncLoadTreeData"
+    :value="treeValue"
+    :treeData="treeData"
+    :multiple="multiple"
+    @change="onChange">
+  </a-tree-select>
+</template>
+<script>
+
+  import { getAction } from '@/api/manage'
+
+  export default {
+    name: 'JCategorySelect',
+    props: {
+      value:{
+        type: String,
+        required: false
+      },
+      placeholder:{
+        type: String,
+        default: '请选择',
+        required: false
+      },
+      disabled:{
+        type:Boolean,
+        default:false,
+        required:false
+      },
+      condition:{
+        type:String,
+        default:'',
+        required:false
+      },
+      // 是否支持多选
+      multiple: {
+        type: Boolean,
+        default: false,
+      },
+      loadTriggleChange:{
+        type: Boolean,
+        default: false,
+        required:false
+      },
+      pid:{
+        type:String,
+        default:'',
+        required:false
+      },
+      pcode:{
+        type:String,
+        default:'',
+        required:false
+      },
+      back:{
+        type:String,
+        default:'',
+        required:false
+      }
+    },
+    data () {
+      return {
+        treeValue:"",
+        treeData:[],
+        url:"/sys/category/loadTreeData",
+        view:'/sys/category/loadDictItem/',
+        tableName:"",
+        text:"",
+        code:"",
+
+      }
+    },
+    watch: {
+      value () {
+        this.loadItemByCode()
+      },
+      pcode(){
+        this.loadRoot();
+      }
+    },
+    created(){
+      this.validateProp().then(()=>{
+        this.loadRoot()
+        this.loadItemByCode()
+      })
+    },
+    methods: {
+      /**加载一级节点 */
+      loadRoot(){
+        let param = {
+          pid:this.pid,
+          pcode:this.pcode,
+          condition:this.condition
+        }
+        getAction(this.url,param).then(res=>{
+          if(res.success && res.result){
+            for(let i of res.result){
+              i.value = i.key
+              if(i.leaf==false){
+                i.isLeaf=false
+              }else if(i.leaf==true){
+                i.isLeaf=true
+              }
+            }
+            this.treeData = [...res.result]
+          }else{
+            console.log("树一级节点查询结果-else",res)
+          }
+        })
+      },
+
+      /** 数据回显*/
+      loadItemByCode(){
+        if(!this.value || this.value=="0"){
+          this.treeValue = []
+        }else{
+          getAction(this.view,{ids:this.value}).then(res=>{
+            console.log(124345)
+            console.log(124345,res)
+            if(res.success){
+              let values = this.value.split(',')
+              this.treeValue = res.result.map((item, index) => ({
+                key: values[index],
+                value: values[index],
+                label: item
+              }))
+              this.onLoadTriggleChange(res.result[0]);
+            }
+          })
+        }
+      },
+      onLoadTriggleChange(text){
+        //只有单选才会触发
+        if(!this.multiple && this.loadTriggleChange){
+          this.backValue(this.value,text)
+        }
+      },
+      backValue(value,label){
+        let obj = {}
+        if(this.back){
+          obj[this.back] = label
+        }
+        this.$emit('change', value, obj)
+      },
+      asyncLoadTreeData (treeNode) {
+        return new Promise((resolve) => {
+          if (treeNode.$vnode.children) {
+            resolve()
+            return
+          }
+          let pid = treeNode.$vnode.key
+          let param = {
+            pid:pid,
+            condition:this.condition
+          }
+          getAction(this.url,param).then(res=>{
+            if(res.success){
+              for(let i of res.result){
+                i.value = i.key
+                if(i.leaf==false){
+                  i.isLeaf=false
+                }else if(i.leaf==true){
+                  i.isLeaf=true
+                }
+              }
+              this.addChildren(pid,res.result,this.treeData)
+              this.treeData = [...this.treeData]
+            }
+            resolve()
+          })
+        })
+      },
+      addChildren(pid,children,treeArray){
+        if(treeArray && treeArray.length>0){
+          for(let item of treeArray){
+            if(item.key == pid){
+              if(!children || children.length==0){
+                item.isLeaf=true
+              }else{
+                item.children = children
+              }
+              break
+            }else{
+              this.addChildren(pid,children,item.children)
+            }
+          }
+        }
+      },
+
+      onChange(value){
+        if(!value){
+          this.$emit('change', '');
+          this.treeValue = ''
+        } else if (value instanceof Array) {
+          //this.$emit('change', value.map(item => item.value).join(','))
+          //this.treeValue = value
+        } else {
+          this.backValue(value.value,value.label)
+          this.treeValue = value
+        }
+      },
+      getCurrTreeData(){
+        return this.treeData
+      },
+      validateProp(){
+        let mycondition = this.condition
+        return new Promise((resolve,reject)=>{
+          if(!mycondition){
+            resolve();
+          }else{
+            try {
+              let test=JSON.parse(mycondition);
+              if(typeof test == 'object' && test){
+                resolve()
+              }else{
+                this.$message.error("组件JTreeSelect-condition传值有误,需要一个json字符串!")
+                reject()
+              }
+            } catch(e) {
+              this.$message.error("组件JTreeSelect-condition传值有误,需要一个json字符串!")
+              reject()
+            }
+          }
+        })
+      }
+    },
+    //2.2新增 在组件内定义 指定父组件调用时候的传值属性和事件类型 这个牛逼
+    model: {
+      prop: 'value',
+      event: 'change'
+    }
+  }
+</script>

+ 65 - 0
src/components/jeecg/JCron.vue

@@ -0,0 +1,65 @@
+<template>
+  <div class="components-input-demo-presuffix">
+    <a-input @click="openModal" placeholder="corn表达式" v-model="cron" @change="handleOK">
+      <a-icon slot="prefix" type="schedule" title="corn控件"/>
+      <a-icon v-if="cron" slot="suffix" type="close-circle" @click="handleEmpty" title="清空"/>
+    </a-input>
+    <JCronModal ref="innerVueCron" :data="cron" @ok="handleOK"></JCronModal>
+  </div>
+</template>
+<script>
+  import JCronModal from "./modal/JCronModal";
+  export default {
+    name: 'JCron',
+    components: {
+      JCronModal
+    },
+    props: {
+      value: {
+        required: false,
+        type: String,
+      }
+    },
+    data(){
+      return {
+        cron: this.value,
+      }
+    },
+    watch:{
+      value(val){
+        this.cron = val
+      }
+    },
+    methods:{
+      openModal(){
+        this.$refs.innerVueCron.show();
+      },
+      handleOK(val){
+        this.cron = val;
+        this.$emit("change", this.cron);
+        //this.$emit("change", Object.assign({},  this.cron));
+      },
+      handleEmpty(){
+        this.handleOK('')
+      }
+    },
+    model: {
+      prop: 'value',
+      event: 'change'
+    }
+  }
+</script>
+<style scoped>
+  .components-input-demo-presuffix .anticon-close-circle {
+    cursor: pointer;
+    color: #ccc;
+    transition: color 0.3s;
+    font-size: 12px;
+  }
+  .components-input-demo-presuffix .anticon-close-circle:hover {
+    color: #f5222d;
+  }
+  .components-input-demo-presuffix .anticon-close-circle:active {
+    color: #666;
+  }
+</style>

+ 95 - 0
src/components/jeecg/JInput.vue

@@ -0,0 +1,95 @@
+<template>
+  <a-input :placeholder="placeholder" :value="inputVal" @input="backValue"></a-input>
+</template>
+
+<script>
+  const JINPUT_QUERY_LIKE = 'like';
+  const JINPUT_QUERY_NE = 'ne';
+  const JINPUT_QUERY_GE = 'ge'; //大于等于
+  const JINPUT_QUERY_LE = 'le'; //小于等于
+  
+  export default {
+    name: 'JInput',
+    props:{
+      value:{
+        type:String,
+        required:false
+      },
+      type:{
+        type:String,
+        required:false,
+        default:JINPUT_QUERY_LIKE
+      },
+      placeholder:{
+        type:String,
+        required:false,
+        default:''
+      }
+    },
+    watch:{
+      value:{
+        immediate:true,
+        handler:function(){
+          this.initVal();
+        }
+      }
+    },
+    model: {
+      prop: 'value',
+      event: 'change'
+    },
+    data(){
+      return {
+        inputVal:''
+      }
+    },
+    methods:{
+      initVal(){
+        if(!this.value){
+          this.inputVal = ''
+        }else{
+          let text = this.value
+          switch (this.type) {
+            case JINPUT_QUERY_LIKE:
+              text = text.substring(1,text.length-1);
+              break;
+            case JINPUT_QUERY_NE:
+              text = text.substring(1);
+              break;
+            case JINPUT_QUERY_GE:
+              text = text.substring(2);
+              break;
+            case JINPUT_QUERY_LE:
+              text = text.substring(2);
+              break;
+            default:
+          }
+          this.inputVal = text
+        }
+      },
+      backValue(e){
+        let text = e.target.value
+        switch (this.type) {
+          case JINPUT_QUERY_LIKE:
+            text = "*"+text+"*";
+            break;
+          case JINPUT_QUERY_NE:
+            text = "!"+text;
+            break;
+          case JINPUT_QUERY_GE:
+            text = ">="+text;
+            break;
+          case JINPUT_QUERY_LE:
+            text = "<="+text;
+            break;
+          default:
+        }
+        this.$emit("change",text)
+      }
+    }
+  }
+</script>
+
+<style scoped>
+
+</style>

+ 928 - 0
src/components/jeecg/modal/JCronModal.vue

@@ -0,0 +1,928 @@
+<template>
+  <a-modal
+          title="corn表达式"
+          :width="modalWidth"
+          :visible="visible"
+          :confirmLoading="confirmLoading"
+          @ok="handleSubmit"
+          @cancel="close"
+          cancelText="关闭">
+    <div class="card-container">
+      <a-tabs type="card">
+        <a-tab-pane key="1" type="card">
+          <span slot="tab"><a-icon type="schedule" /> 秒</span>
+          <a-radio-group v-model="result.second.cronEvery">
+            <a-row>
+              <a-radio value="1">每一秒钟</a-radio>
+            </a-row>
+            <a-row>
+              <a-radio value="2">每隔
+                <a-input-number size="small" v-model="result.second.incrementIncrement" :min="1" :max="59"></a-input-number>
+                秒执行 从
+                <a-input-number size="small" v-model="result.second.incrementStart" :min="0" :max="59"></a-input-number>
+                秒开始
+              </a-radio>
+            </a-row>
+            <a-row>
+              <a-radio value="3">具体秒数(可多选)</a-radio>
+              <a-select style="width:354px;" size="small" mode="multiple" v-model="result.second.specificSpecific">
+                <a-select-option v-for="(val,index) in 60" :key="index" :value="index">{{ index }}</a-select-option>
+              </a-select>
+            </a-row>
+            <a-row>
+              <a-radio value="4">周期从
+                <a-input-number size="small" v-model="result.second.rangeStart" :min="1" :max="59"></a-input-number>
+                到
+                <a-input-number size="small" v-model="result.second.rangeEnd" :min="0" :max="59"></a-input-number>
+                秒
+              </a-radio>
+            </a-row>
+          </a-radio-group>
+        </a-tab-pane>
+        <a-tab-pane key="2">
+          <span slot="tab"><a-icon type="schedule" />分</span>
+          <div class="tabBody">
+            <a-radio-group v-model="result.minute.cronEvery">
+              <a-row>
+                <a-radio value="1">每一分钟</a-radio>
+              </a-row>
+              <a-row>
+                <a-radio value="2">每隔
+                  <a-input-number size="small" v-model="result.minute.incrementIncrement" :min="1" :max="60"></a-input-number>
+                  分执行 从
+                  <a-input-number size="small" v-model="result.minute.incrementStart" :min="0" :max="59"></a-input-number>
+                  分开始
+                </a-radio>
+              </a-row>
+              <a-row>
+                <a-radio value="3">具体分钟数(可多选)</a-radio>
+                <a-select style="width:340px;" size="small" mode="multiple" v-model="result.minute.specificSpecific">
+                  <a-select-option v-for="(val,index) in Array(60)" :key="index" :value="index"> {{ index }}</a-select-option>
+                </a-select>
+              </a-row>
+              <a-row>
+                <a-radio value="4">周期从
+                  <a-input-number size="small" v-model="result.minute.rangeStart" :min="1" :max="60"></a-input-number>
+                  到
+                  <a-input-number size="small" v-model="result.minute.rangeEnd" :min="0" :max="59"></a-input-number>
+                  分
+                </a-radio>
+              </a-row>
+            </a-radio-group>
+          </div>
+        </a-tab-pane>
+        <a-tab-pane key="3">
+          <span slot="tab"><a-icon type="schedule" /> 时</span>
+          <div class="tabBody">
+            <a-radio-group v-model="result.hour.cronEvery">
+              <a-row>
+                <a-radio value="1">每一小时</a-radio>
+              </a-row>
+              <a-row>
+                <a-radio value="2">每隔
+                  <a-input-number size="small" v-model="result.hour.incrementIncrement" :min="0" :max="23"></a-input-number>
+                  小时执行 从
+                  <a-input-number size="small" v-model="result.hour.incrementStart" :min="0" :max="23"></a-input-number>
+                  小时开始
+                </a-radio>
+              </a-row>
+              <a-row>
+                <a-radio class="long" value="3">具体小时数(可多选)</a-radio>
+                <a-select style="width:340px;" size="small" mode="multiple" v-model="result.hour.specificSpecific">
+                  <a-select-option v-for="(val,index) in Array(24)" :key="index" >{{ index }}</a-select-option>
+                </a-select>
+              </a-row>
+              <a-row>
+                <a-radio value="4">周期从
+                  <a-input-number size="small" v-model="result.hour.rangeStart" :min="0" :max="23"></a-input-number>
+                  到
+                  <a-input-number size="small" v-model="result.hour.rangeEnd" :min="0" :max="23"></a-input-number>
+                  小时
+                </a-radio>
+              </a-row>
+            </a-radio-group>
+          </div>
+        </a-tab-pane>
+        <a-tab-pane key="4">
+          <span slot="tab"><a-icon type="schedule" />  天</span>
+          <div class="tabBody">
+            <a-radio-group v-model="result.day.cronEvery">
+              <a-row>
+                <a-radio value="1">每一天</a-radio>
+              </a-row>
+              <a-row>
+                <a-radio value="2">每隔
+                  <a-input-number size="small" v-model="result.week.incrementIncrement" :min="1" :max="7"></a-input-number>
+                  周执行 从
+                  <a-select size="small" v-model="result.week.incrementStart">
+                    <a-select-option v-for="(val,index) in Array(7)" :key="index" :value="index+1">{{ weekDays[index] }}</a-select-option>
+                  </a-select>
+                  开始
+                </a-radio>
+              </a-row>
+              <a-row>
+                <a-radio value="3">每隔
+                  <a-input-number size="small" v-model="result.day.incrementIncrement" :min="1" :max="31"></a-input-number>
+                  天执行 从
+                  <a-input-number size="small" v-model="result.day.incrementStart" :min="1" :max="31"></a-input-number>
+                  天开始
+                </a-radio>
+              </a-row>
+              <a-row>
+                <a-radio class="long" value="4">具体星期几(可多选)</a-radio>
+                <a-select style="width:340px;" size="small" mode="multiple" v-model="result.week.specificSpecific">
+                  <a-select-option v-for="(val,index) in Array(7)" :key="index" :value="index+1">{{ weekDays[index] }}</a-select-option>
+                </a-select>
+              </a-row>
+              <a-row>
+                <a-radio class="long" value="5">具体天数(可多选)</a-radio>
+                <a-select style="width:354px;" size="small" mode="multiple" v-model="result.day.specificSpecific">
+                  <a-select-option v-for="(val,index) in Array(31)" :key="index" :value="index+1">{{ index+1 }}</a-select-option>
+                </a-select>
+              </a-row>
+              <a-row>
+                <a-radio value="6">在这个月的最后一天</a-radio>
+              </a-row>
+              <a-row>
+                <a-radio value="7">在这个月的最后一个工作日</a-radio>
+              </a-row>
+              <a-row>
+                <a-radio value="8">在这个月的最后一个
+                  <a-select size="small" v-model="result.day.cronLastSpecificDomDay">
+                    <a-select-option v-for="(val,index) in Array(7)" :key="index" :value="index+1">{{ weekDays[index] }}</a-select-option>
+                  </a-select>
+                </a-radio>
+              </a-row>
+              <a-row>
+                <a-radio value="9">
+                  在本月底前
+                  <a-input-number size="small" v-model="result.day.cronDaysBeforeEomMinus" :min="1" :max="31"></a-input-number>
+                  天
+                </a-radio>
+              </a-row>
+              <a-row>
+                <a-radio value="10">最近的工作日(周一至周五)至本月
+                  <a-input-number size="small" v-model="result.day.cronDaysNearestWeekday" :min="1" :max="31"></a-input-number>
+                  日
+                </a-radio>
+              </a-row>
+              <a-row>
+                <a-radio value="11">在这个月的第
+                  <a-input-number size="small" v-model="result.week.cronNthDayNth" :min="1" :max="5"></a-input-number>
+                  个
+                  <a-select size="small" v-model="result.week.cronNthDayDay">
+                    <a-select-option v-for="(val,index) in Array(7)" :key="index" :value="index+1">{{ weekDays[index] }}</a-select-option>
+                  </a-select>
+
+                </a-radio>
+              </a-row>
+            </a-radio-group>
+          </div>
+        </a-tab-pane>
+        <a-tab-pane key="5">
+          <span slot="tab"><a-icon type="schedule" /> 月</span>
+          <div class="tabBody">
+            <a-radio-group v-model="result.month.cronEvery">
+              <a-row>
+                <a-radio value="1">每一月</a-radio>
+              </a-row>
+              <a-row>
+                <a-radio value="2">每隔
+                  <a-input-number size="small" v-model="result.month.incrementIncrement" :min="0" :max="12"></a-input-number>
+                  月执行 从
+                  <a-input-number size="small" v-model="result.month.incrementStart" :min="0" :max="12"></a-input-number>
+                  月开始
+                </a-radio>
+              </a-row>
+              <a-row>
+                <a-radio class="long" value="3">具体月数(可多选)</a-radio>
+                <a-select style="width:354px;" size="small" filterable mode="multiple" v-model="result.month.specificSpecific">
+                  <a-select-option v-for="(val,index) in Array(12)" :key="index" :value="index+1">{{ index+1 }}</a-select-option>
+                </a-select>
+              </a-row>
+              <a-row>
+                <a-radio value="4">从
+                  <a-input-number size="small" v-model="result.month.rangeStart" :min="1" :max="12"></a-input-number>
+                  到
+                  <a-input-number size="small" v-model="result.month.rangeEnd" :min="1" :max="12"></a-input-number>
+                  月之间的每个月
+                </a-radio>
+              </a-row>
+            </a-radio-group>
+          </div>
+        </a-tab-pane>
+        <a-tab-pane key="6">
+          <span slot="tab"><a-icon type="schedule" /> 年</span>
+          <div class="tabBody">
+            <a-radio-group v-model="result.year.cronEvery">
+              <a-row>
+                <a-radio value="1">每一年</a-radio>
+              </a-row>
+              <a-row>
+                <a-radio value="2">每隔
+                  <a-input-number size="small" v-model="result.year.incrementIncrement" :min="1" :max="99"></a-input-number>
+                  年执行 从
+                  <a-input-number size="small" v-model="result.year.incrementStart" :min="2019" :max="2119"></a-input-number>
+                  年开始
+                </a-radio>
+              </a-row>
+              <a-row>
+                <a-radio class="long" value="3">具体年份(可多选)</a-radio>
+                <a-select style="width:354px;" size="small" filterable mode="multiple" v-model="result.year.specificSpecific">
+                  <a-select-option v-for="(val,index) in Array(100)" :key="index" :value="2019+index">{{ 2019+index }}</a-select-option>
+                </a-select>
+              </a-row>
+              <a-row>
+                <a-radio value="4">从
+                  <a-input-number size="small" v-model="result.year.rangeStart" :min="2019" :max="2119"></a-input-number>
+                  到
+                  <a-input-number size="small" v-model="result.year.rangeEnd" :min="2019" :max="2119"></a-input-number>
+                  年之间的每一年
+                </a-radio>
+              </a-row>
+            </a-radio-group>
+          </div>
+        </a-tab-pane>
+      </a-tabs>
+      <div class="bottom">
+        <span class="value">{{this.cron }}</span>
+      </div>
+    </div>
+  </a-modal>
+</template>
+<script>
+  export default {
+    name:'VueCron',
+    props:['data'],
+    data(){
+      return {
+        visible: false,
+        confirmLoading:false,
+        size:'large',
+        weekDays:['天','一','二','三','四','五','六'].map(val=>'星期'+val),
+        result: {
+          second:{},
+          minute:{},
+          hour:{},
+          day:{},
+          week:{},
+          month:{},
+          year:{}
+        },
+        defaultValue: {
+          second:{
+            cronEvery:'',
+            incrementStart:3,
+            incrementIncrement:5,
+            rangeStart:1,
+            rangeEnd:0,
+            specificSpecific:[],
+          },
+          minute:{
+            cronEvery:'',
+            incrementStart:3,
+            incrementIncrement:5,
+            rangeStart:1,
+            rangeEnd:'0',
+            specificSpecific:[],
+          },
+          hour:{
+            cronEvery:'',
+            incrementStart:3,
+            incrementIncrement:5,
+            rangeStart:'0',
+            rangeEnd:'0',
+            specificSpecific:[],
+          },
+          day:{
+            cronEvery:'',
+            incrementStart:1,
+            incrementIncrement:'1',
+            rangeStart:'',
+            rangeEnd:'',
+            specificSpecific:[],
+            cronLastSpecificDomDay:1,
+            cronDaysBeforeEomMinus:1,
+            cronDaysNearestWeekday:1,
+          },
+          week:{
+            cronEvery:'',
+            incrementStart:1,
+            incrementIncrement:1,
+            specificSpecific:[],
+            cronNthDayDay:1,
+            cronNthDayNth:1,
+          },
+          month:{
+            cronEvery:'',
+            incrementStart:3,
+            incrementIncrement:5,
+            rangeStart:1,
+            rangeEnd:1,
+            specificSpecific:[],
+          },
+          year:{
+            cronEvery:'',
+            incrementStart:2017,
+            incrementIncrement:1,
+            rangeStart:2019,
+            rangeEnd: 2019,
+            specificSpecific:[],
+          },
+          label:''
+        }
+      }
+    },
+    computed: {
+      modalWidth(){
+        return 608;
+      },
+      secondsText() {
+        let seconds = '';
+        let cronEvery=this.result.second.cronEvery||'';
+        switch (cronEvery.toString()){
+          case '1':
+            seconds = '*';
+            break;
+          case '2':
+            seconds = this.result.second.incrementStart+'/'+this.result.second.incrementIncrement;
+            break;
+          case '3':
+            this.result.second.specificSpecific.map(val=> {seconds += val+','});
+            seconds = seconds.slice(0, -1);
+            break;
+          case '4':
+            seconds = this.result.second.rangeStart+'-'+this.result.second.rangeEnd;
+            break;
+        }
+        return seconds;
+      },
+      minutesText() {
+        let minutes = '';
+        let cronEvery=this.result.minute.cronEvery||'';
+        switch (cronEvery.toString()){
+          case '1':
+            minutes = '*';
+            break;
+          case '2':
+            minutes = this.result.minute.incrementStart+'/'+this.result.minute.incrementIncrement;
+            break;
+          case '3':
+            this.result.minute.specificSpecific.map(val=> {
+              minutes += val+','
+            });
+            minutes = minutes.slice(0, -1);
+            break;
+          case '4':
+            minutes = this.result.minute.rangeStart+'-'+this.result.minute.rangeEnd;
+            break;
+        }
+        return minutes;
+      },
+      hoursText() {
+        let hours = '';
+        let cronEvery=this.result.hour.cronEvery||'';
+        switch (cronEvery.toString()){
+          case '1':
+            hours = '*';
+            break;
+          case '2':
+            hours = this.result.hour.incrementStart+'/'+this.result.hour.incrementIncrement;
+            break;
+          case '3':
+            this.result.hour.specificSpecific.map(val=> {
+              hours += val+','
+            });
+            hours = hours.slice(0, -1);
+            break;
+          case '4':
+            hours = this.result.hour.rangeStart+'-'+this.result.hour.rangeEnd;
+            break;
+        }
+        return hours;
+      },
+      daysText() {
+        let days='';
+        let cronEvery=this.result.day.cronEvery||'';
+        switch (cronEvery.toString()){
+          case '1':
+            break;
+          case '2':
+          case '4':
+          case '11':
+            days = '?';
+            break;
+          case '3':
+            days = this.result.day.incrementStart+'/'+this.result.day.incrementIncrement;
+            break;
+          case '5':
+            this.result.day.specificSpecific.map(val=> {
+              days += val+','
+            });
+            days = days.slice(0, -1);
+            break;
+          case '6':
+            days = "L";
+            break;
+          case '7':
+            days = "LW";
+            break;
+          case '8':
+            days = this.result.day.cronLastSpecificDomDay + 'L';
+            break;
+          case '9':
+            days = 'L-' + this.result.day.cronDaysBeforeEomMinus;
+            break;
+          case '10':
+            days = this.result.day.cronDaysNearestWeekday+"W";
+            break
+        }
+        return days;
+      },
+      weeksText() {
+        let weeks = '';
+        let cronEvery=this.result.day.cronEvery||'';
+        switch (cronEvery.toString()){
+          case '1':
+          case '3':
+          case '5':
+            weeks = '?';
+            break;
+          case '2':
+            weeks = this.result.week.incrementStart+'/'+this.result.week.incrementIncrement;
+            break;
+          case '4':
+            this.result.week.specificSpecific.map(val=> {
+              weeks += val+','
+            });
+            weeks = weeks.slice(0, -1);
+            break;
+          case '6':
+          case '7':
+          case '8':
+          case '9':
+          case '10':
+            weeks = "?";
+            break;
+          case '11':
+            weeks = this.result.week.cronNthDayDay+"#"+this.result.week.cronNthDayNth;
+            break;
+        }
+        return weeks;
+      },
+      monthsText() {
+        let months = '';
+        let cronEvery=this.result.month.cronEvery||'';
+        switch (cronEvery.toString()){
+          case '1':
+            months = '*';
+            break;
+          case '2':
+            months = this.result.month.incrementStart+'/'+this.result.month.incrementIncrement;
+            break;
+          case '3':
+            this.result.month.specificSpecific.map(val=> {
+              months += val+','
+            });
+            months = months.slice(0, -1);
+            break;
+          case '4':
+            months = this.result.month.rangeStart+'-'+this.result.month.rangeEnd;
+            break;
+        }
+        return months;
+      },
+      yearsText() {
+        let years = '';
+        let cronEvery=this.result.year.cronEvery||'';
+        switch (cronEvery.toString()){
+          case '1':
+            years = '*';
+            break;
+          case '2':
+            years = this.result.year.incrementStart+'/'+this.result.year.incrementIncrement;
+            break;
+          case '3':
+            this.result.year.specificSpecific.map(val=> {
+              years += val+','
+            });
+            years = years.slice(0, -1);
+            break;
+          case '4':
+            years = this.result.year.rangeStart+'-'+this.result.year.rangeEnd;
+            break;
+        }
+        return years;
+      },
+      cron(){
+        return `${this.secondsText||'*'} ${this.minutesText||'*'} ${this.hoursText||'*'} ${this.daysText||'*'} ${this.monthsText||'*'} ${this.weeksText||'?'} ${this.yearsText||'*'}`
+      },
+    },
+    watch:{
+      visible:{
+        handler() {
+          // if(this.data){
+          //   //this. result = Object.keys(this.data.value).length>0?this.deepCopy(this.data.value):this.deepCopy(this.defaultValue);
+          //   //this.result = Object.keys(this.data.value).length>0?clone(this.data.value):clone(this.defaultValue);
+          //   //this.result = Object.keys(this.data.value).length>0?clone(JSON.parse(this.data.value)):clone(this.defaultValue);
+          //   this.result = Object.keys(this.data.value).length>0?JSON.parse(this.data.value):JSON.parse(JSON.stringify(this.defaultValue));
+          // }else{
+          //   //this.result = this.deepCopy(this.defaultValue);
+          //   //this.result = clone(this.defaultValue);
+          //   this.result = JSON.parse(JSON.stringify(this.defaultValue));
+          // }
+          let label = this.data;
+          if(label){
+            this.secondsReverseExp(label)
+            this.minutesReverseExp(label);
+            this.hoursReverseExp(label);
+            this.daysReverseExp(label);
+            this.daysReverseExp(label);
+            this.monthsReverseExp(label);
+            this.yearReverseExp(label);
+            JSON.parse(JSON.stringify(label));
+          }else {
+            this.result = JSON.parse(JSON.stringify(this.defaultValue));
+          }
+        }
+      }
+    },
+    methods: {
+      show(){
+        this.visible = true;
+        // console.log('secondsReverseExp',this.secondsReverseExp(this.data));
+        // console.log('minutesReverseExp',this.minutesReverseExp(this.data));
+        // console.log('hoursReverseExp',this.hoursReverseExp(this.data));
+        // console.log('daysReverseExp',this.daysReverseExp(this.data));
+        // console.log('monthsReverseExp',this.monthsReverseExp(this.data));
+        // console.log('yearReverseExp',this.yearReverseExp(this.data));
+
+      },
+      handleSubmit(){
+        this.$emit('ok',this.cron);
+        this.close();
+        this.visible = false;
+      },
+      close(){
+        this.visible = false;
+      },
+      secondsReverseExp(seconds) {
+        let val =  seconds.split(" ")[0];
+        //alert(val);
+        let second = {
+          cronEvery:'',
+          incrementStart:3,
+          incrementIncrement:5,
+          rangeStart:1,
+          rangeEnd:0,
+          specificSpecific:[]
+        };
+        switch (true) {
+          case val.includes('*'):
+            second.cronEvery = '1';
+            break;
+          case val.includes('/'):
+            second.cronEvery = '2';
+            second.incrementStart = val.split('/')[0];
+            second.incrementIncrement = val.split('/')[1];
+            break;
+          case val.includes(','):
+            second.cronEvery = '3';
+            second.specificSpecific = val.split(',').map(Number).sort();
+            break;
+          case val.includes('-'):
+            second.cronEvery = '4';
+            second.rangeStart = val.split('-')[0];
+            second.rangeEnd = val.split('-')[1];
+            break;
+          default:
+            second.cronEvery = '1';
+        }
+        this.result.second = second;
+      },
+      minutesReverseExp(minutes) {
+        let val =  minutes.split(" ")[1];
+        let minute = {
+          cronEvery:'',
+          incrementStart:3,
+          incrementIncrement:5,
+          rangeStart:1,
+          rangeEnd:0,
+          specificSpecific:[],
+        }
+        switch (true) {
+          case val.includes('*'):
+            minute.cronEvery = '1';
+            break;
+          case val.includes('/'):
+            minute.cronEvery = '2';
+            minute.incrementStart = val.split('/')[0];
+            minute.incrementIncrement = val.split('/')[1];
+            break;
+          case val.includes(','):
+            minute.cronEvery = '3';
+            minute.specificSpecific = val.split(',').map(Number).sort();
+            break;
+          case val.includes('-'):
+            minute.cronEvery = '4';
+            minute.rangeStart = val.split('-')[0];
+            minute.rangeEnd = val.split('-')[1];
+            break;
+          default:
+            minute.cronEvery = '1';
+        }
+        this.result.minute = minute;
+      },
+      hoursReverseExp(hours) {
+        let val =  hours.split(" ")[2];
+        let hour ={
+            cronEvery:'',
+            incrementStart:3,
+            incrementIncrement:5,
+            rangeStart:1,
+            rangeEnd:'0',
+            specificSpecific:[],
+          };
+        switch (true) {
+          case val.includes('*'):
+            hour.cronEvery = '1';
+            break;
+          case val.includes('/'):
+            hour.cronEvery = '2';
+            hour.incrementStart = val.split('/')[0];
+            hour.incrementIncrement = val.split('/')[1];
+            break;
+          case val.includes(','):
+            hour.cronEvery = '3';
+            hour.specificSpecific = val.split(',').map(Number).sort();
+            break;
+          case val.includes('-'):
+            hour.cronEvery = '4';
+            hour.rangeStart = val.split('-')[0];
+            hour.rangeEnd = val.split('-')[1];
+            break;
+          default:
+            hour.cronEvery = '1';
+          }
+        this.result.hour = hour;
+      },
+      daysReverseExp(cron) {
+        let days =  cron.split(" ")[3];
+        let weeks =  cron.split(" ")[5];
+        let day ={
+          cronEvery:'',
+          incrementStart:1,
+          incrementIncrement:1,
+          rangeStart:1,
+          rangeEnd:1,
+          specificSpecific:[],
+          cronLastSpecificDomDay:1,
+          cronDaysBeforeEomMinus:1,
+          cronDaysNearestWeekday:1,
+        };
+        let week = {
+            cronEvery:'',
+            incrementStart:1,
+            incrementIncrement:1,
+            specificSpecific:[],
+            cronNthDayDay:1,
+            cronNthDayNth:'1',
+        };
+        if (!days.includes('?')) {
+          switch (true) {
+            case days.includes('*'):
+              day.cronEvery = '1';
+              break;
+            case days.includes('?'):
+              // 2、4、11
+              break;
+            case days.includes('/'):
+              day.cronEvery = '3';
+              day.incrementStart = days.split('/')[0];
+              day.incrementIncrement = days.split('/')[1];
+              break;
+            case days.includes(','):
+              day.cronEvery = '5';
+              day.specificSpecific = days.split(',').map(Number).sort();
+              // day.specificSpecific.forEach(function (value, index) {
+              //   day.specificSpecific[index] = value -1;
+              // });
+              break;
+            case days.includes('LW'):
+              day.cronEvery = '7';
+              break;
+            case days.includes('L-'):
+              day.cronEvery = '9';
+              day.cronDaysBeforeEomMinus = days.split('L-')[1];
+              break;
+            case days.includes('L'):
+
+              //alert(days);
+              if(days.len == 1){
+                day.cronEvery = '6';
+                day.cronLastSpecificDomDay = '1';
+              }
+              else
+              {
+                day.cronEvery = '8';
+                day.cronLastSpecificDomDay = Number(days.split('L')[0]);
+              }
+              break;
+            case days.includes('W'):
+              day.cronEvery = '10';
+              day.cronDaysNearestWeekday = days.split('W')[0];
+              break;
+            default:
+              day.cronEvery = '1';
+          }
+        }else {
+          switch (true){
+            case weeks.includes('/'):
+              day.cronEvery = '2';
+              week.incrementStart = weeks.split("/")[0];
+              week.incrementIncrement = weeks.split("/")[1];
+              break;
+            case weeks.includes(','):
+              day.cronEvery = '4';
+              week.specificSpecific = weeks.split(',').map(Number).sort();
+              break;
+            case '#':
+              day.cronEvery = '11';
+              week.cronNthDayDay = weeks.split("#")[0];
+              week.cronNthDayNth = weeks.split("#")[1];
+              break;
+            default:
+              day.cronEvery = '1';
+              week.cronEvery = '1';
+          }
+        }
+        this.result.day = day;
+        this.result.week = week;
+      },
+      monthsReverseExp(cron) {
+        let months = cron.split(" ")[4];
+        let month = {
+          cronEvery:'',
+            incrementStart:3,
+            incrementIncrement:5,
+            rangeStart:1,
+            rangeEnd:1,
+            specificSpecific:[],
+        };
+        switch (true){
+          case months.includes('*'):
+            month.cronEvery = '1';
+            break;
+          case months.includes('/'):
+            month.cronEvery = '2';
+            month.incrementStart = months.split('/')[0];
+            month.incrementIncrement = months.split('/')[1];
+            break;
+          case months.includes(','):
+            month.cronEvery = '3';
+            month.specificSpecific = months.split(',').map(Number).sort();
+            break;
+          case months.includes('-'):
+            month.cronEvery = '4';
+            month.rangeStart = months.split('-')[0];
+            month.rangeEnd = months.split('-')[1];
+            break;
+          default:
+            month.cronEvery = '1';
+        }
+        this.result.month = month;
+      },
+      yearReverseExp(cron) {
+        let years = cron.split(" ")[6];
+        let year = {
+          cronEvery:'',
+          incrementStart:3,
+          incrementIncrement:5,
+          rangeStart:2019,
+          rangeEnd:2019,
+          specificSpecific:[],
+        };
+        switch (true){
+          case years.includes('*'):
+            year.cronEvery = '1';
+            break;
+          case years.includes('/'):
+            year.cronEvery = '2';
+            year.incrementStart = years.split('/')[0];
+            year.incrementIncrement = years.split('/')[1];
+            break;
+          case years.includes(','):
+            year.cronEvery = '3';
+            year.specificSpecific = years.split(',').map(Number).sort();
+            break;
+          case years.includes('-'):
+            year.cronEvery = '4';
+            year.rangeStart = years.split('-')[0];
+            year.rangeEnd = years.split('-')[1];
+            break;
+          default:
+            year.cronEvery = '1';
+        }
+        this.result.year = year;
+      }
+    }
+  }
+</script>
+
+<style lang="scss">
+  .card-container {
+    background: #fff;
+    overflow: hidden;
+    padding: 12px;
+    position: relative;
+    width: 100%;
+    .ant-tabs{
+      border:1px solid #e6ebf5;
+      padding: 0;
+      .ant-tabs-bar {
+        margin: 0;
+        outline: none;
+        border-bottom: none;
+        .ant-tabs-nav-container{
+          margin: 0;
+          .ant-tabs-tab {
+            padding: 0 24px!important;
+            background-color: #f5f7fa!important;
+            margin-right: 0px!important;
+            border-radius: 0;
+            line-height: 38px;
+            border: 1px solid transparent!important;
+            border-bottom: 1px solid #e6ebf5!important;
+          }
+          .ant-tabs-tab-active.ant-tabs-tab{
+            color: #409eff;
+            background-color: #fff!important;
+            border-right:1px solid #e6ebf5!important;
+            border-left:1px solid #e6ebf5!important;
+            border-bottom:1px solid #fff!important;
+            font-weight: normal;
+            transition:none!important;
+          }
+        }
+      }
+      .ant-tabs-tabpane{
+        padding: 15px;
+        .ant-row{
+          margin: 10px 0;
+        }
+        .ant-select,.ant-input-number{
+          width: 100px;
+        }
+      }
+    }
+  }
+</style>
+<style lang="scss" scoped>
+  .container-widthEn{
+    width: 755px;
+  }
+  .container-widthCn{
+    width: 608px;
+  }
+  .language{
+    text-align: center;
+    position: absolute;
+    right: 13px;
+    top: 13px;
+    border: 1px solid transparent;
+    height: 40px;
+    line-height: 38px;
+    font-size: 16px;
+    color: #409eff;
+    z-index: 1;
+    background: #f5f7fa;
+    outline: none;
+    width: 47px;
+    border-bottom: 1px solid #e6ebf5;
+    border-radius: 0;
+  }
+  .card-container{
+    .bottom{
+      display: flex;
+      justify-content: center;
+      padding: 10px 0 0 0;
+      .cronButton{
+        margin: 0 10px;
+        line-height: 40px;
+      }
+    }
+  }
+  .tabBody{
+    .a-row{
+      margin: 10px 0;
+      .long{
+        .a-select{
+          width:354px;
+        }
+      }
+      .a-input-number{
+        width: 110px;
+      }
+    }
+  }
+</style>

+ 315 - 0
src/components/jeecgbiz/JSelectBizComponent/JSelectBizComponentModal.vue

@@ -0,0 +1,315 @@
+<template>
+  <a-modal
+    centered
+    :title="name + '选择'"
+    :width="width"
+    :visible="visible"
+    @ok="handleOk"
+    @cancel="close"
+    cancelText="关闭">
+
+    <a-row :gutter="18">
+      <a-col :span="16">
+        <!-- 查询区域 -->
+        <div class="table-page-search-wrapper">
+          <a-form layout="inline">
+            <a-row :gutter="24">
+
+              <a-col :span="14">
+                <a-form-item :label="(queryParamText||name)">
+                  <a-input v-model="queryParam[queryParamCode||valueKey]" :placeholder="'请输入' + (queryParamText||name)" @pressEnter="searchQuery"/>
+                </a-form-item>
+              </a-col>
+              <a-col :span="8">
+                  <span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
+                    <a-button type="primary" @click="searchQuery" icon="search">查询</a-button>
+                    <a-button type="primary" @click="searchReset" icon="reload" style="margin-left: 8px">重置</a-button>
+                  </span>
+              </a-col>
+
+            </a-row>
+          </a-form>
+        </div>
+
+        <a-table
+          size="small"
+          bordered
+          :rowKey="rowKey"
+          :columns="innerColumns"
+          :dataSource="dataSource"
+          :pagination="ipagination"
+          :loading="loading"
+          :scroll="{ y: 240 }"
+          :rowSelection="{selectedRowKeys, onChange: onSelectChange, type: multiple ? 'checkbox':'radio'}"
+          :customRow="customRowFn"
+          @change="handleTableChange">
+        </a-table>
+
+      </a-col>
+      <a-col :span="8">
+        <a-card :title="'已选' + name" :bordered="false" :head-style="{padding:0}" :body-style="{padding:0}">
+
+          <a-table size="small" :rowKey="rowKey" bordered v-bind="selectedTable">
+              <span slot="action" slot-scope="text, record, index">
+                <a @click="handleDeleteSelected(record, index)">删除</a>
+              </span>
+          </a-table>
+
+        </a-card>
+      </a-col>
+    </a-row>
+  </a-modal>
+</template>
+
+<script>
+  import { getAction } from '@/api/manage'
+  import { JeecgListMixin } from '@/mixins/JeecgListMixin'
+  import { cloneObject, pushIfNotExist } from '@/utils/util'
+
+  export default {
+    name: 'JSelectBizComponentModal',
+    mixins: [JeecgListMixin],
+    props: {
+      value: {
+        type: Array,
+        default: () => []
+      },
+      visible: {
+        type: Boolean,
+        default: false
+      },
+      valueKey: {
+        type: String,
+        required: true
+      },
+      multiple: {
+        type: Boolean,
+        default: true
+      },
+      width: {
+        type: Number,
+        default: 900
+      },
+
+      name: {
+        type: String,
+        default: ''
+      },
+      listUrl: {
+        type: String,
+        required: true,
+        default: ''
+      },
+      // 根据 value 获取显示文本的地址,例如存的是 username,可以通过该地址获取到 realname
+      valueUrl: {
+        type: String,
+        default: ''
+      },
+      displayKey: {
+        type: String,
+        default: null
+      },
+      columns: {
+        type: Array,
+        required: true,
+        default: () => []
+      },
+      // 查询条件Code
+      queryParamCode: {
+        type: String,
+        default: null
+      },
+      // 查询条件文字
+      queryParamText: {
+        type: String,
+        default: null
+      },
+      rowKey: {
+        type: String,
+        default: 'id'
+      },
+    },
+    data() {
+      return {
+        innerValue: [],
+        // 表头
+        innerColumns: this.columns,
+        // 已选择列表
+        selectedTable: {
+          pagination: false,
+          scroll: { y: 240 },
+          columns: [
+            {
+              ...this.columns[0],
+              width: this.columns[0].widthRight || this.columns[0].width,
+            },
+            { title: '操作', dataIndex: 'action', align: 'center', width: 60, scopedSlots: { customRender: 'action' }, }
+          ],
+          dataSource: [],
+        },
+        url: { list: this.listUrl },
+        /* 分页参数 */
+        ipagination: {
+          current: 1,
+          pageSize: 5,
+          pageSizeOptions: ['5', '10', '20', '30'],
+          showTotal: (total, range) => {
+            return range[0] + '-' + range[1] + ' 共' + total + '条'
+          },
+          showQuickJumper: true,
+          showSizeChanger: true,
+          total: 0
+        },
+        options: [],
+        dataSourceMap: {},
+      }
+    },
+    watch: {
+      value: {
+        deep: true,
+        immediate: true,
+        handler(val) {
+          this.innerValue = cloneObject(val)
+          this.selectedRowKeys = []
+          this.valueWatchHandler(val)
+          this.queryOptionsByValue(val)
+        }
+      },
+      dataSource: {
+        deep: true,
+        handler(val) {
+          this.emitOptions(val)
+          this.valueWatchHandler(this.innerValue)
+        }
+      },
+      selectedRowKeys: {
+        immediate: true,
+        deep: true,
+        handler(val) {
+          this.selectedTable.dataSource = val.map(key => {
+            for (let data of this.dataSource) {
+              if (data[this.rowKey] === key) {
+                pushIfNotExist(this.innerValue, data[this.valueKey])
+                return data
+              }
+            }
+            for (let data of this.selectedTable.dataSource) {
+              if (data[this.rowKey] === key) {
+                pushIfNotExist(this.innerValue, data[this.valueKey])
+                return data
+              }
+            }
+            console.warn('未找到选择的行信息,key:' + key)
+            return {}
+          })
+        },
+      }
+    },
+
+    methods: {
+
+      /** 关闭弹窗 */
+      close() {
+        this.$emit('update:visible', false)
+      },
+
+      valueWatchHandler(val) {
+        val.forEach(item => {
+          this.dataSource.concat(this.selectedTable.dataSource).forEach(data => {
+            if (data[this.valueKey] === item) {
+              pushIfNotExist(this.selectedRowKeys, data[this.rowKey])
+            }
+          })
+        })
+      },
+
+      queryOptionsByValue(value) {
+        if (!value || value.length === 0) {
+          return
+        }
+        // 判断options是否存在value,如果已存在数据就不再请求后台了
+        let notExist = false
+        for (let val of value) {
+          let find = false
+          for (let option of this.options) {
+            if (val === option.value) {
+              find = true
+              break
+            }
+          }
+          if (!find) {
+            notExist = true
+            break
+          }
+        }
+        if (!notExist) return
+        getAction(this.valueUrl || this.listUrl, {
+          // 这里最后加一个 , 的原因是因为无论如何都要使用 in 查询,防止后台进行了模糊匹配,导致查询结果不准确
+          [this.valueKey]: value.join(',') + ',',
+          pageNo: 1,
+          pageSize: value.length
+        }).then((res) => {
+          if (res.success) {
+            let dataSource = res.result
+            if (!(dataSource instanceof Array)) {
+              dataSource = res.result.records
+            }
+            this.emitOptions(dataSource, (data) => {
+              pushIfNotExist(this.innerValue, data[this.valueKey])
+              pushIfNotExist(this.selectedRowKeys, data[this.rowKey])
+              pushIfNotExist(this.selectedTable.dataSource, data, this.rowKey)
+            })
+          }
+        })
+      },
+
+      emitOptions(dataSource, callback) {
+        dataSource.forEach(data => {
+          let key = data[this.valueKey]
+          this.dataSourceMap[key] = data
+          pushIfNotExist(this.options, { label: data[this.displayKey || this.valueKey], value: key }, 'value')
+          typeof callback === 'function' ? callback(data) : ''
+        })
+        this.$emit('options', this.options, this.dataSourceMap)
+      },
+
+      /** 完成选择 */
+      handleOk() {
+        let value = this.selectedTable.dataSource.map(data => data[this.valueKey])
+        this.$emit('input', value)
+        this.close()
+      },
+
+      /** 删除已选择的 */
+      handleDeleteSelected(record, index) {
+        this.selectedRowKeys.splice(this.selectedRowKeys.indexOf(record[this.rowKey]), 1)
+        this.selectedTable.dataSource.splice(index, 1)
+      },
+
+      customRowFn(record) {
+        return {
+          on: {
+            click: () => {
+              let key = record[this.rowKey]
+              if (!this.multiple) {
+                this.selectedRowKeys = [key]
+                this.selectedTable.dataSource = [record]
+              } else {
+                let index = this.selectedRowKeys.indexOf(key)
+                if (index === -1) {
+                  this.selectedRowKeys.push(key)
+                  this.selectedTable.dataSource.push(record)
+                } else {
+                  this.handleDeleteSelected(record, index)
+                }
+              }
+            }
+          }
+        }
+      },
+
+    }
+  }
+</script>
+<style lang="less" scoped>
+</style>

+ 36 - 0
src/components/jeecgbiz/JSelectBizComponent/README.md

@@ -0,0 +1,36 @@
+# JSelectBizComponent
+
+Jeecg 选择组件的公共可复用组件
+
+## 引用方式
+
+```js
+import JSelectBizComponent from '@/src/components/jeecgbiz/JSelectBizComponent'
+
+export default {
+  components: { JSelectBizComponent }
+}
+```
+
+## 参数
+
+### 配置参数
+
+| 参数名                | 类型    | 必填 | 默认值       | 备注                                                                                 |
+|-----------------------|---------|------|--------------|--------------------------------------------------------------------------------------|
+| rowKey                | String  |      | "id"         | 唯一标识的字段名                                                                     |
+| value(v-model)        | String  |      | ""           | 默认选择的数据,多个用半角逗号分割                                                   |
+| name                  | String  |      | ""           | 显示名字,例如选择用户就填写"用户"                                                   |
+| listUrl               | String  | 是   |              | 数据请求地址,必须是封装了分页的地址                                                 |
+| valueUrl              | String  |      | ""           | 获取显示文本的地址,例如存的是 username,可以通过该地址获取到 realname               |
+| displayKey            | String  |      | null         | 显示在标签上的字段 key    ,不传则直接显示数据                                       |
+| returnKeys            | Array   |      | ['id', 'id'] | v-model 绑定的 keys,是个数组,默认使用第二项,当配置了 `returnId=true` 就返回第一项 |
+| returnId              | Boolean |      | false        | 返回ID,设为true后将返回配置的 `returnKeys` 中的第一项                               |
+| selectButtonText      | String  |      | "选择"       | 选择按钮的文字                                                                       |
+| queryParamText        | String  |      | null         | 查询条件显示文字,不传则使用 `name`                                                  |
+| columns               | Array   | 是   |              | 列配置项,与antd的table的配置完全一致。列的第一项会被配置成右侧已选择的列表上        |
+| columns[0].widthRight | Array   |      | null         | 仅列的第一项可以应用此配置,表示右侧已选择列表的宽度,建议 `70%`,不传则应用`width`  |
+| placeholder           | String  |      | "请选择"     | 占位符                                                                               |
+| disabled              | Boolean |      | false        | 是否禁用                                                                             |
+| multiple              | Boolean |      | false        | 是否可多选                                                                           |
+| buttons               | Boolean |      | true         | 是否显示"选择"按钮,如果不显示,可以直接点击文本框打开选择界面                       |

+ 158 - 0
src/components/jeecgbiz/JSelectBizComponent/index.vue

@@ -0,0 +1,158 @@
+<template>
+  <a-row class="j-select-biz-component-box" type="flex" :gutter="8">
+    <a-col class="left" :class="{'full': !buttons}">
+      <slot name="left">
+        <a-select
+          mode="multiple"
+          :placeholder="placeholder"
+          v-model="selectValue"
+          :options="selectOptions"
+          allowClear
+          :disabled="disabled"
+          :open="false"
+          style="width: 100%;"
+          @click.native="visible=(buttons?visible:true)"
+        />
+      </slot>
+    </a-col>
+
+    <a-col v-if="buttons" class="right">
+      <a-button type="primary" icon="search" :disabled="disabled" @click="visible=true">{{selectButtonText}}</a-button>
+    </a-col>
+
+    <j-select-biz-component-modal
+      v-model="selectValue"
+      :visible.sync="visible"
+      v-bind="modalProps"
+      @options="handleOptions"
+    />
+  </a-row>
+</template>
+
+<script>
+  import JSelectBizComponentModal from './JSelectBizComponentModal'
+
+  export default {
+    name: 'JSelectBizComponent',
+    components: { JSelectBizComponentModal },
+    props: {
+      value: {
+        type: String,
+        default: ''
+      },
+      /** 是否返回 id,默认 false,返回 code */
+      returnId: {
+        type: Boolean,
+        default: false
+      },
+      placeholder: {
+        type: String,
+        default: '请选择'
+      },
+      disabled: {
+        type: Boolean,
+        default: false
+      },
+      // 是否支持多选,默认 true
+      multiple: {
+        type: Boolean,
+        default: true
+      },
+      // 是否显示按钮,默认 true
+      buttons: {
+        type: Boolean,
+        default: true
+      },
+      // 显示的 Key
+      displayKey: {
+        type: String,
+        default: null
+      },
+      // 返回的 key
+      returnKeys: {
+        type: Array,
+        default: () => ['id', 'id']
+      },
+      // 选择按钮文字
+      selectButtonText: {
+        type: String,
+        default: '选择'
+      },
+
+    },
+    data() {
+      return {
+        selectValue: [],
+        selectOptions: [],
+        dataSourceMap: {},
+        visible: false
+      }
+    },
+    computed: {
+      valueKey() {
+        return this.returnId ? this.returnKeys[0] : this.returnKeys[1]
+      },
+      modalProps() {
+        return Object.assign({
+          valueKey: this.valueKey,
+          multiple: this.multiple,
+          returnKeys: this.returnKeys,
+          displayKey: this.displayKey || this.valueKey
+        }, this.$attrs)
+      },
+    },
+    watch: {
+      value: {
+        immediate: true,
+        handler(val) {
+          if (val) {
+            this.selectValue = val.split(',')
+          } else {
+            this.selectValue = []
+          }
+        }
+      },
+      selectValue: {
+        deep: true,
+        handler(val) {
+          let rows = val.map(key => this.dataSourceMap[key])
+          this.$emit('select', rows)
+          let data = val.join(',')
+          this.$emit('input', data)
+          this.$emit('change', data)
+        }
+      }
+    },
+    methods: {
+      handleOptions(options, dataSourceMap) {
+        this.selectOptions = options
+        this.dataSourceMap = dataSourceMap
+      },
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  .j-select-biz-component-box {
+
+    $width: 82px;
+
+    .left {
+      width: calc(100% - #{$width} - 8px);
+    }
+
+    .right {
+      width: #{$width};
+    }
+
+    .full {
+      width: 100%;
+    }
+
+    /deep/ {
+      .ant-select-search__field {
+        display: none !important;
+      }
+    }
+  }
+</style>

+ 37 - 0
src/components/jeecgbiz/JSelectPosition.vue

@@ -0,0 +1,37 @@
+<template>
+  <j-select-biz-component :width="1000" v-bind="configs" v-on="$listeners"/>
+</template>
+
+<script>
+  import JSelectBizComponent from './JSelectBizComponent'
+
+  export default {
+    name: 'JSelectPosition',
+    components: { JSelectBizComponent },
+    props: ['value'],
+    data() {
+      return {
+        settings: {
+          name: '职务',
+          displayKey: 'name',
+          returnKeys: ['id', 'code'],
+          listUrl: '/sys/position/list',
+          queryParamCode: 'name',
+          queryParamText: '职务名称',
+          columns: [
+            { title: '职务名称', dataIndex: 'name', align: 'center', width: '30%', widthRight: '70%' },
+            { title: '职务编码', dataIndex: 'code', align: 'center', width: '35%' },
+            { title: '职级', dataIndex: 'rank_dictText', align: 'center', width: '25%' }
+          ]
+        }
+      }
+    },
+    computed: {
+      configs() {
+        return Object.assign({ value: this.value }, this.settings, this.$attrs)
+      }
+    }
+  }
+</script>
+
+<style lang="scss" scoped></style>

+ 38 - 0
src/components/jeecgbiz/JSelectRole.vue

@@ -0,0 +1,38 @@
+<template>
+  <j-select-biz-component
+    :value="value"
+
+    name="角色"
+    displayKey="roleName"
+
+    :returnKeys="returnKeys"
+    :listUrl="url.list"
+    :columns="columns"
+    queryParamText="角色编码"
+
+    v-on="$listeners"
+    v-bind="$attrs"
+  />
+</template>
+
+<script>
+  import JSelectBizComponent from './JSelectBizComponent'
+
+  export default {
+    name: 'JSelectMultiUser',
+    components: { JSelectBizComponent },
+    props: ['value'],
+    data() {
+      return {
+        returnKeys: ['id', 'roleCode'],
+        url: { list: '/sys/role/list' },
+        columns: [
+          { title: '角色名称', dataIndex: 'roleName', align: 'center', width: 120 },
+          { title: '角色编码', dataIndex: 'roleCode', align: 'center', width: 120 }
+        ]
+      }
+    }
+  }
+</script>
+
+<style lang="scss" scoped></style>

+ 14 - 6
src/router/index.js

@@ -2,14 +2,22 @@ import Vue from 'vue'
 import Router from 'vue-router'
 import {constantRouterMap} from '@/config/router.config'
 
+try {
+    const originalPush = Router.prototype.push
+    Router.prototype.push = function push(location) {
+      return originalPush.call(this, location).catch(err => err)
+    }
+  } catch (e) {
+  }
+
 Vue.use(Router)
 
-const originalPush = Router.prototype.push;
-Router.prototype.push = function push(location) {
-  if(location.name){
-    return originalPush.call(this, location).catch(err => err)
-  }
-}
+// const originalPush = Router.prototype.push;
+// Router.prototype.push = function push(location) {
+//   if(location.name){
+//     return originalPush.call(this, location).catch(err => err)
+//   }
+// }
 
 export default new Router({
   mode: 'history',

+ 107 - 0
src/utils/LunarFullCalendarUtil.js

@@ -0,0 +1,107 @@
+/**
+ * LunarFullCalendar 公共 js
+ *
+ * @version 1.0.0
+ * @author sunjianlei
+ *
+ * */
+
+import { getRefPromise } from '@/utils/JEditableTableUtil'
+
+/* 日历的视图类型 */
+const calendarViewType = {
+  month: 'month', // 月视图
+  basicWeek: 'basicWeek',  // 基础周视图
+  basicDay: 'basicDay',//  基础天视图
+  agendaWeek: 'agendaWeek', // 议程周视图
+  agendaDay: 'agendaDay', // 议程天视图
+}
+
+/* 定义默认视图 */
+const defaultView = calendarViewType.month
+
+/* 定义日历默认配置 */
+const defaultSettings = {
+  locale: 'zh-cn',
+  // 按钮文字
+  buttonText: {
+    today: '今天',
+    month: '月',
+    week: '周',
+    day: '日'
+  },
+  // 头部排列方式
+  header: {
+    left: 'prev,next, today',
+    center: 'title',
+    right: 'hide, custom, month,agendaWeek,agendaDay'
+  },
+  //点击今天日列表图
+  eventLimitClick: 'day',
+  // 隐藏超出的事件
+  eventLimit: true,
+  // 设置每周开始日期为周日
+  firstDay: 0,
+  // 默认显示视图
+  defaultView,
+  timeFormat: 'H:mm',
+  axisFormat: 'H:mm',
+  // agenda视图下是否显示all-day
+  allDaySlot: true,
+  // agenda视图下all-day的显示文本
+  allDayText: '全天',
+  // 时区默认本地的
+  timezone: 'local',
+  // 周视图和日视同的左侧时间显示
+  slotLabelFormat: 'HH:mm',
+  // 设置第二天阈值
+  nextDayThreshold: '00:00:00',
+}
+
+/** 提供了一些增强方法 */
+const CalendarMixins = {
+  data() {
+    return {
+      calenderCurrentViewType: defaultView
+    }
+  },
+  methods: {
+
+    getCalendarConfigEventHandler() {
+      return {
+        // 处理 view changed 事件
+        viewRender: (view, element) => {
+          let { type } = view
+
+          let lastViewType = this.calenderCurrentViewType
+          this.calenderCurrentViewType = type
+
+          if (typeof this.handleViewRender === 'function') {
+            this.handleViewRender(type, view, element)
+          }
+
+          if (lastViewType !== this.calenderCurrentViewType && typeof this.handleViewChanged === 'function') {
+            this.handleViewChanged(type, view, element)
+          }
+
+        },
+      }
+    },
+
+    /** 获取 LunarFullCalendar 实例,ref = baseCalendar */
+    getCalendar(fn) {
+      return getRefPromise(this, 'baseCalendar').then(fn)
+    },
+
+    calendarEmit(name, data) {
+      this.getCalendar(ref => ref.$emit(name, data))
+    },
+
+    /** 强制重新加载所有的事件(日程)*/
+    calendarReloadEvents() {
+      this.calendarEmit('reload-events')
+    }
+  }
+}
+
+export { defaultSettings, calendarViewType, CalendarMixins }

+ 40 - 0
src/utils/util.js

@@ -274,3 +274,43 @@ export const transformTozTreeFormat = (sNodes, setting={}) => {
         return [sNodes];
     }
 }
+
+/**
+ * 如果值不存在就 push 进数组,反之不处理
+ * @param array 要操作的数据
+ * @param value 要添加的值
+ * @param key 可空,如果比较的是对象,可能存在地址不一样但值实际上是一样的情况,可以传此字段判断对象中唯一的字段,例如 id。不传则直接比较实际值
+ * @returns {boolean} 成功 push 返回 true,不处理返回 false
+ */
+export function pushIfNotExist(array, value, key) {
+    for (let item of array) {
+      if (key && (item[key] === value[key])) {
+        return false
+      } else if (item === value) {
+        return false
+      }
+    }
+    array.push(value)
+    return true
+  }
+
+  /**
+ * 重复值验证工具方法
+ *
+ * 使用示例:
+ * { validator: (rule, value, callback) => validateDuplicateValue('sys_fill_rule', 'rule_code', value, this.model.id, callback) }
+ *
+ * @param tableName 被验证的表名
+ * @param fieldName 被验证的字段名
+ * @param fieldVal 被验证的值
+ * @param dataId 数据ID,可空
+ * @param callback
+ */
+export function validateDuplicateValue(tableName, fieldName, fieldVal, dataId, callback) {
+    let params = { tableName, fieldName, fieldVal, dataId }
+    api.duplicateCheck(params).then(res => {
+      res['success'] ? callback() : callback(res['message'])
+    }).catch(err => {
+      callback(err.message || err)
+    })
+  }

+ 519 - 0
src/views/dashboard/IndexBdc.vue

@@ -0,0 +1,519 @@
+<template>
+  <div class="page-header-index-wide">
+    <a-row :gutter="24">
+      <a-col :sm="24" :md="12" :xl="6" :style="{ marginBottom: '24px' }">
+        <chart-card :loading="loading" title="受理量" :total="cardCount.sll | NumberFormat">
+          <a-tooltip title="指标说明" slot="action">
+            <a-icon type="info-circle-o" />
+          </a-tooltip>
+          <div>
+            <mini-area :datasource="chartData.sll" />
+          </div>
+          <template slot="footer">今日受理量:<span>{{ todaySll }}</span></template>
+        </chart-card>
+      </a-col>
+      <a-col :sm="24" :md="12" :xl="6" :style="{ marginBottom: '24px' }">
+        <chart-card :loading="loading" title="办结量" :total="cardCount.bjl | NumberFormat">
+          <a-tooltip title="指标说明" slot="action">
+            <a-icon type="info-circle-o" />
+          </a-tooltip>
+          <div>
+            <mini-area :datasource="chartData.bjl"/>
+          </div>
+          <template slot="footer">今日办结量:<span>{{ todayBjl }}</span></template>
+        </chart-card>
+      </a-col>
+      <a-col :sm="24" :md="12" :xl="6" :style="{ marginBottom: '24px' }">
+        <chart-card :loading="loading" title="用户受理量" :total="cardCount.isll | NumberFormat">
+          <a-tooltip title="指标说明" slot="action">
+            <a-icon type="info-circle-o" />
+          </a-tooltip>
+          <div>
+            <mini-bar :datasource="chartData.isll" :height="50"/>
+          </div>
+          <template slot="footer">用户今日受理量:<span>{{ todayISll }}</span></template>
+        </chart-card>
+      </a-col>
+      <a-col :sm="24" :md="12" :xl="6" :style="{ marginBottom: '24px' }">
+        <chart-card :loading="loading" title="用户办结量" :total="cardCount.ibjl | NumberFormat">
+          <a-tooltip title="指标说明" slot="action">
+            <a-icon type="info-circle-o" />
+          </a-tooltip>
+          <div>
+            <mini-bar :datasource="chartData.ibjl" :height="50"/>
+          </div>
+          <template slot="footer">用户今日办结量:<span>{{ todayIBjl }}</span></template>
+        </chart-card>
+      </a-col>
+    </a-row>
+
+    <a-card :loading="loading" :bordered="false" :body-style="{padding: '0'}">
+      <div class="salesCard">
+        <a-tabs default-active-key="1" size="large" :tab-bar-style="{marginBottom: '24px', paddingLeft: '16px'}">
+          <div class="extra-wrapper" slot="tabBarExtraContent">
+            <div class="extra-item">
+              <a>今日</a>
+              <a>本周</a>
+              <a>本月</a>
+              <a>本年</a>
+            </div>
+            <a-range-picker :style="{width: '256px'}" />
+          </div>
+
+          <a-tab-pane loading="true" tab="受理监管" key="1">
+            <a-row>
+              <a-col :xl="16" :lg="12" :md="12" :sm="24" :xs="24">
+                <index-bar title="受理量统计" />
+              </a-col>
+              <a-col :xl="8" :lg="12" :md="12" :sm="24" :xs="24">
+
+                <a-card title="快速开始 / 便捷导航" style="margin-bottom: 24px" :bordered="false" :body-style="{padding: 0}">
+                  <div class="item-group">
+                    <a-row>
+                      <a-col :class="'more-btn'" :span="12" v-for="(item,index) in registerTypeList" :key=" 'registerType'+index ">
+                        <a-button @click="goPage(index)" style="margin-bottom:10px" size="small" type="primary" ghost>{{ item.text }}</a-button>
+                      </a-col>
+                    </a-row>
+                  </div>
+                </a-card>
+
+              </a-col>
+            </a-row>
+          </a-tab-pane>
+
+          <a-tab-pane tab="交互监管" key="2">
+            <a-row>
+              <a-col :xl="16" :lg="12" :md="12" :sm="24" :xs="24">
+                <bar-multid :sourceData="jhjgData" :fields="jhjgFields" title="平台与部门交互量统计"></bar-multid>
+              </a-col>
+              <a-col :xl="8" :lg="12" :md="12" :sm="24" :xs="24">
+
+                <a-card title="快速开始 / 便捷导航" style="margin-bottom: 24px" :bordered="false" :body-style="{padding: 0}">
+                  <div class="item-group">
+                    <a-row>
+                      <a-col :class="'more-btn'" :span="12" v-for="(item,index) in registerTypeList" :key=" 'registerType'+index ">
+                        <a-button @click="goPage(index)" style="margin-bottom:10px" size="small" type="primary" ghost>{{ item.text }}</a-button>
+                      </a-col>
+                    </a-row>
+                  </div>
+                </a-card>
+
+              </a-col>
+            </a-row>
+          </a-tab-pane>
+
+          <a-tab-pane tab="存储监管" key="4">
+            <a-row>
+              <a-col :xl="16" :lg="12" :md="12" :sm="24" :xs="24">
+                <a-row>
+                  <template v-if="diskInfo && diskInfo.length>0">
+                    <a-col :span="12" v-for="(item,index) in diskInfo" :key=" 'diskInfo'+index ">
+                      <dash-chart-demo :title="item.name" :datasource="item.restPPT"></dash-chart-demo>
+                    </a-col>
+                  </template>
+                </a-row>
+              </a-col>
+
+              <a-col :xl="8" :lg="12" :md="12" :sm="24" :xs="24">
+
+                <a-card title="快速开始 / 便捷导航" style="margin-bottom: 24px" :bordered="false" :body-style="{padding: 0}">
+                  <div class="item-group">
+                    <a-row>
+                      <a-col :class="'more-btn'" :span="10" v-for="(item,index) in registerTypeList" :key=" 'registerType'+index ">
+                        <a-button @click="goPage(index)" style="margin-bottom:10px" size="small" type="primary" ghost>{{ item.text }}</a-button>
+                      </a-col>
+                    </a-row>
+                  </div>
+                </a-card>
+
+              </a-col>
+            </a-row>
+          </a-tab-pane>
+
+        </a-tabs>
+
+      </div>
+    </a-card>
+
+    <a-row :gutter="12">
+      <a-card :loading="loading" :class="{ 'anty-list-cust':true }" :bordered="false" :style="{ marginTop: '24px' }">
+
+        <a-tabs v-model="indexBottomTab" size="large" :tab-bar-style="{marginBottom: '24px', paddingLeft: '16px'}">
+          <div class="extra-wrapper" slot="tabBarExtraContent">
+            <a-radio-group v-model="indexRegisterType" @change="changeRegisterType">
+              <a-radio-button value="转移登记">转移登记</a-radio-button>
+              <a-radio-button value="抵押登记">抵押登记</a-radio-button>
+              <a-radio-button value="">所有</a-radio-button>
+            </a-radio-group>
+          </div>
+
+          <a-tab-pane loading="true" tab="业务流程限时监管" key="1">
+
+            <a-table :dataSource="dataSource1" size="default" rowKey="id" :columns="columns" :pagination="ipagination1" @change="tableChange1">
+              <template slot="flowRate" slot-scope="text, record, index">
+                <a-progress :strokeColor="getPercentColor(record.flowRate)" :format="getPercentFormat" :percent="getFlowRateNumber(record.flowRate)" style="width:80px" />
+              </template>
+            </a-table>
+          </a-tab-pane>
+
+          <a-tab-pane loading="true" tab="业务节点限时监管" key="2">
+            <a-table :dataSource="dataSource2" size="default" rowKey="id" :columns="columns2" :pagination="ipagination2" @change="tableChange2">
+              <template slot="flowRate" slot-scope="text, record, index">
+                <span style="color: red;">{{ record.flowRate }}小时</span>
+              </template>
+            </a-table>
+          </a-tab-pane>
+
+        </a-tabs>
+
+
+      </a-card>
+    </a-row>
+
+  </div>
+</template>
+
+<script>
+
+  import ACol from "ant-design-vue/es/grid/Col"
+  import ATooltip from "ant-design-vue/es/tooltip/Tooltip"
+  import ChartCard from '@/components/ChartCard'
+  import MiniBar from '@/components/chart/MiniBar'
+  import MiniArea from '@/components/chart/MiniArea'
+  import IndexBar from '@/components/chart/IndexBar'
+  import BarMultid from '@/components/chart/BarMultid'
+  import DashChartDemo from '@/components/chart/DashChartDemo'
+
+  const jhjgData = [
+    { type: '房管', '1月': 900, '2月': 1120, '3月': 1380, '4月': 1480, '5月': 1450, '6月': 1100, '7月':1300, '8月':900,'9月':1000 ,'10月':1200 ,'11月':600 ,'12月':900 },
+    { type: '税务', '1月':1200, '2月': 1500, '3月': 1980, '4月': 2000, '5月': 1000, '6月': 600, '7月':900, '8月':1100,'9月':1300 ,'10月':2000 ,'11月':900 ,'12月':1100 },
+    { type: '不动产', '1月':2000, '2月': 1430, '3月': 1300, '4月': 1400, '5月': 900, '6月': 500, '7月':600, '8月':1000,'9月':600 ,'10月':1000 ,'11月':1500 ,'12月':1200 }
+  ]
+
+  const jhjgFields=[
+    '1月','2月','3月','4月','5月','6月',
+    '7月','8月','9月','10月','11月','12月'
+  ]
+
+  const xljgData = [
+    {type:'一月',"房管":1.12,"税务":1.55,"不动产":1.2},
+    {type:'二月',"房管":1.65,"税务":1.32,"不动产":1.42},
+    {type:'三月',"房管":1.85,"税务":1.1,"不动产":1.5},
+
+    {type:'四月',"房管":1.33,"税务":1.63,"不动产":1.4},
+    {type:'五月',"房管":1.63,"税务":1.8,"不动产":1.7},
+    {type:'六月',"房管":1.85,"税务":1.98,"不动产":1.8},
+
+    {type:'七月',"房管":1.98,"税务":1.5,"不动产":1.76},
+    {type:'八月',"房管":1.48,"税务":1.2,"不动产":1.3},
+    {type:'九月',"房管":1.41,"税务":1.9,"不动产":1.6},
+
+    {type:'十月',"房管":1.1,"税务":1.1,"不动产":1.4},
+    {type:'十一月',"房管":1.85,"税务":1.6,"不动产":1.5},
+    {type:'十二月',"房管":1.5,"税务":1.4,"不动产":1.3}
+  ]
+  const xljgFields=["房管","税务","不动产"]
+
+  const dataCol1 = [{
+    title: '业务号',
+    align:"center",
+    dataIndex: 'reBizCode'
+  },{
+    title: '业务类型',
+    align:"center",
+    dataIndex: 'type'
+  },{
+    title: '受理人',
+    align:"center",
+    dataIndex: 'acceptBy'
+  },{
+    title: '受理时间',
+    align:"center",
+    dataIndex: 'acceptDate'
+  },{
+    title: '当前节点',
+    align:"center",
+    dataIndex: 'curNode'
+  },{
+    title: '办理时长',
+    align:"center",
+    dataIndex: 'flowRate',
+    scopedSlots: { customRender: 'flowRate' }
+  }];
+  const dataSource1=[
+    {reBizCode:"1",type:"转移登记",acceptBy:'张三',acceptDate:"2019-01-22",curNode:"任务分派",flowRate:60},
+    {reBizCode:"2",type:"抵押登记",acceptBy:'李四',acceptDate:"2019-01-23",curNode:"领导审核",flowRate:30},
+    {reBizCode:"3",type:"转移登记",acceptBy:'王武',acceptDate:"2019-01-25",curNode:"任务处理",flowRate:20},
+    {reBizCode:"4",type:"转移登记",acceptBy:'赵楼',acceptDate:"2019-11-22",curNode:"部门审核",flowRate:80},
+    {reBizCode:"5",type:"转移登记",acceptBy:'钱就',acceptDate:"2019-12-12",curNode:"任务分派",flowRate:90},
+    {reBizCode:"6",type:"转移登记",acceptBy:'孙吧',acceptDate:"2019-03-06",curNode:"任务处理",flowRate:10},
+    {reBizCode:"7",type:"抵押登记",acceptBy:'周大',acceptDate:"2019-04-13",curNode:"任务分派",flowRate:100},
+    {reBizCode:"8",type:"抵押登记",acceptBy:'吴二',acceptDate:"2019-05-09",curNode:"任务上报",flowRate:50},
+    {reBizCode:"9",type:"抵押登记",acceptBy:'郑爽',acceptDate:"2019-07-12",curNode:"任务处理",flowRate:63},
+    {reBizCode:"20",type:"抵押登记",acceptBy:'林有',acceptDate:"2019-12-12",curNode:"任务打回",flowRate:59},
+    {reBizCode:"11",type:"转移登记",acceptBy:'码云',acceptDate:"2019-09-10",curNode:"任务签收",flowRate:87},
+  ]
+
+  const dataCol2 = [{
+    title: '业务号',
+    align:"center",
+    dataIndex: 'reBizCode'
+  },{
+    title: '受理人',
+    align:"center",
+    dataIndex: 'acceptBy'
+  },{
+    title: '发起时间',
+    align:"center",
+    dataIndex: 'acceptDate'
+  },{
+    title: '当前节点',
+    align:"center",
+    dataIndex: 'curNode'
+  },{
+    title: '超时时间',
+    align:"center",
+    dataIndex: 'flowRate',
+    scopedSlots: { customRender: 'flowRate' }
+  }];
+  const dataSource2=[
+    {reBizCode:"A001",type:"转移登记",acceptBy:'张四',acceptDate:"2019-01-22",curNode:"任务分派",flowRate:12},
+    {reBizCode:"A002",type:"抵押登记",acceptBy:'李吧',acceptDate:"2019-01-23",curNode:"任务签收",flowRate:3},
+    {reBizCode:"A003",type:"转移登记",acceptBy:'王三',acceptDate:"2019-01-25",curNode:"任务处理",flowRate:24},
+    {reBizCode:"A004",type:"转移登记",acceptBy:'赵二',acceptDate:"2019-11-22",curNode:"部门审核",flowRate:10},
+    {reBizCode:"A005",type:"转移登记",acceptBy:'钱大',acceptDate:"2019-12-12",curNode:"任务签收",flowRate:8},
+    {reBizCode:"A006",type:"转移登记",acceptBy:'孙就',acceptDate:"2019-03-06",curNode:"任务处理",flowRate:10},
+    {reBizCode:"A007",type:"抵押登记",acceptBy:'周晕',acceptDate:"2019-04-13",curNode:"部门审核",flowRate:24},
+    {reBizCode:"A008",type:"抵押登记",acceptBy:'吴有',acceptDate:"2019-05-09",curNode:"部门审核",flowRate:30},
+    {reBizCode:"A009",type:"抵押登记",acceptBy:'郑武',acceptDate:"2019-07-12",curNode:"任务分派",flowRate:1},
+    {reBizCode:"A0010",type:"抵押登记",acceptBy:'林爽',acceptDate:"2019-12-12",curNode:"部门审核",flowRate:16},
+    {reBizCode:"A0011",type:"转移登记",acceptBy:'码楼',acceptDate:"2019-09-10",curNode:"部门审核",flowRate:7},
+  ]
+
+  export default {
+    name: "IndexBdc",
+    components: {
+      ATooltip,
+      ACol,
+      ChartCard,
+      MiniArea,
+      MiniBar,
+      DashChartDemo,
+      BarMultid,
+      IndexBar
+    },
+    data() {
+      return {
+        loading: true,
+        cardCount:{
+          sll:100,
+          bjl:87,
+          isll:15,
+          ibjl:9
+        },
+
+        todaySll:60,
+        todayBjl:54,
+        todayISll:13,
+        todayIBjl:7,
+
+        chartData:{
+          sll:[],
+          bjl:[],
+          isll:[],
+          ibjl:[]
+        },
+        jhjgFields,
+        jhjgData,
+
+        xljgData,
+        xljgFields,
+
+        diskInfo:[
+          {name:"C盘",restPPT:7},
+          {name:"D盘",restPPT:5}
+        ],
+
+        registerTypeList:[{
+          text:"业务受理"
+        },{
+          text:"业务管理"
+        },{
+          text:"文件管理"
+        },{
+          text:"信息查询"
+        }],
+
+        dataSource1:[],
+        dataSource2:[],
+        columns:dataCol1,
+        columns2:dataCol2,
+        ipagination1:{
+          current: 1,
+          pageSize: 5,
+          pageSizeOptions: ['10', '20', '30'],
+          showTotal: (total, range) => {
+            return range[0] + "-" + range[1] + " 共" + total + "条"
+          },
+          showQuickJumper: true,
+          showSizeChanger: true,
+          total: 0,
+
+        },
+        ipagination2:{
+          current: 1,
+          pageSize: 5,
+          pageSizeOptions: ['10', '20', '30'],
+          showTotal: (total, range) => {
+            return range[0] + "-" + range[1] + " 共" + total + "条"
+          },
+          showQuickJumper: true,
+          showSizeChanger: true,
+          total: 0,
+        },
+        indexRegisterType:"转移登记",
+        indexBottomTab:"1"
+
+      }
+    },
+    methods:{
+      goPage(){
+        this.$message.success("根据业务自行处理跳转页面!")
+      },
+      changeRegisterType(e){
+        this.indexRegisterType = e.target.value
+        if(this.indexBottomTab=="1"){
+          this.loadDataSource1()
+        }else{
+          this.loadDataSource2()
+        }
+      },
+      tableChange1(pagination){
+        this.ipagination1.current = pagination.current
+        this.ipagination1.pageSize = pagination.pageSize
+        this.queryTimeoutInfo()
+      },
+      tableChange2(pagination){
+        this.ipagination2.current = pagination.current
+        this.ipagination2.pageSize = pagination.pageSize
+        this.queryNodeTimeoutInfo()
+      },
+      getFlowRateNumber(value){
+        return Number(value)
+      },
+      getPercentFormat(value){
+        if(value==100){
+          return "超时"
+        }else{
+          return value+"%"
+        }
+      },
+      getPercentColor(value){
+        let p = Number(value)
+        if(p>=90 && p<100){
+          return 'rgb(244, 240, 89)'
+        }else if(p>=100){
+          return 'red'
+        }else{
+          return 'rgb(16, 142, 233)'
+        }
+      },
+
+      loadDataSource1(){
+        this.dataSource1 = dataSource1.filter(item=>{
+          if(!this.indexRegisterType){
+            return true
+          }
+          return item.type==this.indexRegisterType
+        })
+      },
+      loadDataSource2(){
+        this.dataSource2 = dataSource2.filter(item=>{
+          if(!this.indexRegisterType){
+            return true
+          }
+          return item.type==this.indexRegisterType
+        })
+      }
+    },
+    created() {
+      this.loadDataSource1()
+      this.loadDataSource2()
+      setTimeout(() => {
+        this.loading = !this.loading
+      }, 1000)
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  .extra-wrapper {
+    line-height: 55px;
+    padding-right: 24px;
+
+    .extra-item {
+      display: inline-block;
+      margin-right: 24px;
+
+      a {
+        margin-left: 24px;
+      }
+    }
+  }
+
+  .item-group {
+    padding: 20px 0 8px 24px;
+    font-size: 0;
+    a {
+      color: rgba(0, 0, 0, 0.65);
+      display: inline-block;
+      font-size: 14px;
+      margin-bottom: 13px;
+      width: 25%;
+    }
+  }
+
+  .item-group {
+    .more-btn {
+      margin-bottom: 13px;
+      text-align: center;
+    }
+  }
+
+  .list-content-item {
+    color: rgba(0, 0, 0, .45);
+    display: inline-block;
+    vertical-align: middle;
+    font-size: 14px;
+    margin-left: 40px;
+  }
+
+  @media only screen and (min-width: 1600px) {
+    .list-content-item{
+      margin-left:60px;
+    }
+  }
+
+  @media only screen and (max-width: 1300px) {
+    .list-content-item{
+      margin-left:20px;
+    }
+    .width-hidden4{
+      display:none
+    }
+  }
+  .list-content-item{
+    span{line-height: 20px;}
+  }
+  .list-content-item{
+    p{margin-top: 4px;margin-bottom:0;line-height:22px;}
+  }
+  .anty-list-cust {
+    .ant-list-item-meta{flex: 0.3 !important;}
+  }
+  .anty-list-cust {
+    .ant-list-item-content{flex:1 !important; justify-content:flex-start !important;margin-left: 20px;}
+  }
+
+
+</style>

+ 269 - 0
src/views/dashboard/IndexChart.vue

@@ -0,0 +1,269 @@
+<template>
+  <div class="page-header-index-wide">
+    <a-row :gutter="24">
+      <a-col :sm="24" :md="12" :xl="6" :style="{ marginBottom: '24px' }">
+        <chart-card :loading="loading" title="总销售额" total="¥126,560">
+          <a-tooltip title="指标说明" slot="action">
+            <a-icon type="info-circle-o" />
+          </a-tooltip>
+          <div>
+            <trend flag="up" style="margin-right: 16px;">
+              <span slot="term">周同比</span>
+              12%
+            </trend>
+            <trend flag="down">
+              <span slot="term">日同比</span>
+              11%
+            </trend>
+          </div>
+          <template slot="footer">日均销售额<span>¥ 234.56</span></template>
+        </chart-card>
+      </a-col>
+      <a-col :sm="24" :md="12" :xl="6" :style="{ marginBottom: '24px' }">
+        <chart-card :loading="loading" title="访问量" :total="8846 | NumberFormat">
+          <a-tooltip title="指标说明" slot="action">
+            <a-icon type="info-circle-o" />
+          </a-tooltip>
+          <div>
+            <mini-area />
+          </div>
+          <template slot="footer">日访问量<span> {{ '1234' | NumberFormat }}</span></template>
+        </chart-card>
+      </a-col>
+      <a-col :sm="24" :md="12" :xl="6" :style="{ marginBottom: '24px' }">
+        <chart-card :loading="loading" title="支付笔数" :total="6560 | NumberFormat">
+          <a-tooltip title="指标说明" slot="action">
+            <a-icon type="info-circle-o" />
+          </a-tooltip>
+          <div>
+            <mini-bar :height="40" />
+          </div>
+          <template slot="footer">转化率 <span>60%</span></template>
+        </chart-card>
+      </a-col>
+      <a-col :sm="24" :md="12" :xl="6" :style="{ marginBottom: '24px' }">
+        <chart-card :loading="loading" title="运营活动效果" total="78%">
+          <a-tooltip title="指标说明" slot="action">
+            <a-icon type="info-circle-o" />
+          </a-tooltip>
+          <div>
+            <mini-progress color="rgb(19, 194, 194)" :target="80" :percentage="78" :height="8" />
+          </div>
+          <template slot="footer">
+            <trend flag="down" style="margin-right: 16px;">
+              <span slot="term">同周比</span>
+              12%
+            </trend>
+            <trend flag="up">
+              <span slot="term">日环比</span>
+              80%
+            </trend>
+          </template>
+        </chart-card>
+      </a-col>
+    </a-row>
+
+    <a-card :loading="loading" :bordered="false" :body-style="{padding: '0'}">
+      <div class="salesCard">
+        <a-tabs default-active-key="1" size="large" :tab-bar-style="{marginBottom: '24px', paddingLeft: '16px'}">
+          <div class="extra-wrapper" slot="tabBarExtraContent">
+            <div class="extra-item">
+              <a>今日</a>
+              <a>本周</a>
+              <a>本月</a>
+              <a>本年</a>
+            </div>
+            <a-range-picker :style="{width: '256px'}" />
+          </div>
+          <a-tab-pane loading="true" tab="销售额" key="1">
+            <a-row>
+              <a-col :xl="16" :lg="12" :md="12" :sm="24" :xs="24">
+                <bar title="销售额排行" :dataSource="barData"/>
+              </a-col>
+              <a-col :xl="8" :lg="12" :md="12" :sm="24" :xs="24">
+                <rank-list title="门店销售排行榜" :list="rankList"/>
+              </a-col>
+            </a-row>
+          </a-tab-pane>
+          <a-tab-pane tab="访问量" key="2">
+            <a-row>
+              <a-col :xl="16" :lg="12" :md="12" :sm="24" :xs="24">
+                <bar title="销售额趋势" :dataSource="barData"/>
+              </a-col>
+              <a-col :xl="8" :lg="12" :md="12" :sm="24" :xs="24">
+                <rank-list title="门店销售排行榜" :list="rankList"/>
+              </a-col>
+            </a-row>
+          </a-tab-pane>
+        </a-tabs>
+      </div>
+    </a-card>
+
+    <a-row>
+      <a-col :span="24">
+        <a-card :loading="loading" :bordered="false" title="最近一周访问次数统计" :style="{ marginTop: '24px' }">
+          <a-row>
+            <a-col :span="6">
+              <head-info title="今日访问IP数" :content="loginfo.todayIp"></head-info>
+            </a-col>
+            <a-col :span="2">
+              <a-spin class='circle-cust'>
+                <a-icon slot="indicator" type="environment" style="font-size: 24px"  />
+              </a-spin>
+            </a-col>
+            <a-col :span="6">
+              <head-info title="今日访问次数" :content="loginfo.todayVisitCount"></head-info>
+            </a-col>
+            <a-col :span="2">
+              <a-spin class='circle-cust'>
+                <a-icon slot="indicator" type="team" style="font-size: 24px"  />
+              </a-spin>
+            </a-col>
+            <a-col :span="6">
+              <head-info title="访问总次数" :content="loginfo.totalVisitCount"></head-info>
+            </a-col>
+            <a-col :span="2">
+              <a-spin class='circle-cust'>
+                <a-icon slot="indicator" type="rise" style="font-size: 24px"  />
+              </a-spin>
+            </a-col>
+          </a-row>
+          <line-chart-multid :fields="visitFields" :dataSource="visitInfo"></line-chart-multid>
+        </a-card>
+      </a-col>
+    </a-row>
+  </div>
+</template>
+
+<script>
+  import ChartCard from '@/components/ChartCard'
+  import ACol from "ant-design-vue/es/grid/Col"
+  import ATooltip from "ant-design-vue/es/tooltip/Tooltip"
+  import MiniArea from '@/components/chart/MiniArea'
+  import MiniBar from '@/components/chart/MiniBar'
+  import MiniProgress from '@/components/chart/MiniProgress'
+  import RankList from '@/components/chart/RankList'
+  import Bar from '@/components/chart/Bar'
+  import LineChartMultid from '@/components/chart/LineChartMultid'
+  import HeadInfo from '@/components/tools/HeadInfo.vue'
+
+  import Trend from '@/components/Trend'
+  import { getLoginfo,getVisitInfo } from '@/api/api'
+
+  const rankList = []
+  for (let i = 0; i < 7; i++) {
+    rankList.push({
+      name: '白鹭岛 ' + (i+1) + ' 号店',
+      total: 1234.56 - i * 100
+    })
+  }
+  const barData = []
+  for (let i = 0; i < 12; i += 1) {
+    barData.push({
+      x: `${i + 1}月`,
+      y: Math.floor(Math.random() * 1000) + 200
+    })
+  }
+  export default {
+    name: "IndexChart",
+    components: {
+      ATooltip,
+      ACol,
+      ChartCard,
+      MiniArea,
+      MiniBar,
+      MiniProgress,
+      RankList,
+      Bar,
+      Trend,
+      LineChartMultid,
+      HeadInfo
+    },
+    data() {
+      return {
+        loading: true,
+        center: null,
+        rankList,
+        barData,
+        loginfo:{},
+        visitFields:['ip','visit'],
+        visitInfo:[],
+        indicator: <a-icon type="loading" style="font-size: 24px" spin />
+      }
+    },
+    created() {
+      setTimeout(() => {
+        this.loading = !this.loading
+      }, 1000)
+      this.initLogInfo();
+    },
+    methods: {
+      initLogInfo () {
+        getLoginfo(null).then((res)=>{
+          if(res.success){
+            Object.keys(res.result).forEach(key=>{
+              res.result[key] =res.result[key]+""
+            })
+            this.loginfo = res.result;
+          }
+        })
+        getVisitInfo().then(res=>{
+          if(res.success){
+             console.log("aaaaaa",res.result)
+             this.visitInfo = res.result;
+           }
+         })
+      },
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  .circle-cust{
+    position: relative;
+    top: 28px;
+    left: -100%;
+  }
+  .extra-wrapper {
+    line-height: 55px;
+    padding-right: 24px;
+
+    .extra-item {
+      display: inline-block;
+      margin-right: 24px;
+
+      a {
+        margin-left: 24px;
+      }
+    }
+  }
+
+  /* 首页访问量统计 */
+  .head-info {
+    position: relative;
+    text-align: left;
+    padding: 0 32px 0 0;
+    min-width: 125px;
+
+    &.center {
+      text-align: center;
+      padding: 0 32px;
+    }
+
+    span {
+      color: rgba(0, 0, 0, .45);
+      display: inline-block;
+      font-size: .95rem;
+      line-height: 42px;
+      margin-bottom: 4px;
+    }
+    p {
+      line-height: 42px;
+      margin: 0;
+      a {
+        font-weight: 600;
+        font-size: 1rem;
+      }
+    }
+  }
+</style>

+ 372 - 0
src/views/dashboard/IndexTask.vue

@@ -0,0 +1,372 @@
+<template>
+  <div class="index-container-ty">
+    <a-spin :spinning="loading">
+      <a-row type="flex" justify="start" :gutter="3">
+        <a-col :sm="24" :lg="12">
+          <a-card>
+            <div slot="title" class="index-md-title">
+              <img src="../../assets/daiban.png"/>
+              我的待办【{{ dataSource1.length }}】
+            </div>
+            <div slot="extra">
+              <a v-if="dataSource1 && dataSource1.length>0" slot="footer" @click="goPage">更多 <a-icon type="double-right" /></a>
+            </div>
+            <a-table
+              :class="'my-index-table tytable1'"
+              ref="table1"
+              size="small"
+              rowKey="id"
+              :columns="columns"
+              :dataSource="dataSource1"
+              :pagination="false">
+              <template slot="ellipsisText" slot-scope="text">
+                <j-ellipsis :value="text" :length="textMaxLength"></j-ellipsis>
+              </template>
+
+              <template slot="dayWarnning" slot-scope="text,record">
+                <a-icon type="bulb" theme="twoTone" style="font-size:22px" :twoToneColor="getTipColor(record)"/>
+              </template>
+
+              <span slot="action" slot-scope="text, record">
+                <a @click="handleData">办理</a>
+              </span>
+
+            </a-table>
+          </a-card>
+        </a-col>
+
+        <a-col :sm="24" :lg="12">
+          <a-card>
+            <div slot="title" class="index-md-title">
+              <img src="../../assets/zaiban.png"/>
+              我的在办【{{ dataSource2.length }}】
+            </div>
+            <div slot="extra">
+              <a v-if="dataSource2 && dataSource2.length>0" slot="footer" @click="goPage">更多 <a-icon type="double-right" /></a>
+            </div>
+            <a-table
+              :class="'my-index-table tytable2'"
+              ref="table2"
+              size="small"
+              rowKey="id"
+              :columns="columns"
+              :dataSource="dataSource2"
+              :pagination="false">
+              <template slot="ellipsisText" slot-scope="text">
+                <j-ellipsis :value="text" :length="textMaxLength"></j-ellipsis>
+              </template>
+
+              <template slot="dayWarnning" slot-scope="text,record">
+                <a-icon type="bulb" theme="twoTone" style="font-size:22px" :twoToneColor="getTipColor(record)"/>
+              </template>
+
+              <span slot="action" slot-scope="text, record">
+                <a @click="handleData">办理</a>
+              </span>
+
+            </a-table>
+          </a-card>
+        </a-col>
+
+        <a-col :span="24">
+          <div style="height: 5px;"></div>
+        </a-col>
+
+        <a-col :sm="24" :lg="12">
+          <a-card>
+            <div slot="title" class="index-md-title">
+              <img src="../../assets/guaz.png"/>
+              我的挂账【{{ dataSource4.length }}】
+            </div>
+            <a-table
+              :class="'my-index-table tytable4'"
+              ref="table4"
+              size="small"
+              rowKey="id"
+              :columns="columns"
+              :dataSource="dataSource4"
+              :pagination="false">
+              <template slot="ellipsisText" slot-scope="text">
+                <j-ellipsis :value="text" :length="textMaxLength"></j-ellipsis>
+              </template>
+
+              <template slot="dayWarnning" slot-scope="text,record">
+                <a-icon type="bulb" theme="twoTone" style="font-size:22px" :twoToneColor="getTipColor(record)"/>
+              </template>
+
+              <span slot="action" slot-scope="text, record">
+                <a @click="handleData">办理</a>
+              </span>
+
+            </a-table>
+          </a-card>
+        </a-col>
+
+        <a-col :sm="24" :lg="12">
+          <a-card>
+            <div slot="title" class="index-md-title">
+              <img src="../../assets/duban.png"/>
+              我的督办【{{ dataSource3.length }}】
+            </div>
+            <a-table
+              :class="'my-index-table tytable3'"
+              ref="table3"
+              size="small"
+              rowKey="id"
+              :columns="columns"
+              :dataSource="dataSource3"
+              :pagination="false">
+              <template slot="ellipsisText" slot-scope="text">
+                <j-ellipsis :value="text" :length="textMaxLength"></j-ellipsis>
+              </template>
+
+              <template slot="dayWarnning" slot-scope="text,record">
+                <a-icon type="bulb" theme="twoTone" style="font-size:22px" :twoToneColor="getTipColor(record)"/>
+              </template>
+
+              <span slot="action" slot-scope="text, record">
+                <a @click="handleData">办理</a>
+              </span>
+
+            </a-table>
+          </a-card>
+        </a-col>
+
+      </a-row>
+    </a-spin>
+
+  </div>
+</template>
+
+<script>
+  import noDataPng from '@/assets/nodata.png'
+  import JEllipsis from '@/components/jeecg/JEllipsis'
+
+  const tempSs1=[{
+    id:"001",
+    orderNo:"电[1]1267102",
+    orderTitle:"药品出问题了",
+    restDay:1
+  },{
+    id:"002",
+    orderNo:"电[4]5967102",
+    orderTitle:"吃了xxx医院的药,病情越来越严重",
+    restDay:0
+  },{
+    id:"003",
+    orderNo:"电[3]5988987",
+    orderTitle:"今天去超市买鸡蛋,鸡蛋都是坏的",
+    restDay:7
+  },{
+    id:"004",
+    orderNo:"电[2]5213491",
+    orderTitle:"xx宝实体店高价售卖xx",
+    restDay:5
+  },{
+    id:"005",
+    orderNo:"电[1]1603491",
+    orderTitle:"以红利相诱,答应退保后扣一年费用",
+    restDay:0
+  }]
+
+  const tempSs2=[{
+    id:"001",
+    orderTitle:"我要投诉这个大超市",
+    orderNo:"电[1]10299456",
+    restDay:6
+  },{
+    id:"002",
+    orderTitle:"xxx医院乱开药方,售卖假药",
+    orderNo:"电[2]20235691",
+    restDay:0
+  },{
+    id:"003",
+    orderTitle:"我想问问这家店是干啥的",
+    orderNo:"电[3]495867322",
+    restDay:7
+  },{
+    id:"004",
+    orderTitle:"我要举报朝阳区奥森公园酒店",
+    orderNo:"电[2]1193849",
+    restDay:3
+  },{
+    id:"005",
+    orderTitle:"我今天吃饭吃到一个石头子",
+    orderNo:"电[4]56782344",
+    restDay:9
+  }]
+
+  //4-7天
+  const tip_green = "rgba(0, 255, 0, 1)"
+  //1-3天
+  const tip_yellow = "rgba(255, 255, 0, 1)"
+  //超期
+  const tip_red = "rgba(255, 0, 0, 1)"
+
+  export default {
+    name: "IndexTask",
+    components:{ JEllipsis },
+    data() {
+      return {
+        loading:false,
+        textMaxLength:8,
+        dataSource1:[],
+        dataSource2:[],
+        dataSource3:[],
+        dataSource4:[],
+        columns: [
+          {
+            title: '',
+            dataIndex: '',
+            key:'rowIndex',
+            width:50,
+            fixed:'left',
+            align:"center",
+            scopedSlots: {customRender: "dayWarnning"}
+          },
+          {
+            title:'剩余天数',
+            align:"center",
+            dataIndex: 'restDay',
+            width:80
+          },
+          {
+            title:'工单标题',
+            align:"center",
+            dataIndex: 'orderTitle',
+            scopedSlots: {customRender: "ellipsisText"}
+          },
+          {
+            title:'工单编号',
+            align:"center",
+            dataIndex: 'orderNo',
+          },
+          {
+            title: '操作',
+            dataIndex: 'action',
+            align:"center",
+            scopedSlots: { customRender: 'action' }
+          }
+        ]
+
+      }
+    },
+    created() {
+      this.mock();
+    },
+    mounted(){
+
+    },
+    methods: {
+      getTipColor(rd){
+        let num = rd.restDay
+        if(num<=0){
+          return tip_red
+        }else if(num>=1 && num<4){
+          return tip_yellow
+        }else if(num>=4){
+          return tip_green
+        }
+      },
+      goPage(){
+        this.$message.success("请根据具体业务跳转页面")
+        //this.$router.push({ path: '/comp/mytask' })
+      },
+      mock(){
+        this.dataSource1=tempSs1
+        this.dataSource2=tempSs2
+        this.dataSource3=tempSs1
+        this.dataSource4=[]
+        this.ifNullDataSource(this.dataSource4,'.tytable4')
+      },
+
+      ifNullDataSource(ds,tb){
+        this.$nextTick(()=>{
+          if(!ds || ds.length==0){
+            var tmp = document.createElement('img');
+            tmp.src=noDataPng
+            tmp.width=300
+            let tbclass=`${tb} .ant-table-placeholder`
+            document.querySelector(tbclass).innerHTML=""
+            document.querySelector(tbclass).appendChild(tmp)
+          }
+        })
+      },
+      handleData(){
+        this.$message.success("办理完成")
+      }
+
+
+
+
+    }
+  }
+</script>
+
+<style>
+  .my-index-table{height:270px}
+  .my-index-table table{font-size: 14px !important;}
+
+  .index-container-ty .ant-card-head-title{padding-top: 6px;padding-bottom: 6px;}
+  .index-container-ty .ant-card-extra{padding:0}
+  .index-container-ty .ant-card-extra a{color:#fff}
+  .index-container-ty .ant-card-extra a:hover{color:#152ede}
+  .index-container-ty .ant-card-head-wrapper,.index-container-ty .ant-card-head{
+    line-height:24px;
+    min-height:24px;
+    /*background: #90aeff;*/
+    background: #7196fb;
+  }
+  .index-container-ty .ant-card-body{padding: 10px 12px 0px 12px}
+
+  /* .index-container-ty .ant-card-actions{background: #fff}
+   .index-container-ty .ant-card-actions li {margin:2px 0;}
+   .index-container-ty .ant-card-actions > li > span{width: 100%}*/
+
+
+  .index-container-ty .ant-table-footer{text-align: right;padding:6px 12px 6px 6px;background: #fff;border-top: 2px solid #f7f1f1;}
+
+  .index-md-title{
+    postion:relative;
+    padding-left:24px;
+    width: 100%;
+    color: #fff;
+    font-size: 21px;
+    font-family: cursive;
+  }
+  .index-md-title img{
+    position: absolute;
+    height:32px;
+    top: 2px;
+    left:14px;
+  }
+
+  .index-container-ty .ant-card-body{
+    /*border-left:1px solid #90aeff;
+    /*border-right:1px solid #90aeff;
+    border-bottom:1px solid #90aeff;*/
+  }
+
+
+  .index-container-ty .ant-table-thead > tr > th,
+  .index-container-ty .ant-table-tbody > tr > td{
+    border-bottom: 1px solid #90aeff;
+  }
+
+  .index-container-ty .ant-table-small > .ant-table-content > .ant-table-fixed-left > .ant-table-body-outer > .ant-table-body-inner > table > .ant-table-thead > tr > th,
+  .index-container-ty .ant-table-small > .ant-table-content > .ant-table-fixed-right > .ant-table-body-outer > .ant-table-body-inner > table > .ant-table-thead > tr > th{
+    border-bottom: 1px solid #90aeff;
+  }
+
+  .index-container-ty  .ant-table-small > .ant-table-content > .ant-table-scroll > .ant-table-body > table > .ant-table-thead > tr > th{
+    border-bottom: 1px solid #90aeff;
+  }
+
+  .index-container-ty .ant-table-small{
+    border: 1px solid #90aeff;
+  }
+
+  .index-container-ty .ant-table-placeholder {
+    padding: 0
+  }
+</style>

+ 91 - 0
src/views/jeecg/TableTotal.vue

@@ -0,0 +1,91 @@
+<template>
+  <a-card :bordered="false">
+    <a-table
+      rowKey="id"
+      bordered
+      :columns="columns"
+      :dataSource="dataSource"
+      :pagination="false"
+    >
+    </a-table>
+  </a-card>
+</template>
+
+<script>
+  export default {
+    name: 'TableTotal',
+    data() {
+      return {
+        columns: [
+          {
+            title: '#',
+            width: '180px',
+            align: 'center',
+            dataIndex: 'rowIndex',
+            customRender: function (text, r, index) {
+              return (text !== '合计') ? (parseInt(index) + 1) : text
+            }
+          },
+          {
+            title: '姓名',
+            dataIndex: 'name',
+          },
+          {
+            title: '贡献点',
+            dataIndex: 'point',
+          },
+          {
+            title: '等级',
+            dataIndex: 'level',
+          },
+          {
+            title: '更新时间',
+            dataIndex: 'updateTime',
+          },
+        ],
+        dataSource: [
+          { name: '张三', point: 23, level: 3, updateTime: '2019-8-14' },
+          { name: '小王', point: 6, level: 1, updateTime: '2019-8-13' },
+          { name: '李四', point: 53, level: 8, updateTime: '2019-8-12' },
+          { name: '小红', point: 44, level: 5, updateTime: '2019-8-11' },
+          { name: '王五', point: 97, level: 10, updateTime: '2019-8-10' },
+          { name: '小明', point: 33, level: 2, updateTime: '2019-8-10' },
+        ]
+      }
+    },
+    mounted() {
+      this.tableAddTotalRow(this.columns, this.dataSource)
+    },
+    methods: {
+
+      /** 表格增加合计行 */
+      tableAddTotalRow(columns, dataSource) {
+        let numKey = 'rowIndex'
+        let totalRow = { [numKey]: '合计' }
+        columns.forEach(column => {
+          let { key, dataIndex } = column
+          if (![key, dataIndex].includes(numKey)) {
+
+            let total = 0
+            dataSource.forEach(data => {
+              total += /^\d+\.?\d?$/.test(data[dataIndex]) ? Number.parseInt(data[dataIndex]) : Number.NaN
+              console.log(data[dataIndex], ':', (/^\d+\.?\d?$/.test(data[dataIndex]) ? Number.parseInt(data[dataIndex]) : Number.NaN))
+            })
+
+            if (Number.isNaN(total)) {
+              total = '-'
+            }
+            totalRow[dataIndex] = total
+          }
+        })
+
+        dataSource.push(totalRow)
+      }
+
+    }
+  }
+</script>
+
+<style scoped>
+
+</style>

+ 275 - 0
src/views/jeecg/modules/JEditableTable/DefaultTable.vue

@@ -0,0 +1,275 @@
+<template>
+
+  <div>
+    <a-button @click="handleTableCheck" type="primary">表单验证</a-button>
+    <span style="padding-left:8px;"></span>
+    <a-tooltip placement="top" title="获取值,忽略表单验证" :autoAdjustOverflow="true">
+      <a-button @click="handleTableGet" type="primary">获取值</a-button>
+    </a-tooltip>
+    <span style="padding-left:8px;"></span>
+    <a-tooltip placement="top" title="模拟加载1000条数据" :autoAdjustOverflow="true">
+      <a-button @click="handleTableSet" type="primary">设置值</a-button>
+    </a-tooltip>
+
+
+    <j-editable-table
+      ref="editableTable"
+      :loading="loading"
+      :columns="columns"
+      :dataSource="dataSource"
+      :rowNumber="true"
+      :rowSelection="true"
+      :actionButton="true"
+      :dragSort="true"
+      style="margin-top: 8px;"
+      @selectRowChange="handleSelectRowChange">
+
+      <template v-slot:action="props">
+        <a @click="handleDelete(props)">{{ props.text }}</a>
+      </template>
+
+    </j-editable-table>
+  </div>
+
+</template>
+
+<script>
+  import moment from 'moment'
+  import { FormTypes } from '@/utils/JEditableTableUtil'
+  import { randomUUID, randomNumber } from '@/utils/util'
+  import JEditableTable from '@/components/jeecg/JEditableTable'
+
+  export default {
+    name: 'DefaultTable',
+    components: { JEditableTable },
+    data() {
+      return {
+        loading: false,
+        columns: [
+          {
+            title: '字段名称',
+            key: 'dbFieldName',
+            // width: '19%',
+            width: '300px',
+            type: FormTypes.input,
+            defaultValue: '',
+            placeholder: '请输入${title}',
+            validateRules: [
+              {
+                required: true, // 必填
+                message: '请输入${title}' // 显示的文本
+              },
+              {
+                pattern: /^[a-z|A-Z][a-z|A-Z\d_-]{0,}$/, // 正则
+                message: '${title}必须以字母开头,可包含数字、下划线、横杠'
+              },
+              {
+                unique: true,
+                message: '${title}不能重复'
+              },
+              {
+                handler(type, value, row, column, callback, target) {
+                  // type 触发校验的类型(input、change、blur)
+                  // value 当前校验的值
+                  // callback(flag, message) 方法必须执行且只能执行一次
+                  //          flag = 是否通过了校验,不填写或者填写 null 代表不进行任何操作
+                  //          message = 提示的类型,默认使用配置的 message
+                  // target 行编辑的实例对象
+
+                  if (type === 'blur') {
+                    if (value === 'abc') {
+                      callback(false, '${title}不能是abc')  // false = 未通过校验
+                    } else {
+                      callback(true) // true = 通过验证
+                    }
+                  } else {
+                    callback(true) // 不填写或者填写 null 代表不进行任何操作
+                  }
+                },
+                message: '${title}默认提示'
+              }
+            ]
+          },
+          {
+            title: '文件域',
+            key: 'upload',
+            type: FormTypes.upload,
+            // width: '19%',
+            width: '300px',
+            placeholder: '点击上传',
+            token: true,
+            responseName: 'message',
+            action: window._CONFIG['domianURL'] + '/sys/common/upload'
+          },
+          {
+            title: '字段类型',
+            key: 'dbFieldType',
+            // width: '18%',
+            width: '300px',
+            type: FormTypes.select,
+            options: [ // 下拉选项
+              { title: 'String', value: 'string' },
+              { title: 'Integer', value: 'int' },
+              { title: 'Double', value: 'double' },
+              { title: 'Boolean', value: 'boolean' }
+            ],
+            allowInput: true,
+            defaultValue: '',
+            placeholder: '请选择${title}',
+            validateRules: [{ required: true, message: '请选择${title}' }]
+          },
+          {
+            title: '性别(字典)',
+            key: 'sex_dict',
+            width: '300px',
+            type: FormTypes.select,
+            options: [],
+            dictCode: 'sex',
+            placeholder: '请选择${title}',
+            validateRules: [{ required: true, message: '请选择${title}' }]
+          },
+          {
+            title: '多选测试',
+            key: 'multipleSelect',
+            // width: '18%',
+            width: '300px',
+            type: FormTypes.select,
+            props: { 'mode': 'multiple' }, // 支持多选
+            options: [
+              { title: 'String', value: 'string' },
+              { title: 'Integer', value: 'int' },
+              { title: 'Double', value: 'double' },
+              { title: 'Boolean', value: 'boolean' }
+            ],
+            defaultValue: ['int', 'boolean'], // 多个默认项
+            // defaultValue: 'string,double,int', // 也可使用这种方式
+            placeholder: '这里可以多选',
+            validateRules: [{ required: true, message: '请选择${title}' }]
+          },
+          {
+            title: '字段长度',
+            key: 'dbLength',
+            // width: '8%',
+            width: '100px',
+            type: FormTypes.inputNumber,
+            defaultValue: 32,
+            placeholder: '${title}',
+            validateRules: [{ required: true, message: '请输入${title}' }]
+          },
+          {
+            title: '日期',
+            key: 'datetime',
+            // width: '22%',
+            width: '320px',
+            type: FormTypes.datetime,
+            defaultValue: '2019-4-30 14:52:22',
+            placeholder: '请选择${title}',
+            validateRules: [{ required: true, message: '请选择${title}' }]
+          },
+          {
+            title: '可以为空',
+            key: 'isNull',
+            // width: '8%',
+            width: '100px',
+            type: FormTypes.checkbox,
+            customValue: ['Y', 'N'], // true ,false
+            defaultChecked: false
+          },
+          {
+            title: '操作',
+            key: 'action',
+            // width: '8%',
+            width: '100px',
+            type: FormTypes.slot,
+            slotName: 'action',
+            defaultValue: '删除'
+          }
+
+        ],
+        dataSource: [],
+        selectedRowIds: []
+      }
+    },
+    mounted() {
+      this.randomData(23, false)
+    },
+    methods: {
+
+      /** 表单验证 */
+      handleTableCheck() {
+        this.$refs.editableTable.getValues((error) => {
+          if (error === 0) {
+            this.$message.success('验证通过')
+          } else {
+            this.$message.error('验证未通过')
+          }
+        })
+      },
+      /** 获取值,忽略表单验证 */
+      handleTableGet() {
+        this.$refs.editableTable.getValues((error, values) => {
+          console.log('values:', values)
+        }, false)
+        console.log('deleteIds:', this.$refs.editableTable.getDeleteIds())
+
+        this.$message.info('获取值成功,请看控制台输出')
+
+      },
+      /** 模拟加载1000条数据 */
+      handleTableSet() {
+        this.randomData(1000, true)
+      },
+
+      handleSelectRowChange(selectedRowIds) {
+        this.selectedRowIds = selectedRowIds
+      },
+
+      /* 随机生成数据 */
+      randomData(size, loading = false) {
+        if (loading) {
+          this.loading = true
+        }
+
+        let randomDatetime = () => {
+          let time = parseInt(randomNumber(1000, 9999999999999))
+          return moment(new Date(time)).format('YYYY-MM-DD HH:mm:ss')
+        }
+
+        let begin = Date.now()
+        let values = []
+        for (let i = 0; i < size; i++) {
+          values.push({
+            id: randomUUID(),
+            dbFieldName: `name_${i + 1}`,
+            // dbFieldTxt: randomString(10),
+            multipleSelect: ['string', ['int', 'double', 'boolean'][randomNumber(0, 2)]],
+            dbFieldType: ['string', 'int', 'double', 'boolean'][randomNumber(0, 3)],
+            dbLength: randomNumber(0, 233),
+            datetime: randomDatetime(),
+            isNull: ['Y', 'N'][randomNumber(0, 1)]
+          })
+        }
+
+        this.dataSource = values
+        let end = Date.now()
+        let diff = end - begin
+
+        if (loading && diff < size) {
+          setTimeout(() => {
+            this.loading = false
+          }, size - diff)
+        }
+
+      },
+
+      handleDelete(props) {
+        let { rowId, target } = props
+        target.removeRows(rowId)
+      }
+    }
+  }
+</script>
+
+<style scoped>
+
+</style>

+ 70 - 0
src/views/jeecg/modules/JEditableTable/ReadOnlyTable.vue

@@ -0,0 +1,70 @@
+<template>
+  <j-editable-table
+    :columns="columns"
+    :dataSource="dataSource"
+    :rowNumber="true"
+    :rowSelection="true"
+    :maxHeight="400"
+    :disabled="true"
+  />
+</template>
+
+<script>
+  import { FormTypes } from '@/utils/JEditableTableUtil'
+  import JEditableTable from '@/components/jeecg/JEditableTable'
+
+  export default {
+    name: 'ReadOnlyTable',
+    components: { JEditableTable },
+    data() {
+      return {
+
+        columns: [
+          {
+            title: '输入框',
+            key: 'input',
+            type: FormTypes.input,
+            placeholder: '清输入'
+          },
+          {
+            title: '下拉框',
+            key: 'select',
+            type: FormTypes.select,
+            options: [
+              { title: 'String', value: 'string' },
+              { title: 'Integer', value: 'int' },
+              { title: 'Double', value: 'double' },
+              { title: 'Boolean', value: 'boolean' }
+            ],
+            placeholder: '请选择'
+          },
+          {
+            title: '多选框',
+            key: 'checkbox',
+            type: FormTypes.checkbox,
+            customValue: [true, false]
+          },
+          {
+            title: '日期',
+            key: 'datetime',
+            type: FormTypes.datetime
+          }
+        ],
+        dataSource: [
+          { input: 'hello', select: 'int', checkbox: true, datetime: '2019-6-17 14:50:48' },
+          { input: 'world', select: 'string', checkbox: false, datetime: '2019-6-16 14:50:48' },
+          { input: 'one', select: 'double', checkbox: true, datetime: '2019-6-17 15:50:48' },
+          { input: 'two', select: 'boolean', checkbox: false, datetime: '2019-6-14 14:50:48' },
+          { input: 'three', select: '', checkbox: false, datetime: '2019-6-13 14:50:48' }
+        ]
+      }
+    },
+    mounted() {
+
+    }
+  }
+</script>
+
+<style scoped>
+
+</style>

+ 129 - 0
src/views/jeecg/modules/JEditableTable/ThreeLinkage.vue

@@ -0,0 +1,129 @@
+<template>
+  <j-editable-table
+    :columns="columns"
+    :dataSource="dataSource"
+    :rowNumber="true"
+    :actionButton="true"
+    :rowSelection="true"
+    :maxHeight="400"
+    @valueChange="handleValueChange"
+  />
+</template>
+
+<script>
+  import { FormTypes } from '@/utils/JEditableTableUtil'
+  import JEditableTable from '@/components/jeecg/JEditableTable'
+
+  export default {
+    name: 'ThreeLinkage',
+    components: { JEditableTable },
+    data() {
+      return {
+        columns: [
+          {
+            title: '省/直辖市/自治区',
+            key: 's1',
+            type: FormTypes.select,
+            width: '240px',
+            options: [],
+            placeholder: '请选择${title}'
+          },
+          {
+            title: '市',
+            key: 's2',
+            type: FormTypes.select,
+            width: '240px',
+            options: [],
+            placeholder: '请选择${title}'
+          },
+          {
+            title: '县/区',
+            key: 's3',
+            type: FormTypes.select,
+            width: '240px',
+            options: [],
+            placeholder: '请选择${title}'
+          }
+        ],
+        dataSource: [],
+
+        mockData: [
+          { label: '北京市', value: '110000', parent: null },
+          { label: '天津市', value: '120000', parent: null },
+          { label: '河北省', value: '130000', parent: null },
+          { label: '上海市', value: '310000', parent: null },
+
+          { label: '北京市', value: '110100', parent: '110000' },
+          { label: '天津市市', value: '120100', parent: '120000' },
+          { label: '石家庄市', value: '130100', parent: '130000' },
+          { label: '唐山市', value: '130200', parent: '130000' },
+          { label: '秦皇岛市', value: '130300', parent: '130000' },
+          { label: '上海市', value: '310100', parent: '310000' },
+
+          { label: '东城区', value: '110101', parent: '110100' },
+          { label: '西城区', value: '110102', parent: '110100' },
+          { label: '朝阳区', value: '110105', parent: '110100' },
+          { label: '和平区', value: '120101', parent: '120000' },
+          { label: '河东区', value: '120102', parent: '120000' },
+          { label: '河西区', value: '120103', parent: '120000' },
+          { label: '黄浦区', value: '310101', parent: '310100' },
+          { label: '徐汇区', value: '310104', parent: '310100' },
+          { label: '长宁区', value: '310105', parent: '310100' },
+          { label: '长安区', value: '130102', parent: '130100' },
+          { label: '桥西区', value: '130104', parent: '130100' },
+          { label: '新华区', value: '130105', parent: '130100' },
+          { label: '路南区', value: '130202', parent: '130200' },
+          { label: '路北区', value: '130203', parent: '130200' },
+          { label: '古冶区', value: '130204', parent: '130200' },
+          { label: '海港区', value: '130302', parent: '130300' },
+          { label: '山海关区', value: '130303', parent: '130300' },
+          { label: '北戴河区', value: '130304', parent: '130300' },
+        ]
+      }
+    },
+    mounted() {
+      // 初始化数据
+      this.columns[0].options = this.request(null)
+    },
+    methods: {
+
+      request(parentId) {
+        return this.mockData.filter(i => i.parent === parentId)
+      },
+
+      /** 当选项被改变时,联动其他组件 */
+      handleValueChange(event) {
+        const { type, row, column, value, target } = event
+
+        if (type === FormTypes.select) {
+
+          // 第一列
+          if (column.key === 's1') {
+            // 设置第二列的 options
+            this.columns[1].options = this.request(value)
+            // 清空后两列的数据
+            target.setValues([{
+              rowKey: row.id,
+              values: { s2: '', s3: '' }
+            }])
+            this.columns[2].options = []
+          } else
+          // 第二列
+          if (column.key === 's2') {
+            this.columns[2].options = this.request(value)
+            target.setValues([{
+              rowKey: row.id,
+              values: { s3: '' }
+            }])
+          }
+        }
+
+      }
+
+    }
+  }
+</script>
+
+<style scoped>
+
+</style>

+ 63 - 3
src/views/modules/advertiser/UserAllocationList.vue

@@ -42,6 +42,9 @@
       >
         <span slot="mediaId" slot-scope="text">{{ text == 1 ? '头条' : '快手' }}</span>
         <span slot="action" slot-scope="text, record">
+          <a @click="project(record)">绑定项目</a>
+
+          <a-divider type="vertical" />
           <a @click="handleEdit(record)">编辑</a>
 
           <a-divider type="vertical" />
@@ -55,13 +58,30 @@
 
     <!-- 表单区域 -->
     <userAllocation-modal ref="modalForm" @ok="modalFormOk"></userAllocation-modal>
+    <!--  添加项目成员 -->
+    <a-modal title="项目绑定" v-model="visibleAdd" @ok="handleOkAdd" :confirmLoading="confirmLoading">
+      <!-- /sys/user/getAllUserList -->
+      <a-select
+        v-model="projectId"
+        placeholder="请输入项目名称"
+        showSearch
+        optionFilterProp="children"
+        :filterOption="filterOption"
+        style="width:100%"
+      >
+        <a-select-option :value="item.id" v-for="item of options" :key="item.id">
+          {{ item.projectName }}
+        </a-select-option>
+      </a-select>
+    </a-modal>
   </a-card>
 </template>
 
 <script>
 import UserAllocationModal from './modules/UserAllocationModal'
 import { JeecgListMixin } from '@/mixins/JeecgListMixin'
-
+import { mapGetters } from 'vuex'
+import { httpAction, getAction, deleteAction } from '@/api/manage'
 export default {
   name: 'UserAllocationList',
   mixins: [JeecgListMixin],
@@ -71,6 +91,8 @@ export default {
   data() {
     return {
       description: '账号绑定管理页面',
+      visibleAdd: false,
+      options: [],
       // 表头
       columns: [
         {
@@ -132,7 +154,10 @@ export default {
         deleteBatch: '/ctop/userAllocation/deleteBatch',
         exportXlsUrl: 'ctop/userAllocation/exportXls',
         importExcelUrl: 'ctop/userAllocation/importExcel'
-      }
+      },
+      confirmLoading: false,
+      projectId: '',
+      accountId: ''
     }
   },
   computed: {
@@ -140,7 +165,42 @@ export default {
       return `${window._CONFIG['domianURL']}/${this.url.importExcelUrl}`
     }
   },
-  methods: {}
+  methods: {
+    filterOption(input, option) {
+      return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
+    },
+    ...mapGetters(['nickname', 'avatar', 'userInfo']),
+    project(item) {
+      console.log(item)
+      this.visibleAdd = true
+      this.accountId = item.accountId
+      getAction('/ctop/project/getProjectByCompany', { userId: this.userInfo().id, mediaId: item.mediaId }).then(
+        res => {
+          console.log(res)
+          if (res.success) {
+            this.options = res.result
+          }
+        }
+      )
+    },
+    handleOkAdd(e) {
+      var params = {}
+      params.projectId = this.projectId
+      params.accountId = this.accountId
+      this.confirmLoading = true
+      getAction('/ctop/userAllocation/accountTurnProject', params).then(res => {
+        this.confirmLoading = false
+        if (res.success) {
+          this.visibleAdd = false
+          this.projectId = ''
+          this.$message.success('绑定成功')
+          this.loadData()
+        } else {
+          this.$message.error(res.message)
+        }
+      })
+    }
+  }
 }
 </script>
 <style scoped>

+ 31 - 6
src/views/modules/advertiser/modules/UserAllocationModal.vue

@@ -11,7 +11,19 @@
     <a-spin :spinning="confirmLoading">
       <a-form :form="form">
         <a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="账户所属人">
-          <a-input placeholder="请输入userName" v-decorator="['userName', {}]" disabled />
+          <!-- <a-input placeholder="请输入userName" v-decorator="['userName', {}]" disabled /> -->
+          <a-select
+            v-decorator="['userId', {}]"
+            placeholder="请输入userName"
+            showSearch
+            optionFilterProp="children"
+            :filterOption="filterOption"
+            style="width:100%"
+          >
+            <a-select-option :value="item.userId" v-for="(item,index) of options" :key="index">
+              {{ item.realName }}
+            </a-select-option>
+          </a-select>
         </a-form-item>
         <a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="账号id">
           <a-input placeholder="请输入账户id --授权" v-decorator="['accountId', {}]" disabled />
@@ -64,10 +76,10 @@
 </template>
 
 <script>
-import { httpAction } from '@/api/manage'
+import { httpAction, getAction } from '@/api/manage'
 import pick from 'lodash.pick'
 import moment from 'moment'
-
+import { mapGetters } from 'vuex'
 export default {
   name: 'UserAllocationModal',
   data() {
@@ -90,15 +102,25 @@ export default {
       url: {
         add: '/ctop/userAllocation/add',
         edit: '/ctop/userAllocation/edit'
-      }
+      },
+      options: []
     }
   },
   created() {},
   methods: {
+    filterOption(input, option) {
+      return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
+    },
+    ...mapGetters(['nickname', 'avatar', 'userInfo']),
     add() {
       this.edit({})
     },
     edit(record) {
+      getAction('/sys/user/getAllUserListByCompany', { userId: this.userInfo().id }).then(res => {
+        if (res.success) {
+          this.options = res.result
+        }
+      })
       this.form.resetFields()
       this.model = Object.assign({}, record)
       this.visible = true
@@ -116,8 +138,8 @@ export default {
             'accountName',
             'authName',
             'mediaId',
-            "warningAmount",
-            "warningProportion"
+            'warningAmount',
+            'warningProportion'
           )
         )
         //时间格式化
@@ -143,6 +165,9 @@ export default {
             method = 'put'
           }
           let formData = Object.assign(this.model, values)
+          formData.userName = this.options.filter(item => {
+            return item.userId == values.userId
+          })[0].realName
           //时间格式化
 
           console.log(formData)

+ 1 - 1
src/views/modules/kuaishouapp/account/newMould.vue

@@ -63,7 +63,7 @@ export default {
         device_price: ['5', '9'],
         fans_star: [2, 3, 4, 5],
         app_interest: [3, 4],
-        android_osv: '3',
+        android_osv: '4',
         business_interest_type: '2',
         business_interest: [34328, 34330],
         no_age_break: 0,

+ 388 - 0
src/views/modules/online/cgform/OnlCgformCopyList.vue

@@ -0,0 +1,388 @@
+<template>
+  <a-card :bordered="false">
+
+    <!-- 查询区域 -->
+    <div class="table-page-search-wrapper">
+      <a-form layout="inline">
+        <a-row :gutter="24">
+
+          <a-col :md="6" :sm="24">
+            <a-form-item label="表名">
+              <a-input placeholder="请输入表名" v-model="queryParam.tableName"></a-input>
+            </a-form-item>
+          </a-col>
+
+          <a-col :md="6" :sm="24">
+            <span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
+              <a-button type="primary" @click="searchQuery" icon="search">查询</a-button>
+              <a-button type="primary" @click="searchReset" icon="reload" style="margin-left: 8px">重置</a-button>
+            </span>
+          </a-col>
+
+        </a-row>
+      </a-form>
+    </div>
+
+    <!-- 操作按钮区域 -->
+    <div class="table-operator">
+      <a-button @click="doCgformButton" type="primary" icon="highlight" style="margin-left:8px">自定义按钮</a-button>
+      <a-button @click="doEnhanceJs" type="primary" icon="strikethrough" style="margin-left:8px">JS增强</a-button>
+      <a-button @click="doEnhanceSql" type="primary" icon="filter" style="margin-left:8px">SQL增强</a-button>
+      <a-button @click="doEnhanceJava" type="primary" icon="tool" style="margin-left:8px">Java增强</a-button>
+
+      <a-dropdown v-if="selectedRowKeys.length > 0">
+        <a-menu slot="overlay">
+          <a-menu-item key="1" @click="batchDel">
+            <a-icon type="delete"/>
+            删除
+          </a-menu-item>
+        </a-menu>
+        <a-button style="margin-left: 8px"> 批量操作
+          <a-icon type="down"/>
+        </a-button>
+      </a-dropdown>
+    </div>
+
+    <!-- table区域-begin -->
+    <div>
+      <div class="ant-alert ant-alert-info" style="margin-bottom: 16px;">
+        <i class="anticon anticon-info-circle ant-alert-icon"></i>
+        已选择
+        <a style="font-weight: 600">{{ selectedRowKeys.length }}</a>
+        项
+        <a style="margin-left: 24px" @click="onClearSelected">清空</a>
+      </div>
+
+      <a-table
+        ref="table"
+        size="middle"
+        bordered
+        rowKey="id"
+        :columns="columns"
+        :dataSource="dataSource"
+        :pagination="ipagination"
+        :loading="loading"
+        :rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
+        @change="handleTableChange">
+
+        <template slot="action" slot-scope="text, record">
+          <a @click="handleEdit(record)">编辑</a>
+
+          <a-divider type="vertical"/>
+          <a-dropdown>
+            <a class="ant-dropdown-link">更多
+              <a-icon type="down"/>
+            </a>
+            <a-menu slot="overlay">
+              <a-menu-item>
+                <a @click="goPageOnline(record)">功能测试</a>
+              </a-menu-item>
+
+              <a-menu-item>
+                <a @click="handleOnlineUrlShow(record)">配置地址</a>
+              </a-menu-item>
+
+              <a-menu-item>
+                <a @click="handleRemoveRecord(record.id)">移除</a>
+              </a-menu-item>
+
+            </a-menu>
+          </a-dropdown>
+        </template>
+
+        <template slot="dbsync" slot-scope="text">
+          <span v-if="text==='Y'" style="color:limegreen">已同步</span>
+          <span v-if="text==='N'" style="color:red">未同步</span>
+        </template>
+
+      </a-table>
+    </div>
+    <!-- table区域-end -->
+
+    <!-- 表单区域 -->
+    <onl-cgform-head-modal ref="modalForm" @ok="modalFormOk" :action-button="false"></onl-cgform-head-modal>
+
+    <!-- 提示online报表链接 -->
+    <a-modal
+      :title="onlineUrlTitle"
+      :visible="onlineUrlVisible"
+      @cancel="handleOnlineUrlClose">
+      <template slot="footer">
+        <a-button @click="handleOnlineUrlClose">关闭</a-button>
+        <a-button type="primary" class="copy-this-text" :data-clipboard-text="onlineUrl" @click="onCopyUrl">复制</a-button>
+      </template>
+      <p>{{ onlineUrl }}</p>
+    </a-modal>
+
+    <enhance-js ref="ehjs"></enhance-js>
+    <enhance-sql ref="ehsql"></enhance-sql>
+    <enhance-java ref="ehjava"></enhance-java>
+    <trans-db2-online ref="transd2o" @ok="transOk"></trans-db2-online>
+
+    <onl-cgform-button-list ref="btnList"></onl-cgform-button-list>
+  </a-card>
+</template>
+
+<script>
+  import { deleteAction, postAction,getAction } from '@/api/manage'
+  import { JeecgListMixin } from '@/mixins/JeecgListMixin'
+  import Clipboard from 'clipboard'
+  import { filterObj } from '@/utils/util';
+
+  export default {
+    name: 'OnlCgformHeadList',
+    mixins: [JeecgListMixin],
+    components: {
+    },
+    data() {
+      return {
+        description: 'Online表单视图',
+        // 表头
+        columns: [
+          {
+            title: '#',
+            dataIndex: '',
+            key: 'rowIndex',
+            width: 60,
+            align: 'center',
+            customRender: function(t, r, index) {
+              return parseInt(index) + 1
+            }
+          },
+
+          {
+            title: '视图表名',
+            align: 'center',
+            dataIndex: 'tableName'
+          },
+          {
+            title: '视图表描述',
+            align: 'center',
+            dataIndex: 'tableTxt'
+          },
+          {
+            title: '原表版本',
+            align: 'center',
+            dataIndex: 'tableVersion'
+          },
+          {
+            title: '视图版本',
+            align: 'center',
+            dataIndex: 'copyVersion'
+          },
+          {
+            title: '操作',
+            dataIndex: 'action',
+            align: 'center',
+            scopedSlots: { customRender: 'action' }
+          }
+        ],
+        url: {
+          list: '/online/cgform/head/list',
+          delete: '/online/cgform/head/delete',
+          deleteBatch: '/online/cgform/head/deleteBatch',
+          removeRecord: '/online/cgform/head/removeRecord',
+        },
+        tableTypeDictOptions: [],
+        sexDictOptions: [],
+        syncModalVisible: false,
+        syncFormId: '',
+        synMethod: 'normal',
+        syncLoading: false,
+        onlineUrlTitle: '',
+        onlineUrlVisible: false,
+        onlineUrl: '',
+        selectedRowKeys: [],
+        selectedRows: [],
+        physicId:""
+      }
+    },
+    watch: {
+      '$route'() {
+        this.loadData()
+      }
+    },
+    methods: {
+      getQueryParams() {
+        //获取查询条件
+        var param = Object.assign({}, this.queryParam, this.isorter ,this.filters);
+        param.field = this.getQueryField();
+        param.pageNo = this.ipagination.current;
+        param.pageSize = this.ipagination.pageSize;
+        param.copyType = 1;
+        param.physicId = this.physicId;
+        return filterObj(param);
+      },
+
+      loadData(arg) {
+        if(!this.$route.params.code){
+          return false
+        }
+        this.physicId = this.$route.params.code
+        if(!this.url.list){
+          this.$message.error("请设置url.list属性!")
+          return
+        }
+        //加载数据 若传入参数1则加载第一页的内容
+        if (arg === 1) {
+          this.ipagination.current = 1;
+        }
+        var params = this.getQueryParams();//查询条件
+        this.loading = true;
+        getAction(this.url.list, params).then((res) => {
+          if (res.success) {
+            this.dataSource = res.result.records;
+            this.ipagination.total = res.result.total;
+          }
+          if(res.code===510){
+            this.$message.warning(res.message)
+          }
+          this.loading = false;
+        })
+      },
+
+      goPageOnline(rd) {
+        if(rd.isTree=='Y'){
+          this.$router.push({ path: '/online/cgformTreeList/' + rd.id })
+        }else{
+          this.$router.push({ path: '/online/cgformList/' + rd.id })
+        }
+      },
+      handleOnlineUrlClose() {
+        this.onlineUrlTitle = ''
+        this.onlineUrlVisible = false
+      },
+      handleOnlineUrlShow(record) {
+        if(record.isTree=='Y'){
+          this.onlineUrl = `/online/cgformTreeList/${record.id}`
+        }else{
+          this.onlineUrl = `/online/cgformList/${record.id}`
+        }
+        this.onlineUrlVisible = true
+        this.onlineUrlTitle = '菜单链接[' + record.tableTxt + ']'
+      },
+      handleRemoveRecord(id) {
+        let that = this
+        this.$confirm({
+          title: '确认要移除此记录?',
+          onOk() {
+            deleteAction(that.url.removeRecord, { id: id }).then((res) => {
+              if (res.success) {
+                that.$message.success('移除成功')
+                that.loadData()
+              } else {
+                that.$message.warning(res.message)
+              }
+            })
+          },
+          onCancel() {
+          }
+        })
+      },
+      doEnhanceJs() {
+        if (!this.selectedRowKeys || this.selectedRowKeys.length != 1) {
+          this.$message.warning('请先选中一条记录')
+          return
+        }
+        this.$refs.ehjs.show(this.selectedRowKeys[0])
+      },
+      doEnhanceSql() {
+        if (!this.selectedRowKeys || this.selectedRowKeys.length != 1) {
+          this.$message.warning('请先选中一条记录')
+          return
+        }
+        this.$refs.ehsql.show(this.selectedRowKeys[0])
+      },
+      doEnhanceJava() {
+        if (!this.selectedRowKeys || this.selectedRowKeys.length != 1) {
+          this.$message.warning('请先选中一条记录')
+          return
+        }
+        this.$refs.ehjava.show(this.selectedRowKeys[0])
+      },
+      doCgformButton() {
+        if (!this.selectedRowKeys || this.selectedRowKeys.length != 1) {
+          this.$message.warning('请先选中一条记录')
+          return
+        }
+        this.$refs.btnList.show(this.selectedRowKeys[0])
+
+        //this.$router.push({ path: '/online/cgformButton/' + this.selectedRowKeys[0] })
+      },
+      importOnlineForm() {
+        this.$refs.transd2o.show()
+      },
+      transOk() {
+        this.loadData()
+      },
+      onSelectChange(keys, rows) {
+        this.selectedRowKeys = keys
+        this.selectedRows = rows
+      },
+      onCopyUrl(){
+        var clipboard = new Clipboard('.copy-this-text')
+        clipboard.on('success', () => {
+          clipboard.destroy()
+          this.$message.success('复制成功')
+          this.handleOnlineUrlClose()
+        })
+        clipboard.on('error', () => {
+          this.$message.error('该浏览器不支持自动复制')
+          clipboard.destroy()
+        })
+      },
+      showMyCopyInfo(id){
+        console.log("查看复制表单的信息",id)
+      },
+      copyConfig(id){
+        postAction(`${this.url.copyOnline}?code=${id}`).then(res=>{
+          if(res.success){
+            this.$message.success("复制成功")
+            this.loadData()
+          }else{
+            this.$message.error("复制失败>>"+res.message)
+          }
+        })
+      }
+
+    }
+  }
+</script>
+<style lang="less">
+  .ant-card-body .table-operator {
+    margin-bottom: 18px;
+  }
+
+  .ant-table-tbody .ant-table-row td {
+    padding-top: 15px;
+    padding-bottom: 15px;
+  }
+
+  .anty-row-operator button {
+    margin: 0 5px
+  }
+
+  .ant-btn-danger {
+    background-color: #ffffff
+  }
+
+  .ant-modal-cust-warp {
+    height: 100%
+  }
+
+  .ant-modal-cust-warp .ant-modal-body {
+    height: calc(100% - 110px) !important;
+    overflow-y: auto
+  }
+
+  .ant-modal-cust-warp .ant-modal-content {
+    height: 90% !important;
+    overflow-y: hidden
+  }
+
+  .valid-error-cust{
+    .ant-select-selection{
+      border:2px solid #f5222d;
+    }
+  }
+</style>

+ 478 - 0
src/views/modules/online/cgform/OnlCgformHeadList.vue

@@ -0,0 +1,478 @@
+<template>
+  <a-card :bordered="false">
+
+    <!-- 查询区域 -->
+    <div class="table-page-search-wrapper">
+      <a-form layout="inline">
+        <a-row :gutter="24">
+
+          <a-col :md="6" :sm="24">
+            <a-form-item label="表名">
+              <a-input placeholder="请输入表名" v-model="queryParam.tableName"></a-input>
+            </a-form-item>
+          </a-col>
+          <a-col :md="6" :sm="24">
+            <a-form-item label="表类型">
+              <j-dict-select-tag dictCode="cgform_table_type" v-model="queryParam.tableType"/>
+            </a-form-item>
+          </a-col>
+
+          <a-col :md="6" :sm="24">
+            <span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
+              <a-button type="primary" @click="searchQuery" icon="search">查询</a-button>
+              <a-button type="primary" @click="searchReset" icon="reload" style="margin-left: 8px">重置</a-button>
+            </span>
+          </a-col>
+
+        </a-row>
+      </a-form>
+    </div>
+
+    <!-- 操作按钮区域 -->
+    <div class="table-operator">
+      <a-button @click="handleAdd" type="primary" icon="plus">新增</a-button>
+      <a-button @click="doCgformButton" type="primary" icon="highlight" style="margin-left:8px">自定义按钮</a-button>
+      <a-button @click="doEnhanceJs" type="primary" icon="strikethrough" style="margin-left:8px">JS增强</a-button>
+      <a-button @click="doEnhanceSql" type="primary" icon="filter" v-has="'online:sql'" style="margin-left:8px">SQL增强</a-button>
+      <a-button @click="doEnhanceJava" type="primary" icon="tool" style="margin-left:8px">Java增强</a-button>
+      <a-button @click="importOnlineForm" type="primary" icon="database" style="margin-left:8px">从数据库导入表单</a-button>
+      <a-button @click="goGenerateCode" v-has="'online:goGenerateCode'" type="primary" icon="database" style="margin-left:8px">代码生成</a-button>
+
+      <a-dropdown v-if="selectedRowKeys.length > 0">
+        <a-menu slot="overlay">
+          <a-menu-item key="1" @click="batchDel">
+            <a-icon type="delete"/>
+            删除
+          </a-menu-item>
+        </a-menu>
+        <a-button style="margin-left: 8px"> 批量操作
+          <a-icon type="down"/>
+        </a-button>
+      </a-dropdown>
+    </div>
+
+    <!-- table区域-begin -->
+    <div>
+      <div class="ant-alert ant-alert-info" style="margin-bottom: 16px;">
+        <i class="anticon anticon-info-circle ant-alert-icon"></i>
+        已选择
+        <a style="font-weight: 600">{{ selectedRowKeys.length }}</a>
+        项
+        <a style="margin-left: 24px" @click="onClearSelected">清空</a>
+      </div>
+
+      <a-table
+        ref="table"
+        size="middle"
+        bordered
+        rowKey="id"
+        :columns="columns"
+        :dataSource="dataSource"
+        :pagination="ipagination"
+        :loading="loading"
+        :rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
+        @change="handleTableChange">
+
+        <template slot="action" slot-scope="text, record">
+          <a @click="handleEdit(record)">编辑</a>
+
+          <a-divider type="vertical"/>
+          <a-dropdown>
+            <a class="ant-dropdown-link">更多
+              <a-icon type="down"/>
+            </a>
+            <a-menu slot="overlay">
+              <a-menu-item v-if="record.isDbSynch!='Y'">
+                <a @click="openSyncModal(record.id)">同步数据库</a>
+              </a-menu-item>
+
+              <template v-if="record.isDbSynch=='Y' && record.tableType !== 3">
+                <a-menu-item>
+                  <a @click="goPageOnline(record)">功能测试</a>
+                </a-menu-item>
+                <a-menu-item>
+                  <a @click="handleOnlineUrlShow(record)">配置地址</a>
+                </a-menu-item>
+              </template>
+
+              <a-menu-item>
+                <a @click="copyConfig(record.id)">复制视图</a>
+              </a-menu-item>
+
+              <a-menu-item v-if="record.hascopy==1">
+                <a @click="showMyCopyInfo(record.id)">配置视图</a>
+              </a-menu-item>
+
+              <a-menu-item>
+                <a @click="handleRemoveRecord(record.id)">移除</a>
+              </a-menu-item>
+
+              <a-menu-item>
+                <a-popconfirm title="确定删除吗?" @confirm="() => handleDelete(record.id)">
+                  <a>删除</a>
+                </a-popconfirm>
+              </a-menu-item>
+
+            </a-menu>
+          </a-dropdown>
+        </template>
+
+        <template slot="dbsync" slot-scope="text">
+          <span v-if="text==='Y'" style="color:limegreen">已同步</span>
+          <span v-if="text==='N'" style="color:red">未同步</span>
+        </template>
+
+      </a-table>
+    </div>
+    <!-- table区域-end -->
+
+    <!-- 表单区域 -->
+    <onl-cgform-head-modal ref="modalForm" @ok="modalFormOk"></onl-cgform-head-modal>
+
+    <!-- 同步数据库提示框 -->
+    <a-modal
+      :width="500"
+      :height="300"
+      title="同步数据库"
+      :visible="syncModalVisible"
+      @cancel="handleCancleDbSync"
+      style="top:5%;height: 95%;">
+      <template slot="footer">
+        <a-button @click="handleCancleDbSync">关闭</a-button>
+        <a-button type="primary" :loading="syncLoading" @click="handleDbSync">
+          确定
+        </a-button>
+      </template>
+      <a-radio-group v-model="synMethod">
+        <a-radio style="display: block;width: 30px;height: 30px" value="normal">普通同步(保留表数据)</a-radio>
+        <a-radio style="display: block;width: 30px;height: 30px" value="force">强制同步(删除表,重新生成)</a-radio>
+      </a-radio-group>
+    </a-modal>
+
+    <!-- 提示online报表链接 -->
+    <a-modal
+      :title="onlineUrlTitle"
+      :visible="onlineUrlVisible"
+      @cancel="handleOnlineUrlClose">
+      <template slot="footer">
+        <a-button @click="handleOnlineUrlClose">关闭</a-button>
+        <a-button type="primary" class="copy-this-text" :data-clipboard-text="onlineUrl" @click="onCopyUrl">复制</a-button>
+      </template>
+      <p>{{ onlineUrl }}</p>
+    </a-modal>
+
+    <enhance-js ref="ehjs"></enhance-js>
+    <enhance-sql ref="ehsql"></enhance-sql>
+    <enhance-java ref="ehjava"></enhance-java>
+    <trans-db2-online ref="transd2o" @ok="transOk"></trans-db2-online>
+    <code-generator ref="cg"></code-generator>
+
+    <onl-cgform-button-list ref="btnList"></onl-cgform-button-list>
+  </a-card>
+</template>
+
+<script>
+  import { initDictOptions, filterDictText } from '@/components/dict/JDictSelectUtil'
+  import { deleteAction, postAction } from '@/api/manage'
+  import JDictSelectTag from '../../../../components/dict/JDictSelectTag.vue'
+  import { JeecgListMixin } from '@/mixins/JeecgListMixin'
+  import Clipboard from 'clipboard'
+  import { filterObj } from '@/utils/util';
+
+  export default {
+    name: 'OnlCgformHeadList',
+    mixins: [JeecgListMixin],
+    components: {
+      JDictSelectTag,
+    },
+    data() {
+      return {
+        description: 'Online表单开发管理页面',
+        // 表头
+        columns: [
+          {
+            title: '#',
+            dataIndex: '',
+            key: 'rowIndex',
+            width: 60,
+            align: 'center',
+            customRender: function(t, r, index) {
+              return parseInt(index) + 1
+            }
+          },
+          {
+            title: '表类型',
+            align: 'center',
+            dataIndex: 'tableType',
+            customRender: (text) => {
+              return filterDictText(this.tableTypeDictOptions, `${text}`)
+            }
+          },
+          {
+            title: '表名',
+            align: 'center',
+            dataIndex: 'tableName'
+          },
+          {
+            title: '表描述',
+            align: 'center',
+            dataIndex: 'tableTxt'
+          },
+          {
+            title: '版本',
+            align: 'center',
+            dataIndex: 'tableVersion'
+          },
+
+          {
+            title: '同步数据库状态',
+            align: 'center',
+            dataIndex: 'isDbSynch',
+            scopedSlots: { customRender: 'dbsync' }
+          },
+          {
+            title: '操作',
+            dataIndex: 'action',
+            align: 'center',
+            scopedSlots: { customRender: 'action' }
+          }
+        ],
+        url: {
+          list: '/online/cgform/head/list',
+          delete: '/online/cgform/head/delete',
+          deleteBatch: '/online/cgform/head/deleteBatch',
+          doDbSynch: '/online/cgform/api/doDbSynch/',
+          removeRecord: '/online/cgform/head/removeRecord',
+          copyOnline: '/online/cgform/head/copyOnline'
+        },
+        tableTypeDictOptions: [],
+        sexDictOptions: [],
+        syncModalVisible: false,
+        syncFormId: '',
+        synMethod: 'normal',
+        syncLoading: false,
+        onlineUrlTitle: '',
+        onlineUrlVisible: false,
+        onlineUrl: '',
+        selectedRowKeys: [],
+        selectedRows: []
+      }
+    },
+    created() {
+      //初始化字典 - 表类型
+      initDictOptions('cgform_table_type').then((res) => {
+        if (res.success) {
+          this.tableTypeDictOptions = res.result
+        }
+      })
+    },
+    methods: {
+      doDbSynch(id) {
+        postAction(this.url.doDbSynch + id, { synMethod: '1' }).then((res) => {
+          if (res.success) {
+            this.$message.success(res.message)
+            this.loadData()
+          } else {
+            this.$message.warning(res.message)
+          }
+        })
+      },
+      getQueryParams() {
+        //获取查询条件
+        var param = Object.assign({}, this.queryParam, this.isorter ,this.filters);
+        param.field = this.getQueryField();
+        param.pageNo = this.ipagination.current;
+        param.pageSize = this.ipagination.pageSize;
+        param.copyType = 0;
+        return filterObj(param);
+      },
+      handleCancleDbSync() {
+        this.syncModalVisible = false
+      },
+      handleDbSync() {
+        this.syncLoading = true
+        postAction(this.url.doDbSynch + this.syncFormId + '/' + this.synMethod).then((res) => {
+          this.syncModalVisible = false
+          this.syncLoading = false
+          if (res.success) {
+            this.$message.success(res.message)
+            this.loadData()
+          } else {
+            this.$message.warning(res.message)
+          }
+        })
+        setTimeout(()=>{
+          if(this.syncLoading){
+            this.syncModalVisible = false
+            this.syncLoading = false
+            this.$message.success("网络延迟,已自动刷新!")
+            this.loadData()
+          }
+        },10000)
+      },
+      openSyncModal(id) {
+        this.syncModalVisible = true
+        this.syncLoading = false
+        this.syncFormId = id
+      },
+      goPageOnline(rd) {
+        if(rd.isTree=='Y'){
+          this.$router.push({ path: '/online/cgformTreeList/' + rd.id })
+        }else{
+          this.$router.push({ path: '/online/cgformList/' + rd.id })
+        }
+      },
+      handleOnlineUrlClose() {
+        this.onlineUrlTitle = ''
+        this.onlineUrlVisible = false
+      },
+      handleOnlineUrlShow(record) {
+        if(record.isTree=='Y'){
+          this.onlineUrl = `/online/cgformTreeList/${record.id}`
+        }else{
+          this.onlineUrl = `/online/cgformList/${record.id}`
+        }
+        this.onlineUrlVisible = true
+        this.onlineUrlTitle = '菜单链接[' + record.tableTxt + ']'
+      },
+      handleRemoveRecord(id) {
+        let that = this
+        this.$confirm({
+          title: '确认要移除此记录?',
+          onOk() {
+            deleteAction(that.url.removeRecord, { id: id }).then((res) => {
+              if (res.success) {
+                that.$message.success('移除成功')
+                that.loadData()
+              } else {
+                that.$message.warning(res.message)
+              }
+            })
+          },
+          onCancel() {
+          }
+        })
+      },
+      doEnhanceJs() {
+        if (!this.selectedRowKeys || this.selectedRowKeys.length != 1) {
+          this.$message.warning('请先选中一条记录')
+          return
+        }
+        this.$refs.ehjs.show(this.selectedRowKeys[0])
+      },
+      doEnhanceSql() {
+        if (!this.selectedRowKeys || this.selectedRowKeys.length != 1) {
+          this.$message.warning('请先选中一条记录')
+          return
+        }
+        this.$refs.ehsql.show(this.selectedRowKeys[0])
+      },
+      doEnhanceJava() {
+        if (!this.selectedRowKeys || this.selectedRowKeys.length != 1) {
+          this.$message.warning('请先选中一条记录')
+          return
+        }
+        this.$refs.ehjava.show(this.selectedRowKeys[0])
+      },
+      doCgformButton() {
+        if (!this.selectedRowKeys || this.selectedRowKeys.length != 1) {
+          this.$message.warning('请先选中一条记录')
+          return
+        }
+        this.$refs.btnList.show(this.selectedRowKeys[0])
+
+        //this.$router.push({ path: '/online/cgformButton/' + this.selectedRowKeys[0] })
+      },
+      importOnlineForm() {
+        this.$refs.transd2o.show()
+      },
+      transOk() {
+        this.loadData()
+      },
+      goGenerateCode() {
+        if (!this.selectedRowKeys || this.selectedRowKeys.length != 1) {
+          this.$message.warning('请先选中一条记录')
+          return
+        }
+        let row = this.selectedRows[0]
+        if (!row.isDbSynch || row.isDbSynch == 'N') {
+          this.$message.warning('请先同步数据库!')
+          return
+        }
+        if (row.tableType == 3) {
+          this.$message.warning('请选中该表对应的主表生成代码')
+          return
+        }
+        this.$refs.cg.show(this.selectedRowKeys[0])
+      },
+      onSelectChange(keys, rows) {
+        this.selectedRowKeys = keys
+        this.selectedRows = rows
+      },
+      onCopyUrl(){
+        var clipboard = new Clipboard('.copy-this-text')
+        clipboard.on('success', () => {
+          clipboard.destroy()
+          this.$message.success('复制成功')
+          this.handleOnlineUrlClose()
+        })
+        clipboard.on('error', () => {
+          this.$message.error('该浏览器不支持自动复制')
+          clipboard.destroy()
+        })
+      },
+      showMyCopyInfo(id){
+        this.$router.push({ path: '/online/copyform/' + id })
+      },
+      copyConfig(id){
+        postAction(`${this.url.copyOnline}?code=${id}`).then(res=>{
+          if(res.success){
+            this.$message.success("复制成功")
+            this.loadData()
+          }else{
+            this.$message.error("复制失败>>"+res.message)
+          }
+        })
+      }
+
+    }
+  }
+</script>
+<style lang="less">
+  .ant-card-body .table-operator {
+    margin-bottom: 18px;
+  }
+
+  .ant-table-tbody .ant-table-row td {
+    padding-top: 15px;
+    padding-bottom: 15px;
+  }
+
+  .anty-row-operator button {
+    margin: 0 5px
+  }
+
+  .ant-btn-danger {
+    background-color: #ffffff
+  }
+
+  .ant-modal-cust-warp {
+    height: 100%
+  }
+
+  .ant-modal-cust-warp .ant-modal-body {
+    height: calc(100% - 110px) !important;
+    overflow-y: auto
+  }
+
+  .ant-modal-cust-warp .ant-modal-content {
+    height: 90% !important;
+    overflow-y: hidden
+  }
+
+  .valid-error-cust{
+    .ant-select-selection{
+      border:2px solid #f5222d;
+    }
+  }
+</style>

+ 820 - 0
src/views/modules/online/cgform/auto/OnlCgformAutoList.vue

@@ -0,0 +1,820 @@
+<template>
+  <a-card :bordered="false" style="height: 100%">
+    <div class="table-page-search-wrapper">
+      <a-form layout="inline" @keyup.enter.native="searchByquery">
+        <a-row :gutter="24" v-if="queryInfo && queryInfo.length>0">
+          <template v-for="(item,index) in queryInfo">
+            <template v-if=" item.hidden==='1' ">
+              <a-col v-if="item.view=='datetime'" :md="12" :sm="16" :key=" 'query'+index " v-show="toggleSearchStatus">
+                <online-query-form-item :queryParam="queryParam" :item="item" :dictOptions="dictOptions"></online-query-form-item>
+              </a-col>
+              <a-col v-else :md="6" :sm="8" :key=" 'query'+index " v-show="toggleSearchStatus">
+                <online-query-form-item :queryParam="queryParam" :item="item" :dictOptions="dictOptions"></online-query-form-item>
+              </a-col>
+            </template>
+            <template v-else>
+              <a-col v-if="item.view=='datetime'" :md="12" :sm="16" :key=" 'query'+index ">
+                <online-query-form-item :queryParam="queryParam" :item="item" :dictOptions="dictOptions"></online-query-form-item>
+              </a-col>
+              <a-col v-else :md="6" :sm="8" :key=" 'query'+index ">
+                <online-query-form-item :queryParam="queryParam" :item="item" :dictOptions="dictOptions"></online-query-form-item>
+              </a-col>
+            </template>
+          </template>
+
+          <a-col :md="6" :sm="8">
+            <span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
+              <a-button type="primary" @click="searchByquery" icon="search">查询</a-button>
+              <a-button type="primary" @click="searchReset" icon="reload" style="margin-left: 8px">重置</a-button>
+              <a @click="handleToggleSearch" style="margin-left: 8px">
+                {{ toggleSearchStatus ? '收起' : '展开' }}
+                <a-icon :type="toggleSearchStatus ? 'up' : 'down'"/>
+              </a>
+            </span>
+          </a-col>
+
+        </a-row>
+      </a-form>
+    </div>
+
+    <!-- 操作按钮区域 -->
+    <div class="table-operator">
+      <a-button v-if="buttonSwitch.add" @click="handleAdd" type="primary" icon="plus">新增</a-button>
+      <a-button v-if="buttonSwitch.import" @click="handleImportXls" type="primary" icon="upload" style="margin-left:8px">导入</a-button>
+      <a-button v-if="buttonSwitch.export" @click="handleExportXls" type="primary" icon="download" style="margin-left:8px">导出</a-button>
+      <template v-if="cgButtonList && cgButtonList.length>0" v-for="(item,index) in cgButtonList">
+        <a-button
+          v-if=" item.optType=='js' "
+          :key=" 'cgbtn'+index "
+          @click="cgButtonJsHandler(item.buttonCode)"
+          type="primary"
+          :icon="item.buttonIcon"
+          style="margin-left:8px">
+          {{ item.buttonName }}
+        </a-button>
+        <a-button
+          v-else-if=" item.optType=='action' "
+          :key=" 'cgbtn'+index "
+          @click="cgButtonActionHandler(item.buttonCode)"
+          type="primary"
+          :icon="item.buttonIcon"
+          style="margin-left:8px">
+          {{ item.buttonName }}
+        </a-button>
+      </template>
+
+      <!-- 高级查询 -->
+      <j-super-query
+        ref="superQuery"
+        :fieldList="superQuery.fieldList"
+        :saveCode="$route.fullPath"
+        :loading="table.loading"
+        style="margin-left: 8px;"
+        @handleSuperQuery="handleSuperQuery"/>
+
+      <a-button
+        v-if="buttonSwitch.batch_delete"
+        @click="handleDelBatch"
+        style="margin-left:8px"
+        v-show="table.selectedRowKeys.length > 0"
+        ghost
+        type="primary"
+        icon="delete">批量删除</a-button>
+    </div>
+
+    <div>
+      <div class="ant-alert ant-alert-info" style="margin-bottom: 16px;">
+        <i class="anticon anticon-info-circle ant-alert-icon"></i>
+        已选择&nbsp;<a style="font-weight: 600">{{ table.selectedRowKeys.length }}</a>项&nbsp;&nbsp;
+        <a style="margin-left: 24px" @click="onClearSelected">清空</a>
+      </div>
+
+      <a-table
+        ref="cgformAutoList"
+        bordered
+        size="middle"
+        rowKey="id"
+        :columns="table.columns"
+        :dataSource="table.dataSource"
+        :pagination="table.pagination"
+        :loading="table.loading"
+        :rowSelection="rowSelectionConfig"
+        @change="handleTableChange"
+        :scroll="table.scroll"
+        style="min-height: 300px">
+
+        <template slot="dateSlot" slot-scope="text">
+          <span>{{ getFormatDate(text) }}</span>
+        </template>
+
+        <template slot="htmlSlot" slot-scope="text">
+          <div v-html="text"></div>
+        </template>
+
+        <template slot="imgSlot" slot-scope="text">
+          <span v-if="!text" style="font-size: 12px;font-style: italic;">无图片</span>
+          <img v-else :src="getImgView(text)" height="25px" alt="图片不存在" style="max-width:80px;font-size: 12px;font-style: italic;"/>
+        </template>
+
+        <template slot="fileSlot" slot-scope="text">
+          <span v-if="!text" style="font-size: 12px;font-style: italic;">无文件</span>
+          <a-button
+            v-else
+            :ghost="true"
+            type="primary"
+            icon="download"
+            size="small"
+            @click="downloadRowFile(text)">
+            下载
+          </a-button>
+        </template>
+
+        <span slot="action" slot-scope="text, record">
+          <template v-if="hasBpmStatus">
+            <template v-if="record.bpm_status == '1'||record.bpm_status == ''|| record.bpm_status == null">
+              <template v-if="buttonSwitch.update">
+              <a @click="handleEdit(record)">编辑</a>
+              <a-divider type="vertical"/>
+            </template>
+            </template>
+          </template>
+          <template v-else>
+            <template v-if="buttonSwitch.update">
+              <a @click="handleEdit(record)">编辑</a>
+              <a-divider type="vertical"/>
+            </template>
+          </template>
+          <a-dropdown>
+            <a class="ant-dropdown-link">
+              更多 <a-icon type="down" />
+            </a>
+            <a-menu slot="overlay">
+              <a-menu-item >
+                <a href="javascript:;" @click="handleDetail(record)">详情</a>
+              </a-menu-item>
+              <template v-if="hasBpmStatus">
+                <template v-if="record.bpm_status == '1'||record.bpm_status == ''|| record.bpm_status == null">
+                    <a-menu-item>
+                      <a href="javascript:;" @click="startProcess(record)">提交流程</a>
+                    </a-menu-item>
+                    <a-menu-item v-if="buttonSwitch.delete">
+                      <a-popconfirm title="确定删除吗?" @confirm="() => handleDeleteOne(record)">
+                        <a>删除</a>
+                      </a-popconfirm>
+                    </a-menu-item>
+                </template>
+                <template v-else>
+                    <a-menu-item @click="handlePreviewPic(record)">审批进度</a-menu-item>
+                </template>
+              </template>
+              <template v-else>
+                <a-menu-item v-if="buttonSwitch.delete">
+                  <a-popconfirm title="确定删除吗?" @confirm="() => handleDeleteOne(record)">
+                    <a>删除</a>
+                  </a-popconfirm>
+                </a-menu-item>
+               </template>
+              <template v-if="cgButtonLinkList && cgButtonLinkList.length>0" v-for="(btnItem,btnIndex) in cgButtonLinkList">
+                <a-menu-item :key=" 'cgbtnLink'+btnIndex ">
+                  <a href="javascript:void(0);" @click="cgButtonLinkHandler(record,btnItem.buttonCode,btnItem.optType)">
+                    <a-icon v-if="btnItem.buttonIcon" :type="btnItem.buttonIcon" />
+                    {{ btnItem.buttonName }}
+                  </a>
+                </a-menu-item>
+              </template>
+
+            </a-menu>
+          </a-dropdown>
+        </span>
+      </a-table>
+
+      <onl-cgform-auto-modal @success="handleFormSuccess" ref="modal" :code="code" @schema="handleGetSchema" />
+
+      <j-import-modal ref="importModal" :url="getImportUrl()" @ok="importOk"></j-import-modal>
+
+    </div>
+  </a-card>
+</template>
+
+<script>
+
+  import { postAction,getAction,deleteAction,downFile } from '@/api/manage'
+  import { filterMultiDictText } from '@/components/dict/JDictSelectUtil'
+  import { filterObj } from '@/utils/util';
+  import JImportModal from '@/components/jeecg/JImportModal'
+  import JSuperQuery from '@comp/jeecg/JSuperQuery'
+
+  export default {
+    name: 'OnlCgFormAutoList',
+    components: {
+      JSuperQuery,
+      JImportModal,
+    },
+    data() {
+      return {
+        code: '',
+        description: '在线报表功能测试页面',
+        currentTableName:"",
+        url: {
+          getQueryInfo:'/online/cgform/api/getQueryInfo/',
+          getColumns: '/online/cgform/api/getColumns/',
+          getData: '/online/cgform/api/getData/',
+          optPre:"/online/cgform/api/form/",
+          exportXls:'/online/cgform/api/exportXls/',
+          buttonAction:'/online/cgform/api/doButton',
+          startProcess: "/process/extActProcess/startMutilProcess",
+        },
+        flowCodePre:"onl_",
+        isorter:{
+          column: 'createTime',
+          order: 'desc',
+        },
+        //dictOptions:{fieldName:[]}
+        dictOptions:{
+
+        },
+        cgButtonLinkList:[],
+        cgButtonList:[],
+        queryInfo:[],
+        // 查询参数,多个页面的查询参数用 code 作为键来区分
+        queryParamsMap: {},
+        toggleSearchStatus:false,
+        table: {
+          loading: true,
+          scroll:{x:false},
+          // 表头
+          columns: [],
+          //数据集
+          dataSource: [],
+          // 选择器
+          selectedRowKeys: [],
+          selectionRows: [],
+          // 分页参数
+          pagination: {
+
+          }
+        },
+        metaPagination:{
+          current: 1,
+          pageSize: 10,
+          pageSizeOptions: ['10', '20', '30'],
+          showTotal: (total, range) => {
+            return range[0] + '-' + range[1] + ' 共' + total + '条'
+          },
+          showQuickJumper: true,
+          showSizeChanger: true,
+          total: 0
+        },
+        actionColumn:{
+          title: '操作',
+          dataIndex: 'action',
+          scopedSlots: { customRender: 'action' },
+          fixed:"right",
+          align:"center",
+          width:150
+        },
+        formTemplate:"99",
+        EnhanceJS:'',
+        hideColumns:[],
+        buttonSwitch:{
+          add:true,
+          update:true,
+          delete:true,
+          batch_delete:true,
+          import:true,
+          export:true
+        },
+        hasBpmStatus:false,
+        checkboxFlag:false,
+        // 高级查询
+        superQuery: {
+          // 字段列表
+          fieldList: [],
+          // 查询参数
+          params: '',
+          // 查询条件拼接方式 'and' or 'or'
+          matchType: 'and'
+        }
+      }
+    },
+    created() {
+      this.initAutoList();
+    },
+    mounted(){
+      this.cgButtonJsHandler('mounted')
+    },
+    watch: {
+      '$route.path'(newVal,oldVal) {
+        console.log('$route.path: ',oldVal)
+        // 刷新参数放到这里去触发,就可以刷新相同界面了
+        this.initAutoList()
+      }
+    },
+    computed:{
+      rowSelectionConfig:function() {
+        if(!this.checkboxFlag){
+          return null
+        }
+        return {
+          fixed:true,
+          selectedRowKeys:this.table.selectedRowKeys,
+          onChange: this.handleChangeInTableSelect
+        }
+      },
+      queryParam: {
+        get() {
+          return this.queryParamsMap[this.code]
+        },
+        set(newVal) {
+          this.$set(this.queryParamsMap, this.code, newVal)
+        }
+      }
+    },
+    methods: {
+      hasBpmStatusFilter(){
+        var columnObjs = this.table.columns;
+        let columns = [];
+        for (var item of columnObjs) {
+          columns.push(item.dataIndex);
+        }
+        if(columns.includes('bpm_status')||columns.includes('BPM_STATUS')){
+          this.hasBpmStatus = true;
+        }else{
+          this.hasBpmStatus = false;
+        }
+      },
+      startProcess: function(record){
+        var that = this;
+        this.$confirm({
+          title:"提示",
+          content:"确认提交流程吗?",
+          onOk: function(){
+            var param = {
+              flowCode:that.flowCodePre+that.currentTableName,
+              id:record.id,
+              formUrl:"modules/bpm/task/form/OnlineFormDetail",
+              formUrlMobile:"modules/bpm/task/form/OnlineFormDetail"
+            }
+            postAction(that.url.startProcess,param).then((res)=>{
+              if(res.success){
+                that.$message.success(res.message);
+                that.loadData();
+                that.onClearSelected();
+              }else{
+                that.$message.warning(res.message);
+              }
+            });
+          }
+        });
+      },
+      initQueryInfo(){
+        getAction(`${this.url.getQueryInfo}${this.code}`).then((res)=>{
+          console.log("--onlineList-获取查询条件配置",res);
+          if(res.success){
+            this.queryInfo = res.result
+          }else{
+            this.$message.warning(res.message)
+          }
+        })
+      },
+      initAutoList(){
+        if(!this.$route.params.code){
+          return false
+        }
+        // 清空高级查询条件
+        this.superQuery.params = ''
+        if (this.$refs.superQuery) {
+          this.$refs.superQuery.handleReset()
+        }
+
+        this.table.loading = true
+        this.code = this.$route.params.code
+        if (!this.queryParam) {
+          this.queryParam = {}
+        }
+        getAction(`${this.url.getColumns}${this.code}`).then((res)=>{
+          console.log("--onlineList-加载动态列>>",res);
+          if(res.success){
+            if(res.result.checkboxFlag == 'Y'){
+              this.checkboxFlag = true
+            }else{
+              this.checkboxFlag = false
+            }
+
+            if(res.result.paginationFlag=='Y'){
+              this.table.pagination = {...this.metaPagination}
+            }else{
+              this.table.pagination = false
+            }
+
+            this.dictOptions = res.result.dictOptions
+            this.formTemplate = res.result.formTemplate
+            this.description = res.result.description
+            this.currentTableName = res.result.currentTableName
+            this.initCgButtonList(res.result.cgButtonList)
+            this.initCgEnhanceJs(res.result.enhanceJs)
+            this.initButtonSwitch(res.result.hideColumns)
+            let currColumns = res.result.columns
+            for(let a=0;a<currColumns.length;a++){
+              if(currColumns[a].customRender){
+                let dictCode = currColumns[a].customRender;
+                let replaceFlag = '_replace_text_';
+                if(dictCode.startsWith(replaceFlag)){
+                  let textFieldName = dictCode.replace(replaceFlag,'')
+                  currColumns[a].customRender=(text,record)=>{
+                    return record[textFieldName]
+                  }
+                }else{
+                  currColumns[a].customRender=(text)=>{
+                    return filterMultiDictText(this.dictOptions[dictCode], text);
+                  }
+                }
+              }
+            }
+            if(res.result.scrollFlag==1){
+              this.table.scroll = { x :'115%' }
+            }else{
+              this.table.scroll = { x :false }
+            }
+            currColumns.push(this.actionColumn);
+            this.table.columns = [...currColumns]
+            this.hasBpmStatusFilter();
+            this.loadData();
+            this.initQueryInfo();
+          }else{
+            this.$message.warning(res.message)
+          }
+        })
+      },
+      loadData(arg){
+        if(this.table.pagination){
+          if(arg==1){
+            this.table.pagination.current=1
+          }
+          this.table.loading = true
+          let params = this.getQueryParams();//查询条件
+          console.log("--onlineList-查询条件-->",params)
+          getAction(`${this.url.getData}${this.code}`,params).then((res)=>{
+            console.log("--onlineList-列表数据",res)
+            if(res.success){
+              let result = res.result;
+              if(Number(result.total)>0){
+                this.table.pagination.total = Number(result.total)
+                this.table.dataSource = result.records
+              }else{
+                this.table.pagination.total=0;
+                this.table.dataSource=[]
+                //this.$message.warning("查无数据")
+              }
+            }else{
+              this.$message.warning(res.message)
+            }
+          }).finally(() => {
+            this.table.loading = false
+          })
+        }else{
+          this.loadDataNoPage()
+        }
+      },
+      loadDataNoPage(){
+        this.table.loading = true
+        let param = this.getQueryParams()//查询条件
+        param['pageSize'] = -521;
+        getAction(`${this.url.getData}${this.code}`,filterObj(param)).then((res)=>{
+          console.log("--onlineList-列表数据",res)
+          if(res.success){
+            let result = res.result;
+            if(Number(result.total)>0){
+              this.table.dataSource = result.records
+            }else{
+              this.table.dataSource=[]
+            }
+          }else{
+            this.$message.warning(res.message)
+          }
+        }).finally(() => {
+          this.table.loading = false
+        })
+      },
+      getQueryParams() {
+        let param = Object.assign({}, this.queryParam,this.isorter);
+        param.pageNo = this.table.pagination.current;
+        param.pageSize = this.table.pagination.pageSize;
+        param.superQueryMatchType = this.superQuery.matchType
+        param.superQueryParams = encodeURIComponent(this.superQuery.params)
+        return filterObj(param);
+      },
+      handleChangeInTableSelect(selectedRowKeys, selectionRows) {
+        this.table.selectedRowKeys = selectedRowKeys
+        this.table.selectionRows = selectionRows
+        this.selectedRowKeys = selectedRowKeys
+      },
+      handleTableChange(pagination, filters, sorter){
+        //TODO 筛选
+        if (Object.keys(sorter).length>0){
+          this.isorter.column = sorter.field;
+          this.isorter.order = "ascend"==sorter.order?"asc":"desc"
+        }
+        this.table.pagination = pagination;
+        this.loadData();
+      },
+      handleAdd(){
+        this.cgButtonJsHandler('beforeAdd')
+        this.$refs.modal.add(this.formTemplate);
+      },
+      handleImportXls(){
+        this.$refs.importModal.show()
+      },
+      importOk(){
+        this.loadData(1)
+      },
+      handleExportXls2(){
+        let param = this.queryParam;
+        if(this.table.selectedRowKeys && this.table.selectedRowKeys.length>0){
+          param['selections'] = this.table.selectedRowKeys.join(",")
+        }
+        let paramsStr = encodeURI(JSON.stringify(param));
+        console.log('paramsStr: ' + paramsStr)
+        let url = window._CONFIG['domianURL']+this.url.exportXls+this.code+"?paramsStr="+paramsStr
+        window.location.href = url;
+      },
+      handleExportXls(){
+        let param = this.queryParam;
+        if(this.table.selectedRowKeys && this.table.selectedRowKeys.length>0){
+          param['selections'] = this.table.selectedRowKeys.join(",")
+        }
+        console.log("导出参数",param)
+        let paramsStr = JSON.stringify(filterObj(param));
+        downFile(this.url.exportXls+this.code,{paramsStr:paramsStr}).then((data)=>{
+          if (!data) {
+            this.$message.warning("文件下载失败")
+            return
+          }
+          if (typeof window.navigator.msSaveBlob !== 'undefined') {
+            window.navigator.msSaveBlob(new Blob([data]), this.description+'.xls')
+          }else{
+            let url = window.URL.createObjectURL(new Blob([data]))
+            let link = document.createElement('a')
+            link.style.display = 'none'
+            link.href = url
+            link.setAttribute('download', this.description+'.xls')
+            document.body.appendChild(link)
+            link.click()
+            document.body.removeChild(link); //下载完成移除元素
+            window.URL.revokeObjectURL(url); //释放掉blob对象
+          }
+        })
+      },
+      handleEdit(record){
+        this.cgButtonLinkHandler(record,"beforeEdit","js")
+        this.$refs.modal.edit(this.formTemplate,record.id);
+      },
+      handleDetail(record){
+        this.$refs.modal.detail(this.formTemplate,record.id);
+      },
+      handleDeleteOne(record){
+        this.cgButtonLinkHandler(record,"beforeDelete","js")
+        this.handleDelete(record.id)
+      },
+      handleDelete(id){
+        deleteAction(this.url.optPre+this.code+"/"+id).then((res)=>{
+          if(res.success){
+            this.$message.success(res.message)
+            this.loadData()
+          }else{
+            this.$message.warning(res.message)
+          }
+        })
+      },
+
+      handleFormSuccess(){
+        this.loadData()
+      },
+      // 查询完 schema 后,生成高级查询的字段列表
+      handleGetSchema(schema) {
+        if (schema && schema.properties) {
+          let setField = (array, field) => {
+            let type = field.type || 'string'
+            type = (type === 'inputNumber' ? 'number' : type)
+            array.push({
+              type: type,
+              value: field.key,
+              text: field.title,
+              // 额外字典参数
+              dictCode: field.dictCode,
+              dictTable: field.dictTable,
+              dictText: field.dictText,
+              options: field.enum || field.options
+            })
+          }
+          let fieldList = []
+          for (let key in schema.properties) {
+            if (!schema.properties.hasOwnProperty(key)) {
+              continue
+            }
+            let field = schema.properties[key]
+            // tab = 子表
+            if (field.view === 'tab') {
+              let subTable = {
+                type: 'sub-table',
+                value: field.key,
+                text: field.describe,
+                children: []
+              }
+              for (let column of field.columns) {
+                setField(subTable.children, column)
+              }
+              fieldList.push(subTable)
+            } else {
+              field.key = key
+              setField(fieldList, field)
+            }
+          }
+          this.superQuery.fieldList = fieldList
+        }
+      },
+      onClearSelected(){
+        this.table.selectedRowKeys = []
+        this.table.selectionRows = []
+      },
+      getImgView(text){
+        if(text && text.indexOf(",")>0){
+          text = text.substring(0,text.indexOf(","))
+        }
+        return window._CONFIG['imgDomainURL']+"/"+text
+      },
+      downloadRowFile(text){
+        if(!text){
+          this.$message.warning("未知的文件")
+          return;
+        }
+        if(text.indexOf(",")>0){
+          text = text.substring(0,text.indexOf(","))
+        }
+        window.open(window._CONFIG['downloadUrl']+"/"+text);//TODO 下载的方法
+      },
+      handleDelBatch(){
+        if(this.table.selectedRowKeys.length<=0){
+          this.$message.warning('请选择一条记录!');
+          return false;
+        }else{
+          let ids = "";
+          let that = this;
+          that.table.selectedRowKeys.forEach(function(val) {
+            ids+=val+",";
+          });
+          that.$confirm({
+            title:"确认删除",
+            content:"是否删除选中数据?",
+            onOk: function(){
+              that.handleDelete(ids)
+              that.onClearSelected();
+            }
+          });
+        }
+      },
+
+      searchByquery(){
+        this.loadData(1);
+      },
+      searchReset(){
+        this.queryParam = {}
+        this.loadData(1);
+      },
+      handleToggleSearch(){
+        this.toggleSearchStatus = !this.toggleSearchStatus;
+      },
+      getFormatDate(text){
+        if(!text){
+          return ''
+        }
+        let a = text;
+        if(a.length>10){
+          a = a.substring(0,10);
+        }
+        return a;
+      },
+      getImportUrl(){
+        return '/online/cgform/api/importXls/'+this.code
+      },
+      initCgEnhanceJs(enhanceJs){
+        //console.log("--onlineList-js增强",enhanceJs)
+        if(enhanceJs){
+          let Obj = eval ("(" + enhanceJs + ")");
+          this.EnhanceJS = new Obj(getAction,postAction,deleteAction);
+          this.cgButtonJsHandler('created')
+        }else{
+          this.EnhanceJS = ''
+        }
+      },
+      initCgButtonList(btnList){
+        let linkArr = []
+        let buttonArr = []
+        if(btnList && btnList.length>0){
+          for(let i=0;i<btnList.length;i++){
+            let temp = btnList[i]
+            if(temp.buttonStyle=='button'){
+              buttonArr.push(temp)
+            }else if(temp.buttonStyle=='link'){
+              linkArr.push(temp)
+            }
+          }
+        }
+        this.cgButtonLinkList = [...linkArr]
+        this.cgButtonList=[...buttonArr]
+      },
+      cgButtonJsHandler(buttonCode){
+        if(this.EnhanceJS[buttonCode]){
+          this.EnhanceJS[buttonCode](this)
+        }
+      },
+      cgButtonActionHandler(buttonCode){
+        //处理自定义button的 需要配置该button自定义sql
+        if(!this.table.selectedRowKeys || this.table.selectedRowKeys.length==0){
+          this.$message.warning("请先选中一条记录")
+          return false
+        }
+        if(this.table.selectedRowKeys.length>1){
+          this.$message.warning("请只选中一条记录")
+          return false
+        }
+        let params = {
+          formId:this.code,
+          buttonCode:buttonCode,
+          dataId:this.table.selectedRowKeys[0]
+        }
+        console.log("自定义按钮请求后台参数:",params)
+        postAction(this.url.buttonAction,params).then(res=>{
+          if(res.success){
+            this.loadData()
+            this.$message.success("处理完成!")
+          }else{
+            this.$message.warning("处理失败!")
+          }
+        })
+
+      },
+      cgButtonLinkHandler(record,buttonCode,optType){
+        if(optType=="js"){
+          if(this.EnhanceJS[buttonCode]){
+            this.EnhanceJS[buttonCode](this,record)
+          }
+        }else if(optType=="action"){
+          let params = {
+            formId:this.code,
+            buttonCode:buttonCode,
+            dataId:record.id
+          }
+          console.log("自定义按钮link请求后台参数:",params)
+          postAction(this.url.buttonAction,params).then(res=>{
+            if(res.success){
+              this.loadData()
+              this.$message.success("处理完成!")
+            }else{
+              this.$message.warning("处理失败!")
+            }
+          })
+        }
+      },
+      initButtonSwitch(hideColumns){
+        if(hideColumns && hideColumns.length>0){
+          Object.keys(this.buttonSwitch).forEach(key=>{
+            if(hideColumns.indexOf(key)>=0){
+              this.buttonSwitch[key]=false
+            }
+          })
+
+        }
+      },
+
+      // 高级查询
+      handleSuperQuery(params, matchType) {
+        if (!params || params.length === 0) {
+          this.superQuery.params = ''
+        } else {
+          this.superQuery.params = JSON.stringify(params)
+        }
+        this.superQuery.matchType = matchType
+        this.loadData()
+      },
+
+    }
+  }
+</script>
+<style>
+  .ant-card-body .table-operator{
+    margin-bottom: 18px;
+  }
+  .ant-table-tbody .ant-table-row td{
+    padding-top:15px;
+    padding-bottom:15px;
+  }
+  .anty-row-operator button{margin: 0 5px}
+  .ant-btn-danger{background-color: #ffffff}
+
+  .anty-img-wrap{height:25px;position: relative;}
+  .anty-img-wrap > img{max-height:100%;}
+  .ant-modal-cust-warp{height: 100%}
+  .ant-modal-cust-warp .ant-modal-body{height:calc(100% - 110px) !important;overflow-y: auto}
+  .ant-modal-cust-warp .ant-modal-content{height:90% !important;overflow-y: hidden}
+</style>

+ 713 - 0
src/views/modules/online/cgform/auto/OnlCgformTreeList.vue

@@ -0,0 +1,713 @@
+<template>
+  <a-card :bordered="false" style="height: 100%">
+    <!-- 操作按钮区域 -->
+    <div class="table-operator">
+      <a-button v-if="buttonSwitch.add" @click="handleAdd" type="primary" icon="plus">新增</a-button>
+      <a-button v-if="buttonSwitch.import" @click="handleImportXls" type="primary" icon="upload" style="margin-left:8px">导入</a-button>
+      <a-button v-if="buttonSwitch.export" @click="handleExportXls" type="primary" icon="download" style="margin-left:8px">导出</a-button>
+      <template v-if="cgButtonList && cgButtonList.length>0" v-for="(item,index) in cgButtonList">
+        <a-button
+          v-if=" item.optType=='js' "
+          :key=" 'cgbtn'+index "
+          @click="cgButtonJsHandler(item.buttonCode)"
+          type="primary"
+          :icon="item.buttonIcon"
+          style="margin-left:8px">
+          {{ item.buttonName }}
+        </a-button>
+        <a-button
+          v-else-if=" item.optType=='action' "
+          :key=" 'cgbtn'+index "
+          @click="cgButtonActionHandler(item.buttonCode)"
+          type="primary"
+          :icon="item.buttonIcon"
+          style="margin-left:8px">
+          {{ item.buttonName }}
+        </a-button>
+      </template>
+
+      <a-button
+        v-if="buttonSwitch.batch_delete"
+        @click="handleDelBatch"
+        style="margin-left:8px"
+        v-show="selectedRowKeys.length > 0"
+        ghost
+        type="primary"
+        icon="delete">批量删除</a-button>
+    </div>
+
+    <div>
+      <div class="ant-alert ant-alert-info" style="margin-bottom: 16px;">
+        <i class="anticon anticon-info-circle ant-alert-icon"></i>
+        已选择&nbsp;<a style="font-weight: 600">{{ selectedRowKeys.length }}</a>项&nbsp;&nbsp;
+        <a style="margin-left: 24px" @click="onClearSelected">清空</a>
+      </div>
+
+      <a-table
+        ref="cgformTreeList"
+        size="middle"
+        rowKey="id"
+        :columns="columns"
+        :dataSource="dataSource"
+        :pagination="pagination"
+        :loading="loading"
+        @change="handleTableChange"
+        v-bind="tableProps"
+        @expand="handleExpand"
+        style="min-height: 300px"
+        :expandedRowKeys="expandedRowKeys">
+
+        <template slot="dateSlot" slot-scope="text">
+          <span>{{ getDateNoTime(text) }}</span>
+        </template>
+
+        <template slot="htmlSlot" slot-scope="text">
+          <div v-html="text"></div>
+        </template>
+
+        <template slot="imgSlot" slot-scope="text">
+          <span v-if="!text" style="font-size: 12px;font-style: italic;">无图片</span>
+          <img v-else :src="getImgView(text)" height="25px" alt="图片不存在" style="max-width:80px;font-size: 12px;font-style: italic;"/>
+        </template>
+
+        <template slot="fileSlot" slot-scope="text">
+          <span v-if="!text" style="font-size: 12px;font-style: italic;">无文件</span>
+          <a-button
+            v-else
+            :ghost="true"
+            type="primary"
+            icon="download"
+            size="small"
+            @click="downloadRowFile(text)">
+            下载
+          </a-button>
+        </template>
+
+        <span slot="action" slot-scope="text, record">
+
+          <template v-if="showOptButton('update',record)">
+            <a @click="handleEdit(record)">编辑</a>
+            <a-divider type="vertical"/>
+          </template>
+
+          <a-dropdown>
+            <a class="ant-dropdown-link">
+              更多 <a-icon type="down" />
+            </a>
+            <a-menu slot="overlay">
+              <a-menu-item >
+                <a @click="handleDetail(record)">详情</a>
+              </a-menu-item>
+
+              <a-menu-item v-if="showSubmitFlowButton(record)">
+                <a @click="startProcess(record)">提交流程</a>
+              </a-menu-item>
+
+              <template v-if="showViewFlowButton(record)">
+                  <a-menu-item @click="handlePreviewPic(record)">审批进度</a-menu-item>
+              </template>
+
+              <a-menu-item v-if="showOptButton('delete',record)">
+                <a-popconfirm title="确定删除吗?" @confirm="() => handleDeleteOne(record)">
+                  <a>删除</a>
+                </a-popconfirm>
+              </a-menu-item>
+              <!-- 自定义按钮 -->
+              <template v-if="cgButtonLinkList && cgButtonLinkList.length>0" v-for="(btnItem,btnIndex) in cgButtonLinkList">
+                <a-menu-item :key=" 'cgbtnLink'+btnIndex ">
+                  <a href="javascript:void(0);" @click="cgButtonLinkHandler(record,btnItem.buttonCode,btnItem.optType)">
+                    <a-icon v-if="btnItem.buttonIcon" :type="btnItem.buttonIcon" />
+                    {{ btnItem.buttonName }}
+                  </a>
+                </a-menu-item>
+              </template>
+
+            </a-menu>
+          </a-dropdown>
+        </span>
+
+      </a-table>
+
+      <onl-cgform-auto-modal @success="handleFormSuccess" ref="modal" :code="code"></onl-cgform-auto-modal>
+
+      <j-import-modal ref="importModal" :url="getImportUrl()" @ok="importOk"></j-import-modal>
+
+    </div>
+  </a-card>
+</template>
+
+<script>
+
+  import { getAction,postAction,deleteAction,downFile } from '@/api/manage'
+  import { filterMultiDictText } from '@/components/dict/JDictSelectUtil'
+  import { filterObj } from '@/utils/util';
+  import JImportModal from '@/components/jeecg/JImportModal'
+
+  export default {
+    name: 'OnlCgformTreeList',
+    components: {
+      JImportModal
+    },
+    data() {
+      return {
+        code: '87b55a515d3441b6b98e48e5b35474a6',
+        description: '在线报表功能测试页面',
+        currentTableName:"",
+        pidField:"",
+        hasChildrenField:"",
+        textField:'',
+        loading: false,
+        // 表头
+        columns: [],
+        //数据集
+        dataSource: [],
+        // 选择器
+        selectedRowKeys: [],
+        selectionRows: [],
+        // 分页参数
+        pagination: {
+          current: 1,
+          pageSize: 10,
+          pageSizeOptions: ['10', '20', '30'],
+          showTotal: (total, range) => {
+            return range[0] + '-' + range[1] + ' 共' + total + '条'
+          },
+          showQuickJumper: true,
+          showSizeChanger: true,
+          total: 0
+        },
+
+        url: {
+          getColumns: '/online/cgform/api/getColumns/',
+          getTreeData: '/online/cgform/api/getTreeData/',
+          optPre:"/online/cgform/api/form/",
+          exportXls:'/online/cgform/api/exportXls/',
+          buttonAction:'/online/cgform/api/doButton',
+          startProcess: "/process/extActProcess/startMutilProcess"
+        },
+        isorter:{
+          column: 'create_time',
+          order: 'desc',
+        },
+        dictOptions:{
+
+        },
+
+        queryParam:{
+
+        },
+        actionColumn:{
+          title: '操作',
+          dataIndex: 'action',
+          scopedSlots: { customRender: 'action' },
+          fixed:"right",
+          align:"center",
+          width:150
+        },
+        formTemplate:"99",
+
+        /*自定义按钮-link*/
+        cgButtonLinkList:[],
+        /*自定义按钮-button*/
+        cgButtonList:[],
+        /*JS增强*/
+        EnhanceJS:'',
+        /*操作按钮权限*/
+        buttonSwitch:{
+          add:true,
+          update:true,
+          delete:true,
+          batch_delete:true,
+          import:true,
+          export:true
+        },
+        expandedRowKeys:[],
+        hasBpmStatus:false,
+        flowCodePre:"onl_",
+
+      }
+    },
+    created() {
+      this.initAutoListConfig().then(()=>{
+        this.loadData(1)
+      }).catch(msg=>{
+        console.log(msg)
+      })
+    },
+    mounted(){
+      //this.cgButtonJsHandler('mounted')
+    },
+    watch: {
+      '$route'() {
+        // 刷新参数放到这里去触发,就可以刷新相同界面了
+        this.initAutoListConfig().then(()=>{
+          this.loadData(1)
+        }).catch(msg=>{
+          console.log(msg)
+        })
+      }
+    },
+    computed: {
+      tableProps() {
+        let _this = this
+        return {
+          // 列表项是否可选择
+          // https://vue.ant.design/components/table-cn/#rowSelection
+          rowSelection: {
+            selectedRowKeys: _this.selectedRowKeys,
+            onChange: (selectedRowKeys) => _this.selectedRowKeys = selectedRowKeys
+          }
+        }
+      }
+    },
+    methods: {
+      resetData(){
+        this.description=''
+        this.currentTableName=''
+        this.pidField=''
+        this.hasChildrenField=''
+        this.textField=''
+        this.columns = []
+        this.dataSource = []
+        this.selectedRowKeys=[]
+        this.selectionRows=[]
+      },
+      initAutoListConfig() {
+        return new Promise((resolve, reject) => {
+          if (!this.$route.params.code) {
+            reject("列表加载需要参数CODE为空!")
+          } else {
+            this.resetData()
+            this.loading = true
+            this.code = this.$route.params.code
+            getAction(`${this.url.getColumns}${this.code}`)
+              .then(res => {
+                console.log("--onlineList-加载动态列>>", res);
+                if(res.success){
+                  this.configInfohandler(res)
+                  resolve();
+                }else{
+                  reject("onlineList-加载表配置信息失败")
+                }
+                this.loading = false
+              })
+              .catch(err => {
+                reject(err)
+              })
+          }
+        })
+      },
+      configInfohandler(res){
+        this.dictOptions = res.result.dictOptions
+        this.formTemplate = res.result.formTemplate
+        this.description = res.result.description
+        this.currentTableName = res.result.currentTableName
+        this.pidField = res.result.pidField
+        this.hasChildrenField = res.result.hasChildrenField
+        this.textField = res.result.textField
+        //自定义按钮
+        this.initCgButtonList(res.result.cgButtonList)
+        //JS增强
+        this.initCgEnhanceJs(res.result.enhanceJs)
+        //操作按钮权限
+        this.initButtonSwitch(res.result.hideColumns)
+        let currColumns = res.result.columns
+        let textFieldIndex = -1
+        let hasBpmStatus = false
+        for(let a=0;a<currColumns.length;a++){
+          currColumns[a].align = 'left'
+          //找到显示列
+          if(this.textField==currColumns[a].dataIndex){
+            textFieldIndex = a
+          }
+          //数据字典翻译
+          if(currColumns[a].customRender){
+            let dictCode = currColumns[a].customRender;
+            currColumns[a].customRender=(text)=>{
+              return filterMultiDictText(this.dictOptions[dictCode], text);
+            }
+          }
+          //判断是否有bpm_status
+          if(currColumns[a].dataIndex.toLowerCase()=='bpm_status'){
+            hasBpmStatus = true;
+          }
+        }
+        this.hasBpmStatus = hasBpmStatus;
+
+        if(textFieldIndex!=-1){
+          let textFieldColumn = currColumns.splice(textFieldIndex,1)
+          currColumns.unshift(textFieldColumn[0])
+        }
+        currColumns.push(this.actionColumn);
+        this.columns = [...currColumns]
+      },
+      //加载根节点
+      loadData(arg){
+        if(arg==1){
+          this.pagination.current=1
+        }
+        this.loading = true
+        this.expandedRowKeys=[]
+        let params = this.getQueryParams();//查询条件
+        params[this.pidField]='0'
+        console.log("--onlineList-查询条件-->",params)
+        getAction(`${this.url.getTreeData}${this.code}`,params).then((res)=>{
+          console.log("--onlineList-列表数据",res)
+          if(res.success){
+            let result = res.result;
+            if(Number(result.total)>0){
+              this.pagination.total = Number(result.total)
+              let dataSource = res.result.records.map(item => {
+                // 判断是否标记了带有子级
+                if (item[this.hasChildrenField] === true || item[this.hasChildrenField]=='1') {
+                  let loadChild = { id: `${item.id}_loadChild`, name: 'loading...', isLoading: true }
+                  item.children = [loadChild]
+                }
+                return item
+              })
+              this.dataSource = dataSource
+            }else{
+              this.pagination.total=0;
+              this.dataSource=[]
+            }
+          }else{
+            this.$message.warning(res.message)
+          }
+          this.loading = false
+        })
+      },
+      //加载叶子节点
+      handleExpand(expanded, record) {
+        // 判断是否是展开状态
+        if (expanded) {
+          this.expandedRowKeys.push(record.id)
+          if (record.children.length>0 && record.children[0].isLoading === true) {
+            let params = this.getQueryParams();//查询条件
+            params[this.pidField] = record.id
+            getAction(`${this.url.getTreeData}${this.code}`,params).then((res)=>{
+              if(res.success){
+                if(Number(res.result.total)>0){
+                  let dataSource = res.result.records.map(item => {
+                    // 判断是否标记了带有子级
+                    if (item[this.hasChildrenField] === true || item[this.hasChildrenField]=='1') {
+                      let loadChild = { id: `${item.id}_loadChild`, name: 'loading...', isLoading: true }
+                      item.children = [loadChild]
+                    }
+                    return item
+                  })
+                  record.children = dataSource
+                }else{
+                  record.children=''
+                  record.hasChildrenField='0'
+                }
+              }else{
+                this.$message.warning(res.message)
+              }
+            })
+          }
+        }else{
+          let keyIndex = this.expandedRowKeys.indexOf(record.id)
+          if(keyIndex>=0){
+            this.expandedRowKeys.splice(keyIndex, 1);
+          }
+        }
+      },
+      getQueryParams() {
+        let param = Object.assign({}, this.queryParam,this.isorter);
+        param.pageNo = this.pagination.current;
+        param.pageSize = this.pagination.pageSize;
+        return filterObj(param);
+      },
+      initCgButtonList(btnList){
+        let linkArr = []
+        let buttonArr = []
+        if(btnList && btnList.length>0){
+          for(let i=0;i<btnList.length;i++){
+            let temp = btnList[i]
+            if(temp.buttonStyle=='button'){
+              buttonArr.push(temp)
+            }else if(temp.buttonStyle=='link'){
+              linkArr.push(temp)
+            }
+          }
+        }
+        this.cgButtonLinkList = [...linkArr]
+        this.cgButtonList=[...buttonArr]
+      },
+      initCgEnhanceJs(enhanceJs){
+        //console.log("--onlineList-js增强",enhanceJs)
+        if(enhanceJs){
+          let Obj = eval ("(" + enhanceJs + ")");
+          this.EnhanceJS = new Obj(getAction,postAction,deleteAction);
+          this.cgButtonJsHandler('created')
+        }else{
+          this.EnhanceJS = ''
+        }
+      },
+      initButtonSwitch(hideColumns){
+        if(hideColumns && hideColumns.length>0){
+          Object.keys(this.buttonSwitch).forEach(key=>{
+            if(hideColumns.indexOf(key)>=0){
+              this.buttonSwitch[key]=false
+            }
+          })
+
+        }
+      },
+      onClearSelected(){
+        this.selectedRowKeys = []
+        this.selectionRows = []
+      },
+      handleTableChange(pagination, filters, sorter){
+        //TODO 筛选
+        if (Object.keys(sorter).length>0){
+          this.isorter.column = sorter.field;
+          this.isorter.order = "ascend"==sorter.order?"asc":"desc"
+        }
+        this.pagination = pagination;
+        this.loadData();
+      },
+      /*-------数据格式化-begin----------*/
+      getDateNoTime(text){
+        if(!text){
+          return ''
+        }
+        let a = text;
+        if(a.length>10){
+          a = a.substring(0,10);
+        }
+        return a;
+      },
+      getImgView(text){
+        if(text && text.indexOf(",")>0){
+          text = text.substring(0,text.indexOf(","))
+        }
+        return window._CONFIG['imgDomainURL']+"/"+text
+      },
+      downloadRowFile(text){
+        if(!text){
+          this.$message.warning("未知的文件")
+          return;
+        }
+        if(text.indexOf(",")>0){
+          text = text.substring(0,text.indexOf(","))
+        }
+        window.open(window._CONFIG['downloadUrl']+"/"+text);
+      },
+      /*-------数据格式化-end----------*/
+
+      /*-------功能按钮触发事件-begin----------*/
+      handleEdit(record){
+        this.cgButtonLinkHandler(record,"beforeEdit","js")
+        this.$refs.modal.edit(this.formTemplate,record.id);
+      },
+      handleDetail(record){
+        this.$refs.modal.detail(this.formTemplate,record.id);
+      },
+      handleDeleteOne(record){
+        this.cgButtonLinkHandler(record,"beforeDelete","js")
+        this.handleDelete(record.id)
+      },
+      handleDelete(id){
+        deleteAction(this.url.optPre+this.code+"/"+id).then((res)=>{
+          if(res.success){
+            this.$message.success(res.message)
+            this.loadData()
+          }else{
+            this.$message.warning(res.message)
+          }
+        })
+      },
+      handleAdd(){
+        this.cgButtonJsHandler('beforeAdd')
+        this.$refs.modal.add(this.formTemplate);
+      },
+      handleFormSuccess(){
+        this.loadData()
+      },
+      handleImportXls(){
+        this.$refs.importModal.show()
+      },
+      importOk(){
+        this.loadData(1)
+      },
+      getImportUrl(){
+        return '/online/cgform/api/importXls/'+this.code
+      },
+      handleExportXls(){
+        let param = this.queryParam;
+        if(this.selectedRowKeys && this.selectedRowKeys.length>0){
+          param['selections'] = this.selectedRowKeys.join(",")
+        }
+        console.log("导出参数",param)
+        let paramsStr = JSON.stringify(filterObj(param));
+        downFile(this.url.exportXls+this.code,{paramsStr:paramsStr}).then((data)=>{
+          if (!data) {
+            this.$message.warning("文件下载失败")
+            return
+          }
+          if (typeof window.navigator.msSaveBlob !== 'undefined') {
+            window.navigator.msSaveBlob(new Blob([data]), this.description+'.xls')
+          }else{
+            let url = window.URL.createObjectURL(new Blob([data]))
+            let link = document.createElement('a')
+            link.style.display = 'none'
+            link.href = url
+            link.setAttribute('download', this.description+'.xls')
+            document.body.appendChild(link)
+            link.click()
+            document.body.removeChild(link); //下载完成移除元素
+            window.URL.revokeObjectURL(url); //释放掉blob对象
+          }
+        })
+      },
+      handleDelBatch(){
+        if(this.selectedRowKeys.length<=0){
+          this.$message.warning('请选择一条记录!');
+          return false;
+        }else{
+          let ids = "";
+          let that = this;
+          that.selectedRowKeys.forEach(function(val) {
+            ids+=val+",";
+          });
+          that.$confirm({
+            title:"确认删除",
+            content:"是否删除选中数据?",
+            onOk: function(){
+              that.handleDelete(ids)
+              that.onClearSelected();
+            }
+          });
+        }
+      },
+      /*-------功能按钮触发事件-begin----------*/
+
+      /*-------JS增强-begin----------*/
+      cgButtonLinkHandler(record,buttonCode,optType){
+        if(optType=="js"){
+          if(this.EnhanceJS[buttonCode]){
+            this.EnhanceJS[buttonCode](this,record)
+          }
+        }else if(optType=="action"){
+          let params = {
+            formId:this.code,
+            buttonCode:buttonCode,
+            dataId:record.id
+          }
+          console.log("自定义按钮link请求后台参数:",params)
+          postAction(this.url.buttonAction,params).then(res=>{
+            if(res.success){
+              this.loadData()
+              this.$message.success("处理完成!")
+            }else{
+              this.$message.warning("处理失败!")
+            }
+          })
+        }
+      },
+      cgButtonJsHandler(buttonCode){
+        if(this.EnhanceJS[buttonCode]){
+          this.EnhanceJS[buttonCode](this)
+        }
+      },
+      cgButtonActionHandler(buttonCode){
+        //处理自定义button的 需要配置该button自定义sql
+        if(!this.selectedRowKeys || this.selectedRowKeys.length==0){
+          this.$message.warning("请先选中一条记录")
+          return false
+        }
+        if(this.selectedRowKeys.length>1){
+          this.$message.warning("请只选中一条记录")
+          return false
+        }
+        let params = {
+          formId:this.code,
+          buttonCode:buttonCode,
+          dataId:this.selectedRowKeys[0]
+        }
+        console.log("自定义按钮请求后台参数:",params)
+        postAction(this.url.buttonAction,params).then(res=>{
+          if(res.success){
+            this.loadData()
+            this.$message.success("处理完成!")
+          }else{
+            this.$message.warning("处理失败!")
+          }
+        })
+
+      },
+      /*-------JS增强-end----------*/
+      showOptButton(opt,record){
+        //只有当按钮属性为false,或是按钮属性为true但是流程已提交时才隐藏
+        if(!this.buttonSwitch[opt]){
+          return false
+        }else{
+          if(this.hasBpmStatus){
+            if(record.bpm_status !=null && record.bpm_status !='' && record.bpm_status != '1'){
+              return false
+            }
+          }
+        }
+        return true
+      },
+      showSubmitFlowButton(record){
+        if(this.hasBpmStatus){
+          if(record.bpm_status ==null || record.bpm_status =='' || record.bpm_status == '1'){
+            return true
+          }
+        }
+        return false
+      },
+      showViewFlowButton(record){
+        if(this.hasBpmStatus){
+          if(record.bpm_status !=null && record.bpm_status !='' && record.bpm_status != '1'){
+            return true
+          }
+        }
+        return false
+      },
+      startProcess: function(record){
+        var that = this;
+        this.$confirm({
+          title:"提示",
+          content:"确认提交流程吗?",
+          onOk: function(){
+            var param = {
+              flowCode:that.flowCodePre+that.currentTableName,
+              id:record.id,
+              formUrl:"modules/bpm/task/form/OnlineFormDetail",
+              formUrlMobile:"modules/bpm/task/form/OnlineFormDetail"
+            }
+            postAction(that.url.startProcess,param).then((res)=>{
+              if(res.success){
+                that.$message.success(res.message);
+                that.loadData();
+                that.onClearSelected();
+              }else{
+                that.$message.warning(res.message);
+              }
+            });
+          }
+        });
+      },
+    }
+  }
+</script>
+<style>
+  .ant-card-body .table-operator{
+    margin-bottom: 18px;
+  }
+  .ant-table-tbody .ant-table-row td{
+    padding-top:15px;
+    padding-bottom:15px;
+  }
+  .anty-row-operator button{margin: 0 5px}
+  .ant-btn-danger{background-color: #ffffff}
+
+  .anty-img-wrap{height:25px;position: relative;}
+  .anty-img-wrap > img{max-height:100%;}
+  .ant-modal-cust-warp{height: 100%}
+  .ant-modal-cust-warp .ant-modal-body{height:calc(100% - 110px) !important;overflow-y: auto}
+  .ant-modal-cust-warp .ant-modal-content{height:90% !important;overflow-y: hidden}
+</style>

+ 268 - 0
src/views/modules/online/cgform/util/TableUtils.js

@@ -0,0 +1,268 @@
+/**
+ * 同步列表,可以同步新增、修改、删除
+ * @author sunjianlei
+ * */
+export function syncAllTable(vm, table1) {
+  vm.$refs.editableTable.resetScrollTop()
+  let deleteIds = table1.$refs.editableTable.getDeleteIds()
+  let table1Value
+  table1.$refs.editableTable.getValuesPromise(false).then((values) => {
+    table1Value = values
+    return vm.$refs.editableTable.getValuesPromise(false)
+  }).then((values) => {
+
+    table1Value.forEach(value => {
+      let flag = false
+      values.forEach((thisValue) => {
+        if (value.id === thisValue.id) {
+
+          // 判断是否修改了值
+          let dbFieldName = thisValue['dbFieldName']
+          let dbFieldTxt = thisValue['dbFieldTxt']
+
+          // return
+
+          if (value.dbFieldName !== dbFieldName
+            || value.dbFieldTxt !== dbFieldTxt) {
+
+            // 修改了
+            vm.$refs.editableTable.setValues([{
+              rowKey: thisValue.id,
+              values: {
+                dbFieldName: value.dbFieldName,
+                dbFieldTxt: value.dbFieldTxt
+              }
+            }])
+
+          }
+          flag = true
+        } else {
+          // id不匹配则有可能是新增也有可能是删除了的
+          // 遍历传进来的 deleteIds 进行对比
+          deleteIds.forEach(delId => {
+            // 对比成功,则删除该条数据
+            if (delId === thisValue.id) {
+              vm.$refs.editableTable.removeRows(vm.$refs.editableTable.caseId + delId)
+              flag = true
+            }
+          })
+        }
+      })
+      // return
+      // 判断是否操作了该条数据,若没有操作则代表要执行新增操作
+      if (!flag) {
+        let record = Object.assign({}, value)
+        vm.columns.forEach(column => {
+          if (
+            column.dataIndex !== 'dbFieldName' &&
+            column.dataIndex !== 'dbFieldTxt'
+          ) {
+            record[column.dataIndex] = column.defaultValue
+          }
+        })
+        vm.$refs.editableTable.push(record)
+      }
+    })
+  })
+
+}
+
+/**
+ * 将数据分类并Set进dataSource
+ * @author sunjianlei
+ **/
+export function setDataSource(vm, queryData) {
+  let dataSource = []
+  // 遍历查询出来的数据
+  queryData.forEach(value => {
+
+    let data = { id: value['id'] }
+    vm.columns.forEach(column => {
+      let key = column.key
+      if (key) {
+        data[key] = value[key]
+
+        // 由于多选下拉框返回的是一个数组,所以需要改成 [1,2,3] 数组的形式,否则组件不识别
+        // if (key === 'indexField') {
+        //   data[key] = value[key].split(',')
+        // }
+
+      }
+    })
+    dataSource.push(data)
+  })
+  vm.dataSource = dataSource
+}
+
+/** 获取主表的初始化数据 */
+export function getMasterTableInitialData() {
+  return [
+    {
+      dbFieldName: 'id',
+      dbFieldTxt: '主键',
+      dbLength: 36,
+      dbPointLength: 0,
+      dbDefaultVal: '',
+      dbType: 'string',
+      dbIsKey: '1',
+      dbIsNull: '0',
+      // table2
+      isShowForm: '0',
+      isShowList: '0',
+      fieldShowType: 'text',
+      fieldLength: '120',
+      queryMode: 'single',
+      orderNum: 1
+    },
+    {
+      dbFieldName: 'create_by',
+      dbFieldTxt: '创建人',
+      dbLength: 50,
+      dbPointLength: 0,
+      dbDefaultVal: '',
+      dbType: 'string',
+      dbIsKey: '0',
+      dbIsNull: '1',
+      // table2
+      isShowForm: '0',
+      isShowList: '0',
+      fieldShowType: 'text',
+      fieldLength: '120',
+      queryMode: 'single',
+      orderNum: 2
+    },
+    {
+      dbFieldName: 'create_time',
+      dbFieldTxt: '创建日期',
+      dbLength: 20,
+      dbPointLength: 0,
+      dbDefaultVal: '',
+      dbType: 'Date',
+      dbIsKey: '0',
+      dbIsNull: '1',
+      // table2
+      isShowForm: '0',
+      isShowList: '0',
+      fieldShowType: 'datetime',
+      fieldLength: '120',
+      queryMode: 'single',
+      orderNum: 3
+    },
+    {
+      dbFieldName: 'update_by',
+      dbFieldTxt: '更新人',
+      dbLength: 50,
+      dbPointLength: 0,
+      dbDefaultVal: '',
+      dbType: 'string',
+      dbIsKey: '0',
+      dbIsNull: '1',
+      // table2
+      isShowForm: '0',
+      isShowList: '0',
+      fieldShowType: 'text',
+      fieldLength: '120',
+      queryMode: 'single',
+      orderNum: 4
+    },
+    {
+      dbFieldName: 'update_time',
+      dbFieldTxt: '更新日期',
+      dbLength: 20,
+      dbPointLength: 0,
+      dbDefaultVal: '',
+      dbType: 'Date',
+      dbIsKey: '0',
+      dbIsNull: '1',
+      // table2
+      isShowForm: '0',
+      isShowList: '0',
+      fieldShowType: 'datetime',
+      fieldLength: '120',
+      queryMode: 'single',
+      orderNum: 5
+    },{
+      dbFieldName: 'sys_org_code',
+      dbFieldTxt: '所属部门',
+      dbLength: 64,
+      dbPointLength: 0,
+      dbDefaultVal: '',
+      dbType: 'string',
+      dbIsKey: '0',
+      dbIsNull: '1',
+      // table2
+      isShowForm: '0',
+      isShowList: '0',
+      fieldShowType: 'text',
+      fieldLength: '120',
+      queryMode: 'single',
+      orderNum: 6
+    }
+    // {
+    //   dbFieldName: 'sys_org_code',
+    //   dbFieldTxt: '所属部门',
+    //   dbLength: 50,
+    //   dbPointLength: 0,
+    //   dbDefaultVal: '',
+    //   dbType: 'string',
+    //   dbIsKey: false,
+    //   dbIsNull: true
+    // }, {
+    //   dbFieldName: 'sys_company_code',
+    //   dbFieldTxt: '所属公司',
+    //   dbLength: 50,
+    //   dbPointLength: 0,
+    //   dbDefaultVal: '',
+    //   dbType: 'string',
+    //   dbIsKey: false,
+    //   dbIsNull: true
+    // }, {
+    //   dbFieldName: 'bpm_status',
+    //   dbFieldTxt: '流程状态',
+    //   dbLength: 32,
+    //   dbPointLength: 0,
+    //   dbDefaultVal: '',
+    //   dbType: 'string',
+    //   dbIsKey: false,
+    //   dbIsNull: true
+    // }
+  ]
+}
+/** 获取树的初始化数据 */
+export function getTreeNeedFields() {
+  return [{
+    dbFieldName: 'pid',
+    dbFieldTxt: '父级节点',
+    dbLength: 32,
+    dbPointLength: 0,
+    dbDefaultVal: '',
+    dbType: 'string',
+    dbIsKey: '0',
+    dbIsNull: '1',
+    // table2
+    isShowForm: '1',
+    isShowList: '0',
+    fieldShowType: 'text',
+    fieldLength: '120',
+    queryMode: 'single',
+    orderNum: 7
+  },{
+    dbFieldName: 'has_child',
+    dbFieldTxt: '是否有子节点',
+    dbLength: 3,
+    dbPointLength: 0,
+    dbDefaultVal: '',
+    dbType: 'string',
+    dbIsKey: '0',
+    dbIsNull: '1',
+    // table2
+    isShowForm: '0',
+    isShowList: '0',
+    fieldShowType: 'list',
+    fieldLength: '120',
+    queryMode: 'single',
+    orderNum: 8,
+    // table3
+    dictField:"yn"
+  }]
+}

+ 256 - 0
src/views/modules/online/cgreport/OnlCgreportHeadList.vue

@@ -0,0 +1,256 @@
+<template>
+  <a-card :bordered="false">
+
+    <!-- 查询区域 -->
+    <div class="table-page-search-wrapper">
+      <a-form layout="inline">
+        <a-row :gutter="24">
+
+          <a-col :span="6">
+            <a-form-item label="报表编码">
+              <a-input placeholder="请输入报表编码" v-model="queryParam.code"></a-input>
+            </a-form-item>
+          </a-col>
+          <a-col :span="6">
+            <a-form-item label="报表名字">
+              <a-input placeholder="请输入报表名字" v-model="queryParam.name"></a-input>
+            </a-form-item>
+          </a-col>
+
+          <a-col :span="8">
+            <span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
+              <a-button type="primary" @click="searchQuery" icon="search">查询</a-button>
+              <a-button type="primary" @click="searchReset" icon="reload" style="margin-left: 8px">重置</a-button>
+            </span>
+          </a-col>
+
+        </a-row>
+      </a-form>
+    </div>
+
+    <!-- 操作按钮区域 -->
+    <div class="table-operator">
+      <a-button @click="handleAdd" type="primary" icon="plus">录入</a-button>
+
+      <a-dropdown v-if="selectedRowKeys.length > 0">
+        <a-menu slot="overlay">
+          <a-menu-item key="1" @click="batchDel">
+            <a-icon type="delete"/>
+            删除
+          </a-menu-item>
+        </a-menu>
+        <a-button style="margin-left: 8px"> 批量操作
+          <a-icon type="down"/>
+        </a-button>
+      </a-dropdown>
+    </div>
+
+    <!-- table区域-begin -->
+    <div>
+      <div class="ant-alert ant-alert-info" style="margin-bottom: 16px;">
+        <i class="anticon anticon-info-circle ant-alert-icon"></i>
+        已选择
+        <a style="font-weight: 600">
+          {{ selectedRowKeys.length }}
+        </a>
+        项
+        <a style="margin-left: 24px" @click="onClearSelected">清空</a>
+      </div>
+
+      <a-table
+        ref="table"
+        size="middle"
+        bordered
+        rowKey="id"
+        :columns="columns"
+        :dataSource="dataSource"
+        :pagination="ipagination"
+        :loading="loading"
+        :rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
+        @change="handleTableChange">
+
+        <span slot="action" slot-scope="text, record">
+          <a @click="handleEdit(record)">编辑</a>
+
+          <a-divider type="vertical"/>
+          <a-dropdown>
+            <a class="ant-dropdown-link">更多 <a-icon type="down"/></a>
+            <a-menu slot="overlay">
+              <a-menu-item @click="popReportURL(record.id)">
+                配置地址
+              </a-menu-item>
+              <a-menu-item>
+                <a @click="goPageOnline(record.id)">功能测试</a>
+              </a-menu-item>
+              <a-menu-item>
+                <a-popconfirm title="确定删除吗?" @confirm="() => handleDelete(record.id)">
+                  <a>删除</a>
+                </a-popconfirm>
+              </a-menu-item>
+            </a-menu>
+          </a-dropdown>
+        </span>
+
+      </a-table>
+    </div>
+    <!-- table区域-end -->
+
+    <!-- 表单区域 -->
+    <onlCgreportHead-modal ref="modalForm" @ok="modalFormOk"></onlCgreportHead-modal>
+
+    <!-- 提示online报表链接 -->
+    <a-modal
+      title="报表访问链接"
+      :visible="visible"
+      @cancel="handleCancel">
+      <template slot="footer">
+        <a-button @click="handleCancel">关闭</a-button>
+        <a-button type="primary" class="copy-this-text" :data-clipboard-text="reportUrlText" @click="onCopyUrl">复制</a-button>
+      </template>
+      <p>{{ reportUrlText }}</p>
+    </a-modal>
+  </a-card>
+</template>
+
+<script>
+  import {JeecgListMixin} from '@/mixins/JeecgListMixin'
+  import Clipboard from 'clipboard'
+  import { getAction } from '@/api/manage'
+
+  export default {
+    name: 'OnlCgreportHeadList',
+    mixins: [JeecgListMixin],
+    components: {
+      Clipboard
+    },
+    data() {
+      return {
+        description: '在线报表配置管理页面',
+        visible:false,
+        reportUrlText:'',
+        // 表头
+        columns: [
+          {
+            title: '报表名称',
+            align: 'center',
+            dataIndex: 'name'
+          },
+          {
+            title: '编码',
+            align: 'center',
+            dataIndex: 'code'
+          },
+          {
+            title: '查询SQL',
+            align: 'center',
+            dataIndex: 'cgrSql'
+          },
+          {
+            title: '数据源',
+            align: 'center',
+            dataIndex: 'dbSource'
+          },
+          {
+            title: '创建时间',
+            align: 'center',
+            dataIndex: 'createTime'
+          },
+          {
+            title: '描述',
+            align: 'center',
+            dataIndex: 'content'
+          },
+          {
+            title: '操作',
+            dataIndex: 'action',
+            align: 'center',
+            scopedSlots: { customRender: 'action' }
+          }
+        ],
+        url: {
+          list: '/online/cgreport/head/list',
+          delete: '/online/cgreport/head/delete',
+          deleteBatch: '/online/cgreport/head/deleteBatch',
+          getParamsInfo:'/online/cgreport/api/getParamsInfo/'
+        }
+      }
+    },
+    methods: {
+      initReportUrlText(id){
+        getAction(this.url.getParamsInfo+id).then((res) => {
+          let textUrl = ""
+          if (res.success) {
+            if(res.result && res.result.length>0){
+              for(let i of res.result){
+                textUrl+=i.paramName+"=${"+i.paramName+"}&"
+              }
+            }
+          } else {
+            this.$message.warning(res.message)
+          }
+          if(textUrl.length>0){
+            textUrl = textUrl.substring(0,textUrl.length-1)
+            this.reportUrlText = `/online/cgreport/${id}?${textUrl}`
+          }else{
+            this.reportUrlText = `/online/cgreport/${id}`
+          }
+        })
+      },
+      goPageOnline(id){
+        this.$router.push({path: '/online/cgreport/'+id})
+      },
+      popReportURL(id){
+        this.visible = true;
+        this.initReportUrlText(id)
+      },
+      handleCancel(){
+        this.visible = false
+        this.reportUrlText = '';
+      },
+      onCopyUrl(){
+        var clipboard = new Clipboard('.copy-this-text')
+        clipboard.on('success', () => {
+          clipboard.destroy()
+          this.$message.success('复制成功')
+          this.handleCancel()
+        })
+        clipboard.on('error', () => {
+          this.$message.error('该浏览器不支持自动复制')
+          clipboard.destroy()
+        })
+      }
+    }
+  }
+</script>
+<style lang="less" scoped>
+  .ant-card-body .table-operator {
+    margin-bottom: 18px;
+  }
+
+  .ant-table-tbody .ant-table-row td {
+    padding-top: 15px;
+    padding-bottom: 15px;
+  }
+
+  .anty-row-operator button {
+    margin: 0 5px
+  }
+
+  .ant-btn-danger {
+    background-color: #ffffff
+  }
+
+  .ant-modal-cust-warp {
+    height: 100%
+  }
+
+  .ant-modal-cust-warp .ant-modal-body {
+    height: calc(100% - 110px) !important;
+    overflow-y: auto
+  }
+
+  .ant-modal-cust-warp .ant-modal-content {
+    height: 90% !important;
+    overflow-y: hidden
+  }
+</style>

+ 293 - 0
src/views/modules/online/cgreport/auto/OnlCgreportAutoList.vue

@@ -0,0 +1,293 @@
+<template>
+  <a-card :bordered="false" style="height: 100%">
+
+    <div class="table-page-search-wrapper">
+      <a-form layout="inline">
+        <a-row :gutter="24" v-if="queryInfo && queryInfo.length>0">
+          <template v-for="(item,index) in queryInfo">
+            <template v-if=" item.hidden==='1' ">
+              <a-col v-if="item.view.indexOf('Date')>=0" :md="12" :sm="16" :key=" 'query'+index " v-show="toggleSearchStatus">
+                <onl-cgreport-query-form-item :queryParam="queryParam" :item="item" :dictOptions="dictOptions"></onl-cgreport-query-form-item>
+              </a-col>
+              <a-col v-else :md="6" :sm="8" :key=" 'query'+index " v-show="toggleSearchStatus">
+                <onl-cgreport-query-form-item :queryParam="queryParam" :item="item" :dictOptions="dictOptions"></onl-cgreport-query-form-item>
+              </a-col>
+            </template>
+            <template v-else>
+              <a-col v-if="item.view.indexOf('Date')>=0" :md="12" :sm="16" :key=" 'query'+index ">
+                <onl-cgreport-query-form-item :queryParam="queryParam" :item="item" :dictOptions="dictOptions"></onl-cgreport-query-form-item>
+              </a-col>
+              <a-col v-else :md="6" :sm="8" :key=" 'query'+index ">
+                <onl-cgreport-query-form-item :queryParam="queryParam" :item="item" :dictOptions="dictOptions"></onl-cgreport-query-form-item>
+              </a-col>
+            </template>
+          </template>
+
+          <a-col :md="6" :sm="8">
+            <span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
+              <a-button type="primary" @click="searchByQuery" icon="search">查询</a-button>
+              <a-button type="primary" @click="searchReset" icon="reload" style="margin-left: 8px">重置</a-button>
+              <a @click="handleToggleSearch" style="margin-left: 8px">
+                {{ toggleSearchStatus ? '收起' : '展开' }}
+                <a-icon :type="toggleSearchStatus ? 'up' : 'down'"/>
+              </a>
+            </span>
+          </a-col>
+        </a-row>
+      </a-form>
+    </div>
+
+
+    <div class="table-operator" style="margin-bottom: 10px">
+      <a-button type="primary" icon="plus" @click="exportExcel">导出</a-button>
+    </div>
+
+
+    <a-table
+      ref="table"
+      size="middle"
+      bordered
+      rowKey="id"
+      :columns="table.columns"
+      :dataSource="table.dataSource"
+      :pagination="table.pagination"
+      :loading="table.loading"
+      :scroll="table.scroll"
+      :rowSelection="{fixed:true, selectedRowKeys: table.selectedRowKeys, onChange: handleChangeInTableSelect}"
+      @change="handleChangeInTable"
+      style="min-height: 300px"
+    >
+
+    </a-table>
+
+  </a-card>
+</template>
+
+<script>
+  import { getAction,downFile } from '@/api/manage'
+  import { filterMultiDictText } from '@/components/dict/JDictSelectUtil'
+  import {filterObj} from '@/utils/util';
+
+  export default {
+    name: 'OnlCgreportAutoList',
+    components: {
+    },
+    data() {
+      return {
+        // 查询参数
+        queryInfo: [],
+        // 查询参数,多个页面的查询参数用 code 作为键来区分
+        queryParamsMap: {},
+        selfParam:{
+        },
+        sorter: {
+          column: '',
+          order: 'desc',
+        },
+        dictOptions: {},
+        toggleSearchStatus: false, // 高级搜索 展开/关闭
+        reportCode: '',
+        description: '在线报表功能测试页面',
+        url: {
+          getColumns: '/online/cgreport/api/getColumns/',
+          getData: '/online/cgreport/api/getData/',
+          getQueryInfo: '/online/cgreport/api/getQueryInfo/',
+          getParamsInfo:'/online/cgreport/api/getParamsInfo/'
+        },
+        table: {
+          loading: true,
+          // 表头
+          columns: [],
+          //数据集
+          dataSource: [],
+          // 选择器
+          selectedRowKeys: [],
+          selectionRows: [],
+          scroll: { x: false },
+          // 分页参数
+          pagination: {
+            current: 1,
+            pageSize: 10,
+            pageSizeOptions: ['10', '20', '30'],
+            showTotal: (total, range) => {
+              return range[0] + '-' + range[1] + ' 共' + total + '条'
+            },
+            showQuickJumper: true,
+            showSizeChanger: true,
+            total: 0
+          }
+        },
+        cgreportHeadName:""
+      }
+    },
+    mounted() {
+      this.initParamsInfo()
+      this.initQueryInfo();
+    },
+    watch: {
+      '$route'() {
+        // 刷新参数放到这里去触发,就可以刷新相同界面了
+        this.initParamsInfo()
+        this.initQueryInfo();
+      }
+    },
+    computed: {
+      queryParam: {
+        get() {
+          return this.queryParamsMap[this.reportCode]
+        },
+        set(newVal) {
+          this.$set(this.queryParamsMap, this.reportCode, newVal)
+        }
+      }
+    },
+    methods: {
+      initParamsInfo(){
+        if(!this.$route.params.code){
+          return false
+        }
+        //获取报表ID
+        this.reportCode = this.$route.params.code;
+        if (!this.queryParam) {
+          this.queryParam = {}
+        }
+
+        this.selfParam={}
+        getAction(`${this.url.getParamsInfo}${this.$route.params.code}`).then((res) => {
+          if (res.success) {
+            if(res.result && res.result.length>0){
+              for(let i of res.result){
+                this.selfParam['self_'+i.paramName]=(!this.$route.query[i.paramName])?"":this.$route.query[i.paramName]
+              }
+            }
+          } else {
+            this.$message.warning(res.message)
+          }
+          this.loadData()
+        })
+      },
+      initQueryInfo() {
+        if(!this.$route.params.code){
+          return false
+        }
+        getAction(`${this.url.getQueryInfo}${this.$route.params.code}`).then((res) => {
+          console.log("获取查询条件", res);
+          if (res.success) {
+            this.queryInfo = res.result
+          } else {
+            this.$message.warning(res.message)
+          }
+        })
+      },
+      loadData(arg) {
+        if(!this.$route.params.code){
+          return false
+        }
+        if (arg == 1) {
+          this.table.pagination.current = 1
+        }
+        let params = this.getQueryParams();//查询条件
+        console.log(params)
+
+        console.log(' 动态报表 reportCode : ' + this.reportCode);
+        this.table.loading = true
+        Promise.all([
+          getAction(`${this.url.getColumns}${this.reportCode}`),
+          getAction(`${this.url.getData}${this.reportCode}`, params)
+        ]).then(results => {
+          let [{result: {columns,cgreportHeadName,dictOptions}}, {result: data}] = results
+          let columnWidth = 230
+          this.dictOptions = dictOptions
+          for(let a=0;a<columns.length;a++){
+            if(columns[a].customRender){
+              let field_name = columns[a].customRender;
+              columns[a].customRender=(text)=>{
+                if(!text){
+                  return ''
+                }else{
+                  return filterMultiDictText(this.dictOptions[field_name], text+"");
+                }
+              }
+            }
+            columns.width = columnWidth
+          }
+          this.table.scroll.x = columns.length * columnWidth
+          this.table.columns = [...columns]
+          this.cgreportHeadName = cgreportHeadName
+          if (data) {
+            this.table.pagination.total = Number(data.total)
+            this.table.dataSource = data.records
+          } else {
+            this.table.pagination.total = 0
+            this.table.dataSource = []
+          }
+        }).catch((e) => {
+          console.error(e)
+          this.$message.error('查询失败')
+        }).then(() => {
+          this.table.loading = false
+        })
+      },
+      getQueryParams() {
+        let param = Object.assign({}, this.queryParam, this.sorter,this.selfParam);
+        param.pageNo = this.table.pagination.current;
+        param.pageSize = this.table.pagination.pageSize;
+        return filterObj(param);
+      },
+      searchByQuery() {
+        this.loadData(1);
+      },
+      searchReset() {
+        this.queryParam = {}
+        this.loadData(1);
+      },
+      handleToggleSearch() {
+        this.toggleSearchStatus = !this.toggleSearchStatus;
+      },
+      exportExcel() {
+        let fileName = this.cgreportHeadName
+        downFile(`/online/cgreport/api/exportXls/${this.reportCode}`,this.queryParam).then((data)=>{
+          if (!data) {
+            this.$message.warning("文件下载失败")
+            return
+          }
+          if (typeof window.navigator.msSaveBlob !== 'undefined') {
+            window.navigator.msSaveBlob(new Blob([data]), fileName+'.xls')
+          }else{
+            let url = window.URL.createObjectURL(new Blob([data]))
+            let link = document.createElement('a')
+            link.style.display = 'none'
+            link.href = url
+            link.setAttribute('download', fileName+'.xls')
+            document.body.appendChild(link)
+            link.click()
+            document.body.removeChild(link); //下载完成移除元素
+            window.URL.revokeObjectURL(url); //释放掉blob对象
+          }
+        })
+      },
+      handleChangeInTableSelect(selectedRowKeys, selectionRows) {
+        this.table.selectedRowKeys = selectedRowKeys
+        this.table.selectionRows = selectionRows
+      },
+      handleChangeInTable(pagination, filters, sorter) {
+        //分页、排序、筛选变化时触发
+        if (Object.keys(sorter).length > 0) {
+          this.sorter.column = sorter.field
+          this.sorter.order = 'ascend' == sorter.order ? 'asc' : 'desc'
+        }
+        this.table.pagination = pagination
+        this.loadData()
+      }
+
+    }
+  }
+</script>
+<style scoped>
+  .div {
+    display: flex;
+    align-items: center;
+    height: 500px
+  }
+
+</style>

+ 164 - 0
src/views/modules/oss/OSSFileList.vue

@@ -0,0 +1,164 @@
+<template>
+  <a-card :bordered="false">
+    <!-- 查询区域 -->
+    <div class="table-page-search-wrapper">
+      <a-form layout="inline" @keyup.enter.native="searchQuery">
+        <a-row :gutter="24">
+          <a-col :md="6" :sm="8">
+            <a-form-item label="文件名称">
+              <a-input placeholder="请输入文件名称" v-model="queryParam.fileName"></a-input>
+            </a-form-item>
+          </a-col>
+          <a-col :md="6" :sm="8">
+            <a-form-item label="文件地址">
+              <a-input placeholder="请输入文件地址" v-model="queryParam.url"></a-input>
+            </a-form-item>
+          </a-col>
+        </a-row>
+      </a-form>
+    </div>
+
+    <!-- 操作按钮区域 -->
+    <div class="table-operator">
+      <!--      <a-button type="primary" icon="download" @click="handleExportXls('文件列表')">导出</a-button>-->
+      <a-upload
+          name="file"
+          :multiple="false"
+          :action="uploadAction"
+          :headers="tokenHeader"
+          :showUploadList="false"
+          :beforeUpload="beforeUpload"
+          @change="handleChange">
+        <a-button>
+          <a-icon type="upload"/>
+          文件上传
+        </a-button>
+      </a-upload>
+    </div>
+
+    <!-- table区域-begin -->
+    <div>
+      <div class="ant-alert ant-alert-info" style="margin-bottom: 16px;">
+        <i class="anticon anticon-info-circle ant-alert-icon"></i> 已选择 <a
+          style="font-weight: 600">{{
+        selectedRowKeys.length }}</a>项
+        <a style="margin-left: 24px" @click="onClearSelected">清空</a>
+      </div>
+
+      <a-table
+          ref="table"
+          size="middle"
+          bordered
+          rowKey="id"
+          :columns="columns"
+          :dataSource="dataSource"
+          :pagination="ipagination"
+          :loading="loading"
+          :rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
+          @change="handleTableChange">
+
+        <span slot="action" slot-scope="text, record">
+            <a @click="ossDelete(record.id)">删除</a>
+        </span>
+
+      </a-table>
+    </div>
+    <!-- table区域-end -->
+  </a-card>
+</template>
+
+<script>
+  import {JeecgListMixin} from '@/mixins/JeecgListMixin'
+
+  export default {
+    name: "OSSFileList",
+    mixins: [JeecgListMixin],
+    data() {
+      return {
+        description: '文件列表',
+        // 表头
+        columns: [
+          {
+            title: '#',
+            dataIndex: '',
+            key: 'rowIndex',
+            width: 60,
+            align: "center",
+            customRender: function (t, r, index) {
+              return parseInt(index) + 1;
+            }
+          },
+          {
+            title: '文件名称',
+            align: "center",
+            dataIndex: 'fileName'
+          },
+          {
+            title: '文件地址',
+            align: "center",
+            dataIndex: 'url'
+          },
+          {
+            title: '操作',
+            dataIndex: 'action',
+            align: "center",
+            scopedSlots: {customRender: 'action'},
+          }
+        ],
+        url: {
+          upload: "/oss/file/upload",
+          list: "/oss/file/list",
+          delete: "/oss/file/delete"
+        }
+      }
+    },
+    computed: {
+      uploadAction() {
+        return window._CONFIG['domianURL'] + this.url.upload;
+      }
+    },
+    methods: {
+      beforeUpload(file) {
+        var fileType = file.type;
+        if (fileType === 'image') {
+          if (fileType.indexOf('image') < 0) {
+            this.$message.warning('请上传图片');
+            return false;
+          }
+        } else if (fileType === 'file') {
+          if (fileType.indexOf('image') >= 0) {
+            this.$message.warning('请上传文件');
+            return false;
+          }
+        }
+        return true
+      },
+      handleChange(info) {
+        if (info.file.status === 'done') {
+          if (info.file.response.success) {
+            this.loadData()
+            this.$message.success(`${info.file.name} 上传成功!`);
+          } else {
+            this.$message.error(`${info.file.name} 上传失败.`);
+          }
+        } else if (info.file.status === 'error') {
+          this.$message.error(`${info.file.name} 上传失败.`);
+        }
+      },
+      ossDelete(id) {
+        var that = this;
+        that.$confirm({
+          title: "确认删除",
+          content: "是否删除选中文件?",
+          onOk: function () {
+            that.handleDelete(id)
+          }
+        });
+      }
+    }
+  }
+</script>
+
+<style scoped>
+  @import '~@assets/less/common.less'
+</style>

+ 31 - 0
src/views/system/AddressList.vue

@@ -0,0 +1,31 @@
+<template>
+  <a-row type="flex" :gutter="16">
+    <a-col :md="5" :sm="24">
+      <address-list-left v-model="currentOrgCode"/>
+    </a-col>
+    <a-col :md="24-5" :sm="24">
+      <address-list-right v-model="currentOrgCode"/>
+    </a-col>
+  </a-row>
+</template>
+
+<script>
+  import AddressListLeft from './modules/AddressListLeft'
+  import AddressListRight from './modules/AddressListRight'
+
+  export default {
+    name: 'AddressList',
+    components: { AddressListLeft, AddressListRight },
+    data() {
+      return {
+        description: '通讯录页面',
+        currentOrgCode: ''
+      }
+    },
+
+    methods: {}
+  }
+</script>
+<style scoped>
+  @import '~@assets/less/common.less';
+</style>

+ 125 - 0
src/views/system/DictDeleteList.vue

@@ -0,0 +1,125 @@
+<template>
+  <a-modal
+    :width="modalWidth"
+    :style="modalStyle"
+    :visible="visible"
+    :maskClosable="false"
+    @cancel="handleCancel">
+    <template slot="footer">
+      <a-button @click="handleCancel">关闭</a-button>
+    </template>
+    <a-table
+      ref="table"
+      rowKey="id"
+      size="middle"
+      :columns="columns"
+      :loading="loading"
+      :dataSource="dataSource"
+      :pagination="false">
+      <span slot="action" slot-scope="text, record">
+        <a @click="handleBack(record.id)"><a-icon type="redo"/>字典取回</a>
+        <a-divider type="vertical"/>
+        <a @click="handleDelete(record.id)"><a-icon type="scissor"/>彻底删除</a>
+      </span>
+    </a-table>
+
+  </a-modal>
+
+
+</template>
+
+<script>
+  import { getAction,deleteAction,putAction } from '@/api/manage'
+  export default {
+    name: "DictDeleteList",
+    data () {
+      return {
+        modalWidth: '90%',
+        modalStyle: { 'top': '20px'},
+        title: '操作',
+        visible: false,
+        loading: false,
+        dataSource:[],
+        columns:[
+          {
+            title: '#',
+            dataIndex: '',
+            key: 'rowIndex',
+            width: 120,
+            align: "center",
+            customRender: function (t, r, index) {
+              return parseInt(index) + 1;
+            }
+          },
+          {
+            title: '字典名称',
+            align: "left",
+            dataIndex: 'dictName'
+          },
+          {
+            title: '字典编号',
+            align: "left",
+            dataIndex: 'dictCode'
+          },
+          {
+            title: '描述',
+            align: "left",
+            dataIndex: 'description'
+          },
+          {
+            title: '操作',
+            dataIndex: 'action',
+            align: "center",
+            scopedSlots: {customRender: 'action'}
+          }
+        ]
+      }
+    },
+
+    methods: {
+      handleCancel(){
+        this.visible = false
+      },
+      show(){
+        this.visible = true
+        this.loadData();
+      },
+      loadData(){
+        this.loading = true
+        getAction("/sys/dict/deleteList").then(res=>{
+          this.loading = false
+          if(res.success){
+            this.dataSource = res.result
+          }else{
+            this.$message.warning(res.message)
+          }
+        })
+      },
+      handleBack(id){
+        putAction("/sys/dict/back/"+id).then(res=>{
+          if(res.success){
+            this.$message.success(res.message)
+            this.loadData();
+          }else{
+            this.$message.warning(res.message)
+          }
+        })
+      },
+      handleDelete(id){
+        deleteAction("/sys/dict/deletePhysic/"+id).then(res=>{
+          if(res.success){
+            this.$message.success(res.message)
+            this.loadData();
+          }else{
+            this.$message.warning(res.message)
+          }
+        })
+      }
+
+    }
+  }
+</script>
+
+<style scoped>
+
+</style>

+ 198 - 0
src/views/system/NewPermissionList.vue

@@ -0,0 +1,198 @@
+<template>
+  <a-card :bordered="false">
+
+    <!-- 操作按钮区域 -->
+    <div class="table-operator">
+      <a-button @click="handleAdd" type="primary" icon="plus">新增</a-button>
+      <a-button
+        @click="batchDel"
+        v-if="selectedRowKeys.length > 0"
+        ghost
+        type="primary"
+        icon="delete">批量删除
+      </a-button>
+    </div>
+
+    <!-- table区域-begin -->
+    <div>
+
+      <div class="ant-alert ant-alert-info" style="margin-bottom: 16px;">
+        <i class="anticon anticon-info-circle ant-alert-icon"></i>已选择&nbsp;<a style="font-weight: 600">{{
+        selectedRowKeys.length }}</a>项&nbsp;&nbsp;
+        <a style="margin-left: 24px" @click="onClearSelected">清空</a>
+      </div>
+
+      <a-table
+        :columns="columns"
+        size="middle"
+        :pagination="false"
+        :dataSource="dataSource"
+        :loading="loading"
+        @expand="expandSubmenu"
+        :rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}">
+
+        <span slot="action" slot-scope="text, record">
+          <a @click="handleEdit(record)">编辑</a>
+
+          <a-divider type="vertical"/>
+          <a-dropdown>
+            <a class="ant-dropdown-link">
+              更多 <a-icon type="down"/>
+            </a>
+            <a-menu slot="overlay">
+              <a-menu-item>
+                <a href="javascript:;" @click="handleDetail(record)">详情</a>
+              </a-menu-item>
+              <a-menu-item>
+                <a href="javascript:;" @click="handleAddSub(record)">添加子菜单</a>
+              </a-menu-item>
+              <a-menu-item>
+                <a href="javascript:;" @click="handleDataRule(record)">数据规则</a>
+              </a-menu-item>
+
+              <a-menu-item>
+                <a-popconfirm title="确定删除吗?" @confirm="() => handleDelete(record.id)">
+                  <a>删除</a>
+                </a-popconfirm>
+              </a-menu-item>
+            </a-menu>
+          </a-dropdown>
+        </span>
+        <!-- 字符串超长截取省略号显示 -->
+        <span slot="url" slot-scope="text">
+          <j-ellipsis :value="text" :length="25"/>
+        </span>
+        <!-- 字符串超长截取省略号显示-->
+        <span slot="component" slot-scope="text">
+          <j-ellipsis :value="text"/>
+        </span>
+      </a-table>
+
+    </div>
+    <!-- table区域-end -->
+
+    <permission-modal ref="modalForm" @ok="modalFormOk"></permission-modal>
+    <permission-data-rule-list ref="PermissionDataRuleList" @ok="modalFormOk"></permission-data-rule-list>
+
+  </a-card>
+</template>
+
+<script>
+  import PermissionModal from './modules/PermissionModal'
+  import { getSystemMenuList,getSystemSubmenu } from '@/api/api'
+  import { JeecgListMixin } from '@/mixins/JeecgListMixin'
+  import PermissionDataRuleList from './PermissionDataRuleList'
+  import JEllipsis from '@/components/jeecg/JEllipsis'
+
+  const columns = [
+    {
+      title: '菜单名称',
+      dataIndex: 'name',
+      key: 'name'
+    }, {
+      title: '菜单类型',
+      dataIndex: 'menuType',
+      key: 'menuType',
+      customRender: function(text) {
+        if (text == 0) {
+          return '菜单'
+        } else if (text == 1) {
+          return '菜单'
+        } else if (text == 2) {
+          return '按钮/权限'
+        } else {
+          return text
+        }
+      }
+    },/*{
+      title: '权限编码',
+      dataIndex: 'perms',
+      key: 'permissionCode',
+    },*/{
+      title: 'icon',
+      dataIndex: 'icon',
+      key: 'icon'
+    },
+    {
+      title: '组件',
+      dataIndex: 'component',
+      key: 'component',
+      scopedSlots: { customRender: 'component' }
+    },
+    {
+      title: '路径',
+      dataIndex: 'url',
+      key: 'url',
+      scopedSlots: { customRender: 'url' }
+    },
+    {
+      title: '排序',
+      dataIndex: 'sortNo',
+      key: 'sortNo'
+    },
+    {
+      title: '操作',
+      dataIndex: 'action',
+      scopedSlots: { customRender: 'action' },
+      align: 'center',
+      width: 150
+    }
+  ]
+
+  export default {
+    name: 'PermissionList',
+    mixins: [JeecgListMixin],
+    components: {
+      PermissionDataRuleList,
+      PermissionModal,
+      JEllipsis
+    },
+    data() {
+      return {
+        description: '这是菜单管理页面',
+        // 表头
+        columns: columns,
+        loading: false,
+        url: {
+          list: '/sys/permission/list',
+          delete: '/sys/permission/delete',
+          deleteBatch: '/sys/permission/deleteBatch'
+        }
+      }
+    },
+    methods: {
+      loadData() {
+        this.dataSource = []
+        getSystemMenuList().then((res) => {
+          if (res.success) {
+            console.log(res.result)
+            this.dataSource = res.result
+          }
+        })
+      },
+      expandSubmenu(expanded, record){
+        if(expanded){
+          getSystemSubmenu({parentId:record.id}).then((res) => {
+            if (res.success) {
+              record.children = res.result
+            }
+          })
+        }
+
+      },
+      // 打开数据规则编辑
+      handleDataRule(record) {
+        this.$refs.PermissionDataRuleList.edit(record)
+      },
+      handleAddSub(record) {
+        this.$refs.modalForm.title = "添加子菜单";
+        this.$refs.modalForm.localMenuType = 1;
+        this.$refs.modalForm.disableSubmit = false;
+        this.$refs.modalForm.edit({status:'1',permsType:'1',route:true,'parentId':record.id});
+      }
+    }
+  }
+</script>
+<style scoped>
+  @import '~@assets/less/common.less'
+</style>

+ 187 - 0
src/views/system/SysFillRuleList.vue

@@ -0,0 +1,187 @@
+<template>
+  <a-card :bordered="false">
+
+    <!-- 查询区域 -->
+    <div class="table-page-search-wrapper">
+      <a-form layout="inline" @keyup.enter.native="searchQuery">
+        <a-row :gutter="24">
+          <a-col :md="6" :sm="8">
+            <a-form-item label="规则名称">
+              <a-input placeholder="请输入规则名称" v-model="queryParam.ruleName"></a-input>
+            </a-form-item>
+          </a-col>
+          <a-col :md="6" :sm="8">
+            <a-form-item label="规则Code">
+              <a-input placeholder="请输入规则Code" v-model="queryParam.ruleCode"></a-input>
+            </a-form-item>
+          </a-col>
+          <a-col :md="6" :sm="8">
+            <span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
+              <a-button type="primary" @click="searchQuery" icon="search">查询</a-button>
+              <a-button type="primary" @click="searchReset" icon="reload" style="margin-left: 8px">重置</a-button>
+            </span>
+          </a-col>
+        </a-row>
+      </a-form>
+    </div>
+
+    <!-- 操作按钮区域 -->
+    <div class="table-operator">
+      <a-button @click="handleAdd" type="primary" icon="plus">新增</a-button>
+      <a-button type="primary" icon="download" @click="handleExportXls('填值规则')">导出</a-button>
+      <a-upload name="file" :showUploadList="false" :multiple="false" :headers="tokenHeader" :action="importExcelUrl" @change="handleImportExcel">
+        <a-button type="primary" icon="import">导入</a-button>
+      </a-upload>
+      <a-dropdown v-if="selectedRowKeys.length > 0">
+        <a-menu slot="overlay">
+          <a-menu-item key="1" @click="batchDel">
+            <a-icon type="delete"/>
+            删除
+          </a-menu-item>
+        </a-menu>
+        <a-button style="margin-left: 8px"> 批量操作
+          <a-icon type="down"/>
+        </a-button>
+      </a-dropdown>
+    </div>
+
+    <!-- table区域-begin -->
+    <a-alert type="info" showIcon style="margin-bottom: 16px;">
+      <template slot="message">
+        <span>已选择</span>
+        <a style="font-weight: 600;padding: 0 4px;">{{ selectedRowKeys.length }}</a>
+        <span>项</span>
+        <template v-if="selectedRowKeys.length>0">
+          <a-divider type="vertical"/>
+          <a @click="onClearSelected">清空</a>
+        </template>
+      </template>
+    </a-alert>
+
+    <a-table
+      ref="table"
+      size="middle"
+      bordered
+      rowKey="id"
+      :columns="columns"
+      :dataSource="dataSource"
+      :pagination="ipagination"
+      :loading="loading"
+      :rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
+      @change="handleTableChange">
+
+        <span slot="action" slot-scope="text, record">
+          <a @click="handleEdit(record)">编辑</a>
+          <a-divider type="vertical"/>
+          <a-dropdown>
+            <a class="ant-dropdown-link">更多 <a-icon type="down"/></a>
+            <a-menu slot="overlay">
+              <a-menu-item @click="handleTest(record)">
+                功能测试
+              </a-menu-item>
+              <a-menu-item>
+                <a-popconfirm title="确定删除吗?" @confirm="() => handleDelete(record.id)">
+                  <a>删除</a>
+                </a-popconfirm>
+              </a-menu-item>
+            </a-menu>
+          </a-dropdown>
+        </span>
+
+    </a-table>
+    <!-- table区域-end -->
+
+    <!-- 表单区域 -->
+    <sys-fill-rule-modal ref="modalForm" @ok="modalFormOk"/>
+  </a-card>
+</template>
+
+<script>
+  import { getAction } from '@/api/manage'
+  import SysFillRuleModal from './modules/SysFillRuleModal'
+  import { JeecgListMixin } from '@/mixins/JeecgListMixin'
+
+  export default {
+    name: 'SysFillRuleList',
+    mixins: [JeecgListMixin],
+    components: { SysFillRuleModal },
+    data() {
+      return {
+        description: '填值规则管理页面',
+        // 表头
+        columns: [
+          {
+            title: '#',
+            dataIndex: '',
+            key: 'rowIndex',
+            width: 60,
+            align: 'center',
+            customRender: (t, r, index) => 1 + index
+          },
+          {
+            title: '规则名称',
+            align: 'center',
+            dataIndex: 'ruleName'
+          },
+          {
+            title: '规则Code',
+            align: 'center',
+            dataIndex: 'ruleCode'
+          },
+          {
+            title: '规则实现类',
+            align: 'center',
+            dataIndex: 'ruleClass'
+          },
+          {
+            title: '规则参数',
+            align: 'center',
+            dataIndex: 'ruleParams'
+          },
+          {
+            title: '操作',
+            dataIndex: 'action',
+            align: 'center',
+            scopedSlots: { customRender: 'action' },
+          }
+        ],
+        url: {
+          list: '/sys/fillRule/list',
+          test: '/sys/fillRule/testFillRule',
+          delete: '/sys/fillRule/delete',
+          deleteBatch: '/sys/fillRule/deleteBatch',
+          exportXlsUrl: '/sys/fillRule/exportXls',
+          importExcelUrl: '/sys/fillRule/importExcel',
+        },
+      }
+    },
+    computed: {
+      importExcelUrl() {
+        return `${window._CONFIG['domianURL']}${this.url.importExcelUrl}`
+      }
+    },
+    methods: {
+      handleTest(record) {
+        let closeLoading = this.$message.loading('生成中...', 0)
+
+        getAction(this.url.test, {
+          ruleCode: record.ruleCode
+        }).then(res => {
+          if (res.success) {
+            this.$info({
+              title: '填值规则功能测试',
+              content: '生成结果:' + res.result
+            })
+          } else {
+            this.$message.warn(res.message)
+          }
+        }).finally(() => {
+          closeLoading()
+        })
+      }
+    }
+  }
+</script>
+<style scoped>
+  @import '~@assets/less/common.less'
+</style>

+ 178 - 0
src/views/system/SysPositionList.vue

@@ -0,0 +1,178 @@
+<template>
+  <a-card :bordered="false">
+
+    <!-- 查询区域 -->
+    <div class="table-page-search-wrapper">
+      <a-form layout="inline" @keyup.enter.native="searchQuery">
+        <a-row :gutter="24">
+
+          <a-col :md="6" :sm="8">
+            <a-form-item label="职务编码">
+              <a-input placeholder="请输入职务编码" v-model="queryParam.code"></a-input>
+            </a-form-item>
+          </a-col>
+          <a-col :md="6" :sm="8">
+            <a-form-item label="职务名称">
+              <a-input placeholder="请输入职务名称" v-model="queryParam.name"></a-input>
+            </a-form-item>
+          </a-col>
+          <template v-if="toggleSearchStatus">
+            <a-col :md="6" :sm="8">
+              <a-form-item label="职级">
+                <j-dict-select-tag v-model="queryParam.postRank" placeholder="请选择职级" dictCode="position_rank"/>
+              </a-form-item>
+            </a-col>
+
+          </template>
+          <a-col :md="6" :sm="8">
+            <span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
+              <a-button type="primary" @click="searchQuery" icon="search">查询</a-button>
+              <a-button type="primary" @click="searchReset" icon="reload" style="margin-left: 8px">重置</a-button>
+              <a @click="handleToggleSearch" style="margin-left: 8px">
+                {{ toggleSearchStatus ? '收起' : '展开' }}
+                <a-icon :type="toggleSearchStatus ? 'up' : 'down'"/>
+              </a>
+            </span>
+          </a-col>
+
+        </a-row>
+      </a-form>
+    </div>
+
+    <!-- 操作按钮区域 -->
+    <div class="table-operator">
+      <a-button @click="handleAdd" type="primary" icon="plus">新增</a-button>
+      <a-button type="primary" icon="download" @click="handleExportXls('职务表')">导出</a-button>
+      <a-upload name="file" :showUploadList="false" :multiple="false" :headers="tokenHeader" :action="importExcelUrl" @change="handleImportExcel">
+        <a-button type="primary" icon="import">导入</a-button>
+      </a-upload>
+      <a-dropdown v-if="selectedRowKeys.length > 0">
+        <a-menu slot="overlay">
+          <a-menu-item key="1" @click="batchDel">
+            <a-icon type="delete"/>
+            删除
+          </a-menu-item>
+        </a-menu>
+        <a-button style="margin-left: 8px"> 批量操作
+          <a-icon type="down"/>
+        </a-button>
+      </a-dropdown>
+    </div>
+
+    <!-- table区域-begin -->
+    <div>
+      <div class="ant-alert ant-alert-info" style="margin-bottom: 16px;">
+        <i class="anticon anticon-info-circle ant-alert-icon"></i> 已选择 <a style="font-weight: 600">{{ selectedRowKeys.length }}</a>项
+        <a style="margin-left: 24px" @click="onClearSelected">清空</a>
+      </div>
+
+      <a-table
+        ref="table"
+        size="middle"
+        bordered
+        rowKey="id"
+        :columns="columns"
+        :dataSource="dataSource"
+        :pagination="ipagination"
+        :loading="loading"
+        :rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
+        @change="handleTableChange">
+
+        <span slot="action" slot-scope="text, record">
+          <a @click="handleEdit(record)">编辑</a>
+
+          <a-divider type="vertical"/>
+          <a-dropdown>
+            <a class="ant-dropdown-link">更多 <a-icon type="down"/></a>
+            <a-menu slot="overlay">
+              <a-menu-item>
+                <a-popconfirm title="确定删除吗?" @confirm="() => handleDelete(record.id)">
+                  <a>删除</a>
+                </a-popconfirm>
+              </a-menu-item>
+            </a-menu>
+          </a-dropdown>
+        </span>
+
+      </a-table>
+    </div>
+    <!-- table区域-end -->
+
+    <!-- 表单区域 -->
+    <sysPosition-modal ref="modalForm" @ok="modalFormOk"></sysPosition-modal>
+  </a-card>
+</template>
+
+<script>
+  import SysPositionModal from './modules/SysPositionModal'
+  import { JeecgListMixin } from '@/mixins/JeecgListMixin'
+  import JDictSelectTag from '@/components/dict/JDictSelectTag'
+
+  export default {
+    name: 'SysPositionList',
+    mixins: [JeecgListMixin],
+    components: {
+      SysPositionModal,
+      JDictSelectTag
+    },
+    data() {
+      return {
+        description: '职务表管理页面',
+        // 表头
+        columns: [
+          {
+            title: '#',
+            dataIndex: '',
+            key: 'rowIndex',
+            width: 60,
+            align: 'center',
+            customRender: function (t, r, index) {
+              return parseInt(index) + 1
+            }
+          },
+          {
+            title: '职务编码',
+            align: 'center',
+            dataIndex: 'code'
+          },
+          {
+            title: '职务名称',
+            align: 'center',
+            dataIndex: 'name'
+          },
+          {
+            title: '职级',
+            align: 'center',
+            dataIndex: 'postRank_dictText'
+          },
+          // {
+          //   title: '公司id',
+          //   align: 'center',
+          //   dataIndex: 'companyId'
+          // },
+          {
+            title: '操作',
+            dataIndex: 'action',
+            align: 'center',
+            scopedSlots: { customRender: 'action' },
+          }
+        ],
+        url: {
+          list: '/sys/position/list',
+          delete: '/sys/position/delete',
+          deleteBatch: '/sys/position/deleteBatch',
+          exportXlsUrl: '/sys/position/exportXls',
+          importExcelUrl: '/sys/position/importExcel',
+        },
+      }
+    },
+    computed: {
+      importExcelUrl: function () {
+        return `${window._CONFIG['domianURL']}/${this.url.importExcelUrl}`
+      }
+    }
+  }
+</script>
+<style scoped>
+  @import '~@assets/less/common.less'
+</style>

+ 98 - 0
src/views/system/modules/AddressListLeft.vue

@@ -0,0 +1,98 @@
+<template>
+  <a-card :loading="cardLoading" :bordered="false" style="height: 100%;">
+    <a-spin :spinning="loading">
+      <a-input-search @search="handleSearch" style="width:100%;margin-top: 10px" placeholder="输入组织机构名称进行查询..."/>
+
+      <a-tree
+        showLine
+        checkStrictly
+        :expandedKeys.sync="expandedKeys"
+        :selectedKeys="selectedKeys"
+        :dropdownStyle="{maxHeight:'200px',overflow:'auto'}"
+        :treeData="treeDataSource"
+        @select="handleTreeSelect"
+      />
+    </a-spin>
+  </a-card>
+</template>
+
+<script>
+  import { queryDepartTreeList, searchByKeywords } from '@/api/api'
+
+  export default {
+    name: 'AddressListLeft',
+    props: ['value'],
+    data() {
+      return {
+        cardLoading: true,
+        loading: false,
+        treeDataSource: [],
+        selectedKeys: [],
+        expandedKeys: []
+      }
+    },
+    created() {
+      this.queryTreeData()
+    },
+    methods: {
+
+      queryTreeData(keyword) {
+        this.commonRequestThen(queryDepartTreeList({
+          departName: keyword ? keyword : undefined
+        }))
+      },
+
+      handleSearch(value) {
+        if (value) {
+          this.commonRequestThen(searchByKeywords({ keyWord: value }))
+        } else {
+          this.queryTreeData()
+        }
+      },
+
+      handleTreeSelect(selectedKeys, event) {
+        if (selectedKeys.length > 0 && this.selectedKeys[0] !== selectedKeys[0]) {
+          this.selectedKeys = [selectedKeys[0]]
+          let orgCode = event.node.dataRef.orgCode
+          this.emitInput(orgCode)
+        }
+      },
+
+      emitInput(orgCode) {
+        this.$emit('input', orgCode)
+      },
+
+      commonRequestThen(promise) {
+        this.loading = true
+        promise.then(res => {
+          if (res.success) {
+            this.treeDataSource = res.result
+
+            // 默认选中第一条数据、默认展开所有第一级
+            if (res.result.length > 0) {
+              this.expandedKeys = []
+              res.result.forEach((item, index) => {
+                if (index === 0) {
+                  this.selectedKeys = [item.id]
+                  this.emitInput(item.orgCode)
+                }
+                this.expandedKeys.push(item.id)
+              })
+            }
+          } else {
+            this.$message.warn('组织机构查询失败:' + res.message)
+            console.error('组织机构查询失败:', res)
+          }
+        }).finally(() => {
+          this.loading = false
+          this.cardLoading = false
+        })
+      },
+
+    }
+  }
+</script>
+
+<style scoped>
+
+</style>

+ 198 - 0
src/views/system/modules/AddressListRight.vue

@@ -0,0 +1,198 @@
+<template>
+  <a-card class="j-address-list-right-card-box" :loading="cardLoading" :bordered="false">
+    <div class="table-page-search-wrapper">
+      <a-form layout="inline">
+        <a-row :gutter="10">
+
+          <a-col :md="6" :sm="12">
+            <a-form-item label="姓名" style="margin-left:8px">
+              <a-input placeholder="请输入姓名查询" v-model="queryParam.realname"></a-input>
+            </a-form-item>
+          </a-col>
+
+
+          <a-col :md="6" :sm="12">
+            <a-form-item label="工号" style="margin-left:8px">
+              <a-input placeholder="请输入工号查询" v-model="queryParam.workNo"></a-input>
+            </a-form-item>
+          </a-col>
+
+          <span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
+            <a-col :md="6" :sm="24">
+             <a-button type="primary" @click="searchQuery" icon="search" style="margin-left: 18px">查询</a-button>
+              <a-button type="primary" @click="searchReset" icon="reload" style="margin-left: 8px">重置</a-button>
+            </a-col>
+          </span>
+        </a-row>
+      </a-form>
+    </div>
+
+    <a-table
+      ref="table"
+      size="middle"
+      bordered
+      rowKey="userId"
+      :pagination="ipagination"
+      :columns="columns"
+      :dataSource="dataSource"
+      :loading="loading"
+      @change="handleTableChange">
+    </a-table>
+
+  </a-card>
+</template>
+
+<script>
+  import { getAction } from '@/api/manage'
+  import { JeecgListMixin } from '@/mixins/JeecgListMixin'
+
+  export default {
+    name: 'AddressListRight',
+    mixins: [JeecgListMixin],
+    components: {},
+    props: ['value'],
+    data() {
+      return {
+        description: '用户信息',
+        cardLoading: true,
+        positionInfo: {},
+        columns: [
+          {
+            title: '#',
+            key: 'rowIndex',
+            dataIndex: '',
+            width: 60,
+            align: 'center',
+            customRender: (t, r, i) => parseInt(i) + 1
+          },
+          {
+            title: '部门',
+            width: '20%',
+            align: 'center',
+            dataIndex: 'departName'
+          },
+          {
+            title: '姓名',
+            width: '15%',
+            align: 'center',
+            dataIndex: 'realname'
+          },
+          {
+            title: '工号',
+            width: '15%',
+            align: 'center',
+            dataIndex: 'workNo'
+          },
+          {
+            title: '职务',
+            width: '15%',
+            align: 'center',
+            dataIndex: 'post',
+            customRender: (text) => (text || '').split(',').map(t => this.positionInfo[t] ? this.positionInfo[t] : t).join(',')
+          },
+          {
+            title: '座机',
+            width: '15%',
+            align: 'center',
+            dataIndex: 'telephone'
+          },
+          // {
+          //   title: '手机号',
+          //   width: '12%',
+          //   align: 'center',
+          //   dataIndex: 'phone'
+          // },
+          {
+            title: '公司邮箱',
+            width: '15%',
+            align: 'center',
+            dataIndex: 'email'
+          }
+        ],
+        url: {
+          list: '/sys/user/queryByOrgCodeForAddressList',
+          listByPosition: '/sys/position/list'
+        }
+      }
+    },
+    watch: {
+      value: {
+        immediate: true,
+        handler(orgCode) {
+          this.dataSource = []
+          this.loadData(1, orgCode)
+        }
+      },
+    },
+    created() {
+      this.queryPositionInfo()
+    },
+    methods: {
+
+      loadData(pageNum, orgCode) {
+        if (!orgCode) {
+          return
+        }
+        //加载数据 若传入参数1则加载第一页的内容
+        if (pageNum === 1) {
+          this.ipagination.current = 1
+        }
+        this.loading = true
+        getAction(this.url.list, {
+          orgCode,
+          ...this.getQueryParams()
+        }).then((res) => {
+          if (res.success) {
+            this.dataSource = res.result.records
+            this.ipagination.total = res.result.total
+          }
+        }).finally(() => {
+          this.loading = false
+          this.cardLoading = false
+        })
+      },
+
+      searchQuery() {
+        this.loadData(1, this.value)
+      },
+      searchReset() {
+        this.queryParam = {}
+        this.loadData(1, this.value)
+      },
+
+      handleTableChange(pagination, filters, sorter) {
+        if (Object.keys(sorter).length > 0) {
+          this.isorter.column = sorter.field
+          this.isorter.order = 'ascend' === sorter.order ? 'asc' : 'desc'
+        }
+        this.ipagination = pagination
+        this.loadData(null, this.value)
+      },
+
+      // 查询职务信息
+      queryPositionInfo() {
+        getAction(this.url.listByPosition, { pageSize: 99999 }).then(res => {
+          if (res.success) {
+            let positionInfo = {}
+            res.result.records.forEach(record => {
+              positionInfo[record['code']] = record['name']
+            })
+            this.positionInfo = positionInfo
+          }
+        })
+      }
+
+    }
+  }
+</script>
+<style>
+  .j-address-list-right-card-box .ant-table-placeholder {
+    min-height: 46px;
+  }
+</style>
+<style scoped>
+  .j-address-list-right-card-box {
+    height: 100%;
+    min-height: 300px;
+  }
+</style>

+ 158 - 0
src/views/system/modules/SysFillRuleModal.vue

@@ -0,0 +1,158 @@
+<template>
+  <a-modal
+    :title="title"
+    :width="800"
+    :visible="visible"
+    :maskClosable="false"
+    :confirmLoading="confirmLoading"
+    @ok="handleOk"
+    @cancel="handleCancel"
+    cancelText="关闭">
+
+    <a-spin :spinning="confirmLoading">
+      <a-form :form="form">
+
+        <a-form-item
+          :labelCol="labelCol"
+          :wrapperCol="wrapperCol"
+          label="规则名称">
+          <a-input placeholder="请输入规则名称" v-decorator="['ruleName', validatorRules.ruleName]"/>
+        </a-form-item>
+        <a-form-item
+          :labelCol="labelCol"
+          :wrapperCol="wrapperCol"
+          label="规则Code">
+          <a-input placeholder="请输入规则Code" :disabled="disabledCode" v-decorator="['ruleCode', validatorRules.ruleCode]"/>
+        </a-form-item>
+        <a-form-item
+          :labelCol="labelCol"
+          :wrapperCol="wrapperCol"
+          label="规则实现类">
+          <a-input placeholder="请输入规则实现类" v-decorator="['ruleClass', validatorRules.ruleClass]"/>
+        </a-form-item>
+        <a-form-item
+          :labelCol="labelCol"
+          :wrapperCol="wrapperCol"
+          label="规则参数">
+          <a-textarea placeholder="请输入规则参数" :rows="5" v-decorator="['ruleParams', validatorRules.ruleParams]"/>
+        </a-form-item>
+
+      </a-form>
+    </a-spin>
+  </a-modal>
+</template>
+
+<script>
+  import pick from 'lodash.pick'
+  import { httpAction } from '@/api/manage'
+  import { validateDuplicateValue } from '@/utils/util'
+
+  export default {
+    name: 'SysFillRuleModal',
+    components: {},
+    data() {
+      return {
+        title: '操作',
+        visible: false,
+        model: {},
+        labelCol: { xs: { span: 24 }, sm: { span: 5 } },
+        wrapperCol: { xs: { span: 24 }, sm: { span: 16 } },
+
+        confirmLoading: false,
+        form: this.$form.createForm(this),
+        validatorRules: {
+          ruleName: { rules: [{ required: true, message: '规则名称不能为空' }] },
+          ruleCode: {
+            rules: [
+              { required: true, message: '规则Code不能为空' },
+              { validator: (rule, value, callback) => validateDuplicateValue('sys_fill_rule', 'rule_code', value, this.model.id, callback) }
+            ]
+          },
+          ruleClass: { rules: [{ required: true, message: '规则实现类不能为空' }] },
+          ruleParams: {
+            rules: [{
+              validator: (rule, value, callback) => {
+
+                try {
+                  let json = JSON.parse(value)
+                  if (json instanceof Array) {
+                    callback('只能传递JSON对象,不能传递JSON数组')
+                  } else if (json instanceof Object) {
+                    callback()
+                  } else {
+                    callback('请输入JSON字符串')
+                  }
+                } catch {
+                  callback('请输入JSON字符串')
+                }
+              }
+            }]
+          },
+        },
+        url: {
+          add: '/sys/fillRule/add',
+          edit: '/sys/fillRule/edit',
+        },
+      }
+    },
+    computed: {
+      disabledCode() {
+        return !!this.model.id
+      }
+    },
+    created() {
+    },
+    methods: {
+      add() {
+        this.edit({})
+      },
+      edit(record) {
+        this.form.resetFields()
+        this.model = Object.assign({}, record)
+        this.visible = true
+        this.$nextTick(() => {
+          this.form.setFieldsValue(pick(this.model, 'ruleName', 'ruleCode', 'ruleClass', 'ruleParams'))
+        })
+      },
+      close() {
+        this.$emit('close')
+        this.visible = false
+      },
+      handleOk() {
+        const that = this
+        // 触发表单验证
+        this.form.validateFields((err, values) => {
+          if (!err) {
+            that.confirmLoading = true
+            let httpUrl = this.url.add, method = 'post'
+            if (this.model.id) {
+              httpUrl = this.url.edit
+              method = 'put'
+            }
+
+            let formData = Object.assign(this.model, values)
+            httpAction(httpUrl, formData, method).then((res) => {
+              if (res.success) {
+                that.$message.success(res.message)
+                that.$emit('ok')
+              } else {
+                that.$message.warning(res.message)
+              }
+            }).finally(() => {
+              that.confirmLoading = false
+              that.close()
+            })
+          }
+        })
+      },
+      handleCancel() {
+        this.close()
+      }
+
+    }
+  }
+</script>
+
+<style lang="less" scoped>
+
+</style>

+ 180 - 0
src/views/system/modules/SysPositionModal.vue

@@ -0,0 +1,180 @@
+<template>
+  <a-modal
+    :title="title"
+    :width="800"
+    :visible="visible"
+    :maskClosable="false"
+    :confirmLoading="confirmLoading"
+    @ok="handleOk"
+    @cancel="handleCancel"
+    cancelText="关闭">
+
+    <a-spin :spinning="confirmLoading">
+      <a-form :form="form">
+
+        <a-form-item
+          :labelCol="labelCol"
+          :wrapperCol="wrapperCol"
+          label="职务编码">
+          <a-input placeholder="请输入职务编码" v-decorator="['code', validatorRules.code]"/>
+        </a-form-item>
+        <a-form-item
+          :labelCol="labelCol"
+          :wrapperCol="wrapperCol"
+          label="职务名称">
+          <a-input placeholder="请输入职务名称" v-decorator="['name', validatorRules.name]"/>
+        </a-form-item>
+        <a-form-item
+          :labelCol="labelCol"
+          :wrapperCol="wrapperCol"
+          label="职级"
+        >
+          <j-dict-select-tag
+            placeholder="请选择职级"
+            :triggerChange="true"
+            dictCode="position_rank"
+            v-decorator="['postRank', validatorRules.postRank]"
+          />
+        </a-form-item>
+        <!--<a-form-item-->
+        <!--  :labelCol="labelCol"-->
+        <!--  :wrapperCol="wrapperCol"-->
+        <!--  label="公司id">-->
+        <!--  <a-input placeholder="请输入公司id" v-decorator="['companyId', {}]"/>-->
+        <!--</a-form-item>-->
+
+      </a-form>
+    </a-spin>
+  </a-modal>
+</template>
+
+<script>
+  import pick from 'lodash.pick'
+  import { httpAction } from '@/api/manage'
+  import { duplicateCheck } from '@/api/api'
+  import JDictSelectTag from '@/components/dict/JDictSelectTag'
+
+  let validatorCodeTimer = null
+
+  export default {
+    name: 'SysPositionModal',
+    components: { JDictSelectTag },
+    data() {
+      return {
+        title: '操作',
+        visible: false,
+        model: {},
+        labelCol: {
+          xs: { span: 24 },
+          sm: { span: 5 },
+        },
+        wrapperCol: {
+          xs: { span: 24 },
+          sm: { span: 16 },
+        },
+        confirmLoading: false,
+        form: this.$form.createForm(this),
+        validatorRules: {
+          code: {
+            rules: [
+              { required: true, message: '请输入职务编码' },
+              {
+                validator: (rule, value, callback) => {
+                  // 函数消抖的简单实现,防止一段时间内发送多次请求
+                  if (validatorCodeTimer) {
+                    // 停止上次开启的定时器
+                    clearTimeout(validatorCodeTimer)
+                  }
+                  validatorCodeTimer = setTimeout(() => {
+                    duplicateCheck({
+                      tableName: 'sys_position',
+                      fieldName: 'code',
+                      fieldVal: value,
+                      dataId: this.model.id
+                    }).then((res) => {
+                      if (res.success) {
+                        callback()
+                      } else {
+                        callback(res.message)
+                      }
+                    }).catch(console.error)
+                  }, 300)
+                }
+              }
+            ]
+          },
+          name: { rules: [{ required: true, message: '请输入职务名称' }] },
+          postRank: { rules: [{ required: true, message: '请选择职级' }] },
+        },
+        url: {
+          add: '/sys/position/add',
+          edit: '/sys/position/edit',
+        },
+      }
+    },
+    created() {
+    },
+    methods: {
+      add() {
+        this.edit({})
+      },
+      edit(record) {
+        this.form.resetFields()
+        this.model = Object.assign({}, record)
+        this.visible = true
+        this.$nextTick(() => {
+          this.form.setFieldsValue(pick(this.model,
+            'code',
+            'name',
+            'postRank',
+            // 'companyId'
+          ))
+        })
+      },
+      close() {
+        this.$emit('close')
+        this.visible = false
+      },
+      handleOk() {
+        const that = this
+        // 触发表单验证
+        this.form.validateFields((err, values) => {
+          if (!err) {
+            that.confirmLoading = true
+            let httpurl = ''
+            let method = ''
+            if (!this.model.id) {
+              httpurl += this.url.add
+              method = 'post'
+            } else {
+              httpurl += this.url.edit
+              method = 'put'
+            }
+
+            let formData = Object.assign(this.model, values)
+            httpAction(httpurl, formData, method).then((res) => {
+              if (res.success) {
+                that.$message.success(res.message)
+                that.$emit('ok')
+              } else {
+                that.$message.warning(res.message)
+              }
+            }).finally(() => {
+              that.confirmLoading = false
+              that.close()
+            })
+          }
+        })
+      },
+      handleCancel() {
+        this.close()
+      },
+
+
+    }
+  }
+</script>
+
+<style lang="less" scoped>
+
+</style>

文件差異過大導致無法顯示
+ 11794 - 0
yarn.lock