Przeglądaj źródła

chmod(lib): update avue lib

smallwei 7 lat temu
rodzic
commit
c0221ff6b1
99 zmienionych plików z 4971 dodań i 8658 usunięć
  1. 1 1
      package.json
  2. 7 0
      packages/crud-cascader/index.js
  3. 7 0
      packages/crud-checkbox/index.js
  4. 7 0
      packages/crud-date/index.js
  5. 7 0
      packages/crud-input-number/index.js
  6. 7 0
      packages/crud-input/index.js
  7. 7 0
      packages/crud-radio/index.js
  8. 7 0
      packages/crud-rate/index.js
  9. 7 0
      packages/crud-select/index.js
  10. 7 0
      packages/crud-silder/index.js
  11. 7 0
      packages/crud-switch/index.js
  12. 7 0
      packages/crud-time/index.js
  13. 7 0
      packages/crud-upload/index.js
  14. 7 0
      packages/crud/index.js
  15. 80 0
      packages/crud/src/config.js
  16. 68 0
      packages/crud/src/crud-cascader.vue
  17. 46 0
      packages/crud/src/crud-checkbox.vue
  18. 85 0
      packages/crud/src/crud-components.vue
  19. 71 0
      packages/crud/src/crud-date.vue
  20. 64 0
      packages/crud/src/crud-input-number.vue
  21. 270 0
      packages/crud/src/crud-input.vue
  22. 41 0
      packages/crud/src/crud-radio.vue
  23. 64 0
      packages/crud/src/crud-rate.vue
  24. 57 0
      packages/crud/src/crud-select.vue
  25. 68 0
      packages/crud/src/crud-silder.vue
  26. 49 0
      packages/crud/src/crud-switch.vue
  27. 67 0
      packages/crud/src/crud-time.vue
  28. 215 0
      packages/crud/src/crud-upload.vue
  29. 706 0
      packages/crud/src/main.vue
  30. 7 0
      packages/data-box/index.js
  31. 49 0
      packages/data-box/src/data-box.vue
  32. 7 0
      packages/data-card/index.js
  33. 60 0
      packages/data-card/src/data-card.vue
  34. 7 0
      packages/data-display/index.js
  35. 50 0
      packages/data-display/src/data-display.vue
  36. 7 0
      packages/data-icons/index.js
  37. 49 0
      packages/data-icons/src/data-icons.vue
  38. 7 0
      packages/data-tabs/index.js
  39. 53 0
      packages/data-tabs/src/data-tabs.vue
  40. 7 0
      packages/date-select/index.js
  41. 91 0
      packages/date-select/src/main.vue
  42. 7 0
      packages/form-detail/index.js
  43. 111 0
      packages/form-detail/src/main.vue
  44. 7 0
      packages/form-steps/index.js
  45. 151 0
      packages/form-steps/src/main.vue
  46. 7 0
      packages/form-tabs/index.js
  47. 119 0
      packages/form-tabs/src/main.vue
  48. 7 0
      packages/form/index.js
  49. 361 0
      packages/form/src/main.vue
  50. 66 0
      packages/index.js
  51. 45 0
      packages/mixins/bem.js
  52. 39 0
      packages/mixins/column.js
  53. 101 0
      packages/mixins/crud-compoents.js
  54. 164 0
      packages/mixins/crud.js
  55. 7 0
      packages/table-tree/index.js
  56. 25 0
      packages/table-tree/src/eval.js
  57. 130 0
      packages/table-tree/src/main.vue
  58. 25 0
      packages/theme-chalk/gulpfile.js
  59. 1 0
      packages/theme-chalk/lib/common.css
  60. 1 0
      packages/theme-chalk/lib/crud-upload.css
  61. 1 0
      packages/theme-chalk/lib/crud.css
  62. 1 0
      packages/theme-chalk/lib/data-box.css
  63. 1 0
      packages/theme-chalk/lib/data-card.css
  64. 1 0
      packages/theme-chalk/lib/data-display.css
  65. 1 0
      packages/theme-chalk/lib/data-icons.css
  66. 1 0
      packages/theme-chalk/lib/data-tabs.css
  67. 1 0
      packages/theme-chalk/lib/date-select.css
  68. 1 0
      packages/theme-chalk/lib/element-ui.css
  69. 1 0
      packages/theme-chalk/lib/form-detail.css
  70. 1 0
      packages/theme-chalk/lib/form-steps.css
  71. 1 0
      packages/theme-chalk/lib/form.css
  72. 1 0
      packages/theme-chalk/lib/index.css
  73. 1 0
      packages/theme-chalk/lib/select-date.css
  74. 1 0
      packages/theme-chalk/lib/table-tree.css
  75. 24 0
      packages/theme-chalk/package.json
  76. 32 0
      packages/theme-chalk/src/common.scss
  77. 27 0
      packages/theme-chalk/src/crud-upload.scss
  78. 98 0
      packages/theme-chalk/src/crud.scss
  79. 46 0
      packages/theme-chalk/src/data-box.scss
  80. 44 0
      packages/theme-chalk/src/data-card.scss
  81. 25 0
      packages/theme-chalk/src/data-display.scss
  82. 40 0
      packages/theme-chalk/src/data-icons.scss
  83. 64 0
      packages/theme-chalk/src/data-tabs.scss
  84. 17 0
      packages/theme-chalk/src/date-select.scss
  85. 16 0
      packages/theme-chalk/src/element-ui.scss
  86. 46 0
      packages/theme-chalk/src/form-detail.scss
  87. 15 0
      packages/theme-chalk/src/form-steps.scss
  88. 44 0
      packages/theme-chalk/src/form.scss
  89. 14 0
      packages/theme-chalk/src/index.scss
  90. 46 0
      packages/theme-chalk/src/table-tree.scss
  91. 16 0
      packages/utils/create-basic.js
  92. 9 0
      packages/utils/create.js
  93. 25 0
      packages/utils/dateUtil.js
  94. 247 0
      packages/utils/util.js
  95. 20 0
      packages/utils/validate.js
  96. 86 0
      src/const/crud/user.js
  97. 4 4
      src/main.js
  98. 141 339
      src/views/admin/user/index.vue
  99. 0 8314
      yarn.lock

+ 1 - 1
package.json

