Browse Source

Merge pull request #154 from sendya/dev/v2

feat: update to 2.0.0
言肆 6 năm trước cách đây
mục cha
commit
214a7c53a5
94 tập tin đã thay đổi với 3491 bổ sung2812 xóa
  1. 2 0
      .env
  2. 0 24
      README.md
  3. 0 27
      README.zh-CN.md
  4. 7 5
      package.json
  5. 0 0
      src/components/Charts/Bar.vue
  6. 120 120
      src/components/Charts/ChartCard.vue
  7. 0 0
      src/components/Charts/Liquid.vue
  8. 55 55
      src/components/Charts/MiniArea.vue
  9. 56 56
      src/components/Charts/MiniBar.vue
  10. 74 74
      src/components/Charts/MiniProgress.vue
  11. 40 0
      src/components/Charts/MiniSmoothArea.vue
  12. 0 0
      src/components/Charts/Radar.vue
  13. 76 76
      src/components/Charts/RankList.vue
  14. 63 63
      src/components/Charts/TransferBar.vue
  15. 81 81
      src/components/Charts/Trend.vue
  16. 0 0
      src/components/Charts/chart.less
  17. 14 0
      src/components/Charts/smooth.area.less
  18. 153 0
      src/components/DescriptionList/DescriptionList.vue
  19. 2 0
      src/components/DescriptionList/index.js
  20. 87 87
      src/components/Exception/ExceptionPage.vue
  21. 2 0
      src/components/Exception/index.js
  22. 0 0
      src/components/Exception/type.js
  23. 59 0
      src/components/GlobalFooter/GlobalFooter.vue
  24. 2 0
      src/components/GlobalFooter/index.js
  25. 96 128
      src/components/GlobalHeader/GlobalHeader.vue
  26. 2 0
      src/components/GlobalHeader/index.js
  27. 3 4
      src/components/Menu/SideMenu.vue
  28. 0 0
      src/components/Menu/index.js
  29. 5 6
      src/components/Menu/menu.js
  30. 0 0
      src/components/Menu/menu.render.js
  31. 89 88
      src/components/NoticeIcon/NoticeIcon.vue
  32. 2 0
      src/components/NoticeIcon/index.js
  33. 11 50
      src/components/PageHeader/PageHeader.vue
  34. 2 0
      src/components/PageHeader/index.js
  35. 10 0
      src/components/PageLoading/index.jsx
  36. 3 3
      src/components/SettingDrawer/SettingDrawer.vue
  37. 37 37
      src/components/SettingDrawer/SettingItem.vue
  38. 2 0
      src/components/SettingDrawer/index.js
  39. 0 0
      src/components/SettingDrawer/settingConfig.js
  40. 0 0
      src/components/Table/README.md
  41. 5 4
      src/components/Table/index.js
  42. 39 0
      src/components/TagSelect/TagSelectOption.jsx
  43. 111 0
      src/components/TagSelect/index.jsx
  44. 49 51
      src/components/global.less
  45. 18 12
      src/components/index.js
  46. 0 53
      src/components/layouts/BasicLayout.vue
  47. 0 85
      src/components/layouts/PageView.vue
  48. 0 7
      src/components/layouts/index.js
  49. 0 55
      src/components/page/GlobalFooter.vue
  50. 0 157
      src/components/page/GlobalLayout.vue
  51. 0 124
      src/components/page/PageLayout.vue
  52. 0 58
      src/components/page/SHeaderNotice.vue
  53. 8 10
      src/components/tools/Breadcrumb.vue
  54. 3 151
      src/components/tools/DetailList.vue
  55. 3 3
      src/components/tools/UserMenu.vue
  56. 0 0
      src/components/tools/index.js
  57. 1 0
      src/config/defaultSettings.js
  58. 52 45
      src/config/router.config.js
  59. 8 0
      src/core/icons.js
  60. 181 0
      src/layouts/BasicLayout.vue
  61. 0 0
      src/layouts/BlankLayout.vue
  62. 176 0
      src/layouts/PageView.vue
  63. 32 26
      src/layouts/RouteView.vue
  64. 150 150
      src/layouts/UserLayout.vue
  65. 7 0
      src/layouts/index.js
  66. 27 24
      src/main.js
  67. 21 2
      src/views/Home.vue
  68. 2 3
      src/views/account/center/Index.vue
  69. 2 2
      src/views/account/settings/Custom.vue
  70. 3 4
      src/views/account/settings/Index.vue
  71. 355 206
      src/views/dashboard/Analysis.vue
  72. 8 7
      src/views/dashboard/Workplace.vue
  73. 1 1
      src/views/exception/403.vue
  74. 1 1
      src/views/exception/404.vue
  75. 1 1
      src/views/exception/500.vue
  76. 32 1
      src/views/form/stepForm/Step1.vue
  77. 10 1
      src/views/form/stepForm/Step2.vue
  78. 2 2
      src/views/form/stepForm/Step3.vue
  79. 50 0
      src/views/list/QueryList.vue
  80. 243 25
      src/views/list/TableList.vue
  81. 67 0
      src/views/list/modules/CreateForm.vue
  82. 172 0
      src/views/list/modules/StepByStepModal.vue
  83. 1 1
      src/views/list/table/List.vue
  84. 271 271
      src/views/other/PermissionList.vue
  85. 1 1
      src/views/other/RoleList.vue
  86. 1 1
      src/views/other/TableInnerEditList.vue
  87. 192 192
      src/views/other/TreeList.vue
  88. 1 1
      src/views/other/UserList.vue
  89. 0 0
      src/views/other/modules/OrgModal.vue
  90. 11 67
      src/views/other/modules/RoleModal.vue
  91. 6 5
      src/views/profile/advanced/Advanced.vue
  92. 8 8
      src/views/profile/basic/Index.vue
  93. 2 2
      src/views/result/Success.vue
  94. 2 8
      vue.config.js

+ 2 - 0
.env

@@ -0,0 +1,2 @@
+NODE_ENV=production
+VUE_APP_PREVIEW=false

+ 0 - 24
README.md

@@ -121,30 +121,6 @@ eg:
     ]
     ```
 
-
-Docs
-----
-
-- [Router and Menu](https://github.com/sendya/ant-design-pro-vue/blob/master/src/router/README.md)
-- [Table](https://github.com/sendya/ant-design-pro-vue/blob/master/src/components/table/README.md) [@Saraka](https://github.com/saraka-tsukai)
-- [ANTD DefaultConfig](https://github.com/sendya/ant-design-pro-vue/blob/master/src/defaultSettings.js)
-- [Frist page loading animate](https://github.com/sendya/ant-design-pro-vue/blob/master/docs/add-page-loading-animate.md)
-- [Multi-Tabs feature/multi-tabs](https://github.com/sendya/ant-design-pro-vue/tree/feature/multi-tabs) [How to remove](https://github.com/sendya/ant-design-pro-vue/blob/master/docs/multi-tabs.md)
-- [LoadOnDemand Demo feature/demand_load](https://github.com/sendya/ant-design-pro-vue/tree/feature/demand_load)
-- [LoadOnDemand Docs](https://github.com/sendya/ant-design-pro-vue/blob/master/docs/load-on-demand.md)  
-- [i18n feature/lang](https://github.com/sendya/ant-design-pro-vue/tree/feature/lang)  Creator [@musnow](https://github.com/musnow)
-- [Dependency analysis tool: analyzer](https://github.com/sendya/ant-design-pro-vue/blob/master/docs/webpack-bundle-analyzer.md)  
-- ANTD PRO Components:
-  - Trend [Trend.md](https://github.com/sendya/ant-design-pro-vue/blob/master/src/components/Trend/index.md)
-  - AvatarList [AvatarList.md](https://github.com/sendya/ant-design-pro-vue/blob/master/src/components/AvatarList/index.md)
-  - CountDown [CountDown.md](https://github.com/sendya/ant-design-pro-vue/blob/master/src/components/CountDown/index.md)
-  - Ellipsis [Ellipsis.md](https://github.com/sendya/ant-design-pro-vue/blob/master/src/components/Ellipsis/index.md)
-  - NumberInfo [NumberInfo.md](https://github.com/sendya/ant-design-pro-vue/blob/master/src/components/NumberInfo/index.md)
-  - FooterToolbar [FooterToolbar.md](https://github.com/sendya/ant-design-pro-vue/blob/master/src/components/FooterToolbar/index.md)
-  - IconSelector [IconSelector.md](https://github.com/sendya/ant-design-pro-vue/blob/master/src/components/IconSelector/README.md) Creator: [@Saraka](https://github.com/saraka-tsukai)
-- doing...
-
-
 ## Contributors
 
 This project exists thanks to all the people who contribute. 

+ 0 - 27
README.zh-CN.md

@@ -11,7 +11,6 @@ An out-of-box UI solution for enterprise applications as a Vue boilerplate. base
 [![Backers on Open Collective](https://opencollective.com/ant-design-pro-vue/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/ant-design-pro-vue/sponsors/badge.svg)](#sponsors) [![License](https://img.shields.io/npm/l/package.json.svg?style=flat)](https://github.com/sendya/ant-design-pro-vue/blob/master/LICENSE)
 [![Release](https://img.shields.io/github/release/sendya/ant-design-pro-vue.svg?style=flat)](https://github.com/sendya/ant-design-pro-vue/releases/latest)
 [![Travis branch](https://travis-ci.org/sendya/ant-design-pro-vue.svg?branch=master)](https://travis-ci.org/sendya/ant-design-pro-vue)
-[![All Contributors](https://img.shields.io/badge/all_contributors-10-orange.svg?style=flat-square)](#contributors)
 
 </div>
 
@@ -32,8 +31,6 @@ Overview
 
 ![个人设置](https://static-2.loacg.com/open/static/github/20180916-134251.png)
 
-![权限列表](https://static-2.loacg.com/open/static/github/20180916-154937.png)
-
 
 环境和依赖
 ----
@@ -131,30 +128,6 @@ yarn run lint
     ```
 
 
