瀏覽代碼

Merge branch '1.01' of chenlei/dx-element-ui into master

chenlei 11 月之前
父節點
當前提交
122fa3ee83
共有 75 個文件被更改,包括 17651 次插入1 次删除
  1. 8 0
      packages/card/index.js
  2. 198 0
      packages/card/src/main.vue
  3. 8 0
      packages/charts/index.js
  4. 150 0
      packages/charts/src/chart.js
  5. 583 0
      packages/charts/src/echart.js
  6. 114 0
      packages/charts/src/f2ExtendCharts.vue
  7. 576 0
      packages/charts/src/f2chart.js
  8. 3 0
      packages/charts/src/index.js
  9. 91 0
      packages/charts/src/main.vue
  10. 570 0
      packages/charts/src/mapchart.js
  11. 64 0
      packages/charts/src/ntExtendCharts.vue
  12. 47 0
      packages/charts/src/ntMapCharts.vue
  13. 9 0
      packages/col/index.js
  14. 72 0
      packages/col/src/main.js
  15. 9 0
      packages/detail/index.js
  16. 104 0
      packages/detail/src/main.vue
  17. 8 0
      packages/form/index.js
  18. 269 0
      packages/form/src/main.vue
  19. 94 0
      packages/form/src/remove-form.vue
  20. 339 0
      packages/form/src/tool-form.js
  21. 8 0
      packages/infinite-transfer/index.js
  22. 286 0
      packages/infinite-transfer/src/main.vue
  23. 8 0
      packages/input-range/index.js
  24. 139 0
      packages/input-range/src/main.vue
  25. 8 0
      packages/input-table/index.js
  26. 157 0
      packages/input-table/src/main.vue
  27. 8 0
      packages/input/index.js
  28. 1285 0
      packages/input/src/main.vue
  29. 872 0
      packages/input/src/tool-input.js
  30. 8 0
      packages/layout/index.js
  31. 26 0
      packages/layout/src/components/AppMain.vue
  32. 45 0
      packages/layout/src/components/Breadcrumb/index.vue
  33. 24 0
      packages/layout/src/components/Hamburger/index.vue
  34. 16 0
      packages/layout/src/components/Navbar.vue
  35. 71 0
      packages/layout/src/components/ScrollBar/index.vue
  36. 70 0
      packages/layout/src/components/ScrollPane/index.vue
  37. 98 0
      packages/layout/src/components/Sidebar/SidebarItem.vue
  38. 38 0
      packages/layout/src/components/Sidebar/index.vue
  39. 110 0
      packages/layout/src/components/TagsView.vue
  40. 56 0
      packages/layout/src/components/header.vue
  41. 5 0
      packages/layout/src/components/index.js
  42. 55 0
      packages/layout/src/main.vue
  43. 10 0
      packages/list/index.js
  44. 154 0
      packages/list/src/buttons.vue
  45. 513 0
      packages/list/src/cardList.vue
  46. 55 0
      packages/list/src/list.vue
  47. 448 0
      packages/list/src/main.vue
  48. 375 0
      packages/list/src/search.vue
  49. 803 0
      packages/list/src/tables copy.vue
  50. 976 0
      packages/list/src/tables.vue
  51. 1225 0
      packages/list/src/tool-list.js
  52. 240 0
      packages/list/src/tool-table.js
  53. 8 0
      packages/loading/index.js
  54. 890 0
      packages/loading/src/main.vue
  55. 8 0
      packages/map/index.js
  56. 173 0
      packages/map/src/main.vue
  57. 8 0
      packages/re-form/index.js
  58. 9 0
      packages/row/index.js
  59. 48 0
      packages/row/src/main.js
  60. 2 0
      packages/toast/index.js
  61. 60 0
      packages/toast/src/toast.js
  62. 2060 0
      packages/toast/src/toast.vue
  63. 8 0
      packages/ueditor/index.js
  64. 66 0
      packages/ueditor/src/main.vue
  65. 17 0
      packages/ueditor/src/utils/Debounce.js
  66. 14 0
      packages/ueditor/src/utils/Event.js
  67. 273 0
      packages/ueditor/src/vueUeditorWrap.vue
  68. 1005 1
      readme.md
  69. 77 0
      src/index.js
  70. 85 0
      src/tools/directives.js
  71. 3 0
      src/tools/eventBus.js
  72. 47 0
      src/tools/request.js
  73. 850 0
      src/tools/utils.js
  74. 383 0
      src/tools/validate.js
  75. 49 0
      style/index.scss

+ 8 - 0
packages/card/index.js

@@ -0,0 +1,8 @@
+import NtCard from './src/main';
+
+/* istanbul ignore next */
+NtCard.install = function(Vue) {
+  Vue.component(NtCard.name, NtCard);
+};
+
+export default NtCard;

+ 198 - 0
packages/card/src/main.vue

@@ -0,0 +1,198 @@
+<template>
+  <div :class="[mode, myClass]">
+    <nt-loading style="flex: 1;" v-if="!data" :type="'05'"/>
+    <div v-else v-for="(item, index) in options" :key="index" :class="item.class" :style="item.style">
+      <div v-for="(node, nodeIndex) in item.list" :key="nodeIndex" v-if="!(isNotShow && node.isNotShow)">
+        <div v-if="node.type === 1" :style="node.style" :class="node.class">
+          <img :src="node.key ? data[node.key] : node.value" alt="">
+        </div>
+        <div v-else-if="node.type === 2" class="text-space-nowrap" :style="node.style" :class="node.class" v-html="node.key ? (data[node.key] || node.value) : node.value"></div>
+        <div v-else-if="node.type === 3" class="text-space-nowrap" :style="node.style" :class="node.class">{{node.curr}}{{ node.key ? (data[node.key] || node.value) : node.value }}</div>
+        <div v-else-if="node.type === 4" class="text-space-nowrap" :style="node.style" :class="[node.class, rateBGColor(node)]">{{ rateValue(node) }}</div>
+        <div v-else-if="node.type === 5" class="text-space-nowrap" :style="node.style" :class="node.class">
+          <img :src="node.icon" class="icon-image" alt="">{{ node.key ? (data[node.key] || node.value) : node.value }}
+        </div>
+        <div v-else :style="node.style" class="text-space-nowrap" :class="node.class">{{ node.key ? (data[node.key] || node.value) : node.value }}</div>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+import NtLoading from '@/components/thrid/em-element-ui/packages/loading'
+export default {
+  name: 'NtCard',
+  components: { NtLoading },
+  data: function () {
+    return {}
+  },
+  watch: {},
+  props: {
+    data: {
+      required: true,
+      type: Object | null | undefined,
+      default: () => {}
+    },
+    options: {
+      required: true,
+      type: Array,
+      default: () => []
+    },
+    mode: {
+      type: String,
+      default: 'horizontal'
+    },
+    myClass: {
+      type: String,
+      default: ''
+    },
+    isNotShow: {
+      type: Boolean,
+      default: false
+    }
+  },
+  methods: {
+    rateBGColor(node) {
+      const val = node.key ? parseFloat(this.$props.data[node.key]) : parseFloat(node.value)
+
+      if (val > 0) {
+        return 'up'
+      } else if (val < 0) {
+        return 'down'
+      }
+    },
+    rateValue(node) {
+      let sign = ''
+      const val = node.key ? parseFloat(this.$props.data[node.key]) : parseFloat(node.value)
+
+      if (val > 0) {
+        sign = '↗'
+      } else if (val < 0) {
+        sign = '↘'
+      } else {
+        return 0
+      }
+
+      return sign + Math.abs(val) + (node.sign || '')
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+  .horizontal {
+    display: flex;
+    flex-direction: row;
+  }
+
+  .title__size {
+    font-size: 1.6rem;
+  }
+
+  .big__size {
+    font-size: 4.2rem;
+  }
+
+  .small__size {
+    font-size: 1.4rem;
+  }
+
+  .text__color {
+    color: #14121F;
+  }
+
+  .text__size {
+    font-size: 3.4rem;
+  }
+
+  .font-weight__bold {
+    font-weight: bold;
+  }
+
+  .font-family__fb {
+    font-family: AGENCYFB;
+  }
+
+  .font-family__fbo {
+    font-family: AGENCYFB_0;
+  }
+
+  .vertical {
+    display: flex;
+    flex-direction: column;
+  }
+
+  .align-items__center {
+    align-items: center;
+  }
+
+  .align-items__end {
+    align-items: flex-end;
+  }
+
+  .justify-content__center {
+    justify-content: center;
+  }
+
+  .justify-content__between {
+    justify-content: space-between;
+  }
+
+  .card-line {
+    display: flex;
+  }
+
+  .card-images {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    margin-right: 1.3rem;
+    width: 6rem;
+    height: 6rem;
+    border-radius: 50%;
+    background: #5b8ff9;
+    img {
+      width: 3.4rem;
+    }
+  }
+
+  .history-info__text-8 {
+    color: rgba(20, 18, 31, .8)
+  }
+
+  .history-info__text {
+    color: rgba(20, 18, 31, .5)
+  }
+
+  .icon-image {
+    height: 3rem;
+    margin-right: 10px;
+  }
+
+  .order__info {
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+  }
+
+  .rate {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    background: #C2C4D1;
+    padding: 0 5px;
+    height: 1.8rem;
+    width: fit-content;
+    font-size: 1.4rem;
+    color: #fff;
+    &.up {
+      background-color: #E86452;
+    }
+    &.down {
+      background-color: #41CC7C;
+    }
+  }
+  .text-space-nowrap {
+    white-space: nowrap;
+  }
+</style>

+ 8 - 0
packages/charts/index.js

@@ -0,0 +1,8 @@
+import NtCharts from './src/main';
+
+/* istanbul ignore next */
+NtCharts.install = function(Vue) {
+  Vue.component(NtCharts.name, NtCharts);
+};
+
+export default NtCharts;

+ 150 - 0
packages/charts/src/chart.js

@@ -0,0 +1,150 @@
+/*
+ *
+ * type: 1: color数组,2: objColor数组
+ * index对象的序号,循环获取值
+ *
+ * */
+export function getColorValue(type, index) {
+    let number = 15,
+        color = ['#39A1FF', '#FF5C5C', '#4ECB73', '#FBD437', '#F2627B', '#2db9e3', '#90e3aa', '#fd8c6c', '#f75881', '#d6479e', '#de78c7', '#e0aaf0', '#8b7ff2', '#6f5ee4', '#85afff'];
+
+    if (typeof (index) == 'number') {
+        return color[index % number];
+    } else {
+        return color;
+    }
+}
+
+//控制浏览器宽度变化
+export function chartsInitBAutoResize(obj) {
+    if (obj.autoResize) {
+        obj.__resizeHanlder = debounce(() => {
+            if (obj.elChart) {
+                obj.elChart.resize()
+            }
+        }, 100)
+        window.addEventListener('resize', obj.__resizeHanlder)
+    }
+
+    // 监听侧边栏的变化
+    if (document.getElementsByClassName('sidebar-container').length > 0) {
+        const sidebarElm = document.getElementsByClassName('sidebar-container')[0]
+        sidebarElm.addEventListener('transitionend', obj.__resizeHanlder)
+    }
+}
+
+export function chartsDestroyData(obj) {
+    if (!obj.elChart) {
+        return
+    }
+    if (obj.autoResize) {
+        window.removeEventListener('resize', obj.__resizeHanlder)
+    }
+
+    // 监听侧边栏的变化
+    if (document.getElementsByClassName('sidebar-container').length > 0) {
+        const sidebarElm = document.getElementsByClassName('sidebar-container')[0]
+        sidebarElm.removeEventListener('transitionend', obj.__resizeHanlder)
+    }
+
+    obj.elChart.dispose()
+    obj.elChart = null
+}
+
+export function utilInitChartData(options, data) {
+    let chart = {
+        params: {},
+        options: {},
+        chartData: {}
+    };
+
+    if (options.params) {
+        chart.params = options.params;
+    }
+
+    if (options.dropdown) {
+        chart.dropdown = options.dropdown;
+    }
+
+    if (options.x) {
+        chart.options.x = options.x;
+    }
+
+    if (options.y) {
+        chart.options.y = options.y;
+    }
+
+    if (options.name) {
+        chart.options.name = options.name;
+    }
+
+    if (options.size) {
+        chart.options.size = options.size;
+    }
+
+    if (options.isXMulti) {
+        chart.options.isXMulti = options.isXMulti;
+    }
+
+    if (options.type) {
+        chart.options.type = options.type;
+    }
+
+    if (options.scroll) {
+        chart.options.scroll = options.scroll;
+    }
+
+    if (options.adjust) {
+        chart.options.adjust = options.adjust;
+    }
+
+    if (options.legend || options.legend === false) {
+        chart.options.legend = options.legend;
+    }
+
+    if (options.title) {
+        chart.options.title = options.title;
+    }
+
+    if (options.axis || options.axis === false) {
+        chart.options.axis = options.axis;
+    }
+
+    if (options.tooltip) {
+        chart.options.tooltip = options.tooltip;
+    }
+
+    if (options.defs) {
+        chart.options.defs = options.defs;
+    }
+
+    if (options.guide) {
+        chart.options.guide = options.guide;
+    }
+
+    if (options.coord) {
+        chart.options.coord = options.coord;
+    }
+
+    if (typeof (data) == 'undefined') {
+        chart.data = {
+            status: 0,
+            series: []
+        };
+    } else {
+        if (data.length > 0) {
+            chart.data = {
+                status: 2,
+                series: data
+            };
+        } else {
+            chart.data = {
+                status: 1,
+                series: [],
+                message: '暂无数据'
+            };
+        }
+    }
+
+    return chart;
+}

+ 583 - 0
packages/charts/src/echart.js

@@ -0,0 +1,583 @@
+import echarts from 'echarts';
+import { objectMerge } from '@/components/thrid/em-element-ui/src/tools/utils'
+
+export function formatLegendData(params) {
+    let result = '',
+        number = 4,
+        name = params.name.toString(),
+        str = name.replace(/^\s+|\s+$/g, ''),
+        len = str.length;
+
+    number = len > number ? Math.ceil(len / 2) : number;
+    for (let i = 0; i < len; i++) {
+        if (i > 0 && i < len && (i % number) == 0) {
+            result += '\n';
+        }
+        result += str.charAt(i);
+    }
+    //str.replace(/(?=(?:.{4})+$)/g, '\n');
+
+    return result;
+}
+
+export function debounce(func, wait, immediate) {
+    let timeout, args, context, timestamp, result
+
+    const later = function () {
+        // 据上一次触发时间间隔
+        const last = +new Date() - timestamp
+
+        // 上次被包装函数被调用时间间隔last小于设定时间间隔wait
+        if (last < wait && last > 0) {
+            timeout = setTimeout(later, wait - last)
+        } else {
+            timeout = null
+            // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
+            if (!immediate) {
+                result = func.apply(context, args)
+                if (!timeout) context = args = null
+            }
+        }
+    }
+
+    return function (...args) {
+        context = this
+        timestamp = +new Date()
+        const callNow = immediate && !timeout
+        // 如果延时不存在,重新设定延时
+        if (!timeout) timeout = setTimeout(later, wait)
+        if (callNow) {
+            result = func.apply(context, args)
+            context = args = null
+        }
+
+        return result
+    }
+}
+
+//控制浏览器宽度变化
+export function chartsInitBAutoResize(obj) {
+    if (obj.autoResize) {
+        obj.__resizeHanlder = debounce(() => {
+            if (obj.chart) {
+                obj.chart.resize()
+            }
+        }, 100)
+        window.addEventListener('resize', obj.__resizeHanlder)
+    }
+
+    // 监听侧边栏的变化
+    if (document.getElementsByClassName('sidebar-container').length > 0) {
+        const sidebarElm = document.getElementsByClassName('sidebar-container')[0]
+        sidebarElm.addEventListener('transitionend', obj.__resizeHanlder)
+    }
+}
+
+export function chartsDestroyData(obj) {
+    if (!obj.chart) {
+        return
+    }
+    if (obj.autoResize) {
+        window.removeEventListener('resize', obj.__resizeHanlder)
+    }
+
+    // 监听侧边栏的变化
+    if (document.getElementsByClassName('sidebar-container').length > 0) {
+        const sidebarElm = document.getElementsByClassName('sidebar-container')[0]
+        sidebarElm.removeEventListener('transitionend', obj.__resizeHanlder)
+    }
+
+    obj.chart.dispose()
+    obj.chart = null
+}
+
+/*
+*
+* type: 1: color数组,2: objColor数组
+* index对象的序号,循环获取值
+*
+* */
+export function getColorValue(type, index) {
+    let number = 15,
+        objColor = [{
+            normal: ['#39A1FF', '#5AB1FF'],
+            emphasis: ['#5AB1FF', '#39A1FF']
+        }, {
+            normal: ['#5CDBD3', '#61EBE2'],
+            emphasis: ['#61EBE2', '#5CDBD3']
+        }, {
+            normal: ['#4ECB73', '#51CB92'],
+            emphasis: ['#51CB92', '#4ECB73']
+        }, {
+            normal: ['#FBD437', '#C1C417'],
+            emphasis: ['#C1C417', '#FBD437']
+        }],
+        color = ['#5B8FF9', '#F6BD16', '#E86452', '#41CC7C', '#F2627B', '#2db9e3', '#90e3aa', '#fd8c6c', '#f75881', '#d6479e', '#de78c7', '#e0aaf0', '#8b7ff2', '#6f5ee4', '#85afff'];
+
+    if (type == 1) {
+        if (typeof (index) == 'number') {
+            return color[index % number];
+        } else {
+            return color;
+        }
+    } else {
+        return objColor;
+    }
+}
+
+//页面data初始化
+export function chartsInitData() {
+    let options = {},
+        option = {
+            chart: null,
+            barGap: '9%',
+            barWidth: '10',
+            scrollDataIndex: 0,
+            legrendUnSelect: true,
+            color: getColorValue(1),
+            itemColorArr: getColorValue(2)
+        };
+
+    Object.assign(options, option);
+
+    if (arguments.length > 1) {
+        Object.assign(options, arguments[1]())
+    }
+
+    return options;
+}
+
+//option设置
+export function chartsOptionGrid() {
+    return {
+        left: '10%',
+        right: '10%',
+        top: '8%',
+        bottom: '22%'
+    };
+}
+
+//init merge data
+function getObjectValue(currObj, paramObj) {
+    let result = Object.create(currObj ? currObj : null);
+
+    if (currObj) {
+        objectMerge(result, paramObj);
+
+        return result;
+    } else {
+        return paramObj;
+    }
+}
+
+function initChartsTitle(option, obj) {
+    let title = obj.options.title;
+
+    if (title) {
+        option.title = getObjectValue(option.title, title);
+    }
+}
+
+function initChartsColorArray(option, obj) {
+    let color = obj.options.color;
+
+    if (color) {
+        option.color = getObjectValue(option.color, color);
+    }
+}
+
+function initChartsToolbox(option, obj) {
+    let toolbox = obj.options.toolbox;
+
+    if (toolbox) {
+        option.toolbox = getObjectValue(option.toolbox, toolbox);
+    }
+}
+
+function initChartsTooltip(option, obj) {
+    let tooltip = obj.options.tooltip;
+
+    if (tooltip) {
+        option.tooltip = getObjectValue(option.tooltip, tooltip);
+    }
+}
+
+function initChartsGrid(option, obj) {
+    let grid = obj.options.grid;
+
+    if (grid) {
+        option.grid = getObjectValue(option.grid, grid);
+    } else {
+        option.grid = chartsOptionGrid();
+    }
+}
+
+function initChartsLegend(option, obj) {
+    let legend = obj.options.legend;
+
+    if (legend) {
+        option.legend = getObjectValue(option.legend, legend);
+    }
+}
+
+function initChartsLegendData(option, obj) {
+    let legendData = obj.options.legendData;
+
+    if (legendData) {
+        option.legendData = getObjectValue(option.legendData, legendData);
+    }
+}
+
+function initChartsxAxis(option, obj) {
+    if (obj.options.xAxisDel) return false;
+
+    let xAxis = obj.options.xAxis;
+
+    if (xAxis) {
+        option.xAxis = getObjectValue(option.xAxis, xAxis);
+    }
+}
+
+function initChartsyAxis(option, obj) {
+    if (obj.options.yAxisDel) return false;
+
+    let yAxis = obj.options.yAxis;
+
+    if (yAxis) {
+        option.yAxis = getObjectValue(option.yAxis, yAxis);
+    }
+}
+
+function initChartsDataZoom(option, obj) {
+    if (obj.options.dataZoomDel) return false;
+
+    let dataZoom = obj.options.dataZoom;
+
+    if (dataZoom) {
+        option.dataZoom = getObjectValue(option.dataZoom, [{
+                type: 'slider',//图表下方的伸缩条
+                show: false,  //是否显示
+                realtime: true,  //
+                start: 0,  //伸缩条开始位置(1-100),可以随时更改
+                end: 100,  //伸缩条结束位置(1-100),可以随时更改
+                xAxisIndex: [0],
+                filterMode: 'none'
+            }]);
+    }
+}
+
+function initChartsAxisPointer(option, obj) {
+    if (obj.options.axisPointerDel) return false;
+    let axisPointer = obj.options.axisPointer
+    if (axisPointer) {
+        option.axisPointer = getObjectValue(option.axisPointer, axisPointer);
+    }
+}
+
+function getSeriesEmphasis(obj, item, index) {
+    let result = {};
+
+    return result;
+}
+
+function getSeriesAreaStyle(item) {
+    let result = {};
+
+    if (item.areaColor && Array.isArray(item.areaColor)) {
+        result = {
+            normal: {
+                color: new echarts.graphic.LinearGradient(
+                    0, 0, 0, 1,
+                    [
+                        { offset: 0, color: item.areaColor[0] },
+                        { offset: 0.5, color: item.areaColor[1] },
+                        { offset: 1, color: item.areaColor[2] }
+                    ]
+                )
+            }
+        }
+    }
+
+    return result;
+}
+
+function getSeriesItemStyle(obj, item, index) {
+    let result = {},
+        type = item.type ? item.type : obj.type;
+
+    if (item.gradual) {
+        let len = obj.itemColorArr.length,
+            color = obj.itemColorArr[index % len];
+
+        result = {
+            normal: {
+                color: new echarts.graphic.LinearGradient(
+                    0, 0, 0, 1,
+                    [
+                        { offset: 0, color: color.normal[0] },
+                        { offset: 0.5, color: color.normal[1] },
+                        { offset: 1, color: color.normal[1] }
+                    ]
+                )
+            },
+            emphasis: {
+                color: new echarts.graphic.LinearGradient(
+                    0, 0, 0, 1,
+                    [
+                        { offset: 0, color: color.emphasis[0] },
+                        { offset: 0.7, color: color.emphasis[0] },
+                        { offset: 1, color: color.emphasis[1] }
+                    ]
+                )
+            }
+        };
+    } else {
+        if (type == 'pie') {
+            result = {
+                normal: {
+                    color: function (params) {
+                        return obj.color[params.dataIndex]
+                    },
+                    label: {
+                        show: true,
+                        //position: 'inner',
+                        formatter: function (a, b, c, d) {
+                            return c;
+                        }
+                    }
+                }
+            };
+        } else {
+            result = {
+                normal: {
+                    color: item.color ? item.color : obj.color[index],
+                    borderColor: item.borderColor ? item.borderColor : obj.color[index],
+                    shadowOffsetX: item.shadowOffsetX ? item.shadowOffsetX : 0,
+                    opacity: item.opacity ? item.opacity : 1,
+                    lineStyle: {
+                        type: item.borderType ? item.borderType : 'solid'  //'dotted'虚线 'solid'实线
+                    }
+                }
+            };
+        }
+    }
+
+    return result;
+}
+
+function getSeriesData(obj) {
+    let result = [],
+        series = obj.chartData.series;
+
+    series.forEach((item, index) => {
+        let serData = Object.assign({}, item),
+            type = item.type ? item.type : obj.type;
+
+        //series必须的值
+        serData.type = type;
+        serData.name = item.name || 'null';
+        serData.data = item.data;
+        serData.label = item.label || { show: false }
+        serData.itemStyle = item.itemStyle || getSeriesItemStyle(obj, item, index);
+        serData.emphasis = item.emphasis || getSeriesEmphasis(obj, item, index);
+        serData.animationEasing = item.animationEasing || 'cubicInOut';
+
+        if (item.yAxisIndex) {
+            serData.yAxisIndex = item.yAxisIndex;
+        }
+
+        if (item.stack) {
+            serData.stack = item.stack;
+        }
+
+        if (item.markLine) {
+            serData.markLine = item.markLine;
+        }
+
+        switch (type) {
+            case 'bar':
+                serData.animationDuration = 800;
+                serData.barGap = item.barGap || obj.barGap;
+                serData.barWidth = item.barWidth || obj.barWidth;
+                serData.showBackground = item.showBackground || false
+                serData.backgroundStyle = item.backgroundStyle || {
+                    color: '#5D7092',
+                    opacity: 0.1
+                }
+                break;
+            case 'line':
+                serData.symbol = item.symbol || 'none'
+                serData.showSymbol = item.showSymbol || false
+                serData.symbolSize = item.symbolSize || 4
+                serData.lineStyle = {}
+                serData.animation = false;
+                serData.animationDuration = 1500;
+                serData.areaStyle = item.areaStyle || getSeriesAreaStyle(item);
+                serData.lineStyle.width = (item.lineStyle && item.lineStyle.width) || 2
+                break;
+            case 'pie':
+                serData.smooth = item.smooth || true;
+                serData.radius = item.radius || 40;
+                serData.center = item.center || ['30%', '50%'];
+                serData.animationDuration = 800;
+                serData.label = item.label || {
+                    normal: {
+                        formatter: function (params) {
+                            return params.percent + '%';
+                        }
+                    }
+                };
+                serData.labelLine = item.labelLine || {
+                    normal: {
+                        smooth: 0.4,
+                        length: 2,
+                        length2: 20,
+                    }
+                };
+        }
+
+        result.push(serData)
+        // 立体效果
+        if (item.stereoscopic) {
+            result.push(...stereoscopicCharts(serData))
+        }
+        // 是否显示阴影背景
+        if (item.ishadow) {
+            result.push(...shadowCharts(serData))
+        }
+    });
+
+    return result;
+}
+// 立体效果
+function stereoscopicCharts(item) {
+    const data = []
+    const minList = []
+    const barMaxWidth = Math.round(item.barWidth * 2 / 3)
+
+    item.data.forEach(num => {
+        minList.push(1)
+    })
+
+    data.push({
+        data: minList,
+        symbol: 'diamond',
+        type: 'pictorialBar',
+        symbolOffset: [0, '-50%'],
+        symbolSize: [item.barWidth, Math.round(item.barWidth / 2)],
+        barMaxWidth: barMaxWidth
+    })
+    data.push({
+        data: item.data,
+        symbol: 'diamond',
+        symbolPosition: 'end',
+        type: 'pictorialBar',
+        symbolOffset: [0, '-50%'],
+        symbolSize: [item.barWidth, Math.round(item.barWidth / 2) - 2],
+        barMaxWidth: barMaxWidth,
+        zlevel: 2
+    })
+
+    return data
+}
+// 是否显示阴影背景
+function shadowCharts(item) {
+    const data = []
+    const maxData = Math.max(...item.data)
+    const maxList = []
+    const minList = []
+    const barMaxWidth = Math.round(item.barWidth * 2 / 3)
+
+    item.data.forEach(num => {
+        minList.push(1)
+        maxList.push(maxData)
+    })
+
+    data.push({
+        data: maxList,
+        type: 'bar',
+        barGap: '-100%',
+        barWidth: item.barWidth,
+        barMaxWidth: 'auto',
+        zlevel: -1
+    })
+    data.push({
+        data: minList,
+        symbol: 'diamond',
+        type: 'pictorialBar',
+        symbolOffset: [0, '-50%'],
+        symbolSize: [item.barWidth, Math.round(item.barWidth / 2)],
+        barMaxWidth: barMaxWidth,
+        zlevel: -2
+    })
+
+    data.push({
+        data: maxList,
+        symbol: 'diamond',
+        symbolPosition: 'end',
+        type: 'pictorialBar',
+        symbolOffset: [0, '-50%'],
+        symbolSize: [item.barWidth, Math.round(item.barWidth / 2) - 2],
+        barMaxWidth: barMaxWidth,
+        zlevel: -1
+    })
+
+    return data
+}
+
+function initChartsSeries(option, obj) {
+    if (obj.chartData.series) {
+        option.series = getSeriesData(obj);
+    }
+}
+
+export function mergeChartsOptions(obj) {
+    let options = {};
+
+    initChartsTitle(options, obj);//初始化title
+    initChartsColorArray(options, obj);//初始化title
+    initChartsToolbox(options, obj);//初始化toolbox
+    initChartsTooltip(options, obj);//初始化tooltip
+    initChartsGrid(options, obj);//初始化图上下左右的空白
+    initChartsAxisPointer(options, obj); //初始化坐标轴指示器
+    initChartsLegend(options, obj);//实例初始化
+    initChartsLegendData(options, obj);//实例初始化
+    initChartsxAxis(options, obj);//x坐标轴
+    initChartsyAxis(options, obj);//y坐标轴
+    initChartsDataZoom(options, obj);//坐标轴
+    initChartsSeries(options, obj);//数值初始化
+
+    return options;
+}
+
+export function legendSelecthanged(obj, option) {
+    if (!option.legendData) return;
+    obj.chart.on('legendselectchanged', function (params) {
+        let num = 0,
+            name = params.name;
+
+        option.series[num].data.forEach((item, index) => {
+            let value = 0;
+            option.legendData[index].forEach(legend => {
+                if (name == legend.subjectName) {
+                    value = Number(legend.fee);
+                }
+            });
+            if (params.selected[name]) {
+                option.series[num].data[index] = item + value;
+            } else {
+                option.series[num].data[index] = item - value;
+            }
+        });
+
+        obj.chart.setOption(option);
+    });
+}
+
+export function drawCharts(obj) {
+    let option = mergeChartsOptions(obj);
+
+    obj.chart.setOption(option);
+
+    legendSelecthanged(obj, option);//图例选择改变事件
+}

+ 114 - 0
packages/charts/src/f2ExtendCharts.vue

@@ -0,0 +1,114 @@
+<template>
+  <div class="nt-f2-charts">
+    <div v-if="chart.chartOptions.title" class="nt-f2-header" :class="chart.chartOptions.title.line ? 'line-flex': ''">
+      <div
+        v-if="chart.chartOptions.title.text"
+        class="title"
+        :class="chart.chartOptions.title.px == 'center' ? 'center' : (chart.chartOptions.title.px == 'left' ? 'left' : 'right')"
+      >
+        <div class="title__icon">
+          <span v-if="chart.chartOptions.title.textIcon" :class="chart.chartOptions.title.textIcon"></span>
+          <span :class="chart.chartOptions.title.textStyle">{{chart.chartOptions.title.text}}</span>
+        </div>
+        <div v-if="chart.chartOptions.dropdown" class="nt-dropdown-menu">
+          <van-dropdown-menu>
+            <van-dropdown-item
+              @change="dropdownChange()"
+              v-model="chart.chartOptions.dropdown.valueDef"
+              :options="chart.chartOptions.dropdown.list"
+            />
+          </van-dropdown-menu>
+        </div>
+      </div>
+      <div
+        v-if="chart.chartOptions.title.subtext"
+        class="sub-title"
+        :class="chart.chartOptions.title.px == 'center' ? 'center' : (chart.chartOptions.title.px == 'left' ? 'left' : 'right')"
+      >{{chart.chartOptions.title.subtext}}</div>
+    </div>
+    <div class="f2-chart-wrapper" :class="[chart.chartOptions.class, chart.chartOptions.id]">
+      <canvas :id="chart.chartOptions.id"></canvas>
+    </div>
+  </div>
+</template>
+
+<script>
+import F2 from "@antv/f2/lib/index-all";
+import { chartsInitData, drawCharts } from "./f2chart";
+
+export default {
+  name: "F2",
+  props: {},
+  inject: ['chart'],
+  data() {
+    return chartsInitData();
+  },
+  watch: {
+    chartData: {
+      deep: true,
+      handler() {
+        this.initData();
+      }
+    }
+  },
+  created() {},
+  mounted() {
+    this.initData();
+  },
+  methods: {
+    initData() {
+      if (!this.elChart) {
+        let chartClass = "f2-chart-wrapper",
+          el = document.getElementsByClassName(chartClass)[0],
+          chartWidth = el.clientWidth || el.offsetWidth,
+          chartHeight = el.clientHeight || el.offsetHeight,
+          chartDom = document.getElementById(this.chart.chartOptions.id);
+
+        chartDom.addEventListener("touchstart", function(e) {
+          if (e.preventDefault) e.preventDefault();
+        });
+        this.elChart = new F2.Chart({
+          id: this.chart.chartOptions.id,
+          width: chartWidth,
+          height: chartHeight,
+          appendPadding: [10, 0, 20, 0],
+          pixelRatio: window.devicePixelRatio
+        });
+      }
+
+      drawCharts(this);
+    },
+    dropdownChange() {
+      let selected = null,
+        dList = this.dropdown.list;
+
+      for (let i = 0; i < dList.length; i++) {
+        if (dList[i].value == this.dropdown.valueDef) {
+          selected = dList[i];
+        }
+      }
+
+      if (selected && this.chart.chartOptions.hasOwnProperty("series")) {
+        let list = [];
+
+        this.chart.chartOptions.series.forEach(item => {
+          if (selected.select.indexOf(item[selected.name]) !== -1) {
+            list.push(item);
+          }
+        });
+
+        this.downList = list;
+
+        this.elChart.clear();
+        drawCharts(this);
+      }
+    }
+  }
+};
+</script>
+<style lang="scss">
+    .f2-chart-wrapper, .nt-f2-charts{
+        height: 260px;
+        width: 100%;
+    }
+</style>

+ 576 - 0
packages/charts/src/f2chart.js

@@ -0,0 +1,576 @@
+import { getColorValue } from './chart';
+import { isTypeof, objectMerge } from '@/components/thrid/em-element-ui/src/tools/utils';
+
+export function formatLegendData(params) {
+    let result = '',
+        number = 4,
+        name = params.name.toString(),
+        str = name.replace(/^\s+|\s+$/g, ''),
+        len = str.length;
+
+    number = len > number ? Math.ceil(len / 2) : number;
+    for (let i = 0; i < len; i++) {
+        if (i > 0 && i < len && (i % number) == 0) {
+            result += '\n';
+        }
+        result += str.charAt(i);
+    }
+    //str.replace(/(?=(?:.{4})+$)/g, '\n');
+
+    return result;
+}
+
+export function debounce(func, wait, immediate) {
+    let timeout, args, context, timestamp, result
+
+    const later = function () {
+        // 据上一次触发时间间隔
+        const last = +new Date() - timestamp
+
+        // 上次被包装函数被调用时间间隔last小于设定时间间隔wait
+        if (last < wait && last > 0) {
+            timeout = setTimeout(later, wait - last)
+        } else {
+            timeout = null
+            // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
+            if (!immediate) {
+                result = func.apply(context, args)
+                if (!timeout) context = args = null
+            }
+        }
+    }
+
+    return function (...args) {
+        context = this
+        timestamp = +new Date()
+        const callNow = immediate && !timeout
+        // 如果延时不存在,重新设定延时
+        if (!timeout) timeout = setTimeout(later, wait)
+        if (callNow) {
+            result = func.apply(context, args)
+            context = args = null
+        }
+
+        return result
+    }
+}
+
+//页面data初始化
+export function chartsInitData(cls) {
+    let options = {},
+        option = {
+            elChart: null, //chart对象
+            chartOptions: { //画图使用的默认值
+                options: {
+                    type: 'line', //chart类型,默认为line
+                    x: 'name',
+                    y: 'value',
+                    tickCount: 5,
+                    scrollNum: 6,
+                    ScrollColor: 'F1F2F6',
+                    tooltip: {
+                        showCrosshairs: true
+                    },
+                    size: 2,
+                    adjust: '',
+                    shape: ''
+                }
+            },
+            color: getColorValue(1), //chart颜色值
+        };
+
+    Object.assign(options, option);
+
+    if (arguments.length > 1) {
+        options = objectMerge(options, arguments[1]());
+    }
+
+    return options;
+}
+
+function isChartScrollBar(_this, chart) {
+    let option = chart.options;
+
+    if (!option.scroll) return false;
+    if (option.type == 'pie' || (!option.type && option.axis === false)) return false;
+    //是否显示scrollBar
+    let names = [],
+        xname = option.x,
+        series = chart.data.series,
+        scrollMax = option.scroll && option.scroll['number'] ? option.scroll['number'] : option.scrollNum,
+        scrollColor = option.scroll && option.scroll['color'] ? option.scroll['color'] : option.scrollColor;
+
+    //option.adjust = '';//滚动不支持adjust
+    //name重新组合,并去重
+    series.forEach(item => {
+        names.push(item[xname]);
+    });
+    names = names.filter((item, index) => names.indexOf(item) === index);
+
+    if (names.length > scrollMax) {
+        names = names.filter((item, index) => index < scrollMax);
+        option.scrollBar = {
+            mode: 'x',
+            xStyle: {
+                backgroundColor: scrollColor,
+                fillerColor: scrollColor,
+                offsetY: -5
+            }
+        };
+
+        let valueArea = {
+            values: names
+        };
+        sourceDefs(option, xname, valueArea);
+        /*{
+            min: 0,
+            max: scrollMax
+        };type:line*/
+    }
+}
+
+function sourceDefs(option, key, value) {
+    if(option.defs){
+        option.defs = option.defs.filter(item => item.key!==key);
+    }else{
+        option.defs = [];
+    }
+
+    option.defs.push({
+        key: key,
+        value: value
+    });
+}
+
+//init merge data
+function getObjectValue(currObj, paramObj) {
+    let result = Object.create(currObj ? currObj : null);
+
+    if (currObj) {
+        result = objectMerge(result, paramObj);
+
+        return result;
+    } else {
+        return paramObj;
+    }
+}
+
+function initDefSource(_this, chart) {
+    let flagX = false,
+        data = chart.downList ? chart.downList : chart.data.series,
+        defs = chart.options.defs || [],
+        currDef = {};
+
+    if (Array.isArray(defs)) {
+        defs.forEach(item => {
+            if (chart.options.x == item.key) {
+                flagX = true;
+            }
+
+            currDef[item.key] = item.value;
+        });
+    }
+
+    //如果没有y轴校验,增加默认
+    if (!currDef[chart.options.y]){
+        currDef[chart.options.y] = {
+            tickCount: 5
+        }
+    }
+
+    _this.elChart.source(data, currDef);
+}
+
+function initDefScale(_this, chart) {
+    let scale = chart.options.scale;
+
+    if (Array.isArray(scale)) {
+        scale.forEach(item => {
+            _this.elChart.scale(item.key, item.value);
+        });
+    }
+}
+
+function initPieLabel(_this, chart) {
+    let pieLabel = chart.options.pieLabel;
+
+    if (pieLabel) {
+        _this.elChart.pieLabel(pieLabel)
+    }
+}
+
+function initReloadShape(_this, chart){
+    let draw = chart.options.draw;
+    // 绘制内阴影
+    if(draw && draw.iShadow){
+        const frontPlot = _this.elChart.get('frontPlot');
+        const coord = _this.elChart.get('coord'); // 获取坐标系对象
+        frontPlot.addShape('sector', {
+            attrs: {
+                x: coord.center.x,
+                y: coord.center.y,
+                r: coord.circleRadius * coord.innerRadius * 1.2, // 全半径
+                r0: coord.circleRadius * coord.innerRadius,
+                fill: '#000',
+                opacity: 0.15
+            }
+        });
+        _this.elChart.get('canvas').draw();
+    }
+}
+
+function initDefCoord(_this, chart) {
+    let coord = chart.options.coord;
+
+    if (Array.isArray(coord)) {
+        coord.forEach(item => {
+            if (item.value) {
+                _this.elChart.coord(item.key, item.value);
+            } else {
+                _this.elChart.coord(item.key);
+            }
+        });
+    }
+}
+
+function axisTextLabels(text, index, total){
+    const textCfg = {};
+    /*if (index === 0) {
+        textCfg.textAlign = 'start';
+    } else if (index === total - 1) {
+        textCfg.textAlign = 'right';
+    }*/
+
+    return textCfg;
+}
+
+function initDefAxis(_this, chart) {
+    let axis = chart.options.axis;
+
+    if (isTypeof(axis) === 'array') {
+        axis.forEach(item => {
+            _this.elChart.axis(item.key, item.value ? item.value : {
+                label(text, index, total){
+                    return chart.options.scroll ? {} : axisTextLabels(text, index, total);
+                }
+            });
+        });
+    } else if (isTypeof(axis) === 'object'){
+        _this.elChart.axis(axis.key, {
+            label: function(text, index, total){
+                let number = axis.number ? axis.number : 1,
+                    textCfg = chart.options.scroll ? {} : axisTextLabels(text, index, total);
+
+                if(text < chart.data.series.length) {
+                    if(axis.mapping) textCfg.text = chart.data.series[parseInt(text)][axis.mapping];
+
+                    return textCfg;
+                }
+            }
+        });
+    } else if (isTypeof(axis) === 'string'){
+        _this.elChart.axis(axis, {
+            label: function(text, index, total){
+                return chart.options.scroll ? {} : axisTextLabels(text, index, total);
+            }
+        });
+    } else if (axis === false) {
+        _this.elChart.axis(axis);
+    }
+}
+
+function initDefLegend(_this, chart) {
+    let legend = chart.options.legend;
+
+    if (Array.isArray(legend)) {
+        legend.forEach(item => {
+            if(item.key){
+                _this.elChart.legend(item.key, item.value);
+            }else{
+                _this.elChart.legend(item);
+            }
+        });
+    } else {
+        _this.elChart.legend(legend);
+    }
+}
+
+function initDefTooltip(_this, chart, option) {
+    let tooltip = chart.options.tooltip;
+
+    if (tooltip) {
+        tooltip = getObjectValue(option.tooltip, tooltip);
+    }
+
+    _this.elChart.tooltip(tooltip);
+}
+
+function initDefScrollBar(_this, chart, option) {
+    let scrollBar = chart.options.scrollBar;
+
+    if (scrollBar) {
+        scrollBar = getObjectValue(option.scrollBar, scrollBar);
+    }
+
+    _this.elChart.scrollBar(scrollBar);
+}
+
+function initDefGuide(_this, chart) {
+    let guide = chart.options.guide;
+
+    if (Array.isArray(guide)) {
+        guide.forEach(item => {
+            _this.elChart.guide().text(item);
+        });
+    }
+}
+
+function initMarkTag(_this, chart) {
+    let mark = chart.options.mark;
+
+    if (mark && mark.isTrue) {
+        let number = mark.number,
+            name = mark.name;
+
+        // 绘制辅助线
+        _this.elChart.guide().line({
+            start: function start(xScale, yScales) {
+                let price = yScales[0].max * number / yScales[1].max;
+
+                return ['min', price];
+            },
+            end: function end(xScale, yScales) {
+                let price = yScales[0].max * number / yScales[1].max;
+
+                return ['max', price];
+            },
+            style: {
+                stroke: 'red', // 线的颜色
+                lineDash: [0, 2, 2], // 虚线的设置
+                lineWidth: 1 // 线的宽度
+                // 图形样式配置
+            }
+        });
+        _this.elChart.guide().text({
+            position: function position(xScale, yScales) {
+                let price = yScales[0].max * number / yScales[1].max;
+
+                return ['max', price];
+            },
+            content: name + ':' + number,
+            style: {
+                textAlign: 'end',
+                textBaseline: 'bottom',
+                fill: 'red'
+            },
+            offsetY: -5
+        });
+    }
+}
+
+function panEventProcessReload(_this, chart) {
+    let yValues = [],
+        option = chart.options,
+        xScale = _this.elChart.getXScale(),
+        source = _this.elChart.get('data');
+
+    source.map(obj => {
+    if (xScale.translate(obj.year) >= 0) {
+        yValues.push(obj.sales);
+    }
+    });
+
+    sourceDefs(option, option.x, {values: xScale.values});//x轴
+    sourceDefs(option, option.y, {
+        min: 0,
+        tickCount: 5,
+        max: Math.max.apply(null, yValues)
+    });//y轴
+    initDefSource(_this, chart);
+
+    _this.elChart.changeData(source);
+}
+
+function panEventEndReload(_this, chart) {
+    _this.elChart.repaint();
+}
+
+function initDefRender(_this, chart) {
+    let axis = chart.options.axis;
+
+    if (isTypeof(axis) === 'array') {
+        axis.forEach(item => {
+            if (item.position) {
+                drawChartType(_this, chart, item);
+            }
+        });
+    } else {
+        drawChartType(_this, chart);
+    }
+
+    if(chart.options.scroll){
+        _this.elChart.interaction('pan', {
+            mode: 'x',
+            onProcess: () => {
+                panEventProcessReload(_this, chart);
+            },
+            onEnd: () => {
+                panEventEndReload(_this, chart);
+            }
+        });
+    }
+
+    _this.elChart.render();
+}
+
+function drawChartTypeMap(type){
+    let map = {
+        path: '.path()',
+        point: '.point()',
+        line: '.line()',
+        area: '.area()',
+        interval: '.interval()',
+        polygon: '.polygon()',
+        schema: '.schema()'
+    };
+
+    return map[type] || false;
+}
+
+function chartColorValue(options){
+    if(options.mode && options.mode == 'more'){
+        return '.color(name, color)';
+    }else{
+        return '.color(color)';
+    }
+}
+
+function chartAdjustValue(options){
+    if (options.adjust) {
+        if(isTypeof(options.adjust) === 'object') return '.adjust(' + JSON.stringify(options.adjust) + ')';
+        else if(isTypeof(options.adjust) === 'string') return '.adjust("' + options.adjust + '")';
+    }
+
+    return '';
+}
+
+function chartSizeValue(options){
+    if(options.size) return '.size("' + options.size + '")';
+
+    return '';
+}
+
+function chartShapeValue(options){
+    if(options.shape) return '.shape("' + options.shape + '")';
+
+    return '';
+}
+
+function chartStyleValue(type, options){
+    if(options.style){
+        if(isTypeof(options.style) == 'string'){
+            return '.style("' + options.style + '")';
+        }else if(isTypeof(options.style) == 'object'){
+            if(options.style.type == type){
+                return '.style(' + JSON.stringify(options.style) + ')';
+            }
+        }else if(isTypeof(options.style) == 'array'){
+            options.style.forEach(item => {
+                if(item.type == type){
+                    return '.style(' + JSON.stringify(item) + ')';
+                }
+            });
+        }
+    }
+
+    return '';
+}
+
+function execDrawRender(type, options){
+    let execDrawChart = '_this.elChart',
+        execPostions = '.position(position)',//坐标
+        execType = drawChartTypeMap(type),//根据类型执行对应的函数
+        execColor = chartColorValue(options),
+        execAdjust = chartAdjustValue(options),
+        execSize = type === 'line' ? '' : chartSizeValue(options),
+        execStyle = chartStyleValue(type, options),
+        execShape = chartShapeValue(options),
+        animate = '.animate({update: {duration: 1000}})';
+
+    return execDrawChart + execType + execPostions + execColor + execAdjust + execShape + execSize + execStyle + animate;
+}
+
+function drawChartType(_this, chart, item) {
+    let options = chart.options,
+        color = options.color ? options.color : _this.color,
+        size = options.size ? options.size : '',
+        adjust = options.adjust ? options.adjust : '',
+        shape = options.shape ? options.shape : '',
+        name = options.name ? options.name : '',
+        key = options.x ? options.x : '',
+        value = options.y ? options.y : '',
+        position = key + '*' + value,
+        type = options.type ? options.type : '';
+
+    if (typeof (item) == 'object') {
+        type = item.type ? item.type : type;
+        value = item.position ? item.position : value;
+        position = key + '*' + value;
+    }
+
+    if(isTypeof(type) === 'string'){
+        eval('(' + execDrawRender(type, options) + ')');
+    }else if(isTypeof(type) === 'object'){
+        for(let [k, v] of Object.entries(type)){
+            if(v && drawChartTypeMap(k)) eval('(' + execDrawRender(k, options) + ')');
+        }
+    }
+}
+
+export function mergeChartsOptions(_this) {
+    let option = {},
+        chartOptions = objectMerge(_this.chartOptions, _this.chart.chartOptions);
+
+    isChartScrollBar(_this, chartOptions); //判断是否显示滚动条
+
+    initDefSource(_this, chartOptions);
+    initDefScale(_this, chartOptions);
+    initDefCoord(_this, chartOptions);
+    initDefAxis(_this, chartOptions);
+    initDefLegend(_this, chartOptions);
+    initDefTooltip(_this, chartOptions, option);
+    initDefScrollBar(_this, chartOptions, option);
+    initPieLabel(_this, chartOptions);
+    initDefGuide(_this, chartOptions);
+    initMarkTag(_this, chartOptions);
+    initDefRender(_this, chartOptions);
+    initReloadShape(_this, chartOptions);
+}
+
+export function legendSelecthanged(_this, option) {
+    if (!option.legendData) return;
+    _this.elChart.on('legendselectchanged', function (params) {
+        let num = 0,
+            name = params.name;
+
+        option.series[num].data.forEach((item, index) => {
+            let value = 0;
+            option.legendData[index].forEach(legend => {
+                if (name == legend.subjectName) {
+                    value = Number(legend.fee);
+                }
+            });
+            if (params.selected[name]) {
+                option.series[num].data[index] = item + value;
+            } else {
+                option.series[num].data[index] = item - value;
+            }
+        });
+
+        _this.elChart.setOption(option);
+    });
+}
+
+export function drawCharts(_this) {
+    mergeChartsOptions(_this);
+}

+ 3 - 0
packages/charts/src/index.js

@@ -0,0 +1,3 @@
+export { default as NtExtendCharts } from './ntExtendCharts'
+export { default as F2ExtendCharts } from './f2ExtendCharts'
+export { default as NtMapCharts } from './ntMapCharts'

+ 91 - 0
packages/charts/src/main.vue

@@ -0,0 +1,91 @@
+<template>
+    <div class="nt-charts" :class="{'van-charts-null': chartOptions.data.status != 2}">
+        <!--type有19中loading模式(01-19)-->
+        <nt-loading
+            :class="chartOptions.class"
+            v-if="chartOptions.data.status == 0"
+            :type="'05'"
+        />
+        <div v-else-if="chartOptions.data.status == 2" style="height: 100%;">
+            <div v-if="!!chartTitle" :style="titleStyle" :class="titleClass">{{chartTitle}}</div>
+            <f2-extend-charts v-if="webUIType == 'f2'" :chartData="chartOptions.data"></f2-extend-charts>
+	        <nt-map-charts v-else-if="webUIType == 'map'" :chartData="chartOptions.data"></nt-map-charts>
+            <nt-extend-charts v-else :options="chartOptions" :chartData="chartOptions.data" :className="className" :chartsId="chartsId"></nt-extend-charts>
+            <slot name="botton"></slot>
+        </div>
+        <div class="nt-message" :class="chartOptions.class" v-else>{{chartOptions.data.message || '暂无数据!'}}</div>
+        <slot name="legend"></slot>
+    </div>
+</template>
+
+<script>
+import NtLoading from "@/components/thrid/em-element-ui/packages/loading"
+import { NtExtendCharts, F2ExtendCharts, NtMapCharts } from "./index.js";
+
+export default {
+    name: "ntCharts",
+    components: { NtLoading, NtExtendCharts, F2ExtendCharts, NtMapCharts },
+    provide() {
+        return {
+            chart: this
+        }
+    },
+    props: {
+        className: {
+            type: String,
+            default: 'chart'
+        },
+        chartsId: {
+            type: String,
+            default: 'chart'
+        },
+        webUIType: {
+            type: String,
+            default: "f2"
+        },
+        chartTitle: {
+            type: String,
+            default: ''
+        },
+        titleStyle: {
+            type: String,
+            default: ''
+        },
+        titleClass: {
+            type: String,
+            default: ''
+        },
+        chartOptions: {
+            type: Object,
+            default: function() {
+                return {};
+            }
+        },
+        autoResize: {
+            type: Boolean,
+            default: true
+        },
+        dropdown: {
+            type: Object,
+            default: function() {
+                return {};
+            }
+        }
+    }
+};
+</script>
+<style lang="scss">
+    .nt-charts, .nt-f2-charts{
+        height: 100%;
+        width: 100%;
+    }
+    .nt-message{
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        height: inherit;
+        color: #333;
+        opacity: 0.5;
+        font-size: 1.6rem;
+    }
+</style>

+ 570 - 0
packages/charts/src/mapchart.js

@@ -0,0 +1,570 @@
+import echarts from 'echarts';
+import { objectMerge } from '@/components/thrid/em-element-ui/src/tools/utils'
+
+export function formatLegendData(params) {
+    let result = '',
+        number = 4,
+        name = params.name.toString(),
+        str = name.replace(/^\s+|\s+$/g, ''),
+        len = str.length;
+
+    number = len > number ? Math.ceil(len / 2) : number;
+    for (let i = 0; i < len; i++) {
+        if (i > 0 && i < len && (i % number) == 0) {
+            result += '\n';
+        }
+        result += str.charAt(i);
+    }
+    //str.replace(/(?=(?:.{4})+$)/g, '\n');
+
+    return result;
+}
+
+export function debounce(func, wait, immediate) {
+    let timeout, args, context, timestamp, result
+
+    const later = function () {
+        // 据上一次触发时间间隔
+        const last = +new Date() - timestamp
+
+        // 上次被包装函数被调用时间间隔last小于设定时间间隔wait
+        if (last < wait && last > 0) {
+            timeout = setTimeout(later, wait - last)
+        } else {
+            timeout = null
+            // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
+            if (!immediate) {
+                result = func.apply(context, args)
+                if (!timeout) context = args = null
+            }
+        }
+    }
+
+    return function (...args) {
+        context = this
+        timestamp = +new Date()
+        const callNow = immediate && !timeout
+        // 如果延时不存在,重新设定延时
+        if (!timeout) timeout = setTimeout(later, wait)
+        if (callNow) {
+            result = func.apply(context, args)
+            context = args = null
+        }
+
+        return result
+    }
+}
+
+//控制浏览器宽度变化
+export function chartsInitBAutoResize(obj) {
+    if (obj.autoResize) {
+        obj.__resizeHanlder = debounce(() => {
+            if (obj.elChart) {
+                obj.elChart.resize()
+            }
+        }, 100)
+        window.addEventListener('resize', obj.__resizeHanlder)
+    }
+
+    // 监听侧边栏的变化
+    if (document.getElementsByClassName('sidebar-container').length > 0) {
+        const sidebarElm = document.getElementsByClassName('sidebar-container')[0]
+        sidebarElm.addEventListener('transitionend', obj.__resizeHanlder)
+    }
+}
+
+export function chartsDestroyData(obj) {
+    if (!obj.elChart) {
+        return
+    }
+    if (obj.autoResize) {
+        window.removeEventListener('resize', obj.__resizeHanlder)
+    }
+
+    // 监听侧边栏的变化
+    if (document.getElementsByClassName('sidebar-container').length > 0) {
+        const sidebarElm = document.getElementsByClassName('sidebar-container')[0]
+        sidebarElm.removeEventListener('transitionend', obj.__resizeHanlder)
+    }
+
+    obj.elChart.dispose()
+    obj.elChart = null
+}
+
+/*
+ *
+ * type: 1: color数组,2: objColor数组
+ * index对象的序号,循环获取值
+ *
+ * */
+export function getColorValue(type, index) {
+    let number = 15,
+        objColor = [{
+            normal: ['#39A1FF', '#5AB1FF'],
+            emphasis: ['#5AB1FF', '#39A1FF']
+        }, {
+            normal: ['#5CDBD3', '#61EBE2'],
+            emphasis: ['#61EBE2', '#5CDBD3']
+        }, {
+            normal: ['#4ECB73', '#51CB92'],
+            emphasis: ['#51CB92', '#4ECB73']
+        }, {
+            normal: ['#FBD437', '#C1C417'],
+            emphasis: ['#C1C417', '#FBD437']
+        }],
+        color = ['#39A1FF', '#FF5C5C', '#4ECB73', '#FBD437', '#F2627B', '#2db9e3', '#90e3aa', '#fd8c6c', '#f75881', '#d6479e', '#de78c7', '#e0aaf0', '#8b7ff2', '#6f5ee4', '#85afff'];
+
+    if (type == 1) {
+        if (typeof (index) == 'number') {
+            return color[index % number];
+        } else {
+            return color;
+        }
+    } else {
+        return objColor;
+    }
+}
+
+//页面data初始化
+export function chartsInitData() {
+    let options = {},
+        option = {
+            elChart: null,
+            barGap: '9%',
+            barWidth: '10',
+            chartsId: 'chart',
+            className: 'chart',
+            autoResize: true,
+            chartOptions: { //画图使用的默认值
+                options: {
+                    type: 'bar', //chart类型,默认为line
+                    bgColor: '#ffffff'
+                }
+            },
+            scrollDataIndex: 0,
+            legrendUnSelect: true,
+            color: getColorValue(1),
+            itemColorArr: getColorValue(2)
+        };
+
+    Object.assign(options, option);
+
+    if (arguments.length > 1) {
+        Object.assign(options, arguments[1]())
+    }
+
+    return options;
+}
+
+//option设置
+export function chartsOptionGrid() {
+    return {
+        left: 15,
+        right: 15,
+        top: 70,
+        bottom: 50
+    };
+}
+
+//init merge data
+function getObjectValue(currObj, paramObj) {
+    let result = Object.create(currObj ? currObj : null);
+
+    if (currObj) {
+        deepObjectMerge(result, paramObj);
+
+        return result;
+    } else {
+        return paramObj;
+    }
+}
+
+function initChartsTitle(option, obj) {
+    let title = obj.options.title;
+
+    if (title) {
+        option.title = getObjectValue(option.title, title);
+    }
+}
+
+function initChartsToolbox(option, obj) {
+    let toolbox = obj.options.toolbox;
+
+    if (toolbox) {
+        option.toolbox = getObjectValue(option.toolbox, toolbox);
+    }
+}
+
+function initChartsTooltip(option, obj) {
+    let tooltip = obj.options.tooltip;
+
+    if (tooltip) {
+        option.tooltip = getObjectValue(option.tooltip, tooltip);
+    }
+}
+
+function initChartsGrid(option, obj) {
+    let grid = obj.options.grid;
+
+    if (grid) {
+        option.grid = getObjectValue(option.grid, grid);
+    } else {
+        option.grid = chartsOptionGrid();
+    }
+}
+
+function initChartsLegend(option, obj) {
+    let legend = obj.options.legend;
+
+    if (legend) {
+        option.legend = getObjectValue(option.legend, legend);
+    }
+}
+
+function initChartsLegendData(option, obj) {
+    let legendData = obj.options.legendData;
+
+    if (legendData) {
+        option.legendData = getObjectValue(option.legendData, legendData);
+    }
+}
+
+function initDefaultChartsxAxis(obj) {
+    return {
+        data: obj.chartData.xaxis
+    };
+}
+
+function initChartsxAxis(option, obj) {
+    if (obj.options.xAxisDel) return false;
+
+    let xAxis = obj.options.xAxis;
+
+    if (xAxis) {
+        option.xAxis = getObjectValue(option.xAxis, xAxis);
+    } else {
+        if (obj.chartData.xaxis) {
+            option.xAxis = initDefaultChartsxAxis(obj);
+        }
+    }
+}
+
+function initDefaultChartsyAxis(obj) {
+    return {
+        offset: 15,
+        position: 0,
+        splitNumber: 5
+    }
+}
+
+function initChartsyAxis(option, obj) {
+    if (obj.options.yAxisDel) return false;
+
+    let yAxis = obj.options.yAxis;
+
+    if (yAxis) {
+        option.yAxis = getObjectValue(option.yAxis, yAxis);
+    } else {
+        option.yAxis = initDefaultChartsyAxis(obj);
+    }
+}
+
+function getSeriesEmphasis(obj, item, index) {
+    let result = {};
+
+    return result;
+}
+
+function getSeriesItemStyle(obj, item, index) {
+    let result = {},
+        type = item.type ? item.type : obj.type;
+
+    if (item.gradual) {
+        let len = obj.itemColorArr.length,
+            color = obj.itemColorArr[index % len];
+
+        result = {
+            normal: {
+                color: new echarts.graphic.LinearGradient(
+                    0, 0, 0, 1,
+                    [{
+                        offset: 0,
+                        color: color.normal[0]
+                    },
+                        {
+                            offset: 0.5,
+                            color: color.normal[1]
+                        },
+                        {
+                            offset: 1,
+                            color: color.normal[1]
+                        }
+                    ]
+                )
+            },
+            emphasis: {
+                color: new echarts.graphic.LinearGradient(
+                    0, 0, 0, 1,
+                    [{
+                        offset: 0,
+                        color: color.emphasis[0]
+                    },
+                        {
+                            offset: 0.7,
+                            color: color.emphasis[0]
+                        },
+                        {
+                            offset: 1,
+                            color: color.emphasis[1]
+                        }
+                    ]
+                )
+            }
+        };
+    } else {
+        if (type == 'pie') {
+            result = {
+                normal: {
+                    color: function (params) {
+                        return obj.color[params.dataIndex]
+                    },
+                    label: {
+                        show: true,
+                        //position: 'inner',
+                        formatter: function (a, b, c, d) {
+                            return c;
+                        }
+                    }
+                }
+            };
+        } else {
+            result = {
+                normal: {
+                    color: item.color ? item.color : obj.color[index],
+                    borderColor: item.borderColor ? item.borderColor : obj.color[index],
+                    shadowOffsetX: item.shadowOffsetX ? item.shadowOffsetX : 0,
+                    opacity: item.opacity ? item.opacity : 1,
+                    lineStyle: {
+                        type: item.borderType ? item.borderType : 'solid' //'dotted'虚线 'solid'实线
+                    }
+                }
+            };
+        }
+    }
+
+    return result;
+}
+
+function getSeriesData(obj) {
+    let result = [],
+        map = obj.map,
+        series = obj.data.series;
+
+    series.forEach((item, index) => {
+        let serData = {},
+            type = item.type ? item.type : obj.type;
+
+        //series必须的值
+        serData.type = type;
+        serData.name = item.name ? item.name : 'null';
+        serData.data = item.data;
+        serData.itemStyle = item.itemStyle || getSeriesItemStyle(obj, item, index);
+        serData.emphasis = item.emphasis || getSeriesEmphasis(obj, item, index);
+        serData.animationEasing = 'cubicInOut';
+
+        if (item.yAxisIndex) {
+            serData.yAxisIndex = item.yAxisIndex;
+        }
+
+        if (item.stack) {
+            serData.stack = item.stack;
+        }
+
+        if (item.markLine) {
+            serData.markLine = item.markLine;
+        }
+
+        switch (type) {
+            case 'bar':
+                serData.animationDuration = 800;
+                serData.barGap = item.barGap ? item.barGap : obj.barGap;
+                serData.barWidth = item.barWidth ? item.barWidth : obj.barWidth;
+                break;
+            case 'line':
+                serData.animation = false;
+                serData.animationDuration = 1500;
+                break;
+            case 'pie':
+                serData.smooth = true;
+                serData.radius = 40;
+                serData.center = ['30%', '50%'];
+                serData.animationDuration = 800;
+                serData.label = {
+                    normal: {
+                        formatter: function (params) {
+                            return params.percent + '%';
+                        }
+                    }
+                };
+                serData.labelLine = {
+                    normal: {
+                        smooth: 0.4,
+                        length: 2,
+                        length2: 20,
+                    }
+                };
+                break;
+            case 'map':
+                serData.roam = map.roam || false;
+                serData.zoom = map.zoom || 2;
+                serData.map = map.name;
+                serData.center = item.center ? item.center : (map.center ? map.center : [106.0712, 35.9476]);
+                serData.label = item.label || {
+                    normal: {
+                        show: true,
+                        textStyle: {
+                            color: '#1DE9B6'
+                        }
+                    },
+                    emphasis: {
+                        textStyle: {
+                            color: 'rgb(183,185,14)'
+                        }
+                    }
+                };
+                break;
+        }
+        for (let [k, v] of Object.entries(item)) {
+            if (!serData.hasOwnProperty(k)) {
+                serData[k] = v
+            }
+        }
+
+        result.push(serData)
+    });
+
+    return result;
+}
+
+function initChartsSeries(option, obj) {
+    if (obj.data.series) {
+        option.series = getSeriesData(obj);
+    }
+}
+
+function initChartsMapGeo(option, obj) {
+    if (obj.options.geo) {
+        const geo = {
+            map: obj.map.name,
+            aspectScale: obj.map.aspectScale || 0.75, //长宽比
+            zoom: obj.map.zoom || 2,
+            roam: obj.map.roam || false,
+            center: obj.options.geo.center ? obj.options.geo.center : (obj.map.center ? obj.map.center : [106.0712, 35.9476])
+        }
+
+        option.geo = objectMerge(geo, obj.options.geo || {});
+    }
+}
+
+export function mergeChartsOptions(_this) {
+    let options = {},
+        chartOptions = objectMerge(_this.chartOptions, _this.chart.chartOptions);
+
+    options.backgroundColor = chartOptions.bgColor || '#013954'; // 背景色初始化
+    initChartsTitle(options, chartOptions); //初始化title
+    initChartsToolbox(options, chartOptions); //初始化toolbox
+    initChartsTooltip(options, chartOptions); //初始化tooltip
+    initChartsGrid(options, chartOptions); //初始化图上下左右的空白
+    initChartsLegend(options, chartOptions); //实例初始化
+    initChartsLegendData(options, chartOptions); //实例初始化
+    initChartsxAxis(options, chartOptions); //x坐标轴
+    initChartsyAxis(options, chartOptions); //y坐标轴
+    initChartsSeries(options, chartOptions); //数值初始化
+
+    return options;
+}
+
+// map Options
+export function mergeMapChartsOptions(_this) {
+    let options = {},
+        chartOptions = objectMerge(_this.chartOptions, _this.chart.chartOptions);
+
+    initChartsMapGeo(options, chartOptions); // 地图配置
+
+    return options;
+}
+
+export function legendSelecthanged(obj, option) {
+    if (!option.legendData) return;
+    obj.elChart.on('legendselectchanged', function (params) {
+        let num = 0,
+            name = params.name;
+
+        option.series[num].data.forEach((item, index) => {
+            let value = 0;
+            option.legendData[index].forEach(legend => {
+                if (name == legend.subjectName) {
+                    value = Number(legend.fee);
+                }
+            });
+            if (params.selected[name]) {
+                option.series[num].data[index] = item + value;
+            } else {
+                option.series[num].data[index] = item - value;
+            }
+        });
+
+        obj.elChart.setOption(option);
+    });
+}
+
+export function chartMouseEvent(obj, option) {
+    const regions = [...new Set(option.geo.regions)]
+
+    obj.elChart.on('mouseover', params => {
+        const name = params.name
+
+        const currData = regions.filter(item => item.name === name)
+        currData.length > 0 && regions.forEach(item => {
+            if (currData[0].value == item.value) {
+                obj.elChart.dispatchAction({
+                    type: "highlight",
+                    name: item.name
+                })
+            }
+        })
+    })
+    obj.elChart.on('mouseout', params => {
+        const name = params.name
+
+        const currData = regions.filter(item => item.name === name)
+        currData.length > 0 && regions.forEach(item => {
+            if (currData[0].value == item.value) {
+                obj.elChart.dispatchAction({
+                    type: "downplay",
+                    name: item.name
+                })
+            }
+        })
+    })
+}
+
+export function drawCharts(obj) {
+    const charts = obj.chart.chartOptions;
+    const options = mergeChartsOptions(obj);
+    const mapOptions = mergeMapChartsOptions(obj);
+    const map = charts.map
+
+    echarts.registerMap(map.name, map.data);
+    obj.elChart.hideLoading();
+    const option = Object.assign({}, options, mapOptions);
+
+    obj.elChart.on('georoam', function (params) {
+        charts.map.zoom = obj.elChart.getOption().geo[0].zoom //地图中的信息,包括经纬度、缩放大小等
+        charts.map.center = obj.elChart.getOption().geo[0].center //地图中的信息,包括经纬度、缩放大小等
+    })
+    obj.elChart.setOption(option, true);
+
+    charts.mouseEvent && chartMouseEvent(obj, option)
+    legendSelecthanged(obj, option); //图例选择改变事件
+}

+ 64 - 0
packages/charts/src/ntExtendCharts.vue

@@ -0,0 +1,64 @@
+<template>
+  <div :class="[className, chartsId]" style="height: 100%"></div>
+</template>
+
+<script>
+  import echarts from 'echarts/lib/echarts'
+  import { chartsInitData, chartsInitBAutoResize, chartsDestroyData, drawCharts } from './echart'
+
+  export default {
+    props: {
+      className: {
+        type: String,
+        default: 'chart'
+      },
+      chartsId: {
+        type: String,
+        default: 'chart'
+      },
+      autoResize: {
+        type: Boolean,
+        default: true
+      },
+      type: {
+        type: String,
+        default: 'bar'
+      },
+      options: {
+        type: Object,
+        default: () => {}
+      },
+      chartData: {
+        type: Object,
+        default: () => {}
+      }
+    },
+    data() {
+      return chartsInitData(this.type);
+    },
+    mounted() {
+      this.initChart();
+    },
+    beforeDestroy() {
+      chartsDestroyData(this);
+    },
+    watch: {
+      chartData: {
+        deep: true,
+        handler() {
+          this.initChart()
+        }
+      }
+    },
+    methods: {
+      initChart() {
+        // console.log(this)
+        if(!this.chart){
+          this.chart = echarts.init(this.$el, this.className);
+          chartsInitBAutoResize(this);
+        }
+        drawCharts(this);
+      }
+    }
+  }
+</script>

+ 47 - 0
packages/charts/src/ntMapCharts.vue

@@ -0,0 +1,47 @@
+<template>
+  <div :id="[chart.chartOptions.id]" class="map-charts" :class="[chart.chartOptions.class, chart.chartOptions.id]"></div>
+</template>
+
+<script>
+  import echarts from 'echarts/lib/echarts'
+  import { chartsInitData, chartsInitBAutoResize, chartsDestroyData, drawCharts } from './mapchart'
+
+  export default {
+    inject: ['chart'],
+    props: ['chartData'],
+    data() {
+      return chartsInitData(this.type);
+    },
+    mounted() {
+      this.initChart();
+    },
+    beforeDestroy() {
+      chartsDestroyData(this);
+    },
+    watch: {
+      chartData: {
+        deep: true,
+        handler() {
+          this.initChart()
+        }
+      }
+    },
+    methods: {
+      initChart() {
+        if(!this.elChart){
+          this.elChart = echarts.init(this.$el, this.chart.chartOptions.class);
+          chartsInitBAutoResize(this);
+        }
+
+        drawCharts(this);
+      }
+    }
+  }
+</script>
+
+<style lang="scss">
+  .map-charts {
+    width: 100%;
+    height: 260px;
+  }
+</style>

+ 9 - 0
packages/col/index.js

@@ -0,0 +1,9 @@
+import NtCol from './src/main';
+
+/* istanbul ignore next */
+NtCol.install = function(Vue) {
+  Vue.component(NtCol.name, NtCol);
+};
+
+export default NtCol;
+

+ 72 - 0
packages/col/src/main.js

@@ -0,0 +1,72 @@
+export default {
+    name: 'NtCol',
+  
+    props: {
+      span: {
+        type: Number,
+        default: 24
+      },
+      tag: {
+        type: String,
+        default: 'div'
+      },
+      offset: Number,
+      pull: Number,
+      push: Number,
+      xs: [Number, Object],
+      sm: [Number, Object],
+      md: [Number, Object],
+      lg: [Number, Object],
+      xl: [Number, Object]
+    },
+  
+    computed: {
+      gutter() {
+        let parent = this.$parent;
+        while (parent && parent.$options.componentName !== 'NtRow') {
+          parent = parent.$parent;
+        }
+        return parent ? parent.gutter : 0;
+      }
+    },
+    render(h) {
+      let classList = [];
+      let style = {};
+  
+      if (this.gutter) {
+        style.paddingLeft = this.gutter / 2 + 'px';
+        style.paddingRight = style.paddingLeft;
+      }
+  
+      ['span', 'offset', 'pull', 'push'].forEach(prop => {
+        if (this[prop] || this[prop] === 0) {
+          classList.push(
+            prop !== 'span'
+              ? `nt-col-${prop}-${this[prop]}`
+              : `nt-col-${this[prop]}`
+          );
+        }
+      });
+  
+      ['xs', 'sm', 'md', 'lg', 'xl'].forEach(size => {
+        if (typeof this[size] === 'number') {
+          classList.push(`nt-col-${size}-${this[size]}`);
+        } else if (typeof this[size] === 'object') {
+          let props = this[size];
+          Object.keys(props).forEach(prop => {
+            classList.push(
+              prop !== 'span'
+                ? `nt-col-${size}-${prop}-${props[prop]}`
+                : `nt-col-${size}-${props[prop]}`
+            );
+          });
+        }
+      });
+  
+      return h(this.tag, {
+        class: ['nt-col', classList],
+        style
+      }, this.$slots.default);
+    }
+  };
+  

+ 9 - 0
packages/detail/index.js

@@ -0,0 +1,9 @@
+import NtTextInfo from './src/main';
+
+/* istanbul ignore next */
+NtTextInfo.install = function(Vue) {
+  Vue.component(NtTextInfo.name, NtTextInfo);
+};
+
+export default NtTextInfo;
+

+ 104 - 0
packages/detail/src/main.vue

@@ -0,0 +1,104 @@
+<template>
+    <div class="stats-data" :style="headerStyle">
+        <div class="table-detail-info" :style="detailStyle" v-if="detailInfo">{{detailInfo}}</div>
+        <div :class="headerClass" :style="boxStyle">
+            <div class="data-item" :style="itemStyle" v-for="(item, index) in dataList" :key="index">
+                <span class="name" :style="nameStyle">{{item.currField ? rowData[item.currField] : ''}}{{item.name}}</span>
+                <span class="value" :style="valueStyle">{{rowData[item.field]}} {{item.unit}}</span>
+            </div>
+        </div>
+    </div>
+</template>
+<script>
+export default {
+    name: 'NtTextInfo',
+    props: {
+        rowData: {
+            type: Object,
+            required: true
+        },
+        dataList: {
+            type: Array,
+            required: true
+        },
+        headerStyle: {
+            type: String,
+            default: ''
+        },
+        headerClass: {
+            type: String,
+            default: 'total-data'
+        },
+        detailInfo: {
+            type: String,
+            default: ''
+        },
+        detailStyle: {
+          type: String,
+          defalut: ''
+        },
+        boxStyle: {
+          type: String,
+          defalut: ''
+        },
+        itemStyle: {
+          type: String,
+          defalut: ''
+        },
+        nameStyle: {
+          type: String,
+          defalut: ''
+        },
+        valueStyle: {
+          type: String,
+          defalut: ''
+        }
+        
+    }
+}
+</script>
+<style lang="scss" scoped>
+    .stats-data {
+        color: #2F3337;
+        font-size: 13px;
+        position: absolute;
+        top: 115px;
+        left: 160px;
+        display: flex;
+        align-items: center;
+
+        .table-detail-info {
+            color: #ff8c1d;
+            padding-right: 5px;
+        }
+    }
+    .total-data {
+        display: flex;
+        padding: 2px 20px;
+        background-color: rgba(255, 245, 33, .2);
+
+        .data-item {
+            .value {
+                color: #ff4949;
+            }
+        }
+    }
+
+    .top-detail {
+        display: flex;
+        padding: 15px;
+        margin-bottom: 15px;
+        border-radius: 4px;
+        background-color: rgba(255, 255, 255, 1);
+        &.transparent {
+            background-color: transparent;
+        }
+    }
+
+    .total-data .data-item, .top-detail .data-item {
+        margin-left: 25px;
+        &:first-child {
+            margin-left: 0;
+        }
+    }
+</style>

+ 8 - 0
packages/form/index.js

@@ -0,0 +1,8 @@
+import NtForm from './src/main';
+
+/* istanbul ignore next */
+NtForm.install = function(Vue) {
+  Vue.component(NtForm.name, NtForm);
+};
+
+export default NtForm;

+ 269 - 0
packages/form/src/main.vue

@@ -0,0 +1,269 @@
+<template>
+  <el-form :ref="formRef" :model="rowData" size="small" :rules="myRules" :label-position="labelPosition">
+    <div v-if="modeList && modeList.length > 0">
+      <div class="el-form-bottom" v-for="(mode, modeIndex) in modeList" :key="modeIndex">
+        <div class="el-form-box ou" v-if="modeShowStatus(mode)">
+          <div class="emz-divider" :style="mode.style">{{ mode.name }}
+            <el-tag class="mode-tag" v-if="mode.btn" :color="mode.btn.color"
+                    :effect="mode.btn.effect ? mode.btn.effect : 'dark'" size="mini" @click="modeEventClick(mode)">
+              {{ mode.btn.name }}
+            </el-tag>
+          </div>
+          <div class="clearfix em-form-content" v-if="iShowFormInput">
+            <el-col
+                v-for="(item,index) in sortAfterColumn" :key="index"
+                :style="item[inputType] && item[inputType].formStyle ? item[inputType].formStyle : (item.formStyle ? item.formStyle : '')"
+                :xs="colSpanResponsive(item, inputType, 'xs', 24)"
+                :sm="colSpanResponsive(item, inputType, 'sm', 24)"
+                :md="colSpanResponsive(item, inputType, 'md', 24)"
+                :lg="colSpanResponsive(item, inputType, 'lg', 12)"
+                :xl="colSpanResponsive(item, inputType, 'xl', 12)">
+              <nt-input v-if="isShowInput(item, mode.ou)" :inputItem="item" :rowData="rowData" :inputType="inputType"
+                        :itemMode="mode" :selectList="selectList" :axios="axios" :queryURL="queryURL"
+                        :responseSuccess="responseSuccess" :publicParams="publicParams"
+                        @myValidateField="myValidateField" @resetPageColumn="resetPageColumn"></nt-input>
+            </el-col>
+          </div>
+        </div>
+      </div>
+    </div>
+    <div v-else class="el-form-bottom el-mode-list">
+      <div class="el-form-box">
+        <div class="clearfix em-form-content" v-if="iShowFormInput">
+          <el-col
+              v-for="(item,index) in sortAfterColumn" :key="index"
+              :style="item[inputType] && item[inputType].formStyle ? item[inputType].formStyle : (item.formStyle ? item.formStyle : '')"
+              :xs="colSpanResponsive(item, inputType, 'xs', 24)"
+              :sm="colSpanResponsive(item, inputType, 'sm', 24)"
+              :md="colSpanResponsive(item, inputType, 'md', 24)"
+              :lg="colSpanResponsive(item, inputType, 'lg', 12)"
+              :xl="colSpanResponsive(item, inputType, 'xl', 12)">
+            <nt-input v-if="isShowInput(item)" :inputItem="item" :rowData="rowData" :inputType="inputType"
+                      :selectList="selectList" :axios="axios" :queryURL="queryURL" :responseSuccess="responseSuccess"
+                      :publicParams="publicParams" @myValidateField="myValidateField"
+                      @resetPageColumn="resetPageColumn"></nt-input>
+          </el-col>
+        </div>
+      </div>
+    </div>
+
+    <!-- 按钮 -->
+    <el-form-item class="el-del-btn-item" v-if="rowData._btn && rowData._btn.iShow">
+      <div class="btn-item-footer">
+        <el-button v-for="(btnItem, index) in rowData._btn.list" :key="index" :type="btnItem.bType"
+                   size="small"
+                   :icon="btnItem.icon" @click="btnClickEvent(btnItem)">{{ btnItem.label }}
+        </el-button>
+      </div>
+    </el-form-item>
+  </el-form>
+</template>
+<script>
+import NtInput from '@/components/thrid/em-element-ui/packages/input';
+import {submitEditFormInfo, otherEvent} from './tool-form';
+import {isTypeof, toolResetPageColumn, arrayToFieldSort} from '@/components/thrid/em-element-ui/src/tools/utils';
+import {addValidatorObj} from '@/components/thrid/em-element-ui/src/tools/validate';
+import { unitInitSelectList } from "@/components/thrid/em-element-ui/packages/list/src/tool-list";
+
+export default {
+  name: 'NtForm',
+  components: { NtInput },
+  props: {
+    rowData: {
+      type: Object,
+      required: true
+    },
+    pageColumn: {
+      type: Array,
+      required: true
+    },
+    modeList: {
+      type: Array | undefined,
+      default: function () {
+        return [];
+      }
+    },
+    modeStatus: {
+      type: Object,
+      default: function () {
+        return {
+          show: 1,
+          detail: 2
+        }
+      }
+    },
+    inputType: {
+      type: String,
+      default: 'show'
+    },
+    selectList: {
+      type: Object | undefined,
+      default: function () {
+        return {};
+      }
+    },
+    axios: {
+      type: Object,
+      default: function () {
+        return {};
+      }
+    },
+    queryURL: {
+      type: Object,
+      default: function () {
+        return {};
+      }
+    },
+    responseSuccess: {
+      type: Object,
+      default: function () {
+        return {};
+      }
+    },
+    labelPosition: {
+      type: String,
+      default: 'left'
+    },
+    formRef: {
+      type: String,
+      default: 'form'
+    },
+    initSelect: {
+      type: Boolean,
+      default: false
+    },
+    options: {
+      type: Object,
+      default: () => {
+      }
+    }
+  },
+  data() {
+    return {
+      myRules: {},
+      sortAfterColumn: null,
+      iShowFormInput: true,
+      publicParams: {},
+      select_list: this.selectList // 兼容initSelect
+    };
+  },
+  watch: {},
+  created: function () {
+    this.initData();
+  },
+  methods: {
+    initData() {
+      this.publicParams = (this.options && this.options.publicParams) ? this.options.publicParams : {};
+      // 模糊图片初始化
+      this.pageColumn.forEach(item => {
+        if (item[this.inputType] && item[this.inputType].hasOwnProperty('vague')) {
+          item[this.inputType].vague = true;
+        }
+      })
+      // 是否重新加载select
+      this.initSelect && this.initSelectList();
+      // column重新排序
+      this.resetPageColumn();
+    },
+    initSelectList(demandList = null) {
+      unitInitSelectList(this, demandList);
+    },
+    myValidateField(field) {
+      this.$refs[this.formRef].validateField(field)
+    },
+    modeShowStatus(mode) {
+      let tmpModeStatus = false;
+
+      tmpModeStatus = ((mode.status & this.modeStatus[this.inputType]) == this.modeStatus[this.inputType]);
+
+      if (tmpModeStatus && mode.hasOwnProperty('ou')) {
+        let tmpModeOuList = this.sortAfterColumn.filter(item => item[this.inputType] && item[this.inputType].hasOwnProperty('ou') && item[this.inputType].ou === mode.ou);
+        tmpModeStatus = tmpModeOuList.length > 0 ? true : false
+      }
+
+      return tmpModeStatus
+    },
+    colSpanResponsive(item, inputType, node, number) {
+      if ((item[inputType] && item[inputType].colspan) || item.colspan) {
+        return item[inputType] && item[inputType].colspan ? item[inputType].colspan : (item.colspan ? item.colspan : number)
+      } else {
+        return item[inputType] && item[inputType][node] ? item[inputType][node] : (item[node] ? item[node] : number)
+      }
+    },
+    resetPageColumn(value = '', inputItem = {}) {
+      this.sortAfterColumn = arrayToFieldSort(this.pageColumn, this.inputType);
+      this.iShowFormInput = false;
+      this.$nextTick(() => {
+        this.sortAfterColumn = toolResetPageColumn(this, this.pageColumn, this.sortAfterColumn, this.rowData, this.inputType, arguments.length > 0 ? true : false)
+
+        this.iShowFormInput = true
+        // 修改规则校验
+        this.myRules = addValidatorObj(this, this.sortAfterColumn)
+      })
+    },
+    isShowInput(item, mode = '') {
+      let noShow = item[this.inputType] && item[this.inputType].noShow ? item[this.inputType].noShow : 0,
+          ouShow = item[this.inputType] && item[this.inputType].ou ? item[this.inputType].ou : '',
+          btnType = this.rowData._btn && this.rowData._btn.type ? this.rowData._btn.type : '',
+          ouFlag = mode === '' ? true : ouShow == mode,
+          showFlag = item[this.inputType] ? (item[this.inputType] && !((btnType == 'add' && (noShow & 1) == 1) || (btnType == 'edit' && (noShow & 2) == 2))) : false;
+
+      return showFlag && ouFlag;
+    },
+    modeEventClick(item) {
+      this.$emit('handleModeEvent', item, this.pageColumn, this.rowData);
+    },
+    btnClickEvent(item) {
+      switch (item.type) {
+        case 'close':
+          if (isTypeof(this.rowData._oldData) === 'object') {
+            for (let [k, v] of Object.entries(this.rowData._oldData)) {
+              if (this.rowData[k] !== v) {
+                this.rowData[k] = v;
+              }
+            }
+          }
+          this.$emit('clear', 'edit');
+          break;
+        case 'submit':
+          submitEditFormInfo(this, 'form');
+          break;
+        default:
+          otherEvent(this, item, 'form');
+      }
+    }
+  },
+  beforeDestroy() {
+    this.btnClickEvent({ type: 'close' });
+  }
+};
+
+</script>
+<style lang="scss">
+.el-form-box {
+  &.ou {
+    border: 1px solid #f2f2f2;
+    border-radius: 8px;
+    position: relative;
+    margin-top: 20px;
+    margin-bottom: 30px;
+
+    .emz-divider {
+      position: absolute;
+      top: -10px;
+      left: 10px;
+      color: #409EFF;
+      padding: 1px 5px;
+      background-color: #fefefe;
+    }
+
+    .em-form-content {
+      padding-top: 20px;
+    }
+
+    .mode-tag {
+      cursor: pointer;
+    }
+  }
+}
+</style>

+ 94 - 0
packages/form/src/remove-form.vue

@@ -0,0 +1,94 @@
+<template>
+    <el-form ref="form" label-width="80px" size="small" :label-position="labelPosition">
+        <div>
+            <div style="padding: 30px 0;font-weight: bold;">{{optMessage}}</div>
+        </div>
+        <div v-if="isOptMessage">
+            <!-- 按钮 -->
+            <el-form-item class="el-del-btn-item" v-if="!rowData['show']">
+                <div class="btn-item-footer">
+                    <el-button type="info" size="small" icon="el-icon-close" @click="subClearBtn()">取消</el-button>
+                    <el-button type="primary" size="small" icon="el-icon-check" @click="submitEvent()">{{rowData._btn}}</el-button>
+                </div>
+            </el-form-item>
+        </div>
+        <div v-else>
+            <!-- 按钮 -->
+            <el-form-item class="el-del-btn-item" v-if="rowData._btn && rowData._btn.iShow">
+                <div class="btn-item-footer">
+                    <el-button v-for="(btnItem, index) in rowData._btn.list" :key="index" :type="btnItem.bType"
+                               size="small"
+                               :icon="btnItem.icon" @click="btnClickEvent(btnItem)">{{btnItem.label}}
+                    </el-button>
+                </div>
+            </el-form-item>
+        </div>
+    </el-form>
+</template>
+<script>
+    import { otherDialogEvent, submitRemoveEvent } from './tool-form';
+
+    export default {
+        name: 'NtRemoveForm',
+        props: {
+            rowData: {
+                type: Object,
+                required: true
+            },
+            axios: {
+                type: Object,
+                default: function () { return {}; }
+            },
+            queryURL: {
+                type: Object,
+                default: function () { return {}; }
+            },
+            responseSuccess: {
+                type: Object,
+                default: function() { return {}; }
+            },
+            labelPosition: {
+                type: String,
+                default: 'left'
+            },
+            options: {
+                type: Object,
+                default: () => {}
+            }
+        },
+        data() {
+            return {
+                publicParams: {},
+                isOptMessage: this.rowData._optType === 'del' ? true : false,
+                optMessage: this.rowData._optMessage
+            };
+        },
+        watch: {},
+        created: function() {
+            this.publicParams = (this.options && this.options.publicParams) ? this.options.publicParams : {}
+        },
+        methods: {
+            subClearBtn(){
+                this.isOptMessage = this.rowData._optType === 'del' ? true : false;
+
+                this.$emit('clear', 'del');
+            },
+            submitEvent(){
+                submitRemoveEvent(this);
+            },
+            btnClickEvent(item) {
+                switch (item.type) {
+                    case 'close':
+                        this.subClearBtn();
+                        break;
+                    case 'submit':
+                        submitRemoveEvent(this);
+                        break;
+                    default:
+                        otherDialogEvent(this, item);
+                }
+            }
+        }
+    };
+
+</script>

+ 339 - 0
packages/form/src/tool-form.js

@@ -0,0 +1,339 @@
+import request from '@/components/thrid/em-element-ui/src/tools/request';
+import {
+    isTypeof,
+    utilsTrim,
+    formatDate,
+    axiosReqParams,
+    responseKeyToValue,
+    mergeRequestParams,
+    LOCAL_STORAGE_CURR
+} from '@/components/thrid/em-element-ui/src/tools/utils';
+
+//form表单提交数据解释
+export function submitQueryData(_this, paramType = true) {
+    let res = {},
+        rowObj = _this.rowData, // 当前操作数据
+        btnType = rowObj._btn && rowObj._btn.type ? rowObj._btn.type : '', // 操作按钮
+        oldRow = _this.rowData._oldData || {}, // 操作前数据
+        currEvent = _this.queryURL.hasOwnProperty(btnType) ? _this.queryURL[btnType] || {} : {}, // 操作事件的对象
+        isInFormData = currEvent.hasOwnProperty('indata') ? currEvent.indata : true, // 是否是提交所有表单数据,默认true
+        isparams = _this.options.hasOwnProperty('isparams') ? _this.options.isparams : false; // disabled行输入框是否提交参数
+
+    // 用户自带参数
+    if (paramType && rowObj._params && isTypeof(rowObj._params) === 'object') {
+        for (let [pk, pv] of Object.entries(rowObj._params)) {
+            res[pk] = pv;
+        }
+    }
+
+    _this.pageColumn.forEach(item => {
+        let postValue = '',
+            field = item.field,
+            isCompares = true,
+            inputType = _this.inputType ? _this.inputType : 'show',
+            fieldKey = item[inputType] && item[inputType].field ? item[inputType].field : item.field,
+            subFieldKey = item[inputType] && item[inputType].subField ? item[inputType].subField : '',
+            noShow = item[inputType] && item[inputType].noShow ? item[inputType].noShow : 0,
+            is_all_form_data = isInFormData || item.hasOwnProperty(inputType),
+            is_field = !((btnType == 'add' && (noShow & 1) == 1) || (btnType == 'edit' && (noShow & 2) == 2)),
+            is_disabled = isparams === false ? true : ((isparams & 1) == 1 ? true : (btnType == 'add' ? true : (item[inputType] && item[inputType].isDisabled ? false : true))),
+            is_auto_push = isparams === false ? true : ((isparams & 2) == 2 ? true : (item.hasOwnProperty('isAutoPush') ? item.isAutoPush : true)),
+            is_submit = isTypeof(item.ispush) == 'undefined' ? true : item.ispush;
+
+        //is_submit代表是否提交改字段, is_field控制列表字段在不同操作事件中显示情况
+        if (is_submit && is_field && is_auto_push && is_disabled && is_all_form_data) {
+            let currValue = rowObj[field];
+
+            if (isTypeof(currValue) == 'object') {
+                postValue = (item[inputType] && item[inputType].dataType === 'object') ? currValue : JSON.stringify(currValue);
+            } else if (isTypeof(currValue) == 'array') {
+                // 多字段参数
+                if (item[inputType] && item[inputType].mulField && isTypeof(item[inputType].mulField) === 'object') {
+                    // 多选下拉列表对象参数
+                    if (item[inputType].type == 'select' && item[inputType].multiple && currValue.length > 0) {
+                        let tmpDataArray = [];
+                        currValue.forEach(selectItem => {
+                            let tmpDataObject = {},
+                                tmpFieldArray = isTypeof(selectItem) == 'string' ? selectItem.split(item[inputType].sign ? item[inputType].sign : '#0') : selectItem;
+
+                            for (let [kk, vv] of Object.entries(item[inputType].mulField)) {
+                                let fieldType = isTypeof(tmpFieldArray[vv]),
+                                    tmpFieldValue = tmpFieldArray[vv] ? (fieldType === 'object' ? JSON.stringify(tmpFieldArray[vv]) : (fieldType === 'array' ? JSON.stringify(tmpFieldArray[vv]) : (fieldType === 'undefined' ? '' : tmpFieldArray[vv]))) : '';
+
+                                tmpDataObject[kk] = tmpFieldValue;
+                            }
+                            tmpDataArray.push(tmpDataObject);
+                        })
+                        postValue = tmpDataArray;
+                    } else {
+                        // 多字段单个字符串传参
+                        for (let [kk, vv] of Object.entries(item[inputType].mulField)) {
+                            let fieldType = isTypeof(currValue[vv]),
+                                tmpFieldValue = currValue[vv] ? (fieldType === 'object' ? JSON.stringify(currValue[vv]) : (fieldType === 'array' ? JSON.stringify(currValue[vv]) : (fieldType === 'undefined' ? '' : currValue[vv]))) : '';
+
+                            formInputDataCache(item, inputType, kk, tmpFieldValue);
+                            formDataParams(res, kk, tmpFieldValue, item, inputType, isCompares, subFieldKey, rowObj);
+                        }
+                        return false;
+                    }
+                } else if (item[inputType] && item[inputType].type == 'file') {
+                    if (item[inputType].iType == 'string') {
+                        let tmpFileParams = []
+                        currValue.forEach(fileParams => {
+                            tmpFileParams.push(fileParams[(item[inputType].paramField ? item[inputType].paramField : 'url')])
+                        })
+                        postValue = item[inputType].hasOwnProperty('sign') ? tmpFileParams.join(item[inputType].sign) : tmpFileParams.join(',')
+                    } else {
+                        postValue = currValue
+                    }
+                } else if (item[inputType] && (item[inputType].type == 'checkbox-group' || item[inputType].type == 'input-range')) {
+                    if (item[inputType].iType == 'string') {
+                        postValue = item[inputType].hasOwnProperty('sign') ? currValue.join(item[inputType].sign) : currValue.join(',')
+                    } else {
+                        postValue = currValue
+                    }
+                } else if (item[inputType] && (item[inputType].type == 'cascader' || item[inputType].type == 'select')) {
+                    if (item[inputType] && item[inputType].iType == 'string') {
+                        if (item[inputType].hasOwnProperty('sign')) {
+                            postValue = item[inputType].sign ? currValue.join(item[inputType].sign) : currValue.join('');
+                        } else {
+                            postValue = currValue.join();
+                        }
+                    } else {
+                        postValue = item[inputType].dataType === 'array' ? currValue : JSON.stringify(currValue);
+                    }
+                } else {
+                    postValue = item[inputType].dataType === 'array' ? currValue : JSON.stringify(currValue);
+                }
+            } else if (isTypeof(currValue) == 'boolean') {
+                postValue = currValue;
+            } else if (typeof (currValue) == 'undefined') {
+                postValue = '';
+            } else {
+                if (item[inputType] && item[inputType].mulField && isTypeof(item[inputType].mulField) === 'object') {
+                    for (let [kk, vv] of Object.entries(item[inputType].mulField)) {
+                        let tmpFieldArray = isTypeof(currValue) == 'string' ? currValue.split(item[inputType].sign ? item[inputType].sign : '#0') : currValue,
+                            tmpFieldValue = tmpFieldArray[vv] ? tmpFieldArray[vv] : '';
+
+                        formInputDataCache(item, inputType, kk, tmpFieldValue)
+                        formDataParams(res, kk, tmpFieldValue, item, inputType, isCompares, subFieldKey, rowObj);
+                    }
+
+                    return false;
+                } else if (item[inputType] && item[inputType].valueField) {
+                    let mapValue = rowObj[item[inputType].valueField]
+
+                    postValue = mapValue
+                    if (item[inputType].props) {
+                        let tmpMapValue = item[inputType].props && item[inputType].props.hasOwnProperty('index') ? (mapValue && mapValue[item[inputType].props.index]) : mapValue;
+
+                        postValue = item[inputType].props.key && tmpMapValue && tmpMapValue.hasOwnProperty(item[inputType].props.key) ? tmpMapValue[item[inputType].props.key] : ''
+                    }
+                } else {
+                    postValue = currValue;
+                }
+            }
+
+            if (item[inputType] && item[inputType].isCompare && postValue == oldRow[field]) {
+                isCompares = false
+            }
+
+            formInputDataCache(item, inputType, fieldKey, postValue);
+            formDataParams(res, fieldKey, postValue, item, inputType, isCompares, subFieldKey, rowObj);
+
+            // 提交字段对应多个字段
+            if (item[inputType] && item[inputType].fieldList && isTypeof(item[inputType].fieldList) == 'array') {
+                let tmpMergeValue = [];
+
+                item[inputType].fieldList.forEach(currField => {
+                    res[currField] ? tmpMergeValue.push(res[currField]) : tmpMergeValue.push(rowObj[currField].toString())
+                })
+
+                res[fieldKey] = tmpMergeValue.join(item[inputType].sign ? item[inputType].sign : '')
+                formInputDataCache(item, inputType, fieldKey, res[fieldKey]);
+            }
+        }
+    });
+
+    return res;
+}
+
+// 缓存编辑的字段
+export function formInputDataCache(item, inputType, field, value) {
+    value = item[inputType] && (!item[inputType].hasOwnProperty('trim') || (item[inputType].hasOwnProperty('trim') && item[inputType].trim === true))
+        ? utilsTrim(value)
+        : value;
+
+    if (item[inputType] && item[inputType].isCache) {
+        let tmpObj = {},
+            tmpLocalData = [],
+            key = LOCAL_STORAGE_CURR + (item[inputType].cacheField ? item[inputType].cacheField : field);
+
+        if (localStorage.getItem(key)) {
+            tmpLocalData = JSON.parse(localStorage.getItem(key));
+            tmpLocalData = isTypeof(tmpLocalData) == 'array' ? tmpLocalData : [];
+        }
+
+        tmpLocalData.unshift({ value });
+
+        //数组去重
+        tmpLocalData = tmpLocalData.reduce((cur, next) => {
+            tmpObj[next.value] ? "" : tmpObj[next.value] = true && cur.push(next);
+            return cur;
+        }, []);
+        localStorage.setItem(key, JSON.stringify(tmpLocalData));
+    }
+}
+
+export function formDataParams(data, key, value, item, inputType, isCompares, subKey = '', objRow = {}) {
+    if (item[inputType] && item[inputType].dataType) {
+        let dataType = isTypeof(item[inputType].dataType) === 'string' ? item[inputType].dataType : (item[inputType].dataType.hasOwnProperty(key) ? item[inputType].dataType[key] : '')
+        // 根据不同dataType转变value值
+        switch (dataType) {
+            case 'number':
+                value = Number(value);
+                break;
+            case 'boolean':
+                value = value ? true : false;
+                break;
+            case 'date':
+                value = formatDate(value, 'yyyy-MM-dd');
+                break;
+            case 'datetime':
+                value = formatDate(value, 'yyyy-MM-dd hh:mm:ss');
+                break;
+            case 'object':
+                value = value === '' ? null : value;
+                break;
+        }
+    }
+    value = item[inputType] && (!item[inputType].hasOwnProperty('trim') || (item[inputType].hasOwnProperty('trim') && item[inputType].trim === true))
+        ? utilsTrim(value)
+        : value;
+
+    // 封装参数格式
+    if (item[inputType] && item[inputType].parent) {
+        let parent = item[inputType].parent
+        if (isTypeof(parent) === 'array') {
+            const parentList = [...item[inputType].parent];
+
+            parent = parentList.pop()
+            parentList.forEach(dataItem => {
+                if (!data.hasOwnProperty(dataItem)) {
+                    data[dataItem] = {};
+                }
+                data = data[dataItem];
+            })
+        }
+        if (!data.hasOwnProperty(parent)) {
+            data[parent] = {};
+        }
+
+        item[inputType].isNode && (data[key] = value)
+        isCompares && (data[parent][key] = value);
+        if (subKey) {
+            data[parent][subKey] = objRow[subKey];
+        }
+        if (item[inputType].isNode) {
+            data[key] = value;
+            subKey && (data[subKey] = objRow[subKey]);
+        }
+    } else {
+        isCompares && (data[key] = value);
+        if (subKey) {
+            data[subKey] = objRow[subKey];
+        }
+    }
+
+    return data;
+}
+
+///提交edit form表单
+export function submitEditFormInfo(_this, formName) {
+    _this.$refs[formName].validate((valid) => {
+        if (valid) {
+            let queryData = submitQueryData(_this);
+
+            request(axiosReqParams(_this.rowData, mergeRequestParams(queryData, _this.publicParams), _this.rowData._axios, _this.options)).then((response) => {
+                let result = responseKeyToValue(response, _this, _this, _this.rowData._btn.type);
+
+                if (result !== false) {
+                    _this.$emit('reload');
+                    _this.$emit('clear', 'edit');
+                }
+            }).catch((err) => {
+            });
+        }
+    });
+}
+
+///dialog弹出按钮事件处理
+export function otherEvent(_this, item, formName) {
+    if (_this.queryURL.hasOwnProperty(item.type)) {
+        _this.$refs[formName].validate((valid) => {
+            if (valid) {
+                let queryData = submitQueryData(_this, false),
+                    data = _this.rowData._params && _this.rowData._params.data ? (_this.rowData._params.data || {}) : {},
+                    queryTmpData = Object.assign({}, data, queryData);
+
+                request(axiosReqParams(_this.rowData, mergeRequestParams(queryTmpData, _this.publicParams), _this.rowData._axios, _this.options)).then((response) => {
+                    let result = responseKeyToValue(response, _this, _this, _this.rowData._btn.type);
+
+                    if (result !== false) {
+                        _this.$emit('reload');
+                        _this.$emit('clear', 'edit');
+                    }
+                }).catch((err) => {
+                });
+            }
+        });
+    } else {
+        _this.$emit('onListEvent', item, _this.rowData);
+    }
+}
+
+// dialog弹出按钮事件处理
+export function otherDialogEvent(_this, item) {
+    if (_this.queryURL.hasOwnProperty(item.type)) {
+        submitRemoveEvent(_this)
+    } else {
+        _this.$emit('onListEvent', item, _this.rowData);
+    }
+}
+
+//提交确认按钮,做rules验证
+export function submitRemoveEvent(_this) {
+    let rules = _this.rowData._rules;
+
+    if (rules) {
+        if (rules.isRules) {
+            request(axiosReqParams(rules, mergeRequestParams(rules.params, _this.publicParams), rules._axios, _this.options)).then((response) => {
+                if (response.data && response.data.total > 0) {
+                    _this.$message({ type: 'error', message: rules.message });
+                } else {
+                    submitRemoveFormInfo(_this);
+                }
+            });
+        } else {
+            submitRemoveFormInfo(_this);
+        }
+    } else {
+        submitRemoveFormInfo(_this);
+    }
+}
+
+///提交删除操作
+export function submitRemoveFormInfo(_this) {
+    let item = Object.assign({}, _this.rowData),
+        data = _this.rowData._params.data;
+
+    item._url = _this.rowData._url ? _this.rowData._url : _this.rowData.url;
+    item._method = _this.rowData._params.method;
+    request(axiosReqParams(item, mergeRequestParams(data, _this.publicParams), _this.rowData._axios, _this.options)).then((response) => {
+        let result = responseKeyToValue(response, _this, _this, _this.rowData._btntype);
+
+        _this.$emit('reload');
+        _this.subClearBtn();
+    }).catch((err) => {
+    });
+}

+ 8 - 0
packages/infinite-transfer/index.js

@@ -0,0 +1,8 @@
+import NtInfiniteTransfer from './src/main';
+
+/* istanbul ignore next */
+NtInfiniteTransfer.install = function(Vue) {
+  Vue.component(NtInfiniteTransfer.name, NtInfiniteTransfer);
+};
+
+export default NtInfiniteTransfer;

+ 286 - 0
packages/infinite-transfer/src/main.vue

@@ -0,0 +1,286 @@
+<template>
+  <div class="infinite-transfer" :style="{ height: height + 'px' }">
+    <div class="infinite-list-main">
+      <div class="search-input-main">
+        <el-checkbox style="margin-right: 10px" :indeterminate="isIndeterminate" v-model="checkAll" @change="handleCheckAllChange">全选</el-checkbox>
+        <el-input v-model="keyword" placeholder="请输入内容" prefix-icon="el-icon-search" clearable size="small" @input="searchEvent">
+        </el-input>
+      </div>
+      <div v-if="infiniteListStatus" class="infinite-list" v-infinite-scroll="onload" :infinite-scroll-disabled="scrollDisabled">
+        <el-checkbox v-for="(item, index) in dataList" v-model="item.ischecked" :label="item" :key="index" @change="(checked) => checkboxClick(checked, item, index)">{{ infiniteItemName(item) }}</el-checkbox>
+        <div class="transger-number">{{ infiniteCheckedTotal }}/{{ dataTotal }}</div>
+      </div>
+    </div>
+    <div class="transfer-selected">
+      <div class="total-main">您已选择记录总数:<span style="color: #00a2d4">{{ selectedList.length }}</span></div>
+      <div class="infinite-list">
+        <el-tag v-for="(item, index) in selectedList" :key="index" closable @close="handleClose(item, index)"><div>{{ infiniteItemName(item) }}</div></el-tag>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { debounce } from "main/tools/utils";
+
+export default {
+  name: 'NtInfiniteTransfer',
+  props: {
+    height: {
+      type: Number,
+      default: 280
+    },
+    options: {
+      type: Object
+    },
+    primary: {
+      type: String,
+      required: true
+    },
+    dataTotal: {
+      type: String | Number,
+      default: function () {
+        return 0
+      }
+    },
+    fields: {
+      // eslint-disable-next-line vue/require-prop-type-constructor
+      type: Array | String,
+      default: function () {
+        return []
+      }
+    },
+    dataList: {
+      type: Array,
+      required: true
+    },
+    selectedList: {
+      type: Array,
+      default: function () {
+        return []
+      }
+    }
+  },
+  data() {
+    return {
+      keyword: '',
+      checkAll: false,
+      scrollDisabled: false,
+      isIndeterminate: false,
+      infiniteListStatus: true,
+      infiniteCheckedTotal: 0,
+      selectedItemArray: [],
+      debounceInfinite: null
+    }
+  },
+  watch: {
+    dataList: {
+      deep: false,
+      handler(oldList, newList) {
+        // 是否可滚动
+        this.scrollDisabled = false
+        if (this.dataList.length > 0 && this.dataTotal <= this.dataList.length) {
+          this.scrollDisabled = true
+        }
+        this.initDataList();
+      }
+    }
+  },
+  created() {
+    this.initData();
+    this.checkedStatusHandle();
+    this.debounceInfinite = debounce(this.resetInfiniteStatus)
+  },
+  methods: {
+    initData() {
+      this.initSelectItemArray();
+      this.initDataList();
+    },
+    initSelectItemArray() {
+      this.selectedList.forEach(item => {
+        this.selectedItemArray.push(this.infiniteItemPrimary(item));
+      })
+    },
+    initDataList() {
+      this.infiniteCheckedTotal = 0;
+      this.dataList.forEach(item => {
+        // 设置默认值
+        if (!item.hasOwnProperty('ischecked')) {
+          item.ischecked = false
+        }
+        // 全选功能
+        if (this.checkAll) {
+          item.ischecked = true
+        }
+        if (this.selectedItemArray.includes(this.infiniteItemPrimary(item))) {
+          // 右边列表存在,左边没有
+          item.ischecked = true
+        } else {
+          // 右边不存在,左边为选中
+          if (item.ischecked) {
+            this.selectedList.unshift(item)
+            this.selectedItemArray.unshift(this.infiniteItemPrimary(item))
+          }
+        }
+        // 计数
+        if (item.ischecked) {
+          this.infiniteCheckedTotal ++
+        }
+      })
+    },
+    onload() {
+      this.$emit('onload', this.keyword)
+    },
+    resetInfiniteStatus() {
+      this.infiniteListStatus = false
+      this.$emit('remote')
+      this.checkAll = false
+      this.$nextTick(() => {
+        this.infiniteListStatus = true
+      })
+    },
+    searchEvent() {
+      this.debounceInfinite();
+    },
+    infiniteItemPrimary(item) {
+      return item[this.primary]
+    },
+    infiniteItemName(item) {
+      if (typeof (this.fields) === 'string') {
+        return item[this.fields]
+      } else {
+        if (this.fields.length > 0) {
+
+        } else {
+          return item
+        }
+      }
+    },
+    handleCheckAllChange(checked) {
+      this.dataList.forEach(item => {
+        item.ischecked = checked;
+        // 右边是否存在记录
+        if (this.selectedItemArray.includes(this.infiniteItemPrimary(item))) {
+          if (!checked) {
+            for (let i = 0; i < this.selectedItemArray.length; i++) {
+              if (this.infiniteItemPrimary(item) === this.selectedItemArray[i]) {
+                this.selectedList.splice(i, 1);
+                this.selectedItemArray.splice(i, 1);
+                this.infiniteCheckedTotal --;
+                break;
+              }
+            }
+          }
+        } else {
+          if (checked) {
+            this.selectedList.unshift(item);
+            this.selectedItemArray.unshift(this.infiniteItemPrimary(item));
+            this.infiniteCheckedTotal ++;
+          }
+        }
+      })
+
+      this.isIndeterminate = false
+    },
+    checkboxClick(checked, item, index) {
+      if (checked) {
+        this.selectedList.unshift(item);
+        this.selectedItemArray.unshift(this.infiniteItemPrimary(item));
+        this.infiniteCheckedTotal ++;
+      } else {
+        item.ischecked = false
+        for (let i = 0; i < this.selectedItemArray.length; i++) {
+          if (this.infiniteItemPrimary(item) === this.selectedItemArray[i]) {
+            this.selectedList.splice(i, 1);
+            this.selectedItemArray.splice(i, 1);
+            this.infiniteCheckedTotal --;
+            break;
+          }
+        }
+      }
+
+      this.checkedStatusHandle()
+    },
+    handleClose(item, index) {
+      for (let i = 0; i < this.dataList.length; i++) {
+        if (this.infiniteItemName(item) === this.infiniteItemName(this.dataList[i])) {
+          this.dataList[i].ischecked = false;
+          break
+        }
+      }
+      this.selectedList.splice(index, 1);
+      this.selectedItemArray.splice(index, 1);
+      this.infiniteCheckedTotal --;
+
+      this.checkedStatusHandle()
+    },
+    checkedStatusHandle() {
+      if (this.dataList.length === 0) {
+        this.checkAll = false
+        return
+      }
+      if (this.dataList.length === this.infiniteCheckedTotal) {
+        this.checkAll = true
+      } else {
+        this.checkAll = false
+      }
+
+      this.isIndeterminate = false
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.infinite-transfer {
+  height: 280px;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  .infinite-list-main, .transfer-selected {
+    width: 45%;
+    height: 100%;
+    padding: 10px;
+    border-radius: 4px;
+    border: 1px solid #f2f2f2;
+    .infinite-list {
+      height: 220px;
+      line-height: 25px;
+      overflow: hidden;
+      margin-top: 10px;
+      overflow-y: auto;
+      .el-checkbox {
+        display: block;
+      }
+      .el-tag {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        margin-bottom: 5px;
+        i {
+          display: block;
+        }
+      }
+    }
+  }
+  .infinite-list-main {
+    .search-input-main {
+      display: flex;
+      align-items: center;
+      justify-content: flex-start;
+    }
+    .transger-number {
+      position: absolute;
+      bottom: 5px;
+      right: 55%;
+      margin-right: 10px;
+    }
+  }
+  .transfer-selected {
+    .total-main {
+      height: 32px;
+      line-height: 32px;
+    }
+  }
+}
+</style>

+ 8 - 0
packages/input-range/index.js

@@ -0,0 +1,8 @@
+import NtInputRange from './src/main';
+
+/* istanbul ignore next */
+NtInputRange.install = function(Vue) {
+  Vue.component(NtInputRange.name, NtInputRange);
+};
+
+export default NtInputRange;

+ 139 - 0
packages/input-range/src/main.vue

@@ -0,0 +1,139 @@
+<template>
+  <div class="nt-input-range" :style="inputItem[inputType].style ? inputItem[inputType].style : 'width: 95%'" :disabled="disabled || false">
+    <el-input v-model="inputStartValue"
+              :placeholder="inputOptions.startPlaceholder || inputOptions.placeholder || '请输入内容'"
+              :maxlength="inputOptions.startMaxlength || inputOptions.maxlength || ''"
+              :show-word-limit="inputOptions.startWordLimit || inputOptions.isWordLimit ||  false"
+              :disabled="disabled || false"
+              class="nt-input-range__input"
+              @input="val => handleDataInput(val, 0)"
+              :style="inputOptions.startStyle || inputOptions.style ||  ''"
+              :clearable="inputOptions.hasOwnProperty('clearable') ? inputOptions.clearable : false" autocomplete="off"></el-input>
+    <div class="nt-input-range__separator">{{inputOptions.separator || ' 至 '}}</div>
+    <el-input v-model="inputEndValue"
+              :placeholder="inputOptions.endPlaceholder || inputOptions.placeholder || '请输入内容'"
+              :maxlength="inputOptions.endMaxlength || inputOptions.maxlength || ''"
+              :show-word-limit="inputOptions.endWordLimit || inputOptions.isWordLimit ||  false"
+              :disabled="disabled || false"
+              class="nt-input-range__input"
+              @input="val => handleDataInput(val, 1)"
+              :style="inputOptions.endStyle || inputOptions.style ||  ''"
+              :clearable="inputOptions.hasOwnProperty('clearable') ? inputOptions.clearable : false" autocomplete="off"></el-input>
+  </div>
+</template>
+<script>
+  export default {
+    name: 'NtInputRange',
+    data: function () {
+      return {
+        inputStartValue: this.inputRangeValue(0),
+        inputEndValue: this.inputRangeValue(1)
+      }
+    },
+    watch: {
+      rowData: {
+        handler(newV, oldV) {
+          this.initRangeValue()
+        },
+        deep: true
+      }
+    },
+    props: {
+      inputItem: {
+        type: Object,
+        required: true
+      },
+      inputType: {
+        type: String,
+        required: true
+      },
+      field: {
+        type: String,
+        default: ''
+      },
+      rowData: {
+        default: function () {
+          return {
+            serialNumber: ''
+          }
+        }
+      },
+      disabled: {
+        default: function () {
+          return false
+        }
+      }
+    },
+    computed: {
+      inputOptions() {
+        return this.inputItem[this.inputType];
+      }
+    },
+    created() {
+      this.initRangeValue()
+    },
+    methods: {
+      initRangeValue() {
+        let tmpInputRanVal = null;
+
+        if (this.rowData.hasOwnProperty(this.field)) { // 是否有返回值
+          tmpInputRanVal = this.inputValueToData(this.rowData[this.field])
+        } else if (this.inputOptions.hasOwnProperty('value')) { // 是否存在默认值
+          tmpInputRanVal = this.inputValueToData(this.inputOptions.value)
+        } else { // 初始化值
+          tmpInputRanVal = []
+          const defaultValue = this.inputOptions.hasOwnProperty('value') === 'undefined' ? this.inputOptions.value : '';
+
+          tmpInputRanVal.push(defaultValue);
+          tmpInputRanVal.push(defaultValue);
+        }
+        if (this.rowData.hasOwnProperty(this.field)) {
+          this.rowData[this.field] = tmpInputRanVal;
+        } else {
+          this.$set(this.rowData, this.field, tmpInputRanVal)
+        }
+      },
+      inputRangeValue(index) {
+        return this.rowData[this.field][index]
+      },
+      inputValueToData(value) {
+        if (Array.isArray(value)) {
+          return value;
+        } else if (value) {
+          const currValue = value + '';
+          if (this.inputItem[this.inputType].hasOwnProperty('sign')) {
+            return currValue.split(this.inputItem[this.inputType].sign);
+          } else {
+            return currValue.split(',');
+          }
+        } else {
+          this.inputStartValue = ''
+          this.inputEndValue = ''
+          return ['', '']
+        }
+      },
+      handleDataInput(value, index) {
+        this.rowData[this.field][index] = value
+      }
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  .nt-input-range {
+    display: flex;
+    padding: 2px 0;
+    border-radius: 4px;
+    border: 1px solid #dcdfe6;
+    &__item {
+      display: flex;
+      align-items: center;
+      justify-content: flex-start;
+    }
+    &__separator, & ::v-deep input.el-input__inner, & ::v-deep &__input {
+      height: 26px;
+      border-width: 0;
+      line-height: 26px;
+    }
+  }
+</style>

+ 8 - 0
packages/input-table/index.js

@@ -0,0 +1,8 @@
+import NtInputTable from './src/main';
+
+/* istanbul ignore next */
+NtInputTable.install = function(Vue) {
+  Vue.component(NtInputTable.name, NtInputTable);
+};
+
+export default NtInputTable;

+ 157 - 0
packages/input-table/src/main.vue

@@ -0,0 +1,157 @@
+<template>
+  <div class="input-table-wraper" :style="{ height: height + 'px' }">
+    <div class="input-table__form">
+      <nt-form :ref="'inputTable_ref_' + field" :formRef="'inputTable_' + field" :rowData="formData" :inputType="inputType" :pageColumn="pageColumn" :selectList="selectList" :options="options" :axios="axios"></nt-form>
+      <div class="input-table__form-buttons">
+        <el-button size="small" @click="btnResetEvent()">重置</el-button>
+        <el-button size="small" type="primary" @click="btnClickEvent()">增加</el-button>
+      </div>
+    </div>
+
+    <em-table-list :tableListName="'tables_' + field" :ref="'tables_' + field" :source="'data'" :sourceData="sourceData" :axios="axios" :page_status="4" :tableButtonStatus="false" :autoSelectLoad="false" :page_column="pageColumn" :select_list="selectList" @onListEvent="onListEvent"></em-table-list>
+  </div>
+</template>
+
+<script>
+  export default {
+    name: 'NtInputTable',
+    props: {
+      height: {
+        type: Number,
+        default: 280
+      },
+      field: {
+        type: String,
+        default: function () {
+          return '-'
+        }
+      },
+      inputType: {
+        type: String,
+        default: function () {
+          return 'show'
+        }
+      },
+      inputItem: {
+        type: Object,
+        default: function () {
+          return {}
+        }
+      },
+      axios: {
+        type: Object,
+        default: function () {
+          return {};
+        }
+      },
+      rowData: {
+        type: Object | null | undefined,
+        default: function () {
+          return {};
+        }
+      },
+      pageColumn: {
+        type: Array,
+        required: true
+      },
+      selectList: {
+        type: Object | undefined,
+        default: function () {
+          return {};
+        }
+      },
+      publicParams: {
+        type: Object,
+        default: () => {}
+      }
+    },
+    data() {
+      return {
+        options: {
+          publicParams: this.publicParams || {}
+        },
+        formData: {},
+        sourceData: []
+      }
+    },
+    created() {
+      this.initData()
+    },
+    methods: {
+      initData() {
+        if (this.rowData.hasOwnProperty(this.field) && this.rowData[this.field].length > 0) {
+          this.sourceData.push(...this.rowData[this.field])
+        }
+      },
+      onListEvent(type, row) {
+        if (type === 'tableDel') {
+          this.sourceData.forEach((item, index) => {
+            let isChecked = true
+            for (let [k, v] of Object.entries(item)) {
+              if (v !== row[k]) {
+                isChecked = false
+              }
+            }
+            if (isChecked) {
+              this.sourceData.splice(index, 1)
+            }
+          })
+          this.rowData[this.field] = this.sourceData
+        }
+      },
+      btnResetEvent() { // 重置
+        for (let [k, v] of Object.entries(this.formData)) {
+          this.formData[k] = ''
+        }
+      },
+      btnClickEvent() { // 增加
+        console.log(this.$refs['inputTable_ref_' + this.field])
+        this.$refs['inputTable_ref_' + this.field].$refs['inputTable_' + this.field].validate((valid) => {
+          if (valid) {
+
+          }
+        })
+        const tmpFormData = {}
+
+        for (let [k, v] of Object.entries(this.formData)) {
+          tmpFormData[k] = v
+        }
+        this.sourceData.push(tmpFormData)
+
+        this.rowData[this.field] = this.sourceData
+        this.btnResetEvent()
+      }
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  .input-table-wraper {
+    height: 280px;
+    border: 1px solid #DCDFE6;
+    padding: 15px;
+    border-radius: 4px;
+    overflow: hidden;
+    overflow-y: auto;
+    .input-table__form {
+      display: flex;
+      align-items: flex-start;
+      justify-content: space-between;
+      & ::v-deep > .el-form > .el-form-bottom.el-mode-list {
+        padding: 0;
+        .em-form-content {
+          padding: 0;
+        }
+      }
+      &-buttons {
+        display: flex;
+        align-items: flex-start;
+        justify-content: space-between;
+      }
+    }
+    & ::v-deep .container-table-list {
+      padding: 0;
+    }
+  }
+</style>
+

+ 8 - 0
packages/input/index.js

@@ -0,0 +1,8 @@
+import NtInput from './src/main';
+
+/* istanbul ignore next */
+NtInput.install = function(Vue) {
+  Vue.component(NtInput.name, NtInput);
+};
+
+export default NtInput;

File diff suppressed because it is too large
+ 1285 - 0
packages/input/src/main.vue


+ 872 - 0
packages/input/src/tool-input.js

@@ -0,0 +1,872 @@
+import request from '@/components/thrid/em-element-ui/src/tools/request';
+import {isTypeof, isJsonString, createUUID, axiosReqParams, responseNodeParseList, responseKeyToValue, responseToResult, isArrayInnerValue, mergeRequestParams} from '@/components/thrid/em-element-ui/src/tools/utils';
+
+//初始化input值
+export function initInputData(_this) {
+    let item = _this.inputItem,
+      inputType = _this.inputType ? _this.inputType : 'show';
+
+    // 字段优先级使用
+    _this.inputPriField = item[inputType].field ? item[inputType].field : item.field
+    if(item[inputType]){
+        _this.inputItem[inputType].hideName = _this.inputItem[inputType].hasOwnProperty('hideName') ? _this.inputItem[inputType].hideName : (inputType == 'search' ? true : false);
+
+        if (item[inputType].type == 'text-tag') {
+            _this.$set(_this.dynamicTags, item.field, (_this.rowData[item.field] ? (isTypeof(_this.rowData[item.field]) == 'string' ? _this.rowData[item.field].split(',') : _this.rowData[item.field]) : []));
+        } else if (item[inputType].type == 'dynamic' || item[inputType].type == 'tree') {
+            dynamicInitData(_this, item, item);
+        } else if (item[inputType].type == 'file') {
+            let val = item[inputType].parent ? (_this.rowData[item[inputType].parent] && _this.rowData[item[inputType].parent][item.field] ? _this.rowData[item[inputType].parent][item.field] : (_this.rowData[item.field] ? _this.rowData[item.field] : [])) : (_this.rowData[item.field] ? _this.rowData[item.field] : []);
+
+            if (_this.rowData.hasOwnProperty(item.field)) {
+                _this.rowData[item.field] = val;
+            } else {
+                _this.$set(_this.rowData, item.field, val);
+            }
+
+            if(Array.isArray(_this.rowData[item.field])) {
+                if(_this.rowData[item.field] && _this.rowData[item.field].length > 0) {
+                    _this.rowData[item.field].forEach(row => {
+                        for(let [fk, fv] of Object.entries(item[inputType].props)) {
+                            if(!row.hasOwnProperty(fk)){
+                                row[fk] = row[fv];
+                            }
+                        }
+                    })
+                }
+            } else {
+                let tmpVal = [_this.rowData[item.field]]
+                if (item[inputType].iType === 'string') {
+                    tmpVal = _this.rowData[item.field].split(item[inputType].sign || ',')
+                }
+                _this.rowData[item.field] = []
+                // 重新赋值
+                tmpVal.forEach(itemVal => {
+                    _this.rowData[item.field].push({url: itemVal, name: itemVal})
+                })
+            }
+        } else if (item[inputType].type == 'key-val' || item[inputType].type == 'dialog' || item[inputType].type == 'task' || item[inputType].type == 'map') {
+            if (_this.dynamicStatus.hasOwnProperty(item.field)) {
+                _this.dynamicStatus[item.field] = false;
+            } else {
+                _this.$set(_this.dynamicStatus, item.field, false);
+            }
+            if(item[inputType].type == 'map') {
+                initMulFieldValue(_this);
+            }
+        } else if (item[inputType].type == 'select' && !item[inputType].mulField) {
+            if(_this.rowData[item.field]) {
+                if (item.stype == 'mapping') {
+                    let mappingItem = {
+                        label: _this.rowData[item.mapping],
+                        value: _this.rowData[item.field]
+                    };
+
+                    if (mappingItem.label && !isArrayInnerValue(_this.selectList[item[inputType].obj], mappingItem, 'value')) {
+                        _this.selectList[item[inputType].obj].unshift(mappingItem);
+                    }
+                }
+                if (item[inputType].multiple && item[inputType].iType == 'string') {
+                    _this.rowData[item.field] = isTypeof(_this.rowData[item.field]) == 'string' ? _this.rowData[item.field].split(item[inputType].sign) : _this.rowData[item.field];
+                }
+            } else {
+                if (!_this.rowData.hasOwnProperty(item.field)) {
+                    let val = isTypeof(item[inputType].value) === 'undefined' ? (_this.rowData.hasOwnProperty(item.field) ? _this.rowData[item.field] : '') : (isTypeof(item[inputType].value) === 'function' ? eval('(' + item[inputType].value + ')')(_this.rowData) : item[inputType].value);
+
+                    _this.$set(_this.rowData, item.field, val);
+                }
+            }
+            if (item[inputType].hasOwnProperty('subField')) {
+                let currSubFieldValue = '';
+                if (isTypeof(_this.selectList[item[inputType].obj]) === 'array') {
+                    for (let i = 0; i < _this.selectList[item[inputType].obj].length; i++) {
+                        if (_this.rowData[item.field] === _this.selectList[item[inputType].obj][i].value) {
+                            currSubFieldValue = _this.selectList[item[inputType].obj][i].label;
+                            break;
+                        }
+                    }
+                }
+                if (_this.rowData.hasOwnProperty(item[inputType].subField)) {
+                    _this.rowData[item[inputType].subField] = currSubFieldValue;
+                } else {
+                    _this.$set(_this.rowData, item[inputType].subField, currSubFieldValue);
+                }
+            }
+        } else if ((item[inputType].type == 'select' || item[inputType].type == 'cascader' || item[inputType].type == 'date-picker' || item[inputType].type == 'time-picker') && isTypeof(item[inputType].mulField) === 'object') {
+            initMulFieldValue(_this);
+        } else if (item[inputType].type == 'checkbox-group' || item[inputType].type == 'input-range') {
+            let val = isTypeof(item[inputType].value) === 'undefined' ? (_this.rowData.hasOwnProperty(item.field) ? _this.rowData[item.field] : '') : (isTypeof(item[inputType].value) === 'function' ? eval('(' + item[inputType].value + ')')(_this.rowData) : item[inputType].value);
+
+            let tmpVal = val ? (isTypeof(val) === 'array' ? val : (isTypeof(val) === 'string'  ? (val.split(item.sign || ',')) : [])) : []
+            if (_this.rowData.hasOwnProperty(item.field)) {
+                _this.rowData[item.field] = tmpVal;
+            } else {
+                _this.$set(_this.rowData, item.field, tmpVal);
+            }
+        } else {
+            // 优先使用item[inputType].value的值
+            let val = isTypeof(item[inputType].value) === 'undefined' ? (_this.rowData.hasOwnProperty(item.field) ? _this.rowData[item.field] : '') : (isTypeof(item[inputType].value) === 'function' ? eval('(' + item[inputType].value + ')')(_this.rowData) : item[inputType].value);
+            
+            // 设置值
+            if (_this.rowData.hasOwnProperty(item.field)) {
+                _this.rowData[item.field] = val
+            } else {
+                _this.$set(_this.rowData, item.field, val)
+            }
+
+            // 以下逻辑破坏了rowData属性的数据类型,暂时注释掉
+            // if (_this.rowData.hasOwnProperty(item.field)) {
+            //     const tmpRValue = _this.rowData[item.field] ? (isTypeof(_this.rowData[item.field]) === 'number' ? (_this.rowData[item.field] + '') : _this.rowData[item.field]) : (isTypeof(val) === 'number' ? (val + '') : val)
+            //     _this.rowData[item.field] = tmpRValue;
+            // } else {
+            //     isTypeof(_this.rowData) === 'object' && _this.$set(_this.rowData, item.field, (isTypeof(val) === 'number' ? (val + '') : val));
+            // }
+        }
+    }
+
+    //名字前增加必填的标识
+    iShowRequiredStatus(item, inputType);
+}
+
+//名字前增加必填的标识
+export function iShowRequiredStatus(item, inputType) {
+    const tmpRules = []
+
+    item.required = false;
+    // 全局的rules控制
+    if (item.rules && Array.isArray(item.rules)) {
+        const ruleStatus = item.hasOwnProperty(inputType) ? (item[inputType].hasOwnProperty('rule') ? item[inputType].rule : true) : true
+        if (ruleStatus) {
+            tmpRules.push(...new Set(item.rules))
+        }
+    }
+    // 自己inputType中的rules
+    if(item[inputType] && item[inputType].rules && Array.isArray(item[inputType].rules)) {
+        tmpRules.push(...new Set(item[inputType].rules))
+    }
+    for(let i = 0; i < tmpRules.length; i++) {
+        // required必填项
+        if(tmpRules[i].hasOwnProperty('required') && tmpRules[i].required) {
+            item.required = true
+            break;
+        }
+        // 多字段至少一个必填
+        if (tmpRules[i].hasOwnProperty('validator') && tmpRules[i].validator === 'validateMultipleFieldLeast') {
+            item.required = true
+            break;
+        }
+    }
+}
+
+// init mulField value
+export function initMulFieldValue(_this) {
+    let tmpValue = [],
+        flagValue = false,
+        tmpLastValue = null,
+        item = _this.inputItem,
+        currFieldValue = _this.rowData[item.field],
+        inputType = _this.inputType ? _this.inputType : 'show';
+
+    // 遍历对象数据
+    Object.keys(item[inputType].mulField).forEach(kk => {
+        if(_this.rowData[kk]) flagValue = true;
+        _this.rowData[kk] && tmpValue.push(_this.rowData[kk]);
+    })
+    // 重置值
+    tmpLastValue = item[inputType].type == 'cascader' ? (item[inputType].props && item[inputType].props.multiple ? [tmpValue] : tmpValue) : (flagValue ? (item[inputType].iType == 'string' ? tmpValue.join(item[inputType].sign ? item[inputType].sign : '#0') : tmpValue) : '');
+    if (_this.rowData.hasOwnProperty(item.field)) {
+        _this.rowData[item.field] = item[inputType].type == 'select' ? (item[inputType].multiple ? (tmpLastValue || currFieldValue || []) : (tmpLastValue || currFieldValue || '')) : tmpLastValue;
+    } else {
+        isTypeof(_this.rowData) === 'object' && _this.$set(_this.rowData, item.field, tmpLastValue);
+    }
+    if(item[inputType].type == 'select' && tmpLastValue) {
+        var tmpMap = {
+          label: tmpValue.join(' / '),
+          value: _this.rowData[item.field]
+        };
+        if (!isArrayInnerValue(_this.selectList[item[inputType].obj], tmpMap, 'value')) {
+            _this.selectList[item[inputType].obj].unshift(tmpMap);
+        }
+    }
+}
+
+//鼠标进入时,column的parseType:1
+export function parseDynamicDataFun1(item, inputType, value){
+    let res = [],
+        keyVals = {};
+
+    if(item[inputType].type == 'dynamic'){
+        keyVals = {
+            key: item[inputType].list && item[inputType].list[0] && item[inputType].list[0].field,
+            value: item[inputType].list && item[inputType].list[1] && item[inputType].list[1].field
+        }
+    }
+    if(value && value.length > 0){
+        if(typeof(value) === 'string'){
+            let splits = value.split('&&'),
+                mapKey = splits[0].split(','),
+                mapVals = splits[1].split(',');
+
+            for(let i = 0; i < mapKey.length; i++){
+                let tmpItem = {};
+
+                tmpItem[keyVals.key] = mapKey[i];
+                tmpItem[keyVals.value] = mapVals[i];
+
+                res.push(tmpItem);
+            }
+        }else{
+            value.forEach(iList => {
+                res.push(iList);
+            });
+        }
+    }else{
+        let tmpItem = {};
+
+        tmpItem[keyVals.key] = '';
+        tmpItem[keyVals.value] = '';
+
+        res.push(tmpItem);
+    }
+
+    return res;
+}
+
+//鼠标进入时,column的parseType:2
+export function parseDynamicDataFun2(item, inputType, value){
+    let res = [],
+        keyVals = {};
+
+    if(item[inputType].type == 'dynamic'){
+        keyVals = {
+            key: item[inputType].list && item[inputType].list[0] && item[inputType].list[0].field,
+            value: item[inputType].list && item[inputType].list[1] && item[inputType].list[1].field
+        }
+    }
+
+    if(value && value.length > 0){
+        let jsonList = JSON.parse(value);
+
+        if(isTypeof(jsonList) == 'array'){
+            jsonList.length > 0 && jsonList.forEach(iList => {
+                let tmpItem = {};
+
+                for(let [k, v] of Object.entries(iList)){
+                    tmpItem[keyVals.key] = k;
+                    tmpItem[keyVals.value] = v;
+                }
+
+                res.push(tmpItem);
+            });
+        }else if(isTypeof(jsonList) == 'object'){
+            for(let [k, v] of Object.entries(jsonList)){
+                let tmpItem = {};
+
+                tmpItem[keyVals.key] = k;
+                tmpItem[keyVals.value] = isTypeof(v) === 'string' ? v : JSON.stringify(v);
+
+                res.push(tmpItem);
+            };
+        }
+    }else{
+        let tmpItem = {};
+
+        tmpItem[keyVals.key] = '';
+        tmpItem[keyVals.value] = '';
+
+        res.push(tmpItem);
+    }
+
+    return res;
+}
+
+export function parseDynamicDataFun3(item, inputType, value) {
+    let res = [],
+      keyVals = item[inputType].props;
+
+    if(value && value.length > 0){
+        let jsonList = isTypeof(value) === 'string' ? JSON.parse(value) : '',
+            cTree = function (vals, resData) {
+                if(isTypeof(vals) == 'array'){
+                    vals.length > 0 && vals.forEach((node, index) => {
+                        if(isTypeof(node) == 'string'){
+                            let tmpItem = {};
+
+                            tmpItem[item[inputType].key] = createUUID();
+                            tmpItem[keyVals.label] = index;
+                            tmpItem[keyVals.value] = node;
+                            tmpItem[keyVals.children] = [];
+
+                            resData.push(tmpItem);
+                        }else{
+                            cTree(node, resData);
+                        }
+                    });
+                }else if(isTypeof(vals) == 'object'){
+                    for(let [k, v] of Object.entries(vals)){
+                        if(isTypeof(v) == 'array'){
+                            let tmpItem = {};
+
+                            tmpItem[item[inputType].key] = createUUID();
+                            tmpItem[keyVals.label] = k;
+                            tmpItem[keyVals.value] = '';
+                            tmpItem[keyVals.children] = [];
+
+                            v.length > 0 && v.forEach((node, index) => {
+                                cTree(node, tmpItem[keyVals.children]);
+                            });
+
+                            resData.push(tmpItem);
+                        }else if(isTypeof(v) == 'object'){
+                            let tmpItem = {};
+
+                            tmpItem[item[inputType].key] = createUUID();
+                            tmpItem[keyVals.label] = k;
+                            tmpItem[keyVals.value] = '';
+                            tmpItem[keyVals.children] = [];
+                            cTree(v, tmpItem[keyVals.children]);
+
+                            resData.push(tmpItem);
+                        }else{
+                            let tmpItem = {};
+
+                            tmpItem[item[inputType].key] = createUUID();
+                            tmpItem[keyVals.label] = k;
+                            tmpItem[keyVals.value] = v;
+                            tmpItem[keyVals.children] = [];
+
+                            resData.push(tmpItem);
+                        }
+                    }
+                }else if(isTypeof(vals) == 'string'){
+                    let tmpItem = Object.create(null);
+
+                    tmpItem[item[inputType].key] = createUUID();
+                    tmpItem[keyVals.label] = vals;
+                    tmpItem[keyVals.value] = vals;
+                    tmpItem[keyVals.children] = [];
+
+                    resData.push(tmpItem);
+                }
+            };
+
+        cTree(jsonList, res);
+    }else{
+        let tmpItem = {};
+
+        tmpItem[item[inputType].key] = createUUID();
+        tmpItem[keyVals.label] = '';
+        tmpItem[keyVals.value] = '';
+        tmpItem[keyVals.children] = [];
+
+        res.push(tmpItem);
+    }
+
+    return res;
+}
+
+export function createTreeRoot(_this, data) {
+    let newChild = { id: createUUID(), key: '', children: [] };
+
+    data.push(newChild);
+}
+
+export function appendTreeChildren(_this, data) {
+    let newChild = { id: createUUID(), key: '', children: [] };
+
+    if (!data.children) {
+        _this.$set(data, 'children', []);
+    }
+
+    data.children.push(newChild);
+}
+
+export function removeTreeNode(node, data) {
+    let parent = node.parent,
+      children = parent.data.children || parent.data,
+      index = children.findIndex(d => d.id === data.id);
+
+    children.splice(index, 1);
+}
+
+//根据parseType跳转不同的函数
+export function parseDynamicData(_this, item, inputItem){
+    let inputType = _this.inputType ? _this.inputType : 'show';
+
+    if(inputItem[inputType].hasOwnProperty('parseType')){
+        let parseRes = null;
+
+        switch(inputItem[inputType].parseType){
+            case 1:
+                parseRes = parseDynamicDataFun1(inputItem, inputType, _this.rowData[inputItem.field]);
+                break;
+            case 2:
+                parseRes = parseDynamicDataFun2(inputItem, inputType, _this.rowData[inputItem.field]);
+                break;
+            case 3:
+                parseRes = parseDynamicDataFun3(inputItem, inputType, _this.rowData[inputItem.field]);
+                break;
+        }
+
+        return isTypeof(parseRes) === 'array' ? parseRes : [];
+    }else{
+        if(isTypeof(_this.rowData[inputItem.field]) === 'array'){
+            return _this.rowData[inputItem.field];
+        }else{
+            throw new Error("dynamic的input值不是数组。");
+        }
+    }
+}
+
+//初始化弹出框,可以自定义函数或通过parseType跳转
+export function dynamicInitData(_this, item, inputItem){
+    let inputType = _this.inputType ? _this.inputType : 'show';
+
+    if(inputItem[inputType].type != 'dynamic' && inputItem[inputType].type != 'tree') return false;
+
+    let tmpValue = isTypeof(item[inputType].callback) === 'function'
+        ? item[inputType].callback(item, _this.rowData[inputItem.field])
+        : parseDynamicData(_this, item, inputItem);
+
+    if (_this.rowData.hasOwnProperty(item.field)) {
+        _this.rowData[item.field] = tmpValue;
+    } else {
+        _this.$set(_this.rowData, item.field, tmpValue);
+    }
+}
+
+//动态组中有多个字段,遍历出所有节点
+export function dynamicEvent(_this, inputItem){
+    let inputType = _this.inputType ? _this.inputType : 'show';
+
+    if(inputItem[inputType].hasOwnProperty('column')){
+        inputItem[inputType].column && inputItem[inputType].column.forEach(item => {
+            if(item[inputType].type == 'dynamic' || item[inputType].type == 'tree'){
+                if (_this.rowData.hasOwnProperty(item.field)) {
+                    _this.rowData[item.field] = _this.rowData[inputItem.field];
+                } else {
+                    _this.$set(_this.rowData, item.field, _this.rowData[inputItem.field]);
+                }
+                dynamicInitData(_this, item, inputItem);
+            }
+        });
+    }
+}
+
+//鼠标focus入口函数
+export function dynamicKeyToValEvent(_this, inputItem){
+    if(!_this.dynamicStatus[inputItem.field]){
+        dynamicEvent(_this, inputItem);
+        _this.dynamicStatus[inputItem.field] = true;
+    }
+}
+
+//确认输入内容提交回显到输入框中的值,column的parseType:1
+export function parseDynamicBackDataFun1(_this, item, inputItem){
+    let keys = [], vals = [], keyNames = {}, result = '',
+        inputType = _this.inputType ? _this.inputType : 'show';
+
+    keyNames.key = item[inputType].list[0].field;
+    keyNames.val = item[inputType].list[1].field;
+
+    if(isTypeof(_this.rowData[item.field]) === 'array'){
+        _this.rowData[item.field] && _this.rowData[item.field].forEach(dyItem => {
+            keys.push(dyItem[keyNames.key]);
+            vals.push(dyItem[keyNames.val]);
+        });
+    }
+
+    if(keys && keys.join()){
+        result = keys.join() + '&&' + vals.join();
+    }
+
+    return result;
+}
+
+//确认输入内容提交回显到输入框中的值,column的parseType:2
+export function parseDynamicBackDataFun2(_this, item, inputItem){
+    let keyNames = {}, result = '',
+        inputType = _this.inputType ? _this.inputType : 'show';
+
+    keyNames.key = item[inputType].list[0].field;
+    keyNames.val = item[inputType].list[1].field;
+
+    if(isTypeof(_this.rowData[item.field]) == 'array'){
+        let tmpObj = {}, resData = [];
+
+        _this.rowData[item.field].forEach(item => {
+            if(item[keyNames.key]){
+                tmpObj[item[keyNames.key]] = isJsonString(item[keyNames.val]) ? JSON.parse(item[keyNames.val]) : item[keyNames.val];
+            }
+        });
+
+        if(Object.keys(tmpObj).length > 1 || (Object.keys(tmpObj).length == 1 && Object.keys(tmpObj)[0])){
+            result = JSON.stringify(tmpObj);
+        }
+    }
+
+    return result;
+}
+
+//确认输入内容提交回显到输入框中的值,树形结构column的parseType:3
+export function parseDynamicBackDataFun3(_this, item, inputItem){
+    let inputType = _this.inputType ? _this.inputType : 'show',
+        keyNames = item[inputType].props,
+        result = '',
+        parseTree = function (tree, resTree) {
+            tree.forEach(item => {
+                let tmpMap = {};
+
+                if(item.children && item.children.length > 0){
+                    resTree[item[keyNames.label]] = [];
+                    parseTree(item.children, resTree[item[keyNames.label]]);
+                    tmpMap[item[keyNames.label]] = resTree[item[keyNames.label]];
+                }else{
+                    tmpMap[item[keyNames.label]] = item[keyNames.value];
+                }
+
+                resTree.push(tmpMap);
+            });
+        };
+
+    if(isTypeof(_this.rowData[item.field]) == 'array'){
+        let resData = [];
+
+        parseTree(_this.rowData[item.field], resData)
+
+        if(resData.length > 0 && (resData.length > 1 || (Object.keys(resData[0]).length > 1 || (Object.keys(resData[0]).length == 1 && Object.keys(resData[0])[0])))){
+            result = JSON.stringify(resData);
+        }
+    }
+
+    return result;
+}
+
+//系统内部自带解释函数
+export function parseDynamicBackData(_this, item, inputItem){
+    let inputType = _this.inputType ? _this.inputType : 'show';
+
+    if(item[inputType].hasOwnProperty('parseType')){
+        let parseRes = null;
+
+        switch(item[inputType].parseType){
+            case 1:
+                parseRes = parseDynamicBackDataFun1(_this, item, inputItem);
+                break;
+            case 2:
+                parseRes = parseDynamicBackDataFun2(_this, item, inputItem);
+                break;
+            case 3:
+                parseRes = parseDynamicBackDataFun3(_this, item, inputItem);
+                break;
+        }
+
+        //回显值
+        if(_this.rowData.hasOwnProperty(inputItem.field)){
+            _this.rowData[inputItem.field] = parseRes;
+        }else{
+            _this.$set(_this.rowData, inputItem.field, parseRes);
+        }
+    }
+}
+
+//任务规则组件
+export function tranEventBackData(_this, inputItem){
+    let data = {},
+        inputType = _this.inputType ? _this.inputType : 'show';
+
+    data['jobType'] = _this.rowData['taskType'];
+    if(_this.rowData['taskType'] == 7){
+        data[_this.rowData['taskunit']] = _this.rowData['tasktime'];
+        data['scheduleStartTime'] = _this.rowData['scheduleStartTime'];
+    }else{
+        data['month'] = _this.rowData['month'];
+        data['day'] = _this.rowData['day'];
+        data['hour'] = _this.rowData['hours'];
+        data['minute'] = _this.rowData['minutes'];
+        data['second'] = _this.rowData['seconds'];
+    }
+
+    let param = Object.assign({}, inputItem[inputType].queryUrl);
+
+    param['_axios'] = _this.axios;
+
+    createCronExpression(param, mergeRequestParams(data, _this.publicParams), _this.options).then(response => {
+        let taskValue = response;
+
+        if(response.hasOwnProperty('code') && response.code != 200){
+            taskValue = '';
+        }
+
+        if(_this.rowData.hasOwnProperty(inputItem.field)){
+            _this.rowData[inputItem.field] = taskValue;
+        }else{
+            _this.$set(_this.rowData, inputItem.field, taskValue);
+        }
+    });
+}
+
+//确认、取消按钮的条用
+export function parseDataEvent(_this, type, inputItem){
+    let inputType = _this.inputType ? _this.inputType : 'show';
+
+    _this.dynamicStatus[inputItem.field] = false;//隐藏弹出层
+    if(type === 'clear') return false;
+
+    if(inputItem[inputType].hasOwnProperty('column')){
+        if(inputItem[inputType] && inputItem[inputType].type == 'task'){
+            tranEventBackData(_this, inputItem);
+        }else{
+            inputItem[inputType].column && inputItem[inputType].column.forEach(item => {
+                if(item[inputType] && (item[inputType].type == 'dynamic' || item[inputType].type == 'tree')){
+                    if(isTypeof(item[inputType].dataCallback) === 'function'){
+                        item[inputType].dataCallback(item, inputItem);
+                    }else{
+                        parseDynamicBackData(_this, item, inputItem);
+                    }
+                }
+            });
+        }
+    }
+}
+
+//删除文件
+export function handleFileRemove(_this, item, file, fileList) {
+    let inputType = _this.inputType ? _this.inputType : 'show';
+    /* let num = 0, field = item.field;
+
+    _this.rowData[field].forEach((fileItem, fileIndex) => {
+        if (fileItem.url == file.url) {
+            num = fileIndex;
+        }
+    });
+    _this.rowData[field].splice(num, 1); */
+
+    // 成功后,提交请求
+    if (item[inputType].deleteAxios) {
+        const tmpParams = {};
+        const deleteAxios = item[inputType].deleteAxios;
+
+        // headers参数增加
+        if (deleteAxios.axios) {
+            deleteAxios.axios.headers = deleteAxios.axios.headers ? deleteAxios.axios.headers : item[inputType].headers
+        } else {
+            deleteAxios.axios = _this.axios
+        }
+        for (let [k, v] of Object.entries(deleteAxios.params)) {
+            if (isTypeof(v) === 'object') {
+                let tmpValue = null;
+                if (v.type === 'value') {
+                    tmpValue = v.value;
+                } else if (v.type === 'row') {
+                    tmpValue = _this.rowData[v.value];
+                } else {
+                    tmpValue = file[v.value];
+                }
+                tmpParams[k] = isTypeof(v.dataType) === 'array' ? [tmpValue] : tmpValue;
+            } else {
+                tmpParams[k] = file[v];
+            }
+        }
+
+        request(axiosReqParams(deleteAxios.base, mergeRequestParams(tmpParams, _this.publicParams), deleteAxios.axios, _this.options)).then((response) => {}).catch((err) => {});
+    }
+}
+
+//文件上传成功
+export function handleFileSucess(_this, item, response, file, fileList) {
+    var files = {},
+        inputType = _this.inputType ? _this.inputType : 'show',
+        fileRes = responseNodeParseList(response, item[inputType].node),
+        obj = item[inputType] && item[inputType].success ? item[inputType].success : {
+            key: 'errno',
+            value: 0,
+            filename: 'file'
+        };
+
+    obj.keyType = 'result';
+    if (responseKeyToValue(response, _this, _this, obj)) {
+        files.url = fileRes[responseToResult(_this, 'filename', obj)];
+        files.name = file.name;
+
+        // 增加提交参数
+        for ( let [k, v] of Object.entries(item[inputType].params)) {
+            if (isTypeof(v) === 'object') {
+                if (v.type === 'value') {
+                    files[k] = v.value;
+                } else {
+                    files[k] = '';
+                }
+            } else {
+                files[k] = k == 'url' ? (item[inputType].fileHost ? item[inputType].fileHost : '') + fileRes[v] : fileRes[v];
+            }
+        }
+
+        if (isTypeof(_this.rowData[item.field]) == 'array') {
+            _this.rowData[item.field].push(files);
+        } else {
+            _this.rowData[item.field] = [files];
+        }
+
+        // 成功后,提交请求
+        if (item[inputType].successAxios) {
+            const tmpParams = {};
+            const successAxios = item[inputType].successAxios;
+
+            // headers参数增加
+            if (successAxios.axios) {
+                successAxios.axios.headers = successAxios.axios.headers ? successAxios.axios.headers : item[inputType].headers
+            } else {
+                successAxios.axios = _this.axios
+            }
+            for ( let [k, v] of Object.entries(successAxios.params)) {
+                if (isTypeof(v) === 'object') {
+                    let tmpValue = null;
+                    if (v.type === 'value') {
+                        tmpValue = v.value;
+                    } else if (v.type === 'row') {
+                        tmpValue = _this.rowData[v.value];
+                    } else {
+                        tmpValue = fileRes[v.value];
+                    }
+                    tmpParams[k] = isTypeof(v.dataType) === 'array' ? [tmpValue] : tmpValue;
+                } else {
+                    tmpParams[k] = k == 'url' ? (item[inputType].fileHost ? item[inputType].fileHost : '') + fileRes[v] : fileRes[v];
+                }
+            }
+
+            request(axiosReqParams(successAxios.base, mergeRequestParams(tmpParams, _this.publicParams), successAxios.axios, _this.options)).then((response) => {}).catch((err) => {});
+        }
+    }
+}
+
+//文件上传失败
+export function handleFileError(_this, item, response, file, fileList){
+    var inputType = _this.inputType ? _this.inputType : 'show',
+        fileRes = item[inputType].errnode ? (responseNodeParseList(response, item[inputType].errnode) || response) : response,
+        obj = item[inputType] && item[inputType].success ? item[inputType].success : {
+          key: 'errno',
+          value: 0,
+          message: 'message'
+        };
+
+    _this.$message({type: 'error', message: fileRes[responseToResult(_this, 'message', obj)]});
+}
+
+export function editBtnClickEvent(_this, field) {
+    _this.datas[field].forEach((data, index) => {
+        if(_this.subEditTableIndex == index) {
+            _this.selectList[field].forEach(item => {
+                if(item.field != 'useropts'){
+                    data[item.field] = item.value;
+                    item.value = '';
+                }
+            });
+            _this.subTableStatus = false;
+            _this.subEditTableIndex = 0;
+        }
+    });
+}
+
+export function addBtnClickEvent(_this, field) {
+    let result = {};
+
+    _this.selectList[field].forEach(item =>{
+        if(item.field != 'useropts'){
+            result[item.field] = item.value;
+            item.value = '';
+        }
+    });
+    _this.datas[field].push(result);
+    _this.rowData[field] = _this.datas[field];
+}
+
+export function validateSubDiff(_this, field) {
+    var difflag = false, flag = false;
+
+    //是否为空记录,true为不空
+    _this.selectList[field].forEach(item => {
+        if(field == 'mac'){
+            if(item.field == 'mac'){
+                if(item.value == ''){
+                    flag = false;
+                }else{
+                    flag = true;
+                }
+            }
+        }else{
+            if(item['value'] != ''){
+                flag = true;
+            }
+        }
+    });
+
+    //是否相同记录,true为相同记录
+    _this.datas[field].forEach((data, index) => {
+        if(!difflag){
+            var lineflag = true;
+            _this.selectList[field].forEach(item => {
+                if(field == 'mac'){
+                    if(item.field == 'mac'){
+                        if(item['value'] != data.mac){
+                            lineflag = false;
+                        }
+                    }
+                }else{
+                    if(item['value'] != data[item.field]){
+                        lineflag = false;
+                    }
+                }
+            });
+            difflag = lineflag;
+        }
+    });
+
+    return !difflag && flag;
+}
+
+export function addSubTable(_this, field) {
+    //1.判断是否是修改;如果修改验证是否存在相同记录;如果为增加验证记录是否唯一
+    if(validateSubDiff(_this, field)){
+        if(_this.subTableStatus){
+            editBtnClickEvent(_this, field);
+        }else{
+            addBtnClickEvent(_this, field);
+        }
+    }else{
+        _this.$message({ type: 'error', message: '数据为空或者有相同记录!' });
+    }
+}
+
+export function pageBtnClickEvent(_this, item) {
+    let axios = item.axios || {},
+        queryData = item.param ? item.param : {};
+
+    request(axiosReqParams(item, mergeRequestParams(queryData, _this.publicParams), axios, _this.options)).then((response) => {
+        responseKeyToValue(response, _this, _this, item.success);
+    }).catch((err) => {});
+}
+
+export async function inputRemoteEvent(_this, item) {
+    let queryData = item.param ? item.param : {};
+
+    const result = await request(axiosReqParams(item, mergeRequestParams(queryData, _this.publicParams), item._axios, _this.options)).then((response) => {
+        let next = { keyType: 'result' };
+        let tmpData = []
+
+        if (responseKeyToValue(response, _this, _this, next)) {
+            tmpData = responseNodeParseList(response, item.parse) || []
+        }
+
+        return tmpData
+    }).catch((err) => {});
+
+    return result
+}
+
+export function createCronExpression(param, queryData, options) {
+    return request(axiosReqParams(param, queryData, param._axios, options));
+}
+
+
+

+ 8 - 0
packages/layout/index.js

@@ -0,0 +1,8 @@
+import NtLayout from './src/main';
+
+/* istanbul ignore next */
+NtLayout.install = function(Vue) {
+  Vue.component(NtLayout.name, NtLayout);
+};
+
+export default NtLayout;

+ 26 - 0
packages/layout/src/components/AppMain.vue

@@ -0,0 +1,26 @@
+<template>
+  <section class="app-main">
+    <transition name="fade" mode="out-in">
+      <keep-alive :include="cachedViews">
+        <router-view :key="key"></router-view>
+      </keep-alive>
+    </transition>
+  </section>
+</template>
+<script>
+// import ScrollBar from '@/components/ScrollBar'
+
+export default {
+  name: 'AppMain',
+  // components: { ScrollBar },
+  computed: {
+    cachedViews() {
+      return this.$store.state.tagsView.cachedViews
+    },
+    key() {
+      return this.$route.fullPath
+    }
+  }
+}
+
+</script>

+ 45 - 0
packages/layout/src/components/Breadcrumb/index.vue

@@ -0,0 +1,45 @@
+<template>
+  <el-breadcrumb class="app-breadcrumb" separator="/">
+    <transition-group name="breadcrumb">
+      <div v-for="(item,index)  in levelList" :key="item.path">
+        <el-breadcrumb-item v-if='item.meta.title'>
+          <span v-if='item.redirect==="noredirect"||index==levelList.length-1' class="no-redirect">{{item.meta.title}}</span>
+          <router-link v-else :to="item.redirect||item.path" style="cursor: pointer">{{item.meta.title}}</router-link>
+          <span style="display: inline-block;padding: 0 5px;" v-if="index != levelList.length - 1">/</span>
+        </el-breadcrumb-item>
+      </div>
+    </transition-group>
+  </el-breadcrumb>
+</template>
+<script>
+export default {
+  created() {
+    this.getBreadcrumb()
+  },
+  data() {
+    return {
+      levelList: null
+    }
+  },
+  watch: {
+    $route() {
+      this.getBreadcrumb()
+    }
+  },
+  methods: {
+    getBreadcrumb() {
+      let matched = this.$route.matched.filter(item => item.name)
+
+      const first = matched[0]
+      if (first && first.name !== 'home') {
+        matched = [].concat(matched)
+      } else {
+        matched = [{ path: '/home/index', meta: { title: '首页' } }]
+      }
+
+      this.levelList = matched
+    }
+  }
+}
+
+</script>

+ 24 - 0
packages/layout/src/components/Hamburger/index.vue

@@ -0,0 +1,24 @@
+<template>
+  <div>
+    <svg t="1492500959545" @click="toggleClick" class="svg-icon hamburger" :class="{'is-active':isActive}" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1691" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64">
+      <path d="M966.8023 568.849776 57.196677 568.849776c-31.397081 0-56.850799-25.452695-56.850799-56.850799l0 0c0-31.397081 25.452695-56.849776 56.850799-56.849776l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.849776l0 0C1023.653099 543.397081 998.200404 568.849776 966.8023 568.849776z" p-id="1692"></path>
+      <path d="M966.8023 881.527125 57.196677 881.527125c-31.397081 0-56.850799-25.452695-56.850799-56.849776l0 0c0-31.397081 25.452695-56.849776 56.850799-56.849776l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.849776l0 0C1023.653099 856.07443 998.200404 881.527125 966.8023 881.527125z" p-id="1693"></path>
+      <path d="M966.8023 256.17345 57.196677 256.17345c-31.397081 0-56.850799-25.452695-56.850799-56.849776l0 0c0-31.397081 25.452695-56.850799 56.850799-56.850799l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.850799l0 0C1023.653099 230.720755 998.200404 256.17345 966.8023 256.17345z" p-id="1694"></path>
+    </svg>
+  </div>
+</template>
+<script>
+export default {
+  name: 'hamburger',
+  props: {
+    isActive: {
+      type: Boolean,
+      default: false
+    },
+    toggleClick: {
+      type: Function,
+      default: null
+    }
+  }
+}
+</script>

+ 16 - 0
packages/layout/src/components/Navbar.vue

@@ -0,0 +1,16 @@
+<template>
+  <el-menu class="navbar" mode="horizontal">
+    <breadcrumb></breadcrumb>
+  </el-menu>
+</template>
+<script>
+import Breadcrumb from './Breadcrumb'
+
+export default {
+  components: {
+    Breadcrumb
+  },
+  methods: {}
+}
+
+</script>

+ 71 - 0
packages/layout/src/components/ScrollBar/index.vue

@@ -0,0 +1,71 @@
+<template>
+  <div class="scroll-container" ref="scrollContainer" @wheel="handleScroll">
+    <div class="scroll-wrapper" ref="scrollWrapper" :style="{top: top + 'px'}">
+      <slot></slot>
+    </div>
+  </div>
+</template>
+<script>
+const delta = 15
+
+export default {
+  name: 'scrollBar',
+  props: {
+    isResetTop: {
+      type: Boolean,
+      default: false
+    }
+  },
+  data() {
+    return {
+      top: 0
+    }
+  },
+  watch: {
+    $route() {
+      this.isResetTop && (this.top = 0)
+    }
+  },
+  methods: {
+    handleScroll(e) {
+      const eventPath = e.path
+      if (eventPath.some(this.pathCheck)) {
+        return
+      }
+      const eventDelta = e.wheelDelta || -e.deltaY * 3
+      const $container = this.$refs.scrollContainer
+      const $containerHeight = $container.offsetHeight
+      const $wrapper = this.$refs.scrollWrapper
+      const $wrapperHeight = $wrapper.offsetHeight
+      if (eventDelta > 0) {
+        this.top = Math.min(0, this.top + eventDelta)
+      } else {
+        if ($containerHeight - delta < $wrapperHeight) {
+          if (this.top >= -($wrapperHeight - $containerHeight + delta)) {
+            this.top = Math.max(this.top + eventDelta, $containerHeight - $wrapperHeight - delta)
+          }
+        } else {
+          this.top = 0
+        }
+      }
+    },
+    pathCheck(item) {
+      return item.className == 'el-scrollbar'
+    }
+  }
+}
+
+</script>
+<style rel="stylesheet/scss" lang="scss" scoped>
+.scroll-container {
+  position: relative;
+  width: 100%;
+  height: 100%;
+  // background-color: $navBg; //#545c64;
+  .scroll-wrapper {
+    position: absolute;
+    width: 100%!important;
+  }
+}
+
+</style>

+ 70 - 0
packages/layout/src/components/ScrollPane/index.vue

@@ -0,0 +1,70 @@
+<template>
+  <div class="scroll-container" style="height: 33px;overflow: hidden;" ref="scrollContainer" @wheel.prevent="handleScroll">
+    <div class="scroll-wrapper" ref="scrollWrapper" :style="{left: left + 'px'}">
+      <slot></slot>
+    </div>
+  </div>
+</template>
+<script>
+const padding = 15 // tag's padding
+
+export default {
+  name: 'scrollPane',
+  data() {
+    return {
+      left: 0
+    }
+  },
+  methods: {
+    handleScroll(e) {
+      const eventDelta = e.wheelDelta || -e.deltaY * 3
+      const $container = this.$refs.scrollContainer
+      const $containerWidth = $container.offsetWidth
+      const $wrapper = this.$refs.scrollWrapper
+      const $wrapperWidth = $wrapper.offsetWidth
+
+      if (eventDelta > 0) {
+        this.left = Math.min(0, this.left + eventDelta)
+      } else {
+        if ($containerWidth - padding < $wrapperWidth) {
+          if (this.left >= -($wrapperWidth - $containerWidth + padding)) {
+            this.left = Math.max(this.left + eventDelta, $containerWidth - $wrapperWidth - padding)
+          }
+        } else {
+          this.left = 0
+        }
+      }
+    },
+    moveToTarget($target) {
+      const $container = this.$refs.scrollContainer
+      const $containerWidth = $container.offsetWidth
+      const $targetLeft = $target.offsetLeft
+      const $targetWidth = $target.offsetWidth
+
+      if ($targetLeft < -this.left) {
+        // tag in the left
+        this.left = -$targetLeft + padding
+      } else if ($targetLeft + padding > -this.left && $targetLeft + $targetWidth < -this.left + $containerWidth - padding) {
+        // tag in the current view
+        // eslint-disable-line
+      } else {
+        // tag in the right
+        this.left = -($targetLeft - ($containerWidth - $targetWidth) + padding)
+      }
+    }
+  }
+}
+
+</script>
+<style rel="stylesheet/scss" lang="scss" scoped>
+.scroll-container {
+  white-space: nowrap;
+  position: relative;
+  overflow: auto;
+  width: 100%;
+  .scroll-wrapper {
+    position: absolute;
+  }
+}
+
+</style>

+ 98 - 0
packages/layout/src/components/Sidebar/SidebarItem.vue

@@ -0,0 +1,98 @@
+<template>
+  <div class="menu-wrapper" v-if="$store.state.app.menuType">
+    <i v-if="sidebar.opened" @click="toggleSideBar" class="iconfont em-opened el-icon-arrow-left"></i>
+    <i v-else @click="toggleSideBar" class="iconfont em-opened el-icon-arrow-right"></i>
+    <div v-for="(item, index) in routes" :key="index">
+      <template v-if="item.children">
+        <router-link v-if="item.children.length===0" :to="{}" @click.native="routerLinkButton(item, item.routePath + '/index')" :key="item.routeName">
+          <el-menu-item :index="item.routePath + '/index'" :class="{'submenu-title-noDropdown':!isNest}">
+            <i v-if="item.menuIcon" :class="[(item.menuIconFont ? item.menuIconFont : 'iconfont'),item.menuIcon]"></i>
+            <span v-if="item.menuName">{{item.menuName}}</span>
+          </el-menu-item>
+        </router-link>
+        <el-submenu v-else :index="item.routeName||item.routePath" :key="item.routeName">
+          <template slot="title">
+            <i v-if="item.menuIcon" :class="[(item.menuIconFont ? item.menuIconFont : 'iconfont'), item.menuIcon]"></i>
+            <span v-if="item.menuName">{{item.menuName}}</span>
+          </template>
+          <div v-for="(child, childIndex) in item.children" :key="childIndex">
+            <template v-if="!child.hidden">
+              <sidebar-item :is-nest="true" class="nest-menu" v-if="child.children&&child.children.length>0" :routes="[child]" :key="child.routePath"></sidebar-item>
+              <router-link v-else :to="{}" :key="child.routeName" @click.native="routerLinkButton(child, item.routePath+child.routePath)">
+                <el-menu-item :index="item.routePath+child.routePath">
+                  <i v-if="item.children[0].menuIcon" :class="[(child.menuIconFont ? child.menuIconFont : 'iconfont'), child.menuIcon]"></i>
+                  <span v-if="child.menuName">{{child.menuName}}</span>
+                </el-menu-item>
+              </router-link>
+            </template>
+          </div>
+        </el-submenu>
+      </template>
+    </div>
+  </div>
+  <div class="menu-wrapper" v-else>
+    <i v-if="sidebar.opened" @click="toggleSideBar" class="iconfont em-opened el-icon-arrow-left"></i>
+    <i v-else @click="toggleSideBar" class="iconfont em-opened el-icon-arrow-right"></i>
+    <div v-for="(item, index) in routes" :key="index">
+      <template v-if="!item.hidden&&item.children">
+        <router-link v-if="item.children.length===1 && !item.children[0].children && !item.alwaysShow" :to="item.path+item.children[0].path" :key="item.children[0].name">
+          <el-menu-item :index="item.path+item.children[0].path" :class="{'submenu-title-noDropdown':!isNest}">
+            <i v-if="item.children[0].meta&&item.children[0].meta.icon" :class="[(item.children[0].meta.class ? item.children[0].meta.class : 'iconfont'),item.children[0].meta.icon]"></i>
+            <span v-if="item.children[0].meta&&item.children[0].meta.title">{{item.children[0].meta.title}}</span>
+          </el-menu-item>
+        </router-link>
+        <el-submenu v-else :index="item.name||item.path" :key="item.name">
+          <template slot="title">
+            <i v-if="item.meta&&item.meta.icon" :class="[(item.meta.class ? item.meta.class : 'iconfont'),item.meta.icon]"></i>
+            <span v-if="item.meta&&item.meta.title">{{item.meta.title}}</span>
+          </template>
+          <div v-for="(child, childIndex) in item.children" :key="childIndex">
+            <template v-if="!child.hidden">
+              <sidebar-item :is-nest="true" class="nest-menu" v-if="child.children&&child.children.length>0" :routes="[child]" :key="child.path"></sidebar-item>
+              <router-link v-else :to="item.path+child.path" :key="child.name">
+                <el-menu-item :index="item.path+child.path">
+                  <i v-if="item.children[0].meta&&item.children[0].meta.icon" :class="[(child.meta.class ? child.meta.class : 'iconfont'),child.meta.icon]"></i>
+                  <span v-if="child.meta&&child.meta.title">{{child.meta.title}}</span>
+                </el-menu-item>
+              </router-link>
+            </template>
+          </div>
+        </el-submenu>
+      </template>
+    </div>
+  </div>
+</template>
+<script>
+export default {
+  name: 'SidebarItem',
+  data() {
+    return {
+      isNest: false
+    }
+  },
+  props: {
+    routes: {
+      type: Array,
+      required: true
+    },
+    sidebar: {
+      type: Boolean,
+      defalut: true
+    }
+  },
+  mounted() {},
+  methods: {
+    toggleSideBar() {
+      // this.$store.dispatch('toggleSideBar')
+    },
+    routerLinkButton(item, path) {
+      // this.$setSessionStorage('curr_auth_button', item.buttons)
+      //
+      // if (path !== this.$route.path) {
+      //   this.$router.push({ path: path })
+      // }
+    }
+  }
+}
+
+</script>

+ 38 - 0
packages/layout/src/components/Sidebar/index.vue

@@ -0,0 +1,38 @@
+<template>
+  <scroll-bar>
+    <el-menu :default-active="$route.path" :collapse="isCollapse" :background-color="options.menuBg" :text-color="options.textColor" :active-text-color="options.activeTextColor">
+      <sidebar-item :routes="routers"></sidebar-item>
+    </el-menu>
+  </scroll-bar>
+</template>
+<script>
+import SidebarItem from './SidebarItem'
+import ScrollBar from '../ScrollBar'
+
+export default {
+  name: 'NtSideBar',
+  props: {
+    routers: {
+      type: Array,
+      required: true
+    },
+    options: {
+      type: Object,
+      defalut: {
+        menuBg: '#324157',
+        textColor: '#fff',
+        activeTextColor: '#409EFF'
+      }
+    },
+    isCollapse: {
+      type: Boolean,
+      defalut: true
+    }
+  },
+  data() {
+    return {}
+  },
+  components: { SidebarItem, ScrollBar },
+  created: function() {}
+}
+</script>

+ 110 - 0
packages/layout/src/components/TagsView.vue

@@ -0,0 +1,110 @@
+<template>
+  <div class="tags-view-container">
+    <el-tooltip class="item" effect="light" content="鼠标滚动可查看隐藏标签" placement="top-end">
+      <scroll-pane class='tags-view-wrapper' ref='scrollPane'>
+        <router-link ref='tag' class="tags-view-item" :class="isActive(tag)?'active':''" v-for="tag in Array.from(visitedViews)" :to="tag.path" :key="tag.path" @contextmenu.prevent.native="openMenu(tag,$event)">
+          {{tag.title}}
+          <span class='el-icon-close' @click.prevent.stop='closeSelectedTag(tag)'></span>
+        </router-link>
+      </scroll-pane>
+    </el-tooltip>
+  </div>
+</template>
+<script>
+import ScrollPane from './ScrollPane'
+
+export default {
+  components: { ScrollPane },
+  data() {
+    return {
+      visible: false,
+      top: 0,
+      left: 0,
+      selectedTag: {}
+    }
+  },
+  computed: {
+    visitedViews() {
+      return '' // this.$store.state.tagsView.visitedViews
+    }
+  },
+  watch: {
+    $route() {
+      this.addViewTags()
+      this.moveToCurrentTag()
+    },
+    visible(value) {
+      if (value) {
+        document.body.addEventListener('click', this.closeMenu)
+      } else {
+        document.body.removeEventListener('click', this.closeMenu)
+      }
+    }
+  },
+  mounted() {
+    this.addViewTags()
+  },
+  methods: {
+    generateRoute() {
+      if (this.$route.name) {
+        return this.$route
+      }
+      return false
+    },
+    isActive(route) {
+      return route.path === this.$route.path || route.name === this.$route.name
+    },
+    addViewTags() {
+      const route = this.generateRoute()
+
+      if (!route) {
+        return false
+      }
+      this.$store.dispatch('addVisitedViews', route)
+    },
+    moveToCurrentTag() {
+      const tags = this.$refs.tag
+      this.$nextTick(() => {
+        for (const tag of tags) {
+          if (tag.to === this.$route.path) {
+            this.$refs.scrollPane.moveToTarget(tag.$el)
+            break
+          }
+        }
+      })
+    },
+    closeSelectedTag(view) {
+      this.$store.dispatch('delVisitedViews', view).then((views) => {
+        if (this.isActive(view)) {
+          const latestView = views.slice(-1)[0]
+          if (latestView) {
+            this.$router.push(latestView.path)
+          } else {
+            this.$router.push('/')
+          }
+        }
+      })
+    },
+    closeOthersTags() {
+      this.$router.push(this.selectedTag.path)
+      this.$store.dispatch('delOthersViews', this.selectedTag).then(() => {
+        this.moveToCurrentTag()
+      })
+    },
+    closeAllTags() {
+      this.$store.dispatch('delAllViews')
+      this.$router.push('/')
+    },
+    openMenu(tag, e) {
+      this.visible = true
+      this.selectedTag = tag
+      this.left = e.clientX
+      this.top = e.clientY
+    },
+    closeMenu() {
+      this.visible = false
+    }
+  }
+}
+
+</script>

+ 56 - 0
packages/layout/src/components/header.vue

@@ -0,0 +1,56 @@
+<template>
+  <div class="header">
+    <el-row :gutter="10">
+      <el-col v-for="(item, index) of options" :key="index" :xs="item.xs" :sm="item.sm" :md="item.md">
+        <!--Logo area-->
+        <div v-if="item.type === 'logo'" class="logo">
+          <span class="logo_prefix">{{item.prefix}}</span>
+          <span class="logo_suffix">{{item.suffix}}</span>
+        </div>
+        <div v-else-if="item.type === 'user'" class="download-header">
+          <div class="user-header">
+            <el-dropdown trigger="click">
+              <div class="avatar-wrapper">
+                <img class="user-avatar" :src="item.image">
+                <i class="el-icon-caret-bottom"></i>
+              </div>
+              <el-dropdown-menu slot="dropdown">
+                <el-dropdown-item v-for="(userItem, userIndex) of item.userList" :key="userIndex" divided>
+                  <div class="setting-div" @click="userInfoUpdate(userItem.type)">
+                    <span class="setting-icon"><i class="icon iconfont" :class="item.icon"></i></span>
+                    <span class="setting-string">{{userItem.name}}</span>
+                  </div>
+                </el-dropdown-item>
+              </el-dropdown-menu>
+            </el-dropdown>
+          </div>
+        </div>
+        <div v-else class="download-header">
+          <div class="avatar-wrapper-down" @click="userInfoUpdate(item.type)">
+            <img class="download-avatar" :src="item.image">
+          </div>
+        </div>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+<script>
+export default {
+  name: 'NtHeader',
+  props: {
+    options: {
+      type: Array,
+      required: true
+    }
+  },
+  data() {
+    return {}
+  },
+  inject: ['layout'],
+  methods: {
+    userInfoUpdate(evt) {
+      this.layout.emitHeaderEvent(evt)
+    }
+  }
+}
+</script>

+ 5 - 0
packages/layout/src/components/index.js

@@ -0,0 +1,5 @@
+export { default as Navbar } from './Navbar'
+export { default as Sidebar } from './Sidebar'
+export { default as AppMain } from './AppMain'
+export { default as Header } from './header'
+export { default as TagsView } from './TagsView'

+ 55 - 0
packages/layout/src/main.vue

@@ -0,0 +1,55 @@
+<template>
+  <div id="app">
+    <el-header v-if="!subpageFullScreen">
+      <Header :options="options.header"></Header>
+    </el-header>
+    <div class="app-wrapper" :class="{hideSidebar:!options.sidebar}">
+      <sidebar class="sidebar-container" :routers="options.routers" :options="options.menuHeader" v-if="!subpageFullScreen"></sidebar>
+      <div :class="!subpageFullScreen ? 'main-container' : 'subpageFullScreen margLeft0'">
+        <navbar v-if="!subpageFullScreen"></navbar>
+        <app-main></app-main>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+import { Navbar, Sidebar, AppMain, Header } from './components'
+
+export default {
+  name: 'NtLayout',
+  props: {
+    subpageFullScreen: {
+      type: Boolean,
+      defalut: true
+    },
+    options: {
+      type: Object,
+      required: true
+    }
+  },
+  provide() {
+    return {
+      layout: this
+    }
+  },
+  components: {
+    Navbar,
+    Sidebar,
+    AppMain,
+    Header
+  },
+  computed: {
+    sidebar() {
+      return this.$store.state.app.sidebar
+    }
+  },
+  methods: {
+    emitHeaderEvent(evt) {
+      this.$emit('layoutHeaderEvent', evt)
+    }
+  }
+}
+</script>
+<style lang="scss" scoped>
+
+</style>

+ 10 - 0
packages/list/index.js

@@ -0,0 +1,10 @@
+import EmTableList from './src/main';
+import Vue from 'vue'
+import {shortcutKeys} from '../../src/tools/directives'
+Vue.directive('shortcutKeys',shortcutKeys)
+/* istanbul ignore next */
+EmTableList.install = function(Vue) {
+  Vue.component(EmTableList.name, EmTableList);
+};
+
+export default EmTableList;

+ 154 - 0
packages/list/src/buttons.vue

@@ -0,0 +1,154 @@
+<template>
+    <div>
+        <el-row class="btn-list-border btn-form-inline " v-if="emTableList.showHead"
+            :class="(showHead || tableTitle) ? ' el-btn-row' : ' el-btn-no-row'" :gutter="30">
+            <el-col :span="(emTableList.options && emTableList.options.titleSpan ? emTableList.options.titleSpan : 8)"
+                v-if="tableButtonToAuth(64, 'search')" class="btn-list-left">
+                <span class="title text-overflow-ellipsis" @click.stop="reloadTableList">{{ emTableList.custTableTitle ||
+                    tableTitle}}</span>
+                <span v-if="emTableList.tableStatus" v-html="emTableList.tableStatus"></span>
+                <el-input placeholder="请输入内容" prefix-icon="el-icon-search" v-model="finds">
+                    <el-button @click="searchList()" size="mini" slot="append">查 询</el-button>
+                </el-input>
+            </el-col>
+            <el-col :span="(emTableList.options && emTableList.options.titleSpan ? emTableList.options.titleSpan : 8)"
+                v-else class="btn-list-left">
+                <template v-if="emTableList.leftButtonsList.length">
+                    <el-button v-for="(btn,i) in emTableList.leftButtonsList" size="mini" :type="btn.type" :icon="btn.icon"
+                    @click="eventBtnData(btn.event)">{{btn.name}}</el-button>
+                </template>
+                <span v-else class="title text-overflow-ellipsis"
+                    @click.stop="reloadTableList">{{ emTableList.custTableTitle ||
+                    tableTitle}}</span><span v-if="emTableList.tableStatus" v-html="emTableList.tableStatus"></span>
+            </el-col>
+            <el-col
+                :span="(emTableList.options && emTableList.options.buttonSpan ? emTableList.options.buttonSpan : 16)">
+                <el-button v-for="(btn, index) in tableButtonList()" :key="index"
+                    :size="btn.hasOwnProperty('size') ? btn.size : 'mini'" :type="btn.type ? btn.type : 'primary'"
+                    :icon="btn.icon" :plain="btn.shape === 'plain' ? true : false"
+                    :round="btn.shape === 'round' ? true : false" :circle="btn.shape === 'circle' ? true : false"
+                    :disabled="btn.hasOwnProperty('disabled') ? btn.disabled : false" :style="btn.style"
+                    @click="custBtnList(btn)">{{ btn.name }}</el-button>
+                <el-button v-if="tableButtonToAuth(16, 'add')" size="mini" type="primary" icon="el-icon-plus"
+                    @click="eventBtnData('add')">增加</el-button>
+                <el-button v-if="tableButtonToAuth(32, 'delAll')" size="mini" type="danger" icon="el-icon-delete"
+                    @click="eventBtnData('delAll')">删除</el-button>
+                <el-popover v-if="tableButtonToAuth(2, 'checkbox')" trigger="hover" transition="">
+                    <el-collapse-transition>
+                        <div class="filter-container">
+                            <el-checkbox-group v-model="checkboxVals" open-delay="3">
+                                <el-checkbox v-for="item in columnOptions" :key="item.name" :label="item"
+                                    v-if="item.field != 'oid' && item.field != 'useropts'">{{ item.name }}</el-checkbox>
+                            </el-checkbox-group>
+                        </div>
+                    </el-collapse-transition>
+                    <el-button type="success" slot="reference" size="mini" style="margin-left: 10px;"><i
+                            class="el-icon-date"></i></el-button>
+                </el-popover>
+            </el-col>
+        </el-row>
+    </div>
+
+</template>
+<script>
+import { isTypeof, isArrayInnerValue, checkAuthButtonList } from '@/components/thrid/em-element-ui/src/tools/utils';
+export default {
+    name: 'EmButtons',
+    data() {
+        return {
+            finds: '',
+            keys: 1,
+            tableTitle: '',
+            columnOptions: this.emTableList.checkboxVal,
+            checkboxVals: this.emTableList.checkboxVal,
+            showColumns: this.emTableList.checkboxVal,
+            showHead: ((this.emTableList.buttonsList.length > 0) || (this.emTableList.page_status & 32) == 32) || ((this.emTableList.page_status & 16) == 16) || ((this.emTableList.page_status & 2) == 2) || ((this.emTableList.page_status & 64) == 64)
+        };
+    },
+    inject: ['emTableList'],
+    created() {
+        this.initTableTitle();
+    },
+    watch: {
+        checkboxVals(valArr) {
+            this.showColumns = this.columnOptions.filter(item => valArr.indexOf(item) >= 0);
+            this.keys = this.keys + 1// 为了保证table 每次都会重渲
+
+            this.updateShowColumns();
+        }
+    },
+    methods: {
+        initTableTitle() {
+            let routeLastItem = this.$route.matched.filter(item => item.name).pop() || {}
+
+            this.tableTitle = routeLastItem.meta && routeLastItem.meta.title ? routeLastItem.meta.title : ''
+        },
+        tableButtonList() {
+            if (this.emTableList.buttonsList && this.emTableList.buttonsList.length > 0) {
+                return checkAuthButtonList(this.emTableList.buttonsList, this.emTableList.authButtonList, 'event');
+            } else {
+                return [];
+            }
+        },
+        // 指定按钮是否有权限
+        tableButtonToAuth(num, btn) {
+            if (this.emTableList
+                && (this.emTableList.page_status & num) == num
+                && (isTypeof(this.emTableList.authButtonList) === 'null' || isArrayInnerValue(this.emTableList.authButtonList, btn))
+            ) {
+                return true;
+            } else {
+                return false;
+            }
+        },
+        eventBtnData(type) {
+            const btnObj = { event: type };
+
+            this.emTableList.btnEventList(btnObj);
+        },
+        custBtnList(btnObj) {
+            if (btnObj.hasOwnProperty('callback')) {
+                btnObj.callback();
+            } else {
+                this.emTableList.btnEventList(btnObj);
+            }
+        },
+        updateShowColumns() {
+            var obj = {
+                keys: this.keys,
+                showColumns: this.showColumns
+            };
+
+            this.eventBtnData(obj);
+        },
+        reloadTableList() {
+            this.emTableList.initDataList();
+        },
+        searchList() {
+            var flag = true;
+            /*for(var key in this.finds){
+              if(this.finds[key].length > 0){
+                flag = true;
+              }
+            }*/
+            if (flag) {
+                this.emTableList.searchFormList(this.finds);
+            }
+        }
+    }
+}
+
+</script>
+<style rel="stylesheet/scss" lang="scss" scoped>
+.el-btn-row {
+    height: 60px;
+    line-height: 60px;
+    text-align: right;
+}
+
+.el-btn-no-row {
+    height: 20px;
+    line-height: 20px;
+    text-align: right;
+}
+</style>

File diff suppressed because it is too large
+ 513 - 0
packages/list/src/cardList.vue


+ 55 - 0
packages/list/src/list.vue

@@ -0,0 +1,55 @@
+<template>
+  <div class="nt-list-wrap">
+    <template v-for="(item, index) in listData">
+      <em-tables v-if="item === 'table'" :listType="listData" :emTableList="emTableList">
+        <template v-for="slot in (emTableList.slots)" v-slot:[slot]="data">
+          <slot :name="slot" v-bind="data"></slot>
+        </template>
+      </em-tables>
+      <nt-card-list v-if="item === 'card'" :listType="listData" :emTableList="emTableList"></nt-card-list>
+    </template>
+  </div>
+</template>
+<script>
+import EmTables from './tables';
+import NtCardList from './cardList';
+import { isTypeof } from '@/components/thrid/em-element-ui/src/tools/utils';
+
+export default {
+  name: 'NtList',
+  components: { EmTables, NtCardList },
+  inject: ['emTableList'],
+  props: {
+    listType: {
+      type: Array | String,
+      default: 'table'
+    }
+  },
+  data() {
+    return {
+      listData: []
+    }
+  },
+  watch: {},
+  created: function () {
+    if (isTypeof(this.listType) === 'string') {
+      this.listData.push(this.listType)
+    } else {
+      this.listData.push(...this.listType)
+    }
+  },
+  methods: {
+
+  }
+}
+</script>
+<style lang="scss" scoped>
+  .nt-list-wrap {
+    & > div {
+      margin-bottom: 15px;
+      &:last-child {
+        margin-bottom: 0;
+      }
+    }
+  }
+</style>

+ 448 - 0
packages/list/src/main.vue

@@ -0,0 +1,448 @@
+<template>
+  <div>
+    <!-- 左侧搜索条件 -->
+    <search-form-pro :options="searchOptions"></search-form-pro>
+
+    <div class="container-table-list" :class="{'table-list-only': (page_status&8) != 8, 'table-list-hide': ((page_status&8) != 8 && (page_status&4) != 4)}">
+      <!-- button list -->
+      <em-btn-list-pro v-if="tableButtonStatus" :options="btnOptions"></em-btn-list-pro>
+      <!-- 表格 -->
+      <nt-list v-if="(page_status&4) == 4" :listType="listType">
+        <template v-for="slot in (slots)" v-slot:[slot]="data">
+          <slot :name="slot" v-bind="data"></slot>
+        </template>
+        <!-- 当顶层调用组件获取数据时:v-slot:status="scope", $slots拿不到信息
+        <template v-for="slot in Object.keys($slots)" v-slot:[slot]="data">
+          <div>{{data.item}}
+            <slot :name="slot" v-bind="data"></slot>
+          </div>
+        </template> -->
+      </nt-list>
+
+      <!-- 分页 -->
+      <el-pagination
+        v-if="paginationTick && (page_status&8) == 8"
+        background
+        :style="pages.style"
+        :current-page="pages.currentPage"
+        :page-sizes="pages.pageSizes"
+        :page-size="pages.pageSize"
+        :layout="pages.layout"
+        :total="pages.totalCount"
+        @size-change="handleSizeChange"
+        @current-change="handleCurrentChange">
+      </el-pagination>
+    </div>
+
+    <!--弹出编辑dialog框-->
+    <el-dialog :title="row._title" :visible.sync="dialogEditTableVisible" class="nt-edit-dialog-form" :append-to-body="true" @close="setDialogData" :width="add_edit_dialog">
+      <nt-form v-if="dialogEditTableVisible" :rowData="row" :modeList="mode_list" :modeStatus="modeStatus" :inputType="inputType" :pageColumn="page_column" :selectList="select_list" :axios="axios" :queryURL="queryURL" :responseSuccess="responseSuccess" :labelPosition="labelPosition" :options="options" @reload="initDataList" @clear="subClearBtn" @handleModeEvent="handleModeEvent"></nt-form>
+    </el-dialog>
+
+    <!--弹出删除dialog框-->
+    <el-dialog :title="row._title" :visible.sync="dialogDelTableVisible" class="nt-delete-dialog-form" :append-to-body="true" @close="setDialogData" :width="del_dialog">
+      <nt-remove-form v-if="dialogDelTableVisible" :rowData="row" :axios="axios" :queryURL="queryURL" :responseSuccess="responseSuccess" :labelPosition="labelPosition" :options="options" @reload="initDataList" @clear="subClearBtn"></nt-remove-form>
+    </el-dialog>
+  </div>
+</template>
+<script>
+  import NtList from './list';
+  import searchFormPro from './search';
+  import emBtnListPro from './buttons';
+  import NtForm from '../../form';
+  import NtRemoveForm from '../../re-form';
+  import {initVueData, utilBtnEventList, unitTableAllEvent, searchSourceData, unitInitDataTableList, unitInitSelectList, reloadItemSelectList} from './tool-list';
+  import { isTypeof } from '@/components/thrid/em-element-ui/src/tools/utils';
+
+  export default {
+    name: 'DxTableList',
+    components: { NtRemoveForm, NtForm, emBtnListPro, searchFormPro, NtList },
+    props: {
+      listType: {
+        type: Array | String,
+        default: 'table'
+      },
+      autoLoad: {
+        type: Boolean,
+        default: true
+      },
+      tableButtonStatus: {
+        type: Boolean,
+        default: true
+      },
+      autoSelectLoad: {
+        type: Boolean,
+        default: true
+      },
+      tableListName: {
+        type: String,
+        required: true
+      },
+      custTableTitle: {
+        type: String,
+        default: ''
+      },
+      tableStatus: {
+        type: String,
+        default: ''
+      },
+      rowKey: {
+        type: String,
+        default: ''
+      },
+      treeProps: {
+        type: Object,
+        default: function() {
+          return { children: 'children', hasChildren: 'hasChildren' }
+        }
+      },
+      source: {
+        type: String,
+        default: 'query'
+      },
+      rowClassName: {
+        type: Function | String
+      },
+      border: {
+        type: Boolean,
+        default: true
+      },
+      stripe: {
+        type: Boolean,
+        default: true
+      },
+      sourceData: {
+        type: Array,
+        default: function () { return []; }
+      },
+      mode_list: {
+        type: Array,
+        default: function () { return []; }
+      },
+      modeStatus: {
+        type: Object,
+        default: function () {
+          return {
+            show: 1,
+            detail: 2
+          }
+        }
+      },
+      page_status: {
+        type: Number,
+        default: 12
+      },
+      page_column: {
+        type: Array,
+        required: true
+      },
+      select_list: {
+        type: Object,
+        default: function () { return {}; }
+      },
+      queryCustURL: {
+        type: Object,
+        default: function () { return {}; }
+      },
+      composeParam: {
+        type: Array,
+        default: function () { return ['id']; }
+      },
+      buttonsList: {
+        type: Array | Function,
+        default: function () { return []; }
+      },
+      leftButtonsList: {
+        type: Array | Function,
+        default: function () { return []; }
+      },
+      dialogFooter: {
+        type: Object,
+        default: function () { return {}; }
+      },
+      isMainLoading: {
+        type: Boolean,
+        default: false
+      },
+      queryParam: {
+        type: Array | Function,
+        default: function () {
+          return [{
+              type: 1,
+              key: 'page',
+              value: 'pageNum'
+          },{
+              type: 1,
+              key: 'size',
+              value: 'pageSize'
+          }];
+        }
+      },
+      axios: {
+        type: Object,
+        default: function () { return {timeout: 5000,headers: {}}; }
+      },
+      optRules: {
+        type: Object,
+        default: function () { return {}; }
+      },
+      responseSuccess: {
+        type: Object,
+        default: function () {
+          return {
+            key: 'errno',
+            value: 0,
+            message: 'message'
+          };
+        }
+      },
+      headerCellStyle: {
+        type: Object,
+        default: function () {
+          return {
+            padding: '7px 0',
+            background: '#f6f6f6',
+            color: '#999999'
+          }
+        }
+      },
+      bodyCellStyle: {
+        type: Object,
+        default: function () {
+          return { padding: '5px 0' }
+        }
+      },
+      options: {
+        type: Object,
+        default: function () {
+          return { lazy: false, titleSpan: 8, buttonSpan: 16 }
+        }
+      },
+      authButtonList: {
+        type: Array || Function || null,
+        default: function () {
+          return null;
+        }
+      },
+      isMultiple: {
+        type: Boolean,
+        default: false
+      },
+      showHead: {
+        type: Boolean,
+        default: true
+      }
+    },
+    provide() {
+      return {
+        emTableList: this
+      }
+    },
+    data() {
+      return initVueData(this.tableListName, ()=> {
+        return {
+          paginationTick: true,
+          queryURL: this.queryCustURL,// 自定义请求连接
+          btnOptions: {},// 按钮的参数
+          searchOptions: {},// 搜索条件参数
+          constData: {},
+          tmpAutoLoad: this.autoLoad
+        }
+      });
+    },
+    watch: {
+      sourceData: {
+        deep: true,
+        handler() {
+          this.initTableSourceData();
+        }
+      }
+    },
+    computed: {
+      slots(){
+        return this.$props.page_column.filter(f=>f.stype==='slot').map(m=>m.slot)
+      }
+    },
+    created: function() {
+      this.initData();
+      this.initDef();
+      // 初始化select数据
+      this.initSelectList();
+      // 初始化加载列表
+      if(this.source === 'data'){
+        // 静态数据加载列表
+        this.initTableSourceData();
+      }else{
+
+        // 动态加载列表,默认值为false;通过search加载列表
+        this.isMainLoading && this.initDataList();
+      }
+    },
+    methods: {
+      initData() {
+        console.log(this.$slots)
+        this.$set(this.constData, 'pageColumn', this.page_column);
+      },
+      initTableSourceData() {
+        this.loading = false;
+        this.tableData = this.sourceData;
+        this.pages.totalCount = this.sourceData.length;
+      },
+      setDialogData(){
+        this.row = {};
+      },
+      btnEventList(btnObj){
+        utilBtnEventList(this, btnObj);
+      },
+      tableBtnEvent(){
+        unitTableAllEvent(this, arguments);
+      },
+      initDataList() {
+        // 获取数据方式为动态加载是调起接口请求
+        if(this.source !== 'data') {
+          // 是否默认加载table列表
+          if (!this.tmpAutoLoad) {
+            this.tmpAutoLoad = true;
+            this.loading = false;
+            return false;
+          }
+          unitInitDataTableList(this);
+        }
+      },
+      handleSelectionChange(val){
+        this.multipleSelection = val;
+      },
+      handleSizeChange(val) {
+        this.pages.pageSize = val; //修改每页数据量
+        this.initDataList();
+      },
+      handleCurrentChange(val) { //跳转第几页
+        this.pages.pageNum = val;
+        this.initDataList();
+      },
+      initDef(){
+        this.checkboxVal = this.page_column;
+        this.showColumns = this.page_column;
+        this.columnOptions = this.page_column;
+        // 公用的参数
+        this._publicParams = (this.options && this.options.publicParams) ? this.options.publicParams : {}
+      },
+      searchFormList(finds){
+        if(isTypeof(finds) === 'object') {
+          for (let [k, v] of Object.entries(finds)) {
+            this.finds[k] = v;
+          }
+        } else {
+          this.finds = finds;
+        }
+        if(this.source == 'data'){
+          searchSourceData(this);
+        }else{
+          this.initDataList();
+        }
+      },
+      sortReloadData(obj){
+        this.$emit('sortReloadData', obj);
+        if(!obj.order){
+          this.order = {};
+        }else{
+          this.order[obj.prop] = obj.order;
+        }
+        this.initDataList();
+      },
+      initSelectList(demandList = null){
+        if (this.autoSelectLoad) {
+          unitInitSelectList(this, demandList);
+        }
+      },
+      reloadSelectList(type, field){
+        reloadItemSelectList(this, type, field);
+      },
+      subClearBtn(type) { //接收子组件参数
+        if(type == 'del'){
+          this.dialogDelTableVisible = false;
+        }else if(type == 'edit'){
+          this.dialogEditTableVisible = false;
+        }
+      },
+      handleUpdateRowEvent(row, item) {
+        if (this.$listeners && this.$listeners.onUpdateEvent && isTypeof(this.$listeners.onUpdateEvent) === 'function') {
+          this.$emit('onUpdateEvent', row, item);
+        }
+      },
+      handleModeEvent(mode, column, row) {
+        const type = mode.btn && mode.btn.type ? mode.btn.type : ''
+        if (type) {
+          this.$emit('onListFormEvent', mode, column, this.inputType, row);//自定义事件
+        }
+      },
+      handleCheckboxDisable(row, index) {
+        let res = true;
+
+        this.$emit('checkboxStatus', row, index, function(pRes){
+          res = pRes;
+        })
+
+        return res;
+      },
+      reRenderChildrenNodeAfterAdd(treeId, treeData) {
+        if (this.loadTableTreeNodeMap.has(treeId)) {
+          const {tree, treeNode, resolve} = this.loadTableTreeNodeMap.get(treeId);
+
+          resolve(treeData)
+        }
+      }
+    }
+  }
+
+</script>
+
+<style lang="scss">
+  .clearfix {
+    &:after {
+      visibility: hidden;
+      display: block;
+      font-size: 0;
+      content: " ";
+      clear: both;
+      height: 0;
+    }
+  }
+  .em-form-content{
+    padding: 0 20px 20px;
+  }
+  .el-del-btn-item {
+    padding: 20px 0;
+    margin: 0 -20px;
+    background-color: #ffffff;
+    box-shadow: 0 -1px 3px 0 rgba(0, 0, 0, .2);
+  }
+
+  .btn-item-footer {
+    display: flex;
+    flex-direction: row;
+    justify-content: flex-end;
+    align-items: flex-end;
+    margin-right: 50px;
+  }
+
+  .btn-bottom .el-form-bottom {
+    padding-bottom: 100px;
+  }
+
+  .el-form-bottom.el-mode-list{
+    padding: 30px 30px 0;
+  }
+
+  .btn-form-inline{
+    margin-bottom: 10px;
+  }
+
+  .container-table-list.table-list-only {
+    padding-bottom: 15px;
+  }
+
+  .container-table-list.table-list-hide {
+    display: none;
+  }
+</style>

+ 375 - 0
packages/list/src/search.vue

@@ -0,0 +1,375 @@
+<template>
+  <el-form v-if="(emTableList.page_status&1) == 1 && iShowSearch" :inline="true" :model="finds" ref="search"
+           size="small" class="demo-form-inline cust-search-line">
+    <div class="form-search-content" v-if="iShowFormInput">
+      <div class="form-input__only"
+           v-for="(item, index) in searchFormColumn" :key="index"
+           :style="item.search && item.search.findStyle ? item.search.findStyle : (item.findStyle ? item.findStyle : '')">
+        <nt-input v-if="item.currSearch" :inputItem="item" :rowData="currFinds" :inputType="'currSearch'"
+                  :selectList="emTableList.select_list" :axios="emTableList.axios" :queryURL="emTableList.queryURL"
+                  :responseSuccess="emTableList.responseSuccess" :publicParams="emTableList._publicParams"></nt-input>
+        <nt-input v-if="item.search" :inputItem="item" :rowData="finds" :inputType="'search'"
+                  :selectList="emTableList.select_list" :axios="emTableList.axios" :queryURL="emTableList.queryURL"
+                  :responseSuccess="emTableList.responseSuccess" :publicParams="emTableList._publicParams"
+                  @resetPageColumn="resetPageColumn"></nt-input>
+      </div>
+    </div>
+
+    <!-- 右侧按钮 -->
+    <el-form-item class="btn-search-list">
+      <el-button v-if="iShowButton('reset')" type="info" plain @click="resetForm()"> 重置</el-button>
+      <el-button v-if="iShowButton('query')" type="primary" v-shortcutKeys="{key:'s'}" @click="searchList()"> 查询</el-button>
+      <!--<el-button type="success">导出Excel</el-button>-->
+    </el-form-item>
+  </el-form>
+</template>
+<script>
+import NtInput from '../../input';
+import {isTypeof, arrayToFieldSort, isArrayInnerValue, toolResetPageColumn, toolsCascaderLoader} from '../../../src/tools/utils';
+export default {
+  name: 'EmSearchList',
+  components: {NtInput},
+  data() {
+    return {
+      finds: {},
+      keys: 1,
+      sleepTime: 0,
+      currFinds: {},
+      iShowSearch: false,
+      iShowFormInput: true,
+      searchFormColumn: [],
+      firstNumber: 0,
+      optionsFinds: {},
+      isFirstSearch: true,
+      firstDefaultField: []
+    };
+  },
+  inject: ['emTableList'],
+  created: function () {
+    this.initData();
+  },
+  watch: {
+    finds: {
+      deep: true,
+      handler() {
+        if (!this.isFirstSearch) {
+          this.timer && clearTimeout(this.timer);
+          this.timer = setTimeout(() => {
+            if (this.emTableList.options && this.emTableList.options.responseSearch) {
+              this.searchList();
+            }
+          }, 500)
+        }
+      }
+    }
+  },
+  methods: {
+    initCurrFindsData(item) {// 是否有前端字段配置, currFinds赋值
+      if (item.currSearch && item.currSearch.hasOwnProperty('value')) {
+        let tmpFindsValue = '';
+
+        if (isTypeof(item.currSearch.value) === 'function') {
+          tmpFindsValue = eval('(' + item.currSearch.value + ')')();
+        } else {
+          tmpFindsValue = item.currSearch.value;
+        }
+        let tmpField = item.currSearch.field ? item.currSearch.field : item.field
+        if (this.currFinds.hasOwnProperty(tmpField)) {
+          this.currFinds[tmpField] = tmpFindsValue
+        } else {
+          this.$set(this.currFinds, tmpField, tmpFindsValue)
+        }
+      }
+    },
+    cascaderLoaderComponent(itemSearch, value) {
+      // 查询中重新loading
+      if (itemSearch.hasOwnProperty('cascaderLoader')) {
+        toolsCascaderLoader(this.emTableList, itemSearch, value, this.emTableList.select_list, this.finds, false)
+      }
+    },
+    initSearchValue(item) {// 赋初始值,finds赋值
+      let tmpField = this.currFinds.hasOwnProperty(item.search.findField) ? this.currFinds[item.search.findField] : (item.search.field ? item.search.field : item.field);
+
+      if (this.optionsFinds.hasOwnProperty(tmpField)) { // nt-table是否默认值过来
+        let tmpFindsValue = this.optionsFinds[tmpField]
+
+        if (this.finds.hasOwnProperty(tmpField)) {
+          this.finds[tmpField] = tmpFindsValue
+        } else {
+          this.$set(this.finds, tmpField, tmpFindsValue)
+        }
+      } else {
+        if (item.search.hasOwnProperty('value')) { // 组件默认value
+          let tmpFindsValue = '';
+
+          if (isTypeof(item.search.value) === 'function') {
+            tmpFindsValue = eval('(' + item.search.value + ')')(this.emTableList.select_list);
+          } else {
+            tmpFindsValue = item.search.value;
+          }
+          if (this.finds.hasOwnProperty(tmpField)) {
+            this.finds[tmpField] = tmpFindsValue
+          } else {
+            this.$set(this.finds, tmpField, tmpFindsValue)
+          }
+        } else if (item.search.hasOwnProperty('first') && item.search.first) { // 动态下拉列表选中第一个
+          if (isTypeof(this.emTableList.select_list[item.search.obj]) === 'array' && this.emTableList.select_list[item.search.obj].length > 0) {
+            const tmpFindsValue = this.emTableList.select_list[item.search.obj][0].value;
+            if (this.finds.hasOwnProperty(tmpField)) {
+              this.finds[tmpField] = tmpFindsValue;
+            } else {
+              this.$set(this.finds, tmpField, tmpFindsValue);
+            }
+          } else {
+            this.firstDefaultField.push(item)
+          }
+        }
+      }
+    },
+    // 初始化第一次未初始的值
+    resetInitFirstValue() {
+      if (this.firstDefaultField.length > 0) {
+        this.firstNumber++;
+        this.firstTimes = setTimeout(() => {
+          let firstStatus = false
+          this.firstDefaultField.forEach((item, index) => {
+            let tmpField = this.currFinds.hasOwnProperty(item.search.findField) ? this.currFinds[item.search.findField] : (item.search.field ? item.search.field : item.field);
+            if (isTypeof(this.emTableList.select_list[item.search.obj]) === 'array' && this.emTableList.select_list[item.search.obj].length > 0) {
+              const tmpFindsValue = this.emTableList.select_list[item.search.obj][0].value;
+              if (this.finds.hasOwnProperty(tmpField)) {
+                this.finds[tmpField] = tmpFindsValue;
+              } else {
+                this.$set(this.finds, tmpField, tmpFindsValue);
+              }
+              this.firstDefaultField.splice(index, 1);
+              // 查询中重新loading
+              this.cascaderLoaderComponent(item.search, tmpFindsValue)
+            } else {
+              firstStatus = true
+            }
+          })
+          if (firstStatus && this.firstDefaultField.length > 0 && this.firstNumber < 5) {
+            this.resetInitFirstValue();
+          } else {
+            clearTimeout(this.firstTimes);
+            if (!this.emTableList.isMainLoading) {
+              this.searchList();
+              this.isFirstSearch = false
+            }
+          }
+        }, 500)
+      } else {
+        this.firstTimes = setTimeout(() => {
+          if (!this.emTableList.isMainLoading) {
+            this.searchList();
+            this.isFirstSearch = false
+          }
+          clearTimeout(this.firstTimes);
+        }, 100);
+      }
+    },
+    iShowButton(name) {
+      if (isTypeof(this.emTableList.authButtonList) === 'null') {
+        return true
+      } else {
+        return isArrayInnerValue(this.emTableList.authButtonList, name);
+      }
+    },
+    initData() {
+      if ((this.emTableList.page_status & 1) == 1) {
+        this.searchFormColumn = [];
+        this.firstDefaultField = [];
+        this.optionsFinds = (this.emTableList.options && this.emTableList.options.finds) || {}
+
+        this.emTableList.showColumns && this.emTableList.showColumns.forEach(item => {
+          this.initCurrFindsData(item);// 是否有前端字段配置
+
+          if (item.search) {// 默认查询条件
+            this.iShowSearch = isTypeof(this.emTableList.authButtonList) === 'null' ? true : ((this.iShowButton('query') || this.iShowButton('reset')) ? true : false);
+            this.searchFormColumn.push(item);
+
+            // 是否使用缓存条件
+            if (this.emTableList.options
+                && this.emTableList.options.hasOwnProperty('localFind') && this.emTableList.options.localFind) {
+              this.finds = localStorage.getItem(this.emTableList.tableListName)
+            } else {
+              this.initSearchValue(item); // 赋初始值
+            }
+          }
+        })
+
+        this.resetPageColumn()
+      }
+      this.resetInitFirstValue();
+    },
+    resetPageColumn(value = '', inputItem = {}) {
+      this.searchFormColumn = arrayToFieldSort(this.emTableList.showColumns, 'search')
+      this.searchFormColumn = this.searchFormColumn.filter(item => item.search)
+
+      this.iShowFormInput = false;
+      this.$nextTick(() => {
+        this.searchFormColumn = toolResetPageColumn(this.emTableList, this.emTableList.showColumns, this.searchFormColumn, this.finds, 'search', arguments.length > 0 ? true : false)
+
+        this.iShowFormInput = true
+      })
+    },
+    resetForm() {
+      if (isTypeof(this.finds)) {
+        for (let [k, v] of Object.entries(this.finds)) {
+          this.finds[k] = ''
+        }
+      }
+
+      this.firstDefaultField = [];
+      // 初始化数据
+      this.searchFormColumn && this.searchFormColumn.forEach(item => {
+        this.initCurrFindsData(item);// 是否有前端字段配置
+
+        this.initSearchValue(item); // 赋初始值
+      })
+
+      // 联动字段关联
+      this.resetPageColumn();
+
+      this.emTableList.tmpAutoLoad = this.emTableList.autoLoad;
+      this.emTableList.paginationTick = false;
+      this.$nextTick(() => {
+        this.emTableList.pages.pageNum = 1;
+        this.emTableList.searchFormList(this.finds);
+        this.emTableList.paginationTick = true;
+      });
+    },
+    searchList() {
+      let flag = true, tmpFinds = {}, message = '';
+
+      // 增加查询参数
+      if (isTypeof(this.finds) === 'object') {
+        for (let [k, v] of Object.entries(this.finds)) {
+          if (this.currFinds.hasOwnProperty(k)) {
+            this.finds[this.currFinds[k]] = v;
+          }
+        }
+        // 过滤多选的参数
+        this.searchFormColumn && this.searchFormColumn.forEach(item => {
+          let field = '', dataType = '', label = '', required = false;
+
+          if (!item.hasOwnProperty('currSearch')) {
+            label = item.search.name ? item.search.name : item.name;
+            dataType = item.search.dataType ? item.search.dataType : '';
+            required = item.search.required || false;
+            field = item.search.field ? item.search.field : item.field;
+          } else {
+            if (this.currFinds.hasOwnProperty(item.search.findField)) {
+              field = this.currFinds[item.search.findField];
+            }
+            // 值的类型校验
+            this.emTableList.select_list[item.currSearch.obj].forEach(option => {
+              required = option.required || false;
+              if (option.dataType && field === option.value) {
+                dataType = option.dataType;
+                label = option.label;
+              }
+            })
+          }
+
+          if (field && this.emTableList.tmpAutoLoad) {
+            tmpFinds[field] = this.finds[field];
+
+            // 是否必填
+            if (required) {
+              if (!this.finds[field]) {
+                flag = false;
+                if (item.hasOwnProperty('search') && item.search.hasOwnProperty('isNull') && item.search.isNull) {
+                  if (this.emTableList.select_list[item.search.obj].length > 0) {
+                    message = label + '不能为空,请输入';
+                  } else {
+                    message = ''
+                  }
+                } else {
+                  message = label + '不能为空,请输入';
+                }
+              }
+            }
+            // 值合法校验
+            if (this.finds.hasOwnProperty(field)) {
+              switch (dataType) {
+                case 'number':
+                  if (this.finds[field] !== '' && isNaN(this.finds[field])) {
+                    flag = false;
+                    message = label + '不是数字类型,请确认!';
+                  }
+                  break;
+                case 'rangeType':
+                  if (this.finds[field] && this.finds[field].length == 2) {
+                    let endTimer = new Date(this.finds[field][1]).getTime(),
+                        startTimer = new Date(this.finds[field][0]).getTime(),
+                        rangeNumber = item.search.rangeNumber * 24 * 60 * 60 * 1000
+
+                    if (item.search.model === 'daterange') {
+                      endTimer = Number(endTimer) + 24 * 60 * 60 * 1000
+                    }
+                    if ((endTimer - startTimer) > rangeNumber) {
+                      flag = false;
+                      message = `查询时间范围不能超过${item.search.rangeNumber}天`;
+                    }
+                  }
+                  break;
+              }
+            }
+          }
+        })
+        // 去掉父级find由于currSearch多于的参数
+        for (let [k, v] of Object.entries(this.finds)) {
+          if (!tmpFinds.hasOwnProperty(k)) {
+            delete this.emTableList.finds[k];
+          }
+        }
+      }
+
+      if (flag) {
+        this.emTableList.paginationTick = false;
+        // 是否使用缓存查询条件
+        if (this.emTableList.options
+            && this.emTableList.options.hasOwnProperty('localFind') && this.emTableList.options.localFind) {
+          localStorage.setItem(this.emTableList.tableListName, tmpFinds)
+        }
+        this.$nextTick(() => {
+          this.emTableList.pages.pageNum = 1;
+          this.emTableList.searchFormList(tmpFinds);
+          this.emTableList.paginationTick = true;
+        });
+      } else {
+        this.emTableList.loading = false
+        message && this.$message.error(message);
+      }
+    }
+  }
+};
+
+</script>
+<style lang="scss">
+.cust-search-line {
+  display: flex;
+  justify-content: space-between;
+
+  .form-search-content {
+    flex: 1;
+
+    .form-input__only {
+      display: flex;
+      float: left;
+    }
+  }
+
+  .el-form-item.btn-search-list {
+    width: 150px;
+    margin-right: 0;
+    text-align: right;
+
+    .el-form-item__content {
+      display: flex;
+      justify-content: flex-end;
+    }
+  }
+}
+</style>

+ 803 - 0
packages/list/src/tables copy.vue

@@ -0,0 +1,803 @@
+<template>
+  <el-table
+    :data="emTableList.tableData"
+    :key="emTableList.tableName"
+    v-loading="emTableList.loading"
+    :lazy="
+      emTableList.options.hasOwnProperty('lazy')
+        ? emTableList.options.lazy
+        : true
+    "
+    :load="loadChildren"
+    :tree-props="emTableList.treeProps"
+    :stripe="emTableList.stripe === false ? false : true"
+    style="width: 100%"
+    :row-key="emTableList.rowKey"
+    ref="multipleTable"
+    @sort-change="sortReloadData"
+    @selection-change="handleSelectionChange"
+    :cell-style="emTableList.bodyCellStyle"
+    :header-cell-style="emTableList.headerCellStyle"
+    :row-class-name="tableRowClassName"
+    :border="emTableList.border === false ? false : true"
+    @filter-change="filterColHandler"
+  >
+        <!-- :type="item.stype==='checkbox'?'selection':''" -->
+      <el-table-column
+        v-for="(item, index) in emTableList.showColumns"
+        :key="index"
+        v-if="isHideColumn(item)"
+        type="selection"
+        :header-align="item.headerAlign ? sub.headerAlign : 'center'"
+        :prop="item.field"
+        :width="item.width ? item.width : 'auto'"
+        :align="item.align ? item.align : 'center'"
+        :sortable="item.sort ? item.sort : false"
+        :fixed="item.fixed ? item.fixed : false"
+        :show-overflow-tooltip="item.tooltip === false ? false : true"
+        :filters="item.filters ? filtersList[item.field] : null"
+        :column-key="item.field ? item.field : ''"
+        :filter-multiple="item.filterMultiple ? item.filterMultiple : false"
+        :formatter="item.formatter ? formatter : formatterDef"
+        :class-name="item.className"
+      >
+        <!--list属性-->
+        <template v-if="item.stype == 'list'">
+          <el-table-column
+            v-for="(sub, subIndex) in item.sub"
+            :key="subIndex"
+            :header-align="sub.headerAlign ? sub.headerAlign : 'center'"
+            :prop="sub.field"
+            :label="sub.name"
+            :width="sub.width ? sub.width : ''"
+            :align="sub.align ? sub.align : 'center'"
+            :sortable="sub.sort ? sub.sort : false"
+            :fixed="sub.fixed ? sub.fixed : false"
+            :formatter="sub.formatter ? formatter : formatterDef"
+            :show-overflow-tooltip="true"
+            :filters="item.filters ? filtersList[item.field] : null"
+            :column-key="item.field"
+            :filter-multiple="item.filterMultiple ? item.filterMultiple : false"
+          ></el-table-column>
+        </template>
+        <template slot="header">
+          <ul class="table-expand-list" v-if="item.expand">
+            <li
+              v-for="(expandItem, expandIndex) of scope.row[item.expand.list]"
+              :key="expandIndex"
+            >
+              <div class="name" :style="item.expand.nameStyle">
+                {{ expandItem[item.expand.key] }}:
+              </div>
+              <div class="value" :style="item.expand.valueStyle">
+                {{ expandParseValue(expandItem, item.expand) }}
+              </div>
+            </li>
+          </ul>
+          <el-tooltip
+            v-if="item.hasOwnProperty('note') && item.note"
+            class="item"
+            effect="dark"
+            :content="item.note"
+            placement="top"
+          >
+            <span class="icon-name icon-info"
+              ><i class="el-icon-info"></i
+            ></span>
+          </el-tooltip>
+          <el-button
+            v-if="item.hasOwnProperty('explain') && item.explain"
+            icon="el-icon-info"
+            style="padding: 0; margin-right: 4px"
+            circle
+            @click="emTableList.tableBtnEvent(item, { type: item.explain })"
+          ></el-button>
+          <span class="icon-name icon-info-name">{{ item.name }}</span>
+        </template>
+        <template slot-scope="scope" v-if="false">
+          <!--按钮-->
+          <template v-if="item.stype === 'opt'">
+            <el-button
+              v-for="(opt, optIndex) in tableButtonList(item, scope)"
+              :key="optIndex"
+              :type="opt.bType ? opt.bType : 'text'"
+              :style="opt.style ? opt.style : ''"
+              :icon="opt.icon ? opt.icon : ''"
+              :size="opt.size ? opt.size : 'small'"
+              :plain="opt.shape === 'plain' ? true : false"
+              :round="opt.shape === 'round' ? true : false"
+              :circle="opt.shape === 'circle' ? true : false"
+              :disabled="opt.hasOwnProperty('disabled') ? opt.disabled : false"
+              @click="optCellData(scope.row, opt)"
+            >
+              {{ opt.name }}
+            </el-button>
+          </template>
+          <!--tag标签-->
+          <span v-else-if="item.stype === 'tag'">
+            <el-popover
+              v-if="item.tooltip === false"
+              placement="top"
+              style="max-width: 500px"
+              trigger="hover"
+            >
+              <el-tag
+                v-for="(tag, index) in item.tags"
+                :key="index"
+                v-if="scope.row[item.field] == tag.value"
+                :type="tag.type"
+                :effect="tag.effect"
+                disable-transitions
+                :closable="
+                  item.hasOwnProperty('closable')
+                    ? item.closable
+                    : item.action && item.action.url
+                    ? true
+                    : false
+                "
+                @close="handleTagListClose(item, scope.row, tag)"
+                >{{ tag.label }}</el-tag
+              >
+
+              <!--触发tooltip信息-->
+              <span slot="reference"
+                ><el-tag
+                  v-for="(tag, index) in item.tags"
+                  :key="index"
+                  v-if="scope.row[item.field] == tag.value"
+                  :type="tag.type"
+                  :effect="tag.effect"
+                  disable-transitions
+                  >{{ tag.label }}</el-tag
+                ></span
+              >
+            </el-popover>
+            <span v-else
+              ><el-tag
+                v-for="(tag, index) in item.tags"
+                :key="index"
+                v-if="scope.row[item.field] == tag.value"
+                :type="tag.type"
+                :effect="tag.effect"
+                disable-transitions
+                >{{ tag.label }}<br /></el-tag
+            ></span>
+          </span>
+          <!--tag标签list-->
+          <span v-else-if="item.stype === 'list-tag'">
+            <el-popover
+              v-if="item.tooltip === false"
+              placement="top"
+              style="max-width: 500px"
+              trigger="hover"
+            >
+              <span
+                v-if="
+                  scope.row[item.field] &&
+                  typeof scope.row[item.field] === 'string'
+                "
+              >
+                <el-tag
+                  style="margin: 1px"
+                  v-for="(tag, index) in scope.row[item.field].split(',')"
+                  :key="index"
+                  disable-transitions
+                  :closable="
+                    item.hasOwnProperty('closable')
+                      ? item.closable
+                      : item.action && item.action.url
+                      ? true
+                      : false
+                  "
+                  @close="handleTagListClose(item, scope.row, tag)"
+                  >{{ tag }}</el-tag
+                >
+              </span>
+              <span v-else
+                ><el-tag
+                  style="margin: 1px"
+                  v-for="(tag, index) in scope.row[item.field]"
+                  :key="index"
+                  disable-transitions
+                  :closable="
+                    item.hasOwnProperty('closable')
+                      ? item.closable
+                      : item.action && item.action.url
+                      ? true
+                      : false
+                  "
+                  @close="handleTagListClose(item, scope.row, tag)"
+                  >{{
+                    item.action && item.action.name
+                      ? tag[item.action.name]
+                      : tag
+                  }}</el-tag
+                ></span
+              >
+              <!--触发tooltip信息-->
+              <span
+                slot="reference"
+                v-if="
+                  scope.row[item.field] &&
+                  typeof scope.row[item.field] === 'string'
+                "
+              >
+                <el-tag
+                  style="margin: 1px"
+                  v-for="(tag, index) in scope.row[item.field].split(',')"
+                  :key="index"
+                  disable-transitions
+                  >{{ tag }}</el-tag
+                >
+              </span>
+              <span slot="reference" v-else
+                ><el-tag
+                  style="margin: 1px"
+                  v-for="(tag, index) in scope.row[item.field]"
+                  :key="index"
+                  disable-transitions
+                  >{{
+                    item.action && item.action.name
+                      ? tag[item.action.name]
+                      : tag
+                  }}</el-tag
+                ></span
+              >
+            </el-popover>
+            <span v-else>
+              <span
+                slot="reference"
+                v-if="
+                  scope.row[item.field] &&
+                  typeof scope.row[item.field] === 'string'
+                "
+              >
+                <el-tag
+                  style="margin: 1px"
+                  v-for="(tag, index) in scope.row[item.field].split(',')"
+                  :key="index"
+                  disable-transitions
+                  >{{ tag }}<br
+                /></el-tag>
+              </span>
+              <span slot="reference" v-else
+                ><el-tag
+                  style="margin: 1px"
+                  v-for="(tag, index) in scope.row[item.field]"
+                  :key="index"
+                  disable-transitions
+                  >{{
+                    item.action && item.action.name
+                      ? tag[item.action.name]
+                      : tag
+                  }}<br /></el-tag
+              ></span>
+            </span>
+          </span>
+          <!--link属性-->
+          <span v-else-if="item.stype === 'link'">
+            <el-link
+              type="primary"
+              @click="tableContentCellData(scope.row, item)"
+              :underline="false"
+              >{{ scope.row[item.field] }}</el-link
+            >
+          </span>
+          <!--link-value属性-->
+          <span v-else-if="item.stype === 'link-value'">
+            <el-link
+              type="primary"
+              @click="tableContentCellData(scope.row, item)"
+              v-if="scope.row[item.field] == item.value"
+              :underline="false"
+              >{{ item.label }}</el-link
+            >
+            <span v-else>{{
+              scope.row[item.field] ? scope.row[item.field] : "-"
+            }}</span>
+          </span>
+          <!--link-status属性-->
+          <span v-else-if="item.stype === 'link-status'">
+            <span
+              v-for="(objItem, objIndex) in item.obj"
+              :key="objIndex"
+              v-if="scope.row[item.field] == objItem.value"
+            >
+              <el-link
+                v-if="
+                  Array.isArray(item.value) &&
+                  item.value.includes(scope.row[item.field])
+                "
+                type="primary"
+                @click="tableContentCellData(scope.row, item)"
+                :underline="false"
+                >{{ objItem.label ? objItem.label : "-" }}</el-link
+              >
+              <span v-else>{{ objItem.label ? objItem.label : "-" }}</span>
+            </span>
+          </span>
+          <!--switch属性-->
+          <span v-else-if="item.stype == 'switch'">
+            <el-switch
+              v-model="scope.row[item.field]"
+              :active-color="
+                item.action && item.action.aColor
+                  ? item.action.aColor
+                  : '#13ce66'
+              "
+              :inactive-color="
+                item.action && item.action.inColor
+                  ? item.action.inColor
+                  : '#aaaaaa'
+              "
+              :active-value="
+                item.action && item.action.hasOwnProperty('aValue')
+                  ? item.action.aValue
+                  : 1
+              "
+              :inactive-value="
+                item.action && item.action.hasOwnProperty('inValue')
+                  ? item.action.inValue
+                  : 0
+              "
+              :disabled="
+                item.hasOwnProperty('disabled')
+                  ? Object.prototype.toString.call(item.disabled) ===
+                    '[object Function]'
+                    ? item.disabled(scope)
+                    : item.disabled
+                  : item.action && item.action.url
+                  ? false
+                  : true
+              "
+              @change="switchChange(scope.row, item)"
+            ></el-switch>
+          </span>
+          <!--image图片-->
+          <span v-else-if="item.stype == 'image'">
+            <span
+              v-if="
+                Object.prototype.toString.call(scope.row[item.field]) ===
+                '[object Array]'
+              "
+            >
+              <img
+                v-for="(it, itIndex) in scope.row[item.field]"
+                :key="itIndex"
+                :src="
+                  tableImageURL(
+                    item,
+                    it[item.imageUrl] ? it[item.imageUrl] : it.url
+                  )
+                "
+                :style="
+                  item.tStyle ? item.tStyle : 'min-width: 50px; height: 20px;'
+                "
+              />
+            </span>
+            <img
+              v-else
+              :src="tableImageURL(item, scope.row[item.field])"
+              :style="
+                item.tStyle ? item.tStyle : 'min-width: 50px; height: 20px;'
+              "
+            />
+          </span>
+          <!--input属性-->
+          <span v-else-if="item.stype == 'input'">
+            <el-form
+              v-if="columnShowStatus(item, scope)"
+              class="table-input-form"
+              :model="scope.row"
+              size="small"
+            >
+              <div v-if="item.inputShow" class="input-label is-input">
+                <nt-input
+                  :inputItem="item"
+                  :rowData="scope.row"
+                  :inputType="'table'"
+                  :selectList="emTableList.select_list"
+                  :axios="emTableList.axios"
+                  :queryURL="emTableList.queryURL"
+                  :responseSuccess="emTableList.responseSuccess"
+                  :publicParams="emTableList._publicParams"
+                  @onInputBlur="() => onInputBlur(scope.row, item)"
+                ></nt-input>
+              </div>
+              <div v-else>
+                <div v-if="scope.row.isInput" class="input-label is-input">
+                  <nt-input
+                    :inputItem="item"
+                    :rowData="scope.row"
+                    :inputType="'table'"
+                    :selectList="emTableList.select_list"
+                    :axios="emTableList.axios"
+                    :queryURL="emTableList.queryURL"
+                    :responseSuccess="emTableList.responseSuccess"
+                    :publicParams="emTableList._publicParams"
+                  ></nt-input>
+                  <i
+                    class="el-icon-circle-check"
+                    @click.stop="submitInputEvent(scope.row, item)"
+                  ></i>
+                </div>
+                <div
+                  v-else
+                  class="input-label"
+                  @click="cellInputEvent(scope.row)"
+                >
+                  {{ scope.row[item.field] }} <i class="el-icon-edit"></i>
+                </div>
+              </div>
+            </el-form>
+            <span v-else>{{ scope.row[item.field] }}</span>
+          </span>
+          <!--fields属性-->
+          <span v-else-if="item.stype == 'fields'">
+            <span
+              v-for="(currField, currIndex) in item.fieldList"
+              :key="currIndex"
+              ><span v-if="currIndex > 0">{{
+                item.sign ? item.sign : " / "
+              }}</span
+              >{{ scope.row[currField] }}</span
+            >
+          </span>
+          <!--mapping属性-->
+          <span v-else-if="item.stype == 'mapping'">{{
+            scope.row[item.mapping]
+          }}</span>
+          <!--rate属性-->
+          <span v-else-if="item.stype == 'rate'">
+            <el-rate
+              v-model="scope.row[item.field]"
+              :max="item.max ? item.max : 5"
+              :disabled="item.isDisabled ? item.isDisabled : false"
+              :allow-half="item.half ? item.half : false"
+              :show-score="item.score ? item.score : false"
+              :text-color="item.color ? item.color : '#1F2D3D'"
+              score-template="{value}"
+            ></el-rate>
+          </span>
+          <!--value属性-->
+          <span v-else-if="item.stype == 'value'">{{ item.value }}</span>
+          <!--format属性-->
+          <span v-else-if="item.stype == 'format'">{{
+            scope.row[item.field] | vueFiltersInit(item, scope)
+          }}</span>
+          <!--v-html属性-->
+          <span
+            v-else-if="item.stype == 'v-html'"
+            v-html="scope.row[item.field]"
+          ></span>
+          <!--text-style属性-->
+          <span v-else-if="item.stype == 'text-style'">
+            <span
+              :style="
+                Object.prototype.toString.call(item.classValueName) ===
+                '[object Function]'
+                  ? item.classValueName(scope.row, item.field)
+                  : item.classValueName
+              "
+              >{{ scope.row[item.field] }}</span
+            >
+          </span>
+          <!--formatter-style属性-->
+          <span
+            v-else-if="item.stype == 'formatter-style'"
+            v-html="formatterStyle(scope.row, item)"
+          ></span>
+          <span v-else>{{ scope.row[item.field] }}</span>
+        </template>
+      </el-table-column>
+  </el-table>
+</template>
+<script>
+import {
+  formatterSelf,
+  formatterDefault,
+  tableListUpdateRowEvent,
+  unitTreeChildList,
+  selectListToValue,
+  unitTableFiltersNode,
+} from "./tool-table";
+import {
+  isTypeof,
+  checkAuthButtonList,
+  isHttpHeaderURL,
+} from "@/components/thrid/em-element-ui/src/tools/utils";
+import NtInput from "../../input";
+import bus from "@/components/thrid/em-element-ui/src/tools/eventBus";
+
+export default {
+  name: "EmTables",
+  components: { NtInput },
+  props: {
+    emTableList: {
+      type: Object,
+      default: () => {},
+    },
+    listType: {
+      type: Array,
+      default: [],
+    },
+  },
+  data() {
+    return {
+      filtersList: {},
+      isMultipleTables: this.listType.length > 1,
+    };
+  },
+  watch: {},
+  created: function () {
+    unitTableFiltersNode(this);
+  },
+  mounted() {
+    if (this.isMultipleTables) {
+      // 监听兄弟组件选中值变化
+      bus.$on("checkedEventCardToTable", (data) => {
+        data.forEach((item) => {
+          if (item.hasOwnProperty("cardChecked") && item.cardChecked) {
+            this.$refs.multipleTable.toggleRowSelection(item, true);
+          } else {
+            this.$refs.multipleTable.toggleRowSelection(item, false);
+          }
+        });
+      });
+    }
+  },
+  methods: {
+    // 改变行的颜色
+    tableRowClassName({ row, rowIndex }) {
+      if (isTypeof(this.emTableList.rowClassName) === "function") {
+        return this.emTableList.rowClassName(row, rowIndex);
+      } else {
+        return this.emTableList.rowClassName;
+      }
+    },
+    expandParseValue(row, item) {
+      const value = item.hasOwnProperty("value")
+        ? row.hasOwnProperty(item.value)
+          ? row[item.value] === "null" || row[item.value] === null
+            ? "-"
+            : row[item.value]
+          : "-"
+        : "-";
+
+      let resultValue = value;
+      if (item.hasOwnProperty("obj")) {
+        if (Array.isArray(this.emTableList.select_list[item.obj])) {
+          this.emTableList.select_list[item.obj].forEach((node) => {
+            if (node.value == value) {
+              resultValue = node.label;
+            }
+          });
+        }
+      }
+
+      return resultValue;
+    },
+    isHideColumn(item) {
+      if (item.hasOwnProperty("hide")) {
+        if (isTypeof(item.hide) === "function") {
+          const hideStatus = item.hide();
+          return !hideStatus;
+        } else {
+          return !item.hide;
+        }
+      } else {
+        return true;
+      }
+    },
+    // 是否显示stype的组件,false显示文本
+    columnShowStatus(item, scope) {
+      let status = item.hasOwnProperty("iShowStatus")
+        ? isTypeof(item.iShowStatus) === "function"
+          ? item.iShowStatus(scope.row)
+          : item.iShowStatus
+        : true;
+
+      return status;
+    },
+    // 返回有权限的操作事件
+    tableButtonList(item, scope) {
+      let buttons =
+        isTypeof(item.list) === "array"
+          ? item.list
+          : isTypeof(item.list) === "function"
+          ? item.list(scope)
+          : [];
+
+      if (buttons && buttons.length > 0) {
+        return checkAuthButtonList(buttons, this.emTableList.authButtonList);
+      } else {
+        return [];
+      }
+    },
+    optCellData(row, option) {
+      // 操作后改变该行的背景颜色
+      if (option.bgcolor) {
+        if (row.hasOwnProperty("optCellSelfBgcolor")) {
+          row.optCellSelfBgcolor = option.bgcolor;
+        } else {
+          this.$set(row, "optCellSelfBgcolor", option.bgcolor);
+        }
+      }
+      this.emTableList.tableBtnEvent(row, option);
+    },
+    tableContentCellData(row, item) {
+      let obj = Object.create(null);
+      obj = Object.assign({}, item);
+      obj.type = item.field;
+
+      this.optCellData(row, obj);
+    },
+    formatterStyle(row, item) {
+      if (item.formatter) {
+        return selectListToValue(this, item.formatter, row[item.field], true);
+      } else {
+        return "";
+      }
+    },
+    //格式化
+    formatter(row, column, cellValue, index) {
+      return formatterSelf(this, row, column, cellValue, index);
+    },
+    // table图片地址拼接
+    tableImageURL(item, url) {
+      if (isHttpHeaderURL(url)) {
+        return url;
+      } else {
+        return item.fileHost + (item.path || "") + url;
+      }
+    },
+    loadChildren(tree, treeNode, resolve) {
+      if (
+        !this.emTableList.options.hasOwnProperty("lazy") ||
+        (this.emTableList.options.hasOwnProperty("lazy") &&
+          this.emTableList.options.lazy)
+      ) {
+        // 记录点击懒加载树的记录 load
+        const key = tree[this.emTableList.rowKey];
+
+        this.emTableList.loadTableTreeNodeMap.set(key, {
+          tree,
+          treeNode,
+          resolve,
+        });
+
+        unitTreeChildList(this, tree, treeNode, resolve);
+      }
+    },
+    formatterDef(row, column, cellValue, index) {
+      return formatterDefault(this, row, column, cellValue, index);
+    },
+    switchChange(row, item) {
+      if (item.action && item.action.url) {
+        tableListUpdateRowEvent(this, row, item);
+      }
+    },
+    sortReloadData(obj) {
+      this.emTableList.sortReloadData(obj);
+    },
+    handleSelectionChange(val) {
+      this.emTableList.handleSelectionChange(val);
+      if (this.isMultipleTables) {
+        bus.$emit("checkedEventTableToCard", val);
+      }
+    },
+    cellInputEvent(item) {
+      if (item.hasOwnProperty("isInput")) {
+        item.isInput = true;
+      } else {
+        this.$set(item, "isInput", true);
+      }
+    },
+    onInputBlur(row, item) {
+      tableListUpdateRowEvent(this, row, item);
+    },
+    submitInputEvent(row, item) {
+      item.isInput = false;
+      tableListUpdateRowEvent(this, row, item);
+    },
+    filterColHandler(filters) {
+      const finds = {};
+
+      for (let [k, v] of Object.entries(filters)) {
+        finds[k] = isTypeof(v) === "array" ? v.join(",") : v;
+      }
+      this.emTableList.paginationTick = false;
+      this.$nextTick(() => {
+        this.emTableList.pages.pageNum = 1;
+        this.emTableList.searchFormList(finds);
+        this.emTableList.paginationTick = true;
+      });
+
+      return false;
+    },
+    handleDisable(row, index) {
+      return this.emTableList.handleCheckboxDisable(row, index);
+    },
+    handleTagClose(item, row) {
+      tableListUpdateRowEvent(this, row, item);
+    },
+    handleTagListClose(item, row, tag) {
+      let rows = Object.assign({}, row);
+      if (isTypeof(tag) === "object") {
+        rows = Object.assign({}, row, tag);
+      } else {
+        rows["tagCustomId"] = tag;
+      }
+
+      if (item.action && item.action.url) {
+        this.handleTagClose(item, rows);
+      }
+    },
+  },
+};
+</script>
+<style lang="scss">
+.el-table .select-after-line {
+  background-color: #f0f9eb;
+  &.el-table__row--striped td {
+    background-color: #f0f9eb !important;
+  }
+}
+.el-table .cell {
+  white-space: pre-line;
+
+  .icon-name {
+    display: inline-block;
+    i {
+      font-size: 12px;
+      padding-left: 0;
+      padding-right: 5px;
+    }
+  }
+  .input-label {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+
+    /*&.is-input {
+                input {
+                    color: #409EFF;
+                    font-size: 12px;
+                    height: 23px;
+                    line-height: 23px;
+                    border-color: #f2f2f2;
+                }
+                i {
+                    font-size: 15px;
+                    padding-left: 10px;
+                }
+            }
+
+            i {
+                color: #409EFF;
+            }*/
+  }
+
+  .table-input-form {
+    .el-form-item__label {
+      display: none;
+    }
+    .el-form-item {
+      margin-bottom: 0;
+    }
+    .el-input__inner {
+      padding: 2px 8px;
+    }
+    .input-label.is-input {
+      height: 32px;
+      i {
+        padding-left: 0;
+      }
+      input {
+        height: 28px;
+        line-height: 28px;
+      }
+    }
+  }
+}
+</style>

+ 976 - 0
packages/list/src/tables.vue

@@ -0,0 +1,976 @@
+<template>
+  <el-table
+    :data="emTableList.tableData"
+    :key="emTableList.tableName"
+    v-loading="emTableList.loading"
+    :lazy="
+      emTableList.options.hasOwnProperty('lazy')
+        ? emTableList.options.lazy
+        : true
+    "
+    :load="loadChildren"
+    :tree-props="emTableList.treeProps"
+    :stripe="emTableList.stripe === false ? false : true"
+    style="width: 100%"
+    :row-key="emTableList.rowKey"
+    :default-sort="{prop: 'totalQty', order: 'descending'}"
+    ref="multipleTable"
+    @sort-change="sortReloadData"
+    @selection-change="handleSelectionChange"
+    :cell-style="emTableList.bodyCellStyle"
+    :header-cell-style="emTableList.headerCellStyle"
+    :row-class-name="tableRowClassName"
+    :border="emTableList.border === false ? false : true"
+    @filter-change="filterColHandler"
+  >
+      <el-table-column v-if="emTableList.isMultiple" type="selection" :fixed="true" align="center"></el-table-column>
+      <el-table-column
+        v-for="(item, index) in emTableList.showColumns"
+        v-if="isShow(item) && item.stype !== 'checkbox'"
+        :key="index"
+        :header-align="item.headerAlign ? sub.headerAlign : 'center'"
+        :prop="item.field"
+        :width="item.width ? item.width : 'auto'"
+        :align="item.align ? item.align : 'center'"
+        :sort-orders="['descending','ascending',null]"
+        :sortable="item.sort ? item.sort : false"
+        :fixed="item.fixed ? item.fixed : false"
+        :show-overflow-tooltip="item.tooltip === false ? false : true"
+        :filters="tcpFilters(item)"
+        :column-key="item.field ? item.field : ''"
+        :filter-multiple="item.filterMultiple ? item.filterMultiple : false"
+        :formatter="item.formatter ? formatter : formatterDef"
+        :class-name="item.className"
+      >
+        <!--list属性-->
+        <template v-if="item.stype == 'list'">
+          <el-table-column
+            v-for="(sub, subIndex) in item.sub"
+            :key="subIndex"
+            :header-align="sub.headerAlign ? sub.headerAlign : 'center'"
+            :prop="sub.field"
+            :label="sub.name"
+            :width="sub.width ? sub.width : ''"
+            :align="sub.align ? sub.align : 'center'"
+            :sortable="sub.sort ? sub.sort : false"
+            :fixed="sub.fixed ? sub.fixed : false"
+            :show-overflow-tooltip="sub.tooltip === false ? false : true"
+            :formatter="sub.formatter ? formatter : formatterDef"
+            :filters="item.filters"
+            :column-key="item.field"
+            :filter-multiple="item.filterMultiple ? item.filterMultiple : false"
+          >
+            <template slot-scope="scope" >
+              <!--mapping属性-->
+              <span v-if="sub.stype == 'mapping'">{{
+              mapping(scope.row, sub)
+              }}</span>
+            <span v-else-if="sub.stype === 'custom'">
+              <component :is="sub.cellComp" :item="sub" :rowData="scope.row" @eventBus="cellCompEvent(scope.row,sub)"/>
+            </span>
+              <span v-else>{{ scope.row[sub.field] |nullMap }}</span>
+            </template>
+
+
+        </el-table-column>
+        </template>
+        <template slot="header">
+          <ul class="table-expand-list" v-if="item.expand">
+            <li
+              v-for="(expandItem, expandIndex) of scope.row[item.expand.list]"
+              :key="expandIndex"
+            >
+              <div class="name" :style="item.expand.nameStyle">
+                {{ expandItem[item.expand.key] }}:
+              </div>
+              <div class="value" :style="item.expand.valueStyle">
+                {{ expandParseValue(expandItem, item.expand) }}
+              </div>
+            </li>
+          </ul>
+          <el-tooltip
+            v-if="item.hasOwnProperty('note') && item.note"
+            class="item"
+            effect="dark"
+            :content="item.note"
+            placement="top"
+          >
+            <span class="icon-name icon-info"
+              ><i class="el-icon-info"></i
+            ></span>
+          </el-tooltip>
+          <el-button
+            v-if="item.hasOwnProperty('explain') && item.explain"
+            icon="el-icon-info"
+            style="padding: 0; margin-right: 4px"
+            circle
+            @click="emTableList.tableBtnEvent(item, { type: item.explain })"
+          ></el-button>
+          <span class="icon-name icon-info-name">{{ item.name }}</span>
+        </template>
+        <template slot-scope="scope" >
+          <!--按钮-->
+            <template v-if="item.stype === 'opt'">
+                <el-button
+                v-for="(opt, optIndex) in tableButtonList(item, scope)"
+                :key="optIndex"
+                :type="opt.bType ? opt.bType : 'text'"
+                :style="opt.style ? opt.style : ''"
+                :icon="opt.icon ? opt.icon : ''"
+                :size="opt.size ? opt.size : 'small'"
+                :plain="opt.shape === 'plain' ? true : false"
+                :round="opt.shape === 'round' ? true : false"
+                :circle="opt.shape === 'circle' ? true : false"
+                :disabled="opt.hasOwnProperty('disabled') ? opt.disabled : false"
+                @click="optCellData(scope.row, opt)"
+                v-shortcutKeys="{key:opt.type ==='detail'?'d':''}"
+                >
+                {{ opt.name }}
+                </el-button>
+            </template>
+            <span v-else-if="item.stype === 'slot'">
+              <slot :name="item.slot" :item="item" :rowData="scope.row"/>
+            </span>
+            <span v-else-if="item.stype === 'custom'">
+              <component :is="item.cellComp" :item="item" :rowData="scope.row" @eventBus="cellCompEvent(scope.row,item,$event)"/>
+            </span>
+            <span v-else-if="item.stype === 'select'">
+              <el-select
+              v-model="scope.row[item.field]"
+              >
+            <el-option v-for="(o,i) in emTableList.select_list[item[item.stype].obj]" :key="i" :label="o.label" :value="o.value">
+            {{ o.label }}</el-option>
+            </el-select>
+            </span>
+            <!--tag标签-->
+            <span v-else-if="item.stype === 'tag'">
+                <el-popover
+                v-if="item.tooltip === false"
+                placement="top"
+                style="max-width: 500px"
+                trigger="hover"
+                >
+                <template v-for="(tag, index) in item.tags">
+                <el-tag
+                    :key="index"
+                    v-if="scope.row[item.field] == tag.value"
+                    :type="tag.type"
+                    :effect="tag.effect"
+                    disable-transitions
+                    :closable="
+                    item.hasOwnProperty('closable')
+                        ? item.closable
+                        : item.action && item.action.url
+                        ? true
+                        : false
+                    "
+                    @close="handleTagListClose(item, scope.row, tag)"
+                    >{{ tag.label }}</el-tag
+                >
+                </template>
+                <!--触发tooltip信息-->
+                <span slot="reference">
+                  <template v-for="(tag, index) in item.tags">
+                  <el-tag
+                    :key="index"
+                    v-if="scope.row[item.field] == tag.value"
+                    :type="tag.type"
+                    :effect="tag.effect"
+                    disable-transitions
+                    >{{ tag.label }}</el-tag
+                    >
+                  </template>
+                </span>
+                </el-popover>
+                <span v-else
+                >
+
+                <template v-for="(tag, index) in item.tags">
+                <el-tag
+                    :key="index"
+                    v-if="scope.row[item.field] == tag.value"
+                    :type="tag.type"
+                    :effect="tag.effect"
+                    disable-transitions
+                    >{{ tag.label }}<br /></el-tag
+                >
+              </template>
+              </span>
+            </span>
+            <!--tag标签list-->
+            <span v-else-if="item.stype === 'list-tag'">
+                <el-popover
+                v-if="item.tooltip === false"
+                placement="top"
+                style="max-width: 500px"
+                trigger="hover"
+                >
+                <span
+                    v-if="
+                    scope.row[item.field] &&
+                    typeof scope.row[item.field] === 'string'
+                    "
+                >
+                    <el-tag
+                    style="margin: 1px"
+                    v-for="(tag, index) in scope.row[item.field].split(',')"
+                    :key="index"
+                    disable-transitions
+                    :closable="
+                        item.hasOwnProperty('closable')
+                        ? item.closable
+                        : item.action && item.action.url
+                        ? true
+                        : false
+                    "
+                    @close="handleTagListClose(item, scope.row, tag)"
+                    >{{ tag }}</el-tag
+                    >
+                </span>
+                <span v-else
+                    ><el-tag
+                    style="margin: 1px"
+                    v-for="(tag, index) in scope.row[item.field]"
+                    :key="index"
+                    disable-transitions
+                    :closable="
+                        item.hasOwnProperty('closable')
+                        ? item.closable
+                        : item.action && item.action.url
+                        ? true
+                        : false
+                    "
+                    @close="handleTagListClose(item, scope.row, tag)"
+                    >{{
+                        item.action && item.action.name
+                        ? tag[item.action.name]
+                        : tag
+                    }}</el-tag
+                    ></span
+                >
+                <!--触发tooltip信息-->
+                <span
+                    slot="reference"
+                    v-if="
+                    scope.row[item.field] &&
+                    typeof scope.row[item.field] === 'string'
+                    "
+                >
+                    <el-tag
+                    style="margin: 1px"
+                    v-for="(tag, index) in scope.row[item.field].split(',')"
+                    :key="index"
+                    disable-transitions
+                    >{{ tag }}</el-tag
+                    >
+                </span>
+                <span slot="reference" v-else
+                    ><el-tag
+                    style="margin: 1px"
+                    v-for="(tag, index) in scope.row[item.field]"
+                    :key="index"
+                    disable-transitions
+                    >{{
+                        item.action && item.action.name
+                        ? tag[item.action.name]
+                        : tag
+                    }}</el-tag
+                    ></span
+                >
+                </el-popover>
+                <span v-else>
+                <span
+                    slot="reference"
+                    v-if="
+                    scope.row[item.field] &&
+                    typeof scope.row[item.field] === 'string'
+                    "
+                >
+                    <el-tag
+                    style="margin: 1px"
+                    v-for="(tag, index) in scope.row[item.field].split(',')"
+                    :key="index"
+                    disable-transitions
+                    >{{ tag }}<br
+                    /></el-tag>
+                </span>
+                <span slot="reference" v-else
+                    ><el-tag
+                    style="margin: 1px"
+                    v-for="(tag, index) in scope.row[item.field]"
+                    :key="index"
+                    disable-transitions
+                    >{{
+                        item.action && item.action.name
+                        ? tag[item.action.name]
+                        : tag
+                    }}<br /></el-tag
+                ></span>
+                </span>
+            </span>
+            <!--link属性-->
+            <span v-else-if="item.stype === 'link'">
+                <el-link
+                type="primary"
+                @click="tableContentCellData(scope.row, item)"
+                :underline="false"
+                >{{ scope.row[item.field] }}</el-link
+                >
+            </span>
+            <!--link-value属性-->
+            <span v-else-if="item.stype === 'link-value'">
+                <el-link
+                type="primary"
+                @click="tableContentCellData(scope.row, item)"
+                v-if="scope.row[item.field] == item.value"
+                :underline="false"
+                >{{ item.label }}</el-link
+                >
+                <span v-else>{{
+                scope.row[item.field] ? scope.row[item.field] : "-"
+                }}</span>
+            </span>
+            <!--link-status属性-->
+            <span v-else-if="item.stype === 'link-status'">
+              <template v-for="(objItem, objIndex) in item.obj">
+                <span
+                :key="objIndex"
+                v-if="scope.row[item.field] == objItem.value"
+                >
+                <el-link
+                    v-if="
+                    Array.isArray(item.value) &&
+                    item.value.includes(scope.row[item.field])
+                    "
+                    type="primary"
+                    @click="tableContentCellData(scope.row, item)"
+                    :underline="false"
+                    >{{ objItem.label ? objItem.label : "-" }}</el-link
+                >
+                <span v-else>{{ objItem.label ? objItem.label : "-" }}</span>
+                </span>
+              </template>
+            </span>
+            <!--switch属性-->
+            <span v-else-if="item.stype == 'switch'">
+                <el-switch
+                v-model="scope.row[item.field]"
+                :width="getPropValue(item, 'width')"
+                :inactive-value="getPropValue(item, '00')"
+                :inactive-text="getPropValue(item, '01')"
+                :inactive-color="getPropValue(item, '02')"
+                :active-value="getPropValue(item, '10')"
+                :active-text="getPropValue(item, '11')"
+                :active-color="getPropValue(item, '12')"
+                :disabled="getPropValue(item, 'disabled', scope)"
+                @change="switchChange(scope.row, item)"
+                ></el-switch>
+            </span>
+            <!--image图片-->
+            <span v-else-if="item.stype == 'image'">
+                <span
+                v-if="
+                    Object.prototype.toString.call(scope.row[item.field]) ===
+                    '[object Array]'
+                "
+                >
+                <img
+                    v-for="(it, itIndex) in scope.row[item.field]"
+                    :key="itIndex"
+                    :src="
+                    tableImageURL(
+                        item,
+                        it[item.imageUrl] ? it[item.imageUrl] : it.url
+                    )
+                    "
+                    :style="
+                    item.tStyle ? item.tStyle : 'min-width: 50px; height: 20px;'
+                    "
+                />
+                </span>
+                <img
+                v-else
+                :src="tableImageURL(item, scope.row[item.field])"
+                :style="
+                    item.tStyle ? item.tStyle : 'min-width: 50px; height: 20px;'
+                "
+                />
+            </span>
+            <!--input属性-->
+            <span v-else-if="item.stype == 'input'">
+                <el-form
+                v-if="columnShowStatus(item, scope)"
+                class="table-input-form"
+                :model="scope.row"
+                size="small"
+                >
+                <div v-if="item.inputShow" class="input-label is-input">
+                    <nt-input
+                    :inputItem="item"
+                    :rowData="scope.row"
+                    :inputType="'table'"
+                    :selectList="emTableList.select_list"
+                    :axios="emTableList.axios"
+                    :queryURL="emTableList.queryURL"
+                    :responseSuccess="emTableList.responseSuccess"
+                    :publicParams="emTableList._publicParams"
+                    @onInputBlur="() => onInputBlur(scope.row, item)"
+                    ></nt-input>
+                </div>
+                <div v-else>
+                    <div v-if="scope.row.isInput" class="input-label is-input">
+                    <nt-input
+                        :inputItem="item"
+                        :rowData="scope.row"
+                        :inputType="'table'"
+                        :selectList="emTableList.select_list"
+                        :axios="emTableList.axios"
+                        :queryURL="emTableList.queryURL"
+                        :responseSuccess="emTableList.responseSuccess"
+                        :publicParams="emTableList._publicParams"
+                    ></nt-input>
+                    <i
+                        class="el-icon-circle-check"
+                        @click.stop="submitInputEvent(scope.row, item)"
+                    ></i>
+                    </div>
+                    <div
+                    v-else
+                    class="input-label"
+                    @click="cellInputEvent(scope.row)"
+                    >
+                    {{ scope.row[item.field] }} <i class="el-icon-edit"></i>
+                    </div>
+                </div>
+                </el-form>
+                <span v-else>{{ scope.row[item.field] }}</span>
+            </span>
+            <!--fields属性-->
+            <span v-else-if="item.stype == 'fields'">
+                <span
+                v-for="(currField, currIndex) in item.fieldList"
+                :key="currIndex"
+                ><span v-if="currIndex > 0">{{
+                    item.sign ? item.sign : " / "
+                }}</span
+                >{{ scope.row[currField] }}</span
+                >
+            </span>
+            <!--mapping属性-->
+            <span v-else-if="item.stype == 'mapping'">{{
+                mapping(scope.row, item)
+            }}</span>
+            <!--rate属性-->
+            <span v-else-if="item.stype == 'rate'">
+                <el-rate
+                v-model="scope.row[item.field]"
+                :max="item.max ? item.max : 5"
+                :disabled="item.isDisabled ? item.isDisabled : false"
+                :allow-half="item.half ? item.half : false"
+                :show-score="item.score ? item.score : false"
+                :text-color="item.color ? item.color : '#1F2D3D'"
+                score-template="{value}"
+                ></el-rate>
+            </span>
+            <!--value属性-->
+            <span v-else-if="item.stype == 'value'">{{ item.value }}</span>
+            <!--format属性-->
+            <span v-else-if="item.stype == 'format'">{{
+                scope.row[item.field] | vueFiltersInit(item, scope)
+            }}</span>
+            <!--v-html属性-->
+            <span
+                v-else-if="item.stype == 'v-html'"
+                v-html="scope.row[item.field]"
+            ></span>
+            <!--text-style属性-->
+            <span v-else-if="item.stype == 'text-style'">
+                <span
+                :style="
+                    Object.prototype.toString.call(item.classValueName) ===
+                    '[object Function]'
+                    ? item.classValueName(scope.row, item.field)
+                    : item.classValueName
+                "
+                >{{ scope.row[item.field] }}</span
+                >
+            </span>
+            <!--formatter-style属性-->
+            <span
+                v-else-if="item.stype == 'formatter-style'"
+                v-html="formatterStyle(scope.row, item)"
+            ></span>
+            <span v-else>{{ scope.row[item.field] | nullMap}}</span>
+        </template>
+
+      </el-table-column>
+  </el-table>
+</template><script>
+// import {vueFiltersInit} from '@/utils/filters'
+import {
+  formatterSelf,
+  formatterDefault,
+  tableListUpdateRowEvent,
+  unitTreeChildList,
+  selectListToValue,
+  unitTableFiltersNode,
+} from "./tool-table";
+import {
+  isTypeof,
+  checkAuthButtonList,
+  isHttpHeaderURL,
+  nullView
+} from "@/components/thrid/em-element-ui/src/tools/utils";
+import NtInput from "../../input";
+import bus from "@/components/thrid/em-element-ui/src/tools/eventBus";
+
+export default {
+  name: "EmTables",
+  components: { NtInput },
+  props: {
+    emTableList: {
+      type: Object,
+      default: () => {},
+    },
+    listType: {
+      type: Array,
+      default: [],
+    },
+  },
+  data() {
+    return {
+      filtersList: {},
+      isMultipleTables: this.listType.length > 1,
+    };
+  },
+  watch: {},
+  created: function () {
+    // unitTableFiltersNode(this);
+  },
+  filters: {
+    nullMap:nullView
+  },
+  computed: {
+    defaultSort(){
+      const defsort = this.showColumns.find(f=>typeof(f.sort) === 'string')
+      return defsort && {prop: defsort.field, order: defsort.sort}
+    },
+    // 第一个 显示的 && 复选框列
+    checkboxItem() {
+        return this.emTableList.showColumns.find(f=> {
+            return this.isShow(f) && f.stype === 'checkbox' 
+        })
+    },
+    // 所有显示的&& 非复选框列
+    showColumns() {
+        return this.emTableList.showColumns.filter(f=> {
+            return this.isShow(f) && f.stype !== 'checkbox'
+        })
+    }
+  },
+  mounted() {
+    if (this.isMultipleTables) {
+      // 监听兄弟组件选中值变化
+      bus.$on("checkedEventCardToTable", (data) => {
+        data.forEach((item) => {
+          if (item.hasOwnProperty("cardChecked") && item.cardChecked) {
+            this.$refs.multipleTable.toggleRowSelection(item, true);
+          } else {
+            this.$refs.multipleTable.toggleRowSelection(item, false);
+          }
+        });
+      });
+    }
+  },
+  // filters:{
+  //   vueFiltersInit
+  // },
+  methods: {
+    /** 自定义组件事件总线
+     * 将传入的参数赋值给item.ext
+     */
+    cellCompEvent(row, item,ext) {
+      item.ext = ext
+      this.emTableList.tableBtnEvent(row, item);
+    },
+    // 获取表格列-filters属性
+    tcpFilters(item) {
+      let rv = undefined
+      if(item.filters) {
+        if (item.filters.push) {
+          rv = item.filters
+        }
+        if (item.filters.obj) {
+          const sl = this.emTableList.select_list[item.filters.obj]
+          if (sl) {
+            rv = sl.map(m=>{
+              return {
+                text:m.label,
+                value:m.value
+              }
+            })
+          }
+        }
+      }
+      return rv
+    },
+    // 数据字段映射
+    mapping(row, item) {
+      let rv = ''
+      if (typeof item.mapping === 'string') {
+        rv = row[item.mapping]
+      }
+      if (typeof item.mapping === 'function') {
+        rv = item.mapping(row)
+      }
+      if (typeof item.mapping === 'object') {
+        // 存在select_list名称,从select_list 映射
+        if (item.mapping.obj) {
+          // 当前下拉数组
+          const curs = this.emTableList.select_list[item.mapping.obj]
+          // 存在select集合
+          if (curs) {
+            // 通过字段值获取 对应label
+            const cursi = curs.find(f=>f.value == row[item.field])
+            if(cursi) {
+              rv = cursi.label
+            }
+          }
+        }
+      }
+      return nullView(rv)
+    },
+    // 获取组件属性值
+    getPropValue(item, type, scope) {
+      let rv = null
+      // switch 子属性的映射信息
+      const maps = {
+        switch : {
+          // 关闭值
+          '00': {
+            pk:'inValue',
+            def:0
+          },
+          // 关闭文案
+          '01': {
+            pk:'inText',
+            def:'关闭'
+          },
+          // 关闭颜色
+          '02': {
+            pk:'inColor',
+            def:'#aaaaaa'
+          },
+          // 打开值
+          '10': {
+            pk:'aValue',
+            def:1
+          },
+          // 打开文案
+          '11': {
+            pk:'aText',
+            def:'打开'
+          },
+          // 打开颜色
+          '12': {
+            pk:'aColor',
+            def:'#13ce66'
+          },
+          // 宽度
+          'width': {
+            pk:'width',
+            def:50
+          },
+          // 禁用
+          'disabled': {
+            pk:'disabled',
+            def: false
+          }
+        }
+      }
+      // 获取stype类型对应的映射信息
+      const curTypeMap = maps[item.stype]
+      if(!curTypeMap) {
+        return rv
+      }
+      // 获取具体的属性映射信息
+      const curAttrMap = curTypeMap[type]
+      if(curAttrMap) {
+        // stype对应的类型的属性配置信息组
+        const curTypeConfig = item[item.stype]
+        // 存在具体属性配置
+        if(curTypeConfig && curTypeConfig.hasOwnProperty(curAttrMap.pk)) {
+          const cv = curTypeConfig[curAttrMap.pk]
+          if(typeof cv === 'function' && scope) {
+            rv = cv(scope)
+          } else {
+            rv = cv
+          }
+        // 不存在
+        } else {
+          rv = curAttrMap.def
+          // // 取item中的属性信息
+          // if(item.hasOwnProperty(curAttrMap.pk)) {
+          //   rv = item[curAttrMap.pk]
+          // }else {// 都取不到取默认值
+          //   rv = curAttrMap.def
+          // }
+        }
+      }
+      return rv
+    },
+    // 改变行的颜色
+    tableRowClassName({ row, rowIndex }) {
+      if (isTypeof(this.emTableList.rowClassName) === "function") {
+        return this.emTableList.rowClassName(row, rowIndex);
+      } else {
+        return this.emTableList.rowClassName;
+      }
+    },
+    expandParseValue(row, item) {
+      const value = item.hasOwnProperty("value")
+        ? row.hasOwnProperty(item.value)
+          ? row[item.value] === "null" || row[item.value] === null
+            ? "-"
+            : row[item.value]
+          : "-"
+        : "-";
+
+      let resultValue = value;
+      if (item.hasOwnProperty("obj")) {
+        if (Array.isArray(this.emTableList.select_list[item.obj])) {
+          this.emTableList.select_list[item.obj].forEach((node) => {
+            if (node.value == value) {
+              resultValue = node.label;
+            }
+          });
+        }
+      }
+
+      return resultValue;
+    },
+    isShow(item) {
+      if (item.hasOwnProperty("hide")) {
+        if (isTypeof(item.hide) === "function") {
+          const hideStatus = item.hide();
+          return !hideStatus;
+        } else {
+          return !item.hide;
+        }
+      } else {
+        return true;
+      }
+    },
+    // 是否显示stype的组件,false显示文本
+    columnShowStatus(item, scope) {
+      let status = item.hasOwnProperty("iShowStatus")
+        ? isTypeof(item.iShowStatus) === "function"
+          ? item.iShowStatus(scope.row)
+          : item.iShowStatus
+        : true;
+
+      return status;
+    },
+    // 返回有权限的操作事件
+    tableButtonList(item, scope) {
+      let buttons =
+        isTypeof(item.list) === "array"
+          ? item.list
+          : isTypeof(item.list) === "function"
+          ? item.list(scope)
+          : [];
+
+      if (buttons && buttons.length > 0) {
+        return checkAuthButtonList(buttons, this.emTableList.authButtonList);
+      } else {
+        return [];
+      }
+    },
+    optCellData(row, option) {
+      // 操作后改变该行的背景颜色
+      if (option.bgcolor) {
+        if (row.hasOwnProperty("optCellSelfBgcolor")) {
+          row.optCellSelfBgcolor = option.bgcolor;
+        } else {
+          this.$set(row, "optCellSelfBgcolor", option.bgcolor);
+        }
+      }
+      this.emTableList.tableBtnEvent(row, option);
+    },
+    tableContentCellData(row, item) {
+      let obj = Object.create(null);
+      obj = Object.assign({}, item);
+      obj.type = item.field;
+
+      this.optCellData(row, obj);
+    },
+    formatterStyle(row, item) {
+      if (item.formatter) {
+        return selectListToValue(this, item.formatter, row[item.field], true);
+      } else {
+        return "";
+      }
+    },
+    //格式化
+    formatter(row, column, cellValue, index) {
+      return formatterSelf(this, row, column, cellValue, index);
+    },
+    // table图片地址拼接
+    tableImageURL(item, url) {
+      if (isHttpHeaderURL(url)) {
+        return url;
+      } else {
+        return item.fileHost + (item.path || "") + url;
+      }
+    },
+    loadChildren(tree, treeNode, resolve) {
+      if (
+        !this.emTableList.options.hasOwnProperty("lazy") ||
+        (this.emTableList.options.hasOwnProperty("lazy") &&
+          this.emTableList.options.lazy)
+      ) {
+        // 记录点击懒加载树的记录 load
+        const key = tree[this.emTableList.rowKey];
+
+        this.emTableList.loadTableTreeNodeMap.set(key, {
+          tree,
+          treeNode,
+          resolve,
+        });
+
+        unitTreeChildList(this, tree, treeNode, resolve);
+      }
+    },
+    formatterDef(row, column, cellValue, index) {
+      return formatterDefault(this, row, column, cellValue, index);
+    },
+    switchChange(row, item) {
+        tableListUpdateRowEvent(this, row, item);
+    },
+    sortReloadData(obj) {
+      this.emTableList.sortReloadData(obj);
+    },
+    handleSelectionChange(val) {
+      this.emTableList.handleSelectionChange(val);
+      if (this.isMultipleTables) {
+        bus.$emit("checkedEventTableToCard", val);
+      }
+    },
+    cellInputEvent(item) {
+      if (item.hasOwnProperty("isInput")) {
+        item.isInput = true;
+      } else {
+        this.$set(item, "isInput", true);
+      }
+    },
+    onInputBlur(row, item) {
+      tableListUpdateRowEvent(this, row, item);
+    },
+    submitInputEvent(row, item) {
+      item.isInput = false;
+      tableListUpdateRowEvent(this, row, item);
+    },
+    filterColHandler(filters) {
+      const finds = {};
+
+      for (let [k, v] of Object.entries(filters)) {
+        finds[k] = isTypeof(v) === "array" ? v.join(",") : v;
+      }
+      this.emTableList.paginationTick = false;
+      this.$nextTick(() => {
+        this.emTableList.pages.pageNum = 1;
+        this.emTableList.searchFormList(finds);
+        this.emTableList.paginationTick = true;
+      });
+
+      return false;
+    },
+    handleDisable(row, index) {
+      return this.emTableList.handleCheckboxDisable(row, index);
+    },
+    handleTagClose(item, row) {
+      tableListUpdateRowEvent(this, row, item);
+    },
+    handleTagListClose(item, row, tag) {
+      let rows = Object.assign({}, row);
+      if (isTypeof(tag) === "object") {
+        rows = Object.assign({}, row, tag);
+      } else {
+        rows["tagCustomId"] = tag;
+      }
+
+      if (item.action && item.action.url) {
+        this.handleTagClose(item, rows);
+      }
+    },
+  },
+};
+</script>
+<style lang="scss">
+.el-table .select-after-line {
+  background-color: #f0f9eb;
+  &.el-table__row--striped td {
+    background-color: #f0f9eb !important;
+  }
+}
+.el-table .cell {
+  white-space: pre-line;
+  //解决 动态隐藏列时 单元格内容显示不全问题
+  &.el-tooltip {
+    width:100% !important;
+  }
+  .icon-name {
+    display: inline-block;
+    i {
+      font-size: 12px;
+      padding-left: 0;
+      padding-right: 5px;
+    }
+  }
+  .input-label {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+
+    /*&.is-input {
+                input {
+                    color: #409EFF;
+                    font-size: 12px;
+                    height: 23px;
+                    line-height: 23px;
+                    border-color: #f2f2f2;
+                }
+                i {
+                    font-size: 15px;
+                    padding-left: 10px;
+                }
+            }
+
+            i {
+                color: #409EFF;
+            }*/
+  }
+
+  .table-input-form {
+    .el-form-item__label {
+      display: none;
+    }
+    .el-form-item {
+      margin-bottom: 0;
+    }
+    .el-input__inner {
+      padding: 2px 8px;
+    }
+    .input-label.is-input {
+      height: 32px;
+      i {
+        padding-left: 0;
+      }
+      input {
+        height: 28px;
+        line-height: 28px;
+      }
+    }
+  }
+}
+</style>

File diff suppressed because it is too large
+ 1225 - 0
packages/list/src/tool-list.js


+ 240 - 0
packages/list/src/tool-table.js

@@ -0,0 +1,240 @@
+import request from '../../../src/tools/request';
+import {
+    isTypeof,
+    axiosReqParams,
+    responseKeyToValue,
+    responseNodeParseList,
+    mergeRequestParams
+} from '../../../src/tools/utils';
+import { emitOnReqParams, getQueryParams } from './tool-list';
+
+
+function parseSelectData(selectObj, cellValue, vHtml, res) {
+    for (let item of selectObj) {
+        if (item.children) {
+            let itemVal = item.value + '',
+                currValue = cellValue.substring(0, itemVal.length),
+                lastValue = cellValue.substring(itemVal.length, cellValue.length);
+
+            if (itemVal == currValue) {
+                if (vHtml) {
+                    res = res ? (res + ' / <span style="' + item.style + '">' + item.label + '</span>') : ('<span style="' + item.style + '">' + item.label + '</span>');
+                } else {
+                    res = res ? (res + ' / ' + item.label) : item.label;
+                }
+
+                return parseSelectData(item.children, lastValue, vHtml, res);
+            }
+        } else {
+            if (item.value == cellValue) {
+                if (vHtml) {
+                    res = res ? (res + ' / <span style="' + item.style + '">' + item.label + '</span>') : ('<span style="' + item.style + '">' + item.label + '</span>');
+                } else {
+                    res = res ? (res + ' / ' + item.label) : item.label;
+                }
+
+                return res ? res : '-';
+            }
+        }
+    }
+}
+
+// 根据值解释
+export function selectListToValue(obj, formatter, cellValue, vHtml = false) {
+    let res = '',
+        selectList = obj.emTableList.select_list[formatter];
+
+    if (selectList) {
+        cellValue = cellValue ? cellValue.toString() : (cellValue == 0 ? '0' : '');
+        if (cellValue.indexOf(',') != -1) {
+            let vals = isTypeof(cellValue) == 'string' ? cellValue.split(',') : cellValue;
+
+            vals.forEach(item => {
+                for (let i = 0; i < selectList.length; i++) {
+                    if (item == selectList[i].value) {
+                        if (vHtml) {
+                            res = res ? (res + ' / <span style="' + selectList[i].style + '">' + selectList[i].label + '</span>') : ('<span style="' + selectList[i].style + '">' + selectList[i].label + '</span>');
+                        } else {
+                            res = res ? (res + ' / ' + selectList[i].label) : selectList[i].label;
+                        }
+                        break;
+                    }
+                }
+            });
+        } else {
+            res = parseSelectData(selectList, cellValue, vHtml, '');
+        }
+    }
+
+    return res;
+}
+
+//自定义解释字段
+export function formatterSelf(obj, row, column, cellValue, index) {
+    let res = '';
+
+    if (cellValue || cellValue === 0) {
+        obj.emTableList.page_column.forEach(item => {
+            if (item.formatter && item.field == column.property) {
+                res = selectListToValue(obj, item.formatter, cellValue, false);
+            }
+        });
+    } else {
+        res = '';
+    }
+
+    return res;
+}
+
+//默认字段解释
+export function formatterDefault(obj, row, column, cellValue, index) {
+    let res = typeof (cellValue) == 'object' ? '' : cellValue;
+
+    return res;
+}
+
+// table filters过滤条件
+export function unitTableFiltersNode(_this) {
+    _this.emTableList.page_column.forEach(item => {
+        if (item.filters) {
+            let filters = item.filters,
+                filterType = isTypeof(filters);
+
+            // 初始化filtersList的值
+            if (_this.filtersList.hasOwnProperty(item.field)) {
+                _this.filtersList[item.field] = null
+            } else {
+                _this.$set(_this.filtersList, item.field, null)
+            }
+            if (filterType === 'object') {
+                let query = filters,
+                    axios = Object.assign({}, _this.emTableList.axios, filters.axios),
+                    queryData = mergeRequestParams(filters.params, _this.emTableList._publicParams);
+
+                request(axiosReqParams(query, queryData, axios, _this.emTableList.options)).then((response) => {
+                    let next = { keyType: 'result' };
+                    if (responseKeyToValue(response, _this.emTableList, _this.emTableList, next)) {
+                        let data = responseNodeParseList(response, filters.node) || [];
+
+                        if (filters.props && data.length > 0) {
+                            data.forEach(node => {
+                                for (let [k, v] of Object.entries(filters.props)) {
+                                    node[k] = node[v]
+                                }
+                            })
+                        }
+                        _this.filtersList[item.field] = data;
+                    } else {
+                        console.error('请求接口错误,请求数据');
+                        console.error(query, queryData, axios);
+                    }
+                })
+            } else if (filterType === 'function') {
+                _this.filtersList[item.field] = eval('(' + item.filters + ')')();
+            } else if (filterType === 'array') {
+                _this.filtersList[item.field] = item.filters;
+            } else {
+                console.error('table列表中字段(' + item.field + ')的filters类型错误,类型为:null/object/array/function');
+            }
+        }
+    })
+}
+
+export function unitTreeChildList(_this, tree, treeNode, resolve) {
+    let item = _this.emTableList.queryURL['children'],
+        axios = _this.emTableList.axios,
+        queryData = isTypeof(_this.emTableList.queryParam) === 'function'
+            ? emitOnReqParams(_this.emTableList, 'children')
+            : getQueryParams(_this.emTableList, true);
+
+    // tree中获取参数
+    if (item.props) {
+        for (let [k, v] of Object.entries(item.props)) {
+            queryData[k] = tree[v] || (tree[v] === 0 || tree[v] === '0' ? tree[v] : '');
+        }
+    }
+    // 固定参数
+    if (item.param) {
+        for (let [k, v] of Object.entries(item.param)) {
+            queryData[k] = v || (v === 0 || v === '0' ? v : '');
+        }
+    }
+
+    request(axiosReqParams(item, mergeRequestParams(queryData, _this.emTableList._publicParams), axios, _this.emTableList.options)).then((response) => {
+        let next = { keyType: 'result' };
+        if (responseKeyToValue(response, _this.emTableList, _this.emTableList, next)) {
+            let childrenData = responseNodeParseList(response, item.parse.tableData) || [];
+
+            childrenData.forEach(dataItem => {
+                dataItem.hasChildren = item.tree ? (dataItem[item.tree.key] ? true : false) : false;
+            })
+            resolve(childrenData);
+        }
+    })
+}
+
+//table列表中操作,并修改改列的值;接口调用
+export function tableListUpdateRowEventImplement(obj, row, item, type = 'action') {
+    let queryData = {},
+        node = item[type],
+        axios = node.axios ? node.axios : obj.emTableList.axios,
+        success = node.success ? node.success : obj.emTableList.responseSuccess;
+
+    for (let [k, v] of Object.entries(node.params)) {
+        if (isTypeof(v) === 'string') {
+            if (v.startsWith('#')) { // 常量值
+                const val = v.substring(2);
+                if (v.startsWith('#0')) {
+                    queryData[k] = Number(val);
+                } else {
+                    queryData[k] = val;
+                }
+            } else {
+                const tmpValue = row[v] ? row[v] : ''
+                if (node.dataType && node.dataType.hasOwnProperty(k)) {
+                    queryData[k] = node.dataType[k] === 'array' ? [tmpValue] : (node.dataType[k] === 'boolean' ? (tmpValue ? true : false) : (node.dataType[k] === 'number' ? Number(tmpValue) : ''));
+                } else {
+                    queryData[k] = tmpValue;
+                }
+            }
+        } else {
+            queryData[k] = v
+        }
+    }
+
+    request(axiosReqParams(node, mergeRequestParams(queryData, obj.emTableList._publicParams), axios, obj.emTableList.options)).then((response) => {
+        obj.loading = false; //关闭遮罩load
+
+        responseKeyToValue(response, obj, obj.emTableList, success);
+
+        obj.sortReloadData(row);
+    }).catch((err) => {
+        obj.loading = false
+    });
+}
+
+//table列表中操作,并修改改列的值
+export function tableListUpdateRowEvent(obj, row, item, type = 'action') {
+    if (item.hasOwnProperty(type) && isTypeof(item[type]) === 'object' && item[type].url) {
+        let node = item[type];
+
+        if (node.hasOwnProperty('isConfirm') && node.isConfirm === false) {
+            tableListUpdateRowEventImplement(obj, row, item, type)
+        } else {
+            obj.$confirm(node.message ? node.message : '是否继续该操作', '操作提示', {
+                confirmButtonText: '确定',
+                cancelButtonText: '取消',
+                type: 'warning'
+            }).then(() => {
+                tableListUpdateRowEventImplement(obj, row, item, type)
+            }).catch((err) => {
+                obj.sortReloadData(row);
+                obj.loading = false
+            });
+        }
+    } else {
+        obj.emTableList.handleUpdateRowEvent(row, item);
+        // console.error('请配置URL地址');
+    }
+}
+

+ 8 - 0
packages/loading/index.js

@@ -0,0 +1,8 @@
+import NtLoading from './src/main';
+
+/* istanbul ignore next */
+NtLoading.install = function(Vue) {
+  Vue.component(NtLoading.name, NtLoading);
+};
+
+export default NtLoading;

+ 890 - 0
packages/loading/src/main.vue

@@ -0,0 +1,890 @@
+<template>
+  <div class="loading-center">
+    <div class="box"><div :class="'loader-' + type"></div></div>
+  </div>
+</template>
+<script>
+  export default {
+      name: 'emLoading',
+      props: {
+          type: {
+              type: String,
+              required: true
+          }
+      }
+  }
+</script>
+
+<style lang="scss">
+    .loading-center {
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        width: inherit;
+        height: 100%;
+        line-height: inherit;
+        text-align: center;
+    }
+    .box {
+        display: inline-block;
+        border-radius: 3px;
+        font-size: 30px;
+        text-align: center;
+        color: rgba(200, 200, 200, 0.5);
+        vertical-align: top;
+        -webkit-transition: .3s color, .3s border;
+        transition: .3s color, .3s border;
+    }
+    [class*="loader-"] {
+        display: inline-block;
+        width: 1em;
+        height: 1em;
+        color: inherit;
+        vertical-align: middle;
+        pointer-events: none;
+    }
+    .loader-01 {
+        border: .2em dotted currentcolor;
+        border-radius: 50%;
+        -webkit-animation: 1s loader-01 linear infinite;
+        animation: 1s loader-01 linear infinite;
+    }
+    @-webkit-keyframes loader-01 {
+        0% {
+            -webkit-transform: rotate(0deg);
+            transform: rotate(0deg);
+        }
+        100% {
+            -webkit-transform: rotate(360deg);
+            transform: rotate(360deg);
+        }
+    }
+    @keyframes loader-01 {
+        0% {
+            -webkit-transform: rotate(0deg);
+            transform: rotate(0deg);
+        }
+        100% {
+            -webkit-transform: rotate(360deg);
+            transform: rotate(360deg);
+        }
+    }
+    .loader-02 {
+        border: .2em solid transparent;
+        border-left-color: currentcolor;
+        border-right-color: currentcolor;
+        border-radius: 50%;
+        -webkit-animation: 1s loader-02 linear infinite;
+        animation: 1s loader-02 linear infinite;
+    }
+    @-webkit-keyframes loader-02 {
+        0% {
+            -webkit-transform: rotate(0deg);
+            transform: rotate(0deg);
+        }
+        100% {
+            -webkit-transform: rotate(360deg);
+            transform: rotate(360deg);
+        }
+    }
+    @keyframes loader-02 {
+        0% {
+            -webkit-transform: rotate(0deg);
+            transform: rotate(0deg);
+        }
+        100% {
+            -webkit-transform: rotate(360deg);
+            transform: rotate(360deg);
+        }
+    }
+    .loader-03 {
+        border: .2em solid currentcolor;
+        border-bottom-color: transparent;
+        border-radius: 50%;
+        -webkit-animation: 1s loader-03 linear infinite;
+        animation: 1s loader-03 linear infinite;
+        position: relative;
+    }
+    @-webkit-keyframes loader-03 {
+        0% {
+            -webkit-transform: rotate(0deg);
+            transform: rotate(0deg);
+        }
+        100% {
+            -webkit-transform: rotate(360deg);
+            transform: rotate(360deg);
+        }
+    }
+    @keyframes loader-03 {
+        0% {
+            -webkit-transform: rotate(0deg);
+            transform: rotate(0deg);
+        }
+        100% {
+            -webkit-transform: rotate(360deg);
+            transform: rotate(360deg);
+        }
+    }
+    .loader-04 {
+        border: 1px solid currentcolor;
+        border-radius: 50%;
+        -webkit-animation: 1s loader-04 linear infinite;
+        animation: 1s loader-04 linear infinite;
+        position: relative;
+    }
+    .loader-04:before {
+        content: '';
+        display: block;
+        width: 0;
+        height: 0;
+        position: absolute;
+        top: -.2em;
+        left: 50%;
+        border: .2em solid currentcolor;
+        border-radius: 50%;
+    }
+    @-webkit-keyframes loader-04 {
+        0% {
+            -webkit-transform: rotate(0deg);
+            transform: rotate(0deg);
+        }
+        100% {
+            -webkit-transform: rotate(360deg);
+            transform: rotate(360deg);
+        }
+    }
+    @keyframes loader-04 {
+        0% {
+            -webkit-transform: rotate(0deg);
+            transform: rotate(0deg);
+        }
+        100% {
+            -webkit-transform: rotate(360deg);
+            transform: rotate(360deg);
+        }
+    }
+    .loader-05 {
+        border: .2em solid transparent;
+        border-top-color: currentcolor;
+        border-radius: 50%;
+        -webkit-animation: 1s loader-05 linear infinite;
+        animation: 1s loader-05 linear infinite;
+        position: relative;
+    }
+    .loader-05:before {
+        content: '';
+        display: block;
+        width: inherit;
+        height: inherit;
+        position: absolute;
+        top: -.2em;
+        left: -.2em;
+        border: .2em solid currentcolor;
+        border-radius: 50%;
+        opacity: .5;
+    }
+    @-webkit-keyframes loader-05 {
+        0% {
+            -webkit-transform: rotate(0deg);
+            transform: rotate(0deg);
+        }
+        100% {
+            -webkit-transform: rotate(360deg);
+            transform: rotate(360deg);
+        }
+    }
+    @keyframes loader-05 {
+        0% {
+            -webkit-transform: rotate(0deg);
+            transform: rotate(0deg);
+        }
+        100% {
+            -webkit-transform: rotate(360deg);
+            transform: rotate(360deg);
+        }
+    }
+    .loader-06 {
+        border: .2em solid currentcolor;
+        border-radius: 50%;
+        -webkit-animation: loader-06 1s ease-out infinite;
+        animation: loader-06 1s ease-out infinite;
+    }
+    @-webkit-keyframes loader-06 {
+        0% {
+            -webkit-transform: scale(0);
+            transform: scale(0);
+            opacity: 0;
+        }
+        50% {
+            opacity: 1;
+        }
+        100% {
+            -webkit-transform: scale(1);
+            transform: scale(1);
+            opacity: 0;
+        }
+    }
+    @keyframes loader-06 {
+        0% {
+            -webkit-transform: scale(0);
+            transform: scale(0);
+            opacity: 0;
+        }
+        50% {
+            opacity: 1;
+        }
+        100% {
+            -webkit-transform: scale(1);
+            transform: scale(1);
+            opacity: 0;
+        }
+    }
+    .loader-07 {
+        border: 0 solid transparent;
+        border-radius: 50%;
+        position: relative;
+    }
+    .loader-07:before,
+    .loader-07:after {
+        content: '';
+        border: .2em solid currentcolor;
+        border-radius: 50%;
+        width: inherit;
+        height: inherit;
+        position: absolute;
+        top: 0;
+        left: 0;
+        -webkit-animation: loader-07 1s linear infinite;
+        animation: loader-07 1s linear infinite;
+        opacity: 0;
+    }
+    .loader-07:before {
+        -webkit-animation-delay: 1s;
+        animation-delay: 1s;
+    }
+    .loader-07:after {
+        -webkit-animation-delay: .5s;
+        animation-delay: .5s;
+    }
+    @-webkit-keyframes loader-07 {
+        0% {
+            -webkit-transform: scale(0);
+            transform: scale(0);
+            opacity: 0;
+        }
+        50% {
+            opacity: 1;
+        }
+        100% {
+            -webkit-transform: scale(1);
+            transform: scale(1);
+            opacity: 0;
+        }
+    }
+    @keyframes loader-07 {
+        0% {
+            -webkit-transform: scale(0);
+            transform: scale(0);
+            opacity: 0;
+        }
+        50% {
+            opacity: 1;
+        }
+        100% {
+            -webkit-transform: scale(1);
+            transform: scale(1);
+            opacity: 0;
+        }
+    }
+    .loader-08 {
+        position: relative;
+    }
+    .loader-08:before,
+    .loader-08:after {
+        content: '';
+        width: inherit;
+        height: inherit;
+        border-radius: 50%;
+        background-color: currentcolor;
+        opacity: 0.6;
+        position: absolute;
+        top: 0;
+        left: 0;
+        -webkit-animation: loader-08 2.0s infinite ease-in-out;
+        animation: loader-08 2.0s infinite ease-in-out;
+    }
+    .loader-08:after {
+        -webkit-animation-delay: -1.0s;
+        animation-delay: -1.0s;
+    }
+    @-webkit-keyframes loader-08 {
+        0%, 100% {
+            -webkit-transform: scale(0);
+            transform: scale(0);
+        }
+        50% {
+            -webkit-transform: scale(1);
+            transform: scale(1);
+        }
+    }
+    @keyframes loader-08 {
+        0%, 100% {
+            -webkit-transform: scale(0);
+            transform: scale(0);
+        }
+        50% {
+            -webkit-transform: scale(1);
+            transform: scale(1);
+        }
+    }
+    .loader-09 {
+        background-color: currentcolor;
+        border-radius: 50%;
+        -webkit-animation: loader-09 1.0s infinite ease-in-out;
+        animation: loader-09 1.0s infinite ease-in-out;
+    }
+    @-webkit-keyframes loader-09 {
+        0% {
+            -webkit-transform: scale(0);
+            transform: scale(0);
+        }
+        100% {
+            -webkit-transform: scale(1);
+            transform: scale(1);
+            opacity: 0;
+        }
+    }
+    @keyframes loader-09 {
+        0% {
+            -webkit-transform: scale(0);
+            transform: scale(0);
+        }
+        100% {
+            -webkit-transform: scale(1);
+            transform: scale(1);
+            opacity: 0;
+        }
+    }
+    .loader-10 {
+        position: relative;
+        -webkit-animation: loader-10-1 2.0s infinite linear;
+        animation: loader-10-1 2.0s infinite linear;
+    }
+    .loader-10:before,
+    .loader-10:after {
+        content: '';
+        width: 0;
+        height: 0;
+        border: .5em solid currentcolor;
+        display: block;
+        position: absolute;
+        border-radius: 100%;
+        -webkit-animation: loader-10-2 2s infinite ease-in-out;
+        animation: loader-10-2 2s infinite ease-in-out;
+    }
+    .loader-10:before {
+        top: 0;
+        left: 50%;
+    }
+    .loader-10:after {
+        bottom: 0;
+        right: 50%;
+        -webkit-animation-delay: -1s;
+        animation-delay: -1s;
+    }
+    @-webkit-keyframes loader-10-1 {
+        100% {
+            -webkit-transform: rotate(360deg);
+            transform: rotate(360deg);
+        }
+    }
+    @keyframes loader-10-1 {
+        100% {
+            -webkit-transform: rotate(360deg);
+            transform: rotate(360deg);
+        }
+    }
+    @-webkit-keyframes loader-10-2 {
+        0%, 100% {
+            -webkit-transform: scale(0);
+            transform: scale(0);
+        }
+        50% {
+            -webkit-transform: scale(1);
+            transform: scale(1);
+        }
+    }
+    @keyframes loader-10-2 {
+        0%, 100% {
+            -webkit-transform: scale(0);
+            transform: scale(0);
+        }
+        50% {
+            -webkit-transform: scale(1);
+            transform: scale(1);
+        }
+    }
+    .loader-11 {
+        background-color: currentcolor;
+        -webkit-animation: loader-11 1.2s infinite ease-in-out;
+        animation: loader-11 1.2s infinite ease-in-out;
+    }
+    @-webkit-keyframes loader-11 {
+        0% {
+            -webkit-transform: perspective(120px) rotateX(0deg) rotateY(0deg);
+            transform: perspective(120px) rotateX(0deg) rotateY(0deg);
+        }
+        50% {
+            -webkit-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg);
+            transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg);
+        }
+        100% {
+            -webkit-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);
+            transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);
+        }
+    }
+    @keyframes loader-11 {
+        0% {
+            -webkit-transform: perspective(120px) rotateX(0deg) rotateY(0deg);
+            transform: perspective(120px) rotateX(0deg) rotateY(0deg);
+        }
+        50% {
+            -webkit-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg);
+            transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg);
+        }
+        100% {
+            -webkit-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);
+            transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);
+        }
+    }
+    .loader-12 {
+        position: relative;
+    }
+    .loader-12:before,
+    .loader-12:after {
+        content: '';
+        display: block;
+        position: absolute;
+        background-color: currentcolor;
+        left: 50%;
+        right: 0;
+        top: 0;
+        bottom: 50%;
+        box-shadow: -.5em 0 0 currentcolor;
+        -webkit-animation: loader-12 1s linear infinite;
+        animation: loader-12 1s linear infinite;
+    }
+    .loader-12:after {
+        top: 50%;
+        bottom: 0;
+        -webkit-animation-delay: .25s;
+        animation-delay: .25s;
+    }
+    @-webkit-keyframes loader-12 {
+        0%, 100% {
+            box-shadow: -.5em 0 0 transparent;
+            background-color: currentcolor;
+        }
+        50% {
+            box-shadow: -.5em 0 0 currentcolor;
+            background-color: transparent;
+        }
+    }
+    @keyframes loader-12 {
+        0%, 100% {
+            box-shadow: -.5em 0 0 transparent;
+            background-color: currentcolor;
+        }
+        50% {
+            box-shadow: -.5em 0 0 currentcolor;
+            background-color: transparent;
+        }
+    }
+    .loader-13:before,
+    .loader-13:after,
+    .loader-13 {
+        border-radius: 50%;
+        -webkit-animation-fill-mode: both;
+        animation-fill-mode: both;
+        -webkit-animation: loader-13 1.8s infinite ease-in-out;
+        animation: loader-13 1.8s infinite ease-in-out;
+    }
+    .loader-13 {
+        color: currentcolor;
+        position: relative;
+        -webkit-transform: translateZ(0);
+        transform: translateZ(0);
+        -webkit-animation-delay: -0.16s;
+        animation-delay: -0.16s;
+        top: -1em;
+    }
+    .loader-13:before {
+        right: 100%;
+        -webkit-animation-delay: -0.32s;
+        animation-delay: -0.32s;
+    }
+    .loader-13:after {
+        left: 100%;
+    }
+    .loader-13:before,
+    .loader-13:after {
+        content: '';
+        display: block;
+        position: absolute;
+        top: 0;
+        width: inherit;
+        height: inherit;
+    }
+    @-webkit-keyframes loader-13 {
+        0%, 80%, 100% {
+            box-shadow: 0 1em 0 -1em;
+        }
+        40% {
+            box-shadow: 0 1em 0 -.2em;
+        }
+    }
+    @keyframes loader-13 {
+        0%, 80%, 100% {
+            box-shadow: 0 1em 0 -1em;
+        }
+        40% {
+            box-shadow: 0 1em 0 -.2em;
+        }
+    }
+    .loader-14 {
+        border-radius: 50%;
+        box-shadow: 0 1em 0 -.2em currentcolor;
+        position: relative;
+        -webkit-animation: loader-14 0.8s ease-in-out alternate infinite;
+        animation: loader-14 0.8s ease-in-out alternate infinite;
+        -webkit-animation-delay: 0.32s;
+        animation-delay: 0.32s;
+        top: -1em;
+    }
+    .loader-14:after,
+    .loader-14:before {
+        content: '';
+        position: absolute;
+        width: inherit;
+        height: inherit;
+        border-radius: inherit;
+        box-shadow: inherit;
+        -webkit-animation: inherit;
+        animation: inherit;
+    }
+    .loader-14:before {
+        left: -1em;
+        -webkit-animation-delay: 0.48s;
+        animation-delay: 0.48s;
+    }
+    .loader-14:after {
+        right: -1em;
+        -webkit-animation-delay: 0.16s;
+        animation-delay: 0.16s;
+    }
+    @-webkit-keyframes loader-14 {
+        0% {
+            box-shadow: 0 2em 0 -.2em currentcolor;
+        }
+        100% {
+            box-shadow: 0 1em 0 -.2em currentcolor;
+        }
+    }
+    @keyframes loader-14 {
+        0% {
+            box-shadow: 0 2em 0 -.2em currentcolor;
+        }
+        100% {
+            box-shadow: 0 1em 0 -.2em currentcolor;
+        }
+    }
+    .loader-15 {
+        background: currentcolor;
+        position: relative;
+        -webkit-animation: loader-15 1s ease-in-out infinite;
+        animation: loader-15 1s ease-in-out infinite;
+        -webkit-animation-delay: 0.4s;
+        animation-delay: 0.4s;
+        width: .25em;
+        height: .5em;
+    }
+    .loader-15:after,
+    .loader-15:before {
+        content: '';
+        position: absolute;
+        width: inherit;
+        height: inherit;
+        background: inherit;
+        -webkit-animation: inherit;
+        animation: inherit;
+    }
+    .loader-15:before {
+        right: .5em;
+        -webkit-animation-delay: 0.2s;
+        animation-delay: 0.2s;
+    }
+    .loader-15:after {
+        left: .5em;
+        -webkit-animation-delay: 0.6s;
+        animation-delay: 0.6s;
+    }
+    @-webkit-keyframes loader-15 {
+        0%, 100% {
+            box-shadow: 0 0 0 currentcolor, 0 0 0 currentcolor;
+        }
+        50% {
+            box-shadow: 0 -.25em 0 currentcolor, 0 .25em 0 currentcolor;
+        }
+    }
+    @keyframes loader-15 {
+        0%, 100% {
+            box-shadow: 0 0 0 currentcolor, 0 0 0 currentcolor;
+        }
+        50% {
+            box-shadow: 0 -.25em 0 currentcolor, 0 .25em 0 currentcolor;
+        }
+    }
+    .loader-16 {
+        -webkit-transform: rotateZ(45deg);
+        transform: rotateZ(45deg);
+        -webkit-perspective: 1000px;
+        perspective: 1000px;
+        border-radius: 50%;
+    }
+    .loader-16:before,
+    .loader-16:after {
+        content: '';
+        display: block;
+        position: absolute;
+        top: 0;
+        left: 0;
+        width: inherit;
+        height: inherit;
+        border-radius: 50%;
+        -webkit-animation: 1s spin linear infinite;
+        animation: 1s spin linear infinite;
+    }
+    .loader-16:before {
+        -webkit-transform: rotateX(70deg);
+        transform: rotateX(70deg);
+    }
+    .loader-16:after {
+        -webkit-transform: rotateY(70deg);
+        transform: rotateY(70deg);
+        -webkit-animation-delay: .4s;
+        animation-delay: .4s;
+    }
+    @-webkit-keyframes rotate {
+        0% {
+            -webkit-transform: translate(-50%, -50%) rotateZ(0deg);
+            transform: translate(-50%, -50%) rotateZ(0deg);
+        }
+        100% {
+            -webkit-transform: translate(-50%, -50%) rotateZ(360deg);
+            transform: translate(-50%, -50%) rotateZ(360deg);
+        }
+    }
+    @keyframes rotate {
+        0% {
+            -webkit-transform: translate(-50%, -50%) rotateZ(0deg);
+            transform: translate(-50%, -50%) rotateZ(0deg);
+        }
+        100% {
+            -webkit-transform: translate(-50%, -50%) rotateZ(360deg);
+            transform: translate(-50%, -50%) rotateZ(360deg);
+        }
+    }
+    @-webkit-keyframes rotateccw {
+        0% {
+            -webkit-transform: translate(-50%, -50%) rotate(0deg);
+            transform: translate(-50%, -50%) rotate(0deg);
+        }
+        100% {
+            -webkit-transform: translate(-50%, -50%) rotate(-360deg);
+            transform: translate(-50%, -50%) rotate(-360deg);
+        }
+    }
+    @keyframes rotateccw {
+        0% {
+            -webkit-transform: translate(-50%, -50%) rotate(0deg);
+            transform: translate(-50%, -50%) rotate(0deg);
+        }
+        100% {
+            -webkit-transform: translate(-50%, -50%) rotate(-360deg);
+            transform: translate(-50%, -50%) rotate(-360deg);
+        }
+    }
+    @-webkit-keyframes spin {
+        0%, 100% {
+            box-shadow: .2em 0px 0 0px currentcolor;
+        }
+        12% {
+            box-shadow: .2em .2em 0 0 currentcolor;
+        }
+        25% {
+            box-shadow: 0 .2em 0 0px currentcolor;
+        }
+        37% {
+            box-shadow: -.2em .2em 0 0 currentcolor;
+        }
+        50% {
+            box-shadow: -.2em 0 0 0 currentcolor;
+        }
+        62% {
+            box-shadow: -.2em -.2em 0 0 currentcolor;
+        }
+        75% {
+            box-shadow: 0px -.2em 0 0 currentcolor;
+        }
+        87% {
+            box-shadow: .2em -.2em 0 0 currentcolor;
+        }
+    }
+    @keyframes spin {
+        0%, 100% {
+            box-shadow: .2em 0px 0 0px currentcolor;
+        }
+        12% {
+            box-shadow: .2em .2em 0 0 currentcolor;
+        }
+        25% {
+            box-shadow: 0 .2em 0 0px currentcolor;
+        }
+        37% {
+            box-shadow: -.2em .2em 0 0 currentcolor;
+        }
+        50% {
+            box-shadow: -.2em 0 0 0 currentcolor;
+        }
+        62% {
+            box-shadow: -.2em -.2em 0 0 currentcolor;
+        }
+        75% {
+            box-shadow: 0px -.2em 0 0 currentcolor;
+        }
+        87% {
+            box-shadow: .2em -.2em 0 0 currentcolor;
+        }
+    }
+    .loader-17 {
+        position: relative;
+        background-color: currentcolor;
+        border-radius: 50%;
+    }
+    .loader-17:after,
+    .loader-17:before {
+        content: "";
+        position: absolute;
+        width: .25em;
+        height: .25em;
+        border-radius: 50%;
+        opacity: .8;
+    }
+    .loader-17:after {
+        left: -.5em;
+        top: -.25em;
+        background-color: currentcolor;
+        -webkit-transform-origin: .75em 1em;
+        transform-origin: .75em 1em;
+        -webkit-animation: loader-17 1s linear infinite;
+        animation: loader-17 1s linear infinite;
+        opacity: .6;
+    }
+    .loader-17:before {
+        left: -1.25em;
+        top: -.75em;
+        background-color: currentcolor;
+        -webkit-transform-origin: 1.5em 1em;
+        transform-origin: 1.5em 1em;
+        -webkit-animation: loader-17 2s linear infinite;
+        animation: loader-17 2s linear infinite;
+    }
+    @-webkit-keyframes loader-17 {
+        0% {
+            -webkit-transform: rotateZ(0deg) translate3d(0, 0, 0);
+            transform: rotateZ(0deg) translate3d(0, 0, 0);
+        }
+        100% {
+            -webkit-transform: rotateZ(360deg) translate3d(0, 0, 0);
+            transform: rotateZ(360deg) translate3d(0, 0, 0);
+        }
+    }
+    @keyframes loader-17 {
+        0% {
+            -webkit-transform: rotateZ(0deg) translate3d(0, 0, 0);
+            transform: rotateZ(0deg) translate3d(0, 0, 0);
+        }
+        100% {
+            -webkit-transform: rotateZ(360deg) translate3d(0, 0, 0);
+            transform: rotateZ(360deg) translate3d(0, 0, 0);
+        }
+    }
+    .loader-18 {
+        position: relative;
+    }
+    .loader-18:before,
+    .loader-18:after {
+        content: '';
+        display: block;
+        position: absolute;
+        border-radius: 50%;
+        border: .1em solid transparent;
+        border-bottom-color: currentcolor;
+        top: 0;
+        left: 0;
+        -webkit-animation: 1s loader-18 linear infinite;
+        animation: 1s loader-18 linear infinite;
+    }
+    .loader-18:before {
+        width: 1em;
+        height: 1em;
+    }
+    .loader-18:after {
+        width: .8em;
+        height: .8em;
+        top: .1em;
+        left: .1em;
+        -webkit-animation-direction: reverse;
+        animation-direction: reverse;
+    }
+    @-webkit-keyframes loader-18 {
+        0% {
+            -webkit-transform: rotate(0deg);
+            transform: rotate(0deg);
+        }
+        100% {
+            -webkit-transform: rotate(360deg);
+            transform: rotate(360deg);
+        }
+    }
+    @keyframes loader-18 {
+        0% {
+            -webkit-transform: rotate(0deg);
+            transform: rotate(0deg);
+        }
+        100% {
+            -webkit-transform: rotate(360deg);
+            transform: rotate(360deg);
+        }
+    }
+    .loader-19 {
+        border-top: .2em solid currentcolor;
+        border-right: .2em solid transparent;
+        -webkit-animation: loader-19 1s linear infinite;
+        animation: loader-19 1s linear infinite;
+        border-radius: 100%;
+        position: relative;
+    }
+    @-webkit-keyframes loader-19 {
+        to {
+            -webkit-transform: rotate(360deg);
+            transform: rotate(360deg);
+        }
+    }
+    @keyframes loader-19 {
+        to {
+            -webkit-transform: rotate(360deg);
+            transform: rotate(360deg);
+        }
+    }
+</style>

+ 8 - 0
packages/map/index.js

@@ -0,0 +1,8 @@
+import NtMap from './src/main';
+
+/* istanbul ignore next */
+NtMap.install = function(Vue) {
+  Vue.component(NtMap.name, NtMap);
+};
+
+export default NtMap;

+ 173 - 0
packages/map/src/main.vue

@@ -0,0 +1,173 @@
+<template>
+  <div>
+    <baidu-map v-bind:style="mapStyle" class="bm-view" :ak="akey"
+               :center="center"
+               :zoom="zoom"
+               :scroll-wheel-zoom="true"
+               @click="getClickInfo"
+               @moving="syncCenterAndZoom"
+               @moveend="syncCenterAndZoom"
+               @zoomend="syncCenterAndZoom">
+      <bm-view style="width: 100%; height:100%;"></bm-view>
+      <bm-marker v-if="positionUrl" :position="{lng: center.lng, lat: center.lat}" :dragging="true"
+                 animation="BMAP_ANIMATION_BOUNCE" :icon="{ url: positionUrl, size: { width: 54, height: 54 }}"></bm-marker>
+      <bm-marker v-else :position="{lng: center.lng, lat: center.lat}" :dragging="true"
+                 animation="BMAP_ANIMATION_BOUNCE"></bm-marker>
+      <bm-control :offset="{width: '10px', height: '10px'}">
+        <bm-auto-complete v-model="address" :sugStyle="{zIndex: 999999}">
+          <input type="text" v-model="address" placeholder="请输入搜索关键字" class="serachinput">
+        </bm-auto-complete>
+      </bm-control>
+      <bm-local-search :keyword="address" :auto-viewport="true" style="width:0px;height:0px;overflow: hidden;"></bm-local-search>
+    </baidu-map>
+    <div class="footer">
+      <div class="nt-info nt-btn-bottom" @click.stop="mapCancel">取消</div>
+      <div class="nt-primary nt-btn-bottom" @click.prevent.stop="mapConfirm">确定</div>
+    </div>
+  </div>
+</template>
+<script>
+import {BaiduMap, BmControl, BmView, BmAutoComplete, BmLocalSearch, BmMarker} from 'vue-baidu-map'
+
+export default {
+  name: 'NtMap',
+  components: {
+    BaiduMap,
+    BmControl,
+    BmView,
+    BmAutoComplete,
+    BmLocalSearch,
+    BmMarker
+  },
+  data: function () {
+    return {
+      address: this.addressKey,
+      mapStyle: {
+        width: '100%',
+        overflow: 'hidden',
+        minHeight: '280px',
+        height: this.mapHeight
+      },
+      center: {lng: 116.404, lat: 39.915},
+      zoom: 15
+    }
+  },
+  watch: {},
+  props: {
+    akey: {
+      type: String,
+      default: 'dfhycORtYDMz78dNLo9oNiDO1ufI2TZS'
+    },
+    addressKey: {
+      type: String,
+      default: ''
+    },
+    pointValue: {
+      type: String,
+      default: ''
+    },
+    inputItem: {
+      type: Object
+    },
+    mapHeight: {
+      type: String,
+      default: '280px'
+    },
+    positionUrl: {
+      type: String
+    }
+  },
+  created() {
+    this.initData()
+  },
+  methods: {
+    initData() {
+      if (this.pointValue) {
+        const points = this.pointValue.split(((this.inputItem && this.inputItem.sign) || ','));
+
+        this.address = '';
+        this.center = {
+          lng: points[0] || 116.404,
+          lat: points[1] || 39.915
+        }
+      }
+    },
+    /***
+     * 地图点击事件。
+     */
+    getClickInfo(e) {
+      this.center.lng = e.point.lng
+      this.center.lat = e.point.lat
+    },
+    syncCenterAndZoom(e) {
+      const {lng, lat} = e.target.getCenter()
+      this.center.lng = lng
+      this.center.lat = lat
+      this.zoom = e.target.getZoom()
+    },
+    /***确认*/
+    mapConfirm: function () {
+      this.$emit('mapConfirm', this.center)
+    },
+    /***取消*/
+    mapCancel: function () {
+      this.$emit('mapCancel')
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.serachinput {
+  width: 200px;
+  box-sizing: border-box;
+  padding: 9px;
+  border: 1px solid #dddee1;
+  line-height: 12px;
+  font-size: 12px;
+  height: 30px;
+  color: #333;
+  outline-width: 0;
+  outline-color: rgba(0, 0, 0, 0);
+  position: relative;
+  border-radius: 4px;
+  -webkit-box-shadow: #666 0px 0px 10px;
+  -moz-box-shadow: #666 0px 0px 10px;
+  box-shadow: #666 0px 0px 10px;
+}
+
+.footer {
+  padding-top: 10px;
+  text-align: center;
+  display: flex;
+  justify-content: flex-end;
+  align-items: center;
+
+  .nt-btn-bottom {
+    width: 60px;
+    height: 28px;
+    line-height: 25px;
+    margin-left: 10px;
+    cursor: pointer;
+
+    &.nt-info {
+      border: 1px solid #e2e2e2;
+      background-color: #f8f8f8;
+
+      &:hover {
+        background-color: rgba(248, 248, 248, .5)
+      }
+    }
+
+    &.nt-primary {
+      color: #ffffff;
+      border: 1px solid #409EFF;
+      background-color: #409EFF;
+
+      &:hover {
+        background-color: rgba(64, 158, 255, .85)
+      }
+    }
+  }
+}
+</style>

+ 8 - 0
packages/re-form/index.js

@@ -0,0 +1,8 @@
+import NtRemoveForm from '../form/src/remove-form';
+
+/* istanbul ignore next */
+NtRemoveForm.install = function(Vue) {
+  Vue.component(NtRemoveForm.name, NtRemoveForm);
+};
+
+export default NtRemoveForm;

+ 9 - 0
packages/row/index.js

@@ -0,0 +1,9 @@
+import NtRow from './src/main';
+
+/* istanbul ignore next */
+NtRow.install = function(Vue) {
+  Vue.component(NtRow.name, NtRow);
+};
+
+export default NtRow;
+

+ 48 - 0
packages/row/src/main.js

@@ -0,0 +1,48 @@
+export default {
+    name: 'NtRow',
+  
+    componentName: 'NtRow',
+  
+    props: {
+      tag: {
+        type: String,
+        default: 'div'
+      },
+      gutter: Number,
+      type: String,
+      justify: {
+        type: String,
+        default: 'start'
+      },
+      align: {
+        type: String,
+        default: 'top'
+      }
+    },
+  
+    computed: {
+      style() {
+        const ret = {};
+  
+        if (this.gutter) {
+          ret.marginLeft = `-${this.gutter / 2}px`;
+          ret.marginRight = ret.marginLeft;
+        }
+  
+        return ret;
+      }
+    },
+  
+    render(h) {
+      return h(this.tag, {
+        class: [
+          'nt-row',
+          this.justify !== 'start' ? `is-justify-${this.justify}` : '',
+          this.align !== 'top' ? `is-align-${this.align}` : '',
+          { 'nt-row--flex': this.type === 'flex' }
+        ],
+        style: this.style
+      }, this.$slots.default);
+    }
+  };
+  

+ 2 - 0
packages/toast/index.js

@@ -0,0 +1,2 @@
+import Toast from './src/toast.js';
+export default Toast;

+ 60 - 0
packages/toast/src/toast.js

@@ -0,0 +1,60 @@
+import Vue from 'vue';
+import VueToast from './toast.vue'
+
+const ToastConstructor = Vue.extend(VueToast);
+let toastPool = [];
+
+let getAnInstance = () => {
+  if (toastPool.length > 0) {
+    let instance = toastPool[0];
+    toastPool.splice(0, 1);
+    return instance;
+  }
+  return new ToastConstructor({
+    el: document.createElement('div')
+  });
+};
+
+let returnAnInstance = instance => {
+  if (instance) {
+    toastPool.push(instance);
+  }
+};
+
+let removeDom = event => {
+  if (event.target.parentNode) {
+    event.target.parentNode.removeChild(event.target);
+  }
+};
+
+ToastConstructor.prototype.close = function() {
+  this.visible = false;
+  this.$el.addEventListener('transitionend', removeDom);
+  this.closed = true;
+  returnAnInstance(this);
+};
+
+let Toast = (options = {}) => {
+  let duration = options.duration || 3000;
+
+  let instance = getAnInstance();
+  instance.closed = false;
+  clearTimeout(instance.timer);
+  instance.message = typeof options === 'string' ? options : options.message;
+  instance.position = options.position || 'middle';
+  instance.className = options.className || '';
+  instance.iconClass = options.iconClass || '';
+
+  document.body.appendChild(instance.$el);
+  Vue.nextTick(function() {
+    instance.visible = true;
+    instance.$el.removeEventListener('transitionend', removeDom);
+    ~duration && (instance.timer = setTimeout(function() {
+      if (instance.closed) return;
+      instance.close();
+    }, duration));
+  });
+  return instance;
+};
+
+export default Toast;

File diff suppressed because it is too large
+ 2060 - 0
packages/toast/src/toast.vue


+ 8 - 0
packages/ueditor/index.js

@@ -0,0 +1,8 @@
+import NtUEditor from './src/main';
+
+/* istanbul ignore next */
+NtUEditor.install = function (Vue) {
+    Vue.component(NtUEditor.name, NtUEditor);
+};
+
+export default NtUEditor;

+ 66 - 0
packages/ueditor/src/main.vue

@@ -0,0 +1,66 @@
+<template>
+    <vue-ueditor-wrap :ref="custRef" v-model="rowData[inputItem.field]" :config="currConfig"></vue-ueditor-wrap>
+</template>
+<script>
+    import VueUeditorWrap from './vueUeditorWrap'
+
+    export default {
+        name: 'NtUEditor',
+        components: {VueUeditorWrap},
+        props: {
+            custRef: {
+                type: String,
+                default: 'ueditor'
+            },
+            config: {
+                type: Object,
+                default: function () {
+                    return {}
+                }
+            },
+            inputItem: {
+                type: Object,
+                required: true
+            },
+            inputType: {
+                type: String,
+                default: 'show'
+            },
+            rowData: {
+                required: true
+            },
+        },
+        watch: {},
+        data() {
+            return {
+                currConfig: {},
+                defaultConfig: {
+                    // 编辑器不自动被内容撑高
+                    autoHeightEnabled: false,
+                    // 初始容器高度
+                    initialFrameHeight: 400,
+                    // 初始容器宽度
+                    initialFrameWidth: '100%',
+                    // 上传文件接口(这个地址是我为了方便各位体验文件上传功能搭建的临时接口,请勿在生产环境使用!!!部署在国外的服务器,如果无法访问,请自备梯子)
+                    serverUrl: '/',
+                    UEDITOR_HOME_URL: './UEditor/'
+                }
+            };
+        },
+        created: function () {
+            this.initData();
+        },
+        methods: {
+            initData() {
+                if (typeof(this.config.headers) === 'function') {
+                    this.config.headers = this.config.headers()
+                }
+                console.log(this.config)
+                this.currConfig = Object.assign({}, this.defaultConfig, this.config);
+            }
+        }
+    };
+</script>
+<style lang="scss" scoped>
+
+</style>

+ 17 - 0
packages/ueditor/src/utils/Debounce.js

@@ -0,0 +1,17 @@
+/**
+ * 一个简单的函数防抖
+ * @param {Function} fun 需要限制执行频率的函数
+ * @param {Number} delay 延迟时间,这段时间过后,才可触发第二次
+ */
+export default function (fun, delay) {
+  var timer = null;
+  var debounced = function () {
+    var ctx = this;
+    var args = arguments;
+    if (timer) clearTimeout(timer);
+    timer = setTimeout(function () {
+      fun.apply(ctx, args);
+    }, delay);
+  };
+  return debounced;
+}

+ 14 - 0
packages/ueditor/src/utils/Event.js

@@ -0,0 +1,14 @@
+// 一个简单的事件订阅发布的实现,取代原生Event对象,提升IE下的兼容性
+function LoadEvent () {
+  this.listeners = {};
+  this.on = function (eventName, callback) {
+    if (this.listeners[eventName] === undefined) {
+      this.listeners[eventName] = [];
+    }
+    this.listeners[eventName].push(callback);
+  };
+  this.emit = function (eventName) {
+    this.listeners[eventName] && this.listeners[eventName].forEach(callback => callback());
+  };
+}
+export default LoadEvent;

+ 273 - 0
packages/ueditor/src/vueUeditorWrap.vue

@@ -0,0 +1,273 @@
+<template>
+  <div>
+    <script ref="script" :name="name" type="text/plain"></script>
+  </div>
+</template>
+
+<script>
+import LoadEvent from './utils/Event.js';
+import Debounce from './utils/Debounce.js';
+export default {
+  name: 'VueUeditorWrap',
+  data () {
+    return {
+      status: 0,
+      initValue: '',
+      defaultConfig: {
+        // VUE CLI 3 会添加 process.env.BASE_URL 的环境变量,而 VUE CLI 2 没有,所以借此设置 UEDITOR_HOME_URL,能涵盖大部分 Vue 开发者的使用场景
+        UEDITOR_HOME_URL: process.env.BASE_URL ? process.env.BASE_URL + 'UEditor/' : '/static/UEditor/'
+      }
+    };
+  },
+  props: {
+    // v-model 实现方式
+    mode: {
+      type: String,
+      default: 'observer',
+      validator: function (value) {
+        // 1. observer 借助 MutationObserver API https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver
+        // 2. listener 借助 UEditor 的 contentChange 事件 https://ueditor.baidu.com/doc/#UE.Editor:contentChange
+        return ['observer', 'listener'].indexOf(value) !== -1;
+      }
+    },
+    value: {
+      type: String,
+      default: ''
+    },
+    config: {
+      type: Object,
+      default: function () {
+        return {};
+      }
+    },
+    init: {
+      type: Function,
+      default: function () {
+        return () => {};
+      }
+    },
+    destroy: {
+      type: Boolean,
+      default: false
+    },
+    name: {
+      type: String,
+      default: ''
+    },
+    observerDebounceTime: {
+      type: Number,
+      default: 50,
+      validator: function (value) {
+        return value >= 20;
+      }
+    },
+    observerOptions: {
+      type: Object,
+      default: function () {
+        // https://developer.mozilla.org/en-US/docs/Web/API/MutationObserverInit
+        return {
+          attributes: true, // 是否监听 DOM 元素的属性变化
+          attributeFilter: ['src', 'style', 'type', 'name'], // 只有在该数组中的属性值的变化才会监听
+          characterData: true, // 是否监听文本节点
+          childList: true, // 是否监听子节点
+          subtree: true // 是否监听后代元素
+        };
+      }
+    },
+    // 本组件提供对普通 Vue 项目和 Nuxt 项目开箱即用的支持,但如果是自己搭建的 Vue SSR 项目,可能需要自行区分是客户端还是服务端环境并跳过环境检测,直接初始化
+    forceInit: {
+      type: Boolean,
+      default: false
+    },
+    // 手动设置 UEditor ID
+    editorId: {
+      type: String
+    }
+  },
+  computed: {
+    mixedConfig () {
+      return Object.assign({}, this.defaultConfig, this.config);
+    }
+  },
+  methods: {
+    // 添加自定义按钮(自定义按钮,自定义弹窗等操作从 2.2.0 版本开始不再考虑直接集成,这会使得组件和 UEditor 过度耦合,但为了兼容一些老版用户的写法,这个方法依然保留)
+    registerButton ({ name, icon, tip, handler, index, UE = window.UE }) {
+      UE.registerUI(name, (editor, name) => {
+        editor.registerCommand(name, {
+          execCommand: () => {
+            handler(editor, name);
+          }
+        });
+        const btn = new UE.ui.Button({
+          name,
+          title: tip,
+          cssRules: `background-image: url(${icon}) !important;background-size: cover;`,
+          onclick () {
+            editor.execCommand(name);
+          }
+        });
+        editor.addListener('selectionchange', () => {
+          const state = editor.queryCommandState(name);
+          if (state === -1) {
+            btn.setDisabled(true);
+            btn.setChecked(false);
+          } else {
+            btn.setDisabled(false);
+            btn.setChecked(state);
+          }
+        });
+        return btn;
+      }, index, this.id);
+    },
+    // 实例化编辑器
+    _initEditor () {
+      this.$refs.script.id = this.id = this.editorId || 'editor_' + Math.random().toString(16).slice(-6); // 这么做是为了支持 Vue SSR,因为如果把 id 属性放在 data 里会导致服务端和客户端分别计算该属性的值,而造成 id 不匹配无法初始化的 BUG
+      this.init();
+      this.$emit('before-init', this.id, this.mixedConfig);
+      this.$emit('beforeInit', this.id, this.mixedConfig); // 虽然这个驼峰的写法会导致使用 DOM 模版时出现监听事件自动转小写的 BUG,但如果经过编译的话并不会有这个问题,为了兼容历史版本,不做删除,参考 https://vuejs.org/v2/guide/components-custom-events.html#Event-Names
+      this.editor = window.UE.getEditor(this.id, this.mixedConfig);
+      this.editor.addListener('ready', () => {
+        if (this.status === 2) { // 使用 keep-alive 组件会出现这种情况
+          this.editor.setContent(this.value);
+        } else {
+          this.status = 2;
+          this.$emit('ready', this.editor);
+          if (this.initValue) {
+            this.editor.setContent(this.initValue);
+          }
+        }
+        if (this.mode === 'observer' && window.MutationObserver) {
+          this._observerChangeListener();
+        } else {
+          this._normalChangeListener();
+        }
+      });
+    },
+    // 检测依赖,确保 UEditor 资源文件已加载完毕
+    _checkDependencies () {
+      return new Promise((resolve, reject) => {
+        // 判断ueditor.config.js和ueditor.all.js是否均已加载(仅加载完ueditor.config.js时UE对象和UEDITOR_CONFIG对象存在,仅加载完ueditor.all.js时UEDITOR_CONFIG对象存在,但为空对象)
+        let scriptsLoaded = !!window.UE && !!window.UEDITOR_CONFIG && Object.keys(window.UEDITOR_CONFIG).length !== 0 && !!window.UE.getEditor;
+        if (scriptsLoaded) {
+          resolve();
+        } else if (window['$loadEnv']) { // 利用订阅发布,确保同时渲染多个组件时,不会重复创建script标签
+          window['$loadEnv'].on('scriptsLoaded', () => {
+            resolve();
+          });
+        } else {
+          window['$loadEnv'] = new LoadEvent();
+          // 如果在其他地方只引用ueditor.all.min.js,在加载ueditor.config.js之后仍需要重新加载ueditor.all.min.js,所以必须确保ueditor.config.js已加载
+          this._loadConfig().then(() => this._loadCore()).then(() => {
+            resolve();
+            window['$loadEnv'].emit('scriptsLoaded');
+          });
+        }
+      });
+    },
+    _loadConfig () {
+      return new Promise((resolve, reject) => {
+        if (window.UE && window.UEDITOR_CONFIG && Object.keys(window.UEDITOR_CONFIG).length !== 0) {
+          resolve();
+          return;
+        }
+        let configScript = document.createElement('script');
+        configScript.type = 'text/javascript';
+        configScript.src = this.mixedConfig.UEDITOR_HOME_URL + 'ueditor.config.js';
+        document.getElementsByTagName('head')[0].appendChild(configScript);
+        configScript.onload = function () {
+          if (window.UE && window.UEDITOR_CONFIG && Object.keys(window.UEDITOR_CONFIG).length !== 0) {
+            resolve();
+          } else {
+            console.error('加载ueditor.config.js失败,请检查您的配置地址UEDITOR_HOME_URL填写是否正确!\n', configScript.src);
+          }
+        };
+      });
+    },
+    _loadCore () {
+      return new Promise((resolve, reject) => {
+        if (window.UE && window.UE.getEditor) {
+          resolve();
+          return;
+        }
+        let coreScript = document.createElement('script');
+        coreScript.type = 'text/javascript';
+        coreScript.src = this.mixedConfig.UEDITOR_HOME_URL + 'ueditor.all.min.js';
+        document.getElementsByTagName('head')[0].appendChild(coreScript);
+        coreScript.onload = function () {
+          if (window.UE && window.UE.getEditor) {
+            resolve();
+          } else {
+            console.error('加载ueditor.all.min.js失败,请检查您的配置地址UEDITOR_HOME_URL填写是否正确!\n', coreScript.src);
+          }
+        };
+      });
+    },
+    // 设置内容
+    _setContent (value) {
+      value === this.editor.getContent() || this.editor.setContent(value);
+    },
+    contentChangeHandler () {
+      this.$emit('input', this.editor.getContent());
+    },
+    // 基于 UEditor 的 contentChange 事件
+    _normalChangeListener () {
+      this.editor.addListener('contentChange', this.contentChangeHandler);
+    },
+    // 基于 MutationObserver API
+    _observerChangeListener () {
+      const changeHandle = (mutationsList) => {
+        if (this.editor.document.getElementById('baidu_pastebin')) {
+          return;
+        }
+        this.$emit('input', this.editor.getContent());
+      };
+      // 函数防抖
+      this.observer = new MutationObserver(Debounce(changeHandle, this.observerDebounceTime));
+      this.observer.observe(this.editor.body, this.observerOptions);
+    }
+  },
+  deactivated () {
+    this.editor && this.editor.removeListener('contentChange', this.contentChangeHandler);
+    this.observer && this.observer.disconnect();
+  },
+  beforeDestroy () {
+    if (this.destroy && this.editor && this.editor.destroy) {
+      this.editor.destroy();
+    }
+    if (this.observer && this.observer.disconnect) {
+      this.observer.disconnect();
+    }
+  },
+  // v-model语法糖实现
+  watch: {
+    value: {
+      handler (value) {
+        // 修复值为空无法双向绑定的问题
+        if (value === null) {
+          value = '';
+        }
+        // 0: 尚未初始化 1: 开始初始化但尚未ready 2 初始化完成并已ready
+        switch (this.status) {
+          case 0:
+            this.status = 1;
+            this.initValue = value;
+            // 判断执行环境是服务端还是客户端,这里的 process.client 是 Nuxt 添加的环境变量
+            (this.forceInit || (typeof process !== 'undefined' && process.client) || typeof window !== 'undefined') && this._checkDependencies().then(() => {
+              this.$refs.script ? this._initEditor() : this.$nextTick(() => this._initEditor());
+            });
+            break;
+          case 1:
+            this.initValue = value;
+            break;
+          case 2:
+            this._setContent(value);
+            break;
+          default:
+            break;
+        }
+      },
+      immediate: true
+    }
+  }
+};
+</script>

File diff suppressed because it is too large
+ 1005 - 1
readme.md


+ 77 - 0
src/index.js

@@ -0,0 +1,77 @@
+//import EmBox from '../packages/box';
+import EmTableList from '../packages/list';
+import NtRow from '../packages/row';
+import NtCol from '../packages/col';
+import NtForm from '../packages/form';
+import NtInput from '../packages/input';
+import NtRemoveForm from '../packages/re-form';
+import NtMap from '../packages/map';
+import NtQuillEditor from '../packages/editor';
+import NtUEditor from '../packages/editor';
+import NtCharts from '../packages/charts';
+// import Toast from '../packages/toast';
+import NtTextInfo from '../packages/detail';
+import NtCard from '../packages/card';
+// import NtLayout from "../packages/layout/src/main";
+import NtInputRange from "../packages/input-range/src/main";
+import NtInputTable from "../packages/input-table/src/main";
+import NtInfiniteTransfer from "../packages/infinite-transfer/src/main";
+
+const components = [
+    EmTableList,
+    NtRow,
+    NtCol,
+    NtForm,
+    NtRemoveForm,
+    NtInput,
+    NtMap,
+    NtQuillEditor,
+    NtUEditor,
+    NtCharts,
+    NtTextInfo,
+    NtCard,
+    NtInputRange,
+    NtInputTable,
+    NtInfiniteTransfer,
+    // NtLayout
+];
+
+const install = function (Vue, opts = {}) {
+
+    // 判断是否安装
+    if (install.installed) return
+    install.installed = true
+
+    components.map(component => {
+        Vue.component(component.name, component);
+    });
+
+    // Vue.prototype.$toast = Toast;
+};
+
+/* istanbul ignore if 15974086384 2.2.1.1.3653*/
+if (typeof window !== 'undefined' && window.Vue) {
+    install(window.Vue);
+}
+
+export default {
+    version: '1.2.118',
+    install,
+    EmTableList,
+    NtRow,
+    NtCol,
+    NtForm,
+    NtInput,
+    NtRemoveForm,
+    NtMap,
+    NtQuillEditor,
+    NtUEditor,
+    NtCharts,
+    // Toast,
+    NtTextInfo,
+    NtCard,
+    NtInputRange,
+    NtInputTable,
+    NtInfiniteTransfer,
+    // NtLayout
+};

+ 85 - 0
src/tools/directives.js

@@ -0,0 +1,85 @@
+// 快捷键
+const shortcutKeys = {
+    fristKeyPress: false,
+    koe:{},
+    bind(el, binding, vnode) {
+        // const vnodeId = vnode.componentInstance._uid
+        shortcutKeys.koe[binding.value.key] = el
+        // if(!shortcutKeys.koe[binding.value.key]) {
+        // }
+        if(!shortcutKeys.__keypressFunc) {
+            shortcutKeys.__keypressFunc = function(event) {
+                // event.preventDefault()
+                // ctrl + i 入口快捷键
+                if(event.ctrlKey && event.keyCode === 9) {
+                    shortcutKeys.fristKeyPress = true
+                    return
+                }
+                // 入口开启
+                if(shortcutKeys.fristKeyPress) {
+                    shortcutKeys.fristKeyPress = false
+                    // 执行
+                    const matchEl = shortcutKeys.koe[event.key]
+                    if(matchEl) {
+                        matchEl.click()
+                    }
+                    
+                }
+            }
+            document.addEventListener('keypress', shortcutKeys.__keypressFunc)
+        }
+    },
+    unbind(el) {
+        // document.removeEventListener('keypress', shortcutKeys.__keypressFunc)
+    },
+}
+const loadMore = {
+    bind (el, binding) {
+        // 获取element-ui定义好的scroll盒子
+        const SELECTWRAP_DOM = el.querySelector('.el-select-dropdown .el-select-dropdown__wrap')
+        SELECTWRAP_DOM.addEventListener('scroll', function () {
+
+            const CONDITION = this.scrollHeight - this.scrollTop - 1 <= this.clientHeight
+            if (CONDITION) {
+                binding.value()
+            }
+        })
+    }
+};
+
+const clickoutside = {
+    // 初始化指令
+    bind(el, binding, vnode) {
+        function clickHandler(e) {
+            // 这里判断点击的元素是否是本身,是本身,则返回
+            if (el.contains(e.target)) {
+                return false;
+            }
+            // 判断指令中是否绑定了函数
+            if (binding.expression) {
+                // 如果绑定了函数 则调用那个函数,此处binding.value就是handleClose方法
+                binding.value(e);
+            }
+        }
+        // 给当前元素绑定个私有变量,方便在unbind中可以解除事件监听
+        el.__vueClickOutside__ = clickHandler;
+
+        if (document.addEventListener) {
+            document.addEventListener('click', clickHandler);
+        } else {
+            document.attachEvent('onclick', clickHandler);
+        }
+    },
+    update() {},
+    unbind(el, binding) {
+        // 解除事件监听
+        if (document.removeEventListener) {
+            document.removeEventListener('click', el.__vueClickOutside__);
+        } else {
+            document.detachEvent('onclick', el.__vueClickOutside__);
+        }
+        delete el.__vueClickOutside__;
+    }
+};
+
+export { loadMore, clickoutside, shortcutKeys }

+ 3 - 0
src/tools/eventBus.js

@@ -0,0 +1,3 @@
+import Vue from 'vue';
+
+export default new Vue;

+ 47 - 0
src/tools/request.js

@@ -0,0 +1,47 @@
+import axios from 'axios';
+import { Message } from 'element-ui';
+
+// 创建axios实例
+const service = axios.create()
+// request拦截器
+service.interceptors.request.use(config => { // 在发送请求之前做些什么
+  //参数对象转为字符串
+  return config
+}, error => { // 对请求错误做些什么
+  Promise.reject(error)
+})
+// respone拦截器
+service.interceptors.response.use( // 对响应数据做点什么
+  response => {
+    return response.data
+  },
+  err => { // 对响应错误做点什么
+    if (err && err.response) {
+      switch (err.response.status) {
+        case 400 : err.message = '请求错误(NT-400)'; break
+        case 401: err.message = '未授权,请重新登录(NT-401)'; break
+        case 403: err.message = '拒绝访问(NT-403)'; break
+        case 404: err.message = '请求出错(NT-404)'; break
+        case 408: err.message = '请求超时(NT-408)'; break
+        case 500: err.message = '服务器错误(NT-500)'; break
+        case 501: err.message = '服务未实现(NT-501)'; break
+        case 502: err.message = '网络错误(NT-502)'; break
+        case 503: err.message = '服务不可用(NT-503)'; break
+        case 504: err.message = '网络超时(NT-504)'; break
+        case 505: err.message = 'HTTP版本不受支持(NT-505)'; break
+        default: err.message = `连接出错(NT-${err.response.status})!`
+      }
+    } else {
+      err.message = '连接服务器失败-NT!'
+    }
+    Message({
+      message: err.message,
+      type: 'error',
+      duration: 3 * 1000
+    });
+
+    return Promise.reject(err)
+  }
+)
+
+export default service

+ 850 - 0
src/tools/utils.js

@@ -0,0 +1,850 @@
+import { reloadItemSelectList } from '@/components/thrid/em-element-ui/packages/list/src/tool-list';
+
+let Base64 = require('js-base64').Base64;
+
+export const LOCAL_STORAGE_CURR = 'local_storage_'
+/**
+ * Created by wangguiwang on 19/4/3.
+ */
+
+export function parseTime(time, cFormat) {
+  if (arguments.length === 0) {
+    return null
+  }
+  const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
+  let date
+  if (typeof time === 'object') {
+    date = time
+  } else {
+    if (('' + time).length === 10) time = parseInt(time) * 1000
+    date = new Date(time)
+  }
+  const formatObj = {
+    y: date.getFullYear(),
+    m: date.getMonth() + 1,
+    d: date.getDate(),
+    h: date.getHours(),
+    i: date.getMinutes(),
+    s: date.getSeconds(),
+    a: date.getDay()
+  }
+  const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
+    let value = formatObj[key]
+    if (key === 'a') return ['一', '二', '三', '四', '五', '六', '日'][value - 1]
+    if (result.length > 0 && value < 10) {
+      value = '0' + value
+    }
+    return value || 0
+  })
+  return time_str
+}
+
+export function formatTime(time, option) {
+  time = +time * 1000
+  const d = new Date(time)
+  const now = Date.now()
+
+  const diff = (now - d) / 1000
+
+  if (diff < 30) {
+    return '刚刚'
+  } else if (diff < 3600) { // less 1 hour
+    return Math.ceil(diff / 60) + '分钟前'
+  } else if (diff < 3600 * 24) {
+    return Math.ceil(diff / 3600) + '小时前'
+  } else if (diff < 3600 * 24 * 2) {
+    return '1天前'
+  }
+  if (option) {
+    return parseTime(time, option)
+  } else {
+    return d.getMonth() + 1 + '月' + d.getDate() + '日' + d.getHours() + '时' + d.getMinutes() + '分'
+  }
+}
+
+// 格式化时间
+export function formatDate(date, fmt) {
+  if (!date) return ''
+  var padLeftZero = function (str) {
+    return ('00' + str).substr(str.length)
+  }
+  if (isTypeof(date) === 'string') {
+    let tmpDate = date.replace(/-/g, '/')
+    tmpDate = tmpDate.replace(/T/g, ' ')
+
+    date = new Date(tmpDate)
+  }
+  if (/(y+)/.test(fmt)) {
+    fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length))
+  }
+  const o = {
+    'M+': date.getMonth() + 1,
+    'd+': date.getDate(),
+    'h+': date.getHours(),
+    'm+': date.getMinutes(),
+    's+': date.getSeconds()
+  }
+  for (var k in o) {
+    if (new RegExp(`(${k})`).test(fmt)) {
+      var str = o[k] + ''
+      fmt = fmt.replace(RegExp.$1, RegExp.$1.length === 1 ? str : padLeftZero(str))
+    }
+  }
+
+  return fmt
+}
+
+// URL地址解释
+export function getQueryObject(url) {
+  url = url == null ? window.location.href : url
+  const search = url.substring(url.lastIndexOf('?') + 1)
+  const obj = {}
+  const reg = /([^?&=]+)=([^?&=]*)/g
+  search.replace(reg, (rs, $1, $2) => {
+    const name = decodeURIComponent($1)
+    let val = decodeURIComponent($2)
+    val = String(val)
+    obj[name] = val
+    return rs
+  })
+  return obj
+}
+
+/**
+ *get getByteLen
+ * @param {Sting} val input value
+ * @returns {number} output value
+ */
+export function getByteLen(val) {
+  let len = 0
+  for (let i = 0; i < val.length; i++) {
+    if (val[i].match(/[^\x00-\xff]/ig) != null) {
+      len += 1
+    } else {
+      len += 0.5
+    }
+  }
+  return Math.floor(len)
+}
+
+export function cleanArray(actual) {
+  const newArray = []
+  for (let i = 0; i < actual.length; i++) {
+    if (actual[i]) {
+      newArray.push(actual[i])
+    }
+  }
+  return newArray
+}
+
+export function param(json) {
+  if (!json) return ''
+  return cleanArray(Object.keys(json).map(key => {
+    if (json[key] === undefined) return ''
+    return encodeURIComponent(key) + '=' +
+      encodeURIComponent(json[key])
+  })).join('&')
+}
+
+export function param2Obj(url) {
+  const search = url.split('?')[1]
+  if (!search) {
+    return {}
+  }
+  return JSON.parse('{"' + decodeURIComponent(search).replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g, '":"') + '"}')
+}
+
+export function html2Text(val) {
+  const div = document.createElement('div')
+  div.innerHTML = val
+  return div.textContent || div.innerText
+}
+
+// 时间格式化
+export function datepickerToFormat(model) {
+  if (model === 'year') {
+    return 'yyyy';
+  } else if (model === 'month') {
+    return 'yyyy-MM'
+  } else if (model === 'date' || model === 'dates') {
+    return 'yyyy-MM-dd'
+  } else if (model === 'week') {
+    return 'yyyy 第 WW 周'
+  } else {
+    return 'yyyy-MM-dd HH:mm:ss'
+  }
+}
+
+// 数组中是否存在每个值
+export function isArrayInnerValue(arr, value, key = '') {
+  let tmpList = [];
+
+  if (isTypeof(arr) === 'function') {
+    arr = eval( '(' + arr + ')' )();
+  }
+  tmpList = (arr ? arr.filter(item => {
+    if(isTypeof(value) === 'object') {
+      return item[key] == value[key];
+    }else{
+      return item == value;
+    }
+  }) : []);
+
+  return tmpList.length > 0 ? true : false;
+}
+
+// 按钮是否存在权限
+export function checkAuthButtonList(buttons, authList, btnKey = 'type') {
+  if (isTypeof(authList) === 'null') {
+    return buttons;
+  } else {
+    const result = []
+
+    if (isTypeof(authList) === 'function') {
+      authList = eval( '(' + authList + ')' )();
+    }
+    buttons.forEach(button => {
+      if (isArrayInnerValue(authList, (btnKey ? button[btnKey] : button))) {
+        result.push(button);
+      }
+    });
+    return result;
+  }
+}
+
+/*
+*对象深度合并
+*/
+export function objectMerge(target, source) {
+  if (typeof target !== 'object') {
+    target = {}
+  }
+  if (Array.isArray(source)) {
+    return source.slice()
+  }
+  Object.keys(source).forEach((property) => {
+    const sourceProperty = source[property]
+    if (typeof sourceProperty === 'object') {
+      target[property] = objectMerge(target[property], sourceProperty)
+    } else {
+      target[property] = sourceProperty
+    }
+  })
+
+  return target
+}
+/*
+*对象深度合并目标对象没有的值
+*/
+export function objectMergeTargeWithout(target, source) {
+  if (typeof target !== 'object') {
+    target = {}
+  }
+  if (Array.isArray(source)) {
+    return source.slice()
+  }
+  Object.keys(source).forEach((property) => {
+    const sourceProperty = source[property]
+    if (typeof sourceProperty === 'object') {
+      target[property] = objectMergeTargeWithout(target[property], sourceProperty)
+    } else {
+      if (!target.hasOwnProperty(property)) {
+        target[property] = sourceProperty
+      }
+    }
+  })
+
+  return target
+}
+
+/* 深度拷贝对象和数组 */
+export function utilsDeepCopy(target) {
+  if (isTypeof(target) === 'array') {
+    var result = [];
+    for (var i = 0; i < target.length; ++i) {
+      result[i] = utilsDeepCopy(target[i]);
+    }
+    return result;
+
+  } else if (isTypeof(target) === 'object') {
+    var result = {}
+    for (var i in target) {
+      result[i] = utilsDeepCopy(target[i]);
+    }
+    return result;
+  } else {
+    return target;
+  }
+}
+
+export function scrollTo(element, to, duration) {
+  if (duration <= 0) return
+  const difference = to - element.scrollTop
+  const perTick = difference / duration * 10
+  setTimeout(() => {
+    element.scrollTop = element.scrollTop + perTick
+    if (element.scrollTop === to) return
+    scrollTo(element, to, duration - 10)
+  }, 10)
+}
+
+export function toggleClass(element, className) {
+  if (!element || !className) {
+    return
+  }
+  let classString = element.className
+  const nameIndex = classString.indexOf(className)
+  if (nameIndex === -1) {
+    classString += '' + className
+  } else {
+    classString = classString.substr(0, nameIndex) + classString.substr(nameIndex + className.length)
+  }
+  element.className = classString
+}
+
+export const pickerOptions = [
+  {
+    text: '今天',
+    onClick(picker) {
+      const end = new Date()
+      const start = new Date(new Date().toDateString())
+      end.setTime(start.getTime())
+      picker.$emit('pick', [start, end])
+    }
+  }, {
+    text: '最近一周',
+    onClick(picker) {
+      const end = new Date(new Date().toDateString())
+      const start = new Date()
+      start.setTime(end.getTime() - 3600 * 1000 * 24 * 7)
+      picker.$emit('pick', [start, end])
+    }
+  }, {
+    text: '最近一个月',
+    onClick(picker) {
+      const end = new Date(new Date().toDateString())
+      const start = new Date()
+      start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
+      picker.$emit('pick', [start, end])
+    }
+  }, {
+    text: '最近三个月',
+    onClick(picker) {
+      const end = new Date(new Date().toDateString())
+      const start = new Date()
+      start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)
+      picker.$emit('pick', [start, end])
+    }
+  }]
+
+export function getTime(type) {
+  if (type === 'start') {
+    return new Date().getTime() - 3600 * 1000 * 24 * 90
+  } else {
+    return new Date(new Date().toDateString())
+  }
+}
+
+export function isJsonString(str) {
+  if (isTypeof(str) == 'string') {
+    try {
+      var obj = JSON.parse(str);
+      if (typeof obj == 'object' && obj) {
+        return true;
+      } else {
+        return false;
+      }
+    } catch (e) {
+      console.log('error:' + str + '!!!' + e);
+      return false;
+    }
+  }
+}
+
+export function isTypeof(option) {
+  var value = Object.prototype.toString.call(option);
+
+  if (value === '[object Undefined]') return 'undefined';
+  else if (value === '[object Symbol]') return 'symbol';
+  else if (value === '[object String]') return 'string';
+  else if (value === '[object Boolean]') return 'boolean';
+  else if (value === '[object Number]') return 'number';
+  else if (value === '[object Null]') return 'null';
+  else if (value === '[object Object]') return 'object';
+  else if (value === '[object Array]') return 'array';
+  else if (value === '[object Date]') return 'date';
+  else if (value === '[object RegExp]') return 'regexp';
+  else if (value === '[object Function]') return 'function';
+  else return 'no-type';
+}
+
+/**
+ * 搜索关键词输入的去抖延迟,毫秒
+ * @param {Function} func 要执行的函数
+ * @param {number} wait 间隔时间
+ * @param {boolean} immediate
+ * @return {*}
+ */
+export function debounce(func, wait = 300, immediate = false) {
+  let timeout, args, context, timestamp, result
+
+  const later = function () {
+    // 据上一次触发时间间隔
+    const last = +new Date() - timestamp
+
+    // 上次被包装函数被调用时间间隔last小于设定时间间隔wait
+    if (last < wait && last > 0) {
+      timeout = setTimeout(later, wait - last)
+    } else {
+      timeout = null
+      // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
+      if (!immediate) {
+        result = func.apply(context, args)
+        if (!timeout) context = args = null
+      }
+    }
+  }
+
+  return function (...args) {
+    context = this
+    timestamp = +new Date()
+    const callNow = immediate && !timeout
+    // 如果延时不存在,重新设定延时
+    if (!timeout) timeout = setTimeout(later, wait)
+    if (callNow) {
+      result = func.apply(context, args)
+      context = args = null
+    }
+
+    return result
+  }
+}
+
+export function uniqueArr(arr) {
+  return Array.from(new Set(arr))
+}
+
+// 校验URL地址是否是http://或https://开头的
+export function isHttpHeaderURL(url) {
+  if (url && isTypeof(url) === 'string' && (url.indexOf('http://') === 0 || url.indexOf('https://') === 0)) {
+    return true;
+  }
+
+  return false;
+}
+
+// 下载图片
+export function downLoadImage(url) {
+  var aLink = document.createElement('a'); // 创建一个a节点插入的document
+  aLink.style.display = 'none'
+  var event = new MouseEvent('click'); // 模拟鼠标click点击事件
+  aLink.target = '_blank';
+  aLink.download = url; // 设置a节点的download属性值
+  aLink.href = url; // 将图片的src赋值给a节点的href
+  aLink.dispatchEvent(event); // 触发鼠标点击事件
+}
+
+export function createUUID(number) {
+  let currtime = new Date().getTime(),
+    randomNumber = number ? parseInt(number * Math.random()) : parseInt(1000000 * Math.random());
+
+  return currtime + '-' + randomNumber;
+}
+
+// 合并请求参数
+export function mergeRequestParams(params, publicParams, ismerge = true) {
+  if (ismerge) {
+    return Object.assign({}, publicParams, params);
+  } else {
+    return params || {};
+  }
+}
+
+// 解释请求参数
+export function parseRequestParams(params, row, res) {
+  if (isTypeof(params) === 'object') {
+    for (let [k, v] of Object.entries(params)) {
+      if (isTypeof(v) === 'object') {
+        res[k] = {};
+        parseRequestParams(v, row, res[k]);
+      } else if (isTypeof(v) === 'array') {
+        res[k] = responseNodeParseList(row, v, 'object');
+      } else if (isTypeof(v) === 'string') {
+        res[k] = row[v];
+      }
+    }
+  }
+}
+
+//根据节点数据解释list值['data', 'data']
+export function responseNodeParseList(response, node, resType = 'array') {
+  if (isTypeof(node) === 'array') {
+    let res = response;
+    for (let i = 0; i < node.length; i++) {
+      res = res[node[i]] ? res[node[i]] : (res[node[i]] === 0 ? 0 : (resType == 'object' ? {} : (resType == 'number' ? 0 : [])));
+    }
+
+    return res;
+  } else {
+    return response[node];
+  }
+}
+
+// 操作事件的提示信息
+export function responseKeyToValue(response, _this, obj, next, key, value, message, logout) {
+  let keyField = key ? key : 'key',
+    valueField = value ? value : 'value',
+    messageField = message ? message : 'message',
+    keyValue = response[responseToResult(obj, keyField, next)],
+    logoutValues = logout ? obj.responseSuccess[logout] : obj.responseSuccess['logout'];
+
+  if (keyValue == responseToResult(obj, valueField, next)) {
+    if (isTypeof(next) == 'object') {
+      if (next.keyType == 'result') {
+        return true;
+      }
+    } else {
+      _this.$message({type: 'success', message: response[responseToResult(obj, messageField, next)]});
+    }
+  } else if (keyValue == logoutValues) {
+    let vals = logoutValues,
+      keyVal = ',' + keyValue + ',';
+
+    _this.$message({type: 'error', message: response[responseToResult(obj, messageField, next)]});
+
+    if (isTypeof(logoutValues) === 'array') {
+      vals = ',' + logoutValues.join(',') + ',';
+    } else if (isTypeof(logoutValues) === 'string') {
+      vals = ',' + logoutValues + ','
+    }
+
+    if (vals.indexOf(keyVal) != -1) {
+      _this.$store.dispatch('clear').then(() => {
+        location.reload() // 为了重新实例化vue-router对象 避免bug
+      })
+    }
+  } else {
+    _this.$message({type: 'error', message: response[responseToResult(obj, messageField, next)]});
+
+    return false;
+  }
+}
+
+//请求字段解释 并返回字段对应的值
+export function responseToResult(obj, key, next) {
+  let throwMsg = '字段:' + key + ',未找到',
+    funResponseSuccess = function (obj, key) {
+      if (obj && obj.hasOwnProperty(key)) {
+        return obj[key];
+      } else {
+        throw new Error(throwMsg);
+      }
+    };
+
+  if (next && isTypeof(next) === 'object') {
+    if (next.hasOwnProperty(key)) {
+      return next[key];
+    } else {
+      return funResponseSuccess(obj.responseSuccess, key);
+    }
+  }
+  if (next && isTypeof(next) === 'string') {
+    if (obj.queryURL && obj.queryURL[next] && obj.queryURL[next].success) {
+      if (obj.queryURL[next].success.hasOwnProperty(key)) {
+        return obj.queryURL[next].success[key];
+      } else {
+        return funResponseSuccess(obj.responseSuccess, key);
+      }
+    } else {
+      return funResponseSuccess(obj.responseSuccess, key);
+    }
+  } else {
+    return funResponseSuccess(obj.responseSuccess, key);
+  }
+}
+
+export function methodToQueryData(queryData) {
+  let path = [];
+
+  for (let [k, v] of Object.entries(queryData)) {
+    path.push(k + '=' + v);
+  }
+
+  return path.length > 0 ? '?' + path.join('&') : '';
+}
+
+// 请求参数解释
+export function axiosReqParams(item, queryData, axios, options = null) {
+  let reqURL = item._url ? item._url : item.url,
+    method = item._method ? item._method : (item.method ? item.method : 'post'),
+    params = {};
+
+  if (queryData && (method === 'get' || method === 'delete')) {
+    if (item.hasOwnProperty('methodType')) {
+      if (item.methodType === 'string') {
+        reqURL += methodToQueryData(queryData);
+        queryData = {};
+      }
+    } else {
+      reqURL += methodToQueryData(queryData);
+      queryData = {};
+    }
+  }
+
+  params.url = reqURL;
+
+  if (item.hasOwnProperty('axiosDataType') && item.axiosDataType === 'params') {
+    params.params = queryData;
+  } else {
+    params.data = queryData;
+  }
+
+  params.method = method;
+
+  if (axios) {
+    for (let [k, v] of Object.entries(axios)) {
+      if (v !== '') {
+        params[k] = isTypeof(v) === 'function' ? v() : v;
+      }
+    }
+  }
+  // 增加axios请求列表
+  if (options && isTypeof(options.httpRequestList) === 'array') {
+    const axios = require('axios')
+    const CANCEL_TOKEN = axios.CancelToken
+
+    params.cancelToken = new CANCEL_TOKEN((c) => {
+      // 这里的ajax标识我是用请求地址&请求方式拼接的字符串,当然你可以选择其他的一些方式
+      options.httpRequestList.push({ key: (params.url + '&' + params.method + '&' + (isTypeof(params.data) === 'string' ? params.data : JSON.stringify(params.data))), func: c })
+    })
+  }
+
+  return params;
+}
+
+export function utilsTrim(str) {
+  let result = str, reg = /(^\s+)|(\s+$)/g;
+
+  if (str && isTypeof(str) === 'string') {
+    str = isTypeof(str) === 'string' ? str : str.toString();
+
+    result = str.replace(reg, "");
+  }
+  return result;
+}
+
+// 数组根据某个字段排序
+export function arrayToFieldSort(sourceColumn, inputType = 'show', key = 'serial', order = 'asc') {
+  const arrayList = utilsDeepCopy(sourceColumn);
+
+  if(order == 'asc'){
+    arrayList.sort( (arr1, arr2)=> {
+      if (!arr1[inputType] || !arr2[inputType] || !arr1[inputType][key] || !arr2[inputType][key]) {
+        return 0
+      } else {
+        return arr1[inputType][key] - arr2[inputType][key];
+      }
+    } );
+  }else if(order == 'desc'){
+    arrayList.sort( (arr1, arr2)=> {
+      if (!arr1[inputType] || !arr2[inputType] || !arr1[inputType][key] || !arr2[inputType][key]) {
+        return 0
+      } else {
+        return arr2[inputType][key] - arr1[inputType][key];
+      }
+    } );
+  }
+
+  return arrayList;
+}
+
+// 多个字段值同时确认fields里字段是否去掉
+function multipleFieldsStatus(rowData, itemCascader) {
+  let status = true;
+
+  if (itemCascader.hasOwnProperty('multiple') && isTypeof(itemCascader.multiple) === 'object') {
+    for (let [k, v] of Object.entries(itemCascader.multiple)) {
+      if (rowData[k] !== v) {
+        status = false;
+      }
+    }
+  }
+
+  return status;
+}
+
+function cascaderCurrFieldName(item, inputType) {
+  if (item.hasOwnProperty('isfield') && item.isfield) {
+    const tmpItemInput = item.hasOwnProperty(inputType) || {};
+
+    if (tmpItemInput.hasOwnProperty('field')) {
+      return tmpItemInput.field;
+    } else {
+      return item.field;
+    }
+  } else {
+    return item.field;
+  }
+}
+
+// 根据关联字段判断是否出现,或设置column列类别
+function cascaderItemList(item, rowData, inputType, cascaderType = 'cascaderList') {
+  const tmpItemInput = item[inputType];
+  const resultFields = [];
+  const currField = cascaderCurrFieldName(item, inputType);
+
+  if (tmpItemInput && tmpItemInput.hasOwnProperty(cascaderType)) {
+    if (rowData.hasOwnProperty(currField)) {
+      for (let i = 0; i < tmpItemInput[cascaderType].length; i++) {
+        if (multipleFieldsStatus(rowData, tmpItemInput[cascaderType][i]) && rowData[currField] == tmpItemInput[cascaderType][i].value) {
+          resultFields.push(...tmpItemInput[cascaderType][i].fields);
+          break;
+        }
+      }
+    } else if (tmpItemInput.hasOwnProperty('value')) {
+      for (let i = 0; i < tmpItemInput[cascaderType].length; i++) {
+        if (multipleFieldsStatus(rowData, tmpItemInput[cascaderType][i]) && tmpItemInput.value == tmpItemInput[cascaderType][i].value) {
+          resultFields.push(...tmpItemInput[cascaderType][i].fields);
+          break;
+        }
+      }
+    }
+  }
+
+  return resultFields || [];
+}
+
+// 选择select隐藏某个字段
+export function toolResetPageColumn(_this, sourceColumn, sortAfterColumn, rowData, inputType = 'show', isClearField = false) {
+  const tmpHideFields = [];
+  const tmpHideRuleFields = [];
+  const tmpChangeTypeFields = [];
+  let resultColumn = sortAfterColumn;
+
+  sourceColumn.forEach(item => {
+    if (!item[inputType]) return false;
+    // 隐藏字段关联
+    tmpHideFields.push(...cascaderItemList(item, rowData, inputType, 'cascaderList'));
+    // 必填项配置
+    tmpHideRuleFields.push(...cascaderItemList(item, rowData, inputType, 'cascaderRuleList'));
+    // 联动字段类型变化
+    tmpChangeTypeFields.push(...cascaderItemList(item, rowData, inputType, 'cascaderTypeList'));
+
+    // 联动加载其他组件
+    if (item[inputType] && item[inputType].hasOwnProperty('cascaderLoader')) {
+      const currField = cascaderCurrFieldName(item, inputType);
+
+      toolsCascaderLoader(_this, item[inputType], rowData[currField], _this.select_list, rowData, isClearField);
+    }
+  })
+  if (tmpHideFields.length > 0 || tmpHideRuleFields.length > 0 || tmpChangeTypeFields.length > 0) {
+    // 根据隐藏、校验规则、关联字段类型改变;处理排序好的列
+    resultColumn = sortAfterColumn.filter((item, index) => {
+      const currField = cascaderCurrFieldName(item, inputType);
+
+      if (tmpHideFields.length > 0 && isArrayInnerValue(tmpHideFields, currField)) {
+        return false
+      }
+      if (tmpHideRuleFields.length > 0 && isArrayInnerValue(tmpHideRuleFields, currField)) {
+        delete sortAfterColumn[index].rules
+      }
+
+      // 循环判断是否有字段类型需要切换
+      for (let i = 0; i < tmpChangeTypeFields.length; i++) {
+        if (tmpChangeTypeFields[i].hasOwnProperty(currField)) {
+          item[inputType].type = tmpChangeTypeFields[i][currField];
+          if (isClearField) {
+            // 清空改变类型字段的值
+            rowData[currField] = ''
+            // 如果设置默认值,回滚到默认值
+            if (tmpChangeTypeFields[i].hasOwnProperty('value')) {
+              rowData[currField] = tmpChangeTypeFields[i].value;
+            }
+          }
+          break;
+        }
+      }
+
+      return true
+    })
+  }
+
+  // 隐藏字段是否传值
+  sourceColumn.forEach(item => {
+    const currField = cascaderCurrFieldName(item, inputType);
+
+    if (tmpHideFields.length > 0 && isArrayInnerValue(tmpHideFields, currField)) {
+      item.isAutoPush = false
+    } else {
+      item.isAutoPush = true
+    }
+  })
+
+  return resultColumn;
+}
+
+// 递归配置切换联动select参数
+function toolsCascaderParmas(rowData, params, value, out = {}) {
+  for (let [k, v] of Object.entries(params)) {
+    // 为空直接value赋值给
+    if (v === '') {
+      out[k] = value;
+    } else {
+      // 对象重复递归
+      if (isTypeof(v) === 'object') {
+        out[k] = {}
+        toolsCascaderParmas(rowData, v, value, out[k]);
+      } else if (isTypeof(v) === 'string') {
+        out[k] = rowData[v];
+      }
+    }
+  }
+}
+
+export function toolsCascaderLoader(_this, inputItem, value, selectList, rowData, isClearField) {
+  inputItem.cascaderLoader.forEach(node => {
+    if (selectList.hasOwnProperty(node.selectField)
+        && isTypeof(selectList[node.selectField]) === 'array'
+        && isTypeof(selectList[node.selectField + 'Param']) === 'object') {
+
+      // 清空load组件内容
+      if (isClearField && node.hasOwnProperty('cancelField')) {
+        if (isTypeof(node.cancelField) === 'string') {
+          rowData[node.cancelField] = '';
+        } else if (isTypeof(node.cancelField) === 'array') {
+          node.cancelField.forEach(cancelItem => {
+            rowData[cancelItem] = '';
+          })
+        }
+      }
+      // 递归配置select参数
+      const tmpParams = {}
+      const tmpValue = isTypeof(value) === 'undefined' || value === '' ? (inputItem.hasOwnProperty('value') ? inputItem.value : '') : value;
+      // 关联的本select必须有值才加载,关联字段
+      if (tmpValue) {
+        toolsCascaderParmas(rowData, node.params, tmpValue, tmpParams);
+
+        if (node.cover && selectList[node.selectField + 'Param'] && isTypeof(selectList[node.selectField + 'Param']) === 'object') {
+          if (selectList[node.selectField + 'Param'].hasOwnProperty('params')) {
+            if (node.cover === 1) {
+              selectList[node.selectField + 'Param'].params = tmpParams;
+            } else {
+              selectList[node.selectField + 'Param'].params = objectMerge(selectList[node.selectField + 'Param'].params, tmpParams);
+            }
+          } else {
+            selectList[node.selectField + 'Param'].params = tmpParams;
+          }
+        }
+        // 调用函数动态获取select值
+        reloadItemSelectList(_this, 'remote', node.selectField);
+      }
+    }
+  })
+}
+export function nullView(data) {
+  return !data && data !== 0 ? '--' : data
+}
+
+
+
+

+ 383 - 0
src/tools/validate.js

@@ -0,0 +1,383 @@
+/**
+ * Created by 王贵旺 on 19/04/08.
+ */
+
+import { isJsonString, isTypeof } from '@/components/thrid/em-element-ui/src/tools/utils'
+
+// 验证用户名称
+function validcodeName(rule, value, callback) {
+    var reg = /^[\u4e00-\u9fa5A-Za-z0-9\._\s+【】\(\)()-]*$/
+
+    if (!reg.test(value)) {
+        callback(new Error(rule.message))
+    } else {
+        callback()
+    }
+}
+
+// 验证用户名称(字符,数字,下划线)
+function validUserName(rule, value, callback) {
+    var reg = /^[A-Za-z0-9_]*$/
+
+    if (!reg.test(value)) {
+        callback(new Error(rule.message))
+    } else {
+        callback()
+    }
+}
+
+// 验证用户名称(字符,数字,下划线)
+function isNumber(rule, value, callback) {
+    var reg = /^[0-9_]*$/
+
+    if (!reg.test(value)) {
+        callback(new Error(rule.message))
+    } else {
+        callback()
+    }
+}
+
+// 数字校验
+function isValidateNumber(rule, value, callback) {
+    var reg = /^[0-9]*$/
+
+    if (!reg.test(value)) {
+        callback(new Error(rule.message))
+    } else {
+        callback()
+    }
+}
+
+// 支行行号校验
+function isValidateUnionBank(rule, value, callback) {
+    var reg = /^[0-9]{0,12}$/
+
+    if (!reg.test(value)) {
+        callback(new Error(rule.message))
+    } else {
+        callback()
+    }
+}
+
+// 验证金额
+function validateMoneyNumber(rule, value, callback) {
+    var reg = /^([1-9][\d]{0,10}|0)(\.[\d]{1,2})?$/
+
+    if (value !== '' && !reg.test(value)) {
+        callback(new Error(rule.message))
+    } else {
+        callback()
+    }
+}
+
+// 验证手机号码
+function isValidateMobile(rule, value, callback) {
+    var reg = /^((\+86)|(86))?[1][0-9]{10}$/
+
+    if (value !== '' && !reg.test(value)) {
+        callback(new Error(rule.message))
+    } else {
+        callback()
+    }
+}
+
+// 验证电话号码号码
+function isValidatePhones(rule, value, callback) {
+    var reg = /^((\+86)|(86))((([0+]d{2,3}-)?(0d{2,3})-)(d{7,8})(-(d{3,}))?)|([1][0-9]{10})?$/
+
+    // console.log(rule, value, callback);
+    if (value !== '' && !reg.test(value)) {
+        callback(new Error(rule.message))
+    } else {
+        callback()
+    }
+}
+
+// 验证URL
+function isUrl(rule, value, callback) {
+    var reg = /^(ht|f)tp(s?)\:\/\/[0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*(:(0-9)*)*(\/?)([a-zA-Z0-9\-\.\?\,\'\/\\\+&amp;%\$#_]*)?/
+
+    if (value !== '' && !reg.test(value)) {
+        callback(new Error(rule.message))
+    } else {
+        callback()
+    }
+}
+
+// 验证JSON
+function isJson(rule, value, callback) {
+    if (ruleOption(rule)) {
+        if (!isJsonString(value)) {
+            callback(new Error(rule.message))
+        } else {
+            callback()
+        }
+    } else {
+        callback()
+    }
+}
+
+// 验证Email
+function isEmail(rule, value, callback) {
+    var reg = /^[\w!#$%&'*+/=?^_`{|}~-]+(?:\.[\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\w](?:[\w-]*[\w])?\.)+[\w](?:[\w-]*[\w])?/
+
+    if (value !== '' && !reg.test(value)) {
+        callback(new Error(rule.message))
+    } else {
+        callback()
+    }
+}
+
+// 验证身份证号码
+function isIdCard(rule, value, callback) {
+    var reg = /^(\d{6})(\d{4})(\d{2})(\d{2})(\d{3})([0-9]|X)$/
+
+    if (value !== '' && !reg.test(value)) {
+        callback(new Error(rule.message))
+    } else {
+        callback()
+    }
+}
+
+// 判断一个数据是否为真
+function compareToNumber(number, option, type) {
+    // 判断是否开区间,1:左边闭区间,右边开区间;2: 左边开区间,右边闭区间;3: 左右都为闭区间
+    const space = option.space || 3
+    if (type === 'min') {
+        if ((space & 1) === 1 && number >= option[type]) {
+            return true
+        }
+        if ((space & 1) !== 1 && number > option[type]) {
+            return true
+        }
+
+        return false
+    } else if (type === 'max') {
+        if ((space & 2) === 2 && number <= option[type]) {
+            return true
+        }
+        if ((space & 2) !== 2 && number < option[type]) {
+            return true
+        }
+
+        return false
+    }
+}
+
+// 数字范围校验
+function validateNumberMinMax(rule, number, callback, option) {
+    if (isTypeof(option.min) === 'number' && isTypeof(option.max) === 'number') {
+        if (compareToNumber(number, option, 'min') && compareToNumber(number, option, 'max')) {
+            callback()
+        } else {
+            callback(new Error(rule.message))
+        }
+    } else if (isTypeof(option.min) === 'number' && isTypeof(option.max) !== 'number') {
+        if (compareToNumber(number, option, 'min')) {
+            callback()
+        } else {
+            callback(new Error(rule.message))
+        }
+    } else if (isTypeof(option.min) !== 'number' && isTypeof(option.max) === 'number') {
+        if (compareToNumber(number, option, 'max')) {
+            callback()
+        } else {
+            callback(new Error(rule.message))
+        }
+    } else {
+        callback()
+    }
+}
+
+// 字符长度校验
+function validateInputValueLength(rule, value, callback) {
+    if (value === '') {
+        callback()
+    } else {
+        if (rule.option) {
+            const option = rule.option
+            const val = value + ''
+            const valueLen = val.length
+
+            if (valueLen > 0) {
+                validateNumberMinMax(rule, valueLen, callback, option)
+            } else {
+                callback()
+            }
+        } else {
+            callback(new Error('校验数据结构问题'))
+        }
+    }
+}
+
+// 数字大小校验
+function validateInputValueNumber(rule, value, callback) {
+    if (value === '') {
+        callback()
+    } else {
+        if (rule.option) {
+            const reg = /^([1-9][\d]{0,10}|0)(\.[\d]{1,2})?$/
+            const option = rule.option
+
+            if (reg.test(value)) {
+                if (value || value === 0) {
+                    validateNumberMinMax(rule, value, callback, option)
+                } else {
+                    callback()
+                }
+            } else {
+                callback(new Error('请输入数字'))
+            }
+        } else {
+            callback(new Error('校验数据结构问题'))
+        }
+    }
+}
+
+// 确认密码
+function validateConfirmPassword(rule, value, callback) {
+    if (rule.option && rule.option.isObj) {
+        const option = rule.option
+        const form = rule.obj.$refs.form || {}
+        const model = form.model || {}
+        const firstValue = model[rule.field] || ''
+        const secondValue = model[option.refield] || ''
+
+        if (firstValue !== secondValue) {
+            callback(new Error(rule.message))
+        } else {
+            callback()
+        }
+    } else {
+        callback(new Error('校验数据结构问题'))
+    }
+}
+
+// 多字段必须有一个字段填写
+function validateMultipleFieldLeast(rule, value, callback) {
+    if (rule.option && rule.option.isObj) {
+        const option = rule.option
+        const form = rule.obj.$refs.form || {}
+        const model = form.model || {}
+        const multipleField = option.multiple || []
+
+        if (Array.isArray(multipleField)) {
+            let validateFlag = false
+            multipleField.forEach(item => {
+                // 对应的字段
+                if (model.hasOwnProperty(item.field)) {
+                    // 值为空时,直接退出
+                    if (model[item.field] !== '') {
+                        // 判断是否有null字段
+                        if (item.hasOwnProperty('nullValue')) {
+                            const nullValue = item.nullValue
+                            if (Array.isArray(nullValue)) {
+                                if (!nullValue.includes(model[item.field])) {
+                                    validateFlag = true
+                                }
+                            } else {
+                                if (model[item.field] !== nullValue) {
+                                    validateFlag = true
+                                }
+                            }
+                        }
+                    }
+                }
+            })
+            if (validateFlag) {
+                callback()
+            } else {
+                callback(new Error(rule.message))
+            }
+        } else {
+            callback(new Error('字段类型为数组'))
+        }
+    } else {
+        callback(new Error('校验数据结构问题'))
+    }
+}
+
+function ruleOption(rule) {
+    if (rule.option) {
+        const form = rule.obj.$refs.form
+        const model = form.model
+        const option = rule.option
+
+        if (isTypeof(option.value) === 'string') {
+            return option.isNot ? model[option.key] == option.value : model[option.key] != option.value
+        } else if (isTypeof(option.value) === 'array') {
+            let flag = true
+
+            for (let i = 0; i < option.value.length; i++) {
+                if (option.value[i] != '*') {
+                    if (option.isNot) {
+                        if (model[option.key] && model[option.key][i]) {
+                            if (option.value[i] != model[option.key][i]) {
+                                flag = false
+                                break
+                            }
+                        } else {
+                            flag = false
+                            break
+                        }
+                    } else {
+                        if (model[option.key] && model[option.key][i] && option.value[i] == model[option.key][i]) {
+                            flag = false
+                            break
+                        }
+                    }
+                }
+            }
+
+            return flag
+        }
+    }
+
+    return true
+}
+
+function validatorFun(_this, obj) {
+    var row = {}
+
+    row.validator = typeof (obj.validator) === 'function' ? obj.validator : eval('(' + obj.validator + ')')
+    row.message = obj.message
+    row.trigger = obj.trigger
+    if (obj.option) {
+        row.option = obj.option
+
+        if (obj.option.isObj) {
+            row.obj = _this
+        }
+    }
+
+    return row
+}
+
+export function addValidatorObj(obj, currPageColumn) {
+    const res = {}
+    const inputType = obj.inputType || 'show'
+
+    currPageColumn.forEach(item => {
+        const rules = []
+        const nodeRules = []
+        const nodeItem = item[inputType] || {}
+
+        if (nodeItem.rules && Array.isArray(nodeItem.rules)) {
+            rules.push(...new Set(nodeItem.rules))
+        }
+        if (item.rules && Array.isArray(item.rules) && (nodeItem.hasOwnProperty('rule') ? nodeItem.rule : true)) {
+            rules.push(...new Set(item.rules))
+        }
+
+        rules.forEach(it => {
+            if (it.validator) {
+                nodeRules.push(validatorFun(obj, it))
+            } else {
+                nodeRules.push(it)
+            }
+        })
+        res[item.field] = nodeRules
+    })
+
+    return res
+}

+ 49 - 0
style/index.scss

@@ -0,0 +1,49 @@
+// DxTableList 
+.container-table-list {
+
+    /* #region 首列复选框 */
+    .el-table th.el-table__cell>.cell {
+        display: flex;
+        align-items: center;
+        justify-content: center;
+    }
+
+    .el-table th>.cell .el-checkbox {
+        margin-left: 0;
+    }
+
+    .el-table-column--selection .cell {
+        padding-right: 10px;
+    }
+
+    /* #endregion */
+
+    // switch
+    .el-switch .el-switch__label {
+        position: absolute;
+        z-index: 1;
+        font-weight: bold;
+        margin: 0;
+        display: none;
+
+        &.is-active {
+            color: #fff;
+        }
+
+        &.el-switch__label--left {
+            right: 6px;
+        }
+
+        &.el-switch__label--right {
+            left: 6px;
+        }
+
+        &.is-active {
+            display: block;
+        }
+
+        span {
+            font-size: 12px;
+        }
+    }
+}