@@ -10,7 +10,7 @@
         "test:e2e": "vue-cli-service test:e2e"
     },
     "dependencies": {
-        "@smallwei/avue": "^1.1.18",
+        "@smallwei/avue": "^1.1.19",
         "avue-plugin-transfer": "^0.0.2",
         "avue-plugin-ueditor": "^0.0.1",
         "axios": "^0.18.0",

+ 7 - 0
packages/crud-cascader/index.js

@@ -0,0 +1,7 @@
+import CrudCascader from '../crud/src/crud-cascader';
+
+CrudCascader.install = function(Vue) {
+  Vue.component(CrudCascader.name, CrudCascader);
+};
+
+export default CrudCascader;

+ 7 - 0
packages/crud-checkbox/index.js

@@ -0,0 +1,7 @@
+import CrudCheckBox from '../crud/src/crud-checkbox';
+
+CrudCheckBox.install = function(Vue) {
+  Vue.component(CrudCheckBox.name, CrudCheckBox);
+};
+
+export default CrudCheckBox;

+ 7 - 0
packages/crud-date/index.js

@@ -0,0 +1,7 @@
+import CrudData from '../crud/src/crud-date';
+
+CrudData.install = function(Vue) {
+  Vue.component(CrudData.name, CrudData);
+};
+
+export default CrudData;

+ 7 - 0
packages/crud-input-number/index.js

@@ -0,0 +1,7 @@
+import CrudInputNumber from '../crud/src/crud-input-number';
+
+CrudInputNumber.install = function(Vue) {
+  Vue.component(CrudInputNumber.name, CrudInputNumber);
+};
+
+export default CrudInputNumber;

+ 7 - 0
packages/crud-input/index.js

@@ -0,0 +1,7 @@
+import CrudInput from '../crud/src/crud-input';
+
+CrudInput.install = function(Vue) {
+  Vue.component(CrudInput.name, CrudInput);
+};
+
+export default CrudInput;

+ 7 - 0
packages/crud-radio/index.js

@@ -0,0 +1,7 @@
+import CrudRadio from '../crud/src/crud-radio';
+
+CrudRadio.install = function(Vue) {
+  Vue.component(CrudRadio.name, CrudRadio);
+};
+
+export default CrudRadio;

+ 7 - 0
packages/crud-rate/index.js

@@ -0,0 +1,7 @@
+import CrudRate from '../crud/src/crud-rate';
+
+CrudRate.install = function(Vue) {
+  Vue.component(CrudRate.name, CrudRate);
+};
+
+export default CrudRate;

+ 7 - 0
packages/crud-select/index.js

@@ -0,0 +1,7 @@
+import CrudSelect from '../crud/src/crud-select';
+
+CrudSelect.install = function(Vue) {
+  Vue.component(CrudSelect.name, CrudSelect);
+};
+
+export default CrudSelect;

+ 7 - 0
packages/crud-silder/index.js

@@ -0,0 +1,7 @@
+import CrudSilder from '../crud/src/crud-silder';
+
+CrudSilder.install = function(Vue) {
+  Vue.component(CrudSilder.name, CrudSilder);
+};
+
+export default CrudSilder;

+ 7 - 0
packages/crud-switch/index.js

@@ -0,0 +1,7 @@
+import CrudSwitch from '../crud/src/crud-switch';
+
+CrudSwitch.install = function(Vue) {
+  Vue.component(CrudSwitch.name, CrudSwitch);
+};
+
+export default CrudSwitch;

+ 7 - 0
packages/crud-time/index.js

@@ -0,0 +1,7 @@
+import CrudTime from '../crud/src/crud-time';
+
+CrudTime.install = function(Vue) {
+  Vue.component(CrudTime.name, CrudTime);
+};
+
+export default CrudTime;

+ 7 - 0
packages/crud-upload/index.js

@@ -0,0 +1,7 @@
+import CrudUpload from '../crud/src/crud-upload.vue';
+
+CrudUpload.install = function(Vue) {
+  Vue.component(CrudUpload.name, CrudUpload);
+};
+
+export default CrudUpload;

+ 7 - 0
packages/crud/index.js

@@ -0,0 +1,7 @@
+import Crud from './src/main';
+
+Crud.install = function(Vue) {
+  Vue.component(Crud.name, Crud);
+};
+
+export default Crud;

+ 80 - 0
packages/crud/src/config.js

@@ -0,0 +1,80 @@
+//crud配置文件
+export default {
+    //标题
+    editTitle: '编 辑',
+    addTitle: '新 增',
+    viewTitle: '查 看',
+    menuTitle: '操作',
+    tipStartTitle: '当前表格已选择',
+    tipEndTitle: '项',
+    //尺寸
+    menuWidth: 240,
+    cellBtnSize: 'small',
+    viewBtnSize: 'small',
+    delBtnSize: 'small',
+    editBtnSize: 'small',
+    addBtnSize: 'small',
+    searchBtnSize: 'small',
+    emptyBtnSize: 'small',
+    searchComponentSize: 'small',
+    columnBtnSize: 'small',
+    refreshBtnSize: 'small',
+    searchboxBtnSize: 'small',
+    menuBtnSize: 'small',
+    dateBtnSize: 'small',
+    //文字
+    viewBtnTitle: '查 看',
+    editBtnTitle: '编 辑',
+    addBtnTitle: '新 增',
+    delBtnTitle: '删 除',
+    updateBtnTitle: '修 改',
+    saveBtnTitle: '新 增',
+    cancelBtnTitle: '取 消',
+    columnBtnTitle: '多 选',
+    searchBtnTitle: '搜 索',
+    emptyBtnTitle: '清 空',
+    tipBtnTitle: '清空',
+    menuBtnTitle: '菜 单',
+    cellEditBtnTitle: '修改',
+    cellSaveBtnTitle: '保存',
+    //图标
+    viewBtnIcon: 'el-icon-view',
+    editBtnIcon: 'el-icon-edit',
+    addBtnIcon: 'el-icon-plus',
+    delBtnIcon: 'el-icon-delete',
+    searchBtnIcon: 'el-icon-search',
+    emptyBtnIcon: 'el-icon-delete',
+    cellEditBtnIcon: 'el-icon-edit',
+    cellSaveBtnIcon: 'el-icon-check',
+    columnBtnIcon: 'el-icon-menu',
+    refreshBtnIcon: 'el-icon-refresh',
+    searchboxBtnIcon: 'el-icon-search',
+    //显隐
+    viewBtn: false,
+    editBtn: true,
+    addBtn: true,
+    delBtn: true,
+    cellBtn: false,
+    dateBtn: false,
+    refreshBtn: true,
+    columnBtn: true,
+    searchBtn: true,
+    menuBtn: false,
+    selectClearBtn: true,
+    searchShow: true,
+    tip: true,
+    formWidth: '50%',
+    formFullscreen: false,
+    customClass: '',
+    pageBackground: true,
+    page: true,
+    menu: true,
+    indexLabel: '#',
+    filterMultiple: true,
+    calcHeight: 300,
+    title: '表格标题',
+    width: '100%',
+    dateDefault: false,
+    //搜索参数
+    searchMultiple: ['checkbox', 'tree', 'select']
+}

+ 68 - 0
packages/crud/src/crud-cascader.vue

@@ -0,0 +1,68 @@
+<template>
+  <el-cascader :options="dic"
+               v-model="text"
+               :placeholder="placeholder?placeholder:`请选择${label}`"
+               :props="props"
+               :readonly="readonly"
+               :change-on-select="changeoOnSelect"
+               :clearable="clearable"
+               :expand-trigger="expandTrigger"
+               :show-all-levels="showAllLevels"
+               :filterable="filterable"
+               :separator="separator"
+               :disabled="disabled"
+               @click.native="handleClick"
+               @change="handleChange">
+  </el-cascader>
+</template>
+
+<script>
+import create from '../../utils/create';
+import crudCompoents from '../../mixins/crud-compoents.js';
+export default create({
+  name: 'crud-cascader',
+  mixins: [crudCompoents()],
+  props: {
+    value: {
+      type: Array,
+      default: () => []
+    },
+    changeoOnSelect: {
+      type: Boolean,
+      default: false
+    },
+    expandTrigger: {
+      type: String,
+      default: 'hover'
+    },
+    showAllLevels: {
+      type: Boolean,
+      default: true
+    },
+    filterable: {
+      type: Boolean,
+      default: false
+    },
+    separator: {
+      type: String,
+      default: '/'
+    }
+  },
+  data () {
+    return {};
+  },
+  watch: {},
+  created () { },
+  mounted () { },
+  methods: {
+    handleClick () {
+      if (typeof this.click === 'function') this.click({ value: this.text, column: this.column });
+    },
+    handleChange (value) {
+      if (typeof this.change === 'function') this.change({ value: value, column: this.column });
+      this.$emit('input', value);
+      this.$emit('change', value);
+    }
+  }
+});
+</script>

+ 46 - 0
packages/crud/src/crud-checkbox.vue

@@ -0,0 +1,46 @@
+<template>
+  <el-checkbox-group v-model="text"
+                     @change="handleChange"
+                     @click.native="handleClick">
+    <el-checkbox v-for="(item,index) in dic"
+                 :label="item[valueKey]"
+                 :border="border"
+                 :min="min"
+                 :readonly="readonly"
+                 :max="max"
+                 :disabled="item[disabledKey]"
+                 :key="index">{{item[labelKey]}}</el-checkbox>
+  </el-checkbox-group>
+</template>
+
+<script>
+import create from '../../utils/create';
+import crudCompoents from '../../mixins/crud-compoents.js';
+export default create({
+  name: 'crud-checkbox',
+  mixins: [crudCompoents()],
+  data () {
+    return {};
+  },
+  props: {
+    value: {
+      type: Array,
+      default: () => []
+    }
+  },
+  watch: {},
+  created () { },
+  mounted () { },
+  methods: {
+    handleClick () {
+      if (typeof this.click === 'function') this.click({ value: this.text, column: this.column });
+    },
+    handleChange (value) {
+      if (typeof this.change === 'function') this.change({ value: value, column: this.column });
+      this.$emit('input', value);
+      this.$emit('change', value);
+    }
+  }
+});
+</script>
+

+ 85 - 0
packages/crud/src/crud-components.vue

@@ -0,0 +1,85 @@
+<template>
+  <div>
+    <el-table-column v-if="columnIndex.indexOf(column.prop)!=-1"
+                     v-for="(column,index) in columnOption"
+                     :prop="column.prop"
+                     :key="index"
+                     filter-placement="bottom-end"
+                     :filters="column.filters"
+                     :filter-method="column.filterMethod"
+                     :filter-multiple="vaildData(column.filterMultiple,true)"
+                     :show-overflow-tooltip="column.overHidden"
+                     :min-width="column.minWidth"
+                     :sortable="column.sortable"
+                     :align="vaildData(column.align,tableOption.align)"
+                     :header-align="vaildData(column.headerAlign,tableOption.headerAlign)"
+                     :width="column.width"
+                     :label="column.label"
+                     :fixed="column.fixed">
+      <crud-components v-if="column.children"
+                       :columnOption="column.children"
+                       :tableOption="tableOption"
+                       :tableForm="tableForm"
+                       :columnIndex="columnIndex"
+                       :DIC="DIC">
+        <template slot-scope="scope"
+                  v-for="item in column.children"
+                  :slot="item.prop">
+          <slot :row="scope.row"
+                :dic="scope.dic"
+                :label="scope.label"
+                :name="item.prop"
+                v-if="item.solt"></slot>
+        </template>
+      </crud-components>
+      <template slot-scope="scope">
+        <template v-if="cellEditFlag(scope.row,column)">
+          <component size="small"
+                     :is="getSearchType(column.type)"
+                     v-model="tableForm[column.prop]"
+                     :type="getType(column)"
+                     clearable
+                     :placeholder="column.label"
+                     :dic="setDic(column.dicData,DIC[column.dicData])"></component>
+        </template>
+        <slot :row="scope.row"
+              :dic="setDic(column.dicData,DIC[column.dicData])"
+              :label="detail(scope.row,column)"
+              :name="column.prop"
+              v-else-if="column.solt"></slot>
+        <template v-else>
+          <span v-html="detail(scope.row,column)"></span>
+        </template>
+      </template>
+    </el-table-column>
+  </div>
+</template>
+
+<script>
+import column from '../../mixins/column'
+import crudInput from './crud-input';
+import crudSelect from './crud-select';
+export default {
+  name: 'crud-components',
+  mixins: [column()],
+  props: [
+    'columnIndex',
+    'columnOption',
+    'tableOption',
+    'DIC',
+    'tableForm'
+  ],
+  components: {
+    crudInput,
+    crudSelect,
+  },
+  created () {
+    this.initFun();
+  },
+  methods: {
+  }
+}
+</script>
+
+<style>
+</style>

+ 71 - 0
packages/crud/src/crud-date.vue

@@ -0,0 +1,71 @@
+<template>
+  <el-date-picker :type="type"
+                  v-model="text"
+                  :size="size"
+                  :readonly="readonly"
+                  range-separator="至"
+                  :start-placeholder="startPlaceholder"
+                  :end-placeholder="endPlaceholder"
+                  :format="format"
+                  :picker-options="pickerOptions"
+                  :value-format="valueFormat"
+                  :default-time="defaultTime"
+                  :placeholder="placeholder?placeholder:`请输入${label}`"
+                  @change="handleChange"
+                  @click.native="handleClick"
+                  :disabled="disabled"> </el-date-picker>
+</template>
+
+<script>
+import create from '../../utils/create';
+import crudCompoents from '../../mixins/crud-compoents.js';
+export default create({
+  name: 'crud-date',
+  mixins: [crudCompoents()],
+  data () {
+    return {
+      text: ''
+    };
+  },
+  props: {
+    value: {
+    },
+    startPlaceholder: {
+      type: String,
+      default: '开始日期'
+    },
+    endPlaceholder: {
+      type: String,
+      default: '结束日期'
+    },
+    defaultTime: {
+      type: String
+    },
+    pickerOptions: {
+      type: Object,
+      default: () => { }
+    },
+    type: {
+      default: 'date'
+    },
+    valueFormat: {
+    },
+    format: {
+    }
+  },
+  watch: {},
+  created () { },
+  mounted () { },
+  methods: {
+    handleClick () {
+      if (typeof this.click === 'function') this.click({ value: this.text, column: this.column });
+    },
+    handleChange (value) {
+      if (typeof this.change === 'function') this.change({ value: value, column: this.column });
+      this.$emit('input', value);
+      this.$emit('change', value);
+    }
+  }
+});
+</script>
+

+ 64 - 0
packages/crud/src/crud-input-number.vue

@@ -0,0 +1,64 @@
+<template>
+  <el-input-number v-model="text"
+                   class="avue-input-number"
+                   @change="handleChange"
+                   @click.native="handleClick"
+                   :precision="precision"
+                   :size="size"
+                   :min="minRows"
+                   :max="maxRows"
+                   :readonly="readonly"
+                   :controls-position="controlsPosition"
+                   :label="placeholder?placeholder:`请输入${label}`"
+                   :disabled="disabled"></el-input-number>
+</template>
+
+<script>
+import create from '../../utils/create';
+import crudCompoents from '../../mixins/crud-compoents.js';
+export default create({
+  name: 'crud-input-number',
+  mixins: [crudCompoents()],
+  data () {
+    return {};
+  },
+  props: {
+    value: {
+      type: Number
+    },
+    step: {
+      type: Number,
+      default: 1
+    },
+    controlsPosition: {
+      type: String,
+      default: 'right'
+    },
+    precision: {
+      type: Number,
+      default: 0
+    },
+    minRows: {
+      type: Number,
+      default: -Infinity
+    },
+    maxRows: {
+      type: Number,
+      default: Infinity
+    }
+  },
+  watch: {},
+  created () { },
+  mounted () { },
+  methods: {
+    handleClick () {
+      if (typeof this.click === 'function') this.click({ value: this.text, column: this.column });
+    },
+    handleChange (value) {
+      if (typeof this.change === 'function') this.change({ value: value, column: this.column });
+      this.$emit('input', value);
+      this.$emit('change', value);
+    }
+  }
+});
+</script>

+ 270 - 0
packages/crud/src/crud-input.vue

@@ -0,0 +1,270 @@
+<template>
+  <div>
+    <el-input v-if="type==='tree'"
+              :size="size"
+              v-model="labelShow"
+              :type="typeParam"
+              :autosize="{ minRows: minRows, maxRows: maxRows}"
+              :prefix-icon="prefixIcon"
+              :suffix-icon="suffixIcon"
+              :placeholder="placeholder?placeholder:`请选择${label}`"
+              @change="handleChange"
+              :disabled="disabled"
+              :readonly="true"
+              @click.native="disabled?'':open()" />
+    <el-input v-else-if="type==='phone'"
+              :size="size"
+              :clearable="clearable"
+              v-model="text"
+              @click.native="handleClick"
+              :type="typeParam"
+              :maxlength="maxlength"
+              :autosize="{ minRows: minRows, maxRows: maxRows}"
+              :prefix-icon="prefixIcon"
+              :suffix-icon="suffixIcon"
+              :readonly="readonly"
+              :placeholder="placeholder?placeholder:`请输入${label}`"
+              @change="handleChange"
+              :disabled="disabled" />
+    <el-input v-else
+              :size="size"
+              :clearable="clearable"
+              v-model="text"
+              @click.native="handleClick"
+              :type="typeParam"
+              :maxlength="maxlength"
+              :minlength="minlength"
+              :autosize="{ minRows: minRows, maxRows: maxRows}"
+              :prefix-icon="prefixIcon"
+              :suffix-icon="suffixIcon"
+              :readonly="readonly"
+              :placeholder="placeholder?placeholder:`请输入${label}`"
+              @change="handleChange"
+              :disabled="disabled" />
+    <el-dialog :visible.sync="box"
+               append-to-body
+               :title="`请选择${label}`"
+               width="50%">
+      <el-input style="margin-bottom:15px;"
+                placeholder="输入关键字进行过滤"
+                v-model="filterText"
+                v-if="filter">
+      </el-input>
+      <div class="avue-dialog">
+        <el-tree :data="dic"
+                 :node-key="valueKey"
+                 :show-checkbox="multiple"
+                 :props="props"
+                 ref="tree"
+                 @check="checkChange"
+                 :filter-node-method="filterNode"
+                 :default-expanded-keys="multiple?text:[]"
+                 :default-checked-keys="multiple?text:[]"
+                 :default-expand-all="defaultExpandAll"
+                 @node-click="handleNodeClick"></el-tree>
+      </div>
+
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import create from '../../utils/create';
+import crudCompoents from '../../mixins/crud-compoents.js';
+import { validatenull } from '../../utils/validate';
+export default create({
+  name: 'crud-input',
+  mixins: [crudCompoents()],
+  data () {
+    return {
+      filterText: '',
+      box: false,
+      labelText: this.multiple ? [] : ''
+    };
+  },
+  props: {
+    nodeClick: Function,
+    value: {
+    },
+    filter: {
+      type: Boolean,
+      default: true
+    },
+    multiple: {
+      type: Boolean,
+      default: false
+    },
+    parent: {
+      type: Boolean,
+      default: true
+    },
+    defaultExpandAll: {
+      type: Boolean,
+      default: true
+    },
+    prefixIcon: {
+      type: String
+    },
+    suffixIcon: {
+      type: String
+    },
+    minlength: {
+      type: Number
+    },
+    maxlength: {
+      type: Number,
+      default: function () {
+        if (this.type === 'phone') return 11;
+      }
+    },
+    minRows: {
+      type: Number,
+      default: 3
+    },
+    maxRows: {
+      type: Number,
+      default: 4
+    }
+  },
+  watch: {
+    value () {
+      this.init();
+    },
+    filterText (val) {
+      this.$refs.tree.filter(val);
+    }
+  },
+  computed: {
+    labelShow () {
+      return this.multiple ? this.labelText.join('/').toString() : this.labelText;
+    },
+    textShow () {
+      if (this.textLen === 11) return `${this.text.substr(0, 3)} ${this.text.substr(3, 4)} ${this.text.substr(7, 4)}`
+      return this.text;
+    },
+    textLen () {
+      return this.text.length;
+    },
+    typeParam: function () {
+      if (this.type === 'textarea') {
+        return 'textarea';
+      } else if (this.type === 'password') {
+        return 'password';
+      } else {
+        return 'text';
+      }
+    }
+  },
+  created () { },
+  mounted () {
+    this.init();
+  },
+  methods: {
+    filterNode (value, data) {
+      if (!value) return true;
+      return data.label.indexOf(value) !== -1;
+    },
+    checkChange (checkedNodes, checkedKeys, halfCheckedNodes, halfCheckedKeys) {
+      console.log(checkedNodes, checkedKeys, halfCheckedNodes, halfCheckedKeys);
+      this.text = [];
+      this.labelText = [];
+      checkedKeys.checkedNodes.forEach(node => {
+        if (validatenull(node[this.childrenKey])) {
+          this.text.push(node[this.valueKey]);
+          this.labelText.push(node[this.labelKey]);
+        }
+      });
+      this.$emit('input', this.text);
+      this.$emit('change', this.text);
+    },
+    open () {
+      this.box = true;
+      this.handleClick();
+    },
+    init () {
+      if (this.type === 'tree') {
+        if (this.multiple) {
+          this.labelText = ['获取字典中...'];
+        } else {
+          this.labelText = '获取字典中...';
+        }
+        const check = setInterval(() => {
+          if (!validatenull(this.dic)) {
+            if (this.multiple) {
+              this.labelText = [];
+              this.text.forEach(ele => {
+                this.findLabelNode(this.dic, ele, this.props);
+              });
+            } else {
+              this.labelText = this.text;
+              this.findLabelNode(this.dic, this.text, this.props);
+            }
+            if (!this.parent) this.disabledParentNode(this.dic);
+            clearInterval(check);
+          } else {
+            this.labelText = '';
+          }
+        }, 500);
+      } else if (this.type === 'phone') {
+        if (!validatenull(this.text) && this.textLen == 11) {
+          this.text = this.textShow;
+        }
+      }
+    },
+    findLabelNode (dic, value, props) {
+      const labelKey = props.label || 'label';
+      const valueKey = props.value || 'value';
+      const childrenKey = props.children || 'children';
+      dic.forEach(ele => {
+        const children = ele[childrenKey];
+        if (ele[valueKey] === value) {
+          const label = ele[labelKey];
+          this.multiple ? this.labelText.push(label) : this.labelText = label;
+        } else if (!validatenull(children)) {
+          this.findLabelNode(children, value, props);
+        }
+      });
+    },
+    disabledParentNode (dic) {
+      dic.forEach(ele => {
+        const children = ele[this.childrenKey];
+        if (!validatenull(children)) {
+          ele.disabled = true;
+          this.disabledParentNode(children);
+        }
+      });
+    },
+    handleNodeClick (data) {
+      const callback = () => {
+        this.box = false;
+      }
+      if (validatenull(data[this.childrenKey]) && !this.multiple || this.parent) {
+        const value = data[this.valueKey];
+        const label = data[this.labelKey];
+        this.text = value;
+        this.labelText = label;
+        this.$emit('input', value);
+        this.$emit('change', value);
+        if (typeof this.nodeClick === 'function') this.nodeClick(data, callback);
+        else callback();
+      }
+
+    },
+    handleClick () {
+      if (typeof this.click === 'function') this.click({ value: this.text, column: this.column });
+    },
+    handleChange (value) {
+      let text = this.text;
+      if (typeof this.change === 'function') this.change({ value: value, column: this.column });
+      if (this.type === 'phone') {
+        this.text = text.replace(/[^0-9.]/g, '');
+        this.text = this.textShow;
+        text = this.text.replace(/\s/g, "");
+      }
+      this.$emit('input', text);
+      this.$emit('change', text);
+    }
+  }
+});
+</script>
+

+ 41 - 0
packages/crud/src/crud-radio.vue

@@ -0,0 +1,41 @@
+<template>
+  <el-radio-group v-model="text"
+                  @change="handleChange"
+                  @click.native="handleClick"
+                  :disabled="disabled">
+    <el-radio v-for="(item,index) in dic"
+              :label="item[valueKey]"
+              :border="border"
+              :readonly="readonly"
+              :disabled="item[disabledKey]"
+              :key="index">{{item[labelKey]}}</el-radio>
+  </el-radio-group>
+</template>
+
+<script>
+import create from '../../utils/create';
+import crudCompoents from '../../mixins/crud-compoents.js';
+export default create({
+  name: 'crud-radio',
+  mixins: [crudCompoents()],
+  data () {
+    return {};
+  },
+  props: {
+    value: {}
+  },
+  watch: {},
+  created () { },
+  mounted () { },
+  methods: {
+    handleClick () {
+      if (typeof this.click === 'function') this.click({ value: this.text, column: this.column });
+    },
+    handleChange (value) {
+      if (typeof this.change === 'function') this.change({ value: value, column: this.column });
+      this.$emit('input', value);
+      this.$emit('change', value);
+    }
+  }
+});
+</script>

+ 64 - 0
packages/crud/src/crud-rate.vue

@@ -0,0 +1,64 @@
+<template>
+  <el-rate v-model="text"
+           style="margin-top:10px"
+           @change="handleChange"
+           @click.native="handleClick"
+           :max="max"
+           :readonly="readonly"
+           :show-text="showText"
+           :icon-classes="iconClasses"
+           :void-icon-class="voidIconClass"
+           :disabled="disabled"
+           :colors="colors"></el-rate>
+</template>
+
+<script>
+import create from '../../utils/create';
+import crudCompoents from '../../mixins/crud-compoents.js';
+export default create({
+  name: 'crud-rate',
+  mixins: [crudCompoents()],
+  props: {
+    value: {
+      type: Number,
+      default: 0
+    },
+    colors: {
+      type: Array
+    },
+    max: {
+      type: Number,
+      default: 5
+    },
+    iconClasses: {
+      type: Array
+    },
+    texts: {
+      type: Array
+    },
+    showText: {
+      type: Boolean,
+      default: false
+    },
+    voidIconClass: {
+      type: String
+    }
+  },
+  data () {
+    return {};
+  },
+  watch: {},
+  created () { },
+  mounted () { },
+  methods: {
+    handleClick () {
+      if (typeof this.click === 'function') this.click({ value: this.text, column: this.column });
+    },
+    handleChange (value) {
+      if (typeof this.change === 'function') this.change({ value: value, column: this.column });
+      this.$emit('input', value);
+      this.$emit('change', value);
+    }
+  }
+});
+</script>

+ 57 - 0
packages/crud/src/crud-select.vue

@@ -0,0 +1,57 @@
+<template>
+  <el-select v-model="text"
+             :size="size"
+             :multiple="multiple"
+             :filterable="filterable"
+             :readonly="readonly"
+             :clearable="clearable"
+             :placeholder="placeholder?placeholder:`请选择${label}`"
+             @change="handleChange"
+             @click.native="handleClick"
+             :disabled="disabled">
+    <el-option v-for="(item,index) in dic"
+               :key="index"
+               :disabled="item[disabledKey]"
+               :label="item[labelKey]"
+               :value="item[valueKey]">
+    </el-option>
+  </el-select>
+</template>
+
+<script>
+import create from '../../utils/create';
+import crudCompoents from '../../mixins/crud-compoents.js';
+export default create({
+  name: 'crud-select',
+  mixins: [crudCompoents()],
+  data () {
+    return {};
+  },
+  props: {
+    value: {
+    },
+    multiple: {
+      type: Boolean,
+      default: false
+    },
+    filterable: {
+      type: Boolean,
+      default: false
+    },
+  },
+  watch: {
+  },
+  created () { },
+  mounted () { },
+  methods: {
+    handleClick () {
+      if (typeof this.click === 'function') this.click({ value: this.text, column: this.column });
+    },
+    handleChange (value) {
+      if (typeof this.change === 'function') this.change({ value: value, column: this.column });
+      this.$emit('input', value);
+      this.$emit('change', value);
+    }
+  }
+});
+</script>

+ 68 - 0
packages/crud/src/crud-silder.vue

@@ -0,0 +1,68 @@
+<template>
+  <el-slider v-model="text"
+             :disabled="disabled"
+             :step="step"
+             :min="min"
+             :max="max"
+             :range="range"
+             :show-stops="showStops"
+             :show-input="showInput"
+             :format-tooltip="formatTooltip"
+             @click.native="handleClick"
+             @change="handleChange"></el-slider>
+</template>
+
+<script>
+import create from '../../utils/create';
+import crudCompoents from '../../mixins/crud-compoents.js';
+export default create({
+  name: 'crud-silder',
+  mixins: [crudCompoents()],
+  props: {
+    value: {
+
+    },
+    step: {
+      type: Number
+    },
+    min: {
+      type: Number
+    },
+    max: {
+      type: Number
+    },
+    range: {
+      type: Boolean,
+      default: false
+    },
+    showInput: {
+      type: Boolean,
+      default: false
+    },
+    showStops: {
+      type: Boolean,
+      default: false
+    },
+    formatTooltip: Function
+  },
+  data () {
+    return {};
+  },
+  watch: {},
+  created () { },
+  mounted () { },
+  methods: {
+    handleClick () {
+      if (typeof this.click === 'function') this.click({ value: this.text, column: this.column });
+    },
+    handleChange (value) {
+      if (typeof this.change === 'function') this.change({ value: value, column: this.column });
+      this.$emit('input', value);
+      this.$emit('change', value);
+    },
+    getDic (index) {
+      return this.dic[index] ? this.dic[index] : {};
+    }
+  }
+});
+</script>

+ 49 - 0
packages/crud/src/crud-switch.vue

@@ -0,0 +1,49 @@
+<template>
+  <div>
+    <el-switch v-model="text"
+               @change="handleChange"
+               @click.native="handleClick"
+               :active-text="getDic(0)[labelKey]"
+               :active-value="getDic(0)[valueKey] || ''"
+               :inactive-value="getDic(1)[valueKey] || ''"
+               :inactive-text="getDic(1)[labelKey]"
+               :disabled="disabled"
+               :readonly="readonly"
+               :size="size">
+    </el-switch>
+  </div>
+</template>
+
+<script>
+import create from '../../utils/create';
+import crudCompoents from '../../mixins/crud-compoents.js';
+export default create({
+  name: 'crud-switch',
+  mixins: [crudCompoents()],
+  props: {
+    value: {
+
+    }
+  },
+  data () {
+    return {};
+  },
+  watch: {},
+  created () { },
+  mounted () { },
+  methods: {
+    handleClick () {
+      if (typeof this.click === 'function') this.click({ value: this.text, column: this.column });
+    },
+    handleChange (value) {
+      if (typeof this.change === 'function') this.change({ value: value, column: this.column });
+      this.$emit('input', value);
+      this.$emit('change', value);
+    },
+    getDic (index) {
+      return this.dic[index] ? this.dic[index] : {};
+    }
+  }
+});
+</script>
+

+ 67 - 0
packages/crud/src/crud-time.vue

@@ -0,0 +1,67 @@
+<template>
+  <el-time-picker v-model="text"
+                  :is-range="isRange"
+                  :size="size"
+                  range-separator="至"
+                  :start-placeholder="startPlaceholder"
+                  :end-placeholder="endPlaceholder"
+                  :format="format"
+                  :readonly="readonly"
+                  :value-format="valueFormat"
+                  :placeholder="placeholder?placeholder:`请输入${label}`"
+                  @change="handleChange"
+                  @click.native="handleClick"
+                  :disabled="disabled"> </el-time-picker>
+</template>
+
+<script>
+import create from '../../utils/create';
+import crudCompoents from '../../mixins/crud-compoents.js';
+export default create({
+  name: 'crud-time',
+  mixins: [crudCompoents()],
+  data () {
+    return {};
+  },
+  props: {
+    startPlaceholder: {
+      type: String,
+      default: '开始时间'
+    },
+    endPlaceholder: {
+      type: String,
+      default: '结束时间'
+    },
+    value: {
+      required: true
+    },
+    valueFormat: {
+      default: ''
+    },
+    type: {
+      default: ''
+    },
+    format: {
+      default: ''
+    }
+  },
+  watch: {},
+  created () { },
+  mounted () { },
+  computed: {
+    isRange () {
+      return this.type === 'timerange'
+    }
+  },
+  methods: {
+    handleClick () {
+      if (typeof this.click === 'function') this.click({ value: this.text, column: this.column });
+    },
+    handleChange (value) {
+      if (typeof this.change === 'function') this.change({ value: value, column: this.column });
+      this.$emit('input', value);
+      this.$emit('change', value);
+    }
+  }
+});
+</script>

+ 215 - 0
packages/crud/src/crud-upload.vue

@@ -0,0 +1,215 @@
+<template>
+  <div :class="b()">
+    <el-upload :class="b({'list':listType=='picture-img'})"
+               @click.native="handleClick"
+               :action="action"
+               :on-remove="handleRemove"
+               :before-remove="beforeRemove"
+               :multiple="multiple"
+               :on-preview="handlePictureCardPreview"
+               :limit="limit"
+               :http-request="httpRequest"
+               :drag="drag"
+               :readonly="readonly"
+               :show-file-list="showFileList"
+               :list-type="listType"
+               :on-change="handleChange"
+               :on-exceed="handleExceed"
+               :disabled="disabled"
+               :file-list="fileList">
+      <template v-if="listType=='picture-card'">
+        <i class="el-icon-plus"></i>
+      </template>
+      <template v-else-if="listType=='picture-img'">
+        <img v-if="imageUrl"
+             :src="imageUrl"
+             :class="b('avatar')">
+        <i v-else
+           class="el-icon-plus"
+           :class="b('icon')"></i>
+      </template>
+      <template v-else-if="drag">
+        <i class="el-icon-upload"></i>
+        <div class="el-upload__text">将文件拖到此处,或
+          <em>点击上传</em>
+        </div>
+      </template>
+      <template v-else>
+        <el-button size="small"
+                   type="primary">点击上传</el-button>
+      </template>
+      <div slot="tip"
+           class="el-upload__tip">{{tip}}</div>
+    </el-upload>
+    <el-dialog :visible.sync="dialogVisible">
+      <div class="avue-dialog">
+        <img width="100%"
+             :src="dialogImageUrl"
+             alt="">
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+
+import create from '../../utils/create';
+import crudCompoents from '../../mixins/crud-compoents.js';
+export default create({
+  name: 'crud-upload',
+  mixins: [crudCompoents()],
+  data () {
+    return {
+      loading: false,
+      dialogImageUrl: '',
+      dialogVisible: false,
+      text: this.status ? '' : [],
+      fileList: [],
+      file: {}
+    };
+  },
+  props: {
+    value: {
+
+    },
+    showFileList: {
+      type: Boolean,
+      default: true
+    },
+    limit: {
+      type: Number,
+      default: 3
+    },
+    listType: {
+      type: String
+    },
+    drag: {
+      type: Boolean,
+      default: false
+    },
+    multiple: {
+      type: Boolean,
+      default: true
+    },
+    loadText: {
+      type: String,
+      default: '文件上传中,请稍等'
+    },
+    action: {
+      type: String,
+      default: ''
+    },
+    uploadBefore: Function,
+    uploadAfter: Function
+  },
+  computed: {
+    status () {
+      return this.listType === 'picture-img';
+    },
+    imageUrl () {
+      return this.status ? this.text : '';
+    }
+  },
+  created () {
+
+  },
+  watch: {
+    text () {
+      this.fileList = this.text || [];
+    }
+  },
+  mounted () { },
+  methods: {
+    handleClick () {
+      if (typeof this.click === 'function') this.click({ value: this.text, column: this.column });
+    },
+    handleChange (file, fileList) {
+      if (typeof this.change === 'function') this.change({ value: this.value, column: this.column });
+      this.loading = this.$loading({
+        lock: true,
+        text: this.loadText,
+        spinner: 'el-icon-loading',
+      });
+      const len = fileList.length;
+      this.fileList = this.fileList.splice(len, 1);
+    },
+    handleSuccess (file) {
+      {
+        this.fileList.push({
+          name: file.label,
+          url: file.value
+        });
+        this.$message.success('上传成功');
+        this.setVal();
+      }
+
+    },
+    handleRemove (file, fileList) {
+      this.fileList = fileList;
+      this.$message.success('删除成功');
+      this.setVal();
+    },
+    handleError () {
+      this.$message.error('上传失败');
+    },
+    show (res) {
+      const data = res.data;
+      this.loading.close();
+      this.handleSuccess({
+        label: data[this.labelKey],
+        value: data[this.valueKey]
+      });
+    },
+    hide () {
+      this.loading.close();
+      this.handleError();
+    },
+    httpRequest (config) {
+      const file = config.file;
+      this.file = config.file;
+      const headers = { 'Content-Type': 'multipart/form-data' }
+      let param = new FormData()
+      param.append('file', file, file.name)
+
+
+      const callack = () => {
+        this.$http.post(this.action, param, { headers }).then(res => {
+          if (typeof this.uploadAfter === 'function') this.uploadAfter(res, () => {
+            this.show(res)
+          })
+          else this.show(res);
+        }).catch((error) => {
+          if (typeof this.uploadAfter === 'function') this.uploadAfter(error, this.hide);
+          else this.hide(error);
+        });
+      };
+
+
+      if (typeof this.uploadBefore === 'function') this.uploadBefore(this.file, callack);
+      else callack();
+
+
+    },
+    setVal () {
+      let value;
+      if (this.status) {
+        value = this.text;
+      } else {
+        value = this.fileList;
+      }
+      this.$emit('input', value);
+      this.$emit('change', value);
+    },
+    handleExceed (files, fileList) {
+      this.$message.warning(`当前限制选择 ${this.limit} 个文件,本次选择了 ${files.length} 个文件,共选择了 ${files.length + fileList.length} 个文件`);
+    },
+    handlePictureCardPreview (file) {
+      this.dialogImageUrl = file.url;
+      this.dialogVisible = true;
+    },
+    beforeRemove (file) {
+      return this.$confirm(`确定移除 ${file.name}?`);
+    }
+  }
+});
+</script>

+ 706 - 0
packages/crud/src/main.vue

@@ -0,0 +1,706 @@
+<template>
+  <div :class="b()">
+    <el-card :class="b('box')">
+      <div slot="header"
+           :class="b('title')"
+           v-if="vaildData(tableOption.title,false) || vaildData(tableOption.dateBtn,config.dateBtn)">
+        <span>{{tableOption.title}}</span>
+        <date-select @change="dateChange"
+                     v-if="vaildData(tableOption.dateBtn,config.dateBtn)"
+                     :default="vaildData(tableOption.dateDefault,config.dateDefault)"
+                     :size="vaildData(tableOption.dateSize,config.dateBtnSize)">
+        </date-select>
+      </div>
+      <div :class="b('header')">
+        <el-collapse-transition>
+          <el-form :model="searchForm"
+                   :inline="true"
+                   ref="searchForm"
+                   v-if="searchShow && searchFlag">
+            <!-- 循环列搜索框 -->
+            <el-form-item :prop="column.prop"
+                          :label="column.label"
+                          v-for="(column,index) in columnOption"
+                          :key="index"
+                          v-if="column.search">
+              <component :size="vaildData(tableOption.searchSize,config.searchComponentSize)"
+                         :is="getSearchType(column.type)"
+                         v-model="searchForm[column.prop]"
+                         :type="getType(column)"
+                         :props="column.props || tableOption.props"
+                         :format="column.format"
+                         :filterable="column.searchFilterable"
+                         :filter-method="column.searchFilterMethod"
+                         :value-format="column.valueFormat"
+                         :multiple="config.searchMultiple.includes(column.type) && vaildData(column.searchMmultiple,false)"
+                         clearable
+                         :placeholder="column.label"
+                         :dic="setDic(column.dicData,DIC[column.dicData])"></component>
+            </el-form-item>
+            <slot name="search"></slot>
+            <el-form-item>
+              <el-button type="primary"
+                         @click="searchChange"
+                         :icon="config.searchBtnIcon"
+                         :size="vaildData(tableOption.searchSize,config.searchBtnSize)">{{config.searchBtnTitle}}</el-button>
+              <el-button @click="searchReset"
+                         :icon="config.emptyBtnIcon"
+                         :size="vaildData(tableOption.searchSize,config.emptyBtnSize)">{{config.emptyBtnTitle}}</el-button>
+              <slot name="searchMenu"></slot>
+            </el-form-item>
+          </el-form>
+        </el-collapse-transition>
+      </div>
+      <!-- 表格功能列 -->
+      <div :class="b('menu')">
+        <div :class="b('left')">
+          <el-button type="primary"
+                     @click="rowAdd"
+                     :icon="config.addBtnIcon"
+                     :size="config.addBtnSize"
+                     v-if="vaildData(tableOption.addBtn,config.addBtn)">{{config.addBtnTitle}}</el-button>
+          <slot name="menuLeft"></slot>
+        </div>
+        <div :class="b('right')">
+          <slot name="menuRight"></slot>
+          <el-button :icon="config.refreshBtnIcon"
+                     circle
+                     :size="config.refreshBtnSize"
+                     @click="refreshChange"
+                     v-if="vaildData(tableOption.refreshBtn,config.refreshBtn)"></el-button>
+          <el-button :icon="config.columnBtnIcon"
+                     circle
+                     :size="config.columnBtnSize"
+                     @click="columnBox=true"
+                     v-if="vaildData(tableOption.columnBtn,config.columnBtn)"></el-button>
+          <el-button :icon="config.searchboxBtnIcon"
+                     circle
+                     :size="config.searchboxBtnSize"
+                     @click="searchShow=!searchShow"
+                     v-if="searchFlag && vaildData(tableOption.searchBtn,config.searchBtn)"></el-button>
+        </div>
+      </div>
+      <el-tag class="avue-tip"
+              v-if="vaildData(tableOption.tip,config.tip) && tableOption.selection">
+        <i class="el-icon-info avue-tip__icon">&nbsp;</i>
+        <span class="avue-tip__name">
+          {{config.tipStartTitle}}
+          <span class="avue-tip__name--bold">{{selectLen}}</span> {{config.tipEndTitle}}
+        </span>
+        <span class="avue-tip__btn"
+              @click="selectClear"
+              v-if="vaildData(tableOption.selectClearBtn,config.selectClearBtn) && tableOption.selection">
+          {{config.tipBtnTitle}}
+        </span>
+      </el-tag>
+      <el-table :data="list"
+                :highlight-current-row="tableOption.highlightCurrentRow"
+                @current-change="currentRowChange"
+                :stripe="tableOption.stripe"
+                :show-header="tableOption.showHeader"
+                :default-sort="tableOption.defaultSort"
+                @row-click="rowClick"
+                @row-dblclick="rowDblclick"
+                :row-class-name="rowClassName"
+                :max-height="tableOption.maxHeight"
+                :height="tableOption.height=='auto'?(clientHeight - vaildData(tableOption.calcHeight,config.calcHeight)):tableOption.height"
+                ref="table"
+                :width="setPx(tableOption.width,config.width)"
+                :border="tableOption.border"
+                v-loading="tableLoading"
+                @selection-change="selectionChange"
+                @sort-change="sortChange">
+        <!-- 折叠面板  -->
+        <el-table-column type="expand"
+                         width="50"
+                         fixed="left"
+                         align="center"
+                         v-if="tableOption.expand">
+          <template slot-scope="props">
+            <slot :row="props.row"
+                  name="expand"></slot>
+          </template>
+        </el-table-column>
+        <!-- 选择框 -->
+        <el-table-column v-if="tableOption.selection"
+                         type="selection"
+                         width="50"
+                         fixed="left"
+                         align="center">
+        </el-table-column>
+        <!-- 序号 -->
+        <el-table-column v-if="tableOption.index"
+                         :label="vaildData(tableOption.indexLabel,config.indexLabel)"
+                         type="index"
+                         width="50"
+                         :index="indexMethod"
+                         fixed="left"
+                         align="center">
+        </el-table-column>
+        <!-- 循环列 -->
+        <el-table-column v-if="columnIndex.indexOf(column.prop)!=-1"
+                         v-for="(column,index) in columnOption"
+                         :prop="column.prop"
+                         :key="index"
+                         filter-placement="bottom-end"
+                         :filters="column.filters"
+                         :filter-method="column.filterMethod"
+                         :filter-multiple="vaildData(column.filterMultiple,config.filterMultiple)"
+                         :show-overflow-tooltip="column.overHidden"
+                         :min-width="column.minWidth"
+                         :sortable="column.sortable"
+                         :align="vaildData(column.align,tableOption.align)"
+                         :header-align="vaildData(column.headerAlign,tableOption.headerAlign)"
+                         :width="column.width"
+                         :label="column.label"
+                         :fixed="column.fixed">
+          <crud-components v-if="column.children"
+                           :columnOption="column.children"
+                           :tableOption="tableOption"
+                           :tableForm="tableForm"
+                           :columnIndex="columnIndex"
+                           :DIC="DIC">
+            <template slot-scope="scope"
+                      v-for="item in column.children"
+                      :slot="item.prop">
+              <slot :row="scope.row"
+                    :dic="scope.dic"
+                    :label="scope.label"
+                    :name="item.prop"
+                    v-if="item.solt"></slot>
+            </template>
+          </crud-components>
+          <template slot-scope="scope">
+            <template v-if="cellEditFlag(scope.row,column)">
+              <component size="small"
+                         :is="getSearchType(column.type)"
+                         v-model="tableForm[column.prop]"
+                         :type="getType(column)"
+                         clearable
+                         :placeholder="column.label"
+                         :dic="setDic(column.dicData,DIC[column.dicData])"></component>
+            </template>
+            <slot :row="scope.row"
+                  :dic="setDic(column.dicData,DIC[column.dicData])"
+                  :label="detail(scope.row,column)"
+                  :name="column.prop"
+                  v-else-if="column.solt"></slot>
+            <template v-else>
+              <span v-html="detail(scope.row,column)"></span>
+            </template>
+          </template>
+        </el-table-column>
+        <el-table-column fixed="right"
+                         v-if="vaildData(tableOption.menu,config.menu)"
+                         :label="config.menuTitle"
+                         :align="tableOption.menuAlign"
+                         :header-align="tableOption.menuHeaderAlign"
+                         :width="vaildData(tableOption.menuWidth,config.menuWidth)">
+          <template slot-scope="scope">
+            <el-dropdown split-button
+                         icon="el-icon-menu"
+                         type="primary"
+                         v-if="vaildData(tableOption.menuBtn,config.menuBtn)"
+                         :size="config.menuBtnSize">
+              {{config.menuBtnTitle}}
+              <el-dropdown-menu slot="dropdown">
+                <el-dropdown-item v-if="vaildData(tableOption.viewBtn,true)"
+                                  @click.native="rowView(scope.row,scope.$index)">{{config.viewBtnTitle}}</el-dropdown-item>
+                <el-dropdown-item divided
+                                  v-if="vaildData(tableOption.editBtn,true)"
+                                  @click.native="rowEdit(scope.row,scope.$index)">{{config.editBtnTitle}}</el-dropdown-item>
+                <el-dropdown-item divided
+                                  v-if="vaildData(tableOption.delBtn,true)"
+                                  @click.native="rowDel(scope.row,scope.$index)">{{config.delBtnTitle}}</el-dropdown-item>
+                <slot name="dropMenu"
+                      :row="scope.row"
+                      :dic="scope.dic"
+                      :label="scope.label"
+                      :index="scope.$index"></slot>
+              </el-dropdown-menu>
+            </el-dropdown>
+            <el-button type="primary"
+                       :icon="scope.row.$cellEdit?config.cellSaveBtnIcon:config.cellEditBtnIcon"
+                       :size="config.cellBtnSize"
+                       @click.stop="rowCell(scope.row,scope.$index)"
+                       v-if="vaildData(tableOption.cellBtn ,config.cellBtn)">{{scope.row.$cellEdit?config.cellSaveBtnTitle:config.cellEditBtnTitle}}</el-button>
+            <el-button type="success"
+                       :icon="config.viewBtnIcon"
+                       :size="config.viewBtnSize"
+                       @click.stop="rowView(scope.row,scope.$index)"
+                       v-if="vaildData(tableOption.viewBtn,tableOption.menuBtn?false:config.viewBtn)">{{config.viewBtnTitle}}</el-button>
+            <el-button type="primary"
+                       :icon="config.editBtnIcon"
+                       :size="config.editBtnSize"
+                       @click.stop="rowEdit(scope.row,scope.$index)"
+                       v-if="vaildData(tableOption.editBtn,tableOption.menuBtn?false:config.editBtn)">{{config.editBtnTitle}}</el-button>
+            <el-button type="danger"
+                       :icon="config.delBtnIcon"
+                       :size="config.delBtnSize"
+                       @click.stop="rowDel(scope.row,scope.$index)"
+                       v-if="vaildData(tableOption.delBtn,tableOption.menuBtn?false:config.delBtn)">{{config.delBtnTitle}}</el-button>
+            <slot name="menu"
+                  :row="scope.row"
+                  :dic="scope.dic"
+                  :label="scope.label"
+                  :index="scope.$index"></slot>
+          </template>
+        </el-table-column>
+      </el-table>
+      <!-- 分页 -->
+      <div :class="b('pagination')"
+           v-if="vaildData(tableOption.page,config.page) && listLen">
+        <el-pagination :current-page.sync="page.currentPage"
+                       :background="vaildData(tableOption.pageBackground,config.pageBackground)"
+                       :page-size="page.pageSize"
+                       :page-sizes="page.pageSizes"
+                       @size-change="sizeChange"
+                       @current-change="currentChange"
+                       layout="total, sizes, prev, pager, next, jumper"
+                       :total="page.total"></el-pagination>
+      </div>
+    </el-card>
+    <!-- 表单 -->
+    <el-dialog lock-scroll
+               :custom-class="vaildData(tableOption.customClass,config.customClass)"
+               :fullscreen="vaildData(tableOption.formFullscreen,config.formFullscreen)"
+               :modal-append-to-body="false"
+               :append-to-body="true"
+               :title="dialogTitle"
+               :visible.sync="boxVisible"
+               :width="vaildData(tableOption.formWidth,config.formWidth)"
+               @close="hide">
+      <div :class="b('dialog', ['overflow'])">
+        <avue-form v-model="tableForm"
+                   ref="tableForm"
+                   :disabled="keyBtn"
+                   :uploadBefore="uploadBefore"
+                   :uploadAfter="uploadAfter"
+                   :option="formOption">
+          <template slot-scope="scope"
+                    v-for="item in columnOption"
+                    :slot="item.prop">
+            <slot :value="scope.value"
+                  :column="scope.column"
+                  :dic="scope.dic"
+                  :name="item.prop+'Form'"
+                  v-if="item.formsolt"></slot>
+          </template>
+        </avue-form>
+      </div>
+      <span slot="footer"
+            class="dialog-footer">
+        <slot name="menuForm"
+              :row="tableForm"
+              :type="boxType"></slot>
+        <el-button type="primary"
+                   @click="rowUpdate"
+                   v-if="boxType=='edit'"
+                   :loading="keyBtn">{{config.updateBtnTitle}}</el-button>
+        <el-button type="primary"
+                   @click="rowSave"
+                   :loading="keyBtn"
+                   v-else-if="boxType=='add'">{{config.saveBtnTitle}}</el-button>
+        <el-button @click="closeDialog">{{config.cancelBtnTitle}}</el-button>
+      </span>
+    </el-dialog>
+    <!-- 动态列 -->
+    <el-dialog lock-scroll
+               :modal-append-to-body="false"
+               :append-to-body="true"
+               :title="config.columnBtnTitle"
+               :visible.sync="columnBox">
+      <el-checkbox-group v-model="columnIndex">
+        <el-row :span="24">
+          <el-col :span="6"
+                  v-for="(item,index) in columnList"
+                  :key="index">
+            <el-checkbox :label="item.prop">{{item.label}}</el-checkbox>
+          </el-col>
+        </el-row>
+      </el-checkbox-group>
+    </el-dialog>
+  </div>
+</template>
+<script>
+import dateSelect from '../../date-select'
+import create from '../../utils/create';
+import crud from '../../mixins/crud.js';
+import column from '../../mixins/column.js';
+import crudComponents from './crud-components';
+import config from './config.js';
+import {
+  validatenull
+} from '../../utils/validate.js';
+import {
+  setTimeout
+} from 'timers';
+export default create({
+  name: 'crud',
+  mixins: [crud(), column()],
+  components: {
+    crudComponents,
+    dateSelect
+  },
+  data () {
+    return {
+      clientHeight: document.documentElement.clientHeight,
+      defaultForm: {
+        tableForm: {},
+        searchForm: {}
+      },
+      defaultParam: ['$index'],
+      keyBtn: false,
+      config: config,
+      list: [],
+      searchShow: true,
+      searchForm: {},
+      boxVisible: false,
+      boxType: 'add',
+      columnIndex: [],
+      columnBox: false,
+      columnList: [],
+      tableForm: {},
+      tableOption: {},
+      tableFormRules: {},
+      tableIndex: -1,
+      tableSelect: []
+    };
+  },
+  created () {
+    // 初始化数据
+    this.dataInit();
+    // 初始化列
+    this.columnInit();
+  },
+  computed: {
+    dialogTitle () {
+      const key = `${this.boxType}Title`;
+      return this.tableOption[key] || this.config[key];
+    },
+    listLen () {
+      return this.list.length !== 0
+    },
+    columnOption () {
+      return this.tableOption.column || [];
+    },
+    selectLen () {
+      return this.tableSelect ? this.tableSelect.length : 0;
+    },
+    searchSolt () {
+      return this.vaildData(this.tableOption.searchsolt, false);
+    },
+    searchFlag () {
+      if (this.searchSolt) return true;
+      else return !validatenull(this.searchForm);
+    },
+    formOption () {
+      let option = this.deepClone(this.tableOption);
+      option.submitBtn = false;
+      option.submitPostion = 'right';
+      option.boxType = this.boxType;
+      option.dicFlag = false;
+      option.dicData = this.DIC;
+      option.emptytBtn = false;
+      return option;
+    }
+  },
+  watch: {
+    columnOption () {
+      this.columnInit();
+    },
+    data () {
+      this.dataInit();
+    }
+  },
+  mounted () { },
+  props: {
+    value: {
+      type: Object,
+      default: () => {
+        return {};
+      }
+    },
+    beforeClose: Function,
+    beforeOpen: Function,
+    rowClassName: Function,
+    uploadBefore: Function,
+    uploadAfter: Function,
+    page: {
+      type: Object,
+      default () {
+        return {
+          total: 0, // 总页数
+          currentPage: 0, // 当前页数
+          pageSize: 10, // 每页显示多少条
+          pageSizes: [10, 20, 30, 40, 50, 100],
+          background: true // 背景颜色
+        };
+      }
+    },
+    tableLoading: {
+      type: Boolean,
+      default: false
+    },
+    data: {
+      type: Array,
+      required: true,
+      default: () => {
+        return [];
+      }
+    }
+  },
+  methods: {
+    closeDialog () {
+      this.tableIndex = -1;
+      this.tableForm = {};
+      this.boxVisible = false;
+      this.keyBtn = false;
+    },
+    selectClear () {
+      this.$refs.table.clearSelection();
+    },
+    indexMethod (index) {
+      return (index + 1) + (((this.page.currentPage || 1) - 1) * (this.page.pageSize || 10));
+    },
+    refreshChange () {
+      this.$emit('refresh-change', {
+        page: this.page,
+        searchForm: this.searchForm
+      });
+    },
+    rulesInit () {
+      this.tableFormRules = {};
+      this.columnOption.forEach(ele => {
+        if (ele.rules) this.tableFormRules[ele.prop] = ele.rules;
+      });
+    },
+    columnInit: function () {
+      const safe = this;
+      this.columnIndex = [];
+      this.columnList = [];
+
+      function addChild (list) {
+        list.forEach((ele, index) => {
+          const children = ele.children;
+          if (!validatenull(children)) {
+            safe.tableOption.columnBtn = false;
+            addChild(children);
+          }
+          if (validatenull(ele.hide)) safe.columnIndex.push(ele.prop);
+          if (ele.showClomnu !== false) {
+            let obj = {
+              label: ele.label,
+              prop: ele.prop,
+              index: index
+            };
+            safe.columnList.push(safe.deepClone(obj));
+          }
+        });
+      }
+      addChild(this.columnOption)
+    },
+    formVal () {
+      Object.keys(this.value).forEach(ele => {
+        this.tableForm[ele] = this.value[ele];
+      })
+      this.$emit('input', this.tableForm);
+    },
+    dataInit () {
+      this.list = [].concat(this.data);
+      //初始化序号
+      this.list.forEach((ele, index) => {
+        ele.$index = index;
+      })
+    },
+    formInit () {
+      this.defaultForm = this.formInitVal(this.columnOption);
+      this.tableForm = this.deepClone(this.defaultForm.tableForm);
+      this.searchForm = this.deepClone(this.defaultForm.searchForm);
+      this.searchShow = this.vaildData(this.tableOption.searchShow, this.config.searchShow);
+      this.formVal();
+    },
+    // 搜索清空
+    searchReset () {
+      this.$refs['searchForm'].resetFields();
+      this.$emit('search-reset');
+    },
+    // 页大小回调
+    sizeChange (val) {
+      this.$emit('size-change', val);
+    },
+    //日期组件回调
+    dateChange (val) {
+      this.$emit('date-change', val);
+    },
+    // 页码回调
+    currentChange (val) {
+      this.$emit('current-change', val);
+    },
+    //设置单选
+    currentRowChange (currentRow, oldCurrentRow) {
+      this.$emit('current-row-change', currentRow, oldCurrentRow);
+    },
+    //设置多选选中
+    setCurrentRow (row) {
+      this.$refs.table.setCurrentRow(row);
+    },
+    // 选中实例
+    toggleSelection (rows) {
+      if (rows) {
+        rows.forEach(row => {
+          this.$refs.table.toggleRowSelection(row);
+        });
+      } else {
+        this.$refs.table.clearSelection();
+      }
+    },
+    // 选择回调
+    selectionChange (val) {
+      this.tableSelect = val;
+      this.$emit('selection-change', this.tableSelect);
+    },
+    // 排序回调
+    sortChange (val) {
+      this.$emit('sort-change', val);
+    },
+    // 搜索回调
+    searchChange () {
+      this.$emit('search-change', this.searchForm);
+    },
+    // 行双击
+    rowDblclick (row, event) {
+      this.$emit('row-dblclick', row, event);
+    },
+    // 行单机
+    rowClick (row, event, column) {
+      this.$emit('row-click', row, event, column);
+    },
+    // 新增
+    rowAdd () {
+      this.boxType = 'add';
+      this.tableForm = this.deepClone(this.defaultForm.tableForm);
+      this.clearDefaultParam();
+      this.$emit('input', this.tableForm);
+      this.show();
+    },
+    rowCell (row, index) {
+      if (row.$cellEdit) this.rowCellUpdate(row, index)
+      else this.rowCellEdit(row, index)
+    },
+    // 单元格编辑
+    rowCellEdit (row, index) {
+      if (this.tableIndex != -1) {
+        this.$message.error('先保存当前编辑的数据');
+        return;
+      }
+      this.tableIndex = index;
+      this.tableForm = this.deepClone(row);
+      this.$emit('input', this.tableForm);
+      row.$cellEdit = !row.$cellEdit;
+      this.$set(this.list, index, row);
+    },
+    // 编辑
+    rowEdit (row, index) {
+      this.tableForm = this.deepClone(row);
+      this.clearDefaultParam();
+      this.$emit('input', this.tableForm);
+      this.tableIndex = index;
+      this.boxType = 'edit';
+      this.show();
+    },
+
+    //查看
+    rowView (row, index) {
+      this.tableForm = this.deepClone(row);
+      this.clearDefaultParam();
+      this.$emit('input', this.tableForm);
+      this.tableIndex = index;
+      this.boxType = 'view';
+      this.keyBtn = true;
+      this.show();
+    },
+    rowCellUpdate (row, index) {
+      const form = this.deepClone(this.tableForm);
+      this.$emit('input', form);
+      this.$emit(
+        'row-update',
+        form,
+        index,
+        () => {
+          row.$cellEdit = !row.$cellEdit;
+          this.tableForm = {};
+          this.tableIndex = -1;
+          this.$set(this.list, index, form);
+        }
+      );
+    },
+    // 删除
+    rowDel (row, index) {
+      this.$emit('row-del', row, index);
+    },
+    // 保存
+    rowSave () {
+      this.$refs['tableForm'].validate().then(() => {
+        this.keyBtn = true;
+        this.$emit('row-save', this.deepClone(this.tableForm), this.closeDialog,
+          () => {
+            this.keyBtn = false;
+          });
+      });
+    },
+    // 更新
+    rowUpdate () {
+      this.$refs['tableForm'].validate().then(() => {
+        this.keyBtn = true;
+        const index = this.tableIndex;
+        this.$emit(
+          'row-update',
+          this.deepClone(this.tableForm),
+          index,
+          this.closeDialog,
+          () => {
+            this.keyBtn = false;
+          }
+        );
+      });
+    },
+    // 显示表单
+    show (cancel) {
+      const callack = () => {
+        if (cancel !== true) {
+          this.$nextTick(() => {
+            this.$refs['tableForm'].clearValidate();
+            this.$refs['tableForm'].cascadeInit();
+          });
+          this.boxVisible = true;
+        }
+      };
+      if (typeof this.beforeOpen === 'function') this.beforeOpen(callack, this.boxType);
+      else callack();
+    },
+    // 隐藏表单
+    hide (cancel) {
+      const callack = () => {
+        if (cancel !== false) {
+          this.$refs['tableForm'].resetForm();
+          this.$refs['tableForm'].clearValidate();
+        }
+      };
+      if (typeof this.beforeClose === 'function') this.beforeClose(callack, this.boxType);
+      else callack();
+    },
+    //清空多余字段
+    clearDefaultParam () {
+      this.defaultParam.forEach(ele => {
+        delete this.tableForm[ele];
+      })
+    },
+    resetForm () {
+      this.$refs['tableForm'].resetForm();
+      this.$emit('input', this.tableForm);
+    }
+
+  }
+});
+</script>

+ 7 - 0
packages/data-box/index.js

@@ -0,0 +1,7 @@
+import DataBox from './src/data-box.vue';
+
+DataBox.install = function(Vue) {
+  Vue.component(DataBox.name, DataBox);
+};
+
+export default DataBox;

+ 49 - 0
packages/data-box/src/data-box.vue

@@ -0,0 +1,49 @@
+<template>
+  <div class="data-box">
+    <el-row :span="24">
+      <el-col :md="span"
+              :xs="24"
+              :sm="12"
+              v-for="(item,index) in data"
+              :key="index">
+        <div class="item">
+          <div class="item-icon"
+               :style="{backgroundColor:item.color}">
+            <i :class="item.icon"></i>
+          </div>
+          <div class="item-info">
+            <div :style="{color:item.color}"
+                 class="title">{{item.count}}</div>
+            <div class="info">{{item.title}}</div>
+          </div>
+        </div>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import create from '../../utils/create';
+export default create({
+  name: 'data-box',
+  data () {
+    return {
+
+    };
+  },
+  props: {
+    option: {
+      type: Object,
+      default: () => { }
+    }
+  },
+  computed: {
+    span () { return this.option.span || 8; },
+    data () { return this.option.data || []; }
+  },
+  created () { },
+  mounted () { },
+  watch: {},
+  methods: {}
+});
+</script>

+ 7 - 0
packages/data-card/index.js

@@ -0,0 +1,7 @@
+import DataCard from './src/data-card.vue';
+
+DataCard.install = function(Vue) {
+  Vue.component(DataCard.name, DataCard);
+};
+
+export default DataCard;

+ 60 - 0
packages/data-card/src/data-card.vue

@@ -0,0 +1,60 @@
+<template>
+  <div class="data-card">
+    <el-row :span="24">
+      <el-col :md="span"
+              :xs="24"
+              :sm="12"
+              v-for="(item,index) in data"
+              :key="index">
+        <div class="item">
+          <img :src="item.src"
+               class="item-img" />
+          <div class="item-text"
+               :style="{backgroundColor:bgText}">
+            <h3 :style="{color:colorText}">{{item.name}}</h3>
+            <p :style="{color:colorText}">{{item.text}}</p>
+          </div>
+        </div>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import create from '../../utils/create';
+export default create({
+  name: 'data-card',
+  data () {
+    return {
+
+    };
+  },
+  props: {
+    option: {
+      type: Object,
+      default: () => { }
+    }
+  },
+  computed: {
+    span () {
+      return this.option.span || 6;
+    },
+    data () {
+      return this.option.data || [];
+    },
+    colorText () {
+      return this.option.colorText || '#fff';
+    },
+    bgText () {
+      return this.option.bgText || '#2e323f';
+    },
+    borderColor () {
+      return this.option.borderColor || '#2e323f';
+    }
+  },
+  created () { },
+  mounted () { },
+  watch: {},
+  methods: {}
+});
+</script>

+ 7 - 0
packages/data-display/index.js

@@ -0,0 +1,7 @@
+import DataDisplay from './src/data-display.vue';
+
+DataDisplay.install = function(Vue) {
+  Vue.component(DataDisplay.name, DataDisplay);
+};
+
+export default DataDisplay;

+ 50 - 0
packages/data-display/src/data-display.vue

@@ -0,0 +1,50 @@
+<template>
+  <div class="avue-data-display">
+    <el-row :span="24">
+      <el-col v-for="(item,index) in data"
+              :key="index"
+              :md="span"
+              :xs="12"
+              :sm="12">
+        <div class="item"
+             :style="{color:color}">
+          <h5 class="count">{{item.count}}</h5>
+          <span class="splitLine" />
+          <p class="title">{{item.title}}</p>
+        </div>
+      </el-col>
+    </el-row>
+
+  </div>
+</template>
+
+<script>
+import create from '../../utils/create';
+export default create({
+  name: 'data-display',
+  data () {
+    return {
+
+    };
+  },
+  computed: {
+    span () {
+      return this.option.span || 6;
+    },
+    data () {
+      return this.option.data || [];
+    },
+    color () {
+      return this.option.color || 'rgb(63, 161, 255)';
+    }
+  },
+  props: {
+    option: {
+      type: Object,
+      default: () => { }
+    }
+  },
+  created () { },
+  methods: {}
+});
+</script>

+ 7 - 0
packages/data-icons/index.js

@@ -0,0 +1,7 @@
+import DataIcons from './src/data-icons.vue';
+
+DataIcons.install = function(Vue) {
+  Vue.component(DataIcons.name, DataIcons);
+};
+
+export default DataIcons;

+ 49 - 0
packages/data-icons/src/data-icons.vue

@@ -0,0 +1,49 @@
+<template>
+  <div class="data-icons">
+    <el-row :span="24">
+      <template v-for="(item,index) in data">
+        <el-col :xs="12"
+                :sm="6"
+                :md="span"
+                :key="index">
+          <div class="item"
+               :class="[{'item--easy':discount}]">
+            <div class="item-icon"
+                 :style="{color:color}">
+              <i :class="item.icon"></i>
+            </div>
+            <div class="item-info">
+              <span>{{item.title}}</span>
+              <div class="count"
+                   :style="{color:color}">{{item.count}}</div>
+            </div>
+          </div>
+        </el-col>
+      </template>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import create from '../../utils/create';
+export default create({
+  name: 'data-icons',
+  data () {
+    return {
+
+    };
+  },
+  computed: {
+    span () { return this.option.span || 4; },
+    data () { return this.option.data; },
+    color () { return this.option.color || 'rgb(63, 161, 255)'; },
+    discount () { return this.option.discount || false; }
+  },
+  props: {
+    option: {
+      type: Object,
+      default: () => { }
+    }
+  }
+});
+</script>

+ 7 - 0
packages/data-tabs/index.js

@@ -0,0 +1,7 @@
+import DataTabs from './src/data-tabs.vue';
+
+DataTabs.install = function(Vue) {
+  Vue.component(DataTabs.name, DataTabs);
+};
+
+export default DataTabs;

+ 53 - 0
packages/data-tabs/src/data-tabs.vue

@@ -0,0 +1,53 @@
+<template>
+  <div class="data-tabs">
+    <el-row :span="24">
+      <el-col :md="span"
+              :xs="24"
+              :sm="12"
+              v-for="(item,index) in data"
+              :key="index">
+        <div class="item"
+             :style="{background:item.color}">
+          <div class="item-header">
+            <p>{{item.title}}</p>
+            <span>{{item.subtitle}}</span>
+          </div>
+          <div class="item-body">
+            <h2>{{item.count}}</h2>
+          </div>
+          <div class="item-footer">
+            <span>{{item.allcount}}</span>
+            <p>{{item.text}}</p>
+          </div>
+          <p class="item-tip">{{item.key}}</p>
+        </div>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import create from '../../utils/create';
+export default create({
+  name: 'data-tabs',
+  data () {
+    return {
+
+    };
+  },
+  computed: {
+    span () {
+      return this.option.span || 8;
+    },
+    data () {
+      return this.option.data || [];
+    }
+  },
+  props: {
+    option: {
+      type: Object,
+      default: () => { }
+    }
+  }
+});
+</script>

+ 7 - 0
packages/date-select/index.js

@@ -0,0 +1,7 @@
+import DateSelect from './src/main';
+
+DateSelect.install = function(Vue) {
+    Vue.component(DateSelect.name, DateSelect);
+};
+
+export default DateSelect;

+ 91 - 0
packages/date-select/src/main.vue

@@ -0,0 +1,91 @@
+<template>
+  <div :class="b()">
+    <div :class="b('radio')">
+      <el-radio-group :size="size"
+                      @change="handleChange"
+                      v-model="text">
+        <el-radio-button :label="item.value"
+                         v-for="(item,index) in menu"
+                         :key="index">{{item.label}}</el-radio-button>
+      </el-radio-group>
+    </div>
+    <div :class="b('date')">
+      <el-date-picker v-model="datetime"
+                      type="daterange"
+                      :size="size"
+                      format="yyyy-MM-dd"
+                      value-format="yyyy-MM-dd"
+                      range-separator="至"
+                      start-placeholder="开始日期"
+                      end-placeholder="结束日期">
+      </el-date-picker>
+    </div>
+  </div>
+</template>
+
+<script>
+import create from '../../utils/create';
+import { GetDateStr } from '../../utils/dateUtil.js';
+export default create({
+  name: 'date-select',
+  data () {
+    return {
+      menu: [],
+      text: '',
+      datetime: [GetDateStr(0), GetDateStr(30)]
+    };
+  },
+  watch: {
+    datetime () {
+      this.text = '';
+      this.setCurrent(this.datetime.join(','));
+    },
+  },
+  computed: {
+
+  },
+  props: {
+    default: {
+      type: Boolean,
+      default: false
+    },
+    size: {
+      type: String,
+      default: 'medium'
+    }
+  },
+  created () {
+    this.init();
+  },
+  methods: {
+    handleChange (val) {
+      this.setCurrent(val);
+    },
+    setCurrent (val) {
+      this.$emit('input', val);
+      this.$emit('change', val);
+    },
+    init () {
+      this.menu = [
+        {
+          label: '今天',
+          value: GetDateStr(0)
+        }, {
+          label: '昨天',
+          value: GetDateStr(-1)
+        }, {
+          label: '近7天',
+          value: GetDateStr(-7)
+        }, {
+          label: '全部',
+          value: '-1'
+        }
+      ];
+      if (this.default) {
+        this.text = GetDateStr(0);
+        this.setCurrent(this.text);
+      }
+    }
+  }
+});
+</script>

+ 7 - 0
packages/form-detail/index.js

@@ -0,0 +1,7 @@
+import FormDetail from './src/main';
+
+FormDetail.install = function(Vue) {
+  Vue.component(FormDetail.name, FormDetail);
+};
+
+export default FormDetail;

+ 111 - 0
packages/form-detail/src/main.vue

@@ -0,0 +1,111 @@
+<template>
+  <div :class="b()">
+    <el-row span="24"
+            :class="b('item')"
+            v-for="(item,index) in columnOption"
+            :key="index">
+      <div :class="b('header')">
+        <i :class="[item.icon,b('icon')]"></i>
+        <h1 :class="b('title')">{{item.label}}</h1>
+      </div>
+      <slot :name="item.prop"
+            :form="form"
+            :column="item"
+            v-if="item.slot"></slot>
+      <el-col :md="column.span||8"
+              :xs="24"
+              v-else
+              v-for="(column,cindex) in item.column"
+              :key="cindex">
+        <div :class="b('box')">
+          <span :class="b('label',[labelPostion])"
+                :style="{width:`${item.labelWidth}px`}">{{column.label}}:</span>
+          <slot :name="column.prop+'Form'"
+                :column="column"
+                :form="form"
+                v-if="column.slot"></slot>
+          <span v-else
+                :class="b('content')">{{detail(form,column)}}</span>
+        </div>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+
+import create from '../../utils/create';
+import crud from '../../mixins/crud.js';
+import column from '../../mixins/column.js';
+import { validatenull } from '../../utils/validate.js';
+export default create({
+  name: 'form-detail',
+  mixins: [crud(), column()],
+  props: {
+    option: {
+      type: Object,
+      default: () => { }
+    },
+    value: {}
+  },
+  computed: {
+    labelPostion: function () {
+      if (this.option.labelPostion) {
+        return this.option.labelPostion;
+      }
+      return 'left';
+    },
+    columnOption () {
+      return this.option.option || [];
+    }
+  },
+  watch: {
+    value: {
+      handler (n) {
+        this.form = n;
+      },
+      deep: true
+    }
+  },
+  data () {
+    return {
+      tableOption: {},
+      form: {}
+    };
+  },
+  created () {
+    this.formInit();
+  },
+  methods: {
+    dicInit () {
+      let locaDic = this.tableOption.dicData || {};
+      this.columnOption.forEach(child => {
+        child.column.forEach(ele => {
+          if (this.vaildData(ele.dicFlag, true)) {
+            if (!validatenull(ele.dicUrl)) {
+              this.dicCascaderList.push({
+                dicUrl: ele.dicUrl,
+                dicData: ele.dicData
+              });
+            } else if (!validatenull(this.tableOption.dicUrl) && typeof ele.dicData === 'string') {
+              this.dicCascaderList.push({
+                dicUrl: this.tableOption.dicUrl,
+                dicData: ele.dicData
+              });
+            }
+          }
+        })
+      });
+      this.GetDic().then(data => {
+        this.DIC = Object.assign({}, locaDic, data);
+      });
+    },
+    rulesInit () {
+
+    },
+    formInit () {
+      this.form = this.value;
+    }
+  }
+});
+</script>

+ 7 - 0
packages/form-steps/index.js

@@ -0,0 +1,7 @@
+import FormSteps from './src/main';
+
+FormSteps.install = function(Vue) {
+  Vue.component(FormSteps.name, FormSteps);
+};
+
+export default FormSteps;

+ 151 - 0
packages/form-steps/src/main.vue

@@ -0,0 +1,151 @@
+<template>
+  <div :class="b()">
+    <el-steps :active="formIndex"
+              :space="option.space"
+              :simple="option.simple"
+              :process-status="option.processStatus"
+              :align-center='vaildData(option.alignCenter,true)'
+              :direction="option.direction"
+              :class="b({'steps':option.direction})">
+      <el-step :title="item.label"
+               :icon="item.icon"
+               :status="vaildData(item.status,status[index])"
+               :description="item.description"
+               v-for="(item,index) in columnOption"
+               :key="index"
+               @click.native="option.switchBtn?switchs(index):''"></el-step>
+    </el-steps>
+    <div :class="b('contail')">
+      <slot name="before"></slot>
+      <avue-form :option="formOption"
+                 :class="b('form')"
+                 :style="{width:vaildData(formOption.width,'40%')}"
+                 @submit="submit"
+                 v-model="text">
+        <template slot-scope="scope"
+                  v-for="item in formOption.column"
+                  :slot="item.prop">
+          <slot :value="scope.value"
+                :column="scope.column"
+                :dic="scope.dic"
+                :name="item.prop"
+                v-if="item.formsolt"></slot>
+        </template>
+        <template slot="menuForm">
+          <el-button @click="breaks"
+                     v-if="formIndex!=1&&formIndex!=columnLen&&vaildData(option.breakBtn,true)">返回</el-button>
+          <slot name="menuForm"></slot>
+        </template>
+      </avue-form>
+      <slot name="after"></slot>
+    </div>
+  </div>
+</template>
+
+<script>
+import create from '../../utils/create';
+import { formInitVal } from '../../utils/util';
+export default create({
+  name: 'form-steps',
+  props: {
+    value: {
+      type: Object,
+      default: () => { }
+    },
+    option: {
+      type: Object,
+      required: true
+    }
+  },
+  computed: {
+    columnOption () {
+      return this.option.column || [];
+    },
+    columnLen () {
+      return this.columnOption.length;
+    },
+    formOption () {
+      return this.objectOption.option;
+    },
+    objectOption () {
+      return this.columnOption[this.formIndex - 1];
+    },
+    status () {
+      let status = [];
+      const leng = this.step - 1;
+      for (let i = 0; i < leng; i++) {
+        status.push(this.vaildData(this.option.finishStatus, 'success'));
+      }
+      return status;
+    }
+
+  },
+  watch: {
+    formOption () {
+      this.formInit();
+      this.$emit('change', this.objectOption);
+    },
+    text: {
+      handler () {
+        for (let o in this.tableForm) {
+          this.tableForm[o] = this.text[o];
+        }
+      },
+      deep: true
+    },
+    value: {
+      handler () {
+        this.formVal();
+      },
+      deep: true
+    }
+  },
+  data () {
+    return {
+      step: 1,
+      tableForm: {},
+      text: {},
+      formIndex: 1
+    };
+  },
+  created () {
+    this.formInit();
+  },
+  methods: {
+    reset () {
+      this.formIndex = 1;
+      this.step = 1;
+    },
+    switchs (index) {
+      if (index < this.step) { this.formIndex = index + 1; }
+    },
+    next () {
+      if (this.step <= this.formIndex) {
+        this.step++;
+      }
+      this.formIndex++;
+    },
+    breaks () {
+      this.formIndex--;
+    },
+    formInit () {
+      const column = this.formOption.column;
+      this.tableForm = formInitVal(column).tableForm;
+      this.formVal();
+    },
+    formVal () {
+      for (let o in this.value) {
+        this.text[o] = this.value[o];
+      }
+      for (let o in this.tableForm) {
+        this.tableForm[o] = this.text[o];
+      }
+      this.$emit('input', this.tableForm);
+    },
+    submit () {
+      this.$emit('submit', this.tableForm, this.next);
+    }
+  }
+});
+</script>
+

+ 7 - 0
packages/form-tabs/index.js

@@ -0,0 +1,7 @@
+import FormTabs from './src/main';
+
+FormTabs.install = function(Vue) {
+  Vue.component(FormTabs.name, FormTabs);
+};
+
+export default FormTabs;

+ 119 - 0
packages/form-tabs/src/main.vue

@@ -0,0 +1,119 @@
+<template>
+  <div class="b()">
+    <el-tabs v-model="formIndex"
+             :type="option.type"
+             :tab-position="option.tabPosition">
+      <el-tab-pane v-for="(item,index) in columnOption"
+                   :key="index"
+                   :disabled="item.disabled"
+                   :name="index+''">
+        <span slot="label">
+          <i :class="item.icon"></i> {{item.label}}</span>
+      </el-tab-pane>
+    </el-tabs>
+    <div>
+      <slot name="before"></slot>
+      <avue-form :option="formOption"
+                 @submit="submit"
+                 v-model="text">
+        <template slot-scope="scope"
+                  v-for="item in formColumnOption"
+                  :slot="item.prop">
+          <slot :value="scope.value"
+                :column="scope.column"
+                :dic="scope.dic"
+                :name="item.prop"
+                v-if="item.formsolt"></slot>
+        </template>
+        <template slot="menuForm">
+          <slot name="menuForm"></slot>
+        </template>
+      </avue-form>
+      <slot name="after"></slot>
+    </div>
+  </div>
+</template>
+
+<script>
+import create from '../../utils/create';
+import { formInitVal } from '../../utils/util';
+export default create({
+  name: 'form-tabs',
+  props: {
+    value: {
+      type: Object,
+      default: () => { }
+    },
+    option: {
+      type: Object,
+      required: true
+    }
+  },
+  computed: {
+    columnOption () {
+      return this.option.column || [];
+    },
+    columnLen () {
+      return this.columnOption.length;
+    },
+    formOption () {
+      return this.objectOption.option;
+    },
+    formColumnOption () {
+      return this.formOption.column || [];
+    },
+    objectOption () {
+      return this.columnOption[this.formIndex];
+    }
+  },
+  watch: {
+    formOption () {
+      this.formInit();
+      this.$emit('change', this.columnOption[this.formIndex]);
+    },
+    text: {
+      handler () {
+        for (let o in this.tableForm) {
+          this.tableForm[o] = this.text[o];
+        }
+      },
+      deep: true
+    },
+    value: {
+      handler () {
+        this.formVal();
+      },
+      deep: true
+    }
+  },
+  data () {
+    return {
+      tableForm: {},
+      text: {},
+      formIndex: '0'
+    };
+  },
+  created () {
+    this.formInit();
+  },
+  methods: {
+    formInit () {
+      const column = this.formOption.column;
+      this.tableForm = formInitVal(column).tableForm;
+      this.formVal();
+    },
+    formVal () {
+      for (let o in this.value) {
+        this.text[o] = this.value[o];
+      }
+      for (let o in this.tableForm) {
+        this.tableForm[o] = this.text[o];
+      }
+      this.$emit('input', this.tableForm);
+    },
+    submit () {
+      this.$emit('submit', this.tableForm);
+    }
+  }
+});
+</script>

+ 7 - 0
packages/form/index.js

@@ -0,0 +1,7 @@
+import Form from './src/main';
+
+Form.install = function(Vue) {
+  Vue.component(Form.name, Form);
+};
+
+export default Form;

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

@@ -0,0 +1,361 @@
+<template>
+  <div :class="b()">
+    <el-form ref="form"
+             :model="form"
+             :label-position="tableOption.labelPosition"
+             :size="tableOption.size"
+             :label-width="setPx(tableOption.labelWidth,80)"
+             :rules="formRules">
+      <el-row :gutter="20"
+              :span="24">
+        <draggable :list="columnOption"
+                   :options="dragOptions"
+                   :class="b('group')">
+          <div :class="b('row',{'block':column.row,'cursor':draggableStart})"
+               v-for="(column,index) in columnOption"
+               v-if="vaildVisdiplay(column)"
+               :key="index">
+            <el-col :md="column.span||12"
+                    :xs="24"
+                    @mouseover.native="draggableMenu?mouseover(index):''"
+                    @mouseout.native="draggableMenu?mouseout(index):''">
+              <div :class="b('option')"
+                   v-if="optionIndex[index]">
+                <i class="el-icon-menu"
+                   @click="optionMenu(column,index)"></i>
+                <i class="el-icon-delete"
+                   @click="optionDelete(column,index)"></i>
+              </div>
+              <el-form-item :label="column.label"
+                            :prop="column.prop"
+                            :label-width="setPx(column.labelWidth,tableOption.labelWidth || 80)">
+                <slot :value="form[column.prop]"
+                      :column="column"
+                      :dic="setDic(column.dicData,DIC[column.dicData])"
+                      :name="column.prop"
+                      v-if="column.formsolt"></slot>
+
+                <component :is="getComponent({type:column.type,component:column.component})"
+                           v-else
+                           :props="column.props || tableOption.props"
+                           v-model="form[column.prop]"
+                           :change="column.change"
+                           :click="column.click"
+                           :column="column"
+                           :filter="column.filter"
+                           :precision="column.precision"
+                           :multiple="column.multiple"
+                           :readonly="vaildData(draggableStart,column.readonly)"
+                           :placeholder="column.placeholder"
+                           :step="column.step"
+                           :range="column.range"
+                           :showStops="column.showStops"
+                           :showInput="column.showInput"
+                           :controls-position="column.controlsPosition"
+                           :expand-trigger="column.expandTrigger"
+                           :size="column.size"
+                           :parent="column.parent"
+                           :colors="column.colors"
+                           :action="column.action"
+                           :limit="column.limit"
+                           :tip="column.tip"
+                           :loadText="column.loadText"
+                           :listType="column.listType"
+                           :drag="column.drag"
+                           :showFileList="column.showFileList"
+                           :iconClasses="column.iconClasses"
+                           :voidIconClass="column.voidIconClass"
+                           :showText="column.showText"
+                           :texts="column.texts"
+                           :filterable="column.filterable"
+                           :separator="column.separator"
+                           :border="column.border"
+                           :minlength="column.minlength"
+                           :maxlength="column.maxlength"
+                           :prefixIcon="column.prefixIcon"
+                           :suffixIcon="column.suffixIcon"
+                           :options="column.options"
+                           :pickerOptions="column.pickerOptions"
+                           :defaultTime="column.defaultTime"
+                           :min="column.min"
+                           :max="column.max"
+                           :changeoOnSelect="column.changeoOnSelect"
+                           :label="column.label"
+                           :clearable="column.clearable"
+                           :startPlaceholder="column.startPlaceholder"
+                           :endPlaceholder="column.endPlaceholder"
+                           :type="column.type"
+                           :minRows="column.minRows"
+                           :maxRows="column.maxRows"
+                           :format="column.format"
+                           :formatTooltip="column.formatTooltip"
+                           :value-format="column.valueFormat"
+                           :dic="setDic(column.dicData,DIC[column.dicData])"
+                           :disabled="vaildDisabled(column)"
+                           :upload-before="uploadBefore"
+                           :upload-after="uploadAfter"
+                           @change="column.cascader?change(index):''"></component>
+                <!-- <p class="avue-tip">{{column.tip}}</p> -->
+              </el-form-item>
+            </el-col>
+          </div>
+        </draggable>
+        <el-col :span="24"
+                v-if="vaildData(tableOption.menuBtn,true)">
+          <el-form-item :label-width="menuWidth">
+            <div :class="b('menu',[menuPostion])">
+              <el-button type="primary"
+                         @click="submit"
+                         :size="tableOption.submitSize"
+                         icon="el-icon-check"
+                         v-if="vaildData(tableOption.submitBtn,true)">{{vaildData(tableOption.submitText,'提 交')}}</el-button>
+              <el-button icon="el-icon-delete"
+                         :size="tableOption.emptySize"
+                         v-if="vaildData(tableOption.emptytBtn,true)"
+                         @click="resetForm">{{vaildData(tableOption.emptyText,'清 空')}}</el-button>
+              <slot name="menuForm"></slot>
+            </div>
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+  </div>
+</template>
+
+<script>
+
+import create from '../../utils/create';
+import draggable from "vuedraggable";
+import crud from '../../mixins/crud';
+import { validatenull } from '../../utils/validate.js';
+export default create({
+  name: 'form',
+  mixins: [crud()],
+  components: {
+    draggable
+  },
+  data () {
+    return {
+      first: true,
+      optionIndex: [],
+      optionBox: false,
+      tableOption: {},
+      form: {},
+      formDefault: {},
+      formRules: {}
+    };
+  },
+  created () {
+  },
+  mounted () { },
+  computed: {
+    columnOption () {
+      return this.tableOption.column || [];
+    },
+    draggable () {
+      return this.tableOption.draggable || {};
+    },
+    draggableMenu () {
+      return this.draggable.menu || false;
+    },
+    draggableStart () {
+      return this.draggable.start;
+    },
+    draggableFlag () {
+      return this.vaildData(this.draggable.clone, true)
+    },
+    dragOptions () {
+      if (!this.draggableStart) {
+        return {
+          disabled: true,
+        }
+      }
+      let pull = (() => {
+        if (this.draggableFlag) {
+          return {
+            pull: 'clone',
+            revertClone: false
+          }
+        }
+        return {
+
+        }
+      })();
+      return {
+        animation: 0,
+        ghostClass: "avue-ghost",
+        group: (function () {
+          return Object.assign({ name: "avue" }, pull);
+        })(),
+        sort: this.vaildData(this.draggable.sort, false),
+      };
+    },
+    menuWidth: function () {
+      if (this.tableOption.submitPostion === 'left') {
+        return '';
+      } else {
+        return '0';
+      }
+    },
+    menuPostion: function () {
+      if (this.tableOption.submitPostion) {
+        return this.tableOption.submitPostion;
+      } else {
+        return 'center';
+      }
+    },
+    boxType: function () {
+      return this.tableOption.boxType;
+    }
+  },
+  props: {
+    disabled: {
+      type: Boolean,
+      default: false
+    },
+    uploadBefore: Function,
+    uploadAfter: Function,
+    value: {
+      type: Object,
+      required: true,
+      default: () => {
+        return {};
+      }
+    }
+  },
+  methods: {
+    optionDelete (column, index) {
+      this.$emit('option-delete', { column, index })
+    },
+    optionMenu (column, index) {
+      this.$emit('option-menu', { column, index })
+    },
+    mouseover (index) {
+      this.optionIndex[index] = true;
+      this.optionIndex.splice(0, 0);
+    },
+    mouseout (index) {
+      this.optionIndex[index] = false;
+      this.optionIndex.splice(0, 0);
+    },
+    // 验证表单是否禁止
+    vaildDisabled (column) {
+      if (this.disabled) return true;
+      if (!validatenull(column.disabled)) {
+        return this.vaildData(column.disabled, false);
+      } else if (this.boxType === 'add') {
+        return this.vaildData(column.addDisabled, false);
+      } else if (this.boxType === 'edit') {
+        return this.vaildData(column.editDisabled, false);
+      } else if (this.boxType === 'view') {
+        return true;
+      } else {
+        return false;
+      }
+    },
+    // 验证表单是否显隐
+    vaildVisdiplay (column) {
+      if (!validatenull(column.visdiplay)) {
+        return this.vaildData(column.visdiplay, true);
+      } else if (this.boxType === 'add') {
+        return this.vaildData(column.addVisdiplay, true);
+      } else if (this.boxType === 'edit') {
+        return this.vaildData(column.editVisdiplay, true);
+      } else if (this.boxType === 'view') {
+        return this.vaildData(column.viewVisdiplay, true);
+      } else {
+        return true;
+      }
+
+    },
+    rulesInit () {
+      this.formRules = {};
+      this.columnOption.forEach(ele => {
+        if (ele.rules) this.formRules[ele.prop] = ele.rules;
+      });
+    },
+    change (index) {
+      const column = this.columnOption;
+      const list = column[index].cascader;
+      const prop = column[index].prop;
+      const url = column[index + 1].dicUrl;
+      const type = column[index + 1].dicData;
+      if (!this.first) {
+        list.forEach((ele) => {
+          this.form[ele] = '';
+          this.DIC[ele] = [];
+          this.DIC = Object.assign({}, this.DIC);
+        });
+      }
+      this.GetDicByType(url.replace('{{key}}', this.form[prop])).then(res => {
+        let data = res;
+        this.DIC[type] = data;
+        this.DIC = Object.assign({}, this.DIC);
+      });
+    },
+    formInit () {
+      this.formDefault = this.formInitVal(this.columnOption);
+      this.form = this.deepClone(this.formDefault).tableForm;
+      this.formVal();
+      const dicFlag = this.vaildData(this.tableOption.dicFlag, true);
+      //初始化联动
+      if (dicFlag) this.cascadeInit();
+    },
+    cascadeInit () {
+      this.first = true;
+      for (let i = 0; i < this.columnOption.length; i++) {
+        const ele = this.columnOption[i];
+        if (ele.cascaderFirst) {
+          const cascader = [].concat(ele.cascader);
+          const cascaderLen = ele.cascader.length - 1;
+          cascader.forEach((ele) => {
+            this.DIC[ele] = [];
+            this.DIC = Object.assign({}, this.DIC);
+          });
+          if (!validatenull(this.form[ele.prop])) this.change(i);
+          for (let j = 0; j < cascaderLen; j++) {
+            const cindex = i + (j + 1);
+            const cele = this.columnOption[cindex];
+            cele.cascader = cascader.slice(cindex);
+            if (!validatenull(this.form[cele.prop])) this.change(cindex);
+          }
+        }
+      }
+      this.first = false;
+    },
+    formVal () {
+      Object.keys(this.value).forEach(ele => {
+        this.form[ele] = this.value[ele];
+      })
+      this.$emit('input', this.form);
+    },
+    clearValidate () {
+      this.$refs['form'].clearValidate();
+    },
+    validate () {
+      return new Promise((resolve, reject) => {
+        this.$refs.form.validate(valid => {
+          if (valid) {
+            resolve();
+          } else {
+            reject();
+          }
+        });
+      });
+    },
+    resetForm () {
+      this.form = this.deepClone(this.formDefault).tableForm;
+      this.$emit('input', this.form);
+      this.$emit('reset-change');
+    },
+    submit () {
+      this.$refs['form'].validate(valid => {
+        if (valid) {
+          this.$emit('submit', this.form);
+        }
+      });
+    }
+  }
+});
+</script>
+

+ 66 - 0
packages/index.js

@@ -0,0 +1,66 @@
+import Crud from './crud/index.js';
+import CrudCheckbox from './crud-checkbox/index.js';
+import CrudDate from './crud-date/index.js';
+import CrudTime from './crud-time/index.js';
+import CrudInput from './crud-input/index.js';
+import CrudRadio from './crud-radio/index.js';
+import CrudSelect from './crud-select/index.js';
+import CrudCascader from './crud-cascader/index.js';
+import CrudNumberInput from './crud-input-number/index.js';
+import CrudSwitch from './crud-switch/index.js';
+import CrudRate from './crud-rate/index.js';
+import CrudUpload from './crud-upload/index.js';
+import CrudSilder from './crud-silder/index.js';
+import DataDisplay from './data-display/index.js';
+import DataCard from './data-card/index.js';
+import DataTabs from './data-tabs/index.js';
+import DataIcons from './data-icons/index.js';
+import DataBox from './data-box/index.js';
+import Form from './form/index.js';
+import FormTabs from './form-tabs/index.js';
+import FormSteps from './form-steps/index.js';
+import FormDetail from './form-detail/index.js';
+import TableTree from './table-tree/index.js';
+import DateSelect from './date-select/index.js';
+const components = [
+    Crud,
+    Form,
+    FormTabs,
+    FormSteps,
+    FormDetail,
+    CrudCheckbox,
+    CrudDate,
+    CrudTime,
+    CrudInput,
+    CrudRadio,
+    CrudSelect,
+    CrudCascader,
+    CrudNumberInput,
+    CrudSwitch,
+    CrudRate,
+    CrudUpload,
+    CrudSilder,
+    DataDisplay,
+    DataCard,
+    DataIcons,
+    DataTabs,
+    DataBox,
+    TableTree,
+    DateSelect
+];
+
+function install(Vue, axios) {
+    Vue.prototype.$http = axios;
+    const AVUE = {};
+    components.map(component => {
+        Vue.component(component.name, component);
+    });
+
+    Vue.prototype.$AVUE = AVUE;
+}
+
+if (window.Vue) {
+    install(window.Vue, window.axios);
+}
+
+export default install;

+ 45 - 0
packages/mixins/bem.js

@@ -0,0 +1,45 @@
+/**
+ * bem helper
+ * b() // 'button'
+ * b('text') // 'button__text'
+ * b({ disabled }) // 'button button--disabled'
+ * b('text', { disabled }) // 'button__text button__text--disabled'
+ * b(['disabled', 'primary']) // 'button button--disabled button--primary'
+ */
+
+const ELEMENT = '__';
+const MODS = '--';
+
+const join = (name, el, symbol) => el ? name + symbol + el : name;
+
+const prefix = (name, mods) => {
+    if (typeof mods === 'string') {
+        return join(name, mods, MODS);
+    }
+
+    if (Array.isArray(mods)) {
+        return mods.map(item => prefix(name, item));
+    }
+
+    const ret = {};
+    Object.keys(mods).forEach(key => {
+        ret[name + MODS + key] = mods[key];
+    });
+    return ret;
+};
+
+export default {
+    methods: {
+        b(el, mods) {
+            const { name } = this.$options;
+
+            if (el && typeof el !== 'string') {
+                mods = el;
+                el = '';
+            }
+            el = join(name, el, ELEMENT);
+
+            return mods ? [el, prefix(el, mods)] : el;
+        }
+    }
+};

+ 39 - 0
packages/mixins/column.js

@@ -0,0 +1,39 @@
+import * as utils from '../utils/util.js';
+import dayjs from 'dayjs';
+export default function() {
+    return {
+        methods: {
+            initFun() {
+                Object.keys(utils).forEach(key => {
+                    this[key] = utils[key];
+                });
+            },
+            cellEditFlag(row, column) {
+                return row.$cellEdit && [undefined, 'select', 'input'].includes(column.type) && column.solt !== true && column.cell;
+            },
+            // 处理数据
+            detail(row, column) {
+                let result = row[column.prop || column.value] || '';
+                if (column.type) {
+                    if (['date', 'time', 'datetime'].includes(column.type) && column.format) {
+                        const format = column.format
+                            .replace('dd', 'DD')
+                            .replace('yyyy', 'YYYY');
+                        result = dayjs(result).format(format);
+                    }
+                    if (column.dicData) {
+                        result = this.findByvalue(
+                            typeof column.dicData === 'string' ? this.DIC[column.dicData] : column.dicData,
+                            result,
+                            (column.props || this.tableOption.props)
+                        );
+                    }
+                }
+                if (column.formatter && typeof column.formatter === 'function') {
+                    result = column.formatter(row, row[column.prop], result, column);
+                }
+                return result;
+            },
+        }
+    }
+}

+ 101 - 0
packages/mixins/crud-compoents.js

@@ -0,0 +1,101 @@
+export default function() {
+    //props配置
+    const propsDefault = {
+        id: 'id',
+        label: 'label',
+        value: 'value',
+        children: 'children',
+        disabled: 'disabled'
+    }
+    return {
+        data() {
+            return {
+                text: undefined,
+                propsDefault: propsDefault
+            }
+        },
+        props: {
+            change: Function,
+            click: Function,
+            column: {
+                type: Object,
+                default: () => {}
+            },
+            label: {
+                type: String,
+                default: ''
+            },
+            readonly: {
+                type: Boolean,
+                default: false
+            },
+            size: {
+                type: String,
+                default: ''
+            },
+            tip: {
+                type: String,
+                default: ''
+            },
+            disabled: {
+                type: Boolean,
+                default: false
+            },
+            clearable: {
+                type: Boolean,
+                default: true
+            },
+            type: {
+                type: String,
+                default: ''
+            },
+            dic: {
+                type: Array,
+                default: () => []
+            },
+            placeholder: {
+                type: String,
+                default: ''
+            },
+            min: {
+                type: Number
+            },
+            max: {
+                type: Number
+            },
+            border: {
+                type: Boolean,
+                default: false
+            },
+            props: {
+                type: Object,
+                default: () => propsDefault
+            }
+        },
+        watch: {
+            value() {
+                this.text = this.value;
+            }
+        },
+        computed: {
+            valueKey: function() {
+                return this.props.value || this.propsDefault.value;
+            },
+            labelKey: function() {
+                return this.props.label || this.propsDefault.label;
+            },
+            childrenKey: function() {
+                return this.props.children || this.propsDefault.children;
+            },
+            disabledKey: function() {
+                return this.props.disabled || this.propsDefault.disabled;
+            },
+            idKey: function() {
+                return this.props.id || this.propsDefault.id;
+            }
+        },
+        created() {
+            this.text = this.value;
+        }
+    };
+}

+ 164 - 0
packages/mixins/crud.js

@@ -0,0 +1,164 @@
+import * as utils from '../utils/util.js';
+import { validatenull } from '../utils/validate.js';
+import crudInput from '../crud/src/crud-input';
+import crudSelect from '../crud/src/crud-select';
+import crudRadio from '../crud/src/crud-radio';
+import crudCheckbox from '../crud/src/crud-checkbox';
+import crudCascader from '../crud/src/crud-cascader';
+import crudDate from '../crud/src/crud-date';
+import crudTime from '../crud/src/crud-time';
+import crudInputNumber from '../crud/src/crud-input-number';
+import crudSwitch from '../crud/src/crud-switch';
+import crudRate from '../crud/src/crud-rate';
+import crudUpload from '../crud/src/crud-upload';
+import crudSilder from '../crud/src/crud-silder';
+export default function() {
+    return {
+        props: {
+            option: {
+                type: Object,
+                required: true,
+                default: () => {
+                    return {};
+                }
+            }
+        },
+        components: {
+            crudInput,
+            crudSelect,
+            crudRadio,
+            crudCheckbox,
+            crudDate,
+            crudTime,
+            crudCascader,
+            crudInputNumber,
+            crudSwitch,
+            crudRate,
+            crudUpload,
+            crudSilder
+        },
+        watch: {
+            tableForm: {
+                handler() {
+                    this.$emit('input', this.tableForm);
+                },
+                deep: true
+            },
+            form: {
+                handler() {
+                    this.$emit('input', this.form);
+                },
+                deep: true
+            },
+            value: {
+                handler() {
+                    this.formVal();
+                },
+                deep: true
+            },
+            option: {
+                handler() {
+                    this.init();
+                },
+                deep: true
+            }
+        },
+        data() {
+            return {
+                DIC: {},
+                dicCascaderList: []
+            };
+        },
+        created() {
+            this.init();
+        },
+        methods: {
+            init() {
+                // 初始化工具
+                this.initFun();
+                this.tableOption = this.deepClone(this.option);
+                const dicFlag = this.vaildData(this.tableOption.dicFlag, true);
+                // 规则初始化
+                this.rulesInit();
+
+                // 初始化字典
+                if (dicFlag) this.dicInit();
+                else this.DIC = this.tableOption.dicData;
+
+                // 初始化表单formInitVal
+                this.formInit();
+
+
+            },
+            dicInit() {
+                let locaDic = this.tableOption.dicData || {};
+                this.columnOption.forEach(ele => {
+                    if (this.vaildData(ele.dicFlag, true)) {
+                        if (!validatenull(ele.dicUrl)) {
+                            this.dicCascaderList.push({
+                                dicUrl: ele.dicUrl,
+                                dicData: ele.dicData
+                            });
+                        } else if (!validatenull(this.tableOption.dicUrl) && typeof ele.dicData === 'string') {
+                            this.dicCascaderList.push({
+                                dicUrl: this.tableOption.dicUrl,
+                                dicData: ele.dicData
+                            });
+                        }
+                    }
+                });
+                this.GetDic().then(data => {
+                    this.DIC = Object.assign({}, locaDic, data);
+                });
+            },
+            vaildData(val, dafult) {
+                if (typeof val === 'boolean') {
+                    return val;
+                }
+                return !validatenull(val) ? val : dafult;
+            },
+            GetDicByType(href) {
+                return new Promise((resolve) => {
+                    this.$http.get(href).then(function(res) {
+                        // 降级处理
+                        const list = res.data;
+                        if (!validatenull(list.data)) {
+                            resolve(list.data instanceof Array ? list.data : []);
+                        } else if (!validatenull(list)) {
+                            resolve(list instanceof Array ? list : []);
+                        } else {
+                            resolve([]);
+                        }
+                    });
+                });
+            },
+            GetDic() {
+                return new Promise((resolve) => {
+                    let result = [];
+                    let dicData = {};
+                    let cascaderList = Object.assign([], this.dicCascaderList);
+                    if (validatenull(cascaderList)) resolve({});
+                    cascaderList.forEach(ele => {
+                        result.push(new Promise((resolve) => {
+                            this.GetDicByType(`${ele.dicUrl.replace('{{key}}', ele.dicData)}`).then(function(res) {
+                                resolve(res);
+                            });
+                        }));
+                    });
+                    Promise.all(result).then(data => {
+                        cascaderList.forEach((ele, index) => {
+                            dicData[ele.dicData] = data[index];
+                        });
+                        resolve(dicData);
+                    });
+                });
+
+            },
+            initFun() {
+                Object.keys(utils).forEach(key => {
+                    this[key] = utils[key];
+                });
+            }
+        }
+    };
+}

+ 7 - 0
packages/table-tree/index.js

@@ -0,0 +1,7 @@
+import TableTree from './src/main';
+
+TableTree.install = function(Vue) {
+  Vue.component(TableTree.name, TableTree);
+};
+
+export default TableTree;

+ 25 - 0
packages/table-tree/src/eval.js

@@ -0,0 +1,25 @@
+'use strict';
+import Vue from 'vue';
+export default function treeToArray(data, expandAll, parent = null, level = null) {
+  let tmp = [];
+  Array.from(data).forEach(function(record) {
+    if (record._expanded === undefined) {
+      Vue.set(record, '_expanded', expandAll);
+    }
+    let _level = 1;
+    if (level !== undefined && level !== null) {
+      _level = level + 1;
+    }
+    Vue.set(record, '_level', _level);
+    // 如果有父元素
+    if (parent) {
+      Vue.set(record, 'parent', parent);
+    }
+    tmp.push(record);
+    if (record.children && record.children.length > 0) {
+      const children = treeToArray(record.children, expandAll, record, _level);
+      tmp = tmp.concat(children);
+    }
+  });
+  return tmp;
+}

+ 130 - 0
packages/table-tree/src/main.vue

@@ -0,0 +1,130 @@
+<template>
+  <el-table :data="formatData"
+            :class="b()"
+            :stripe="option.stripe"
+            :row-style="showRow"
+            :row-class-name="rowClassName"
+            v-bind="$attrs"
+            :border="border">
+    <el-table-column v-if="columns.length===0"
+                     width="150">
+      <template slot-scope="scope">
+        <span v-for="space in scope.row._level"
+              class="ms-tree-space"
+              :key="space"></span>
+        <span class="tree-ctrl"
+              v-if="iconShow(0,scope.row)"
+              @click="toggleExpanded(scope.$index)">
+          <i v-if="!scope.row._expanded"
+             class="el-icon-plus"></i>
+          <i v-else
+             class="el-icon-minus"></i>
+        </span>
+        {{scope.$index}}
+      </template>
+    </el-table-column>
+    <el-table-column v-else
+                     v-for="(column, index) in columns"
+                     :key="column.value"
+                     :label="column.text"
+                     :width="column.width">
+      <template slot-scope="scope">
+        <span v-if="index === 0"
+              v-for="space in scope.row._level"
+              class="ms-tree-space"
+              :key="space"></span>
+        <span class="tree-ctrl"
+              v-if="iconShow(index,scope.row)"
+              @click="toggleExpanded(scope.$index)">
+          <i v-if="!scope.row._expanded"
+             class="el-icon-plus"></i>
+          <i v-else
+             class="el-icon-minus"></i>
+        </span>
+        {{detail(scope.row,column)}}
+      </template>
+    </el-table-column>
+    <slot></slot>
+  </el-table>
+</template>
+
+<script>
+import create from '../../utils/create';
+import treeToArray from './eval';
+import crud from '../../mixins/crud.js';
+import column from '../../mixins/column.js';
+export default create({
+  name: 'tree-table',
+  mixins: [crud(), column()],
+  props: {
+    option: {
+      type: Object,
+      required: true
+    },
+    rowClassName: Function,
+    evalFunc: Function,
+    evalArgs: Array
+  },
+  data () {
+    return {};
+  },
+  created () { },
+  computed: {
+    columnOption () {
+      return this.option.columns || [];
+    },
+    data: function () {
+      return this.option.data || [];
+    },
+    columns: function () {
+      return this.option.columns || [];
+    },
+    expandAll: function () {
+      return this.option.expandAll;
+    },
+    border: function () {
+      return this.option.border || true;
+    },
+    // 格式化数据源
+    formatData: function () {
+      let tmp;
+      if (!Array.isArray(this.data)) {
+        tmp = [this.data];
+      } else {
+        tmp = this.data;
+      }
+      const func = this.evalFunc || treeToArray;
+      const args = this.evalArgs
+        ? Array.concat([tmp, this.expandAll], this.evalArgs)
+        : [tmp, this.expandAll];
+      return func.apply(null, args);
+    }
+  },
+  methods: {
+    rulesInit () {
+
+    },
+    formInit () {
+
+    },
+    showRow: function (row) {
+      const show = row.row.parent
+        ? row.row.parent._expanded && row.row.parent._show
+        : true;
+      row.row._show = show;
+      return show
+        ? 'animation:treeTableShow 1s;-webkit-animation:treeTableShow 1s;'
+        : 'display:none;';
+    },
+    // 切换下级是否展开
+    toggleExpanded: function (trIndex) {
+      const record = this.formatData[trIndex];
+      record._expanded = !record._expanded;
+    },
+    // 图标显示
+    iconShow (index, record) {
+      return index === 0 && record.children && record.children.length > 0;
+    }
+  }
+});
+</script>

+ 25 - 0
packages/theme-chalk/gulpfile.js

@@ -0,0 +1,25 @@
+'use strict';
+
+var gulp = require('gulp');
+var sass = require('gulp-sass');
+var autoprefixer = require('gulp-autoprefixer');
+var cssmin = require('gulp-cssmin');
+
+gulp.task('compile', function() {
+  return gulp.src('./src/**')
+    .pipe(sass.sync())
+    .pipe(autoprefixer({
+      browsers: ['ie > 9', 'last 2 versions'],
+      cascade: false
+    }))
+    .pipe(cssmin())
+    .pipe(gulp.dest('./lib'));
+});
+
+gulp.task('copyfont', function() {
+  return gulp.src('./src/fonts/**')
+    .pipe(cssmin())
+    .pipe(gulp.dest('./lib/fonts'));
+});
+
+gulp.task('build', ['compile', 'copyfont']);

Plik diff jest za duży
+ 1 - 0
packages/theme-chalk/lib/common.css


+ 1 - 0
packages/theme-chalk/lib/crud-upload.css

@@ -0,0 +1 @@
+.avue-crud-upload--list .el-upload{border:1px dashed #d9d9d9;border-radius:6px;cursor:pointer;position:relative;overflow:hidden}.avue-crud-upload--list .el-upload:hover{border-color:#409eff}.avue-crud-upload__icon{font-size:28px;color:#8c939d;width:178px;height:178px;line-height:178px;text-align:center}.avue-crud-upload__avatar{width:178px;height:178px;display:block}

Plik diff jest za duży
+ 1 - 0
packages/theme-chalk/lib/crud.css


Plik diff jest za duży
+ 1 - 0
packages/theme-chalk/lib/data-box.css


Plik diff jest za duży
+ 1 - 0
packages/theme-chalk/lib/data-card.css


Plik diff jest za duży
+ 1 - 0
packages/theme-chalk/lib/data-display.css


Plik diff jest za duży
+ 1 - 0
packages/theme-chalk/lib/data-icons.css


Plik diff jest za duży
+ 1 - 0
packages/theme-chalk/lib/data-tabs.css


+ 1 - 0
packages/theme-chalk/lib/date-select.css

@@ -0,0 +1 @@
+.avue-date-select{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.avue-date-select__radio .el-radio-button:last-child .el-radio-button__inner{border-radius:0;border-right:0}.avue-date-select__date{width:300px}.avue-date-select__date .el-date-editor{border-top-left-radius:0;border-bottom-left-radius:0}

+ 1 - 0
packages/theme-chalk/lib/element-ui.css

@@ -0,0 +1 @@
+.avue-input-number,.el-cascader,.el-date-editor.el-input,.el-date-editor.el-input__inner,.el-select{width:100%!important}.el-input__inner{height:38px;line-height:38px}.el-tooltip__popper{max-width:60%}

Plik diff jest za duży
+ 1 - 0
packages/theme-chalk/lib/form-detail.css


+ 1 - 0
packages/theme-chalk/lib/form-steps.css

@@ -0,0 +1 @@
+.avue-form-steps--vertical{width:100%;display:-webkit-box;display:-ms-flexbox;display:flex}.avue-form-steps__steps{width:230px}.avue-form-steps__contail{-webkit-box-flex:1;-ms-flex:1;flex:1}.avue-form-steps__form{margin:0 auto}

+ 1 - 0
packages/theme-chalk/lib/form.css

@@ -0,0 +1 @@
+.avue-form__menu,.avue-form__row--block{width:100%}.avue-form__group,.avue-form__row--block{height:auto;overflow:hidden}.avue-form{padding:8px 10px}.avue-form__menu--center{text-align:center}.avue-form__menu--left{text-align:left}.avue-form__menu--right{text-align:right}.avue-form__group .el-col{position:relative}.avue-form__row--cursor{cursor:pointer}.avue-form__option{position:absolute;right:0;top:-10px;z-index:999}.avue-form__option i{color:#666}.avue-form__option i+i{margin-left:10px}

Plik diff jest za duży
+ 1 - 0
packages/theme-chalk/lib/index.css


+ 1 - 0
packages/theme-chalk/lib/select-date.css

@@ -0,0 +1 @@
+.avue-select-date{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.avue-select-date__radio .el-radio-button:last-child .el-radio-button__inner{border-radius:0;border-right:0}.avue-select-date__date{width:300px}.avue-select-date__date .el-date-editor{padding:0 10px;height:36px;border-top-left-radius:0;border-bottom-left-radius:0}

Plik diff jest za duży
+ 1 - 0
packages/theme-chalk/lib/table-tree.css


+ 24 - 0
packages/theme-chalk/package.json

@@ -0,0 +1,24 @@
+{
+    "name": "avue-theme-chalk",
+    "version": "2.0.0",
+    "main": "lib/index.css",
+    "style": "lib/index.css",
+    "files": [
+        "lib",
+        "src"
+    ],
+    "scripts": {
+        "build": "gulp build"
+    },
+    "keywords": [],
+    "license": "MIT",
+    "bugs": {
+        "devDependencies": {
+            "gulp": "^3.9.1",
+            "gulp-cssmin": "^0.1.7",
+            "gulp-sass": "^3.1.0",
+            "gulp-autoprefixer": "^4.0.0"
+        },
+        "dependencies": {}
+    }
+}

+ 32 - 0
packages/theme-chalk/src/common.scss

@@ -0,0 +1,32 @@
+.avue-ghost {
+    opacity: 0.5;
+    background: #c8ebfb;
+}
+
+.avue-tip {
+    position: relative;
+    margin-bottom: 12px;
+    width: 100%;
+    display: flex;
+    align-items: center;
+    &__icon {
+        margin-top: 3px;
+        margin-right: 5px;
+        font-size: 14px;
+    }
+    &__name {
+        font-size: 12px;
+        margin-right: 15px;
+        color: #515a6e;
+        &--bold {
+            padding: 0 6px;
+            color: #409eff;
+            font-weight: bold;
+            font-size: 18px;
+        }
+    }
+    &__btn {
+        margin-top: 3px;
+        cursor: pointer;
+    }
+}

+ 27 - 0
packages/theme-chalk/src/crud-upload.scss

@@ -0,0 +1,27 @@
+.avue-crud-upload {
+    &--list {
+        .el-upload {
+            border: 1px dashed #d9d9d9;
+            border-radius: 6px;
+            cursor: pointer;
+            position: relative;
+            overflow: hidden;
+        }
+        .el-upload:hover {
+            border-color: #409eff;
+        }
+    }
+    &__icon {
+        font-size: 28px;
+        color: #8c939d;
+        width: 178px;
+        height: 178px;
+        line-height: 178px;
+        text-align: center;
+    }
+    &__avatar {
+        width: 178px;
+        height: 178px;
+        display: block;
+    }
+}

+ 98 - 0
packages/theme-chalk/src/crud.scss

@@ -0,0 +1,98 @@
+.avue-crud {
+    margin: 0 auto;
+    width: 99%;
+    .el-table th {
+        word-break: break-word;
+        color: rgba(0, 0, 0, 0.85);
+        background: #fafafa;
+    }
+    .el-table td {
+        padding: 8px 0;
+    }
+    &__pagination {
+        position: relative;
+        height: 25px;
+        margin-top: 15px;
+        margin-bottom: 10px;
+        padding: 10px 20px;
+        .el-pagination {
+            position: absolute;
+            right: 0;
+        }
+    }
+    &__form {
+        padding: 0 8px;
+    }
+    &__header {
+        margin-bottom: 10px;
+        &>.el-button {
+            padding: 12px 25px;
+        }
+    }
+    &__box {
+        box-shadow: none !important;
+        border: none;
+        .el-card__body,
+        .el-card__header {
+            padding: 18px 0;
+        }
+    }
+    &__title {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+    }
+    &__menu {
+        position: relative;
+        width: 100%;
+        min-height: 40px;
+        height: auto;
+        overflow: hidden;
+        margin-bottom: 12px;
+    }
+    &__left,
+    &__right {
+        position: absolute;
+        height: auto;
+        overflow: hidden;
+    }
+    &__left {
+        left: 0;
+        .el-button {
+            margin-right: 5px;
+        }
+    }
+    &__right {
+        right: 0;
+        .el-button {
+            margin-left: 5px;
+        }
+    }
+    &__dialog {
+        .el-dialog__header {
+            border-top-left-radius: 5px;
+            border-top-right-radius: 5px;
+            padding: 12px 20px;
+            background-color: #409eff;
+        }
+        .el-dialog__close {
+            color: #fff;
+            font-size: 18px;
+            &:hover {
+                color: #ffddff;
+            }
+        }
+        .el-dialog__title {
+            color: #fff;
+            font-size: 18px;
+        }
+        .el-dialog__headerbtn {
+            top: 18px;
+        }
+        &--overflow {
+            max-height: 400px;
+            overflow: hidden;
+            overflow-y: auto;
+        }
+    }
+}

+ 46 - 0
packages/theme-chalk/src/data-box.scss

@@ -0,0 +1,46 @@
+.data-box {
+    $height: 100px;
+    $radius: 5px;
+    .item {
+        display: flex;
+        position: relative;
+        margin: 0 auto 10px auto;
+        width: 96%;
+        height: $height;
+        overflow: hidden;
+        border-radius: $radius;
+        box-sizing: border-box;
+        &:hover .item-text {
+            top: 0;
+        }
+    }
+    .item-icon {
+        width: 100px;
+        height: 100%;
+        color: #fff;
+        text-align: center;
+        line-height: 100px;
+        i {
+            font-size: 48px !important;
+        }
+    }
+    .item-info {
+        border: 1px solid #eee;
+        border-left: none;
+        background-color: #fff;
+        flex: 1;
+        display: flex;
+        justify-content: center;
+        flex-direction: column;
+        .title {
+            font-size: 30px;
+            line-height: 40px;
+            text-align: center;
+        }
+        .info {
+            color: #999;
+            font-size: 14px;
+            text-align: center;
+        }
+    }
+}

+ 44 - 0
packages/theme-chalk/src/data-card.scss

@@ -0,0 +1,44 @@
+.data-card {
+    $height: 340px;
+    .item {
+        position: relative;
+        margin: 0 auto;
+        margin-bottom: 50px;
+        width: 230px;
+        height: $height;
+        overflow: hidden;
+        border-radius: 5px;
+        border-color: #fff;
+        border-width: 1px;
+        border-style: solid;
+        &:hover .item-text {
+            top: 0;
+        }
+    }
+    .item-img {
+        width: 100%;
+        background: red;
+        border-radius: 5px;
+        border-bottom-left-radius: 0;
+        border-bottom-right-radius: 0;
+    }
+    .item-text {
+        position: absolute;
+        top: 150px;
+        padding: 20px 15px;
+        width: 100%;
+        height: $height;
+        overflow: auto;
+        box-sizing: border-box;
+        border-radius: 5px;
+        border-top-left-radius: 0;
+        border-top-right-radius: 0;
+        opacity: 0.9;
+        transition: top 0.4s;
+        &>p {
+            font-size: 12px;
+            line-height: 25px;
+            text-indent: 2em;
+        }
+    }
+}

+ 25 - 0
packages/theme-chalk/src/data-display.scss

@@ -0,0 +1,25 @@
+.avue-data-display {
+    .item {
+        display: flex;
+        align-items: center;
+        flex-direction: column;
+        margin: 5px 0;
+        text-align: center;
+    }
+    .count {
+        margin: 8px 0;
+        font-weight: bold;
+        font-size: 32px;
+        color: #15A0FF;
+    }
+    .title {
+        color: #999;
+    }
+    .splitLine {
+        display: block;
+        margin: 0 auto;
+        width: 24px;
+        height: 1px;
+        background: #9B9B9B;
+    }
+}

+ 40 - 0
packages/theme-chalk/src/data-icons.scss

@@ -0,0 +1,40 @@
+.data-icons {
+    .item {
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        width: 90%;
+        margin: 0 auto;
+    }
+    .item-icon {
+        margin-top: 3px;
+        margin-right: 8px;
+        &>i {
+            font-size: 46px !important;
+        }
+    }
+    .item-info {
+        text-align: center;
+        &>span {
+            display: block;
+            color: #999;
+            font-size: 12px;
+        }
+        .count {
+            font-size: 20px;
+            line-height: 25px;
+        }
+    }
+    .item--easy {
+        flex-direction: column;
+        &>.item-icon {
+            margin: 0;
+        }
+        &>.item-info {
+            margin-top: -15px;
+            &>span {
+                font-size: 14px;
+            }
+        }
+    }
+}

+ 64 - 0
packages/theme-chalk/src/data-tabs.scss

@@ -0,0 +1,64 @@
+.data-tabs {
+    .item {
+        position: relative;
+        margin: 15px;
+        padding: 12px;
+        height: 160px;
+        border-radius: 4px;
+        box-sizing: border-box;
+        overflow: hidden;
+        color: #fff;
+    }
+    .item-header {
+        position: relative;
+        &>p {
+            color: #fff;
+            margin: 0px;
+            font-size: 14px;
+        }
+        &>span {
+            position: absolute;
+            right: 0px;
+            top: 0px;
+            padding: 2px 8px;
+            border-radius: 4px;
+            font-size: 12px;
+            background: rgba(255, 255, 255, 0.3);
+        }
+    }
+    .item-body {
+        &>h2 {
+            color: #fff;
+            margin: 0;
+            font-size: 32px;
+            line-height: 60px;
+        }
+    }
+    .item-footer {
+        padding-top: 8px;
+        line-height: 20px;
+        &>span {
+            font-size: 10px;
+        }
+        &>p {
+            color: #fff;
+            margin: 0px;
+            font-size: 12px;
+        }
+    }
+    .item-tip {
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        position: absolute;
+        width: 80px;
+        height: 80px;
+        bottom: 10px;
+        right: 10px;
+        border: 2px solid #fff;
+        border-radius: 100%;
+        font-size: 48px;
+        transform: rotate(-40deg);
+        opacity: 0.1;
+    }
+}

+ 17 - 0
packages/theme-chalk/src/date-select.scss

@@ -0,0 +1,17 @@
+.avue-date-select {
+    display: flex;
+    align-items: center;
+    &__radio {
+        .el-radio-button:last-child .el-radio-button__inner {
+            border-radius: 0;
+            border-right: 0;
+        }
+    }
+    &__date {
+        width: 300px;
+        .el-date-editor {
+            border-top-left-radius: 0px;
+            border-bottom-left-radius: 0px;
+        }
+    }
+}

+ 16 - 0
packages/theme-chalk/src/element-ui.scss

@@ -0,0 +1,16 @@
+.el-select,
+.el-date-editor.el-input,
+.el-date-editor.el-input__inner,
+.el-cascader,
+.avue-input-number {
+    width: 100% !important;
+}
+
+.el-input__inner {
+    height: 38px;
+    line-height: 38px;
+}
+
+.el-tooltip__popper {
+    max-width: 60%;
+}

+ 46 - 0
packages/theme-chalk/src/form-detail.scss

@@ -0,0 +1,46 @@
+.avue-form-detail {
+    &__item {
+        padding: 20px 0 20px 0;
+        border-bottom: 1px solid #eee;
+        &:last-child {
+            border-bottom: none;
+        }
+    }
+    &__icon {
+        margin-right: 10px;
+        font-size: 20px;
+    }
+    &__header {
+        display: flex;
+        align-items: center;
+        margin-bottom: 15px;
+    }
+    &__title {
+        font-size: 16px;
+        font-weight: 500;
+        color: rgba(0, 0, 0, 0.85);
+    }
+    &__box {
+        margin-bottom: 20px;
+        font-size: 14px;
+    }
+    &__label {
+        margin-right: 10px;
+        line-height: 20px;
+        color: rgba(0, 0, 0, 0.85);
+        &--center {
+            text-align: center;
+        }
+        &--left {
+            text-align: left;
+        }
+        &--right {
+            text-align: right;
+        }
+    }
+    &__content {
+        line-height: 22px;
+        width: 100%;
+        color: rgba(0, 0, 0, 0.65);
+    }
+}

+ 15 - 0
packages/theme-chalk/src/form-steps.scss

@@ -0,0 +1,15 @@
+.avue-form-steps {
+    &--vertical {
+        width: 100%;
+        display: flex;
+    }
+    &__steps {
+        width: 230px;
+    }
+    &__contail {
+        flex: 1;
+    }
+    &__form {
+        margin: 0 auto;
+    }
+}

+ 44 - 0
packages/theme-chalk/src/form.scss

@@ -0,0 +1,44 @@
+.avue-form {
+    padding: 8px 10px;
+    &__menu {
+        width: 100%;
+        &--center {
+            text-align: center;
+        }
+        &--left {
+            text-align: left;
+        }
+        &--right {
+            text-align: right;
+        }
+    }
+    &__group {
+        height: auto;
+        overflow: hidden;
+        .el-col {
+            position: relative;
+        }
+    }
+    &__row {
+        &--block {
+            width: 100%;
+            height: auto;
+            overflow: hidden;
+        }
+        &--cursor {
+            cursor: pointer;
+        }
+    }
+    &__option {
+        position: absolute;
+        right: 0;
+        top: -10px;
+        z-index: 999;
+        i {
+            color: #666;
+        }
+        i+i {
+            margin-left: 10px;
+        }
+    }
+}

+ 14 - 0
packages/theme-chalk/src/index.scss

@@ -0,0 +1,14 @@
+@import './common.scss';
+@import './element-ui.scss';
+@import './crud.scss';
+@import './crud-upload.scss';
+@import './table-tree.scss';
+@import './form.scss';
+@import './form-steps.scss';
+@import './form-detail.scss';
+@import './data-display.scss';
+@import './data-card.scss';
+@import './data-tabs.scss';
+@import './data-icons.scss';
+@import './data-box.scss';
+@import './date-select.scss';

+ 46 - 0
packages/theme-chalk/src/table-tree.scss

@@ -0,0 +1,46 @@
+$color-blue: #2196f3;
+$space-width: 18px;
+.avue-tree-table {
+    .ms-tree-space {
+        position: relative;
+        top: 1px;
+        display: inline-block;
+        font-style: normal;
+        font-weight: 400;
+        line-height: 1;
+        width: $space-width;
+        height: 14px;
+        &::before {
+            content: "";
+        }
+    }
+    .processContainer {
+        width: 100%;
+        height: 100%;
+    }
+    table td {
+        line-height: 26px;
+    }
+    .tree-ctrl {
+        position: relative;
+        cursor: pointer;
+        color: $color-blue;
+        margin-left: -$space-width;
+    }
+    @keyframes treeTableShow {
+        from {
+            opacity: 0;
+        }
+        to {
+            opacity: 1;
+        }
+    }
+    @-webkit-keyframes treeTableShow {
+        from {
+            opacity: 0;
+        }
+        to {
+            opacity: 1;
+        }
+    }
+}

+ 16 - 0
packages/utils/create-basic.js

@@ -0,0 +1,16 @@
+/**
+ * Create a basic component with common options
+ */
+
+import bem from '../mixins/bem';
+const install = function(Vue) {
+    Vue.component(this.name, this);
+};
+
+export default function(sfc) {
+    sfc.name = 'avue-' + sfc.name;
+    sfc.install = sfc.install || install;
+    sfc.mixins = sfc.mixins || [];
+    sfc.mixins.push(bem);
+    return sfc;
+}

+ 9 - 0
packages/utils/create.js

@@ -0,0 +1,9 @@
+/**
+ * Create a component with common options
+ */
+import createBasic from './create-basic';
+
+export default function(sfc) {
+
+    return createBasic(sfc);
+}

+ 25 - 0
packages/utils/dateUtil.js

@@ -0,0 +1,25 @@
+export const dateFtt = (fmt, date) => { // author: meizz
+  var o = {
+    'M+': date.getMonth() + 1, // 月份
+    'd+': date.getDate(), // 日
+    'h+': date.getHours(), // 小时
+    'm+': date.getMinutes(), // 分
+    's+': date.getSeconds(), // 秒
+    'q+': Math.floor((date.getMonth() + 3) / 3), // 季度
+    'S': date.getMilliseconds() // 毫秒
+  };
+  if (/(y+)/.test(fmt)) { fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length)); }
+  for (var k in o) {
+    if (new RegExp('(' + k + ')').test(fmt)) { fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length))); }
+  }
+  return fmt;
+};
+
+export const GetDateStr = (AddDayCount) => {
+  var dd = new Date();
+  dd.setDate(dd.getDate() + AddDayCount); // 获取AddDayCount天后的日期
+  var y = dd.getFullYear();
+  var m = (dd.getMonth() + 1) < 10 ? '0' + (dd.getMonth() + 1) : (dd.getMonth() + 1); // 获取当前月份的日期,不足10补0
+  var d = dd.getDate() < 10 ? '0' + dd.getDate() : dd.getDate(); // 获取当前几号,不足10补0
+  return y + '-' + m + '-' + d;
+};