-附属文档
-----
-
-- [路由/菜单说明](https://github.com/sendya/ant-design-pro-vue/blob/master/src/router/README.md)
-- [Table 重封装组件](https://github.com/sendya/ant-design-pro-vue/blob/master/src/components/table/README.md) [@Saraka](https://github.com/saraka-tsukai)
-- [ANTD 默认配置项](https://github.com/sendya/ant-design-pro-vue/blob/master/src/defaultSettings.js)
-- [按需加载/减少打包大小](https://github.com/sendya/ant-design-pro-vue/blob/master/docs/load-on-demand.md)  
-- [为首屏增加 Loading 动画](https://github.com/sendya/ant-design-pro-vue/blob/master/docs/add-page-loading-animate.md)
-- [多标签页组件 feature/multi-tabs](https://github.com/sendya/ant-design-pro-vue/tree/feature/multi-tabs) [如何移除](https://github.com/sendya/ant-design-pro-vue/blob/master/docs/multi-tabs.md)
-- [按需加载用例 feature/demand_load](https://github.com/sendya/ant-design-pro-vue/tree/feature/demand_load)
-- [多语言使用案例 feature/lang](https://github.com/sendya/ant-design-pro-vue/tree/feature/lang) [@musnow](https://github.com/musnow) 提供
-- [为项目增加依赖项分析工具 analyzer](https://github.com/sendya/ant-design-pro-vue/blob/master/docs/webpack-bundle-analyzer.md)  
-- ANTD PRO 额外组件
-  - Trend 趋势标记 [Trend.md](https://github.com/sendya/ant-design-pro-vue/blob/master/src/components/Trend/index.md)
-  - AvatarList 用户头像列表 [AvatarList.md](https://github.com/sendya/ant-design-pro-vue/blob/master/src/components/AvatarList/index.md)
-  - CountDown 倒计时 [CountDown.md](https://github.com/sendya/ant-design-pro-vue/blob/master/src/components/CountDown/index.md)
-  - Ellipsis 文本自动省略号 [Ellipsis.md](https://github.com/sendya/ant-design-pro-vue/blob/master/src/components/Ellipsis/index.md)
-  - NumberInfo 数据文本 [NumberInfo.md](https://github.com/sendya/ant-design-pro-vue/blob/master/src/components/NumberInfo/index.md)
-  - FooterToolbar 底部工具栏 [FooterToolbar.md](https://github.com/sendya/ant-design-pro-vue/blob/master/src/components/FooterToolbar/index.md)
-  - IconSelector 图标选择组件 [IconSelector.md](https://github.com/sendya/ant-design-pro-vue/blob/master/src/components/IconSelector/README.md) 提供: [@Saraka](https://github.com/saraka-tsukai)
-- 其他待补充...
-
-
-
 ## Contributors
 
 This project exists thanks to all the people who contribute. 

+ 7 - 5
package.json

@@ -1,11 +1,13 @@
 {
   "name": "vue-antd-pro",
-  "version": "1.3.0",
+  "version": "2.0.0",
   "private": true,
   "scripts": {
     "serve": "vue-cli-service serve",
     "build": "vue-cli-service build",
+    "build:preview": "vue-cli-service build --mode preview",
     "lint": "vue-cli-service lint",
+    "lint:nofix": "vue-cli-service lint --no-fix",
     "test:unit": "vue-cli-service test:unit",
     "postinstall": "opencollective-postinstall"
   },
@@ -20,7 +22,6 @@
     "md5": "^2.2.1",
     "moment": "^2.24.0",
     "nprogress": "^0.2.0",
-    "opencollective-postinstall": "^2.0.2",
     "viser-vue": "^2.3.3",
     "vue": "^2.5.22",
     "vue-clipboard2": "^0.2.1",
@@ -28,8 +29,7 @@
     "vue-ls": "^3.2.0",
     "vue-router": "^3.0.1",
     "vue-svg-component-runtime": "^1.0.1",
-    "vuex": "^3.1.0",
-    "opencollective": "^1.0.3"
+    "vuex": "^3.1.0"
   },
   "devDependencies": {
     "@babel/polyfill": "^7.2.5",
@@ -49,7 +49,9 @@
     "less": "^3.8.1",
     "less-loader": "^4.1.0",
     "vue-svg-icon-loader": "^2.1.1",
-    "vue-template-compiler": "^2.5.22"
+    "vue-template-compiler": "^2.5.22",
+    "opencollective": "^1.0.3",
+    "opencollective-postinstall": "^2.0.2"
   },
   "eslintConfig": {
     "root": true,

+ 0 - 0
src/components/chart/Bar.vue → src/components/Charts/Bar.vue


+ 120 - 120
src/components/chart/ChartCard.vue → src/components/Charts/ChartCard.vue

@@ -1,120 +1,120 @@
-<template>
-  <a-card :loading="loading" :body-style="{ padding: '20px 24px 8px' }" :bordered="false">
-    <div class="chart-card-header">
-      <div class="meta">
-        <span class="chart-card-title">
-          <slot name="title">
-            {{ title }}
-          </slot>
-        </span>
-        <span class="chart-card-action">
-          <slot name="action"></slot>
-        </span>
-      </div>
-      <div class="total">
-        <slot name="total">
-          <span>{{ typeof total === 'function' && total() || total }}</span>
-        </slot>
-      </div>
-    </div>
-    <div class="chart-card-content">
-      <div class="content-fix">
-        <slot></slot>
-      </div>
-    </div>
-    <div class="chart-card-footer">
-      <div class="field">
-        <slot name="footer"></slot>
-      </div>
-    </div>
-  </a-card>
-</template>
-
-<script>
-export default {
-  name: 'ChartCard',
-  props: {
-    title: {
-      type: String,
-      default: ''
-    },
-    total: {
-      type: [Function, Number, String],
-      required: false,
-      default: null
-    },
-    loading: {
-      type: Boolean,
-      default: false
-    }
-  }
-}
-</script>
-
-<style lang="less" scoped>
-  .chart-card-header {
-    position: relative;
-    overflow: hidden;
-    width: 100%;
-
-    .meta {
-      position: relative;
-      overflow: hidden;
-      width: 100%;
-      color: rgba(0, 0, 0, .45);
-      font-size: 14px;
-      line-height: 22px;
-    }
-  }
-
-  .chart-card-action {
-    cursor: pointer;
-    position: absolute;
-    top: 0;
-    right: 0;
-  }
-
-  .chart-card-footer {
-    border-top: 1px solid #e8e8e8;
-    padding-top: 9px;
-    margin-top: 8px;
-
-    > * {
-      position: relative;
-    }
-
-    .field {
-      white-space: nowrap;
-      overflow: hidden;
-      text-overflow: ellipsis;
-      margin: 0;
-    }
-  }
-
-  .chart-card-content {
-    margin-bottom: 12px;
-    position: relative;
-    height: 46px;
-    width: 100%;
-
-    .content-fix {
-      position: absolute;
-      left: 0;
-      bottom: 0;
-      width: 100%;
-    }
-  }
-
-  .total {
-    overflow: hidden;
-    text-overflow: ellipsis;
-    word-break: break-all;
-    white-space: nowrap;
-    color: #000;
-    margin-top: 4px;
-    margin-bottom: 0;
-    font-size: 30px;
-    line-height: 38px;
-    height: 38px;
-  }
-</style>
+<template>
+  <a-card :loading="loading" :body-style="{ padding: '20px 24px 8px' }" :bordered="false">
+    <div class="chart-card-header">
+      <div class="meta">
+        <span class="chart-card-title">
+          <slot name="title">
+            {{ title }}
+          </slot>
+        </span>
+        <span class="chart-card-action">
+          <slot name="action"></slot>
+        </span>
+      </div>
+      <div class="total">
+        <slot name="total">
+          <span>{{ typeof total === 'function' && total() || total }}</span>
+        </slot>
+      </div>
+    </div>
+    <div class="chart-card-content">
+      <div class="content-fix">
+        <slot></slot>
+      </div>
+    </div>
+    <div class="chart-card-footer">
+      <div class="field">
+        <slot name="footer"></slot>
+      </div>
+    </div>
+  </a-card>
+</template>
+
+<script>
+export default {
+  name: 'ChartCard',
+  props: {
+    title: {
+      type: String,
+      default: ''
+    },
+    total: {
+      type: [Function, Number, String],
+      required: false,
+      default: null
+    },
+    loading: {
+      type: Boolean,
+      default: false
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+  .chart-card-header {
+    position: relative;
+    overflow: hidden;
+    width: 100%;
+
+    .meta {
+      position: relative;
+      overflow: hidden;
+      width: 100%;
+      color: rgba(0, 0, 0, .45);
+      font-size: 14px;
+      line-height: 22px;
+    }
+  }
+
+  .chart-card-action {
+    cursor: pointer;
+    position: absolute;
+    top: 0;
+    right: 0;
+  }
+
+  .chart-card-footer {
+    border-top: 1px solid #e8e8e8;
+    padding-top: 9px;
+    margin-top: 8px;
+
+    > * {
+      position: relative;
+    }
+
+    .field {
+      white-space: nowrap;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      margin: 0;
+    }
+  }
+
+  .chart-card-content {
+    margin-bottom: 12px;
+    position: relative;
+    height: 46px;
+    width: 100%;
+
+    .content-fix {
+      position: absolute;
+      left: 0;
+      bottom: 0;
+      width: 100%;
+    }
+  }
+
+  .total {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    word-break: break-all;
+    white-space: nowrap;
+    color: #000;
+    margin-top: 4px;
+    margin-bottom: 0;
+    font-size: 30px;
+    line-height: 38px;
+    height: 38px;
+  }
+</style>

+ 0 - 0
src/components/chart/Liquid.vue → src/components/Charts/Liquid.vue


+ 55 - 55
src/components/chart/MiniArea.vue → src/components/Charts/MiniArea.vue

@@ -1,56 +1,56 @@
-<template>
-  <div class="antv-chart-mini">
-    <div class="chart-wrapper" :style="{ height: 46 }">
-      <v-chart :force-fit="true" :height="height" :data="data" :padding="[36, 0, 18, 0]">
-        <v-tooltip />
-        <v-smooth-area position="x*y" />
-      </v-chart>
-    </div>
-  </div>
-</template>
-
-<script>
-import moment from 'moment'
-const data = []
-const beginDay = new Date().getTime()
-
-for (let i = 0; i < 10; i++) {
-  data.push({
-    x: moment(new Date(beginDay + 1000 * 60 * 60 * 24 * i)).format('YYYY-MM-DD'),
-    y: Math.round(Math.random() * 10)
-  })
-}
-
-const tooltip = [
-  'x*y',
-  (x, y) => ({
-    name: x,
-    value: y
-  })
-]
-const scale = [{
-  dataKey: 'x',
-  min: 2
-}, {
-  dataKey: 'y',
-  title: '时间',
-  min: 1,
-  max: 22
-}]
-
-export default {
-  name: 'MiniArea',
-  data () {
-    return {
-      data,
-      tooltip,
-      scale,
-      height: 100
-    }
-  }
-}
-</script>
-
-<style lang="less" scoped>
-  @import "chart";
+<template>
+  <div class="antv-chart-mini">
+    <div class="chart-wrapper" :style="{ height: 46 }">
+      <v-chart :force-fit="true" :height="height" :data="data" :padding="[36, 0, 18, 0]">
+        <v-tooltip />
+        <v-smooth-area position="x*y" />
+      </v-chart>
+    </div>
+  </div>
+</template>
+
+<script>
+import moment from 'moment'
+const data = []
+const beginDay = new Date().getTime()
+
+for (let i = 0; i < 10; i++) {
+  data.push({
+    x: moment(new Date(beginDay + 1000 * 60 * 60 * 24 * i)).format('YYYY-MM-DD'),
+    y: Math.round(Math.random() * 10)
+  })
+}
+
+const tooltip = [
+  'x*y',
+  (x, y) => ({
+    name: x,
+    value: y
+  })
+]
+const scale = [{
+  dataKey: 'x',
+  min: 2
+}, {
+  dataKey: 'y',
+  title: '时间',
+  min: 1,
+  max: 22
+}]
+
+export default {
+  name: 'MiniArea',
+  data () {
+    return {
+      data,
+      tooltip,
+      scale,
+      height: 100
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+  @import "chart";
 </style>

+ 56 - 56
src/components/chart/MiniBar.vue → src/components/Charts/MiniBar.vue

@@ -1,57 +1,57 @@
-<template>
-  <div class="antv-chart-mini">
-    <div class="chart-wrapper" :style="{ height: 46 }">
-      <v-chart :force-fit="true" :height="height" :data="data" :padding="[36, 5, 18, 5]">
-        <v-tooltip />
-        <v-bar position="x*y" />
-      </v-chart>
-    </div>
-  </div>
-</template>
-
-<script>
-import moment from 'moment'
-const data = []
-const beginDay = new Date().getTime()
-
-for (let i = 0; i < 10; i++) {
-  data.push({
-    x: moment(new Date(beginDay + 1000 * 60 * 60 * 24 * i)).format('YYYY-MM-DD'),
-    y: Math.round(Math.random() * 10)
-  })
-}
-
-const tooltip = [
-  'x*y',
-  (x, y) => ({
-    name: x,
-    value: y
-  })
-]
-
-const scale = [{
-  dataKey: 'x',
-  min: 2
-}, {
-  dataKey: 'y',
-  title: '时间',
-  min: 1,
-  max: 30
-}]
-
-export default {
-  name: 'MiniBar',
-  data () {
-    return {
-      data,
-      tooltip,
-      scale,
-      height: 100
-    }
-  }
-}
-</script>
-
-<style lang="less" scoped>
-  @import "chart";
+<template>
+  <div class="antv-chart-mini">
+    <div class="chart-wrapper" :style="{ height: 46 }">
+      <v-chart :force-fit="true" :height="height" :data="data" :padding="[36, 5, 18, 5]">
+        <v-tooltip />
+        <v-bar position="x*y" />
+      </v-chart>
+    </div>
+  </div>
+</template>
+
+<script>
+import moment from 'moment'
+const data = []
+const beginDay = new Date().getTime()
+
+for (let i = 0; i < 10; i++) {
+  data.push({
+    x: moment(new Date(beginDay + 1000 * 60 * 60 * 24 * i)).format('YYYY-MM-DD'),
+    y: Math.round(Math.random() * 10)
+  })
+}
+
+const tooltip = [
+  'x*y',
+  (x, y) => ({
+    name: x,
+    value: y
+  })
+]
+
+const scale = [{
+  dataKey: 'x',
+  min: 2
+}, {
+  dataKey: 'y',
+  title: '时间',
+  min: 1,
+  max: 30
+}]
+
+export default {
+  name: 'MiniBar',
+  data () {
+    return {
+      data,
+      tooltip,
+      scale,
+      height: 100
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+  @import "chart";
 </style>

+ 74 - 74
src/components/chart/MiniProgress.vue → src/components/Charts/MiniProgress.vue

@@ -1,75 +1,75 @@
-<template>
-  <div class="chart-mini-progress">
-    <div class="target" :style="{ left: target + '%'}">
-      <span :style="{ backgroundColor: color }" />
-      <span :style="{ backgroundColor: color }"/>
-    </div>
-    <div class="progress-wrapper">
-      <div class="progress" :style="{ backgroundColor: color, width: percentage + '%', height: height }"></div>
-    </div>
-  </div>
-</template>
-
-<script>
-export default {
-  name: 'MiniProgress',
-  props: {
-    target: {
-      type: Number,
-      default: 0
-    },
-    height: {
-      type: String,
-      default: '10px'
-    },
-    color: {
-      type: String,
-      default: '#13C2C2'
-    },
-    percentage: {
-      type: Number,
-      default: 0
-    }
-  }
-}
-</script>
-
-<style lang="less" scoped>
-  .chart-mini-progress {
-    padding: 5px 0;
-    position: relative;
-    width: 100%;
-
-    .target {
-      position: absolute;
-      top: 0;
-      bottom: 0;
-
-      span {
-        border-radius: 100px;
-        position: absolute;
-        top: 0;
-        left: 0;
-        height: 4px;
-        width: 2px;
-
-        &:last-child {
-          top: auto;
-          bottom: 0;
-        }
-      }
-    }
-    .progress-wrapper {
-      background-color: #f5f5f5;
-      position: relative;
-
-      .progress {
-        transition: all .4s cubic-bezier(.08,.82,.17,1) 0s;
-        border-radius: 1px 0 0 1px;
-        background-color: #1890ff;
-        width: 0;
-        height: 100%;
-      }
-    }
-  }
+<template>
+  <div class="chart-mini-progress">
+    <div class="target" :style="{ left: target + '%'}">
+      <span :style="{ backgroundColor: color }" />
+      <span :style="{ backgroundColor: color }"/>
+    </div>
+    <div class="progress-wrapper">
+      <div class="progress" :style="{ backgroundColor: color, width: percentage + '%', height: height }"></div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'MiniProgress',
+  props: {
+    target: {
+      type: Number,
+      default: 0
+    },
+    height: {
+      type: String,
+      default: '10px'
+    },
+    color: {
+      type: String,
+      default: '#13C2C2'
+    },
+    percentage: {
+      type: Number,
+      default: 0
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+  .chart-mini-progress {
+    padding: 5px 0;
+    position: relative;
+    width: 100%;
+
+    .target {
+      position: absolute;
+      top: 0;
+      bottom: 0;
+
+      span {
+        border-radius: 100px;
+        position: absolute;
+        top: 0;
+        left: 0;
+        height: 4px;
+        width: 2px;
+
+        &:last-child {
+          top: auto;
+          bottom: 0;
+        }
+      }
+    }
+    .progress-wrapper {
+      background-color: #f5f5f5;
+      position: relative;
+
+      .progress {
+        transition: all .4s cubic-bezier(.08,.82,.17,1) 0s;
+        border-radius: 1px 0 0 1px;
+        background-color: #1890ff;
+        width: 0;
+        height: 100%;
+      }
+    }
+  }
 </style>

+ 40 - 0
src/components/Charts/MiniSmoothArea.vue

@@ -0,0 +1,40 @@
+<template>
+  <div :class="prefixCls">
+    <div class="chart-wrapper" :style="{ height: 46 }">
+      <v-chart :force-fit="true" :height="100" :data="dataSource" :scale="scale" :padding="[36, 0, 18, 0]">
+        <v-tooltip />
+        <v-smooth-line position="x*y" :size="2" />
+        <v-smooth-area position="x*y" />
+      </v-chart>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'MiniSmoothArea',
+  props: {
+    prefixCls: {
+      type: String,
+      default: 'ant-pro-smooth-area'
+    },
+    scale: {
+      type: [Object, Array],
+      required: true
+    },
+    dataSource: {
+      type: Array,
+      required: true
+    }
+  },
+  data () {
+    return {
+      height: 100
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+  @import "smooth.area.less";
+</style>

+ 0 - 0
src/components/chart/Radar.vue → src/components/Charts/Radar.vue


+ 76 - 76
src/components/chart/RankList.vue → src/components/Charts/RankList.vue

@@ -1,77 +1,77 @@
-<template>
-  <div class="rank">
-    <h4 class="title">{{ title }}</h4>
-    <ul class="list">
-      <li :key="index" v-for="(item, index) in list">
-        <span :class="index < 3 ? 'active' : null">{{ index + 1 }}</span>
-        <span>{{ item.name }}</span>
-        <span>{{ item.total }}</span>
-      </li>
-    </ul>
-  </div>
-</template>
-
-<script>
-export default {
-  name: 'RankList',
-  // ['title', 'list']
-  props: {
-    title: {
-      type: String,
-      default: ''
-    },
-    list: {
-      type: Array,
-      default: null
-    }
-  }
-}
-</script>
-
-<style lang="less" scoped>
-
-  .rank {
-    padding: 0 32px 32px 72px;
-
-    .list {
-      margin: 25px 0 0;
-      padding: 0;
-      list-style: none;
-
-      li {
-        margin-top: 16px;
-
-        span {
-          color: rgba(0, 0, 0, .65);
-          font-size: 14px;
-          line-height: 22px;
-
-          &:first-child {
-            background-color: #f5f5f5;
-            border-radius: 20px;
-            display: inline-block;
-            font-size: 12px;
-            font-weight: 600;
-            margin-right: 24px;
-            height: 20px;
-            line-height: 20px;
-            width: 20px;
-            text-align: center;
-          }
-          &.active {
-            background-color: #314659;
-            color: #fff;
-          }
-          &:last-child {
-            float: right;
-          }
-        }
-      }
-    }
-  }
-
-  .mobile .rank {
-    padding: 0 32px 32px 32px;
-  }
-
+<template>
+  <div class="rank">
+    <h4 class="title">{{ title }}</h4>
+    <ul class="list">
+      <li :key="index" v-for="(item, index) in list">
+        <span :class="index < 3 ? 'active' : null">{{ index + 1 }}</span>
+        <span>{{ item.name }}</span>
+        <span>{{ item.total }}</span>
+      </li>
+    </ul>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'RankList',
+  // ['title', 'list']
+  props: {
+    title: {
+      type: String,
+      default: ''
+    },
+    list: {
+      type: Array,
+      default: null
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+
+  .rank {
+    padding: 0 32px 32px 72px;
+
+    .list {
+      margin: 25px 0 0;
+      padding: 0;
+      list-style: none;
+
+      li {
+        margin-top: 16px;
+
+        span {
+          color: rgba(0, 0, 0, .65);
+          font-size: 14px;
+          line-height: 22px;
+
+          &:first-child {
+            background-color: #f5f5f5;
+            border-radius: 20px;
+            display: inline-block;
+            font-size: 12px;
+            font-weight: 600;
+            margin-right: 24px;
+            height: 20px;
+            line-height: 20px;
+            width: 20px;
+            text-align: center;
+          }
+          &.active {
+            background-color: #314659;
+            color: #fff;
+          }
+          &:last-child {
+            float: right;
+          }
+        }
+      }
+    }
+  }
+
+  .mobile .rank {
+    padding: 0 32px 32px 32px;
+  }
+
 </style>

+ 63 - 63
src/components/chart/TransferBar.vue → src/components/Charts/TransferBar.vue

@@ -1,64 +1,64 @@
-<template>
-  <div :style="{ padding: '0 0 32px 32px' }">
-    <h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
-    <v-chart
-      height="254"
-      :data="data"
-      :scale="scale"
-      :forceFit="true"
-      :padding="['auto', 'auto', '40', '50']">
-      <v-tooltip />
-      <v-axis />
-      <v-bar position="x*y"/>
-    </v-chart>
-  </div>
-</template>
-
-<script>
-const tooltip = [
-  'x*y',
-  (x, y) => ({
-    name: x,
-    value: y
-  })
-]
-const scale = [{
-  dataKey: 'x',
-  title: '日期(天)',
-  alias: '日期(天)',
-  min: 2
-}, {
-  dataKey: 'y',
-  title: '流量(Gb)',
-  alias: '流量(Gb)',
-  min: 1
-}]
-
-export default {
-  name: 'Bar',
-  props: {
-    title: {
-      type: String,
-      default: ''
-    }
-  },
-  data () {
-    return {
-      data: [],
-      scale,
-      tooltip
-    }
-  },
-  created () {
-    this.getMonthBar()
-  },
-  methods: {
-    getMonthBar () {
-      this.$http.get('/analysis/month-bar')
-        .then(res => {
-          this.data = res.result
-        })
-    }
-  }
-}
+<template>
+  <div :style="{ padding: '0 0 32px 32px' }">
+    <h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
+    <v-chart
+      height="254"
+      :data="data"
+      :scale="scale"
+      :forceFit="true"
+      :padding="['auto', 'auto', '40', '50']">
+      <v-tooltip />
+      <v-axis />
+      <v-bar position="x*y"/>
+    </v-chart>
+  </div>
+</template>
+
+<script>
+const tooltip = [
+  'x*y',
+  (x, y) => ({
+    name: x,
+    value: y
+  })
+]
+const scale = [{
+  dataKey: 'x',
+  title: '日期(天)',
+  alias: '日期(天)',
+  min: 2
+}, {
+  dataKey: 'y',
+  title: '流量(Gb)',
+  alias: '流量(Gb)',
+  min: 1
+}]
+
+export default {
+  name: 'Bar',
+  props: {
+    title: {
+      type: String,
+      default: ''
+    }
+  },
+  data () {
+    return {
+      data: [],
+      scale,
+      tooltip
+    }
+  },
+  created () {
+    this.getMonthBar()
+  },
+  methods: {
+    getMonthBar () {
+      this.$http.get('/analysis/month-bar')
+        .then(res => {
+          this.data = res.result
+        })
+    }
+  }
+}
 </script>

+ 81 - 81
src/components/chart/Trend.vue → src/components/Charts/Trend.vue

@@ -1,82 +1,82 @@
-<template>
-  <div class="chart-trend">
-    {{ term }}
-    <span>{{ rate }}%</span>
-    <span :class="['trend-icon', trend]"><a-icon :type="'caret-' + trend"/></span>
-  </div>
-</template>
-
-<script>
-export default {
-  name: 'Trend',
-  props: {
-    term: {
-      type: String,
-      default: '',
-      required: true
-    },
-    percentage: {
-      type: Number,
-      default: null
-    },
-    type: {
-      type: Boolean,
-      default: null
-    },
-    target: {
-      type: Number,
-      default: 0
-    },
-    value: {
-      type: Number,
-      default: 0
-    },
-    fixed: {
-      type: Number,
-      default: 2
-    }
-  },
-  data () {
-    return {
-      trend: this.type && 'up' || 'down',
-      rate: this.percentage
-    }
-  },
-  created () {
-    const type = this.type === null ? this.value >= this.target : this.type
-    this.trend = type ? 'up' : 'down'
-    this.rate = (this.percentage === null ? Math.abs(this.value - this.target) * 100 / this.target : this.percentage).toFixed(this.fixed)
-  }
-}
-</script>
-
-<style lang="less" scoped>
-  .chart-trend {
-    display: inline-block;
-    font-size: 14px;
-    line-height: 22px;
-
-    .trend-icon {
-      font-size: 12px;
-
-      &.up, &.down {
-        margin-left: 4px;
-        position: relative;
-        top: 1px;
-
-        i {
-          font-size: 12px;
-          transform: scale(.83);
-        }
-      }
-
-      &.up {
-        color: #f5222d;
-      }
-      &.down {
-        color: #52c41a;
-        top: -1px;
-      }
-    }
-  }
+<template>
+  <div class="chart-trend">
+    {{ term }}
+    <span>{{ rate }}%</span>
+    <span :class="['trend-icon', trend]"><a-icon :type="'caret-' + trend"/></span>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'Trend',
+  props: {
+    term: {
+      type: String,
+      default: '',
+      required: true
+    },
+    percentage: {
+      type: Number,
+      default: null
+    },
+    type: {
+      type: Boolean,
+      default: null
+    },
+    target: {
+      type: Number,
+      default: 0
+    },
+    value: {
+      type: Number,
+      default: 0
+    },
+    fixed: {
+      type: Number,
+      default: 2
+    }
+  },
+  data () {
+    return {
+      trend: this.type && 'up' || 'down',
+      rate: this.percentage
+    }
+  },
+  created () {
+    const type = this.type === null ? this.value >= this.target : this.type
+    this.trend = type ? 'up' : 'down'
+    this.rate = (this.percentage === null ? Math.abs(this.value - this.target) * 100 / this.target : this.percentage).toFixed(this.fixed)
+  }
+}
+</script>
+
+<style lang="less" scoped>
+  .chart-trend {
+    display: inline-block;
+    font-size: 14px;
+    line-height: 22px;
+
+    .trend-icon {
+      font-size: 12px;
+
+      &.up, &.down {
+        margin-left: 4px;
+        position: relative;
+        top: 1px;
+
+        i {
+          font-size: 12px;
+          transform: scale(.83);
+        }
+      }
+
+      &.up {
+        color: #f5222d;
+      }
+      &.down {
+        color: #52c41a;
+        top: -1px;
+      }
+    }
+  }
 </style>

+ 0 - 0
src/components/chart/chart.less → src/components/Charts/chart.less


+ 14 - 0
src/components/Charts/smooth.area.less

@@ -0,0 +1,14 @@
+@import "../index";
+
+@smoothArea-prefix-cls: ~"@{ant-pro-prefix}-smooth-area";
+
+.@{smoothArea-prefix-cls} {
+    position: relative;
+    width: 100%;
+
+    .chart-wrapper {
+        position: absolute;
+        bottom: -28px;
+        width: 100%;
+    }
+}

+ 153 - 0
src/components/DescriptionList/DescriptionList.vue

@@ -0,0 +1,153 @@
+<template>
+  <div :class="['description-list', size, layout === 'vertical' ? 'vertical': 'horizontal']">
+    <div v-if="title" class="title">{{ title }}</div>
+    <a-row>
+      <slot></slot>
+    </a-row>
+  </div>
+</template>
+
+<script>
+import { Col } from 'ant-design-vue/es/grid/'
+
+const Item = {
+  name: 'DetailListItem',
+  props: {
+    term: {
+      type: String,
+      default: '',
+      required: false
+    }
+  },
+  inject: {
+    col: {
+      type: Number
+    }
+  },
+  render () {
+    return (
+      <Col {...{ props: responsive[this.col] }}>
+        <div class="term">{this.$props.term}</div>
+        <div class="content">{this.$slots.default}</div>
+      </Col>
+    )
+  }
+}
+
+const responsive = {
+  1: { xs: 24 },
+  2: { xs: 24, sm: 12 },
+  3: { xs: 24, sm: 12, md: 8 },
+  4: { xs: 24, sm: 12, md: 6 }
+}
+
+export default {
+  name: 'DetailList',
+  Item: Item,
+  components: {
+    Col
+  },
+  props: {
+    title: {
+      type: String,
+      default: '',
+      required: false
+    },
+    col: {
+      type: Number,
+      required: false,
+      default: 3
+    },
+    size: {
+      type: String,
+      required: false,
+      default: 'large'
+    },
+    layout: {
+      type: String,
+      required: false,
+      default: 'horizontal'
+    }
+  },
+  provide () {
+    return {
+      col: this.col > 4 ? 4 : this.col
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+
+  .description-list {
+
+    .title {
+      color: rgba(0,0,0,.85);
+      font-size: 14px;
+      font-weight: 500;
+      margin-bottom: 16px;
+    }
+
+    /deep/ .term {
+      color: rgba(0,0,0,.85);
+      display: table-cell;
+      line-height: 20px;
+      margin-right: 8px;
+      padding-bottom: 16px;
+      white-space: nowrap;
+
+      &:not(:empty):after {
+        content: ":";
+        margin: 0 8px 0 2px;
+        position: relative;
+        top: -.5px;
+      }
+    }
+
+    /deep/ .content {
+      color: rgba(0,0,0,.65);
+      display: table-cell;
+      min-height: 22px;
+      line-height: 22px;
+      padding-bottom: 16px;
+      width: 100%;
+      &:empty {
+        content: ' ';
+        height: 38px;
+        padding-bottom: 16px;
+      }
+    }
+
+    &.small {
+
+      .title {
+        font-size: 14px;
+        color: rgba(0, 0, 0, .65);
+        font-weight: normal;
+        margin-bottom: 12px;
+      }
+      /deep/ .term, .content {
+        padding-bottom: 8px;
+      }
+    }
+
+    &.large {
+      /deep/ .term, .content {
+        padding-bottom: 16px;
+      }
+
+      .title {
+        font-size: 16px;
+      }
+    }
+
+    &.vertical {
+      .term {
+        padding-bottom: 8px;
+      }
+      /deep/ .term, .content {
+        display: block;
+      }
+    }
+  }
+</style>

+ 2 - 0
src/components/DescriptionList/index.js

@@ -0,0 +1,2 @@
+import DescriptionList from './DescriptionList'
+export default DescriptionList

+ 87 - 87
src/views/exception/ExceptionPage.vue → src/components/Exception/ExceptionPage.vue

@@ -1,88 +1,88 @@
-<template>
-  <div class="exception">
-    <div class="img">
-      <img :src="config[type].img"/>
-    </div>
-    <div class="content">
-      <h1>{{ config[type].title }}</h1>
-      <div class="desc">{{ config[type].desc }}</div>
-      <div class="action">
-        <a-button type="primary" @click="handleToHome">返回首页</a-button>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script>
-import types from './type'
-
-export default {
-  name: 'Exception',
-  props: {
-    type: {
-      type: String,
-      default: '404'
-    }
-  },
-  data () {
-    return {
-      config: types
-    }
-  },
-  methods: {
-    handleToHome () {
-      this.$router.push({ name: 'dashboard' })
-    }
-  }
-}
-</script>
-
-<style lang="less" scoped>
-  .exception {
-    min-height: 500px;
-    height: 80%;
-    align-items: center;
-    text-align: center;
-    margin-top: 150px;
-    .img {
-      display: inline-block;
-      padding-right: 52px;
-      zoom: 1;
-      img {
-        height: 360px;
-        max-width: 430px;
-      }
-    }
-    .content {
-      display: inline-block;
-      flex: auto;
-      h1 {
-        color: #434e59;
-        font-size: 72px;
-        font-weight: 600;
-        line-height: 72px;
-        margin-bottom: 24px;
-      }
-      .desc {
-        color: rgba(0, 0, 0, .45);
-        font-size: 20px;
-        line-height: 28px;
-        margin-bottom: 16px;
-      }
-    }
-  }
-
-  .mobile {
-    .exception {
-      margin-top: 30px;
-      .img {
-        padding-right: unset;
-
-        img {
-          height: 40%;
-          max-width: 80%;
-        }
-      }
-    }
-  }
+<template>
+  <div class="exception">
+    <div class="img">
+      <img :src="config[type].img"/>
+    </div>
+    <div class="content">
+      <h1>{{ config[type].title }}</h1>
+      <div class="desc">{{ config[type].desc }}</div>
+      <div class="action">
+        <a-button type="primary" @click="handleToHome">返回首页</a-button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import types from './type'
+
+export default {
+  name: 'Exception',
+  props: {
+    type: {
+      type: String,
+      default: '404'
+    }
+  },
+  data () {
+    return {
+      config: types
+    }
+  },
+  methods: {
+    handleToHome () {
+      this.$router.push({ name: 'dashboard' })
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+  .exception {
+    min-height: 500px;
+    height: 80%;
+    align-items: center;
+    text-align: center;
+    margin-top: 150px;
+    .img {
+      display: inline-block;
+      padding-right: 52px;
+      zoom: 1;
+      img {
+        height: 360px;
+        max-width: 430px;
+      }
+    }
+    .content {
+      display: inline-block;
+      flex: auto;
+      h1 {
+        color: #434e59;
+        font-size: 72px;
+        font-weight: 600;
+        line-height: 72px;
+        margin-bottom: 24px;
+      }
+      .desc {
+        color: rgba(0, 0, 0, .45);
+        font-size: 20px;
+        line-height: 28px;
+        margin-bottom: 16px;
+      }
+    }
+  }
+
+  .mobile {
+    .exception {
+      margin-top: 30px;
+      .img {
+        padding-right: unset;
+
+        img {
+          height: 40%;
+          max-width: 80%;
+        }
+      }
+    }
+  }
 </style>

+ 2 - 0
src/components/Exception/index.js

@@ -0,0 +1,2 @@
+import ExceptionPage from './ExceptionPage.vue'
+export default ExceptionPage

+ 0 - 0
src/views/exception/type.js → src/components/Exception/type.js


+ 59 - 0
src/components/GlobalFooter/GlobalFooter.vue

@@ -0,0 +1,59 @@
+<template>
+  <div class="footer">
+    <div class="links">
+      <a
+        href="https://pro.loacg.com/"
+        target="_blank"
+      >Pro 首页</a>
+      <a
+        href="https://github.com/sendya/ant-design-pro-vue"
+        target="_blank"
+      >
+        <a-icon type="github" />
+      </a>
+      <a href="https://ant.design/">Ant Design</a>
+      <a href="https://vue.ant.design/">Vue Antd</a>
+    </div>
+    <div class="copyright">
+      Copyright
+      <a-icon type="copyright" /> 2018 <span>白鹭学园技术组出品</span>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'GlobalFooter',
+  data () {
+    return {}
+  }
+}
+</script>
+
+<style lang="less" scoped>
+.footer {
+  padding: 0 16px;
+  margin: 48px 0 24px;
+  text-align: center;
+
+  .links {
+    margin-bottom: 8px;
+
+    a {
+      color: rgba(0, 0, 0, 0.45);
+
+      &:hover {
+        color: rgba(0, 0, 0, 0.65);
+      }
+
+      &:not(:last-child) {
+        margin-right: 40px;
+      }
+    }
+  }
+  .copyright {
+    color: rgba(0, 0, 0, 0.45);
+    font-size: 14px;
+  }
+}
+</style>

+ 2 - 0
src/components/GlobalFooter/index.js

@@ -0,0 +1,2 @@
+import GlobalFooter from './GlobalFooter'
+export default GlobalFooter

+ 96 - 128
src/components/page/GlobalHeader.vue → src/components/GlobalHeader/GlobalHeader.vue

@@ -1,128 +1,96 @@
-<template>
-  <!-- , width: fixedHeader ? `calc(100% - ${sidebarOpened ? 256 : 80}px)` : '100%'  -->
-  <a-layout-header
-    v-if="!headerBarFixed"
-    :class="[fixedHeader && 'ant-header-fixedHeader', sidebarOpened ? 'ant-header-side-opened' : 'ant-header-side-closed', ]"
-    :style="{ padding: '0' }"
-  >
-    <div
-      v-if="mode === 'sidemenu'"
-      class="header"
-    >
-      <a-icon
-        v-if="device==='mobile'"
-        class="trigger"
-        :type="collapsed ? 'menu-fold' : 'menu-unfold'"
-        @click="toggle"
-      ></a-icon>
-      <a-icon
-        v-else
-        class="trigger"
-        :type="collapsed ? 'menu-unfold' : 'menu-fold'"
-        @click="toggle"
-      />
-
-      <user-menu></user-menu>
-    </div>
-    <div
-      v-else
-      :class="['top-nav-header-index', theme]"
-    >
-      <div class="header-index-wide">
-        <div class="header-index-left">
-          <logo
-            class="top-nav-header"
-            :show-title="device !== 'mobile'"
-          />
-          <s-menu
-            v-if="device !== 'mobile'"
-            mode="horizontal"
-            :menu="menus"
-            :theme="theme"
-          ></s-menu>
-          <a-icon
-            v-else
-            class="trigger"
-            :type="collapsed ? 'menu-fold' : 'menu-unfold'"
-            @click="toggle"
-          ></a-icon>
-        </div>
-        <user-menu class="header-index-right"></user-menu>
-      </div>
-    </div>
-
-  </a-layout-header>
-</template>
-
-<script>
-import UserMenu from '../tools/UserMenu'
-import SMenu from '../menu/'
-import Logo from '../tools/Logo'
-
-import { mixin } from '@/utils/mixin'
-import { handleScrollHeader } from '@/utils/util'
-
-export default {
-  name: 'GlobalHeader',
-  components: {
-    UserMenu,
-    SMenu,
-    Logo
-  },
-  mixins: [mixin],
-  props: {
-    mode: {
-      type: String,
-      // sidemenu, topmenu
-      default: 'sidemenu'
-    },
-    menus: {
-      type: Array,
-      required: true
-    },
-    theme: {
-      type: String,
-      required: false,
-      default: 'dark'
-    },
-    collapsed: {
-      type: Boolean,
-      required: false,
-      default: false
-    },
-    device: {
-      type: String,
-      required: false,
-      default: 'desktop'
-    }
-  },
-  data () {
-    return {
-      headerBarFixed: false
-    }
-  },
-  mounted () {
-    const _this = this
-    handleScrollHeader(direction => {
-      _this.handleScroll(direction)
-    })
-  },
-  methods: {
-    handleScroll (direction) {
-      if (this.autoHideHeader) {
-        const scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
-        if (direction === 'up') {
-          this.headerBarFixed = false
-        } else {
-          scrollTop > 100 && (this.headerBarFixed = true)
-        }
-      } else {
-        this.headerBarFixed = false
-      }
-    },
-    toggle () {
-      this.$emit('toggle')
-    }
-  }
-}
-</script>
+<template>
+  <a-layout-header
+    v-if="!headerBarFixed"
+    :class="[fixedHeader && 'ant-header-fixedHeader', sidebarOpened ? 'ant-header-side-opened' : 'ant-header-side-closed', ]"
+    :style="{ padding: '0' }"
+  >
+    <div v-if="mode === 'sidemenu'" class="header">
+      <a-icon v-if="device==='mobile'" class="trigger" :type="collapsed ? 'menu-fold' : 'menu-unfold'" @click="toggle"/>
+      <a-icon v-else class="trigger" :type="collapsed ? 'menu-unfold' : 'menu-fold'" @click="toggle"/>
+      <user-menu></user-menu>
+    </div>
+    <div v-else :class="['top-nav-header-index', theme]">
+      <div class="header-index-wide">
+        <div class="header-index-left">
+          <logo class="top-nav-header" :show-title="device !== 'mobile'"/>
+          <s-menu v-if="device !== 'mobile'" mode="horizontal" :menu="menus" :theme="theme" />
+          <a-icon v-else class="trigger" :type="collapsed ? 'menu-fold' : 'menu-unfold'" @click="toggle" />
+        </div>
+        <user-menu class="header-index-right"></user-menu>
+      </div>
+    </div>
+  </a-layout-header>
+</template>
+
+<script>
+import UserMenu from '../tools/UserMenu'
+import SMenu from '../Menu/'
+import Logo from '../tools/Logo'
+
+import { mixin } from '@/utils/mixin'
+import { handleScrollHeader } from '@/utils/util'
+
+export default {
+  name: 'GlobalHeader',
+  components: {
+    UserMenu,
+    SMenu,
+    Logo
+  },
+  mixins: [mixin],
+  props: {
+    mode: {
+      type: String,
+      // sidemenu, topmenu
+      default: 'sidemenu'
+    },
+    menus: {
+      type: Array,
+      required: true
+    },
+    theme: {
+      type: String,
+      required: false,
+      default: 'dark'
+    },
+    collapsed: {
+      type: Boolean,
+      required: false,
+      default: false
+    },
+    device: {
+      type: String,
+      required: false,
+      default: 'desktop'
+    }
+  },
+  data () {
+    return {
+      headerBarFixed: false
+    }
+  },
+  mounted () {
+    const _this = this
+    handleScrollHeader(direction => {
+      _this.handleScroll(direction)
+    })
+  },
+  methods: {
+    handleScroll (direction) {
+      if (this.autoHideHeader) {
+        const scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
+        if (direction === 'up') {
+          this.headerBarFixed = false
+        } else {
+          scrollTop > 100 && (this.headerBarFixed = true)
+        }
+      } else {
+        this.headerBarFixed = false
+      }
+    },
+    toggle () {
+      this.$emit('toggle')
+    }
+  }
+}
+</script>

+ 2 - 0
src/components/GlobalHeader/index.js

@@ -0,0 +1,2 @@
+import GlobalHeader from './GlobalHeader'
+export default GlobalHeader

+ 3 - 4
src/components/menu/SideMenu.vue → src/components/Menu/SideMenu.vue

@@ -18,14 +18,13 @@
 </template>
 
 <script>
-import ALayoutSider from 'ant-design-vue/es/layout/Sider'
-import Logo from '../tools/Logo'
+import Logo from '@/components/tools/Logo'
 import SMenu from './index'
-import { mixin, mixinDevice } from '@/utils/mixin.js'
+import { mixin, mixinDevice } from '@/utils/mixin'
 
 export default {
   name: 'SideMenu',
-  components: { ALayoutSider, Logo, SMenu },
+  components: { Logo, SMenu },
   mixins: [mixin, mixinDevice],
   props: {
     mode: {

+ 0 - 0
src/components/menu/index.js → src/components/Menu/index.js


+ 5 - 6
src/components/menu/menu.js → src/components/Menu/menu.js

@@ -101,16 +101,15 @@ export default {
     },
     renderMenuItem (menu) {
       const target = menu.meta.target || null
-      const props = {
-        to: { name: menu.name },
-        target: target
-      }
+      const tag = target && 'a' || 'router-link'
+      const props = { to: { name: menu.name } }
+      const attrs = { href: menu.path, target: menu.meta.target }
       return (
         <Item {...{ key: menu.path }}>
-          <router-link {...{ props }}>
+          <tag {...{ props, attrs }}>
             {this.renderIcon(menu.meta.icon)}
             <span>{menu.meta.title}</span>
-          </router-link>
+          </tag>
         </Item>
       )
     },

+ 0 - 0
src/components/menu/menu.render.js → src/components/Menu/menu.render.js


+ 89 - 88
src/components/tools/HeaderNotice.vue → src/components/NoticeIcon/NoticeIcon.vue

@@ -1,88 +1,89 @@
-<template>
-  <a-popover
-    v-model="visible"
-    trigger="click"
-    placement="bottomRight"
-    :autoAdjustOverflow="true"
-    :arrowPointAtCenter="true"
-    overlayClassName="header-notice-wrapper"
-    :overlayStyle="{ width: '300px', top: '50px' }">
-    <template slot="content">
-      <a-spin :spinning="loadding">
-        <a-tabs>
-          <a-tab-pane tab="通知" key="1">
-            <a-list>
-              <a-list-item>
-                <a-list-item-meta title="你收到了 14 份新周报" description="一年前">
-                  <a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png"/>
-                </a-list-item-meta>
-              </a-list-item>
-              <a-list-item>
-                <a-list-item-meta title="你推荐的 曲妮妮 已通过第三轮面试" description="一年前">
-                  <a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png"/>
-                </a-list-item-meta>
-              </a-list-item>
-              <a-list-item>
-                <a-list-item-meta title="这种模板可以区分多种通知类型" description="一年前">
-                  <a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png"/>
-                </a-list-item-meta>
-              </a-list-item>
-            </a-list>
-          </a-tab-pane>
-          <a-tab-pane tab="消息" key="2">
-            123
-          </a-tab-pane>
-          <a-tab-pane tab="待办" key="3">
-            123
-          </a-tab-pane>
-        </a-tabs>
-      </a-spin>
-    </template>
-    <span @click="fetchNotice" class="header-notice">
-      <a-badge count="12">
-        <a-icon style="font-size: 16px; padding: 4px" type="bell" />
-      </a-badge>
-    </span>
-  </a-popover>
-</template>
-
-<script>
-export default {
-  name: 'HeaderNotice',
-  data () {
-    return {
-      loadding: false,
-      visible: false
-    }
-  },
-  methods: {
-    fetchNotice () {
-      if (!this.visible) {
-        this.loadding = true
-        setTimeout(() => {
-          this.loadding = false
-        }, 2000)
-      } else {
-        this.loadding = false
-      }
-      this.visible = !this.visible
-    }
-  }
-}
-</script>
-
-<style lang="css">
-  .header-notice-wrapper {
-    top: 50px !important;
-  }
-</style>
-<style lang="less" scoped>
-  .header-notice{
-    display: inline-block;
-    transition: all 0.3s;
-
-    span {
-      vertical-align: initial;
-    }
-  }
-</style>
+<template>
+  <a-popover
+    v-model="visible"
+    trigger="click"
+    placement="bottomRight"
+    overlayClassName="header-notice-wrapper"
+    :autoAdjustOverflow="true"
+    :arrowPointAtCenter="true"
+    :overlayStyle="{ width: '300px', top: '50px' }"
+  >
+    <template slot="content">
+      <a-spin :spinning="loadding">
+        <a-tabs>
+          <a-tab-pane tab="通知" key="1">
+            <a-list>
+              <a-list-item>
+                <a-list-item-meta title="你收到了 14 份新周报" description="一年前">
+                  <a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png"/>
+                </a-list-item-meta>
+              </a-list-item>
+              <a-list-item>
+                <a-list-item-meta title="你推荐的 曲妮妮 已通过第三轮面试" description="一年前">
+                  <a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png"/>
+                </a-list-item-meta>
+              </a-list-item>
+              <a-list-item>
+                <a-list-item-meta title="这种模板可以区分多种通知类型" description="一年前">
+                  <a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png"/>
+                </a-list-item-meta>
+              </a-list-item>
+            </a-list>
+          </a-tab-pane>
+          <a-tab-pane tab="消息" key="2">
+            123
+          </a-tab-pane>
+          <a-tab-pane tab="待办" key="3">
+            123
+          </a-tab-pane>
+        </a-tabs>
+      </a-spin>
+    </template>
+    <span @click="fetchNotice" class="header-notice">
+      <a-badge count="12">
+        <a-icon style="font-size: 16px; padding: 4px" type="bell" />
+      </a-badge>
+    </span>
+  </a-popover>
+</template>
+
+<script>
+export default {
+  name: 'HeaderNotice',
+  data () {
+    return {
+      loadding: false,
+      visible: false
+    }
+  },
+  methods: {
+    fetchNotice () {
+      if (!this.visible) {
+        this.loadding = true
+        setTimeout(() => {
+          this.loadding = false
+        }, 2000)
+      } else {
+        this.loadding = false
+      }
+      this.visible = !this.visible
+    }
+  }
+}
+</script>
+
+<style lang="css">
+  .header-notice-wrapper {
+    top: 50px !important;
+  }
+</style>
+<style lang="less" scoped>
+  .header-notice{
+    display: inline-block;
+    transition: all 0.3s;
+
+    span {
+      vertical-align: initial;
+    }
+  }
+</style>

+ 2 - 0
src/components/NoticeIcon/index.js

@@ -0,0 +1,2 @@
+import NoticeIcon from './NoticeIcon'
+export default NoticeIcon

+ 11 - 50
src/components/page/PageHeader.vue → src/components/PageHeader/PageHeader.vue

@@ -1,16 +1,7 @@
 <template>
   <div class="page-header">
     <div class="page-header-index-wide">
-      <a-breadcrumb class="breadcrumb">
-        <a-breadcrumb-item v-for="(item, index) in breadList" :key="index">
-          <router-link
-            v-if="item.name != name && index != 1"
-            :to="{ path: item.path === '' ? '/' : item.path }"
-          >{{ item.meta.title }}</router-link>
-          <span v-else>{{ item.meta.title }}</span>
-        </a-breadcrumb-item>
-      </a-breadcrumb>
-
+      <s-breadcrumb />
       <div class="detail">
         <div class="main" v-if="!$route.meta.hiddenHeaderContent">
           <div class="row">
@@ -22,7 +13,7 @@
           </div>
           <div class="row">
             <div v-if="avatar" class="avatar">
-              <a-avatar :src="avatar"/>
+              <a-avatar :src="avatar" />
             </div>
             <div v-if="this.$slots.content" class="headerContent">
               <slot name="content"></slot>
@@ -50,13 +41,8 @@ export default {
   },
   props: {
     title: {
-      type: String,
-      default: '',
-      required: false
-    },
-    breadcrumb: {
-      type: Array,
-      default: null,
+      type: [String, Boolean],
+      default: true,
       required: false
     },
     logo: {
@@ -71,36 +57,12 @@ export default {
     }
   },
   data () {
-    return {
-      name: '',
-      breadList: []
-    }
-  },
-  created () {
-    this.getBreadcrumb()
-  },
-  methods: {
-    getBreadcrumb () {
-      this.breadList = []
-      // this.breadList.push({name: 'index', path: '/dashboard/', meta: {title: '首页'}})
-
-      this.name = this.$route.name
-      this.$route.matched.forEach((item) => {
-        // item.name !== 'index' && this.breadList.push(item)
-        this.breadList.push(item)
-      })
-    }
-  },
-  watch: {
-    $route () {
-      this.getBreadcrumb()
-    }
+    return {}
   }
 }
 </script>
 
 <style lang="less" scoped>
-
 .page-header {
   background: #fff;
   padding: 16px 32px 0;
@@ -146,10 +108,9 @@ export default {
         font-size: 20px;
         line-height: 28px;
         font-weight: 500;
-        color: rgba(0,0,0,.85);
+        color: rgba(0, 0, 0, 0.85);
         margin-bottom: 16px;
         flex: auto;
-
       }
       .logo {
         width: 28px;
@@ -157,9 +118,10 @@ export default {
         border-radius: 4px;
         margin-right: 16px;
       }
-      .content, .headerContent {
+      .content,
+      .headerContent {
         flex: auto;
-        color: rgba(0,0,0,.45);
+        color: rgba(0, 0, 0, 0.45);
         line-height: 22px;
 
         .link {
@@ -192,9 +154,7 @@ export default {
 }
 
 .mobile .page-header {
-
   .main {
-
     .row {
       flex-wrap: wrap;
 
@@ -203,7 +163,8 @@ export default {
         margin: 0 2% 8px 0;
       }
 
-      .content, .headerContent {
+      .content,
+      .headerContent {
         flex: 0 1 70%;
 
         .link {

+ 2 - 0
src/components/PageHeader/index.js

@@ -0,0 +1,2 @@
+import PageHeader from './PageHeader'
+export default PageHeader

+ 10 - 0
src/components/PageLoading/index.jsx

@@ -0,0 +1,10 @@
+import { Spin } from 'ant-design-vue'
+
+export default {
+  name: 'PageLoading',
+  render () {
+    return (<div style={{ paddingTop: 100, textAlign: 'center' }}>
+      <Spin size="large" />
+    </div>)
+  }
+}

+ 3 - 3
src/components/setting/SettingDrawer.vue → src/components/SettingDrawer/SettingDrawer.vue

@@ -168,10 +168,10 @@
 </template>
 
 <script>
-import DetailList from '@/components/tools/DetailList'
-import SettingItem from '@/components/setting/SettingItem'
+import { DetailList } from '@/components'
+import SettingItem from './SettingItem'
 import config from '@/config/defaultSettings'
-import { updateTheme, updateColorWeak, colorList } from '@/components/tools/setting'
+import { updateTheme, updateColorWeak, colorList } from './settingConfig'
 import { mixin, mixinDevice } from '@/utils/mixin'
 
 export default {

+ 37 - 37
src/components/setting/SettingItem.vue → src/components/SettingDrawer/SettingItem.vue

@@ -1,38 +1,38 @@
-<template>
-  <div class="setting-drawer-index-item">
-    <h3 class="setting-drawer-index-title">{{ title }}</h3>
-    <slot></slot>
-    <a-divider v-if="divider"/>
-  </div>
-</template>
-
-<script>
-export default {
-  name: 'SettingItem',
-  props: {
-    title: {
-      type: String,
-      default: ''
-    },
-    divider: {
-      type: Boolean,
-      default: false
-    }
-  }
-}
-</script>
-
-<style lang="less" scoped>
-
-  .setting-drawer-index-item {
-    margin-bottom: 24px;
-
-    .setting-drawer-index-title {
-      font-size: 14px;
-      color: rgba(0, 0, 0, .85);
-      line-height: 22px;
-      margin-bottom: 12px;
-    }
-
-  }
+<template>
+  <div class="setting-drawer-index-item">
+    <h3 class="setting-drawer-index-title">{{ title }}</h3>
+    <slot></slot>
+    <a-divider v-if="divider"/>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'SettingItem',
+  props: {
+    title: {
+      type: String,
+      default: ''
+    },
+    divider: {
+      type: Boolean,
+      default: false
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+
+  .setting-drawer-index-item {
+    margin-bottom: 24px;
+
+    .setting-drawer-index-title {
+      font-size: 14px;
+      color: rgba(0, 0, 0, .85);
+      line-height: 22px;
+      margin-bottom: 12px;
+    }
+
+  }
 </style>

+ 2 - 0
src/components/SettingDrawer/index.js

@@ -0,0 +1,2 @@
+import SettingDrawer from './SettingDrawer'
+export default SettingDrawer

+ 0 - 0
src/components/tools/setting.js → src/components/SettingDrawer/settingConfig.js


+ 0 - 0
src/components/table/README.md → src/components/Table/README.md


+ 5 - 4
src/components/table/index.js → src/components/Table/index.js

@@ -59,7 +59,7 @@ export default {
       default: false
     },
     showPagination: {
-      type: String,
+      type: String | Boolean,
       default: 'auto'
     }
   }),
@@ -119,9 +119,9 @@ export default {
       this.localLoading = true
       const parameter = Object.assign({
         pageNo: (pagination && pagination.current) ||
-            this.localPagination.current,
+            this.localPagination.current || this.pageNum,
         pageSize: (pagination && pagination.pageSize) ||
-            this.localPagination.pageSize
+            this.localPagination.pageSize || this.pageSize
       },
       (sorter && sorter.field && {
         sortField: sorter.field
@@ -153,7 +153,8 @@ export default {
 
           // 这里用于判断接口是否有返回 r.totalCount 或 this.showPagination = false
           // 当情况满足时,表示数据不满足分页大小,关闭 table 分页功能
-          !r.totalCount && ['auto', false].includes(this.showPagination) && (this.localPagination = false)
+
+          (!this.showPagination || !r.totalCount && this.showPagination === 'auto') && (this.localPagination = false)
           this.localDataSource = r.data // 返回结果中的数组数据
           this.localLoading = false
         })

+ 39 - 0
src/components/TagSelect/TagSelectOption.jsx

@@ -0,0 +1,39 @@
+import { Tag } from 'ant-design-vue'
+const { CheckableTag } = Tag
+
+export default {
+  name: 'TagSelectOption',
+  props: {
+    prefixCls: {
+      type: String,
+      default: 'ant-pro-tag-select-option'
+    },
+    value: {
+      type: [String, Number, Object],
+      default: ''
+    },
+    checked: {
+      type: Boolean,
+      default: false
+    }
+  },
+  data () {
+    return {
+      localChecked: this.checked || false
+    }
+  },
+  watch: {
+    '$parent.checkAll' (val) {
+      this.localChecked = val
+    }
+  },
+  render () {
+    const { $slots, value } = this
+    const onChange = (checked) => {
+      this.$emit('change', { value, checked })
+    }
+    return (<CheckableTag key={value} vModel={this.localChecked} onChange={onChange}>
+      {$slots.default}
+    </CheckableTag>)
+  }
+}

+ 111 - 0
src/components/TagSelect/index.jsx

@@ -0,0 +1,111 @@
+import PropTypes from 'ant-design-vue/es/_util/vue-types'
+import Option from './TagSelectOption.jsx'
+import { filterEmpty } from '@/components/_util/util'
+
+export default {
+  Option,
+  name: 'TagSelect',
+  model: {
+    prop: 'checked',
+    event: 'change'
+  },
+  props: {
+    prefixCls: {
+      type: String,
+      default: 'ant-pro-tag-select'
+    },
+    defaultValue: {
+      type: PropTypes.array,
+      default: null
+    },
+    value: {
+      type: PropTypes.array,
+      default: null
+    },
+    expandable: {
+      type: Boolean,
+      default: false
+    },
+    hideCheckAll: {
+      type: Boolean,
+      default: false
+    }
+  },
+  data () {
+    return {
+      expand: false,
+      checkAll: false,
+      localCheckAll: false,
+      items: [],
+      val: this.value || this.defaultValue || []
+    }
+  },
+  methods: {
+    onChange (checked) {
+      const key = Object.keys(this.items).filter(key => key === checked.value)
+      this.items[key] = checked.checked
+      // console.log(this.items)
+      const bool = Object.values(this.items).lastIndexOf(false)
+      console.log('bool', bool, 'this.checkAll', this.checkAll)
+      if (bool === -1) {
+        !this.checkAll && (this.checkAll = true)
+      } else {
+        this.checkAll && (this.checkAll = false)
+        this.localCheckAll = false
+      }
+    },
+    onCheckAll (checked) {
+      this.checkAll = checked.checked
+      // Object.keys(this.items)
+      // this.items[k] = checked.checked
+      Object.values(this.items).forEach(v => {
+        v = checked.checked
+      })
+    },
+    getItemsKey (items) {
+      const totalItem = {}
+      items.forEach(item => {
+        totalItem[item.componentOptions.propsData && item.componentOptions.propsData.value] = false
+      })
+      return totalItem
+    },
+    // CheckAll Button
+    renderCheckAll () {
+      return !this.hideCheckAll && (<Option key={'total'} checked={this.localCheckAll} onChange={this.onCheckAll}>All</Option>) || null
+    },
+    // expandable
+    renderExpandable () {
+
+    },
+    // render option
+    renderTags (items) {
+      const listeners = {
+        change: (checked) => {
+          this.onChange(checked)
+          this.$emit('change', checked)
+        }
+      }
+
+      return items.map(vnode => {
+        const options = vnode.componentOptions
+        options.listeners = listeners
+        return vnode
+      })
+    }
+  },
+  render () {
+    const { $props: { prefixCls } } = this
+    const classString = {
+      [`${prefixCls}`]: true
+    }
+    const tagItems = filterEmpty(this.$slots.default)
+    this.items = this.getItemsKey(tagItems)
+    console.log(this.items)
+    return (
+      <div class={classString}>
+        {this.renderCheckAll()}
+        {this.renderTags(tagItems)}
+      </div>
+    )
+  }
+}

+ 49 - 51
src/components/global.less

@@ -1,5 +1,9 @@
 @import './index.less';
 
+html {
+  overflow: auto;
+}
+
 body {
   // 打开滚动条固定显示
   overflow-y: scroll;
@@ -7,16 +11,18 @@ body {
   &.colorWeak {
     filter: invert(80%);
   }
+  &.userLayout {
+    overflow: auto;
+  }
 }
 
 .layout.ant-layout {
   height: auto;
   overflow-x: hidden;
 
-  &.mobile,&.tablet {
-
+  &.mobile,
+  &.tablet {
     .ant-layout-content {
-
       .content {
         margin: 24px 0 0;
       }
@@ -47,9 +53,9 @@ body {
   &.mobile {
     .sidemenu {
       .ant-header-fixedHeader {
-
-        &.ant-header-side-opened, &.ant-header-side-closed  {
-          width: 100%
+        &.ant-header-side-opened,
+        &.ant-header-side-closed {
+          width: 100%;
         }
       }
     }
@@ -64,7 +70,7 @@ body {
     line-height: 64px;
     padding: 0 24px;
     cursor: pointer;
-    transition: color .3s;
+    transition: color 0.3s;
     &:hover {
       background: rgba(0, 0, 0, 0.025);
     }
@@ -77,7 +83,7 @@ body {
       right: 0;
       z-index: 9;
       width: 100%;
-      transition: width .2s;
+      transition: width 0.2s;
 
       &.ant-header-side-opened {
         width: 100%;
@@ -98,7 +104,6 @@ body {
         max-width: unset;
       }
     }
-
   }
 
   .sidemenu {
@@ -108,14 +113,14 @@ body {
       right: 0;
       z-index: 9;
       width: 100%;
-      transition: width .2s;
+      transition: width 0.2s;
 
       &.ant-header-side-opened {
-        width: calc(100% - 256px)
+        width: calc(100% - 256px);
       }
 
       &.ant-header-side-closed {
-        width: calc(100% - 80px)
+        width: calc(100% - 80px);
       }
     }
   }
@@ -124,12 +129,12 @@ body {
     height: 64px;
     padding: 0 12px 0 0;
     background: #fff;
-    box-shadow: 0 1px 4px rgba(0, 21, 41, .08);
+    box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
     position: relative;
   }
 
-  .header, .top-nav-header-index {
-
+  .header,
+  .top-nav-header-index {
     .user-wrapper {
       float: right;
       height: 100%;
@@ -138,7 +143,7 @@ body {
         cursor: pointer;
         padding: 0 12px;
         display: inline-block;
-        transition: all .3s;
+        transition: all 0.3s;
         height: 100%;
         color: rgba(0, 0, 0, 0.65);
 
@@ -149,7 +154,7 @@ body {
         .avatar {
           margin: 20px 8px 20px 0;
           color: #1890ff;
-          background: hsla(0, 0%, 100%, .85);
+          background: hsla(0, 0%, 100%, 0.85);
           vertical-align: middle;
         }
 
@@ -162,7 +167,6 @@ body {
 
     &.dark {
       .user-wrapper {
-
         .action {
           color: rgba(255, 255, 255, 0.85);
           a {
@@ -177,13 +181,11 @@ body {
     }
   }
 
-  &.mobile,&.tablet {
+  &.mobile,
+  &.tablet {
     .top-nav-header-index {
-
       .header-index-wide {
-
         .header-index-left {
-
           .trigger {
             color: rgba(255, 255, 255, 0.85);
             padding: 0 12px;
@@ -201,9 +203,7 @@ body {
       }
 
       &.light {
-
         .header-index-wide {
-
           .header-index-left {
             .trigger {
               color: rgba(0, 0, 0, 0.65);
@@ -218,13 +218,11 @@ body {
   &.tablet {
     // overflow: hidden; text-overflow:ellipsis; white-space: nowrap;
     .top-nav-header-index {
-
       .header-index-wide {
-
         .header-index-left {
           .logo > a {
             overflow: hidden;
-            text-overflow:ellipsis;
+            text-overflow: ellipsis;
             white-space: nowrap;
           }
         }
@@ -234,13 +232,12 @@ body {
         }
       }
     }
-
   }
 
   .top-nav-header-index {
-    box-shadow: 0 1px 4px rgba(0,21,41,.08);
+    box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
     position: relative;
-    transition: background .3s,width .2s;
+    transition: background 0.3s, width 0.2s;
 
     .header-index-wide {
       max-width: 1200px;
@@ -267,10 +264,11 @@ body {
           height: 64px;
           position: relative;
           line-height: 64px;
-          transition: all .3s;
+          transition: all 0.3s;
           overflow: hidden;
 
-          img, svg {
+          img,
+          svg {
             display: inline-block;
             vertical-align: middle;
             height: 32px;
@@ -318,6 +316,10 @@ body {
     padding: 0 12px 0 0;
   }
 
+  // footer
+  .ant-layout-footer {
+    padding: 0;
+  }
 }
 
 .topmenu {
@@ -346,21 +348,21 @@ body {
   }
 
   .ant-drawer-body {
-    padding: 0
+    padding: 0;
   }
 }
 
 // 菜单样式
 .sider {
-  box-shadow: 2px 0 6px rgba(0, 21, 41, .35);
+  box-shadow: 2px 0 6px rgba(0, 21, 41, 0.35);
   position: relative;
   z-index: 10;
   height: auto;
 
-  .ant-layout-sider-children  {
+  .ant-layout-sider-children {
     padding-top: 64px;
     overflow-y: hidden;
-    &:hover{
+    &:hover {
       overflow-y: auto;
     }
   }
@@ -378,18 +380,21 @@ body {
     height: 64px;
     line-height: 64px;
     padding-left: 24px;
-    -webkit-transition: all .3s;
-    transition: all .3s;
+    -webkit-transition: all 0.3s;
+    transition: all 0.3s;
     background: #002140;
     overflow: hidden;
     z-index: 9;
 
-    img, svg, h1 {
+    img,
+    svg,
+    h1 {
       display: inline-block;
       vertical-align: middle;
     }
 
-    img, svg {
+    img,
+    svg {
       height: 32px;
       width: 32px;
     }
@@ -398,7 +403,7 @@ body {
       color: #fff;
       font-size: 20px;
       margin: 0 0 0 12px;
-      font-family: Avenir,Helvetica Neue,Arial,Helvetica,sans-serif;
+      font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
       font-weight: 600;
       vertical-align: middle;
     }
@@ -421,7 +426,6 @@ body {
       border-right-color: transparent;
     }
   }
-
 }
 
 // 外置的样式控制
@@ -439,12 +443,10 @@ body {
 
   .ant-dropdown-menu-item > .anticon:first-child,
   .ant-dropdown-menu-item > a > .anticon:first-child,
-  .ant-dropdown-menu-submenu-title > .anticon:first-child
-  .ant-dropdown-menu-submenu-title > a > .anticon:first-child {
+  .ant-dropdown-menu-submenu-title > .anticon:first-child .ant-dropdown-menu-submenu-title > a > .anticon:first-child {
     min-width: 12px;
     margin-right: 8px;
   }
-
 }
 
 // 数据列表 样式
@@ -453,9 +455,7 @@ body {
 }
 
 .table-page-search-wrapper {
-
   .ant-form-inline {
-
     .ant-form-item {
       display: flex;
       margin-bottom: 24px;
@@ -467,7 +467,7 @@ body {
         vertical-align: middle;
       }
 
-      >.ant-form-item-label {
+      > .ant-form-item-label {
         line-height: 32px;
         padding-right: 8px;
         width: auto;
@@ -484,11 +484,9 @@ body {
     margin-bottom: 24px;
     white-space: nowrap;
   }
-
 }
 
 .content {
-
   .table-operator {
     margin-bottom: 18px;
 
@@ -496,4 +494,4 @@ body {
       margin-right: 8px;
     }
   }
-}
+}

+ 18 - 12
src/components/index.js

@@ -1,13 +1,14 @@
 // chart
-import Bar from '@/components/chart/Bar'
-import ChartCard from '@/components/chart/ChartCard'
-import Liquid from '@/components/chart/Liquid'
-import MiniArea from '@/components/chart/MiniArea'
-import MiniBar from '@/components/chart/MiniBar'
-import MiniProgress from '@/components/chart/MiniProgress'
-import Radar from '@/components/chart/Radar'
-import RankList from '@/components/chart/RankList'
-import TransferBar from '@/components/chart/TransferBar'
+import Bar from '@/components/Charts/Bar'
+import ChartCard from '@/components/Charts/ChartCard'
+import Liquid from '@/components/Charts/Liquid'
+import MiniArea from '@/components/Charts/MiniArea'
+import MiniSmoothArea from '@/components/Charts/MiniSmoothArea'
+import MiniBar from '@/components/Charts/MiniBar'
+import MiniProgress from '@/components/Charts/MiniProgress'
+import Radar from '@/components/Charts/Radar'
+import RankList from '@/components/Charts/RankList'
+import TransferBar from '@/components/Charts/TransferBar'
 
 // pro components
 import AvatarList from '@/components/AvatarList'
@@ -15,13 +16,14 @@ import CountDown from '@/components/CountDown'
 import Ellipsis from '@/components/Ellipsis'
 import FooterToolbar from '@/components/FooterToolbar'
 import NumberInfo from '@/components/NumberInfo'
-import DetailList from '@/components/tools/DetailList'
+import DescriptionList from '@/components/DescriptionList'
 import Tree from '@/components/Tree/Tree'
 import Trend from '@/components/Trend'
-import STable from '@/components/table'
+import STable from '@/components/Table'
 import MultiTab from '@/components/MultiTab'
 import Result from '@/components/Result'
 import IconSelector from '@/components/IconSelector'
+import ExceptionPage from '@/components/Exception'
 
 export {
   AvatarList,
@@ -29,6 +31,7 @@ export {
   ChartCard,
   Liquid,
   MiniArea,
+  MiniSmoothArea,
   MiniBar,
   MiniProgress,
   Radar,
@@ -39,10 +42,13 @@ export {
   Ellipsis,
   FooterToolbar,
   NumberInfo,
-  DetailList,
+  DescriptionList,
+  // 兼容写法,请勿继续使用
+  DescriptionList as DetailList,
   Tree,
   STable,
   MultiTab,
   Result,
+  ExceptionPage,
   IconSelector
 }

+ 0 - 53
src/components/layouts/BasicLayout.vue

@@ -1,53 +0,0 @@
-<template>
-  <global-layout>
-    <multi-tab v-if="$store.getters.multiTab"></multi-tab>
-    <transition name="page-transition">
-      <route-view />
-    </transition>
-  </global-layout>
-</template>
-
-<script>
-import RouteView from '@/components/layouts/RouteView'
-import MultiTab from '@/components/MultiTab'
-import GlobalLayout from '@/components/page/GlobalLayout'
-
-export default {
-  name: 'BasicLayout',
-  components: {
-    RouteView,
-    MultiTab,
-    GlobalLayout
-  },
-  data () {
-    return {}
-  }
-}
-</script>
-
-<style lang="less">
-  @import url('../global.less');
-
-  /*
- * The following styles are auto-applied to elements with
- * transition="page-transition" when their visibility is toggled
- * by Vue.js.
- *
- * You can easily play with the page transition by editing
- * these styles.
- */
-
-  .page-transition-enter {
-    opacity: 0;
-  }
-
-  .page-transition-leave-active {
-    opacity: 0;
-  }
-
-  .page-transition-enter .page-transition-container,
-  .page-transition-leave-active .page-transition-container {
-    -webkit-transform: scale(1.1);
-    transform: scale(1.1);
-  }
-</style>

+ 0 - 85
src/components/layouts/PageView.vue

@@ -1,85 +0,0 @@
-<template>
-  <page-layout :desc="description" :title="getTitle" :link-list="linkList" :search="search" :tabs="tabs">
-    <div slot="extra" class="extra-img">
-      <img v-if="typeof extraImage !== 'undefined'" :src="extraImage"/>
-    </div>
-    <!-- keep-alive  -->
-    <route-view ref="content"></route-view>
-  </page-layout>
-</template>
-
-<script>
-import PageLayout from '../page/PageLayout'
-import RouteView from './RouteView'
-
-export default {
-  name: 'PageContent',
-  components: {
-    RouteView,
-    PageLayout
-  },
-  data () {
-    return {
-      title: '',
-      description: '',
-      linkList: [],
-      extraImage: '',
-      search: false,
-      tabs: {}
-    }
-  },
-  mounted () {
-    this.getPageHeaderInfo()
-  },
-  updated () {
-    this.getPageHeaderInfo()
-  },
-  computed: {
-
-    getTitle () {
-      return this.$route.meta.title
-    }
-
-  },
-  methods: {
-    getPageHeaderInfo () {
-      // eslint-disable-next-line
-        this.title = this.$route.meta.title
-      // 因为套用了一层 route-view 所以要取 ref 对象下的子节点的第一个对象
-      const content = this.$refs.content && this.$refs.content.$children[0]
-
-      if (content) {
-        this.description = content.description
-        this.linkList = content.linkList
-        this.extraImage = content.extraImage
-        this.search = content.search === true
-        this.tabs = content.tabs
-      }
-    }
-  }
-}
-</script>
-
-<style lang="less" scoped>
-  .extra-img {
-    margin-top: -60px;
-    text-align: center;
-    width: 195px;
-
-    img {
-      width: 100%;
-    }
-  }
-
-  .mobile {
-    .extra-img{
-      margin-top: 0;
-      text-align: center;
-      width: 96px;
-
-      img{
-        width: 100%;
-      }
-    }
-  }
-</style>

+ 0 - 7
src/components/layouts/index.js

@@ -1,7 +0,0 @@
-import UserLayout from '@/components/layouts/UserLayout'
-import BlankLayout from '@/components/layouts/BlankLayout'
-import BasicLayout from '@/components/layouts/BasicLayout'
-import RouteView from '@/components/layouts/RouteView'
-import PageView from '@/components/layouts/PageView'
-
-export { UserLayout, BasicLayout, BlankLayout, RouteView, PageView }

+ 0 - 55
src/components/page/GlobalFooter.vue

@@ -1,55 +0,0 @@
-<template>
-  <div class="footer">
-    <div class="links">
-      <a href="https://pro.ant.design/" target="_blank">Pro 首页</a>
-      <a href="https://github.com/ant-design/ant-design-pro" target="_blank">
-        <a-icon type="github"/>
-      </a>
-      <a href="https://ant.design/">Ant Design</a>
-      <a href="https://vue.ant.design/">Vue Antd</a>
-    </div>
-    <div class="copyright">
-      Copyright
-      <a-icon type="copyright"/>
-      2018 <span>白鹭学园技术组出品</span>
-    </div>
-  </div>
-</template>
-
-<script>
-export default {
-  name: 'LayoutFooter',
-  data () {
-    return {
-    }
-  }
-}
-</script>
-
-<style lang="less" scoped>
-  .footer {
-    padding: 0 16px;
-    margin: 48px 0 24px;
-    text-align: center;
-
-    .links {
-      margin-bottom: 8px;
-
-      a {
-        color: rgba(0, 0, 0, .45);
-
-        &:hover {
-          color: rgba(0, 0, 0, .65);
-        }
-
-        &:not(:last-child) {
-          margin-right: 40px;
-        }
-      }
-    }
-    .copyright {
-      color: rgba(0, 0, 0, .45);
-      font-size: 14px;
-    }
-  }
-</style>

+ 0 - 157
src/components/page/GlobalLayout.vue

@@ -1,157 +0,0 @@
-<template>
-  <a-layout class="layout" :class="[device]">
-
-    <template v-if="isSideMenu()">
-      <a-drawer
-        v-if="isMobile()"
-        :wrapClassName="'drawer-sider ' + navTheme"
-        :closable="false"
-        :visible="collapsed"
-        placement="left"
-        @close="() => this.collapsed = false"
-      >
-        <side-menu
-          :menus="menus"
-          :theme="navTheme"
-          :collapsed="false"
-          :collapsible="true"
-          mode="inline"
-          @menuSelect="menuSelect"></side-menu>
-      </a-drawer>
-
-      <side-menu
-        v-else
-        mode="inline"
-        :menus="menus"
-        :theme="navTheme"
-        :collapsed="collapsed"
-        :collapsible="true"></side-menu>
-    </template>
-    <!-- 下次优化这些代码 -->
-    <template v-else>
-      <a-drawer
-        v-if="isMobile()"
-        :wrapClassName="'drawer-sider ' + navTheme"
-        placement="left"
-        @close="() => this.collapsed = false"
-        :closable="false"
-        :visible="collapsed"
-      >
-        <side-menu
-          :menus="menus"
-          :theme="navTheme"
-          :collapsed="false"
-          :collapsible="true"
-          mode="inline"
-          @menuSelect="menuSelect"></side-menu>
-      </a-drawer>
-    </template>
-
-    <a-layout :class="[layoutMode, `content-width-${contentWidth}`]" :style="{ paddingLeft: contentPaddingLeft, minHeight: '100vh' }">
-      <!-- layout header -->
-      <global-header
-        :mode="layoutMode"
-        :menus="menus"
-        :theme="navTheme"
-        :collapsed="collapsed"
-        :device="device"
-        @toggle="toggle"
-      />
-
-      <!-- layout content -->
-      <a-layout-content :style="{ margin: $store.getters.multiTab ? '24px 24px 0' : '24px 24px 0', height: '100%', paddingTop: fixedHeader ? '64px' : '0' }">
-        <slot></slot>
-      </a-layout-content>
-
-      <!-- layout footer -->
-      <a-layout-footer style="padding: 0">
-        <global-footer />
-      </a-layout-footer>
-      <setting-drawer></setting-drawer>
-    </a-layout>
-  </a-layout>
-</template>
-
-<script>
-import SideMenu from '@/components/menu/SideMenu'
-import GlobalHeader from '@/components/page/GlobalHeader'
-import GlobalFooter from '@/components/page/GlobalFooter'
-import SettingDrawer from '@/components/setting/SettingDrawer'
-import { triggerWindowResizeEvent } from '@/utils/util'
-import { mapState, mapActions } from 'vuex'
-import { mixin, mixinDevice } from '@/utils/mixin.js'
-
-export default {
-  name: 'GlobalLayout',
-  components: {
-    SideMenu,
-    GlobalHeader,
-    GlobalFooter,
-    SettingDrawer
-  },
-  mixins: [mixin, mixinDevice],
-  data () {
-    return {
-      collapsed: false,
-      menus: []
-    }
-  },
-  computed: {
-    ...mapState({
-      // 主路由
-      mainMenu: state => state.permission.addRouters
-    }),
-    contentPaddingLeft () {
-      if (!this.fixSidebar || this.isMobile()) {
-        return '0'
-      }
-      if (this.sidebarOpened) {
-        return '256px'
-      }
-      return '80px'
-    }
-  },
-  watch: {
-    sidebarOpened (val) {
-      this.collapsed = !val
-    }
-  },
-  created () {
-    this.menus = this.mainMenu.find((item) => item.path === '/').children
-    this.collapsed = !this.sidebarOpened
-  },
-  mounted () {
-    const userAgent = navigator.userAgent
-    if (userAgent.indexOf('Edge') > -1) {
-      this.$nextTick(() => {
-        this.collapsed = !this.collapsed
-        setTimeout(() => {
-          this.collapsed = !this.collapsed
-        }, 16)
-      })
-    }
-  },
-  methods: {
-    ...mapActions(['setSidebar']),
-    toggle () {
-      this.collapsed = !this.collapsed
-      this.setSidebar(!this.collapsed)
-      triggerWindowResizeEvent()
-    },
-    paddingCalc () {
-      let left = ''
-      if (this.sidebarOpened) {
-        left = this.isDesktop() ? '256px' : '80px'
-      } else {
-        left = this.isMobile() && '0' || (this.fixSidebar && '80px' || '0')
-      }
-      return left
-    },
-    menuSelect () {
-      if (!this.isDesktop()) {
-        this.collapsed = false
-      }
-    }
-  }
-}
-</script>

+ 0 - 124
src/components/page/PageLayout.vue

@@ -1,124 +0,0 @@
-<template>
-  <div :style="!$route.meta.pageHeader ? 'margin: -24px -24px 0px;' : null">
-    <!-- pageHeader , route meta hideHeader:true on hide -->
-    <page-header v-if="!$route.meta.pageHeader" :title="title" :logo="logo" :avatar="avatar">
-      <slot slot="action" name="action"></slot>
-      <slot slot="content" name="headerContent"></slot>
-      <div slot="content" v-if="!this.$slots.headerContent && desc">
-        <p style="font-size: 14px;color: rgba(0,0,0,.65)">{{ desc }}</p>
-        <div class="link">
-          <template v-for="(link, index) in linkList">
-            <a :key="index" :href="link.href">
-              <a-icon :type="link.icon"/>
-              <span>{{ link.title }}</span>
-            </a>
-          </template>
-        </div>
-      </div>
-      <slot slot="extra" name="extra"></slot>
-      <div slot="pageMenu">
-        <div class="page-menu-search" v-if="search">
-          <a-input-search style="width: 80%; max-width: 522px;" placeholder="请输入..." size="large" enterButton="搜索" />
-        </div>
-        <div class="page-menu-tabs" v-if="tabs && tabs.items">
-          <!-- @change="callback" :activeKey="activeKey" -->
-          <a-tabs :tabBarStyle="{margin: 0}" @change="tabs.callback" :activeKey="tabs.active()">
-            <a-tab-pane v-for="item in tabs.items" :tab="item.title" :key="item.key"></a-tab-pane>
-          </a-tabs>
-        </div>
-      </div>
-    </page-header>
-    <div class="content">
-      <div :class="['page-header-index-wide']">
-        <slot></slot>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script>
-import PageHeader from './PageHeader'
-
-export default {
-  name: 'LayoutContent',
-  components: {
-    PageHeader
-  },
-  // ['desc', 'logo', 'title', 'avatar', 'linkList', 'extraImage']
-  props: {
-    desc: {
-      type: String,
-      default: null
-    },
-    logo: {
-      type: String,
-      default: null
-    },
-    title: {
-      type: String,
-      default: null
-    },
-    avatar: {
-      type: String,
-      default: null
-    },
-    linkList: {
-      type: Array,
-      default: null
-    },
-    extraImage: {
-      type: String,
-      default: null
-    },
-    search: {
-      type: Boolean,
-      default: false
-    },
-    tabs: {
-      type: Object,
-      default: () => ({})
-    }
-  },
-  methods: {
-  }
-}
-</script>
-
-<style lang="less" scoped>
-  .content {
-    margin: 24px 24px 0;
-
-    .link {
-      margin-top: 16px;
-
-      &:not(:empty) {
-        margin-bottom: 16px;
-      }
-      a {
-        margin-right: 32px;
-        height: 24px;
-        line-height: 24px;
-        display: inline-block;
-
-        i {
-          font-size: 24px;
-          margin-right: 8px;
-          vertical-align: middle;
-        }
-        span {
-          height: 24px;
-          line-height: 24px;
-          display: inline-block;
-          vertical-align: middle;
-        }
-      }
-    }
-  }
-  .page-menu-search {
-    text-align: center;
-    margin-bottom: 16px;
-  }
-  .page-menu-tabs {
-    margin-top: 48px;
-  }
-</style>

+ 0 - 58
src/components/page/SHeaderNotice.vue

@@ -1,58 +0,0 @@
-<template>
-  <a-popover trigger="click" placement="bottomRight" :overlayStyle="{ width: '300px' }">
-    <template slot="content">
-      <a-spin :spinning="loadding">
-        <a-tabs>
-          <a-tab-pane v-for="(tab, k) in tabs" :tab="tab.title" :key="k">
-
-          </a-tab-pane>
-        </a-tabs>
-      </a-spin>
-    </template>
-    <span @click="fetchNotice" class="header-notice">
-      <a-badge count="12">
-        <a-icon style="font-size: 16px; padding: 4px" type="bell" />
-      </a-badge>
-    </span>
-  </a-popover>
-</template>
-
-<script>
-export default {
-  name: 'HeaderNotice',
-  props: {
-    tabs: {
-      type: Array,
-      default: null,
-      required: true
-    }
-  },
-  data () {
-    return {
-      loadding: false
-    }
-  },
-  methods: {
-    fetchNotice () {
-      if (this.loadding) {
-        this.loadding = false
-        return
-      }
-      this.loadding = true
-      setTimeout(() => {
-        this.loadding = false
-      }, 2000)
-    }
-  }
-}
-</script>
-
-<style lang="less" scoped>
-  .header-notice{
-    display: inline-block;
-    transition: all 0.3s;
-    span {
-      vertical-align: initial;
-    }
-  }
-</style>

+ 8 - 10
src/components/tools/Breadcrumb.vue

@@ -1,9 +1,10 @@
 <template>
   <a-breadcrumb class="breadcrumb">
-    <a-breadcrumb-item v-for="(item, index) in breadList" :key="index">
-      <router-link v-if="item.name != name" :to="{ path: item.path }">
-        {{ item.meta.title }}
-      </router-link>
+    <a-breadcrumb-item v-for="(item, index) in breadList" :key="item.name">
+      <router-link
+        v-if="item.name != name && index != 1"
+        :to="{ path: item.path === '' ? '/' : item.path }"
+      >{{ item.meta.title }}</router-link>
       <span v-else>{{ item.meta.title }}</span>
     </a-breadcrumb-item>
   </a-breadcrumb>
@@ -22,14 +23,12 @@ export default {
   },
   methods: {
     getBreadcrumb () {
-      console.log('this.$route.matched', this.$route.matched)
-
       this.breadList = []
-      this.breadList.push({ name: 'index', path: '/dashboard/', meta: { title: '首页' } })
+      // this.breadList.push({name: 'index', path: '/dashboard/', meta: {title: '首页'}})
 
       this.name = this.$route.name
-      this.$route.matched.forEach((item) => {
-        // item.meta.name === 'dashboard' ? item.path = '/dashboard' : this.$route.path === item.path
+      this.$route.matched.forEach(item => {
+        // item.name !== 'index' && this.breadList.push(item)
         this.breadList.push(item)
       })
     }
@@ -43,5 +42,4 @@ export default {
 </script>
 
 <style scoped>
-
 </style>

+ 3 - 151
src/components/tools/DetailList.vue

@@ -1,153 +1,5 @@
-<template>
-  <div :class="['detail-list', size, layout === 'vertical' ? 'vertical': 'horizontal']">
-    <div v-if="title" class="title">{{ title }}</div>
-    <a-row>
-      <slot></slot>
-    </a-row>
-  </div>
-</template>
-
 <script>
-import { Col } from 'ant-design-vue/es/grid/'
-
-const Item = {
-  name: 'DetailListItem',
-  props: {
-    term: {
-      type: String,
-      default: '',
-      required: false
-    }
-  },
-  inject: {
-    col: {
-      type: Number
-    }
-  },
-  render () {
-    return (
-      <Col {...{ props: responsive[this.col] }}>
-        <div class="term">{this.$props.term}</div>
-        <div class="content">{this.$slots.default}</div>
-      </Col>
-    )
-  }
-}
-
-const responsive = {
-  1: { xs: 24 },
-  2: { xs: 24, sm: 12 },
-  3: { xs: 24, sm: 12, md: 8 },
-  4: { xs: 24, sm: 12, md: 6 }
-}
-
-export default {
-  name: 'DetailList',
-  Item: Item,
-  components: {
-    Col
-  },
-  props: {
-    title: {
-      type: String,
-      default: '',
-      required: false
-    },
-    col: {
-      type: Number,
-      required: false,
-      default: 3
-    },
-    size: {
-      type: String,
-      required: false,
-      default: 'large'
-    },
-    layout: {
-      type: String,
-      required: false,
-      default: 'horizontal'
-    }
-  },
-  provide () {
-    return {
-      col: this.col > 4 ? 4 : this.col
-    }
-  }
-}
+/* WARNING: 兼容老引入,请勿继续使用 */
+import DescriptionList from '@/components/DescriptionList'
+export default DescriptionList
 </script>
-
-<style lang="less" scoped>
-
-  .detail-list {
-
-    .title {
-      color: rgba(0,0,0,.85);
-      font-size: 14px;
-      font-weight: 500;
-      margin-bottom: 16px;
-    }
-
-    /deep/ .term {
-      color: rgba(0,0,0,.85);
-      display: table-cell;
-      line-height: 20px;
-      margin-right: 8px;
-      padding-bottom: 16px;
-      white-space: nowrap;
-
-      &:not(:empty):after {
-        content: ":";
-        margin: 0 8px 0 2px;
-        position: relative;
-        top: -.5px;
-      }
-    }
-
-    /deep/ .content {
-      color: rgba(0,0,0,.65);
-      display: table-cell;
-      min-height: 22px;
-      line-height: 22px;
-      padding-bottom: 16px;
-      width: 100%;
-      &:empty {
-        content: ' ';
-        height: 38px;
-        padding-bottom: 16px;
-      }
-    }
-
-    &.small {
-
-      .title {
-        font-size: 14px;
-        color: rgba(0, 0, 0, .65);
-        font-weight: normal;
-        margin-bottom: 12px;
-      }
-      /deep/ .term, .content {
-        padding-bottom: 8px;
-      }
-    }
-
-    &.large {
-      /deep/ .term, .content {
-        padding-bottom: 16px;
-      }
-
-      .title {
-        font-size: 16px;
-      }
-    }
-
-    &.vertical {
-      .term {
-        padding-bottom: 8px;
-      }
-      /deep/ .term, .content {
-        display: block;
-      }
-    }
-  }
-</style>

+ 3 - 3
src/components/tools/UserMenu.vue

@@ -5,7 +5,7 @@
         <a-icon type="question-circle-o"></a-icon>
       </span>
     </a>
-    <header-notice class="action"/>
+    <notice-icon class="action"/>
     <a-dropdown>
       <span class="action ant-dropdown-link user-dropdown-menu">
         <a-avatar class="avatar" size="small" :src="avatar()"/>
@@ -41,13 +41,13 @@
 </template>
 
 <script>
-import HeaderNotice from './HeaderNotice'
+import NoticeIcon from '@/components/NoticeIcon'
 import { mapActions, mapGetters } from 'vuex'
 
 export default {
   name: 'UserMenu',
   components: {
-    HeaderNotice
+    NoticeIcon
   },
   methods: {
     ...mapActions(['Logout']),

+ 0 - 0
src/components/tools/index.js


+ 1 - 0
src/config/defaultSettings.js

@@ -23,6 +23,7 @@ export default {
   autoHideHeader: false, //  auto hide header
   colorWeak: false,
   multiTab: false,
+  production: process.env.NODE_ENV === 'production' && process.env.VUE_APP_PREVIEW !== 'true',
   // vue-ls options
   storageOptions: {
     namespace: 'pro__', // key prefix

+ 52 - 45
src/config/router.config.js

@@ -1,5 +1,5 @@
 // eslint-disable-next-line
-import { UserLayout, BasicLayout, RouteView, BlankLayout, PageView } from '@/components/layouts'
+import { UserLayout, BasicLayout, RouteView, BlankLayout, PageView } from '@/layouts'
 import { bxAnaalyse } from '@/core/icons'
 
 export const asyncRouterMap = [
@@ -25,12 +25,11 @@ export const asyncRouterMap = [
             component: () => import('@/views/dashboard/Analysis'),
             meta: { title: '分析页', keepAlive: false, permission: [ 'dashboard' ] }
           },
+          // 外部链接
           {
-            path: '/dashboard/monitor',
+            path: 'https://www.baidu.com/',
             name: 'Monitor',
-            hidden: true,
-            component: () => import('@/views/dashboard/Monitor'),
-            meta: { title: '监控页', keepAlive: true, permission: [ 'dashboard' ] }
+            meta: { title: '监控页(外部)', target: '_blank' }
           },
           {
             path: '/dashboard/workplace',
@@ -74,52 +73,16 @@ export const asyncRouterMap = [
         path: '/list',
         name: 'list',
         component: PageView,
-        redirect: '/list/query-list',
+        redirect: '/list/table-list',
         meta: { title: '列表页', icon: 'table', permission: [ 'table' ] },
         children: [
           {
-            path: '/list/query-list',
-            name: 'QueryListWrapper',
+            path: '/list/table-list',
+            name: 'TableListWrapper',
             hideChildrenInMenu: true, // 强制显示 MenuItem 而不是 SubMenu
             component: () => import('@/views/list/TableList'),
             meta: { title: '查询表格', keepAlive: true, permission: [ 'table' ] }
           },
-          {
-            path: '/list/tree-list',
-            name: 'TreeList',
-            component: () => import('@/views/list/TreeList'),
-            meta: { title: '树目录表格', keepAlive: true, permission: [ 'table' ] }
-          },
-          {
-            path: '/list/edit-table',
-            name: 'EditList',
-            component: () => import('@/views/list/TableInnerEditList'),
-            meta: { title: '内联编辑表格', keepAlive: true, permission: [ 'table' ] }
-          },
-          {
-            path: '/list/user-list',
-            name: 'UserList',
-            component: () => import('@/views/list/UserList'),
-            meta: { title: '用户列表', keepAlive: true, permission: [ 'table' ] }
-          },
-          {
-            path: '/list/role-list',
-            name: 'RoleList',
-            component: () => import('@/views/list/RoleList'),
-            meta: { title: '角色列表', keepAlive: true, permission: [ 'table' ] }
-          },
-          {
-            path: '/list/system-role',
-            name: 'SystemRole',
-            component: () => import('@/views/role/RoleList'),
-            meta: { title: '角色列表2', keepAlive: true, permission: [ 'table' ] }
-          },
-          {
-            path: '/list/permission-list',
-            name: 'PermissionList',
-            component: () => import('@/views/list/PermissionList'),
-            meta: { title: '权限列表', keepAlive: true, permission: [ 'table' ] }
-          },
           {
             path: '/list/basic-list',
             name: 'BasicList',
@@ -306,7 +269,51 @@ export const asyncRouterMap = [
             path: '/other/icon-selector',
             name: 'TestIconSelect',
             component: () => import('@/views/other/IconSelectorView'),
-            meta: { title: 'IconSelector', keepAlive: true, permission: [ 'dashboard' ] }
+            meta: { title: 'IconSelector', icon: 'tool', keepAlive: true, permission: [ 'dashboard' ] }
+          },
+          {
+            path: '/other/list',
+            component: RouteView,
+            meta: { title: '业务布局', icon: 'layout', permission: [ 'support' ] },
+            redirect: '/other/list/tree-list',
+            children: [
+              {
+                path: '/other/list/tree-list',
+                name: 'TreeList',
+                component: () => import('@/views/other/TreeList'),
+                meta: { title: '树目录表格', keepAlive: true }
+              },
+              {
+                path: '/other/list/edit-table',
+                name: 'EditList',
+                component: () => import('@/views/other/TableInnerEditList'),
+                meta: { title: '内联编辑表格', keepAlive: true }
+              },
+              {
+                path: '/other/list/user-list',
+                name: 'UserList',
+                component: () => import('@/views/other/UserList'),
+                meta: { title: '用户列表', keepAlive: true }
+              },
+              {
+                path: '/other/list/role-list',
+                name: 'RoleList',
+                component: () => import('@/views/other/RoleList'),
+                meta: { title: '角色列表', keepAlive: true }
+              },
+              {
+                path: '/other/list/system-role',
+                name: 'SystemRole',
+                component: () => import('@/views/role/RoleList'),
+                meta: { title: '角色列表2', keepAlive: true }
+              },
+              {
+                path: '/other/list/permission-list',
+                name: 'PermissionList',
+                component: () => import('@/views/other/PermissionList'),
+                meta: { title: '权限列表', keepAlive: true }
+              }
+            ]
           }
         ]
       }

+ 8 - 0
src/core/icons.js

@@ -1,3 +1,11 @@
+/**
+ * Custom icon list
+ * All icons are loaded here for easy management
+ * @see https://vue.ant.design/components/icon/#Custom-Font-Icon
+ *
+ * 自定义图标加载表
+ * 所有图标均从这里加载,方便管理
+ */
 import bxAnaalyse from '@/assets/icons/bx-analyse.svg?inline' // path to your '*.svg?inline' file.
 
 export { bxAnaalyse }

+ 181 - 0
src/layouts/BasicLayout.vue

@@ -0,0 +1,181 @@
+<template>
+  <a-layout :class="['layout', device]">
+    <!-- SideMenu -->
+    <a-drawer
+      v-if="isMobile()"
+      placement="left"
+      :wrapClassName="`drawer-sider ${navTheme}`"
+      :closable="false"
+      :visible="collapsed"
+      @close="drawerClose"
+    >
+      <side-menu
+        mode="inline"
+        :menus="menus"
+        :theme="navTheme"
+        :collapsed="false"
+        :collapsible="true"
+        @menuSelect="menuSelect"
+      ></side-menu>
+    </a-drawer>
+
+    <side-menu
+      v-else-if="isSideMenu()"
+      mode="inline"
+      :menus="menus"
+      :theme="navTheme"
+      :collapsed="collapsed"
+      :collapsible="true"
+    ></side-menu>
+
+    <a-layout :class="[layoutMode, `content-width-${contentWidth}`]" :style="{ paddingLeft: contentPaddingLeft, minHeight: '100vh' }">
+      <!-- layout header -->
+      <global-header
+        :mode="layoutMode"
+        :menus="menus"
+        :theme="navTheme"
+        :collapsed="collapsed"
+        :device="device"
+        @toggle="toggle"
+      />
+
+      <!-- layout content -->
+      <a-layout-content :style="{ height: '100%', margin: multiTab ? '24px 24px 0' : '24px 24px 0', paddingTop: fixedHeader ? '64px' : '0' }">
+        <multi-tab v-if="multiTab"></multi-tab>
+        <transition name="page-transition">
+          <route-view />
+        </transition>
+      </a-layout-content>
+
+      <!-- layout footer -->
+      <a-layout-footer>
+        <global-footer />
+      </a-layout-footer>
+
+      <!-- Setting Drawer (show in development mode) -->
+      <setting-drawer v-if="!production"></setting-drawer>
+    </a-layout>
+  </a-layout>
+
+</template>
+
+<script>
+import { triggerWindowResizeEvent } from '@/utils/util'
+import { mapState, mapActions } from 'vuex'
+import { mixin, mixinDevice } from '@/utils/mixin'
+import config from '@/config/defaultSettings'
+
+import RouteView from './RouteView'
+import MultiTab from '@/components/MultiTab'
+import SideMenu from '@/components/Menu/SideMenu'
+import GlobalHeader from '@/components/GlobalHeader'
+import GlobalFooter from '@/components/GlobalFooter'
+import SettingDrawer from '@/components/SettingDrawer'
+
+export default {
+  name: 'BasicLayout',
+  mixins: [mixin, mixinDevice],
+  components: {
+    RouteView,
+    MultiTab,
+    SideMenu,
+    GlobalHeader,
+    GlobalFooter,
+    SettingDrawer
+  },
+  data () {
+    return {
+      production: config.production,
+      collapsed: false,
+      menus: []
+    }
+  },
+  computed: {
+    ...mapState({
+      // 动态主路由
+      mainMenu: state => state.permission.addRouters
+    }),
+    contentPaddingLeft () {
+      if (!this.fixSidebar || this.isMobile()) {
+        return '0'
+      }
+      if (this.sidebarOpened) {
+        return '256px'
+      }
+      return '80px'
+    }
+  },
+  watch: {
+    sidebarOpened (val) {
+      this.collapsed = !val
+    }
+  },
+  created () {
+    this.menus = this.mainMenu.find(item => item.path === '/').children
+    this.collapsed = !this.sidebarOpened
+  },
+  mounted () {
+    const userAgent = navigator.userAgent
+    if (userAgent.indexOf('Edge') > -1) {
+      this.$nextTick(() => {
+        this.collapsed = !this.collapsed
+        setTimeout(() => {
+          this.collapsed = !this.collapsed
+        }, 16)
+      })
+    }
+  },
+  methods: {
+    ...mapActions(['setSidebar']),
+    toggle () {
+      this.collapsed = !this.collapsed
+      this.setSidebar(!this.collapsed)
+      triggerWindowResizeEvent()
+    },
+    paddingCalc () {
+      let left = ''
+      if (this.sidebarOpened) {
+        left = this.isDesktop() ? '256px' : '80px'
+      } else {
+        left = (this.isMobile() && '0') || ((this.fixSidebar && '80px') || '0')
+      }
+      return left
+    },
+    menuSelect () {
+      if (!this.isDesktop()) {
+        this.collapsed = false
+      }
+    },
+    drawerClose () {
+      this.collapsed = false
+    }
+  }
+}
+</script>
+
+<style lang="less">
+@import url('../components/global.less');
+
+/*
+ * The following styles are auto-applied to elements with
+ * transition="page-transition" when their visibility is toggled
+ * by Vue.js.
+ *
+ * You can easily play with the page transition by editing
+ * these styles.
+ */
+
+.page-transition-enter {
+  opacity: 0;
+}
+
+.page-transition-leave-active {
+  opacity: 0;
+}
+
+.page-transition-enter .page-transition-container,
+.page-transition-leave-active .page-transition-container {
+  -webkit-transform: scale(1.1);
+  transform: scale(1.1);
+}
+</style>

+ 0 - 0
src/components/layouts/BlankLayout.vue → src/layouts/BlankLayout.vue


+ 176 - 0
src/layouts/PageView.vue

@@ -0,0 +1,176 @@
+<template>
+  <div :style="!$route.meta.hiddenHeaderContent ? 'margin: -24px -24px 0px;' : null">
+    <!-- pageHeader , route meta :true on hide -->
+    <page-header v-if="!$route.meta.hiddenHeaderContent" :title="pageTitle" :logo="logo" :avatar="avatar">
+      <slot slot="action" name="action"></slot>
+      <slot slot="content" name="headerContent"></slot>
+      <div slot="content" v-if="!this.$slots.headerContent && description">
+        <p style="font-size: 14px;color: rgba(0,0,0,.65)">{{ description }}</p>
+        <div class="link">
+          <template v-for="(link, index) in linkList">
+            <a :key="index" :href="link.href">
+              <a-icon :type="link.icon" />
+              <span>{{ link.title }}</span>
+            </a>
+          </template>
+        </div>
+      </div>
+      <slot slot="extra" name="extra">
+        <div class="extra-img">
+          <img v-if="typeof extraImage !== 'undefined'" :src="extraImage"/>
+        </div>
+      </slot>
+      <div slot="pageMenu">
+        <div class="page-menu-search" v-if="search">
+          <a-input-search
+            style="width: 80%; max-width: 522px;"
+            placeholder="请输入..."
+            size="large"
+            enterButton="搜索"
+          />
+        </div>
+        <div class="page-menu-tabs" v-if="tabs && tabs.items">
+          <!-- @change="callback" :activeKey="activeKey" -->
+          <a-tabs :tabBarStyle="{margin: 0}" :activeKey="tabs.active()" @change="tabs.callback">
+            <a-tab-pane v-for="item in tabs.items" :tab="item.title" :key="item.key"></a-tab-pane>
+          </a-tabs>
+        </div>
+      </div>
+    </page-header>
+    <div class="content">
+      <div class="page-header-index-wide">
+        <slot>
+          <!-- keep-alive  -->
+          <keep-alive v-if="multiTab">
+            <router-view ref="content" />
+          </keep-alive>
+          <router-view v-else ref="content" />
+        </slot>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { mapState } from 'vuex'
+import PageHeader from '@/components/PageHeader'
+
+export default {
+  name: 'PageView',
+  components: {
+    PageHeader
+  },
+  props: {
+    avatar: {
+      type: String,
+      default: null
+    },
+    title: {
+      type: [String, Boolean],
+      default: true
+    },
+    logo: {
+      type: String,
+      default: null
+    }
+  },
+  data () {
+    return {
+      pageTitle: null,
+      description: null,
+      linkList: [],
+      extraImage: '',
+      search: false,
+      tabs: {}
+    }
+  },
+  computed: {
+    ...mapState({
+      multiTab: state => state.app.multiTab
+    })
+  },
+  mounted () {
+    this.getPageMeta()
+  },
+  updated () {
+    this.getPageMeta()
+  },
+  methods: {
+    getPageMeta () {
+      // eslint-disable-next-line
+      this.pageTitle = (typeof(this.title) === 'string' || !this.title) ? this.title : this.$route.meta.title
+
+      const content = this.$refs.content
+      if (content) {
+        if (content.pageMeta) {
+          Object.assign(this, content.pageMeta)
+        } else {
+          this.description = content.description
+          this.linkList = content.linkList
+          this.extraImage = content.extraImage
+          this.search = content.search === true
+          this.tabs = content.tabs
+        }
+      }
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+  .content {
+    margin: 24px 24px 0;
+    .link {
+      margin-top: 16px;
+      &:not(:empty) {
+        margin-bottom: 16px;
+      }
+      a {
+        margin-right: 32px;
+        height: 24px;
+        line-height: 24px;
+        display: inline-block;
+        i {
+          font-size: 24px;
+          margin-right: 8px;
+          vertical-align: middle;
+        }
+        span {
+          height: 24px;
+          line-height: 24px;
+          display: inline-block;
+          vertical-align: middle;
+        }
+      }
+    }
+  }
+  .page-menu-search {
+    text-align: center;
+    margin-bottom: 16px;
+  }
+  .page-menu-tabs {
+    margin-top: 48px;
+  }
+
+  .extra-img {
+    margin-top: -60px;
+    text-align: center;
+    width: 195px;
+
+    img {
+      width: 100%;
+    }
+  }
+
+  .mobile {
+    .extra-img{
+      margin-top: 0;
+      text-align: center;
+      width: 96px;
+
+      img{
+        width: 100%;
+      }
+    }
+  }
+</style>

+ 32 - 26
src/components/layouts/RouteView.vue → src/layouts/RouteView.vue

@@ -1,26 +1,32 @@
-<script>
-export default {
-  name: 'RouteView',
-  data () {
-    return {}
-  },
-  render () {
-    const { $route: { meta }, $store: { getters } } = this
-    const inKeep = (
-      <keep-alive>
-        <router-view />
-      </keep-alive>
-    )
-    const notKeep = (
-      <router-view />
-    )
-    // 这里增加了 multiTab 的判断,当开启了 multiTab 时
-    // 应当全部组件皆缓存,否则会导致切换页面后页面还原成原始状态
-    // 若确实不需要,可改为 return meta.keepAlive ? inKeep : notKeep
-    if (meta.keepAlive === false) {
-      return notKeep
-    }
-    return getters.multiTab || meta.keepAlive ? inKeep : notKeep
-  }
-}
-</script>
+<script>
+export default {
+  name: 'RouteView',
+  props: {
+    keepAlive: {
+      type: Boolean,
+      default: true
+    }
+  },
+  data () {
+    return {}
+  },
+  render () {
+    const { $route: { meta }, $store: { getters } } = this
+    const inKeep = (
+      <keep-alive>
+        <router-view />
+      </keep-alive>
+    )
+    const notKeep = (
+      <router-view />
+    )
+    // 这里增加了 multiTab 的判断,当开启了 multiTab 时
+    // 应当全部组件皆缓存,否则会导致切换页面后页面还原成原始状态
+    // 若确实不需要,可改为 return meta.keepAlive ? inKeep : notKeep
+    if (!getters.multiTab && meta.keepAlive === false) {
+      return notKeep
+    }
+    return this.keepAlive || getters.multiTab || meta.keepAlive ? inKeep : notKeep
+  }
+}
+</script>

+ 150 - 150
src/components/layouts/UserLayout.vue → src/layouts/UserLayout.vue

@@ -1,150 +1,150 @@
-<template>
-  <div id="userLayout" :class="['user-layout-wrapper', device]">
-    <div class="container">
-      <div class="top">
-        <div class="header">
-          <a href="/">
-            <img src="~@/assets/logo.svg" class="logo" alt="logo">
-            <span class="title">Ant Design</span>
-          </a>
-        </div>
-        <div class="desc">
-          Ant Design 是西湖区最具影响力的 Web 设计规范
-        </div>
-      </div>
-
-      <route-view></route-view>
-
-      <div class="footer">
-        <div class="links">
-          <a href="_self">帮助</a>
-          <a href="_self">隐私</a>
-          <a href="_self">条款</a>
-        </div>
-        <div class="copyright">
-          Copyright &copy; 2018 白鹭学园技术组出品
-        </div>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script>
-import RouteView from '@/components/layouts/RouteView'
-import { mixinDevice } from '@/utils/mixin.js'
-
-export default {
-  name: 'UserLayout',
-  components: { RouteView },
-  mixins: [mixinDevice],
-  data () {
-    return {}
-  },
-  mounted () {
-    document.body.classList.add('userLayout')
-  },
-  beforeDestroy () {
-    document.body.classList.remove('userLayout')
-  }
-}
-</script>
-
-<style lang="less" scoped>
-  #userLayout.user-layout-wrapper {
-    height: 100%;
-
-    &.mobile {
-      .container {
-        .main {
-          max-width: 368px;
-          width: 98%;
-        }
-      }
-    }
-
-    .container {
-      width: 100%;
-      min-height: 100%;
-      background: #f0f2f5 url(~@/assets/background.svg) no-repeat 50%;
-      background-size: 100%;
-      padding: 110px 0 144px;
-      position: relative;
-
-      a {
-        text-decoration: none;
-      }
-
-      .top {
-        text-align: center;
-
-        .header {
-          height: 44px;
-          line-height: 44px;
-
-          .badge {
-            position: absolute;
-            display: inline-block;
-            line-height: 1;
-            vertical-align: middle;
-            margin-left: -12px;
-            margin-top: -10px;
-            opacity: 0.8;
-          }
-
-          .logo {
-            height: 44px;
-            vertical-align: top;
-            margin-right: 16px;
-            border-style: none;
-          }
-
-          .title {
-            font-size: 33px;
-            color: rgba(0, 0, 0, .85);
-            font-family: Avenir, 'Helvetica Neue', Arial, Helvetica, sans-serif;
-            font-weight: 600;
-            position: relative;
-            top: 2px;
-          }
-        }
-        .desc {
-          font-size: 14px;
-          color: rgba(0, 0, 0, 0.45);
-          margin-top: 12px;
-          margin-bottom: 40px;
-        }
-      }
-
-      .main {
-        min-width: 260px;
-        width: 368px;
-        margin: 0 auto;
-      }
-
-      .footer {
-        position: absolute;
-        width: 100%;
-        bottom: 0;
-        padding: 0 16px;
-        margin: 48px 0 24px;
-        text-align: center;
-
-        .links {
-          margin-bottom: 8px;
-          font-size: 14px;
-          a {
-            color: rgba(0, 0, 0, 0.45);
-            transition: all 0.3s;
-            &:not(:last-child) {
-              margin-right: 40px;
-            }
-          }
-        }
-        .copyright {
-          color: rgba(0, 0, 0, 0.45);
-          font-size: 14px;
-        }
-      }
-    }
-  }
-</style>
+<template>
+  <div id="userLayout" :class="['user-layout-wrapper', device]">
+    <div class="container">
+      <div class="top">
+        <div class="header">
+          <a href="/">
+            <img src="~@/assets/logo.svg" class="logo" alt="logo">
+            <span class="title">Ant Design</span>
+          </a>
+        </div>
+        <div class="desc">
+          Ant Design 是西湖区最具影响力的 Web 设计规范
+        </div>
+      </div>
+
+      <route-view></route-view>
+
+      <div class="footer">
+        <div class="links">
+          <a href="_self">帮助</a>
+          <a href="_self">隐私</a>
+          <a href="_self">条款</a>
+        </div>
+        <div class="copyright">
+          Copyright &copy; 2018 白鹭学园技术组出品
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import RouteView from './RouteView'
+import { mixinDevice } from '@/utils/mixin'
+
+export default {
+  name: 'UserLayout',
+  components: { RouteView },
+  mixins: [mixinDevice],
+  data () {
+    return {}
+  },
+  mounted () {
+    document.body.classList.add('userLayout')
+  },
+  beforeDestroy () {
+    document.body.classList.remove('userLayout')
+  }
+}
+</script>
+
+<style lang="less" scoped>
+  #userLayout.user-layout-wrapper {
+    height: 100%;
+
+    &.mobile {
+      .container {
+        .main {
+          max-width: 368px;
+          width: 98%;
+        }
+      }
+    }
+
+    .container {
+      width: 100%;
+      min-height: 100%;
+      background: #f0f2f5 url(~@/assets/background.svg) no-repeat 50%;
+      background-size: 100%;
+      padding: 110px 0 144px;
+      position: relative;
+
+      a {
+        text-decoration: none;
+      }
+
+      .top {
+        text-align: center;
+
+        .header {
+          height: 44px;
+          line-height: 44px;
+
+          .badge {
+            position: absolute;
+            display: inline-block;
+            line-height: 1;
+            vertical-align: middle;
+            margin-left: -12px;
+            margin-top: -10px;
+            opacity: 0.8;
+          }
+
+          .logo {
+            height: 44px;
+            vertical-align: top;
+            margin-right: 16px;
+            border-style: none;
+          }
+
+          .title {
+            font-size: 33px;
+            color: rgba(0, 0, 0, .85);
+            font-family: Avenir, 'Helvetica Neue', Arial, Helvetica, sans-serif;
+            font-weight: 600;
+            position: relative;
+            top: 2px;
+          }
+        }
+        .desc {
+          font-size: 14px;
+          color: rgba(0, 0, 0, 0.45);
+          margin-top: 12px;
+          margin-bottom: 40px;
+        }
+      }
+
+      .main {
+        min-width: 260px;
+        width: 368px;
+        margin: 0 auto;
+      }
+
+      .footer {
+        position: absolute;
+        width: 100%;
+        bottom: 0;
+        padding: 0 16px;
+        margin: 48px 0 24px;
+        text-align: center;
+
+        .links {
+          margin-bottom: 8px;
+          font-size: 14px;
+          a {
+            color: rgba(0, 0, 0, 0.45);
+            transition: all 0.3s;
+            &:not(:last-child) {
+              margin-right: 40px;
+            }
+          }
+        }
+        .copyright {
+          color: rgba(0, 0, 0, 0.45);
+          font-size: 14px;
+        }
+      }
+    }
+  }
+</style>

+ 7 - 0
src/layouts/index.js

@@ -0,0 +1,7 @@
+import UserLayout from './UserLayout'
+import BlankLayout from './BlankLayout'
+import BasicLayout from './BasicLayout'
+import RouteView from './RouteView'
+import PageView from './PageView'
+
+export { UserLayout, BasicLayout, BlankLayout, RouteView, PageView }

+ 27 - 24
src/main.js

@@ -1,24 +1,27 @@
-import '@babel/polyfill'
-import Vue from 'vue'
-import App from './App.vue'
-import router from './router'
-import store from './store/'
-import { VueAxios } from '@/utils/request' // axios 不建议引入到 Vue 原型链上
-
-import './core/use'
-import bootstrap from './core/bootstrap'
-import '@/permission' // permission control
-import '@/utils/filter' // global filter
-
-Vue.config.productionTip = false
-
-Vue.use(VueAxios, router)
-
-new Vue({
-  router,
-  store,
-  created () {
-    bootstrap()
-  },
-  render: h => h(App)
-}).$mount('#app')
+// ie polyfill
+import '@babel/polyfill'
+
+import Vue from 'vue'
+import App from './App.vue'
+import router from './router'
+import store from './store/'
+import { VueAxios } from './utils/request'
+
+import bootstrap from './core/bootstrap'
+import './core/use'
+import './permission' // permission control
+import './utils/filter' // global filter
+
+Vue.config.productionTip = false
+
+// mount axios Vue.$http and this.$http
+Vue.use(VueAxios)
+
+new Vue({
+  router,
+  store,
+  created () {
+    bootstrap()
+  },
+  render: h => h(App)
+}).$mount('#app')

+ 21 - 2
src/views/Home.vue

@@ -99,13 +99,28 @@
     <h2># NumberInfo 组件 </h2>
 
     <a-divider> NumberInfo </a-divider>
-    <a-card>
+    <a-card style="margin-bottom: 3rem">
       <number-info
         :sub-title="() => { return 'Visits this week' }"
         :total="12321"
         status="up"
         :sub-total="17.1"></number-info>
     </a-card>
+
+    <h2># TagSelect 组件 </h2>
+
+    <a-divider> TagSelect </a-divider>
+    <a-card style="margin-bottom: 3rem">
+      <tag-select>
+        <tag-select-option value="cat1">类目1</tag-select-option>
+        <tag-select-option value="cat2">类目2</tag-select-option>
+        <tag-select-option value="cat3">类目3</tag-select-option>
+        <tag-select-option value="cat4">类目4</tag-select-option>
+        <tag-select-option value="cat5">类目5</tag-select-option>
+        <tag-select-option value="cat6">类目6</tag-select-option>
+        <tag-select-option value="cat7">类目7</tag-select-option>
+      </tag-select>
+    </a-card>
   </div>
 </template>
 
@@ -117,8 +132,10 @@ import AvatarList from '@/components/AvatarList'
 import CountDown from '@/components/CountDown/CountDown'
 import Ellipsis from '@/components/Ellipsis'
 import NumberInfo from '@/components/NumberInfo'
+import TagSelect from '@/components/TagSelect'
 
 const AvatarListItem = AvatarList.AvatarItem
+const TagSelectOption = TagSelect.Option
 
 export default {
   name: 'Home',
@@ -128,7 +145,9 @@ export default {
     CountDown,
     Trend,
     AvatarList,
-    AvatarListItem
+    AvatarListItem,
+    TagSelect,
+    TagSelectOption
   },
   data () {
     return {

+ 2 - 3
src/views/account/center/Index.vue

@@ -96,8 +96,7 @@
 </template>
 
 <script>
-import PageLayout from '@/components/page/PageLayout'
-import RouteView from '@/components/layouts/RouteView'
+import { PageView, RouteView } from '@/layouts'
 import { AppPage, ArticlePage, ProjectPage } from './page'
 
 import { mapGetters } from 'vuex'
@@ -105,7 +104,7 @@ import { mapGetters } from 'vuex'
 export default {
   components: {
     RouteView,
-    PageLayout,
+    PageView,
     AppPage,
     ArticlePage,
     ProjectPage

+ 2 - 2
src/views/account/settings/Custom.vue

@@ -1,9 +1,9 @@
 <script>
-import { colorList } from '@/components/tools/setting'
+import { colorList } from '@/components/SettingDrawer/settingConfig'
 import ASwitch from 'ant-design-vue/es/switch'
 import AList from 'ant-design-vue/es/list'
 import AListItem from 'ant-design-vue/es/list/Item'
-import { mixin } from '@/utils/mixin.js'
+import { mixin } from '@/utils/mixin'
 
 const Meta = AListItem.Meta
 

+ 3 - 4
src/views/account/settings/Index.vue

@@ -49,14 +49,13 @@
 </template>
 
 <script>
-import PageLayout from '@/components/page/PageLayout'
-import RouteView from '@/components/layouts/RouteView'
+import { PageView, RouteView } from '@/layouts'
 import { mixinDevice } from '@/utils/mixin.js'
 
 export default {
   components: {
     RouteView,
-    PageLayout
+    PageView
   },
   mixins: [mixinDevice],
   data () {
@@ -148,4 +147,4 @@ export default {
     }
   }
 
-</style>
+</style>

+ 355 - 206
src/views/dashboard/Analysis.vue

@@ -1,207 +1,356 @@
-<template>
-  <div class="page-header-index-wide">
-    <a-row :gutter="24">
-      <a-col :sm="24" :md="12" :xl="6" :style="{ marginBottom: '24px' }">
-        <chart-card :loading="loading" title="总销售额" total="¥126,560">
-          <a-tooltip title="指标说明" slot="action">
-            <a-icon type="info-circle-o" />
-          </a-tooltip>
-          <div>
-            <trend flag="up" style="margin-right: 16px;">
-              <span slot="term">周同比</span>
-              12%
-            </trend>
-            <trend flag="down">
-              <span slot="term">日同比</span>
-              11%
-            </trend>
-          </div>
-          <template slot="footer">日均销售额<span>¥ 234.56</span></template>
-        </chart-card>
-      </a-col>
-      <a-col :sm="24" :md="12" :xl="6" :style="{ marginBottom: '24px' }">
-        <chart-card :loading="loading" title="访问量" :total="8846 | NumberFormat">
-          <a-tooltip title="指标说明" slot="action">
-            <a-icon type="info-circle-o" />
-          </a-tooltip>
-          <div>
-            <mini-area />
-          </div>
-          <template slot="footer">日访问量<span> {{ '1234' | NumberFormat }}</span></template>
-        </chart-card>
-      </a-col>
-      <a-col :sm="24" :md="12" :xl="6" :style="{ marginBottom: '24px' }">
-        <chart-card :loading="loading" title="支付笔数" :total="6560 | NumberFormat">
-          <a-tooltip title="指标说明" slot="action">
-            <a-icon type="info-circle-o" />
-          </a-tooltip>
-          <div>
-            <mini-bar />
-          </div>
-          <template slot="footer">转化率 <span>60%</span></template>
-        </chart-card>
-      </a-col>
-      <a-col :sm="24" :md="12" :xl="6" :style="{ marginBottom: '24px' }">
-        <chart-card :loading="loading" title="运营活动效果" total="78%">
-          <a-tooltip title="指标说明" slot="action">
-            <a-icon type="info-circle-o" />
-          </a-tooltip>
-          <div>
-            <mini-progress color="rgb(19, 194, 194)" :target="80" :percentage="78" height="8px" />
-          </div>
-          <template slot="footer">
-            <trend flag="down" style="margin-right: 16px;">
-              <span slot="term">同周比</span>
-              12%
-            </trend>
-            <trend flag="up">
-              <span slot="term">日环比</span>
-              80%
-            </trend>
-          </template>
-        </chart-card>
-      </a-col>
-    </a-row>
-
-    <a-card :loading="loading" :bordered="false" :body-style="{padding: '0'}">
-      <div class="salesCard">
-        <a-tabs default-active-key="1" size="large" :tab-bar-style="{marginBottom: '24px', paddingLeft: '16px'}">
-          <div class="extra-wrapper" slot="tabBarExtraContent">
-            <div class="extra-item">
-              <a>今日</a>
-              <a>本周</a>
-              <a>本月</a>
-              <a>本年</a>
-            </div>
-            <a-range-picker :style="{width: '256px'}" />
-          </div>
-          <a-tab-pane loading="true" tab="销售额" key="1">
-            <a-row>
-              <a-col :xl="16" :lg="12" :md="12" :sm="24" :xs="24">
-                <bar title="销售额排行" />
-              </a-col>
-              <a-col :xl="8" :lg="12" :md="12" :sm="24" :xs="24">
-                <rank-list title="门店销售排行榜" :list="rankList"/>
-              </a-col>
-            </a-row>
-          </a-tab-pane>
-          <a-tab-pane tab="访问量" key="2">
-            <a-row>
-              <a-col :xl="16" :lg="12" :md="12" :sm="24" :xs="24">
-                <bar title="销售额趋势" />
-              </a-col>
-              <a-col :xl="8" :lg="12" :md="12" :sm="24" :xs="24">
-                <rank-list title="门店销售排行榜" :list="rankList"/>
-              </a-col>
-            </a-row>
-          </a-tab-pane>
-        </a-tabs>
-      </div>
-    </a-card>
-
-    <a-row :gutter="12">
-      <a-col :xl="12" :lg="24" :md="24" :sm="24" :xs="24">
-        <a-card :loading="loading" :bordered="false" title="线上热门搜索" :style="{ marginTop: '24px' }">
-          <a-dropdown :trigger="['click']" placement="bottomLeft" slot="extra">
-            <a class="ant-dropdown-link" href="#">
-              <a-icon type="ellipsis" />
-            </a>
-            <a-menu slot="overlay">
-              <a-menu-item>
-                <a href="javascript:;">操作一</a>
-              </a-menu-item>
-              <a-menu-item>
-                <a href="javascript:;">操作二</a>
-              </a-menu-item>
-            </a-menu>
-          </a-dropdown>
-          <p>card content</p>
-          <p>card content</p>
-          <p>card content</p>
-        </a-card>
-      </a-col>
-      <a-col :xl="12" :lg="24" :md="24" :sm="24" :xs="24">
-        <a-card :loading="loading" :bordered="false" title="销售额类别占比" :style="{ marginTop: '24px' }">
-          <a-dropdown :trigger="['click']" placement="bottomLeft" slot="extra">
-            <a class="ant-dropdown-link" href="#">
-              <a-icon type="ellipsis" />
-            </a>
-            <a-menu slot="overlay">
-              <a-menu-item>
-                <a href="javascript:;">操作一</a>
-              </a-menu-item>
-              <a-menu-item>
-                <a href="javascript:;">操作二</a>
-              </a-menu-item>
-            </a-menu>
-          </a-dropdown>
-          <p>card content</p>
-          <p>card content</p>
-          <p>card content</p>
-        </a-card>
-      </a-col>
-    </a-row>
-  </div>
-</template>
-
-<script>
-import ChartCard from '@/components/chart/ChartCard'
-import ACol from 'ant-design-vue/es/grid/Col'
-import ATooltip from 'ant-design-vue/es/tooltip/Tooltip'
-import MiniArea from '@/components/chart/MiniArea'
-import MiniBar from '@/components/chart/MiniBar'
-import MiniProgress from '@/components/chart/MiniProgress'
-import RankList from '@/components/chart/RankList'
-import Bar from '@/components/chart/Bar'
-import Trend from '@/components/Trend'
-
-const rankList = []
-for (let i = 0; i < 7; i++) {
-  rankList.push({
-    name: '白鹭岛 ' + (i + 1) + ' 号店',
-    total: 1234.56 - i * 100
-  })
-}
-
-export default {
-  name: 'Analysis',
-  components: {
-    ATooltip,
-    ACol,
-    ChartCard,
-    MiniArea,
-    MiniBar,
-    MiniProgress,
-    RankList,
-    Bar,
-    Trend
-  },
-  data () {
-    return {
-      loading: true,
-      rankList
-    }
-  },
-  created () {
-    setTimeout(() => {
-      this.loading = !this.loading
-    }, 1000)
-  }
-}
-</script>
-
-<style lang="less" scoped>
-  .extra-wrapper {
-    line-height: 55px;
-    padding-right: 24px;
-
-    .extra-item {
-      display: inline-block;
-      margin-right: 24px;
-
-      a {
-        margin-left: 24px;
-      }
-    }
-  }
+<template>
+  <div class="page-header-index-wide">
+    <a-row :gutter="24">
+      <a-col :sm="24" :md="12" :xl="6" :style="{ marginBottom: '24px' }">
+        <chart-card :loading="loading" title="总销售额" total="¥126,560">
+          <a-tooltip title="指标说明" slot="action">
+            <a-icon type="info-circle-o" />
+          </a-tooltip>
+          <div>
+            <trend flag="up" style="margin-right: 16px;">
+              <span slot="term">周同比</span>
+              12%
+            </trend>
+            <trend flag="down">
+              <span slot="term">日同比</span>
+              11%
+            </trend>
+          </div>
+          <template slot="footer">日均销售额<span>¥ 234.56</span></template>
+        </chart-card>
+      </a-col>
+      <a-col :sm="24" :md="12" :xl="6" :style="{ marginBottom: '24px' }">
+        <chart-card :loading="loading" title="访问量" :total="8846 | NumberFormat">
+          <a-tooltip title="指标说明" slot="action">
+            <a-icon type="info-circle-o" />
+          </a-tooltip>
+          <div>
+            <mini-area />
+          </div>
+          <template slot="footer">日访问量<span> {{ '1234' | NumberFormat }}</span></template>
+        </chart-card>
+      </a-col>
+      <a-col :sm="24" :md="12" :xl="6" :style="{ marginBottom: '24px' }">
+        <chart-card :loading="loading" title="支付笔数" :total="6560 | NumberFormat">
+          <a-tooltip title="指标说明" slot="action">
+            <a-icon type="info-circle-o" />
+          </a-tooltip>
+          <div>
+            <mini-bar />
+          </div>
+          <template slot="footer">转化率 <span>60%</span></template>
+        </chart-card>
+      </a-col>
+      <a-col :sm="24" :md="12" :xl="6" :style="{ marginBottom: '24px' }">
+        <chart-card :loading="loading" title="运营活动效果" total="78%">
+          <a-tooltip title="指标说明" slot="action">
+            <a-icon type="info-circle-o" />
+          </a-tooltip>
+          <div>
+            <mini-progress color="rgb(19, 194, 194)" :target="80" :percentage="78" height="8px" />
+          </div>
+          <template slot="footer">
+            <trend flag="down" style="margin-right: 16px;">
+              <span slot="term">同周比</span>
+              12%
+            </trend>
+            <trend flag="up">
+              <span slot="term">日环比</span>
+              80%
+            </trend>
+          </template>
+        </chart-card>
+      </a-col>
+    </a-row>
+
+    <a-card :loading="loading" :bordered="false" :body-style="{padding: '0'}">
+      <div class="salesCard">
+        <a-tabs default-active-key="1" size="large" :tab-bar-style="{marginBottom: '24px', paddingLeft: '16px'}">
+          <div class="extra-wrapper" slot="tabBarExtraContent">
+            <div class="extra-item">
+              <a>今日</a>
+              <a>本周</a>
+              <a>本月</a>
+              <a>本年</a>
+            </div>
+            <a-range-picker :style="{width: '256px'}" />
+          </div>
+          <a-tab-pane loading="true" tab="销售额" key="1">
+            <a-row>
+              <a-col :xl="16" :lg="12" :md="12" :sm="24" :xs="24">
+                <bar title="销售额排行" />
+              </a-col>
+              <a-col :xl="8" :lg="12" :md="12" :sm="24" :xs="24">
+                <rank-list title="门店销售排行榜" :list="rankList"/>
+              </a-col>
+            </a-row>
+          </a-tab-pane>
+          <a-tab-pane tab="访问量" key="2">
+            <a-row>
+              <a-col :xl="16" :lg="12" :md="12" :sm="24" :xs="24">
+                <bar title="销售额趋势" />
+              </a-col>
+              <a-col :xl="8" :lg="12" :md="12" :sm="24" :xs="24">
+                <rank-list title="门店销售排行榜" :list="rankList"/>
+              </a-col>
+            </a-row>
+          </a-tab-pane>
+        </a-tabs>
+      </div>
+    </a-card>
+
+    <a-row :gutter="12">
+      <a-col :xl="12" :lg="24" :md="24" :sm="24" :xs="24">
+        <a-card :loading="loading" :bordered="false" title="线上热门搜索" :style="{ marginTop: '24px', height: '500px' }">
+          <a-dropdown :trigger="['click']" placement="bottomLeft" slot="extra">
+            <a class="ant-dropdown-link" href="#">
+              <a-icon type="ellipsis" />
+            </a>
+            <a-menu slot="overlay">
+              <a-menu-item>
+                <a href="javascript:;">操作一</a>
+              </a-menu-item>
+              <a-menu-item>
+                <a href="javascript:;">操作二</a>
+              </a-menu-item>
+            </a-menu>
+          </a-dropdown>
+          <a-row :gutter="68">
+            <a-col :xs="24" :sm="12" :style="{ marginBottom: ' 24px'}">
+              <number-info :total="12321" :sub-total="17.1">
+                <span slot="subtitle">
+                  <span>搜索用户数</span>
+                  <a-tooltip title="指标说明" slot="action">
+                    <a-icon type="info-circle-o" :style="{ marginLeft: '8px' }" />
+                  </a-tooltip>
+                </span>
+              </number-info>
+              <!-- miniChart -->
+              <div>
+                <mini-smooth-area :style="{ height: '45px' }" :dataSource="searchUserData" :scale="searchUserScale" />
+              </div>
+            </a-col>
+            <a-col :xs="24" :sm="12" :style="{ marginBottom: ' 24px'}">
+              <number-info :total="2.7" :sub-total="26.2" status="down">
+                <span slot="subtitle">
+                  <span>人均搜索次数</span>
+                  <a-tooltip title="指标说明" slot="action">
+                    <a-icon type="info-circle-o" :style="{ marginLeft: '8px' }" />
+                  </a-tooltip>
+                </span>
+              </number-info>
+              <!-- miniChart -->
+              <div>
+                <mini-smooth-area :style="{ height: '45px' }" :dataSource="searchUserData" :scale="searchUserScale" />
+              </div>
+            </a-col>
+          </a-row>
+          <div class="ant-table-wrapper">
+            <a-table
+              row-key="index"
+              size="small"
+              :columns="searchTableColumns"
+              :dataSource="searchData"
+              :pagination="{ pageSize: 5 }"
+            />
+          </div>
+        </a-card>
+      </a-col>
+      <a-col :xl="12" :lg="24" :md="24" :sm="24" :xs="24">
+        <a-card :loading="loading" :bordered="false" title="销售额类别占比" :style="{ marginTop: '24px', height: '500px' }">
+          <div slot="extra" style="height: inherit;">
+            <div style="bottom: 12px;display: inline-block;">
+              <a-radio-group defaultValue="a">
+                <a-radio-button value="a">全部渠道</a-radio-button>
+                <a-radio-button value="b">线上</a-radio-button>
+                <a-radio-button value="c">门店</a-radio-button>
+              </a-radio-group>
+            </div>
+            <span class="dashboard-analysis-iconGroup">
+              <a-dropdown :trigger="['click']" placement="bottomLeft">
+                <a-icon type="ellipsis" class="ant-dropdown-link" />
+                <a-menu slot="overlay">
+                  <a-menu-item>
+                    <a href="javascript:;">操作一</a>
+                  </a-menu-item>
+                  <a-menu-item>
+                    <a href="javascript:;">操作二</a>
+                  </a-menu-item>
+                </a-menu>
+              </a-dropdown>
+            </span>
+          </div>
+          <h4>销售额</h4>
+          <div>
+            <v-chart :force-fit="true" :height="350" :data="pieData" :scale="pieScale">
+              <v-tooltip :showTitle="false" dataKey="item*percent" />
+              <v-axis />
+              <v-legend dataKey="item" position="right" :offsetX="-140"/>
+              <v-pie position="percent" color="item" :vStyle="pieStyle" />
+              <v-coord type="theta" :radius="0.75" :innerRadius="0.6" />
+            </v-chart>
+          </div>
+        </a-card>
+      </a-col>
+    </a-row>
+  </div>
+</template>
+
+<script>
+import moment from 'moment'
+import { ChartCard, MiniArea, MiniBar, MiniProgress, RankList, Bar, Trend, NumberInfo, MiniSmoothArea } from '@/components'
+
+const rankList = []
+for (let i = 0; i < 7; i++) {
+  rankList.push({
+    name: '白鹭岛 ' + (i + 1) + ' 号店',
+    total: 1234.56 - i * 100
+  })
+}
+
+const searchUserData = []
+for (let i = 0; i < 7; i++) {
+  searchUserData.push({
+    x: moment().add(i, 'days').format('YYYY-MM-DD'),
+    y: Math.ceil(Math.random() * 10)
+  })
+}
+const searchUserScale = [
+  {
+    dataKey: 'x',
+    alias: '时间'
+  },
+  {
+    dataKey: 'y',
+    alias: '用户数',
+    min: 0,
+    max: 10
+  }]
+
+const searchTableColumns = [
+  {
+    dataIndex: 'index',
+    title: '排名',
+    width: 90
+  },
+  {
+    dataIndex: 'keyword',
+    title: '搜索关键词'
+  },
+  {
+    dataIndex: 'count',
+    title: '用户数',
+    width: 164
+  },
+  {
+    dataIndex: 'range',
+    title: '周涨幅',
+    align: 'right',
+    sorter: (a, b) => a.range - b.range,
+    width: 164
+  }
+]
+const searchData = []
+for (let i = 0; i < 50; i += 1) {
+  searchData.push({
+    index: i + 1,
+    keyword: `搜索关键词-${i}`,
+    count: Math.floor(Math.random() * 1000),
+    range: Math.floor(Math.random() * 100),
+    status: Math.floor((Math.random() * 10) % 2)
+  })
+}
+
+const DataSet = require('@antv/data-set')
+
+const sourceData = [
+  { item: '家用电器', count: 32.2 },
+  { item: '食用酒水', count: 21 },
+  { item: '个护健康', count: 17 },
+  { item: '服饰箱包', count: 13 },
+  { item: '母婴产品', count: 9 },
+  { item: '其他', count: 7.8 }
+]
+
+const pieScale = [{
+  dataKey: 'percent',
+  min: 0,
+  formatter: '.0%'
+}]
+
+const dv = new DataSet.View().source(sourceData)
+dv.transform({
+  type: 'percent',
+  field: 'count',
+  dimension: 'item',
+  as: 'percent'
+})
+const pieData = dv.rows
+
+export default {
+  name: 'Analysis',
+  components: {
+    ChartCard,
+    MiniArea,
+    MiniBar,
+    MiniProgress,
+    RankList,
+    Bar,
+    Trend,
+    NumberInfo,
+    MiniSmoothArea
+  },
+  data () {
+    return {
+      loading: true,
+      rankList,
+
+      // 搜索用户数
+      searchUserData,
+      searchUserScale,
+      searchTableColumns,
+      searchData,
+
+      //
+      pieScale,
+      pieData,
+      sourceData,
+      pieStyle: {
+        stroke: '#fff',
+        lineWidth: 1
+      }
+    }
+  },
+  created () {
+    setTimeout(() => {
+      this.loading = !this.loading
+    }, 1000)
+  }
+}
+</script>
+
+<style lang="less" scoped>
+  .extra-wrapper {
+    line-height: 55px;
+    padding-right: 24px;
+
+    .extra-item {
+      display: inline-block;
+      margin-right: 24px;
+
+      a {
+        margin-left: 24px;
+      }
+    }
+  }
+
+  .dashboard-analysis-iconGroup {
+    i {
+      margin-left: 16px;
+      color: rgba(0,0,0,.45);
+      cursor: pointer;
+      transition: color .32s;
+      color: black;
+    }
+  }
 </style>

+ 8 - 7
src/views/dashboard/Workplace.vue

@@ -1,5 +1,6 @@
+
 <template>
-  <page-layout :avatar="avatar">
+  <page-view :avatar="avatar" :title="false">
     <div slot="headerContent">
       <div class="title">{{ timeFix }},{{ user.name }}<span class="welcome-text">,{{ welcome() }}</span></div>
       <div>前端工程师 | 蚂蚁金服 - 某某某事业群 - VUE平台</div>
@@ -106,16 +107,16 @@
         </a-col>
       </a-row>
     </div>
-  </page-layout>
+  </page-view>
 </template>
 
 <script>
 import { timeFix } from '@/utils/util'
 import { mapGetters } from 'vuex'
 
-import PageLayout from '@/components/page/PageLayout'
+import { PageView } from '@/layouts'
 import HeadInfo from '@/components/tools/HeadInfo'
-import Radar from '@/components/chart/Radar'
+import { Radar } from '@/components'
 
 import { getRoleList, getServiceList } from '@/api/manage'
 
@@ -124,7 +125,7 @@ const DataSet = require('@antv/data-set')
 export default {
   name: 'Workplace',
   components: {
-    PageLayout,
+    PageView,
     HeadInfo,
     Radar
   },
@@ -227,7 +228,7 @@ export default {
       this.radarLoading = true
 
       this.$http.get('/workplace/radar')
-        .then(res => {
+        .then(res => {
           const dv = new DataSet.View().source(res.result)
           dv.transform({
             type: 'fold',
@@ -356,4 +357,4 @@ export default {
     }
   }
 
-</style>
+</style>

+ 1 - 1
src/views/exception/403.vue

@@ -3,7 +3,7 @@
 </template>
 
 <script>
-import ExceptionPage from './ExceptionPage'
+import { ExceptionPage } from '@/components'
 
 export default {
   components: {

+ 1 - 1
src/views/exception/404.vue

@@ -3,7 +3,7 @@
 </template>
 
 <script>
-import ExceptionPage from './ExceptionPage'
+import { ExceptionPage } from '@/components'
 
 export default {
   components: {

+ 1 - 1
src/views/exception/500.vue

@@ -3,7 +3,7 @@
 </template>
 
 <script>
-import ExceptionPage from './ExceptionPage'
+import { ExceptionPage } from '@/components'
 
 export default {
   components: {

+ 32 - 1
src/views/form/stepForm/Step1.vue

@@ -41,6 +41,14 @@
         <a-button type="primary" @click="nextStep">下一步</a-button>
       </a-form-item>
     </a-form>
+    <a-divider />
+    <div class="step-form-style-desc">
+      <h3>说明</h3>
+      <h4>转账到支付宝账户</h4>
+      <p>如果需要,这里可以放一些关于产品的常见问题说明。如果需要,这里可以放一些关于产品的常见问题说明。如果需要,这里可以放一些关于产品的常见问题说明。</p>
+      <h4>转账到银行卡</h4>
+      <p>如果需要,这里可以放一些关于产品的常见问题说明。如果需要,这里可以放一些关于产品的常见问题说明。如果需要,这里可以放一些关于产品的常见问题说明。</p>
+    </div>
   </div>
 </template>
 
@@ -55,6 +63,29 @@ export default {
 }
 </script>
 
-<style scoped>
+<style lang="less" scoped>
+.step-form-style-desc {
+  padding: 0 56px;
+  color: rgba(0,0,0,.45);
 
+  h3 {
+    margin: 0 0 12px;
+    color: rgba(0,0,0,.45);
+    font-size: 16px;
+    line-height: 32px;
+  }
+
+  h4 {
+    margin: 0 0 4px;
+    color: rgba(0,0,0,.45);
+    font-size: 14px;
+    line-height: 22px;
+  }
+
+  p {
+    margin-top: 0;
+    margin-bottom: 12px;
+    line-height: 22px;
+  }
+}
 </style>

+ 10 - 1
src/views/form/stepForm/Step2.vue

@@ -38,6 +38,15 @@
       >
         ¥ 5,000.00
       </a-form-item>
+      <a-divider />
+      <a-form-item
+        label="支付密码"
+        :labelCol="{span: 5}"
+        :wrapperCol="{span: 19}"
+        class="stepFormText"
+      >
+        <a-input type="password" style="width: 80%;" value="123456" />
+      </a-form-item>
       <a-form-item :wrapperCol="{span: 19, offset: 5}">
         <a-button :loading="loading" type="primary" @click="nextStep">提交</a-button>
         <a-button style="margin-left: 8px" @click="prevStep">上一步</a-button>
@@ -79,4 +88,4 @@ export default {
     }
   }
 
-</style>
+</style>

+ 2 - 2
src/views/form/stepForm/Step3.vue

@@ -1,7 +1,7 @@
 <template>
   <div>
     <a-form style="margin: 40px auto 0;">
-      <result title="操作成功" :is-success="true" description="预计两小时内到账">
+      <result title="操作成功" :is-success="true" description="预计两小时内到账" style="max-width: 560px;">
         <div class="information">
           <a-row>
             <a-col :sm="8" :xs="24">付款账户:</a-col>
@@ -47,7 +47,7 @@ export default {
       this.$emit('finish')
     },
     toOrderList () {
-      this.$router.push('/list/query-list')
+      this.$router.push('/list/table-list')
     }
   }
 }

+ 50 - 0
src/views/list/QueryList.vue

@@ -0,0 +1,50 @@
+<template>
+  <a-card :bordered="false">
+    <component @onEdit="handleEdit" @onGoBack="handleGoBack" :record="record" :is="currentComponet"></component>
+  </a-card>
+</template>
+
+<script>
+
+import ATextarea from 'ant-design-vue/es/input/TextArea'
+import AInput from 'ant-design-vue/es/input/Input'
+// 动态切换组件
+import List from '@/views/list/table/List'
+import Edit from '@/views/list/table/Edit'
+
+export default {
+  name: 'TableListWrapper',
+  components: {
+    AInput,
+    ATextarea,
+    List,
+    Edit
+  },
+  data () {
+    return {
+      currentComponet: 'List',
+      record: ''
+    }
+  },
+  created () {
+
+  },
+  methods: {
+    handleEdit (record) {
+      this.record = record || ''
+      this.currentComponet = 'Edit'
+      console.log(record)
+    },
+    handleGoBack () {
+      this.record = ''
+      this.currentComponet = 'List'
+    }
+  },
+  watch: {
+    '$route.path' () {
+      this.record = ''
+      this.currentComponet = 'List'
+    }
+  }
+}
+</script>

+ 243 - 25
src/views/list/TableList.vue

@@ -1,49 +1,267 @@
 <template>
   <a-card :bordered="false">
-    <component @onEdit="handleEdit" @onGoBack="handleGoBack" :record="record" :is="currentComponet"></component>
+    <div class="table-page-search-wrapper">
+      <a-form layout="inline">
+        <a-row :gutter="48">
+          <a-col :md="8" :sm="24">
+            <a-form-item label="规则编号">
+              <a-input v-model="queryParam.id" placeholder=""/>
+            </a-form-item>
+          </a-col>
+          <a-col :md="8" :sm="24">
+            <a-form-item label="使用状态">
+              <a-select v-model="queryParam.status" placeholder="请选择" default-value="0">
+                <a-select-option value="0">全部</a-select-option>
+                <a-select-option value="1">关闭</a-select-option>
+                <a-select-option value="2">运行中</a-select-option>
+              </a-select>
+            </a-form-item>
+          </a-col>
+          <template v-if="advanced">
+            <a-col :md="8" :sm="24">
+              <a-form-item label="调用次数">
+                <a-input-number v-model="queryParam.callNo" style="width: 100%"/>
+              </a-form-item>
+            </a-col>
+            <a-col :md="8" :sm="24">
+              <a-form-item label="更新日期">
+                <a-date-picker v-model="queryParam.date" style="width: 100%" placeholder="请输入更新日期"/>
+              </a-form-item>
+            </a-col>
+            <a-col :md="8" :sm="24">
+              <a-form-item label="使用状态">
+                <a-select v-model="queryParam.useStatus" placeholder="请选择" default-value="0">
+                  <a-select-option value="0">全部</a-select-option>
+                  <a-select-option value="1">关闭</a-select-option>
+                  <a-select-option value="2">运行中</a-select-option>
+                </a-select>
+              </a-form-item>
+            </a-col>
+            <a-col :md="8" :sm="24">
+              <a-form-item label="使用状态">
+                <a-select placeholder="请选择" default-value="0">
+                  <a-select-option value="0">全部</a-select-option>
+                  <a-select-option value="1">关闭</a-select-option>
+                  <a-select-option value="2">运行中</a-select-option>
+                </a-select>
+              </a-form-item>
+            </a-col>
+          </template>
+          <a-col :md="!advanced && 8 || 24" :sm="24">
+            <span class="table-page-search-submitButtons" :style="advanced && { float: 'right', overflow: 'hidden' } || {} ">
+              <a-button type="primary" @click="$refs.table.refresh(true)">查询</a-button>
+              <a-button style="margin-left: 8px" @click="() => queryParam = {}">重置</a-button>
+              <a @click="toggleAdvanced" style="margin-left: 8px">
+                {{ advanced ? '收起' : '展开' }}
+                <a-icon :type="advanced ? 'up' : 'down'"/>
+              </a>
+            </span>
+          </a-col>
+        </a-row>
+      </a-form>
+    </div>
+
+    <div class="table-operator">
+      <a-button type="primary" icon="plus" @click="$refs.createModal.add()">新建</a-button>
+      <a-button type="dashed" @click="tableOption">{{ optionAlertShow && '关闭' || '开启' }} alert</a-button>
+      <a-dropdown v-action:edit v-if="selectedRowKeys.length > 0">
+        <a-menu slot="overlay">
+          <a-menu-item key="1"><a-icon type="delete" />删除</a-menu-item>
+          <!-- lock | unlock -->
+          <a-menu-item key="2"><a-icon type="lock" />锁定</a-menu-item>
+        </a-menu>
+        <a-button style="margin-left: 8px">
+          批量操作 <a-icon type="down" />
+        </a-button>
+      </a-dropdown>
+    </div>
+
+    <s-table
+      ref="table"
+      size="default"
+      rowKey="key"
+      :columns="columns"
+      :data="loadData"
+      :alert="options.alert"
+      :rowSelection="options.rowSelection"
+    >
+      <span slot="serial" slot-scope="text, record, index">
+        {{ index + 1 }}
+      </span>
+      <span slot="status" slot-scope="text">
+        <a-badge :status="text | statusTypeFilter" :text="text | statusFilter" />
+      </span>
+
+      <span slot="action" slot-scope="text, record">
+        <template>
+          <a @click="handleEdit(record)">配置</a>
+          <a-divider type="vertical" />
+          <a @click="handleSub(record)">订阅报警</a>
+        </template>
+      </span>
+    </s-table>
+    <create-form ref="createModal" @ok="handleOk" />
+    <step-by-step-modal ref="modal" @ok="handleOk"/>
   </a-card>
 </template>
 
 <script>
+import moment from 'moment'
+import { STable } from '@/components'
+import StepByStepModal from './modules/StepByStepModal'
+import CreateForm from './modules/CreateForm'
+import { getRoleList, getServiceList } from '@/api/manage'
 
-import ATextarea from 'ant-design-vue/es/input/TextArea'
-import AInput from 'ant-design-vue/es/input/Input'
-// 动态切换组件
-import List from '@/views/list/table/List'
-import Edit from '@/views/list/table/Edit'
+const statusMap = {
+  0: {
+    status: 'default',
+    text: '关闭'
+  },
+  1: {
+    status: 'processing',
+    text: '运行中'
+  },
+  2: {
+    status: 'success',
+    text: '已上线'
+  },
+  3: {
+    status: 'error',
+    text: '异常'
+  }
+}
 
 export default {
-  name: 'TableListWrapper',
+  name: 'TableList',
   components: {
-    AInput,
-    ATextarea,
-    List,
-    Edit
+    STable,
+    CreateForm,
+    StepByStepModal
   },
   data () {
     return {
-      currentComponet: 'List',
-      record: ''
+      mdl: {},
+      // 高级搜索 展开/关闭
+      advanced: false,
+      // 查询参数
+      queryParam: {},
+      // 表头
+      columns: [
+        {
+          title: '#',
+          scopedSlots: { customRender: 'serial' }
+        },
+        {
+          title: '规则编号',
+          dataIndex: 'no'
+        },
+        {
+          title: '描述',
+          dataIndex: 'description'
+        },
+        {
+          title: '服务调用次数',
+          dataIndex: 'callNo',
+          sorter: true,
+          needTotal: true,
+          customRender: (text) => text + ' 次'
+        },
+        {
+          title: '状态',
+          dataIndex: 'status',
+          scopedSlots: { customRender: 'status' }
+        },
+        {
+          title: '更新时间',
+          dataIndex: 'updatedAt',
+          sorter: true
+        },
+        {
+          title: '操作',
+          dataIndex: 'action',
+          width: '150px',
+          scopedSlots: { customRender: 'action' }
+        }
+      ],
+      // 加载数据方法 必须为 Promise 对象
+      loadData: parameter => {
+        console.log('loadData.parameter', parameter)
+        return getServiceList(Object.assign(parameter, this.queryParam))
+          .then(res => {
+            return res.result
+          })
+      },
+      selectedRowKeys: [],
+      selectedRows: [],
+
+      // custom table alert & rowSelection
+      options: {
+        alert: { show: true, clear: () => { this.selectedRowKeys = [] } },
+        rowSelection: {
+          selectedRowKeys: this.selectedRowKeys,
+          onChange: this.onSelectChange
+        }
+      },
+      optionAlertShow: false
+    }
+  },
+  filters: {
+    statusFilter (type) {
+      return statusMap[type].text
+    },
+    statusTypeFilter (type) {
+      return statusMap[type].status
     }
   },
   created () {
-
+    this.tableOption()
+    getRoleList({ t: new Date() })
   },
   methods: {
+    tableOption () {
+      if (!this.optionAlertShow) {
+        this.options = {
+          alert: { show: true, clear: () => { this.selectedRowKeys = [] } },
+          rowSelection: {
+            selectedRowKeys: this.selectedRowKeys,
+            onChange: this.onSelectChange
+          }
+        }
+        this.optionAlertShow = true
+      } else {
+        this.options = {
+          alert: false,
+          rowSelection: null
+        }
+        this.optionAlertShow = false
+      }
+    },
+
     handleEdit (record) {
-      this.record = record || ''
-      this.currentComponet = 'Edit'
       console.log(record)
+      this.$refs.modal.edit(record)
     },
-    handleGoBack () {
-      this.record = ''
-      this.currentComponet = 'List'
-    }
-  },
-  watch: {
-    '$route.path' () {
-      this.record = ''
-      this.currentComponet = 'List'
+    handleSub (record) {
+      if (record.status !== 0) {
+        this.$message.info(`${record.no} 订阅成功`)
+      } else {
+        this.$message.error(`${record.no} 订阅失败,规则已关闭`)
+      }
+    },
+    handleOk () {
+      this.$refs.table.refresh()
+    },
+    onSelectChange (selectedRowKeys, selectedRows) {
+      this.selectedRowKeys = selectedRowKeys
+      this.selectedRows = selectedRows
+    },
+    toggleAdvanced () {
+      this.advanced = !this.advanced
+    },
+    resetSearchForm () {
+      this.queryParam = {
+        date: moment(new Date())
+      }
     }
   }
 }

+ 67 - 0
src/views/list/modules/CreateForm.vue

@@ -0,0 +1,67 @@
+<template>
+  <a-modal
+    title="新建规则"
+    :width="640"
+    :visible="visible"
+    :confirmLoading="confirmLoading"
+    @ok="handleSubmit"
+    @cancel="handleCancel"
+  >
+    <a-spin :spinning="confirmLoading">
+      <a-form :form="form">
+        <a-form-item
+          label="描述"
+          :labelCol="labelCol"
+          :wrapperCol="wrapperCol"
+        >
+          <a-input v-decorator="['desc', {rules: [{required: true, min: 5, message: '请输入至少五个字符的规则描述!'}]}]" />
+        </a-form-item>
+      </a-form>
+    </a-spin>
+  </a-modal>
+</template>
+
+<script>
+export default {
+  data () {
+    return {
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 7 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 13 }
+      },
+      visible: false,
+      confirmLoading: false,
+
+      form: this.$form.createForm(this)
+    }
+  },
+  methods: {
+    add () {
+      this.visible = true
+    },
+    handleSubmit () {
+      const { form: { validateFields } } = this
+      this.confirmLoading = true
+      validateFields((errors, values) => {
+        if (!errors) {
+          console.log('values', values)
+          setTimeout(() => {
+            this.visible = false
+            this.confirmLoading = false
+            this.$emit('ok', values)
+          }, 1500)
+        } else {
+          this.confirmLoading = false
+        }
+      })
+    },
+    handleCancel () {
+      this.visible = false
+    }
+  }
+}
+</script>

+ 172 - 0
src/views/list/modules/StepByStepModal.vue

@@ -0,0 +1,172 @@
+<template>
+  <a-modal
+    title="分步对话框"
+    :width="640"
+    :visible="visible"
+    :confirmLoading="confirmLoading"
+    @cancel="handleCancel"
+  >
+    <a-spin :spinning="confirmLoading">
+      <a-steps :current="currentStep" :style="{ marginBottom: '28px' }" size="small">
+        <a-step title="基本信息" />
+        <a-step title="配置规则属性" />
+        <a-step title="设定调度周期" />
+      </a-steps>
+      <a-form :form="form">
+        <!-- step1 -->
+        <div v-show="currentStep === 0">
+          <a-form-item
+            label="规则名称"
+            :labelCol="labelCol"
+            :wrapperCol="wrapperCol"
+          >
+            <a-input v-decorator="['name', {rules: [{required: true}]}]" />
+          </a-form-item>
+          <a-form-item
+            label="规则描述"
+            :labelCol="labelCol"
+            :wrapperCol="wrapperCol"
+          >
+            <a-textarea :rows="4" v-decorator="['desc', {rules: [{required: true}]}]"></a-textarea>
+          </a-form-item>
+        </div>
+        <div v-show="currentStep === 1">
+          <a-form-item
+            label="监控对象"
+            :labelCol="labelCol"
+            :wrapperCol="wrapperCol"
+          >
+            <a-select v-decorator="['target', {initialValue: 0, rules: [{required: true}]}]" style="width: 100%">
+              <a-select-option :value="0">表一</a-select-option>
+              <a-select-option :value="1">表二</a-select-option>
+            </a-select>
+          </a-form-item>
+
+          <a-form-item
+            label="规则模板"
+            :labelCol="labelCol"
+            :wrapperCol="wrapperCol"
+          >
+            <a-select v-decorator="['template', { initialValue: 0, rules: [{required: true}]}]" style="width: 100%">
+              <a-select-option :value="0">规则模板一</a-select-option>
+              <a-select-option :value="1">规则模板二</a-select-option>
+            </a-select>
+          </a-form-item>
+
+          <a-form-item
+            label="规则类型"
+            :labelCol="labelCol"
+            :wrapperCol="wrapperCol"
+          >
+            <a-radio-group v-decorator="['type', {initialValue: 0, rules: [{required: true}]}]" style="width: 100%">
+              <a-radio :value="0">强</a-radio>
+              <a-radio :value="1">弱</a-radio>
+            </a-radio-group>
+          </a-form-item>
+        </div>
+
+        <div v-show="currentStep === 2">
+          <a-form-item
+            label="开始时间"
+            :labelCol="labelCol"
+            :wrapperCol="wrapperCol"
+          >
+            <a-date-picker v-decorator="['time', {rules: [{ type: 'object', required: true, message: 'Please select time!' }]}]" style="width: 100%" />
+          </a-form-item>
+          <a-form-item
+            label="调度周期"
+            :labelCol="labelCol"
+            :wrapperCol="wrapperCol"
+          >
+            <a-select v-decorator="['frequency', { initialValue: 'month', rules: [{required: true}]}]" style="width: 100%">
+              <a-select-option value="month">月</a-select-option>
+              <a-select-option value="week">周</a-select-option>
+            </a-select>
+          </a-form-item>
+        </div>
+        <!-- step1 end -->
+      </a-form>
+    </a-spin>
+    <template slot="footer">
+      <a-button key="back" @click="backward" v-if="currentStep > 0" :style="{ float: 'left' }" >上一步</a-button>
+      <a-button key="cancel" @click="handleCancel">取消</a-button>
+      <a-button key="forward" :loading="confirmLoading" type="primary" @click="handleNext(currentStep)">{{ currentStep === 2 && '完成' || '下一步' }}</a-button>
+    </template>
+  </a-modal>
+</template>
+
+<script>
+import pick from 'lodash.pick'
+
+const stepForms = [
+  ['name', 'desc'],
+  ['target', 'template', 'type'],
+  ['time', 'frequency']
+]
+
+export default {
+  name: 'StepByStepModal',
+  data () {
+    return {
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 7 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 13 }
+      },
+      visible: false,
+      confirmLoading: false,
+      currentStep: 0,
+      mdl: {},
+
+      form: this.$form.createForm(this)
+    }
+  },
+  methods: {
+    edit (record) {
+      this.visible = true
+      const { form: { setFieldsValue } } = this
+      this.$nextTick(() => {
+        setFieldsValue(pick(record, []))
+      })
+    },
+    handleNext (step) {
+      const { form: { validateFields } } = this
+      const currentStep = step + 1
+      if (currentStep <= 2) {
+        // stepForms
+        validateFields(stepForms[ this.currentStep ], (errors, values) => {
+          if (!errors) {
+            this.currentStep = currentStep
+          }
+        })
+        return
+      }
+      // last step
+      this.confirmLoading = true
+      validateFields((errors, values) => {
+        console.log('errors:', errors, 'val:', values)
+        if (!errors) {
+          console.log('values:', values)
+          setTimeout(() => {
+            this.confirmLoading = false
+            this.$emit('ok', values)
+          }, 1500)
+        } else {
+          this.confirmLoading = false
+        }
+      })
+    },
+    backward () {
+      this.currentStep--
+    },
+    handleCancel () {
+      // clear form & currentStep
+      this.visible = false
+      this.currentStep = 0
+    }
+  }
+}
+</script>

+ 1 - 1
src/views/list/table/List.vue

@@ -116,7 +116,7 @@
 
 <script>
 import moment from 'moment'
-import STable from '@/components/table/'
+import { STable } from '@/components'
 import { getRoleList, getServiceList } from '@/api/manage'
 
 export default {

+ 271 - 271
src/views/list/PermissionList.vue → src/views/other/PermissionList.vue

@@ -1,272 +1,272 @@
-<template>
-  <a-card :bordered="false">
-    <div class="table-page-search-wrapper">
-      <a-form layout="inline">
-        <a-row :gutter="48">
-          <a-col :md="8" :sm="24">
-            <a-form-item label="角色ID">
-              <a-input placeholder="请输入"/>
-            </a-form-item>
-          </a-col>
-          <a-col :md="8" :sm="24">
-            <a-form-item label="状态">
-              <a-select placeholder="请选择" default-value="0">
-                <a-select-option value="0">全部</a-select-option>
-                <a-select-option value="1">关闭</a-select-option>
-                <a-select-option value="2">运行中</a-select-option>
-              </a-select>
-            </a-form-item>
-          </a-col>
-          <a-col :md="8" :sm="24">
-            <span class="table-page-search-submitButtons">
-              <a-button type="primary">查询</a-button>
-              <a-button style="margin-left: 8px">重置</a-button>
-            </span>
-          </a-col>
-        </a-row>
-      </a-form>
-    </div>
-
-    <s-table :columns="columns" :data="loadData">
-
-      <span slot="actions" slot-scope="text, record">
-        <a-tag v-for="(action, index) in record.actionList" :key="index">{{ action.describe }}</a-tag>
-      </span>
-
-      <span slot="status" slot-scope="text">
-        {{ text | statusFilter }}
-      </span>
-
-      <span slot="action" slot-scope="text, record">
-        <a @click="handleEdit(record)">编辑</a>
-        <a-divider type="vertical" />
-        <a-dropdown>
-          <a class="ant-dropdown-link">
-            更多 <a-icon type="down" />
-          </a>
-          <a-menu slot="overlay">
-            <a-menu-item>
-              <a href="javascript:;">详情</a>
-            </a-menu-item>
-            <a-menu-item>
-              <a href="javascript:;">禁用</a>
-            </a-menu-item>
-            <a-menu-item>
-              <a href="javascript:;">删除</a>
-            </a-menu-item>
-          </a-menu>
-        </a-dropdown>
-      </span>
-    </s-table>
-
-    <a-modal
-      title="操作"
-      :width="800"
-      v-model="visible"
-      @ok="handleOk"
-    >
-      <a-form :autoFormCreate="(form)=>{this.form = form}">
-
-        <a-form-item
-          :labelCol="labelCol"
-          :wrapperCol="wrapperCol"
-          label="唯一识别码"
-          hasFeedback
-          validateStatus="success"
-        >
-          <a-input placeholder="唯一识别码" v-model="mdl.id" id="no" disabled="disabled" />
-        </a-form-item>
-
-        <a-form-item
-          :labelCol="labelCol"
-          :wrapperCol="wrapperCol"
-          label="权限名称"
-          hasFeedback
-          validateStatus="success"
-        >
-          <a-input placeholder="起一个名字" v-model="mdl.name" id="permission_name" />
-        </a-form-item>
-
-        <a-form-item
-          :labelCol="labelCol"
-          :wrapperCol="wrapperCol"
-          label="状态"
-          hasFeedback
-          validateStatus="warning"
-        >
-          <a-select v-model="mdl.status">
-            <a-select-option value="1">正常</a-select-option>
-            <a-select-option value="2">禁用</a-select-option>
-          </a-select>
-        </a-form-item>
-
-        <a-form-item
-          :labelCol="labelCol"
-          :wrapperCol="wrapperCol"
-          label="描述"
-          hasFeedback
-        >
-          <a-textarea :rows="5" v-model="mdl.describe" placeholder="..." id="describe"/>
-        </a-form-item>
-
-        <a-divider />
-
-        <a-form-item
-          :labelCol="labelCol"
-          :wrapperCol="wrapperCol"
-          label="赋予权限"
-          hasFeedback
-        >
-          <a-select
-            style="width: 100%"
-            mode="multiple"
-            v-model="mdl.actions"
-            :allowClear="true"
-          >
-            <a-select-option v-for="(action, index) in permissionList" :key="index" :value="action.value">{{ action.label }}</a-select-option>
-          </a-select>
-        </a-form-item>
-
-      </a-form>
-    </a-modal>
-
-  </a-card>
-</template>
-
-<script>
-import STable from '@/components/table/'
-
-export default {
-  name: 'TableList',
-  components: {
-    STable
-  },
-  data () {
-    return {
-      description: '列表使用场景:后台管理中的权限管理以及角色管理,可用于基于 RBAC 设计的角色权限控制,颗粒度细到每一个操作类型。',
-
-      visible: false,
-      labelCol: {
-        xs: { span: 24 },
-        sm: { span: 5 }
-      },
-      wrapperCol: {
-        xs: { span: 24 },
-        sm: { span: 16 }
-      },
-      form: null,
-      mdl: {},
-
-      // 高级搜索 展开/关闭
-      advanced: false,
-      // 查询参数
-      queryParam: {},
-      // 表头
-      columns: [
-        {
-          title: '唯一识别码',
-          dataIndex: 'id'
-        },
-        {
-          title: '权限名称',
-          dataIndex: 'name'
-        },
-        {
-          title: '可操作权限',
-          dataIndex: 'actions',
-          scopedSlots: { customRender: 'actions' }
-        },
-        {
-          title: '状态',
-          dataIndex: 'status',
-          scopedSlots: { customRender: 'status' }
-        },
-        {
-          title: '操作',
-          width: '150px',
-          dataIndex: 'action',
-          scopedSlots: { customRender: 'action' }
-        }
-      ],
-      // 向后端拉取可以用的操作列表
-      permissionList: null,
-      // 加载数据方法 必须为 Promise 对象
-      loadData: parameter => {
-        return this.$http.get('/permission', {
-          params: Object.assign(parameter, this.queryParam)
-        }).then(res => {
-          const result = res.result
-          result.data.map(permission => {
-            permission.actionList = JSON.parse(permission.actionData)
-            return permission
-          })
-          return result
-        })
-      },
-
-      selectedRowKeys: [],
-      selectedRows: []
-    }
-  },
-  filters: {
-    statusFilter (status) {
-      const statusMap = {
-        1: '正常',
-        2: '禁用'
-      }
-      return statusMap[status]
-    }
-  },
-  created () {
-    this.loadPermissionList()
-  },
-  methods: {
-    loadPermissionList () {
-      // permissionList
-      new Promise(resolve => {
-        const data = [
-          { label: '新增', value: 'add', defaultChecked: false },
-          { label: '查询', value: 'get', defaultChecked: false },
-          { label: '修改', value: 'update', defaultChecked: false },
-          { label: '列表', value: 'query', defaultChecked: false },
-          { label: '删除', value: 'delete', defaultChecked: false },
-          { label: '导入', value: 'import', defaultChecked: false },
-          { label: '导出', value: 'export', defaultChecked: false }
-        ]
-        setTimeout(resolve(data), 1500)
-      }).then(res => {
-        this.permissionList = res
-      })
-    },
-    handleEdit (record) {
-      this.mdl = Object.assign({}, record)
-      console.log(this.mdl)
-      this.visible = true
-    },
-    handleOk () {
-
-    },
-    onChange (selectedRowKeys, selectedRows) {
-      this.selectedRowKeys = selectedRowKeys
-      this.selectedRows = selectedRows
-    },
-    toggleAdvanced () {
-      this.advanced = !this.advanced
-    }
-  },
-  watch: {
-    /*
-      'selectedRows': function (selectedRows) {
-        this.needTotalList = this.needTotalList.map(item => {
-          return {
-            ...item,
-            total: selectedRows.reduce( (sum, val) => {
-              return sum + val[item.dataIndex]
-            }, 0)
-          }
-        })
-      }
-      */
-  }
-}
+<template>
+  <a-card :bordered="false">
+    <div class="table-page-search-wrapper">
+      <a-form layout="inline">
+        <a-row :gutter="48">
+          <a-col :md="8" :sm="24">
+            <a-form-item label="角色ID">
+              <a-input placeholder="请输入"/>
+            </a-form-item>
+          </a-col>
+          <a-col :md="8" :sm="24">
+            <a-form-item label="状态">
+              <a-select placeholder="请选择" default-value="0">
+                <a-select-option value="0">全部</a-select-option>
+                <a-select-option value="1">关闭</a-select-option>
+                <a-select-option value="2">运行中</a-select-option>
+              </a-select>
+            </a-form-item>
+          </a-col>
+          <a-col :md="8" :sm="24">
+            <span class="table-page-search-submitButtons">
+              <a-button type="primary">查询</a-button>
+              <a-button style="margin-left: 8px">重置</a-button>
+            </span>
+          </a-col>
+        </a-row>
+      </a-form>
+    </div>
+
+    <s-table :columns="columns" :data="loadData">
+
+      <span slot="actions" slot-scope="text, record">
+        <a-tag v-for="(action, index) in record.actionList" :key="index">{{ action.describe }}</a-tag>
+      </span>
+
+      <span slot="status" slot-scope="text">
+        {{ text | statusFilter }}
+      </span>
+
+      <span slot="action" slot-scope="text, record">
+        <a @click="handleEdit(record)">编辑</a>
+        <a-divider type="vertical" />
+        <a-dropdown>
+          <a class="ant-dropdown-link">
+            更多 <a-icon type="down" />
+          </a>
+          <a-menu slot="overlay">
+            <a-menu-item>
+              <a href="javascript:;">详情</a>
+            </a-menu-item>
+            <a-menu-item>
+              <a href="javascript:;">禁用</a>
+            </a-menu-item>
+            <a-menu-item>
+              <a href="javascript:;">删除</a>
+            </a-menu-item>
+          </a-menu>
+        </a-dropdown>
+      </span>
+    </s-table>
+
+    <a-modal
+      title="操作"
+      :width="800"
+      v-model="visible"
+      @ok="handleOk"
+    >
+      <a-form :autoFormCreate="(form)=>{this.form = form}">
+
+        <a-form-item
+          :labelCol="labelCol"
+          :wrapperCol="wrapperCol"
+          label="唯一识别码"
+          hasFeedback
+          validateStatus="success"
+        >
+          <a-input placeholder="唯一识别码" v-model="mdl.id" id="no" disabled="disabled" />
+        </a-form-item>
+
+        <a-form-item
+          :labelCol="labelCol"
+          :wrapperCol="wrapperCol"
+          label="权限名称"
+          hasFeedback
+          validateStatus="success"
+        >
+          <a-input placeholder="起一个名字" v-model="mdl.name" id="permission_name" />
+        </a-form-item>
+
+        <a-form-item
+          :labelCol="labelCol"
+          :wrapperCol="wrapperCol"
+          label="状态"
+          hasFeedback
+          validateStatus="warning"
+        >
+          <a-select v-model="mdl.status">
+            <a-select-option value="1">正常</a-select-option>
+            <a-select-option value="2">禁用</a-select-option>
+          </a-select>
+        </a-form-item>
+
+        <a-form-item
+          :labelCol="labelCol"
+          :wrapperCol="wrapperCol"
+          label="描述"
+          hasFeedback
+        >
+          <a-textarea :rows="5" v-model="mdl.describe" placeholder="..." id="describe"/>
+        </a-form-item>
+
+        <a-divider />
+
+        <a-form-item
+          :labelCol="labelCol"
+          :wrapperCol="wrapperCol"
+          label="赋予权限"
+          hasFeedback
+        >
+          <a-select
+            style="width: 100%"
+            mode="multiple"
+            v-model="mdl.actions"
+            :allowClear="true"
+          >
+            <a-select-option v-for="(action, index) in permissionList" :key="index" :value="action.value">{{ action.label }}</a-select-option>
+          </a-select>
+        </a-form-item>
+
+      </a-form>
+    </a-modal>
+
+  </a-card>
+</template>
+
+<script>
+import { STable } from '@/components'
+
+export default {
+  name: 'TableList',
+  components: {
+    STable
+  },
+  data () {
+    return {
+      description: '列表使用场景:后台管理中的权限管理以及角色管理,可用于基于 RBAC 设计的角色权限控制,颗粒度细到每一个操作类型。',
+
+      visible: false,
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 5 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 16 }
+      },
+      form: null,
+      mdl: {},
+
+      // 高级搜索 展开/关闭
+      advanced: false,
+      // 查询参数
+      queryParam: {},
+      // 表头
+      columns: [
+        {
+          title: '唯一识别码',
+          dataIndex: 'id'
+        },
+        {
+          title: '权限名称',
+          dataIndex: 'name'
+        },
+        {
+          title: '可操作权限',
+          dataIndex: 'actions',
+          scopedSlots: { customRender: 'actions' }
+        },
+        {
+          title: '状态',
+          dataIndex: 'status',
+          scopedSlots: { customRender: 'status' }
+        },
+        {
+          title: '操作',
+          width: '150px',
+          dataIndex: 'action',
+          scopedSlots: { customRender: 'action' }
+        }
+      ],
+      // 向后端拉取可以用的操作列表
+      permissionList: null,
+      // 加载数据方法 必须为 Promise 对象
+      loadData: parameter => {
+        return this.$http.get('/permission', {
+          params: Object.assign(parameter, this.queryParam)
+        }).then(res => {
+          const result = res.result
+          result.data.map(permission => {
+            permission.actionList = JSON.parse(permission.actionData)
+            return permission
+          })
+          return result
+        })
+      },
+
+      selectedRowKeys: [],
+      selectedRows: []
+    }
+  },
+  filters: {
+    statusFilter (status) {
+      const statusMap = {
+        1: '正常',
+        2: '禁用'
+      }
+      return statusMap[status]
+    }
+  },
+  created () {
+    this.loadPermissionList()
+  },
+  methods: {
+    loadPermissionList () {
+      // permissionList
+      new Promise(resolve => {
+        const data = [
+          { label: '新增', value: 'add', defaultChecked: false },
+          { label: '查询', value: 'get', defaultChecked: false },
+          { label: '修改', value: 'update', defaultChecked: false },
+          { label: '列表', value: 'query', defaultChecked: false },
+          { label: '删除', value: 'delete', defaultChecked: false },
+          { label: '导入', value: 'import', defaultChecked: false },
+          { label: '导出', value: 'export', defaultChecked: false }
+        ]
+        setTimeout(resolve(data), 1500)
+      }).then(res => {
+        this.permissionList = res
+      })
+    },
+    handleEdit (record) {
+      this.mdl = Object.assign({}, record)
+      console.log(this.mdl)
+      this.visible = true
+    },
+    handleOk () {
+
+    },
+    onChange (selectedRowKeys, selectedRows) {
+      this.selectedRowKeys = selectedRowKeys
+      this.selectedRows = selectedRows
+    },
+    toggleAdvanced () {
+      this.advanced = !this.advanced
+    }
+  },
+  watch: {
+    /*
+      'selectedRows': function (selectedRows) {
+        this.needTotalList = this.needTotalList.map(item => {
+          return {
+            ...item,
+            total: selectedRows.reduce( (sum, val) => {
+              return sum + val[item.dataIndex]
+            }, 0)
+          }
+        })
+      }
+      */
+  }
+}
 </script>

+ 1 - 1
src/views/list/RoleList.vue → src/views/other/RoleList.vue

@@ -79,7 +79,7 @@
 </template>
 
 <script>
-import STable from '@/components/table/'
+import { STable } from '@/components'
 import RoleModal from './modules/RoleModal'
 
 export default {

+ 1 - 1
src/views/list/TableInnerEditList.vue → src/views/other/TableInnerEditList.vue

@@ -116,7 +116,7 @@
 </template>
 
 <script>
-import STable from '@/components/table/'
+import { STable } from '@/components'
 
 export default {
   name: 'TableList',

+ 192 - 192
src/views/list/TreeList.vue → src/views/other/TreeList.vue

@@ -1,192 +1,192 @@
-<template>
-  <a-card :bordered="false">
-    <a-row :gutter="8">
-      <a-col :span="5">
-        <s-tree
-          :dataSource="orgTree"
-          :openKeys.sync="openKeys"
-          :search="true"
-          @click="handleClick"
-          @add="handleAdd"
-          @titleClick="handleTitleClick"></s-tree>
-      </a-col>
-      <a-col :span="19">
-        <s-table
-          ref="table"
-          size="default"
-          :columns="columns"
-          :data="loadData"
-          :alert="false"
-          :rowSelection="{ selectedRowKeys: selectedRowKeys, onChange: onSelectChange }"
-        >
-          <span slot="action" slot-scope="text, record">
-            <template v-if="$auth('table.update')">
-              <a @click="handleEdit(record)">编辑</a>
-              <a-divider type="vertical" />
-            </template>
-            <a-dropdown>
-              <a class="ant-dropdown-link">
-                更多 <a-icon type="down" />
-              </a>
-              <a-menu slot="overlay">
-                <a-menu-item>
-                  <a href="javascript:;">详情</a>
-                </a-menu-item>
-                <a-menu-item v-if="$auth('table.disable')">
-                  <a href="javascript:;">禁用</a>
-                </a-menu-item>
-                <a-menu-item v-if="$auth('table.delete')">
-                  <a href="javascript:;">删除</a>
-                </a-menu-item>
-              </a-menu>
-            </a-dropdown>
-          </span>
-        </s-table>
-      </a-col>
-    </a-row>
-
-    <org-modal ref="modal" @ok="handleSaveOk" @close="handleSaveClose" />
-  </a-card>
-</template>
-
-<script>
-import STree from '@/components/Tree/Tree'
-import STable from '@/components/table/'
-import OrgModal from './modules/OrgModal'
-import { getOrgTree, getServiceList } from '@/api/manage'
-
-export default {
-  name: 'TreeList',
-  components: {
-    STable,
-    STree,
-    OrgModal
-  },
-  data () {
-    return {
-      openKeys: ['key-01'],
-
-      // 查询参数
-      queryParam: {},
-      // 表头
-      columns: [
-        {
-          title: '#',
-          dataIndex: 'no'
-        },
-        {
-          title: '成员名称',
-          dataIndex: 'description'
-        },
-        {
-          title: '登录次数',
-          dataIndex: 'callNo',
-          sorter: true,
-          needTotal: true,
-          customRender: (text) => text + ' 次'
-        },
-        {
-          title: '状态',
-          dataIndex: 'status',
-          needTotal: true
-        },
-        {
-          title: '更新时间',
-          dataIndex: 'updatedAt',
-          sorter: true
-        },
-        {
-          table: '操作',
-          dataIndex: 'action',
-          width: '150px',
-          scopedSlots: { customRender: 'action' }
-        }
-      ],
-      // 加载数据方法 必须为 Promise 对象
-      loadData: parameter => {
-        return getServiceList(Object.assign(parameter, this.queryParam))
-          .then(res => {
-            return res.result
-          })
-      },
-      orgTree: [],
-      selectedRowKeys: [],
-      selectedRows: []
-    }
-  },
-  created () {
-    getOrgTree().then(res => {
-      this.orgTree = res.result
-    })
-  },
-  methods: {
-    handleClick (e) {
-      console.log('handleClick', e)
-      this.queryParam = {
-        key: e.key
-      }
-      this.$refs.table.refresh(true)
-    },
-    handleAdd (item) {
-      console.log('add button, item', item)
-      this.$message.info(`提示:你点了 ${item.key} - ${item.title} `)
-      this.$refs.modal.add(item.key)
-    },
-    handleTitleClick (item) {
-      console.log('handleTitleClick', item)
-    },
-    titleClick (e) {
-      console.log('titleClick', e)
-    },
-    handleSaveOk () {
-
-    },
-    handleSaveClose () {
-
-    },
-
-    onSelectChange (selectedRowKeys, selectedRows) {
-      this.selectedRowKeys = selectedRowKeys
-      this.selectedRows = selectedRows
-    }
-  }
-}
-</script>
-
-<style lang="less">
-  .custom-tree {
-
-    /deep/ .ant-menu-item-group-title {
-      position: relative;
-      &:hover {
-        .btn {
-          display: block;
-        }
-      }
-    }
-
-    /deep/ .ant-menu-item {
-      &:hover {
-        .btn {
-          display: block;
-        }
-      }
-    }
-
-    /deep/ .btn {
-      display: none;
-      position: absolute;
-      top: 0;
-      right: 10px;
-      width: 20px;
-      height: 40px;
-      line-height: 40px;
-      z-index: 1050;
-
-      &:hover {
-        transform: scale(1.2);
-        transition: 0.5s all;
-      }
-    }
-  }
-</style>
+<template>
+  <a-card :bordered="false">
+    <a-row :gutter="8">
+      <a-col :span="5">
+        <s-tree
+          :dataSource="orgTree"
+          :openKeys.sync="openKeys"
+          :search="true"
+          @click="handleClick"
+          @add="handleAdd"
+          @titleClick="handleTitleClick"></s-tree>
+      </a-col>
+      <a-col :span="19">
+        <s-table
+          ref="table"
+          size="default"
+          :columns="columns"
+          :data="loadData"
+          :alert="false"
+          :rowSelection="{ selectedRowKeys: selectedRowKeys, onChange: onSelectChange }"
+        >
+          <span slot="action" slot-scope="text, record">
+            <template v-if="$auth('table.update')">
+              <a @click="handleEdit(record)">编辑</a>
+              <a-divider type="vertical" />
+            </template>
+            <a-dropdown>
+              <a class="ant-dropdown-link">
+                更多 <a-icon type="down" />
+              </a>
+              <a-menu slot="overlay">
+                <a-menu-item>
+                  <a href="javascript:;">详情</a>
+                </a-menu-item>
+                <a-menu-item v-if="$auth('table.disable')">
+                  <a href="javascript:;">禁用</a>
+                </a-menu-item>
+                <a-menu-item v-if="$auth('table.delete')">
+                  <a href="javascript:;">删除</a>
+                </a-menu-item>
+              </a-menu>
+            </a-dropdown>
+          </span>
+        </s-table>
+      </a-col>
+    </a-row>
+
+    <org-modal ref="modal" @ok="handleSaveOk" @close="handleSaveClose" />
+  </a-card>
+</template>
+
+<script>
+import STree from '@/components/Tree/Tree'
+import { STable } from '@/components'
+import OrgModal from './modules/OrgModal'
+import { getOrgTree, getServiceList } from '@/api/manage'
+
+export default {
+  name: 'TreeList',
+  components: {
+    STable,
+    STree,
+    OrgModal
+  },
+  data () {
+    return {
+      openKeys: ['key-01'],
+
+      // 查询参数
+      queryParam: {},
+      // 表头
+      columns: [
+        {
+          title: '#',
+          dataIndex: 'no'
+        },
+        {
+          title: '成员名称',
+          dataIndex: 'description'
+        },
+        {
+          title: '登录次数',
+          dataIndex: 'callNo',
+          sorter: true,
+          needTotal: true,
+          customRender: (text) => text + ' 次'
+        },
+        {
+          title: '状态',
+          dataIndex: 'status',
+          needTotal: true
+        },
+        {
+          title: '更新时间',
+          dataIndex: 'updatedAt',
+          sorter: true
+        },
+        {
+          table: '操作',
+          dataIndex: 'action',
+          width: '150px',
+          scopedSlots: { customRender: 'action' }
+        }
+      ],
+      // 加载数据方法 必须为 Promise 对象
+      loadData: parameter => {
+        return getServiceList(Object.assign(parameter, this.queryParam))
+          .then(res => {
+            return res.result
+          })
+      },
+      orgTree: [],
+      selectedRowKeys: [],
+      selectedRows: []
+    }
+  },
+  created () {
+    getOrgTree().then(res => {
+      this.orgTree = res.result
+    })
+  },
+  methods: {
+    handleClick (e) {
+      console.log('handleClick', e)
+      this.queryParam = {
+        key: e.key
+      }
+      this.$refs.table.refresh(true)
+    },
+    handleAdd (item) {
+      console.log('add button, item', item)
+      this.$message.info(`提示:你点了 ${item.key} - ${item.title} `)
+      this.$refs.modal.add(item.key)
+    },
+    handleTitleClick (item) {
+      console.log('handleTitleClick', item)
+    },
+    titleClick (e) {
+      console.log('titleClick', e)
+    },
+    handleSaveOk () {
+
+    },
+    handleSaveClose () {
+
+    },
+
+    onSelectChange (selectedRowKeys, selectedRows) {
+      this.selectedRowKeys = selectedRowKeys
+      this.selectedRows = selectedRows
+    }
+  }
+}
+</script>
+
+<style lang="less">
+  .custom-tree {
+
+    /deep/ .ant-menu-item-group-title {
+      position: relative;
+      &:hover {
+        .btn {
+          display: block;
+        }
+      }
+    }
+
+    /deep/ .ant-menu-item {
+      &:hover {
+        .btn {
+          display: block;
+        }
+      }
+    }
+
+    /deep/ .btn {
+      display: none;
+      position: absolute;
+      top: 0;
+      right: 10px;
+      width: 20px;
+      height: 40px;
+      line-height: 40px;
+      z-index: 1050;
+
+      &:hover {
+        transform: scale(1.2);
+        transition: 0.5s all;
+      }
+    }
+  }
+</style>

+ 1 - 1
src/views/list/UserList.vue → src/views/other/UserList.vue

@@ -149,7 +149,7 @@
 </template>
 
 <script>
-import STable from '@/components/table/'
+import { STable } from '@/components'
 import { getRoleList, getServiceList } from '@/api/manage'
 
 export default {

+ 0 - 0
src/views/list/modules/OrgModal.vue → src/views/other/modules/OrgModal.vue


+ 11 - 67
src/views/list/modules/RoleModal.vue → src/views/other/modules/RoleModal.vue

@@ -7,73 +7,17 @@
     @ok="handleOk"
     @cancel="handleCancel"
   >
-    <a-spin :spinning="confirmLoading">
-      <a-form :form="form">
-
-        <a-form-item
-          :labelCol="labelCol"
-          :wrapperCol="wrapperCol"
-          label="唯一识别码"
-          hasFeedback
-        >
-          <a-input placeholder="唯一识别码" disabled="disabled" v-decorator="[ 'id', {rules: []} ]" />
-        </a-form-item>
-
-        <a-form-item
-          :labelCol="labelCol"
-          :wrapperCol="wrapperCol"
-          label="角色名称"
-          hasFeedback >
-          <a-input placeholder="起一个名字" v-decorator="[ 'name', {rules: [{ required: true, message: '不起一个名字吗?' }] }]" />
-        </a-form-item>
-
-        <a-form-item
-          :labelCol="labelCol"
-          :wrapperCol="wrapperCol"
-          label="状态"
-          hasFeedback >
-          <a-select v-decorator="[ 'status', {rules: []} ]">
-            <a-select-option :value="1">正常</a-select-option>
-            <a-select-option :value="2">禁用</a-select-option>
-          </a-select>
-        </a-form-item>
-
-        <a-form-item
-          :labelCol="labelCol"
-          :wrapperCol="wrapperCol"
-          label="描述"
-          hasFeedback
-        >
-          <a-textarea :rows="5" placeholder="..." v-decorator="[ 'describe', { rules: [] } ]" />
-        </a-form-item>
-
-        <a-divider/>
-
-        <a-form-item
-          :labelCol="labelCol"
-          :wrapperCol="wrapperCol"
-          label="拥有权限"
-          hasFeedback
-        >
-          <a-row :gutter="16" v-for="(permission, index) in permissions" :key="index">
-            <a-col :span="4">
-              {{ permission.name }}:
-            </a-col>
-            <a-col :span="20">
-              <a-checkbox
-                v-if="permission.actionsOptions.length > 0"
-                :indeterminate="permission.indeterminate"
-                :checked="permission.checkedAll"
-                @change="onChangeCheckAll($event, permission)">
-                全选
-              </a-checkbox>
-              <a-checkbox-group :options="permission.actionsOptions" v-model="permission.selected" @change="onChangeCheck(permission)" />
-            </a-col>
-          </a-row>
-
-        </a-form-item>
-      </a-form>
-    </a-spin>
+    <a-steps :current="1">
+      <a-step>
+        <!-- <span slot="title">Finished</span> -->
+        <template slot="title">
+          Finished
+        </template>
+        <span slot="description">This is a description.</span>
+      </a-step>
+      <a-step title="In Progress" description="This is a description." />
+      <a-step title="Waiting" description="This is a description." />
+    </a-steps>
   </a-modal>
 </template>
 

+ 6 - 5
src/views/profile/advanced/Advanced.vue

@@ -1,5 +1,6 @@
+
 <template>
-  <page-layout title="单号:234231029431" logo="https://gw.alipayobjects.com/zos/rmsportal/nxkuOJlFJuAUhzlMTCEe.png">
+  <page-view title="单号:234231029431" logo="https://gw.alipayobjects.com/zos/rmsportal/nxkuOJlFJuAUhzlMTCEe.png">
 
     <detail-list slot="headerContent" size="small" :col="2" class="detail-layout">
       <detail-list-item term="创建人">曲丽丽</detail-list-item>
@@ -129,12 +130,12 @@
       </a-table>
     </a-card>
 
-  </page-layout>
+  </page-view>
 </template>
 
 <script>
-import { mixinDevice } from '@/utils/mixin.js'
-import PageLayout from '@/components/page/PageLayout'
+import { mixinDevice } from '@/utils/mixin'
+import { PageView } from '@/layouts'
 import DetailList from '@/components/tools/DetailList'
 
 const DetailListItem = DetailList.Item
@@ -142,7 +143,7 @@ const DetailListItem = DetailList.Item
 export default {
   name: 'Advanced',
   components: {
-    PageLayout,
+    PageView,
     DetailList,
     DetailListItem
   },

+ 8 - 8
src/views/profile/basic/Index.vue

@@ -1,5 +1,5 @@
 <template>
-  <page-layout :title="title">
+  <page-view :title="title">
     <a-card :bordered="false">
       <detail-list title="退款申请">
         <detail-list-item term="取货单号">1000000000</detail-list-item>
@@ -20,6 +20,7 @@
       <div class="title">退货商品</div>
       <s-table
         style="margin-bottom: 24px"
+        row-key="id"
         :columns="goodsColumns"
         :data="loadGoodsData">
 
@@ -28,6 +29,7 @@
       <div class="title">退货进度</div>
       <s-table
         style="margin-bottom: 24px"
+        row-key="key"
         :columns="scheduleColumns"
         :data="loadScheduleData">
 
@@ -39,20 +41,18 @@
 
       </s-table>
     </a-card>
-  </page-layout>
+  </page-view>
 </template>
 
 <script>
-import PageLayout from '@/components/page/PageLayout'
-import STable from '@/components/table/'
+import { PageView } from '@/layouts'
+import { STable } from '@/components'
 import DetailList from '@/components/tools/DetailList'
-import ABadge from 'ant-design-vue/es/badge/Badge'
 const DetailListItem = DetailList.Item
 
 export default {
   components: {
-    PageLayout,
-    ABadge,
+    PageView,
     DetailList,
     DetailListItem,
     STable
@@ -252,4 +252,4 @@ export default {
     font-weight: 500;
     margin-bottom: 16px;
   }
-</style>
+</style>

+ 2 - 2
src/views/result/Success.vue

@@ -26,7 +26,7 @@
           <a-step >
             <span style="font-size: 14px" slot="title">创建项目</span>
             <template slot="description">
-              <div style="fontSize: 12px; color: rgba(0, 0, 0, 0.45); position: relative; left: 42px;" slot="description" >
+              <div style="fontSize: 12px; color: rgba(0, 0, 0, 0.45); position: relative; left: 42px;text-align: left;" slot="description" >
                 <div style="margin: 8px 0 4px">
                   曲丽丽
                   <a-icon style="margin-left: 8px" type="dingding-o" />
@@ -38,7 +38,7 @@
           <a-step title="部门初审">
             <span style="font-size: 14px" slot="title">部门初审</span>
             <template slot="description">
-              <div style="fontSize: 12px; color: rgba(0, 0, 0, 0.45); position: relative; left: 42px;" slot="description" >
+              <div style="fontSize: 12px; color: rgba(0, 0, 0, 0.45); position: relative; left: 42px;text-align: left;" slot="description" >
                 <div style="margin: 8px 0 4px">
                   周毛毛
                   <a-icon style="margin-left: 8px; color: #00A0E9" type="dingding-o" />

+ 2 - 8
vue.config.js

@@ -86,20 +86,14 @@ module.exports = {
   },
 
   devServer: {
+    // development server port 8000
+    port: 8000,
     proxy: {
       '/api': {
         // target: 'https://mock.ihx.me/mock/5baf3052f7da7e07e04a5116/antd-pro',
         target: 'https://mock.ihx.me/mock/5baf3052f7da7e07e04a5116/antd-pro',
         ws: false,
         changeOrigin: true
-      },
-      '/gateway': {
-        target: 'https://www.easy-mock.com/mock/5b7bce071f130e5b7fe8cd7d/antd-pro',
-        ws: false,
-        changeOrigin: true,
-        pathRewrite: {
-          '^/gateway': '/api'
-        }
       }
     }
   },