+ 247 - 0
packages/utils/util.js

@@ -0,0 +1,247 @@
+import {
+    validatenull
+} from './validate';
+
+
+export const getObjType = obj => {
+    var toString = Object.prototype.toString;
+    var map = {
+        '[object Boolean]': 'boolean',
+        '[object Number]': 'number',
+        '[object String]': 'string',
+        '[object Function]': 'function',
+        '[object Array]': 'array',
+        '[object Date]': 'date',
+        '[object RegExp]': 'regExp',
+        '[object Undefined]': 'undefined',
+        '[object Null]': 'null',
+        '[object Object]': 'object'
+    };
+    if (obj instanceof Element) {
+        return 'element';
+    }
+    return map[toString.call(obj)];
+};
+/**
+ * 对象深拷贝
+ */
+export const deepClone = data => {
+    var type = getObjType(data);
+    var obj;
+    if (type === 'array') {
+        obj = [];
+    } else if (type === 'object') {
+        obj = {};
+    } else {
+        //不再具有下一层次
+        return data;
+    }
+    if (type === 'array') {
+        for (var i = 0, len = data.length; i < len; i++) {
+            obj.push(deepClone(data[i]));
+        }
+    } else if (type === 'object') {
+        for (var key in data) {
+            obj[key] = deepClone(data[key]);
+        }
+    }
+    return obj;
+};
+
+/**
+ * 根据字典的value显示label
+ */
+
+export const findByvalue = (dic, value, props) => {
+    props = props || {};
+    const labelKey = props.label || 'label';
+    const valueKey = props.value || 'value';
+    const childrenKey = props.children || 'children';
+    let result = '';
+    if (validatenull(dic)) return value;
+    if (typeof(value) === 'string' || typeof(value) === 'number' || typeof(value) === 'boolean') {
+        let index = 0;
+        index = findArray(dic, value, valueKey);
+        if (index !== -1) {
+            result = (dic[index][labelKey]);
+        } else {
+            result = value;
+        }
+    } else if (value instanceof Array && dic[0][childrenKey]) {
+        let index = 0;
+        let count = 0;
+        while (count < value.length) {
+            index = findArray(dic, value[count]);
+            if (!validatenull(dic[index])) {
+                result = result + dic[index][labelKey] + '/';
+                dic = dic[index][childrenKey];
+            }
+            count++;
+        }
+        if (result.length > 0) {
+            result = result.substr(0, result.length - 1);
+        }
+    } else if (value instanceof Array) {
+        result = [];
+        let index = 0;
+        value.forEach(ele => {
+            index = findArray(dic, ele);
+            if (index !== -1) {
+                result.push(dic[index][labelKey]);
+            } else {
+                result.push(ele);
+            }
+        });
+        result = result.toString();
+    }
+    return result;
+};
+/**
+ * 根据字典的value查找对应的index
+ */
+export const findArray = (dic, value, valueKey) => {
+    valueKey = valueKey || 'value';
+    for (let i = 0; i < dic.length; i++) {
+        if (dic[i][valueKey] === value) {
+            return i;
+        }
+    }
+    return -1;
+};
+/**
+ * 获取字典
+ */
+export const setDic = (dicData, DIC) => {
+    return (typeof(dicData) === 'string') ? DIC : dicData;
+};
+/**
+ * 设置px
+ */
+export const setPx = (val, defval) => {
+    if (validatenull(val)) {
+        val = defval;
+    }
+    val = val + '';
+    if (val.indexOf('%') === -1) {
+        val = val + 'px';
+    }
+    return val;
+};
+
+/**
+ * 表格初始化值
+ */
+
+export const formInitVal = (list) => {
+    let tableForm = {};
+    let searchForm = {};
+    list.forEach(ele => {
+        if (
+            ele.type === 'checkbox' ||
+            ele.type === 'cascader' ||
+            ele.type === 'dates' ||
+            (ele.type === 'upload' && ele.listType !== 'picture-img') ||
+            ele.multiple ||
+            ele.range || ele.dataType === 'array'
+        ) {
+            tableForm[ele.prop] = [];
+            if (ele.search) searchForm[ele.prop] = [];
+        } else if (['number', 'rate', 'silder'].includes(ele.type) || ele.dataType === 'number') {
+            tableForm[ele.prop] = 0;
+            if (ele.search) {
+                searchForm[ele.prop] = 0;
+            }
+        } else {
+            tableForm[ele.prop] = '';
+            if (ele.search) {
+                searchForm[ele.prop] = '';
+            }
+        }
+        //表单默认值设置
+        if (!validatenull(ele.valueDefault)) tableForm[ele.prop] = ele.valueDefault;
+        //搜索表单默认值设置
+        if (!validatenull(ele.searchDefault)) searchForm[ele.prop] = ele.searchDefault;
+    });
+    return {
+        tableForm,
+        searchForm
+    };
+};
+
+export const getType = (column) => {
+    const type = column.type
+    const more = column.more || false;
+    if (more) {
+        if (type === 'date') {
+            return 'daterange';
+        } else if (type === 'datetime') {
+            return 'datetimerange';
+        } else if (type === 'time') {
+            return 'timerange';
+        } else {
+            return type;
+        }
+    }
+    return type;
+};
+
+/**
+ * 搜索框获取动态组件
+ */
+export const getSearchType = (type) => {
+    if (['select', 'radio', 'checkbox'].includes(type)) {
+        return 'crudSelect';
+    } else if (['time', 'timerange'].includes(type)) {
+        return 'crudTime';
+    } else if (['dates', 'date', 'datetime', 'datetimerange', 'daterange', 'week', 'month', 'year'].includes(type)) {
+        return 'crudDate';
+    } else if (['cascader'].includes(type)) {
+        return 'crudCascader';
+    } else if (['number'].includes(type)) {
+        return 'crudInputNumber';
+    } else {
+        return 'crudInput';
+    }
+};
+
+/**
+ * 动态获取组件
+ */
+export const getComponent = ({ type, component }) => {
+    if (!validatenull(component)) {
+        return component;
+    } else if (type === 'select') {
+        return 'crudSelect';
+    } else if (type === 'radio') {
+        return 'crudRadio';
+    } else if (type === 'checkbox') {
+        return 'crudCheckbox';
+    } else if (['time', 'timerange'].includes(type)) {
+        return 'crudTime';
+    } else if (['dates', 'date', 'datetime', 'datetimerange', 'daterange', 'week', 'month', 'year'].includes(type)) {
+        return 'crudDate';
+    } else if (type === 'cascader') {
+        return 'crudCascader';
+    } else if (type === 'number') {
+        return 'crudInputNumber';
+    } else if (type === 'password') {
+        return 'crudInput';
+    } else if (type === 'switch') {
+        return 'crudSwitch';
+    } else if (type === 'rate') {
+        return 'crudRate';
+    } else if (type === 'upload') {
+        return 'crudUpload';
+    } else if (type === 'silder') {
+        return 'crudSilder';
+    } else {
+        return 'crudInput';
+    }
+};
+
+export const vaildData = (val, dafult) => {
+    if (typeof val === 'boolean') {
+        return val;
+    }
+    return !validatenull(val) ? val : dafult;
+};

+ 20 - 0
packages/utils/validate.js

@@ -0,0 +1,20 @@
+/**
+ * 判断是否为空
+ */
+export function validatenull(val) {
+  if (typeof val === 'boolean') {
+    return false;
+  }
+  if (typeof val === 'number') {
+    return false;
+  }
+  if (val instanceof Array) {
+    if (val.length === 0) return true;
+  } else if (val instanceof Object) {
+    if (JSON.stringify(val) === '{}') return true;
+  } else {
+    if (val === 'null' || val == null || val === 'undefined' || val === undefined || val === '') return true;
+    return false;
+  }
+  return false;
+}

+ 86 - 0
src/const/crud/user.js

@@ -0,0 +1,86 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+export const tableOption = {
+    border: true,
+    index: true,
+    indexLabel: '序号',
+    stripe: true,
+    menuAlign: 'center',
+    editBtn: false,
+    delBtn: false,
+    align: 'center',
+    addBtn: false,
+    column: [{
+        fixed: true,
+        label: 'id',
+        prop: 'userId',
+        span: 24,
+        editDisabled: true,
+        addVisdiplay: false,
+    }, {
+        fixed: true,
+        label: '用户名',
+        prop: 'username',
+        solt: true,
+        search: true,
+        span: 24,
+    }, {
+        label: '密码',
+        prop: 'password',
+        type: 'password',
+        hide: true,
+        span: 24,
+    }, {
+        label: '所属部门',
+        prop: 'deptId',
+        formsolt: true,
+        solt: true,
+        span: 24,
+    }, {
+        label: '角色',
+        prop: 'role',
+        formsolt: true,
+        solt: true,
+        span: 24,
+    }, {
+        label: '状态',
+        prop: 'delFlag',
+        type: "select",
+        solt: true,
+        span: 24,
+        dicData: [{
+            label: '有效',
+            value: '0'
+        }, {
+            label: '无效',
+            value: '1'
+        }, {
+            label: '锁定',
+            value: '9'
+        }]
+    }, {
+        width: 180,
+        label: '创建时间',
+        prop: 'createTime',
+        type: 'datetime',
+        format: 'yyyy-MM-dd HH:mm',
+        valueFormat: 'yyyy-MM-dd HH:mm:ss',
+        editDisabled: true,
+        addVisdiplay: false,
+        span: 24,
+    }]
+}

+ 4 - 4
src/main.js

@@ -25,9 +25,9 @@ import '@smallwei/avue/lib/index.js';
 // 引入avue的样式文件
 import '@smallwei/avue/lib/theme-chalk/index.css';
 
-// //源文件包
-// import './packages/index.js';
-// import './packages/theme-chalk/src/index.scss';
+// // //源文件包
+// import '../packages/index.js';
+// import '../packages/theme-chalk/src/index.scss';
 
 import basicContainer from './components/basic-container/main'
 import VueClipboard from 'vue-clipboard2'
@@ -63,4 +63,4 @@ new Vue({
     router,
     store,
     render: h => h(App)
-}).$mount('#app')
+}).$mount('#app')

+ 141 - 339
src/views/admin/user/index.vue

@@ -18,304 +18,128 @@
 <template>
   <div class="app-container calendar-list-container">
     <basic-container>
-      <div class="filter-container">
-        <el-input @keyup.enter.native="handleFilter"
-                  style="width: 200px;"
-                  class="filter-item"
-                  placeholder="用户名"
-                  v-model="listQuery.username">
-        </el-input>
-        <el-button class="filter-item"
-                   type="primary"
-                   icon="search"
-                   @click="handleFilter">搜索</el-button>
-        <el-button v-if="sys_user_add"
-                   class="filter-item"
-                   style="margin-left: 10px;"
-                   @click="handleCreate"
-                   type="primary"
-                   icon="edit">添加</el-button>
-      </div>
-
-      <el-table :key='tableKey'
-                :data="list"
-                v-loading="listLoading"
-                element-loading-text="给我一点时间"
-                border
-                fit
-                highlight-current-row
-                style="width: 99%">
-
-        <el-table-column align="center"
-                         label="序号">
-          <template slot-scope="scope">
-            <span>{{scope.row.userId}}</span>
-          </template>
-        </el-table-column>
-
-        <el-table-column align="center"
-                         label="用户名">
-          <template slot-scope="scope">
-            <span>
-              <img v-if="scope.row.avatar"
+      <avue-crud :option="option"
+                 ref="crud"
+                 v-model="form"
+                 :page="page"
+                 :table-loading="listLoading"
+                 @current-change="handleCurrentChange"
+                 @size-change="handleSizeChange"
+                 @search-change="handleFilter"
+                 @refresh-change="handleRefreshChange"
+                 @row-update="update"
+                 @row-save="create"
+                 :before-open="handleOpenBefore"
+                 :data="list">
+        <template slot="menuLeft">
+          <el-button v-if="sys_user_add"
+                     class="filter-item"
+                     @click="handleCreate"
+                     size="small"
+                     type="primary"
+                     icon="el-icon-edit">添加</el-button>
+        </template>
+        <template slot="username"
+                  slot-scope="scope">
+          <img v-if="scope.row.avatar"
                    class="user-avatar"
                    style="width: 20px; height: 20px; border-radius: 50%;"
-                   :src="scope.row.avatar+'?imageView2/1/w/20/h/20'"> {{scope.row.username}}
-            </span>
-          </template>
-        </el-table-column>
-
-        <el-table-column align="center"
-                         label="所属部门"
-                         show-overflow-tooltip>
-          <template slot-scope="scope">
-            <span>{{scope.row.deptName}} </span>
-          </template>
-        </el-table-column>
-
-        <el-table-column align="center"
-                         label="角色">
-          <template slot-scope="scope">
-            <span v-for="(role,index) in scope.row.roleList"
-                  :key="index">{{role.roleDesc}} </span>
-          </template>
-        </el-table-column>
-
-        <el-table-column align="center"
-                         label="创建时间">
-          <template slot-scope="scope">
-            <span>{{scope.row.createTime | parseTime('{y}-{m}-{d} {h}:{i}')}}</span>
-          </template>
-        </el-table-column>
-
-        <el-table-column align="center"
-                         class-name="status-col"
-                         label="状态">
-          <template slot-scope="scope">
-            <el-tag>{{scope.row.delFlag | statusFilter}}</el-tag>
-          </template>
-        </el-table-column>
-
-        <el-table-column align="center"
-                         label="操作"
-                         width="200">
-          <template slot-scope="scope">
-            <el-button v-if="sys_user_edit"
-                       size="small"
-                       type="success"
-                       @click="handleUpdate(scope.row)">编辑
-            </el-button>
-            <el-button v-if="sys_user_del"
-                       size="small"
-                       type="danger"
-                       @click="deletes(scope.row)">删除
-            </el-button>
-          </template>
-        </el-table-column>
-
-      </el-table>
-
-      <div v-show="!listLoading"
-           class="pagination-container">
-        <el-pagination @size-change="handleSizeChange"
-                       @current-change="handleCurrentChange"
-                       :current-page.sync="listQuery.page"
-                       :page-sizes="[10,20,30, 50]"
-                       :page-size="listQuery.limit"
-                       layout="total, sizes, prev, pager, next, jumper"
-                       :total="total">
-        </el-pagination>
-      </div>
+                   :src="scope.row.avatar+'?imageView2/1/w/20/h/20'" />
+          <span>{{scope.row.username}}</span>
+        </template>
+        <template slot="role"
+                  slot-scope="scope">
+          <span v-for="(role,index) in scope.row.roleList"
+                :key="index">
+            <el-tag>{{role.roleDesc}} </el-tag>&nbsp;&nbsp;
+          </span>
+        </template>
+        <template slot="deptId"
+                  slot-scope="scope">
+          {{scope.row.deptName}}
+        </template>
+        <template slot="delFlag"
+                  slot-scope="scope">
+          <el-tag>{{scope.label}}</el-tag>
+        </template>
+        <template slot="menu"
+                  slot-scope="scope">
+          <el-button v-if="sys_user_edit"
+                     size="small"
+                     type="success"
+                     @click="handleUpdate(scope.row,scope.id)">编辑
+          </el-button>
+          <el-button v-if="sys_user_del"
+                     size="small"
+                     type="danger"
+                     @click="deletes(scope.row,scope.id)">删除
+          </el-button>
+        </template>
+        <template slot="deptIdForm"
+                  slot-scope="scope">
+          <avue-crud-input v-model="form.deptId"
+                           type="tree"
+                           :node-click="getNodeData"
+                           :dic="treeDeptData"
+                           :props="defaultProps"></avue-crud-input>
+        </template>
+        <template slot="roleForm"
+                  slot-scope="scope">
+          <avue-crud-select v-model="role"
+                            multiple
+                            :dic="rolesOptions"
+                            :props="roleProps"></avue-crud-select>
+        </template>
+      </avue-crud>
     </basic-container>
-    <el-dialog :title="textMap[dialogStatus]"
-               :visible.sync="dialogDeptVisible">
-      <el-tree class="filter-tree"
-               :data="treeDeptData"
-               :default-checked-keys="checkedKeys"
-               check-strictly
-               node-key="id"
-               highlight-current
-               ref="deptTree"
-               :props="defaultProps"
-               @node-click="getNodeData"
-               default-expand-all>
-      </el-tree>
-    </el-dialog>
-
-    <el-dialog :title="textMap[dialogStatus]"
-               :visible.sync="dialogFormVisible">
-      <el-form :model="form"
-               :rules="rules"
-               ref="form"
-               label-width="100px">
-        <el-form-item label="用户名"
-                      prop="username">
-          <el-input v-model="form.username"
-                    placeholder="请输用户名"></el-input>
-        </el-form-item>
-
-        <el-form-item v-if="dialogStatus == 'create'"
-                      label="密码"
-                      placeholder="请输入密码"
-                      prop="newpassword1">
-          <el-input type="password"
-                    v-model="form.newpassword1"></el-input>
-        </el-form-item>
-
-        <el-form-item label="所属部门"
-                      prop="deptName">
-          <el-input v-model="form.deptName"
-                    placeholder="选择部门"
-                    @focus="handleDept()"
-                    readonly></el-input>
-          <input type="hidden"
-                 v-model="form.deptId" />
-        </el-form-item>
-
-        <el-form-item label="角色"
-                      prop="role">
-          <el-select class="filter-item"
-                     v-model="role"
-                     placeholder="请选择"
-                     multiple>
-            <el-option v-for="item in rolesOptions"
-                       :key="item.roleId"
-                       :label="item.roleDesc"
-                       :value="item.roleId"
-                       :disabled="isDisabled[item.delFlag]">
-              <span style="float: left">{{ item.roleDesc }}</span>
-              <span style="float: right; color: #8492a6; font-size: 13px">{{ item.roleCode }}</span>
-            </el-option>
-          </el-select>
-        </el-form-item>
-
-        <el-form-item label="状态"
-                      v-if="dialogStatus == 'update' && sys_user_del "
-                      prop="delFlag">
-          <el-select class="filter-item"
-                     v-model="form.delFlag"
-                     placeholder="请选择">
-            <el-option v-for="item in statusOptions"
-                       :key="item"
-                       :label="item | statusFilter"
-                       :value="item"> </el-option>
-          </el-select>
-        </el-form-item>
-      </el-form>
-      <div slot="footer"
-           class="dialog-footer">
-        <el-button @click="cancel('form')">取 消</el-button>
-        <el-button v-if="dialogStatus=='create'"
-                   type="primary"
-                   @click="create('form')">确 定</el-button>
-        <el-button v-else
-                   type="primary"
-                   @click="update('form')">修 改</el-button>
-      </div>
-    </el-dialog>
   </div>
 </template>
 
 <script>
 import { fetchList, getObj, addObj, putObj, delObj } from "@/api/user";
 import { deptRoleList, fetchDeptTree } from "@/api/role";
-// import { parseTime } from '@/utils'
+import { tableOption } from '@/const/crud/user';
 import { mapGetters } from "vuex";
-import ElRadioGroup from "element-ui/packages/radio/src/radio-group";
-import ElOption from "element-ui/packages/select/src/option";
 
 export default {
-  components: {
-    ElOption,
-    ElRadioGroup
-  },
   name: "table_user",
   data () {
     return {
+      option: tableOption,
       treeDeptData: [],
       checkedKeys: [],
+      roleProps: {
+        label: "roleDesc",
+        value: 'roleId'
+      },
       defaultProps: {
-        children: "children",
-        label: "name"
+        label: "name",
+        value: 'id',
+      },
+      page: {
+        total: 0, // 总页数
+        currentPage: 1, // 当前页数
+        pageSize: 20 // 每页显示多少条
       },
-      list: null,
-      total: null,
+      list: [],
       listLoading: true,
       listQuery: {
         page: 1,
         limit: 20
       },
       role: [],
-      form: {
-        username: undefined,
-        newpassword1: undefined,
-        delFlag: undefined,
-        deptId: undefined,
-      },
-      rules: {
-        username: [
-          {
-            required: true,
-            message: "请输入账户",
-            trigger: "blur"
-          },
-          {
-            min: 3,
-            max: 20,
-            message: "长度在 3 到 20 个字符",
-            trigger: "blur"
-          }
-        ],
-        newpassword1: [
-          {
-            required: true,
-            message: "请输入密码",
-            trigger: "blur"
-          },
-          {
-            min: 6,
-            max: 20,
-            message: "长度在 6 到 20 个字符",
-            trigger: "blur"
-          }
-        ],
-        deptId: [
-          {
-            required: true,
-            message: "请选择部门",
-            trigger: "blur"
-          }
-        ],
-        role: [
-          {
-            required: true,
-            message: "请选择角色",
-            trigger: "blur"
-          }
-        ]
-      },
-      statusOptions: ["0", "1"],
+      form: {},
       rolesOptions: [],
-      dialogFormVisible: false,
-      dialogDeptVisible: false,
-      userAdd: false,
-      userUpd: false,
-      userDel: false,
-      dialogStatus: "",
-      textMap: {
-        update: "编辑",
-        create: "创建"
-      },
-      isDisabled: {
-        0: false,
-        1: true
-      },
-      tableKey: 0
     };
   },
   computed: {
     ...mapGetters(["permissions"])
   },
+  watch: {
+    role () {
+      this.form.role = this.role
+    }
+  },
   filters: {
     statusFilter (status) {
       const statusMap = {
@@ -338,25 +162,23 @@ export default {
       this.listQuery.isAsc = false;
       fetchList(this.listQuery).then(response => {
         this.list = response.data.records;
-        this.total = response.data.total;
+        this.page.total = response.data.total
         this.listLoading = false;
       });
     },
-    getNodeData (data) {
-      this.dialogDeptVisible = false;
-      this.form.deptId = data.id;
-      this.form.deptName = data.name;
-      deptRoleList(data.id).then(response => {
+    getNodeData (data, done) {
+      done();
+      deptRoleList(this.form.deptId).then(response => {
         this.rolesOptions = response.data;
       });
     },
     handleDept () {
       fetchDeptTree().then(response => {
         this.treeDeptData = response.data;
-        this.dialogDeptVisible = true;
       });
     },
-    handleFilter () {
+    handleFilter (param) {
+      this.listQuery.username = param.username;
       this.listQuery.page = 1;
       this.getList();
     },
@@ -364,76 +186,66 @@ export default {
       this.listQuery.limit = val;
       this.getList();
     },
+    handleRefreshChange () {
+      this.getList()
+    },
     handleCurrentChange (val) {
       this.listQuery.page = val;
       this.getList();
     },
     handleCreate () {
-      this.resetTemp();
-      this.dialogStatus = "create";
-      this.dialogFormVisible = true;
+      this.$refs.crud.rowAdd();
     },
-    handleUpdate (row) {
-      getObj(row.userId).then(response => {
-        this.form = response.data;
-        this.dialogFormVisible = true;
-        this.dialogStatus = "update";
+    handleOpenBefore (show, type) {
+      this.handleDept();
+      if (['edit', 'views'].includes(type)) {
         this.role = [];
-        for (var i = 0; i < row.roleList.length; i++) {
-          this.role[i] = row.roleList[i].roleId;
+        for (var i = 0; i < this.form.roleList.length; i++) {
+          this.role[i] = this.form.roleList[i].roleId;
         }
-        deptRoleList(response.data.deptId).then(response => {
+        deptRoleList(this.form.deptId).then(response => {
           this.rolesOptions = response.data;
         });
-      });
+      } else if (type === 'add') {
+        this.role = [];
+      }
+      show();
     },
-    create (formName) {
-      const set = this.$refs;
-      this.form.role = this.role;
-      set[formName].validate(valid => {
-        if (valid) {
-          addObj(this.form).then(() => {
-            this.dialogFormVisible = false;
-            this.getList();
-            this.$notify({
-              title: "成功",
-              message: "创建成功",
-              type: "success",
-              duration: 2000
-            });
-          });
-        } else {
-          return false;
-        }
-      });
+    handleUpdate (row, index) {
+      this.$refs.crud.rowEdit(row, index);
+    },
+    create (row, done, loading) {
+      addObj(this.form).then(() => {
+        this.getList();
+        done();
+        this.$notify({
+          title: "成功",
+          message: "创建成功",
+          type: "success",
+          duration: 2000
+        });
+      }).catch(() => {
+        loading();
+      });;
     },
     cancel (formName) {
-      this.dialogFormVisible = false;
       this.$refs[formName].resetFields();
     },
-    update (formName) {
-      const set = this.$refs;
-      this.form.role = this.role;
-      set[formName].validate(valid => {
-        if (valid) {
-          this.dialogFormVisible = false;
-          this.form.password = undefined;
-          putObj(this.form).then(() => {
-            this.dialogFormVisible = false;
-            this.getList();
-            this.$notify({
-              title: "成功",
-              message: "修改成功",
-              type: "success",
-              duration: 2000
-            });
-          });
-        } else {
-          return false;
-        }
+    update (row, index, done, loading) {
+      putObj(this.form).then(() => {
+        this.getList();
+        done();
+        this.$notify({
+          title: "成功",
+          message: "修改成功",
+          type: "success",
+          duration: 2000
+        });
+      }).catch(() => {
+        loading();
       });
     },
-    deletes (row) {
+    deletes (row, index) {
       this.$confirm(
         "此操作将永久删除该用户(用户名:" + row.username + "), 是否继续?",
         "提示",
@@ -445,7 +257,7 @@ export default {
       ).then(() => {
         delObj(row.userId)
           .then(() => {
-            this.getList();
+            this.list.splice(index, 1);
             this.$notify({
               title: "成功",
               message: "删除成功",
@@ -462,16 +274,6 @@ export default {
             });
           });
       });
-    },
-    resetTemp () {
-      this.form = {
-        id: undefined,
-        username: "",
-        password: "",
-        role: [],
-        delFlag: "",
-        deptId: ""
-      };
     }
   }
 };

Plik diff jest za duży
+ 0 - 8314
yarn.lock