Browse Source

FIX: Unite LF. Fixed partial file newline format

Signed-off-by: mynuolr <3831242+mynuolr@users.noreply.github.com>
mynuolr 6 years ago
parent
commit
30715b5729

+ 1 - 1
.editorconfig

@@ -1,6 +1,6 @@
 [*]
 charset=utf-8
-end_of_line=crlf
+end_of_line=lf
 insert_final_newline=false
 indent_style=space
 indent_size=2

+ 20 - 20
LICENSE

@@ -1,21 +1,21 @@
-MIT License
-
-Copyright (c) 2018 Anan Yang
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+MIT License
+
+Copyright (c) 2018 Anan Yang
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.

+ 11 - 11
src/api/index.js

@@ -1,12 +1,12 @@
-const api = {
-  Login: '/auth/login',
-  Logout: '/auth/logout',
-  ForgePassword: '/auth/forge-password',
-  Register: '/auth/register',
-  twoStepCode: '/auth/2step-code',
-  SendSms: '/account/sms',
-  SendSmsErr: '/account/sms_err',
-  // get my info
-  UserInfo: '/user/info'
-}
+const api = {
+  Login: '/auth/login',
+  Logout: '/auth/logout',
+  ForgePassword: '/auth/forge-password',
+  Register: '/auth/register',
+  twoStepCode: '/auth/2step-code',
+  SendSms: '/account/sms',
+  SendSmsErr: '/account/sms_err',
+  // get my info
+  UserInfo: '/user/info'
+}
 export default api

+ 60 - 60
src/api/login.js

@@ -1,61 +1,61 @@
-import api from './index'
-import { axios } from '@/utils/request'
-
-/**
- * login func
- * parameter: {
- *     username: '',
- *     password: '',
- *     remember_me: true,
- *     captcha: '12345'
- * }
- * @param parameter
- * @returns {*}
- */
-export function login (parameter) {
-  return axios({
-    url: '/auth/login',
-    method: 'post',
-    data: parameter
-  })
-}
-
-export function getSmsCaptcha (parameter) {
-  return axios({
-    url: api.SendSms,
-    method: 'post',
-    data: parameter
-  })
-}
-
-export function getInfo () {
-  return axios({
-    url: '/user/info',
-    method: 'get',
-    headers: {
-      'Content-Type': 'application/json;charset=UTF-8'
-    }
-  })
-}
-
-export function logout () {
-  return axios({
-    url: '/auth/logout',
-    method: 'post',
-    headers: {
-      'Content-Type': 'application/json;charset=UTF-8'
-    }
-  })
-}
-
-/**
- * get user 2step code open?
- * @param parameter {*}
- */
-export function get2step (parameter) {
-  return axios({
-    url: api.twoStepCode,
-    method: 'post',
-    data: parameter
-  })
+import api from './index'
+import { axios } from '@/utils/request'
+
+/**
+ * login func
+ * parameter: {
+ *     username: '',
+ *     password: '',
+ *     remember_me: true,
+ *     captcha: '12345'
+ * }
+ * @param parameter
+ * @returns {*}
+ */
+export function login (parameter) {
+  return axios({
+    url: '/auth/login',
+    method: 'post',
+    data: parameter
+  })
+}
+
+export function getSmsCaptcha (parameter) {
+  return axios({
+    url: api.SendSms,
+    method: 'post',
+    data: parameter
+  })
+}
+
+export function getInfo () {
+  return axios({
+    url: '/user/info',
+    method: 'get',
+    headers: {
+      'Content-Type': 'application/json;charset=UTF-8'
+    }
+  })
+}
+
+export function logout () {
+  return axios({
+    url: '/auth/logout',
+    method: 'post',
+    headers: {
+      'Content-Type': 'application/json;charset=UTF-8'
+    }
+  })
+}
+
+/**
+ * get user 2step code open?
+ * @param parameter {*}
+ */
+export function get2step (parameter) {
+  return axios({
+    url: api.twoStepCode,
+    method: 'post',
+    data: parameter
+  })
 }

+ 5 - 5
src/components/tools/DetailList.vue

@@ -1,5 +1,5 @@
-<script>
-/* WARNING: 兼容老引入,请勿继续使用 */
-import DescriptionList from '@/components/DescriptionList'
-export default DescriptionList
-</script>
+<script>
+/* WARNING: 兼容老引入,请勿继续使用 */
+import DescriptionList from '@/components/DescriptionList'
+export default DescriptionList
+</script>

+ 66 - 66
src/components/tools/HeadInfo.vue

@@ -1,67 +1,67 @@
-<template>
-  <div class="head-info" :class="center && 'center'">
-    <span>{{ title }}</span>
-    <p>{{ content }}</p>
-    <em v-if="bordered"/>
-  </div>
-</template>
-
-<script>
-export default {
-  name: 'HeadInfo',
-  props: {
-    title: {
-      type: String,
-      default: ''
-    },
-    content: {
-      type: String,
-      default: ''
-    },
-    bordered: {
-      type: Boolean,
-      default: false
-    },
-    center: {
-      type: Boolean,
-      default: true
-    }
-  }
-}
-</script>
-
-<style lang="less" scoped>
-  .head-info {
-    position: relative;
-    text-align: left;
-    padding: 0 32px 0 0;
-    min-width: 125px;
-
-    &.center {
-      text-align: center;
-      padding: 0 32px;
-    }
-
-    span {
-      color: rgba(0, 0, 0, .45);
-      display: inline-block;
-      font-size: 14px;
-      line-height: 22px;
-      margin-bottom: 4px;
-    }
-    p {
-      color: rgba(0, 0, 0, .85);
-      font-size: 24px;
-      line-height: 32px;
-      margin: 0;
-    }
-    em {
-      background-color: #e8e8e8;
-      position: absolute;
-      height: 56px;
-      width: 1px;
-      top: 0;
-      right: 0;
-    }
-  }
+<template>
+  <div class="head-info" :class="center && 'center'">
+    <span>{{ title }}</span>
+    <p>{{ content }}</p>
+    <em v-if="bordered"/>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'HeadInfo',
+  props: {
+    title: {
+      type: String,
+      default: ''
+    },
+    content: {
+      type: String,
+      default: ''
+    },
+    bordered: {
+      type: Boolean,
+      default: false
+    },
+    center: {
+      type: Boolean,
+      default: true
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+  .head-info {
+    position: relative;
+    text-align: left;
+    padding: 0 32px 0 0;
+    min-width: 125px;
+
+    &.center {
+      text-align: center;
+      padding: 0 32px;
+    }
+
+    span {
+      color: rgba(0, 0, 0, .45);
+      display: inline-block;
+      font-size: 14px;
+      line-height: 22px;
+      margin-bottom: 4px;
+    }
+    p {
+      color: rgba(0, 0, 0, .85);
+      font-size: 24px;
+      line-height: 32px;
+      margin: 0;
+    }
+    em {
+      background-color: #e8e8e8;
+      position: absolute;
+      height: 56px;
+      width: 1px;
+      top: 0;
+      right: 0;
+    }
+  }
 </style>

+ 88 - 88
src/components/tools/TwoStepCaptcha.vue

@@ -1,89 +1,89 @@
-<template>
-  <!-- 两步验证 -->
-  <a-modal
-    centered
-    v-model="visible"
-    @cancel="handleCancel"
-    :maskClosable="false"
-  >
-    <div slot="title" :style="{ textAlign: 'center' }">两步验证</div>
-    <template slot="footer">
-      <div :style="{ textAlign: 'center' }">
-        <a-button key="back" @click="handleCancel">返回</a-button>
-        <a-button key="submit" type="primary" :loading="stepLoading" @click="handleStepOk">
-          继续
-        </a-button>
-      </div>
-    </template>
-
-    <a-spin :spinning="stepLoading">
-      <a-form layout="vertical" :auto-form-create="(form)=>{this.form = form}">
-        <div class="step-form-wrapper">
-          <p style="text-align: center" v-if="!stepLoading">请在手机中打开 Google Authenticator 或两步验证 APP<br />输入 6 位动态码</p>
-          <p style="text-align: center" v-else>正在验证..<br/>请稍后</p>
-          <a-form-item
-            :style="{ textAlign: 'center' }"
-            hasFeedback
-            fieldDecoratorId="stepCode"
-            :fieldDecoratorOptions="{rules: [{ required: true, message: '请输入 6 位动态码!', pattern: /^\d{6}$/, len: 6 }]}"
-          >
-            <a-input :style="{ textAlign: 'center' }" @keyup.enter.native="handleStepOk" placeholder="000000" />
-          </a-form-item>
-          <p style="text-align: center">
-            <a @click="onForgeStepCode">遗失手机?</a>
-          </p>
-        </div>
-      </a-form>
-    </a-spin>
-  </a-modal>
-</template>
-
-<script>
-export default {
-  props: {
-    visible: {
-      type: Boolean,
-      default: false
-    }
-  },
-  data () {
-    return {
-      stepLoading: false,
-
-      form: null
-    }
-  },
-  methods: {
-    handleStepOk () {
-      const vm = this
-      this.stepLoading = true
-      this.form.validateFields((err, values) => {
-        if (!err) {
-          console.log('values', values)
-          setTimeout(() => {
-            vm.stepLoading = false
-            vm.$emit('success', { values })
-          }, 2000)
-          return
-        }
-        this.stepLoading = false
-        this.$emit('error', { err })
-      })
-    },
-    handleCancel () {
-      this.visible = false
-      this.$emit('cancel')
-    },
-    onForgeStepCode () {
-
-    }
-  }
-}
-</script>
-<style lang="less" scoped>
-  .step-form-wrapper {
-    margin: 0 auto;
-    width: 80%;
-    max-width: 400px;
-  }
+<template>
+  <!-- 两步验证 -->
+  <a-modal
+    centered
+    v-model="visible"
+    @cancel="handleCancel"
+    :maskClosable="false"
+  >
+    <div slot="title" :style="{ textAlign: 'center' }">两步验证</div>
+    <template slot="footer">
+      <div :style="{ textAlign: 'center' }">
+        <a-button key="back" @click="handleCancel">返回</a-button>
+        <a-button key="submit" type="primary" :loading="stepLoading" @click="handleStepOk">
+          继续
+        </a-button>
+      </div>
+    </template>
+
+    <a-spin :spinning="stepLoading">
+      <a-form layout="vertical" :auto-form-create="(form)=>{this.form = form}">
+        <div class="step-form-wrapper">
+          <p style="text-align: center" v-if="!stepLoading">请在手机中打开 Google Authenticator 或两步验证 APP<br />输入 6 位动态码</p>
+          <p style="text-align: center" v-else>正在验证..<br/>请稍后</p>
+          <a-form-item
+            :style="{ textAlign: 'center' }"
+            hasFeedback
+            fieldDecoratorId="stepCode"
+            :fieldDecoratorOptions="{rules: [{ required: true, message: '请输入 6 位动态码!', pattern: /^\d{6}$/, len: 6 }]}"
+          >
+            <a-input :style="{ textAlign: 'center' }" @keyup.enter.native="handleStepOk" placeholder="000000" />
+          </a-form-item>
+          <p style="text-align: center">
+            <a @click="onForgeStepCode">遗失手机?</a>
+          </p>
+        </div>
+      </a-form>
+    </a-spin>
+  </a-modal>
+</template>
+
+<script>
+export default {
+  props: {
+    visible: {
+      type: Boolean,
+      default: false
+    }
+  },
+  data () {
+    return {
+      stepLoading: false,
+
+      form: null
+    }
+  },
+  methods: {
+    handleStepOk () {
+      const vm = this
+      this.stepLoading = true
+      this.form.validateFields((err, values) => {
+        if (!err) {
+          console.log('values', values)
+          setTimeout(() => {
+            vm.stepLoading = false
+            vm.$emit('success', { values })
+          }, 2000)
+          return
+        }
+        this.stepLoading = false
+        this.$emit('error', { err })
+      })
+    },
+    handleCancel () {
+      this.visible = false
+      this.$emit('cancel')
+    },
+    onForgeStepCode () {
+
+    }
+  }
+}
+</script>
+<style lang="less" scoped>
+  .step-form-wrapper {
+    margin: 0 auto;
+    width: 80%;
+    max-width: 400px;
+  }
 </style>

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

@@ -1,79 +1,79 @@
-<template>
-  <div class="user-wrapper">
-    <div class="content-box">
-      <a href="https://pro.loacg.com/docs/getting-started" target="_blank">
-        <span class="action">
-          <a-icon type="question-circle-o"></a-icon>
-        </span>
-      </a>
-      <notice-icon class="action"/>
-      <a-dropdown>
-        <span class="action ant-dropdown-link user-dropdown-menu">
-          <a-avatar class="avatar" size="small" :src="avatar()"/>
-          <span>{{ nickname() }}</span>
-        </span>
-        <a-menu slot="overlay" class="user-dropdown-menu-wrapper">
-          <a-menu-item key="0">
-            <router-link :to="{ name: 'center' }">
-              <a-icon type="user"/>
-              <span>个人中心</span>
-            </router-link>
-          </a-menu-item>
-          <a-menu-item key="1">
-            <router-link :to="{ name: 'settings' }">
-              <a-icon type="setting"/>
-              <span>账户设置</span>
-            </router-link>
-          </a-menu-item>
-          <a-menu-item key="2" disabled>
-            <a-icon type="setting"/>
-            <span>测试</span>
-          </a-menu-item>
-          <a-menu-divider/>
-          <a-menu-item key="3">
-            <a href="javascript:;" @click="handleLogout">
-              <a-icon type="logout"/>
-              <span>退出登录</span>
-            </a>
-          </a-menu-item>
-        </a-menu>
-      </a-dropdown>
-    </div>
-  </div>
-</template>
-
-<script>
-import NoticeIcon from '@/components/NoticeIcon'
-import { mapActions, mapGetters } from 'vuex'
-
-export default {
-  name: 'UserMenu',
-  components: {
-    NoticeIcon
-  },
-  methods: {
-    ...mapActions(['Logout']),
-    ...mapGetters(['nickname', 'avatar']),
-    handleLogout () {
-      const that = this
-
-      this.$confirm({
-        title: '提示',
-        content: '真的要注销登录吗 ?',
-        onOk () {
-          return that.Logout({}).then(() => {
-            window.location.reload()
-          }).catch(err => {
-            that.$message.error({
-              title: '错误',
-              description: err.message
-            })
-          })
-        },
-        onCancel () {
-        }
-      })
-    }
-  }
-}
-</script>
+<template>
+  <div class="user-wrapper">
+    <div class="content-box">
+      <a href="https://pro.loacg.com/docs/getting-started" target="_blank">
+        <span class="action">
+          <a-icon type="question-circle-o"></a-icon>
+        </span>
+      </a>
+      <notice-icon class="action"/>
+      <a-dropdown>
+        <span class="action ant-dropdown-link user-dropdown-menu">
+          <a-avatar class="avatar" size="small" :src="avatar()"/>
+          <span>{{ nickname() }}</span>
+        </span>
+        <a-menu slot="overlay" class="user-dropdown-menu-wrapper">
+          <a-menu-item key="0">
+            <router-link :to="{ name: 'center' }">
+              <a-icon type="user"/>
+              <span>个人中心</span>
+            </router-link>
+          </a-menu-item>
+          <a-menu-item key="1">
+            <router-link :to="{ name: 'settings' }">
+              <a-icon type="setting"/>
+              <span>账户设置</span>
+            </router-link>
+          </a-menu-item>
+          <a-menu-item key="2" disabled>
+            <a-icon type="setting"/>
+            <span>测试</span>
+          </a-menu-item>
+          <a-menu-divider/>
+          <a-menu-item key="3">
+            <a href="javascript:;" @click="handleLogout">
+              <a-icon type="logout"/>
+              <span>退出登录</span>
+            </a>
+          </a-menu-item>
+        </a-menu>
+      </a-dropdown>
+    </div>
+  </div>
+</template>
+
+<script>
+import NoticeIcon from '@/components/NoticeIcon'
+import { mapActions, mapGetters } from 'vuex'
+
+export default {
+  name: 'UserMenu',
+  components: {
+    NoticeIcon
+  },
+  methods: {
+    ...mapActions(['Logout']),
+    ...mapGetters(['nickname', 'avatar']),
+    handleLogout () {
+      const that = this
+
+      this.$confirm({
+        title: '提示',
+        content: '真的要注销登录吗 ?',
+        onOk () {
+          return that.Logout({}).then(() => {
+            window.location.reload()
+          }).catch(err => {
+            that.$message.error({
+              title: '错误',
+              description: err.message
+            })
+          })
+        },
+        onCancel () {
+        }
+      })
+    }
+  }
+}
+</script>

+ 11 - 11
src/router/index.js

@@ -1,12 +1,12 @@
-import Vue from 'vue'
-import Router from 'vue-router'
-import { constantRouterMap } from '@/config/router.config'
-
-Vue.use(Router)
-
-export default new Router({
-  mode: 'history',
-  base: process.env.BASE_URL,
-  scrollBehavior: () => ({ y: 0 }),
-  routes: constantRouterMap
+import Vue from 'vue'
+import Router from 'vue-router'
+import { constantRouterMap } from '@/config/router.config'
+
+Vue.use(Router)
+
+export default new Router({
+  mode: 'history',
+  base: process.env.BASE_URL,
+  scrollBehavior: () => ({ y: 0 }),
+  routes: constantRouterMap
 })

+ 288 - 288
src/views/account/center/Index.vue

@@ -1,288 +1,288 @@
-<template>
-  <div class="page-header-index-wide page-header-wrapper-grid-content-main">
-    <a-row :gutter="24">
-      <a-col :md="24" :lg="7">
-        <a-card :bordered="false">
-          <div class="account-center-avatarHolder">
-            <div class="avatar">
-              <img :src="avatar()">
-            </div>
-            <div class="username">{{ nickname() }}</div>
-            <div class="bio">海纳百川,有容乃大</div>
-          </div>
-          <div class="account-center-detail">
-            <p>
-              <i class="title"></i>交互专家
-            </p>
-            <p>
-              <i class="group"></i>蚂蚁金服-某某某事业群-某某平台部-某某技术部-UED
-            </p>
-            <p>
-              <i class="address"></i>
-              <span>浙江省</span>
-              <span>杭州市</span>
-            </p>
-          </div>
-          <a-divider/>
-
-          <div class="account-center-tags">
-            <div class="tagsTitle">标签</div>
-            <div>
-              <template v-for="(tag, index) in tags">
-                <a-tooltip v-if="tag.length > 20" :key="tag" :title="tag">
-                  <a-tag
-                    :key="tag"
-                    :closable="index !== 0"
-                    :afterClose="() => handleTagClose(tag)"
-                  >{{ `${tag.slice(0, 20)}...` }}</a-tag>
-                </a-tooltip>
-                <a-tag
-                  v-else
-                  :key="tag"
-                  :closable="index !== 0"
-                  :afterClose="() => handleTagClose(tag)"
-                >{{ tag }}</a-tag>
-              </template>
-              <a-input
-                v-if="tagInputVisible"
-                ref="tagInput"
-                type="text"
-                size="small"
-                :style="{ width: '78px' }"
-                :value="tagInputValue"
-                @change="handleInputChange"
-                @blur="handleTagInputConfirm"
-                @keyup.enter="handleTagInputConfirm"
-              />
-              <a-tag v-else @click="showTagInput" style="background: #fff; borderStyle: dashed;">
-                <a-icon type="plus"/>New Tag
-              </a-tag>
-            </div>
-          </div>
-          <a-divider :dashed="true"/>
-
-          <div class="account-center-team">
-            <div class="teamTitle">团队</div>
-            <a-spin :spinning="teamSpinning">
-              <div class="members">
-                <a-row>
-                  <a-col :span="12" v-for="(item, index) in teams" :key="index">
-                    <a>
-                      <a-avatar size="small" :src="item.avatar"/>
-                      <span class="member">{{ item.name }}</span>
-                    </a>
-                  </a-col>
-                </a-row>
-              </div>
-            </a-spin>
-          </div>
-        </a-card>
-      </a-col>
-      <a-col :md="24" :lg="17">
-        <a-card
-          style="width:100%"
-          :bordered="false"
-          :tabList="tabListNoTitle"
-          :activeTabKey="noTitleKey"
-          @tabChange="key => handleTabChange(key, 'noTitleKey')"
-        >
-          <article-page v-if="noTitleKey === 'article'"></article-page>
-          <app-page v-else-if="noTitleKey === 'app'"></app-page>
-          <project-page v-else-if="noTitleKey === 'project'"></project-page>
-        </a-card>
-      </a-col>
-    </a-row>
-  </div>
-</template>
-
-<script>
-import { PageView, RouteView } from '@/layouts'
-import { AppPage, ArticlePage, ProjectPage } from './page'
-
-import { mapGetters } from 'vuex'
-
-export default {
-  components: {
-    RouteView,
-    PageView,
-    AppPage,
-    ArticlePage,
-    ProjectPage
-  },
-  data () {
-    return {
-      tags: ['很有想法的', '专注设计', '辣~', '大长腿', '川妹子', '海纳百川'],
-
-      tagInputVisible: false,
-      tagInputValue: '',
-
-      teams: [],
-      teamSpinning: true,
-
-      tabListNoTitle: [
-        {
-          key: 'article',
-          tab: '文章(8)'
-        },
-        {
-          key: 'app',
-          tab: '应用(8)'
-        },
-        {
-          key: 'project',
-          tab: '项目(8)'
-        }
-      ],
-      noTitleKey: 'app'
-    }
-  },
-  mounted () {
-    this.getTeams()
-  },
-  methods: {
-    ...mapGetters(['nickname', 'avatar']),
-
-    getTeams () {
-      this.$http.get('/workplace/teams').then(res => {
-        this.teams = res.result
-        this.teamSpinning = false
-      })
-    },
-
-    handleTabChange (key, type) {
-      this[type] = key
-    },
-
-    handleTagClose (removeTag) {
-      const tags = this.tags.filter(tag => tag !== removeTag)
-      this.tags = tags
-    },
-
-    showTagInput () {
-      this.tagInputVisible = true
-      this.$nextTick(() => {
-        this.$refs.tagInput.focus()
-      })
-    },
-
-    handleInputChange (e) {
-      this.tagInputValue = e.target.value
-    },
-
-    handleTagInputConfirm () {
-      const inputValue = this.tagInputValue
-      let tags = this.tags
-      if (inputValue && !tags.includes(inputValue)) {
-        tags = [...tags, inputValue]
-      }
-
-      Object.assign(this, {
-        tags,
-        tagInputVisible: false,
-        tagInputValue: ''
-      })
-    }
-  }
-}
-</script>
-
-<style lang="less" scoped>
-.page-header-wrapper-grid-content-main {
-  width: 100%;
-  height: 100%;
-  min-height: 100%;
-  transition: 0.3s;
-
-  .account-center-avatarHolder {
-    text-align: center;
-    margin-bottom: 24px;
-
-    & > .avatar {
-      margin: 0 auto;
-      width: 104px;
-      height: 104px;
-      margin-bottom: 20px;
-      border-radius: 50%;
-      overflow: hidden;
-      img {
-        height: 100%;
-        width: 100%;
-      }
-    }
-
-    .username {
-      color: rgba(0, 0, 0, 0.85);
-      font-size: 20px;
-      line-height: 28px;
-      font-weight: 500;
-      margin-bottom: 4px;
-    }
-  }
-
-  .account-center-detail {
-    p {
-      margin-bottom: 8px;
-      padding-left: 26px;
-      position: relative;
-    }
-
-    i {
-      position: absolute;
-      height: 14px;
-      width: 14px;
-      left: 0;
-      top: 4px;
-      background: url(https://gw.alipayobjects.com/zos/rmsportal/pBjWzVAHnOOtAUvZmZfy.svg);
-    }
-
-    .title {
-      background-position: 0 0;
-    }
-    .group {
-      background-position: 0 -22px;
-    }
-    .address {
-      background-position: 0 -44px;
-    }
-  }
-
-  .account-center-tags {
-    .ant-tag {
-      margin-bottom: 8px;
-    }
-  }
-
-  .account-center-team {
-    .members {
-      a {
-        display: block;
-        margin: 12px 0;
-        line-height: 24px;
-        height: 24px;
-        .member {
-          font-size: 14px;
-          color: rgba(0, 0, 0, 0.65);
-          line-height: 24px;
-          max-width: 100px;
-          vertical-align: top;
-          margin-left: 12px;
-          transition: all 0.3s;
-          display: inline-block;
-        }
-        &:hover {
-          span {
-            color: #1890ff;
-          }
-        }
-      }
-    }
-  }
-
-  .tagsTitle,
-  .teamTitle {
-    font-weight: 500;
-    color: rgba(0, 0, 0, 0.85);
-    margin-bottom: 12px;
-  }
-}
-</style>
+<template>
+  <div class="page-header-index-wide page-header-wrapper-grid-content-main">
+    <a-row :gutter="24">
+      <a-col :md="24" :lg="7">
+        <a-card :bordered="false">
+          <div class="account-center-avatarHolder">
+            <div class="avatar">
+              <img :src="avatar()">
+            </div>
+            <div class="username">{{ nickname() }}</div>
+            <div class="bio">海纳百川,有容乃大</div>
+          </div>
+          <div class="account-center-detail">
+            <p>
+              <i class="title"></i>交互专家
+            </p>
+            <p>
+              <i class="group"></i>蚂蚁金服-某某某事业群-某某平台部-某某技术部-UED
+            </p>
+            <p>
+              <i class="address"></i>
+              <span>浙江省</span>
+              <span>杭州市</span>
+            </p>
+          </div>
+          <a-divider/>
+
+          <div class="account-center-tags">
+            <div class="tagsTitle">标签</div>
+            <div>
+              <template v-for="(tag, index) in tags">
+                <a-tooltip v-if="tag.length > 20" :key="tag" :title="tag">
+                  <a-tag
+                    :key="tag"
+                    :closable="index !== 0"
+                    :afterClose="() => handleTagClose(tag)"
+                  >{{ `${tag.slice(0, 20)}...` }}</a-tag>
+                </a-tooltip>
+                <a-tag
+                  v-else
+                  :key="tag"
+                  :closable="index !== 0"
+                  :afterClose="() => handleTagClose(tag)"
+                >{{ tag }}</a-tag>
+              </template>
+              <a-input
+                v-if="tagInputVisible"
+                ref="tagInput"
+                type="text"
+                size="small"
+                :style="{ width: '78px' }"
+                :value="tagInputValue"
+                @change="handleInputChange"
+                @blur="handleTagInputConfirm"
+                @keyup.enter="handleTagInputConfirm"
+              />
+              <a-tag v-else @click="showTagInput" style="background: #fff; borderStyle: dashed;">
+                <a-icon type="plus"/>New Tag
+              </a-tag>
+            </div>
+          </div>
+          <a-divider :dashed="true"/>
+
+          <div class="account-center-team">
+            <div class="teamTitle">团队</div>
+            <a-spin :spinning="teamSpinning">
+              <div class="members">
+                <a-row>
+                  <a-col :span="12" v-for="(item, index) in teams" :key="index">
+                    <a>
+                      <a-avatar size="small" :src="item.avatar"/>
+                      <span class="member">{{ item.name }}</span>
+                    </a>
+                  </a-col>
+                </a-row>
+              </div>
+            </a-spin>
+          </div>
+        </a-card>
+      </a-col>
+      <a-col :md="24" :lg="17">
+        <a-card
+          style="width:100%"
+          :bordered="false"
+          :tabList="tabListNoTitle"
+          :activeTabKey="noTitleKey"
+          @tabChange="key => handleTabChange(key, 'noTitleKey')"
+        >
+          <article-page v-if="noTitleKey === 'article'"></article-page>
+          <app-page v-else-if="noTitleKey === 'app'"></app-page>
+          <project-page v-else-if="noTitleKey === 'project'"></project-page>
+        </a-card>
+      </a-col>
+    </a-row>
+  </div>
+</template>
+
+<script>
+import { PageView, RouteView } from '@/layouts'
+import { AppPage, ArticlePage, ProjectPage } from './page'
+
+import { mapGetters } from 'vuex'
+
+export default {
+  components: {
+    RouteView,
+    PageView,
+    AppPage,
+    ArticlePage,
+    ProjectPage
+  },
+  data () {
+    return {
+      tags: ['很有想法的', '专注设计', '辣~', '大长腿', '川妹子', '海纳百川'],
+
+      tagInputVisible: false,
+      tagInputValue: '',
+
+      teams: [],
+      teamSpinning: true,
+
+      tabListNoTitle: [
+        {
+          key: 'article',
+          tab: '文章(8)'
+        },
+        {
+          key: 'app',
+          tab: '应用(8)'
+        },
+        {
+          key: 'project',
+          tab: '项目(8)'
+        }
+      ],
+      noTitleKey: 'app'
+    }
+  },
+  mounted () {
+    this.getTeams()
+  },
+  methods: {
+    ...mapGetters(['nickname', 'avatar']),
+
+    getTeams () {
+      this.$http.get('/workplace/teams').then(res => {
+        this.teams = res.result
+        this.teamSpinning = false
+      })
+    },
+
+    handleTabChange (key, type) {
+      this[type] = key
+    },
+
+    handleTagClose (removeTag) {
+      const tags = this.tags.filter(tag => tag !== removeTag)
+      this.tags = tags
+    },
+
+    showTagInput () {
+      this.tagInputVisible = true
+      this.$nextTick(() => {
+        this.$refs.tagInput.focus()
+      })
+    },
+
+    handleInputChange (e) {
+      this.tagInputValue = e.target.value
+    },
+
+    handleTagInputConfirm () {
+      const inputValue = this.tagInputValue
+      let tags = this.tags
+      if (inputValue && !tags.includes(inputValue)) {
+        tags = [...tags, inputValue]
+      }
+
+      Object.assign(this, {
+        tags,
+        tagInputVisible: false,
+        tagInputValue: ''
+      })
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+.page-header-wrapper-grid-content-main {
+  width: 100%;
+  height: 100%;
+  min-height: 100%;
+  transition: 0.3s;
+
+  .account-center-avatarHolder {
+    text-align: center;
+    margin-bottom: 24px;
+
+    & > .avatar {
+      margin: 0 auto;
+      width: 104px;
+      height: 104px;
+      margin-bottom: 20px;
+      border-radius: 50%;
+      overflow: hidden;
+      img {
+        height: 100%;
+        width: 100%;
+      }
+    }
+
+    .username {
+      color: rgba(0, 0, 0, 0.85);
+      font-size: 20px;
+      line-height: 28px;
+      font-weight: 500;
+      margin-bottom: 4px;
+    }
+  }
+
+  .account-center-detail {
+    p {
+      margin-bottom: 8px;
+      padding-left: 26px;
+      position: relative;
+    }
+
+    i {
+      position: absolute;
+      height: 14px;
+      width: 14px;
+      left: 0;
+      top: 4px;
+      background: url(https://gw.alipayobjects.com/zos/rmsportal/pBjWzVAHnOOtAUvZmZfy.svg);
+    }
+
+    .title {
+      background-position: 0 0;
+    }
+    .group {
+      background-position: 0 -22px;
+    }
+    .address {
+      background-position: 0 -44px;
+    }
+  }
+
+  .account-center-tags {
+    .ant-tag {
+      margin-bottom: 8px;
+    }
+  }
+
+  .account-center-team {
+    .members {
+      a {
+        display: block;
+        margin: 12px 0;
+        line-height: 24px;
+        height: 24px;
+        .member {
+          font-size: 14px;
+          color: rgba(0, 0, 0, 0.65);
+          line-height: 24px;
+          max-width: 100px;
+          vertical-align: top;
+          margin-left: 12px;
+          transition: all 0.3s;
+          display: inline-block;
+        }
+        &:hover {
+          span {
+            color: #1890ff;
+          }
+        }
+      }
+    }
+  }
+
+  .tagsTitle,
+  .teamTitle {
+    font-weight: 500;
+    color: rgba(0, 0, 0, 0.85);
+    margin-bottom: 12px;
+  }
+}
+</style>

+ 109 - 109
src/views/account/settings/AvatarModal.vue

@@ -1,109 +1,109 @@
-<template>
-  <a-modal
-    title="修改头像"
-    :visible="visible"
-    :maskClosable="false"
-    :confirmLoading="confirmLoading"
-    :width="800"
-    @cancel="cancelHandel">
-    <a-row>
-      <a-col :xs="24" :md="12" :style="{height: '350px'}">
-        <vue-cropper
-          ref="cropper"
-          :img="options.img"
-          :info="true"
-          :autoCrop="options.autoCrop"
-          :autoCropWidth="options.autoCropWidth"
-          :autoCropHeight="options.autoCropHeight"
-          :fixedBox="options.fixedBox"
-          @realTime="realTime"
-        >
-        </vue-cropper>
-      </a-col>
-      <a-col :xs="24" :md="12" :style="{height: '350px'}">
-        <div class="avatar-upload-preview">
-          <img :src="previews.url" :style="previews.img"/>
-        </div>
-      </a-col>
-    </a-row>
-
-    <template slot="footer">
-      <a-button key="back" @click="cancelHandel">取消</a-button>
-      <a-button key="submit" type="primary" :loading="confirmLoading" @click="okHandel">保存</a-button>
-    </template>
-  </a-modal>
-</template>
-<script>
-// import { VueCropper } from 'vue-cropper'
-
-export default {
-  /*
-  components: {
-    VueCropper
-  },
-  */
-  data () {
-    return {
-      visible: false,
-      id: null,
-      confirmLoading: false,
-
-      options: {
-        img: '/avatar2.jpg',
-        autoCrop: true,
-        autoCropWidth: 200,
-        autoCropHeight: 200,
-        fixedBox: true
-      },
-      previews: {}
-    }
-  },
-  methods: {
-    edit (id) {
-      this.visible = true
-      this.id = id
-      /* 获取原始头像 */
-    },
-    close () {
-      this.id = null
-      this.visible = false
-    },
-    cancelHandel () {
-      this.close()
-    },
-    okHandel () {
-      const vm = this
-
-      vm.confirmLoading = true
-      setTimeout(() => {
-        vm.confirmLoading = false
-        vm.close()
-        vm.$message.success('上传头像成功')
-      }, 2000)
-    },
-
-    realTime (data) {
-      this.previews = data
-    }
-  }
-}
-</script>
-
-<style lang="less" scoped>
-
-  .avatar-upload-preview {
-    position: absolute;
-    top: 50%;
-    transform: translate(50%, -50%);
-    width: 180px;
-    height: 180px;
-    border-radius: 50%;
-    box-shadow: 0 0 4px #ccc;
-    overflow: hidden;
-
-    img {
-      width: 100%;
-      height: 100%;
-    }
-  }
-</style>
+<template>
+  <a-modal
+    title="修改头像"
+    :visible="visible"
+    :maskClosable="false"
+    :confirmLoading="confirmLoading"
+    :width="800"
+    @cancel="cancelHandel">
+    <a-row>
+      <a-col :xs="24" :md="12" :style="{height: '350px'}">
+        <vue-cropper
+          ref="cropper"
+          :img="options.img"
+          :info="true"
+          :autoCrop="options.autoCrop"
+          :autoCropWidth="options.autoCropWidth"
+          :autoCropHeight="options.autoCropHeight"
+          :fixedBox="options.fixedBox"
+          @realTime="realTime"
+        >
+        </vue-cropper>
+      </a-col>
+      <a-col :xs="24" :md="12" :style="{height: '350px'}">
+        <div class="avatar-upload-preview">
+          <img :src="previews.url" :style="previews.img"/>
+        </div>
+      </a-col>
+    </a-row>
+
+    <template slot="footer">
+      <a-button key="back" @click="cancelHandel">取消</a-button>
+      <a-button key="submit" type="primary" :loading="confirmLoading" @click="okHandel">保存</a-button>
+    </template>
+  </a-modal>
+</template>
+<script>
+// import { VueCropper } from 'vue-cropper'
+
+export default {
+  /*
+  components: {
+    VueCropper
+  },
+  */
+  data () {
+    return {
+      visible: false,
+      id: null,
+      confirmLoading: false,
+
+      options: {
+        img: '/avatar2.jpg',
+        autoCrop: true,
+        autoCropWidth: 200,
+        autoCropHeight: 200,
+        fixedBox: true
+      },
+      previews: {}
+    }
+  },
+  methods: {
+    edit (id) {
+      this.visible = true
+      this.id = id
+      /* 获取原始头像 */
+    },
+    close () {
+      this.id = null
+      this.visible = false
+    },
+    cancelHandel () {
+      this.close()
+    },
+    okHandel () {
+      const vm = this
+
+      vm.confirmLoading = true
+      setTimeout(() => {
+        vm.confirmLoading = false
+        vm.close()
+        vm.$message.success('上传头像成功')
+      }, 2000)
+    },
+
+    realTime (data) {
+      this.previews = data
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+
+  .avatar-upload-preview {
+    position: absolute;
+    top: 50%;
+    transform: translate(50%, -50%);
+    width: 180px;
+    height: 180px;
+    border-radius: 50%;
+    box-shadow: 0 0 4px #ccc;
+    overflow: hidden;
+
+    img {
+      width: 100%;
+      height: 100%;
+    }
+  }
+</style>

+ 161 - 161
src/views/account/settings/BaseSetting.vue

@@ -1,161 +1,161 @@
-<template>
-  <div class="account-settings-info-view">
-    <a-row :gutter="16">
-      <a-col :md="24" :lg="16">
-
-        <a-form layout="vertical">
-          <a-form-item
-            label="昵称"
-          >
-            <a-input placeholder="给自己起个名字" />
-          </a-form-item>
-          <a-form-item
-            label="Bio"
-          >
-            <a-textarea rows="4" placeholder="You are not alone."/>
-          </a-form-item>
-
-          <a-form-item
-            label="电子邮件"
-            :required="false"
-          >
-            <a-input placeholder="exp@admin.com"/>
-          </a-form-item>
-          <a-form-item
-            label="加密方式"
-            :required="false"
-          >
-            <a-select defaultValue="aes-256-cfb">
-              <a-select-option value="aes-256-cfb">aes-256-cfb</a-select-option>
-              <a-select-option value="aes-128-cfb">aes-128-cfb</a-select-option>
-              <a-select-option value="chacha20">chacha20</a-select-option>
-            </a-select>
-          </a-form-item>
-          <a-form-item
-            label="连接密码"
-            :required="false"
-          >
-            <a-input placeholder="h3gSbecd"/>
-          </a-form-item>
-          <a-form-item
-            label="登录密码"
-            :required="false"
-          >
-            <a-input placeholder="密码"/>
-          </a-form-item>
-
-          <a-form-item>
-            <a-button type="primary">提交</a-button>
-            <a-button style="margin-left: 8px">保存</a-button>
-          </a-form-item>
-        </a-form>
-
-      </a-col>
-      <a-col :md="24" :lg="8" :style="{ minHeight: '180px' }">
-        <div class="ant-upload-preview" @click="$refs.modal.edit(1)" >
-          <a-icon type="cloud-upload-o" class="upload-icon"/>
-          <div class="mask">
-            <a-icon type="plus" />
-          </div>
-          <img :src="option.img"/>
-        </div>
-      </a-col>
-
-    </a-row>
-
-    <avatar-modal ref="modal">
-
-    </avatar-modal>
-  </div>
-</template>
-
-<script>
-import AvatarModal from './AvatarModal'
-
-export default {
-  components: {
-    AvatarModal
-  },
-  data () {
-    return {
-      // cropper
-      preview: {},
-      option: {
-        img: '/avatar2.jpg',
-        info: true,
-        size: 1,
-        outputType: 'jpeg',
-        canScale: false,
-        autoCrop: true,
-        // 只有自动截图开启 宽度高度才生效
-        autoCropWidth: 180,
-        autoCropHeight: 180,
-        fixedBox: true,
-        // 开启宽度和高度比例
-        fixed: true,
-        fixedNumber: [1, 1]
-      }
-    }
-  },
-  methods: {
-
-  }
-}
-</script>
-
-<style lang="less" scoped>
-
-  .avatar-upload-wrapper {
-    height: 200px;
-    width: 100%;
-  }
-
-  .ant-upload-preview {
-    position: relative;
-    margin: 0 auto;
-    width: 100%;
-    max-width: 180px;
-    border-radius: 50%;
-    box-shadow: 0 0 4px #ccc;
-
-    .upload-icon {
-      position: absolute;
-      top: 0;
-      right: 10px;
-      font-size: 1.4rem;
-      padding: 0.5rem;
-      background: rgba(222, 221, 221, 0.7);
-      border-radius: 50%;
-      border: 1px solid rgba(0, 0, 0, 0.2);
-    }
-    .mask {
-      opacity: 0;
-      position: absolute;
-      background: rgba(0,0,0,0.4);
-      cursor: pointer;
-      transition: opacity 0.4s;
-
-      &:hover {
-        opacity: 1;
-      }
-
-      i {
-        font-size: 2rem;
-        position: absolute;
-        top: 50%;
-        left: 50%;
-        margin-left: -1rem;
-        margin-top: -1rem;
-        color: #d6d6d6;
-      }
-    }
-
-    img, .mask {
-      width: 100%;
-      max-width: 180px;
-      height: 100%;
-      border-radius: 50%;
-      overflow: hidden;
-    }
-  }
-</style>
+<template>
+  <div class="account-settings-info-view">
+    <a-row :gutter="16">
+      <a-col :md="24" :lg="16">
+
+        <a-form layout="vertical">
+          <a-form-item
+            label="昵称"
+          >
+            <a-input placeholder="给自己起个名字" />
+          </a-form-item>
+          <a-form-item
+            label="Bio"
+          >
+            <a-textarea rows="4" placeholder="You are not alone."/>
+          </a-form-item>
+
+          <a-form-item
+            label="电子邮件"
+            :required="false"
+          >
+            <a-input placeholder="exp@admin.com"/>
+          </a-form-item>
+          <a-form-item
+            label="加密方式"
+            :required="false"
+          >
+            <a-select defaultValue="aes-256-cfb">
+              <a-select-option value="aes-256-cfb">aes-256-cfb</a-select-option>
+              <a-select-option value="aes-128-cfb">aes-128-cfb</a-select-option>
+              <a-select-option value="chacha20">chacha20</a-select-option>
+            </a-select>
+          </a-form-item>
+          <a-form-item
+            label="连接密码"
+            :required="false"
+          >
+            <a-input placeholder="h3gSbecd"/>
+          </a-form-item>
+          <a-form-item
+            label="登录密码"
+            :required="false"
+          >
+            <a-input placeholder="密码"/>
+          </a-form-item>
+
+          <a-form-item>
+            <a-button type="primary">提交</a-button>
+            <a-button style="margin-left: 8px">保存</a-button>
+          </a-form-item>
+        </a-form>
+
+      </a-col>
+      <a-col :md="24" :lg="8" :style="{ minHeight: '180px' }">
+        <div class="ant-upload-preview" @click="$refs.modal.edit(1)" >
+          <a-icon type="cloud-upload-o" class="upload-icon"/>
+          <div class="mask">
+            <a-icon type="plus" />
+          </div>
+          <img :src="option.img"/>
+        </div>
+      </a-col>
+
+    </a-row>
+
+    <avatar-modal ref="modal">
+
+    </avatar-modal>
+  </div>
+</template>
+
+<script>
+import AvatarModal from './AvatarModal'
+
+export default {
+  components: {
+    AvatarModal
+  },
+  data () {
+    return {
+      // cropper
+      preview: {},
+      option: {
+        img: '/avatar2.jpg',
+        info: true,
+        size: 1,
+        outputType: 'jpeg',
+        canScale: false,
+        autoCrop: true,
+        // 只有自动截图开启 宽度高度才生效
+        autoCropWidth: 180,
+        autoCropHeight: 180,
+        fixedBox: true,
+        // 开启宽度和高度比例
+        fixed: true,
+        fixedNumber: [1, 1]
+      }
+    }
+  },
+  methods: {
+
+  }
+}
+</script>
+
+<style lang="less" scoped>
+
+  .avatar-upload-wrapper {
+    height: 200px;
+    width: 100%;
+  }
+
+  .ant-upload-preview {
+    position: relative;
+    margin: 0 auto;
+    width: 100%;
+    max-width: 180px;
+    border-radius: 50%;
+    box-shadow: 0 0 4px #ccc;
+
+    .upload-icon {
+      position: absolute;
+      top: 0;
+      right: 10px;
+      font-size: 1.4rem;
+      padding: 0.5rem;
+      background: rgba(222, 221, 221, 0.7);
+      border-radius: 50%;
+      border: 1px solid rgba(0, 0, 0, 0.2);
+    }
+    .mask {
+      opacity: 0;
+      position: absolute;
+      background: rgba(0,0,0,0.4);
+      cursor: pointer;
+      transition: opacity 0.4s;
+
+      &:hover {
+        opacity: 1;
+      }
+
+      i {
+        font-size: 2rem;
+        position: absolute;
+        top: 50%;
+        left: 50%;
+        margin-left: -1rem;
+        margin-top: -1rem;
+        color: #d6d6d6;
+      }
+    }
+
+    img, .mask {
+      width: 100%;
+      max-width: 180px;
+      height: 100%;
+      border-radius: 50%;
+      overflow: hidden;
+    }
+  }
+</style>

+ 150 - 150
src/views/account/settings/Index.vue

@@ -1,150 +1,150 @@
-<template>
-  <div class="page-header-index-wide">
-    <a-card :bordered="false" :bodyStyle="{ padding: '16px 0', height: '100%' }" :style="{ height: '100%' }">
-      <div class="account-settings-info-main" :class="device">
-        <div class="account-settings-info-left">
-          <a-menu
-            :mode="device == 'mobile' ? 'horizontal' : 'inline'"
-            :style="{ border: '0', width: device == 'mobile' ? '560px' : 'auto'}"
-            :defaultSelectedKeys="defaultSelectedKeys"
-            type="inner"
-            @openChange="onOpenChange"
-          >
-            <a-menu-item key="/account/settings/base">
-              <router-link :to="{ name: 'BaseSettings' }">
-                基本设置
-              </router-link>
-            </a-menu-item>
-            <a-menu-item key="/account/settings/security">
-              <router-link :to="{ name: 'SecuritySettings' }">
-                安全设置
-              </router-link>
-            </a-menu-item>
-            <a-menu-item key="/account/settings/custom">
-              <router-link :to="{ name: 'CustomSettings' }">
-                个性化
-              </router-link>
-            </a-menu-item>
-            <a-menu-item key="/account/settings/binding">
-              <router-link :to="{ name: 'BindingSettings' }">
-                账户绑定
-              </router-link>
-            </a-menu-item>
-            <a-menu-item key="/account/settings/notification">
-              <router-link :to="{ name: 'NotificationSettings' }">
-                新消息通知
-              </router-link>
-            </a-menu-item>
-          </a-menu>
-        </div>
-        <div class="account-settings-info-right">
-          <div class="account-settings-info-title">
-            <span>{{ $route.meta.title }}</span>
-          </div>
-          <route-view></route-view>
-        </div>
-      </div>
-    </a-card>
-  </div>
-</template>
-
-<script>
-import { PageView, RouteView } from '@/layouts'
-import { mixinDevice } from '@/utils/mixin.js'
-
-export default {
-  components: {
-    RouteView,
-    PageView
-  },
-  mixins: [mixinDevice],
-  data () {
-    return {
-      // horizontal  inline
-      mode: 'inline',
-
-      openKeys: [],
-      defaultSelectedKeys: [],
-
-      // cropper
-      preview: {},
-      option: {
-        img: '/avatar2.jpg',
-        info: true,
-        size: 1,
-        outputType: 'jpeg',
-        canScale: false,
-        autoCrop: true,
-        // 只有自动截图开启 宽度高度才生效
-        autoCropWidth: 180,
-        autoCropHeight: 180,
-        fixedBox: true,
-        // 开启宽度和高度比例
-        fixed: true,
-        fixedNumber: [1, 1]
-      },
-
-      pageTitle: ''
-    }
-  },
-  created () {
-    this.updateMenu()
-  },
-  methods: {
-    onOpenChange (openKeys) {
-      this.openKeys = openKeys
-    },
-    updateMenu () {
-      const routes = this.$route.matched.concat()
-      this.defaultSelectedKeys = [ routes.pop().path ]
-    }
-  }
-}
-</script>
-
-<style lang="less" scoped>
-  .account-settings-info-main {
-    width: 100%;
-    display: flex;
-    height: 100%;
-    overflow: auto;
-
-    &.mobile {
-      display: block;
-
-      .account-settings-info-left {
-        border-right: unset;
-        border-bottom: 1px solid #e8e8e8;
-        width: 100%;
-        height: 50px;
-        overflow-x: auto;
-        overflow-y: scroll;
-      }
-      .account-settings-info-right {
-        padding: 20px 40px;
-      }
-    }
-
-    .account-settings-info-left {
-      border-right: 1px solid #e8e8e8;
-      width: 224px;
-    }
-
-    .account-settings-info-right {
-      flex: 1 1;
-      padding: 8px 40px;
-
-      .account-settings-info-title {
-        color: rgba(0,0,0,.85);
-        font-size: 20px;
-        font-weight: 500;
-        line-height: 28px;
-        margin-bottom: 12px;
-      }
-      .account-settings-info-view {
-        padding-top: 12px;
-      }
-    }
-  }
-
-</style>
+<template>
+  <div class="page-header-index-wide">
+    <a-card :bordered="false" :bodyStyle="{ padding: '16px 0', height: '100%' }" :style="{ height: '100%' }">
+      <div class="account-settings-info-main" :class="device">
+        <div class="account-settings-info-left">
+          <a-menu
+            :mode="device == 'mobile' ? 'horizontal' : 'inline'"
+            :style="{ border: '0', width: device == 'mobile' ? '560px' : 'auto'}"
+            :defaultSelectedKeys="defaultSelectedKeys"
+            type="inner"
+            @openChange="onOpenChange"
+          >
+            <a-menu-item key="/account/settings/base">
+              <router-link :to="{ name: 'BaseSettings' }">
+                基本设置
+              </router-link>
+            </a-menu-item>
+            <a-menu-item key="/account/settings/security">
+              <router-link :to="{ name: 'SecuritySettings' }">
+                安全设置
+              </router-link>
+            </a-menu-item>
+            <a-menu-item key="/account/settings/custom">
+              <router-link :to="{ name: 'CustomSettings' }">
+                个性化
+              </router-link>
+            </a-menu-item>
+            <a-menu-item key="/account/settings/binding">
+              <router-link :to="{ name: 'BindingSettings' }">
+                账户绑定
+              </router-link>
+            </a-menu-item>
+            <a-menu-item key="/account/settings/notification">
+              <router-link :to="{ name: 'NotificationSettings' }">
+                新消息通知
+              </router-link>
+            </a-menu-item>
+          </a-menu>
+        </div>
+        <div class="account-settings-info-right">
+          <div class="account-settings-info-title">
+            <span>{{ $route.meta.title }}</span>
+          </div>
+          <route-view></route-view>
+        </div>
+      </div>
+    </a-card>
+  </div>
+</template>
+
+<script>
+import { PageView, RouteView } from '@/layouts'
+import { mixinDevice } from '@/utils/mixin.js'
+
+export default {
+  components: {
+    RouteView,
+    PageView
+  },
+  mixins: [mixinDevice],
+  data () {
+    return {
+      // horizontal  inline
+      mode: 'inline',
+
+      openKeys: [],
+      defaultSelectedKeys: [],
+
+      // cropper
+      preview: {},
+      option: {
+        img: '/avatar2.jpg',
+        info: true,
+        size: 1,
+        outputType: 'jpeg',
+        canScale: false,
+        autoCrop: true,
+        // 只有自动截图开启 宽度高度才生效
+        autoCropWidth: 180,
+        autoCropHeight: 180,
+        fixedBox: true,
+        // 开启宽度和高度比例
+        fixed: true,
+        fixedNumber: [1, 1]
+      },
+
+      pageTitle: ''
+    }
+  },
+  created () {
+    this.updateMenu()
+  },
+  methods: {
+    onOpenChange (openKeys) {
+      this.openKeys = openKeys
+    },
+    updateMenu () {
+      const routes = this.$route.matched.concat()
+      this.defaultSelectedKeys = [ routes.pop().path ]
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+  .account-settings-info-main {
+    width: 100%;
+    display: flex;
+    height: 100%;
+    overflow: auto;
+
+    &.mobile {
+      display: block;
+
+      .account-settings-info-left {
+        border-right: unset;
+        border-bottom: 1px solid #e8e8e8;
+        width: 100%;
+        height: 50px;
+        overflow-x: auto;
+        overflow-y: scroll;
+      }
+      .account-settings-info-right {
+        padding: 20px 40px;
+      }
+    }
+
+    .account-settings-info-left {
+      border-right: 1px solid #e8e8e8;
+      width: 224px;
+    }
+
+    .account-settings-info-right {
+      flex: 1 1;
+      padding: 8px 40px;
+
+      .account-settings-info-title {
+        color: rgba(0,0,0,.85);
+        font-size: 20px;
+        font-weight: 500;
+        line-height: 28px;
+        margin-bottom: 12px;
+      }
+      .account-settings-info-view {
+        padding-top: 12px;
+      }
+    }
+  }
+
+</style>

+ 360 - 360
src/views/dashboard/Workplace.vue

@@ -1,360 +1,360 @@
-
-<template>
-  <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>
-    </div>
-    <div slot="extra">
-      <a-row class="more-info">
-        <a-col :span="8">
-          <head-info title="项目数" content="56" :center="false" :bordered="false"/>
-        </a-col>
-        <a-col :span="8">
-          <head-info title="团队排名" content="8/24" :center="false" :bordered="false"/>
-        </a-col>
-        <a-col :span="8">
-          <head-info title="项目访问" content="2,223" :center="false" />
-        </a-col>
-      </a-row>
-    </div>
-
-    <div>
-      <a-row :gutter="24">
-        <a-col :xl="16" :lg="24" :md="24" :sm="24" :xs="24">
-          <a-card
-            class="project-list"
-            :loading="loading"
-            style="margin-bottom: 24px;"
-            :bordered="false"
-            title="进行中的项目"
-            :body-style="{ padding: 0 }">
-            <a slot="extra">全部项目</a>
-            <div>
-              <a-card-grid class="project-card-grid" :key="i" v-for="(item, i) in projects">
-                <a-card :bordered="false" :body-style="{ padding: 0 }">
-                  <a-card-meta>
-                    <div slot="title" class="card-title">
-                      <a-avatar size="small" :src="item.cover"/>
-                      <a>{{ item.title }}</a>
-                    </div>
-                    <div slot="description" class="card-description">
-                      {{ item.description }}
-                    </div>
-                  </a-card-meta>
-                  <div class="project-item">
-                    <a href="/#/">科学搬砖组</a>
-                    <span class="datetime">9小时前</span>
-                  </div>
-                </a-card>
-              </a-card-grid>
-            </div>
-          </a-card>
-
-          <a-card :loading="loading" title="动态" :bordered="false">
-            <a-list>
-              <a-list-item :key="index" v-for="(item, index) in activities">
-                <a-list-item-meta>
-                  <a-avatar slot="avatar" :src="item.user.avatar" />
-                  <div slot="title">
-                    <span>{{ item.user.nickname }}</span>&nbsp;
-                    在&nbsp;<a href="#">{{ item.project.name }}</a>&nbsp;
-                    <span>{{ item.project.action }}</span>&nbsp;
-                    <a href="#">{{ item.project.event }}</a>
-                  </div>
-                  <div slot="description">{{ item.time }}</div>
-                </a-list-item-meta>
-              </a-list-item>
-            </a-list>
-          </a-card>
-        </a-col>
-        <a-col
-          style="padding: 0 12px"
-          :xl="8"
-          :lg="24"
-          :md="24"
-          :sm="24"
-          :xs="24">
-          <a-card title="快速开始 / 便捷导航" style="margin-bottom: 24px" :bordered="false" :body-style="{padding: 0}">
-            <div class="item-group">
-              <a>操作一</a>
-              <a>操作二</a>
-              <a>操作三</a>
-              <a>操作四</a>
-              <a>操作五</a>
-              <a>操作六</a>
-              <a-button size="small" type="primary" ghost icon="plus">添加</a-button>
-            </div>
-          </a-card>
-          <a-card title="XX 指数" style="margin-bottom: 24px" :loading="radarLoading" :bordered="false" :body-style="{ padding: 0 }">
-            <div style="min-height: 400px;">
-              <!-- :scale="scale" :axis1Opts="axis1Opts" :axis2Opts="axis2Opts"  -->
-              <radar :data="radarData" />
-            </div>
-          </a-card>
-          <a-card :loading="loading" title="团队" :bordered="false">
-            <div class="members">
-              <a-row>
-                <a-col :span="12" v-for="(item, index) in teams" :key="index">
-                  <a>
-                    <a-avatar size="small" :src="item.avatar" />
-                    <span class="member">{{ item.name }}</span>
-                  </a>
-                </a-col>
-              </a-row>
-            </div>
-          </a-card>
-        </a-col>
-      </a-row>
-    </div>
-  </page-view>
-</template>
-
-<script>
-import { timeFix } from '@/utils/util'
-import { mapGetters } from 'vuex'
-
-import { PageView } from '@/layouts'
-import HeadInfo from '@/components/tools/HeadInfo'
-import { Radar } from '@/components'
-
-import { getRoleList, getServiceList } from '@/api/manage'
-
-const DataSet = require('@antv/data-set')
-
-export default {
-  name: 'Workplace',
-  components: {
-    PageView,
-    HeadInfo,
-    Radar
-  },
-  data () {
-    return {
-      timeFix: timeFix(),
-      avatar: '',
-      user: {},
-
-      projects: [],
-      loading: true,
-      radarLoading: true,
-      activities: [],
-      teams: [],
-
-      // data
-      axis1Opts: {
-        dataKey: 'item',
-        line: null,
-        tickLine: null,
-        grid: {
-          lineStyle: {
-            lineDash: null
-          },
-          hideFirstLine: false
-        }
-      },
-      axis2Opts: {
-        dataKey: 'score',
-        line: null,
-        tickLine: null,
-        grid: {
-          type: 'polygon',
-          lineStyle: {
-            lineDash: null
-          }
-        }
-      },
-      scale: [{
-        dataKey: 'score',
-        min: 0,
-        max: 80
-      }],
-      axisData: [
-        { item: '引用', a: 70, b: 30, c: 40 },
-        { item: '口碑', a: 60, b: 70, c: 40 },
-        { item: '产量', a: 50, b: 60, c: 40 },
-        { item: '贡献', a: 40, b: 50, c: 40 },
-        { item: '热度', a: 60, b: 70, c: 40 },
-        { item: '引用', a: 70, b: 50, c: 40 }
-      ],
-      radarData: []
-    }
-  },
-  computed: {
-    userInfo () {
-      return this.$store.getters.userInfo
-    }
-  },
-  created () {
-    this.user = this.userInfo
-    this.avatar = this.userInfo.avatar
-
-    getRoleList().then(res => {
-      console.log('workplace -> call getRoleList()', res)
-    })
-
-    getServiceList().then(res => {
-      console.log('workplace -> call getServiceList()', res)
-    })
-  },
-  mounted () {
-    this.getProjects()
-    this.getActivity()
-    this.getTeams()
-    this.initRadar()
-  },
-  methods: {
-    ...mapGetters(['nickname', 'welcome']),
-    getProjects () {
-      this.$http.get('/list/search/projects')
-        .then(res => {
-          this.projects = res.result && res.result.data
-          this.loading = false
-        })
-    },
-    getActivity () {
-      this.$http.get('/workplace/activity')
-        .then(res => {
-          this.activities = res.result
-        })
-    },
-    getTeams () {
-      this.$http.get('/workplace/teams')
-        .then(res => {
-          this.teams = res.result
-        })
-    },
-    initRadar () {
-      this.radarLoading = true
-
-      this.$http.get('/workplace/radar')
-        .then(res => {
-          const dv = new DataSet.View().source(res.result)
-          dv.transform({
-            type: 'fold',
-            fields: ['个人', '团队', '部门'],
-            key: 'user',
-            value: 'score'
-          })
-
-          this.radarData = dv.rows
-          this.radarLoading = false
-        })
-    }
-  }
-}
-</script>
-
-<style lang="less" scoped>
-  .project-list {
-
-    .card-title {
-      font-size: 0;
-
-      a {
-        color: rgba(0, 0, 0, 0.85);
-        margin-left: 12px;
-        line-height: 24px;
-        height: 24px;
-        display: inline-block;
-        vertical-align: top;
-        font-size: 14px;
-
-        &:hover {
-          color: #1890ff;
-        }
-      }
-    }
-    .card-description {
-      color: rgba(0, 0, 0, 0.45);
-      height: 44px;
-      line-height: 22px;
-      overflow: hidden;
-    }
-    .project-item {
-      display: flex;
-      margin-top: 8px;
-      overflow: hidden;
-      font-size: 12px;
-      height: 20px;
-      line-height: 20px;
-      a {
-        color: rgba(0, 0, 0, 0.45);
-        display: inline-block;
-        flex: 1 1 0;
-
-        &:hover {
-          color: #1890ff;
-        }
-      }
-      .datetime {
-        color: rgba(0, 0, 0, 0.25);
-        flex: 0 0 auto;
-        float: right;
-      }
-    }
-    .ant-card-meta-description {
-      color: rgba(0, 0, 0, 0.45);
-      height: 44px;
-      line-height: 22px;
-      overflow: hidden;
-    }
-  }
-
-  .item-group {
-    padding: 20px 0 8px 24px;
-    font-size: 0;
-    a {
-      color: rgba(0, 0, 0, 0.65);
-      display: inline-block;
-      font-size: 14px;
-      margin-bottom: 13px;
-      width: 25%;
-    }
-  }
-
-  .members {
-    a {
-      display: block;
-      margin: 12px 0;
-      line-height: 24px;
-      height: 24px;
-      .member {
-        font-size: 14px;
-        color: rgba(0, 0, 0, .65);
-        line-height: 24px;
-        max-width: 100px;
-        vertical-align: top;
-        margin-left: 12px;
-        transition: all 0.3s;
-        display: inline-block;
-      }
-      &:hover {
-        span {
-          color: #1890ff;
-        }
-      }
-    }
-  }
-
-  .mobile {
-
-    .project-list {
-
-      .project-card-grid {
-        width: 100%;
-      }
-    }
-
-    .more-info {
-      border: 0;
-      padding-top: 16px;
-      margin: 16px 0 16px;
-    }
-
-    .headerContent .title .welcome-text {
-      display: none;
-    }
-  }
-
-</style>
+
+<template>
+  <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>
+    </div>
+    <div slot="extra">
+      <a-row class="more-info">
+        <a-col :span="8">
+          <head-info title="项目数" content="56" :center="false" :bordered="false"/>
+        </a-col>
+        <a-col :span="8">
+          <head-info title="团队排名" content="8/24" :center="false" :bordered="false"/>
+        </a-col>
+        <a-col :span="8">
+          <head-info title="项目访问" content="2,223" :center="false" />
+        </a-col>
+      </a-row>
+    </div>
+
+    <div>
+      <a-row :gutter="24">
+        <a-col :xl="16" :lg="24" :md="24" :sm="24" :xs="24">
+          <a-card
+            class="project-list"
+            :loading="loading"
+            style="margin-bottom: 24px;"
+            :bordered="false"
+            title="进行中的项目"
+            :body-style="{ padding: 0 }">
+            <a slot="extra">全部项目</a>
+            <div>
+              <a-card-grid class="project-card-grid" :key="i" v-for="(item, i) in projects">
+                <a-card :bordered="false" :body-style="{ padding: 0 }">
+                  <a-card-meta>
+                    <div slot="title" class="card-title">
+                      <a-avatar size="small" :src="item.cover"/>
+                      <a>{{ item.title }}</a>
+                    </div>
+                    <div slot="description" class="card-description">
+                      {{ item.description }}
+                    </div>
+                  </a-card-meta>
+                  <div class="project-item">
+                    <a href="/#/">科学搬砖组</a>
+                    <span class="datetime">9小时前</span>
+                  </div>
+                </a-card>
+              </a-card-grid>
+            </div>
+          </a-card>
+
+          <a-card :loading="loading" title="动态" :bordered="false">
+            <a-list>
+              <a-list-item :key="index" v-for="(item, index) in activities">
+                <a-list-item-meta>
+                  <a-avatar slot="avatar" :src="item.user.avatar" />
+                  <div slot="title">
+                    <span>{{ item.user.nickname }}</span>&nbsp;
+                    在&nbsp;<a href="#">{{ item.project.name }}</a>&nbsp;
+                    <span>{{ item.project.action }}</span>&nbsp;
+                    <a href="#">{{ item.project.event }}</a>
+                  </div>
+                  <div slot="description">{{ item.time }}</div>
+                </a-list-item-meta>
+              </a-list-item>
+            </a-list>
+          </a-card>
+        </a-col>
+        <a-col
+          style="padding: 0 12px"
+          :xl="8"
+          :lg="24"
+          :md="24"
+          :sm="24"
+          :xs="24">
+          <a-card title="快速开始 / 便捷导航" style="margin-bottom: 24px" :bordered="false" :body-style="{padding: 0}">
+            <div class="item-group">
+              <a>操作一</a>
+              <a>操作二</a>
+              <a>操作三</a>
+              <a>操作四</a>
+              <a>操作五</a>
+              <a>操作六</a>
+              <a-button size="small" type="primary" ghost icon="plus">添加</a-button>
+            </div>
+          </a-card>
+          <a-card title="XX 指数" style="margin-bottom: 24px" :loading="radarLoading" :bordered="false" :body-style="{ padding: 0 }">
+            <div style="min-height: 400px;">
+              <!-- :scale="scale" :axis1Opts="axis1Opts" :axis2Opts="axis2Opts"  -->
+              <radar :data="radarData" />
+            </div>
+          </a-card>
+          <a-card :loading="loading" title="团队" :bordered="false">
+            <div class="members">
+              <a-row>
+                <a-col :span="12" v-for="(item, index) in teams" :key="index">
+                  <a>
+                    <a-avatar size="small" :src="item.avatar" />
+                    <span class="member">{{ item.name }}</span>
+                  </a>
+                </a-col>
+              </a-row>
+            </div>
+          </a-card>
+        </a-col>
+      </a-row>
+    </div>
+  </page-view>
+</template>
+
+<script>
+import { timeFix } from '@/utils/util'
+import { mapGetters } from 'vuex'
+
+import { PageView } from '@/layouts'
+import HeadInfo from '@/components/tools/HeadInfo'
+import { Radar } from '@/components'
+
+import { getRoleList, getServiceList } from '@/api/manage'
+
+const DataSet = require('@antv/data-set')
+
+export default {
+  name: 'Workplace',
+  components: {
+    PageView,
+    HeadInfo,
+    Radar
+  },
+  data () {
+    return {
+      timeFix: timeFix(),
+      avatar: '',
+      user: {},
+
+      projects: [],
+      loading: true,
+      radarLoading: true,
+      activities: [],
+      teams: [],
+
+      // data
+      axis1Opts: {
+        dataKey: 'item',
+        line: null,
+        tickLine: null,
+        grid: {
+          lineStyle: {
+            lineDash: null
+          },
+          hideFirstLine: false
+        }
+      },
+      axis2Opts: {
+        dataKey: 'score',
+        line: null,
+        tickLine: null,
+        grid: {
+          type: 'polygon',
+          lineStyle: {
+            lineDash: null
+          }
+        }
+      },
+      scale: [{
+        dataKey: 'score',
+        min: 0,
+        max: 80
+      }],
+      axisData: [
+        { item: '引用', a: 70, b: 30, c: 40 },
+        { item: '口碑', a: 60, b: 70, c: 40 },
+        { item: '产量', a: 50, b: 60, c: 40 },
+        { item: '贡献', a: 40, b: 50, c: 40 },
+        { item: '热度', a: 60, b: 70, c: 40 },
+        { item: '引用', a: 70, b: 50, c: 40 }
+      ],
+      radarData: []
+    }
+  },
+  computed: {
+    userInfo () {
+      return this.$store.getters.userInfo
+    }
+  },
+  created () {
+    this.user = this.userInfo
+    this.avatar = this.userInfo.avatar
+
+    getRoleList().then(res => {
+      console.log('workplace -> call getRoleList()', res)
+    })
+
+    getServiceList().then(res => {
+      console.log('workplace -> call getServiceList()', res)
+    })
+  },
+  mounted () {
+    this.getProjects()
+    this.getActivity()
+    this.getTeams()
+    this.initRadar()
+  },
+  methods: {
+    ...mapGetters(['nickname', 'welcome']),
+    getProjects () {
+      this.$http.get('/list/search/projects')
+        .then(res => {
+          this.projects = res.result && res.result.data
+          this.loading = false
+        })
+    },
+    getActivity () {
+      this.$http.get('/workplace/activity')
+        .then(res => {
+          this.activities = res.result
+        })
+    },
+    getTeams () {
+      this.$http.get('/workplace/teams')
+        .then(res => {
+          this.teams = res.result
+        })
+    },
+    initRadar () {
+      this.radarLoading = true
+
+      this.$http.get('/workplace/radar')
+        .then(res => {
+          const dv = new DataSet.View().source(res.result)
+          dv.transform({
+            type: 'fold',
+            fields: ['个人', '团队', '部门'],
+            key: 'user',
+            value: 'score'
+          })
+
+          this.radarData = dv.rows
+          this.radarLoading = false
+        })
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+  .project-list {
+
+    .card-title {
+      font-size: 0;
+
+      a {
+        color: rgba(0, 0, 0, 0.85);
+        margin-left: 12px;
+        line-height: 24px;
+        height: 24px;
+        display: inline-block;
+        vertical-align: top;
+        font-size: 14px;
+
+        &:hover {
+          color: #1890ff;
+        }
+      }
+    }
+    .card-description {
+      color: rgba(0, 0, 0, 0.45);
+      height: 44px;
+      line-height: 22px;
+      overflow: hidden;
+    }
+    .project-item {
+      display: flex;
+      margin-top: 8px;
+      overflow: hidden;
+      font-size: 12px;
+      height: 20px;
+      line-height: 20px;
+      a {
+        color: rgba(0, 0, 0, 0.45);
+        display: inline-block;
+        flex: 1 1 0;
+
+        &:hover {
+          color: #1890ff;
+        }
+      }
+      .datetime {
+        color: rgba(0, 0, 0, 0.25);
+        flex: 0 0 auto;
+        float: right;
+      }
+    }
+    .ant-card-meta-description {
+      color: rgba(0, 0, 0, 0.45);
+      height: 44px;
+      line-height: 22px;
+      overflow: hidden;
+    }
+  }
+
+  .item-group {
+    padding: 20px 0 8px 24px;
+    font-size: 0;
+    a {
+      color: rgba(0, 0, 0, 0.65);
+      display: inline-block;
+      font-size: 14px;
+      margin-bottom: 13px;
+      width: 25%;
+    }
+  }
+
+  .members {
+    a {
+      display: block;
+      margin: 12px 0;
+      line-height: 24px;
+      height: 24px;
+      .member {
+        font-size: 14px;
+        color: rgba(0, 0, 0, .65);
+        line-height: 24px;
+        max-width: 100px;
+        vertical-align: top;
+        margin-left: 12px;
+        transition: all 0.3s;
+        display: inline-block;
+      }
+      &:hover {
+        span {
+          color: #1890ff;
+        }
+      }
+    }
+  }
+
+  .mobile {
+
+    .project-list {
+
+      .project-card-grid {
+        width: 100%;
+      }
+    }
+
+    .more-info {
+      border: 0;
+      padding-top: 16px;
+      margin: 16px 0 16px;
+    }
+
+    .headerContent .title .welcome-text {
+      display: none;
+    }
+  }
+
+</style>

+ 334 - 334
src/views/form/advancedForm/AdvancedForm.vue

@@ -1,334 +1,334 @@
-<template>
-  <div>
-    <a-card class="card" title="仓库管理" :bordered="false">
-      <repository-form ref="repository" :showSubmit="false" />
-    </a-card>
-    <a-card class="card" title="任务管理" :bordered="false">
-      <task-form ref="task" :showSubmit="false" />
-    </a-card>
-
-    <!-- table -->
-    <a-card>
-      <a-table
-        :columns="columns"
-        :dataSource="data"
-        :pagination="false"
-        :loading="memberLoading"
-      >
-        <template v-for="(col, i) in ['name', 'workId', 'department']" :slot="col" slot-scope="text, record">
-          <a-input
-            :key="col"
-            v-if="record.editable"
-            style="margin: -5px 0"
-            :value="text"
-            :placeholder="columns[i].title"
-            @change="e => handleChange(e.target.value, record.key, col)"
-          />
-          <template v-else>{{ text }}</template>
-        </template>
-        <template slot="operation" slot-scope="text, record">
-          <template v-if="record.editable">
-            <span v-if="record.isNew">
-              <a @click="saveRow(record)">添加</a>
-              <a-divider type="vertical" />
-              <a-popconfirm title="是否要删除此行?" @confirm="remove(record.key)">
-                <a>删除</a>
-              </a-popconfirm>
-            </span>
-            <span v-else>
-              <a @click="saveRow(record)">保存</a>
-              <a-divider type="vertical" />
-              <a @click="cancel(record.key)">取消</a>
-            </span>
-          </template>
-          <span v-else>
-            <a @click="toggle(record.key)">编辑</a>
-            <a-divider type="vertical" />
-            <a-popconfirm title="是否要删除此行?" @confirm="remove(record.key)">
-              <a>删除</a>
-            </a-popconfirm>
-          </span>
-        </template>
-      </a-table>
-      <a-button style="width: 100%; margin-top: 16px; margin-bottom: 8px" type="dashed" icon="plus" @click="newMember">新增成员</a-button>
-    </a-card>
-
-    <!-- fixed footer toolbar -->
-    <footer-tool-bar :style="{ width: isSideMenu() && isDesktop() ? `calc(100% - ${sidebarOpened ? 256 : 80}px)` : '100%'}">
-      <span class="popover-wrapper">
-        <a-popover title="表单校验信息" overlayClassName="antd-pro-pages-forms-style-errorPopover" trigger="click" :getPopupContainer="trigger => trigger.parentNode">
-          <template slot="content">
-            <li v-for="item in errors" :key="item.key" @click="scrollToField(item.key)" class="antd-pro-pages-forms-style-errorListItem">
-              <a-icon type="cross-circle-o" class="antd-pro-pages-forms-style-errorIcon" />
-              <div class="">{{ item.message }}</div>
-              <div class="antd-pro-pages-forms-style-errorField">{{ item.fieldLabel }}</div>
-            </li>
-          </template>
-          <span class="antd-pro-pages-forms-style-errorIcon" v-if="errors.length > 0">
-            <a-icon type="exclamation-circle" />{{ errors.length }}
-          </span>
-        </a-popover>
-      </span>
-      <a-button type="primary" @click="validate" :loading="loading">提交</a-button>
-    </footer-tool-bar>
-  </div>
-</template>
-
-<script>
-import RepositoryForm from './RepositoryForm'
-import TaskForm from './TaskForm'
-import FooterToolBar from '@/components/FooterToolbar'
-import { mixin, mixinDevice } from '@/utils/mixin'
-
-const fieldLabels = {
-  name: '仓库名',
-  url: '仓库域名',
-  owner: '仓库管理员',
-  approver: '审批人',
-  dateRange: '生效日期',
-  type: '仓库类型',
-  name2: '任务名',
-  url2: '任务描述',
-  owner2: '执行人',
-  approver2: '责任人',
-  dateRange2: '生效日期',
-  type2: '任务类型'
-}
-
-export default {
-  name: 'AdvancedForm',
-  mixins: [mixin, mixinDevice],
-  components: {
-    FooterToolBar,
-    RepositoryForm,
-    TaskForm
-  },
-  data () {
-    return {
-      description: '高级表单常见于一次性输入和提交大批量数据的场景。',
-      loading: false,
-      memberLoading: false,
-
-      // table
-      columns: [
-        {
-          title: '成员姓名',
-          dataIndex: 'name',
-          key: 'name',
-          width: '20%',
-          scopedSlots: { customRender: 'name' }
-        },
-        {
-          title: '工号',
-          dataIndex: 'workId',
-          key: 'workId',
-          width: '20%',
-          scopedSlots: { customRender: 'workId' }
-        },
-        {
-          title: '所属部门',
-          dataIndex: 'department',
-          key: 'department',
-          width: '40%',
-          scopedSlots: { customRender: 'department' }
-        },
-        {
-          title: '操作',
-          key: 'action',
-          scopedSlots: { customRender: 'operation' }
-        }
-      ],
-      data: [
-        {
-          key: '1',
-          name: '小明',
-          workId: '001',
-          editable: false,
-          department: '行政部'
-        },
-        {
-          key: '2',
-          name: '李莉',
-          workId: '002',
-          editable: false,
-          department: 'IT部'
-        },
-        {
-          key: '3',
-          name: '王小帅',
-          workId: '003',
-          editable: false,
-          department: '财务部'
-        }
-      ],
-
-      errors: []
-    }
-  },
-  methods: {
-    handleSubmit (e) {
-      e.preventDefault()
-    },
-    newMember () {
-      const length = this.data.length
-      this.data.push({
-        key: length === 0 ? '1' : (parseInt(this.data[length - 1].key) + 1).toString(),
-        name: '',
-        workId: '',
-        department: '',
-        editable: true,
-        isNew: true
-      })
-    },
-    remove (key) {
-      const newData = this.data.filter(item => item.key !== key)
-      this.data = newData
-    },
-    saveRow (record) {
-      this.memberLoading = true
-      const { key, name, workId, department } = record
-      if (!name || !workId || !department) {
-        this.memberLoading = false
-        this.$message.error('请填写完整成员信息。')
-        return
-      }
-      // 模拟网络请求、卡顿 800ms
-      new Promise((resolve) => {
-        setTimeout(() => {
-          resolve({ loop: false })
-        }, 800)
-      }).then(() => {
-        const target = this.data.filter(item => item.key === key)[0]
-        target.editable = false
-        target.isNew = false
-        this.memberLoading = false
-      })
-    },
-    toggle (key) {
-      const target = this.data.filter(item => item.key === key)[0]
-      target.editable = !target.editable
-    },
-    getRowByKey (key, newData) {
-      const data = this.data
-      return (newData || data).filter(item => item.key === key)[0]
-    },
-    cancel (key) {
-      const target = this.data.filter(item => item.key === key)[0]
-      target.editable = false
-    },
-    handleChange (value, key, column) {
-      const newData = [...this.data]
-      const target = newData.filter(item => key === item.key)[0]
-      if (target) {
-        target[column] = value
-        this.data = newData
-      }
-    },
-
-    // 最终全页面提交
-    validate () {
-      const { $refs: { repository, task }, $notification } = this
-      const repositoryForm = new Promise((resolve, reject) => {
-        repository.form.validateFields((err, values) => {
-          if (err) {
-            reject(err)
-            return
-          }
-          resolve(values)
-        })
-      })
-      const taskForm = new Promise((resolve, reject) => {
-        task.form.validateFields((err, values) => {
-          if (err) {
-            reject(err)
-            return
-          }
-          resolve(values)
-        })
-      })
-
-      // clean this.errors
-      this.errors = []
-      Promise.all([repositoryForm, taskForm]).then(values => {
-        $notification['error']({
-          message: 'Received values of form:',
-          description: JSON.stringify(values)
-        })
-      }).catch(() => {
-        const errors = Object.assign({}, repository.form.getFieldsError(), task.form.getFieldsError())
-        const tmp = { ...errors }
-        this.errorList(tmp)
-        console.log(tmp)
-      })
-    },
-    errorList (errors) {
-      if (!errors || errors.length === 0) {
-        return null
-      }
-      this.errors = Object.keys(errors).map(key => {
-        if (!errors[key]) {
-          return null
-        }
-
-        return {
-          key: key,
-          message: errors[key][0],
-          fieldLabel: fieldLabels[key]
-        }
-      })
-    },
-    scrollToField (fieldKey) {
-      const labelNode = document.querySelector(`label[for="${fieldKey}"]`)
-      if (labelNode) {
-        labelNode.scrollIntoView(true)
-      }
-    }
-  }
-}
-</script>
-
-<style lang="less" scoped>
-  .card{
-    margin-bottom: 24px;
-  }
-  .popover-wrapper {
-    /deep/ .antd-pro-pages-forms-style-errorPopover .ant-popover-inner-content {
-      min-width: 256px;
-      max-height: 290px;
-      padding: 0;
-      overflow: auto;
-    }
-  }
-  .antd-pro-pages-forms-style-errorIcon {
-    user-select: none;
-    margin-right: 24px;
-    color: #f5222d;
-    cursor: pointer;
-    i {
-          margin-right: 4px;
-    }
-  }
-  .antd-pro-pages-forms-style-errorListItem {
-    padding: 8px 16px;
-    list-style: none;
-    border-bottom: 1px solid #e8e8e8;
-    cursor: pointer;
-    transition: all .3s;
-
-    &:hover {
-      background: #e6f7ff;
-    }
-    .antd-pro-pages-forms-style-errorIcon {
-      float: left;
-      margin-top: 4px;
-      margin-right: 12px;
-      padding-bottom: 22px;
-      color: #f5222d;
-    }
-    .antd-pro-pages-forms-style-errorField {
-      margin-top: 2px;
-      color: rgba(0,0,0,.45);
-      font-size: 12px;
-    }
-  }
-</style>
+<template>
+  <div>
+    <a-card class="card" title="仓库管理" :bordered="false">
+      <repository-form ref="repository" :showSubmit="false" />
+    </a-card>
+    <a-card class="card" title="任务管理" :bordered="false">
+      <task-form ref="task" :showSubmit="false" />
+    </a-card>
+
+    <!-- table -->
+    <a-card>
+      <a-table
+        :columns="columns"
+        :dataSource="data"
+        :pagination="false"
+        :loading="memberLoading"
+      >
+        <template v-for="(col, i) in ['name', 'workId', 'department']" :slot="col" slot-scope="text, record">
+          <a-input
+            :key="col"
+            v-if="record.editable"
+            style="margin: -5px 0"
+            :value="text"
+            :placeholder="columns[i].title"
+            @change="e => handleChange(e.target.value, record.key, col)"
+          />
+          <template v-else>{{ text }}</template>
+        </template>
+        <template slot="operation" slot-scope="text, record">
+          <template v-if="record.editable">
+            <span v-if="record.isNew">
+              <a @click="saveRow(record)">添加</a>
+              <a-divider type="vertical" />
+              <a-popconfirm title="是否要删除此行?" @confirm="remove(record.key)">
+                <a>删除</a>
+              </a-popconfirm>
+            </span>
+            <span v-else>
+              <a @click="saveRow(record)">保存</a>
+              <a-divider type="vertical" />
+              <a @click="cancel(record.key)">取消</a>
+            </span>
+          </template>
+          <span v-else>
+            <a @click="toggle(record.key)">编辑</a>
+            <a-divider type="vertical" />
+            <a-popconfirm title="是否要删除此行?" @confirm="remove(record.key)">
+              <a>删除</a>
+            </a-popconfirm>
+          </span>
+        </template>
+      </a-table>
+      <a-button style="width: 100%; margin-top: 16px; margin-bottom: 8px" type="dashed" icon="plus" @click="newMember">新增成员</a-button>
+    </a-card>
+
+    <!-- fixed footer toolbar -->
+    <footer-tool-bar :style="{ width: isSideMenu() && isDesktop() ? `calc(100% - ${sidebarOpened ? 256 : 80}px)` : '100%'}">
+      <span class="popover-wrapper">
+        <a-popover title="表单校验信息" overlayClassName="antd-pro-pages-forms-style-errorPopover" trigger="click" :getPopupContainer="trigger => trigger.parentNode">
+          <template slot="content">
+            <li v-for="item in errors" :key="item.key" @click="scrollToField(item.key)" class="antd-pro-pages-forms-style-errorListItem">
+              <a-icon type="cross-circle-o" class="antd-pro-pages-forms-style-errorIcon" />
+              <div class="">{{ item.message }}</div>
+              <div class="antd-pro-pages-forms-style-errorField">{{ item.fieldLabel }}</div>
+            </li>
+          </template>
+          <span class="antd-pro-pages-forms-style-errorIcon" v-if="errors.length > 0">
+            <a-icon type="exclamation-circle" />{{ errors.length }}
+          </span>
+        </a-popover>
+      </span>
+      <a-button type="primary" @click="validate" :loading="loading">提交</a-button>
+    </footer-tool-bar>
+  </div>
+</template>
+
+<script>
+import RepositoryForm from './RepositoryForm'
+import TaskForm from './TaskForm'
+import FooterToolBar from '@/components/FooterToolbar'
+import { mixin, mixinDevice } from '@/utils/mixin'
+
+const fieldLabels = {
+  name: '仓库名',
+  url: '仓库域名',
+  owner: '仓库管理员',
+  approver: '审批人',
+  dateRange: '生效日期',
+  type: '仓库类型',
+  name2: '任务名',
+  url2: '任务描述',
+  owner2: '执行人',
+  approver2: '责任人',
+  dateRange2: '生效日期',
+  type2: '任务类型'
+}
+
+export default {
+  name: 'AdvancedForm',
+  mixins: [mixin, mixinDevice],
+  components: {
+    FooterToolBar,
+    RepositoryForm,
+    TaskForm
+  },
+  data () {
+    return {
+      description: '高级表单常见于一次性输入和提交大批量数据的场景。',
+      loading: false,
+      memberLoading: false,
+
+      // table
+      columns: [
+        {
+          title: '成员姓名',
+          dataIndex: 'name',
+          key: 'name',
+          width: '20%',
+          scopedSlots: { customRender: 'name' }
+        },
+        {
+          title: '工号',
+          dataIndex: 'workId',
+          key: 'workId',
+          width: '20%',
+          scopedSlots: { customRender: 'workId' }
+        },
+        {
+          title: '所属部门',
+          dataIndex: 'department',
+          key: 'department',
+          width: '40%',
+          scopedSlots: { customRender: 'department' }
+        },
+        {
+          title: '操作',
+          key: 'action',
+          scopedSlots: { customRender: 'operation' }
+        }
+      ],
+      data: [
+        {
+          key: '1',
+          name: '小明',
+          workId: '001',
+          editable: false,
+          department: '行政部'
+        },
+        {
+          key: '2',
+          name: '李莉',
+          workId: '002',
+          editable: false,
+          department: 'IT部'
+        },
+        {
+          key: '3',
+          name: '王小帅',
+          workId: '003',
+          editable: false,
+          department: '财务部'
+        }
+      ],
+
+      errors: []
+    }
+  },
+  methods: {
+    handleSubmit (e) {
+      e.preventDefault()
+    },
+    newMember () {
+      const length = this.data.length
+      this.data.push({
+        key: length === 0 ? '1' : (parseInt(this.data[length - 1].key) + 1).toString(),
+        name: '',
+        workId: '',
+        department: '',
+        editable: true,
+        isNew: true
+      })
+    },
+    remove (key) {
+      const newData = this.data.filter(item => item.key !== key)
+      this.data = newData
+    },
+    saveRow (record) {
+      this.memberLoading = true
+      const { key, name, workId, department } = record
+      if (!name || !workId || !department) {
+        this.memberLoading = false
+        this.$message.error('请填写完整成员信息。')
+        return
+      }
+      // 模拟网络请求、卡顿 800ms
+      new Promise((resolve) => {
+        setTimeout(() => {
+          resolve({ loop: false })
+        }, 800)
+      }).then(() => {
+        const target = this.data.filter(item => item.key === key)[0]
+        target.editable = false
+        target.isNew = false
+        this.memberLoading = false
+      })
+    },
+    toggle (key) {
+      const target = this.data.filter(item => item.key === key)[0]
+      target.editable = !target.editable
+    },
+    getRowByKey (key, newData) {
+      const data = this.data
+      return (newData || data).filter(item => item.key === key)[0]
+    },
+    cancel (key) {
+      const target = this.data.filter(item => item.key === key)[0]
+      target.editable = false
+    },
+    handleChange (value, key, column) {
+      const newData = [...this.data]
+      const target = newData.filter(item => key === item.key)[0]
+      if (target) {
+        target[column] = value
+        this.data = newData
+      }
+    },
+
+    // 最终全页面提交
+    validate () {
+      const { $refs: { repository, task }, $notification } = this
+      const repositoryForm = new Promise((resolve, reject) => {
+        repository.form.validateFields((err, values) => {
+          if (err) {
+            reject(err)
+            return
+          }
+          resolve(values)
+        })
+      })
+      const taskForm = new Promise((resolve, reject) => {
+        task.form.validateFields((err, values) => {
+          if (err) {
+            reject(err)
+            return
+          }
+          resolve(values)
+        })
+      })
+
+      // clean this.errors
+      this.errors = []
+      Promise.all([repositoryForm, taskForm]).then(values => {
+        $notification['error']({
+          message: 'Received values of form:',
+          description: JSON.stringify(values)
+        })
+      }).catch(() => {
+        const errors = Object.assign({}, repository.form.getFieldsError(), task.form.getFieldsError())
+        const tmp = { ...errors }
+        this.errorList(tmp)
+        console.log(tmp)
+      })
+    },
+    errorList (errors) {
+      if (!errors || errors.length === 0) {
+        return null
+      }
+      this.errors = Object.keys(errors).map(key => {
+        if (!errors[key]) {
+          return null
+        }
+
+        return {
+          key: key,
+          message: errors[key][0],
+          fieldLabel: fieldLabels[key]
+        }
+      })
+    },
+    scrollToField (fieldKey) {
+      const labelNode = document.querySelector(`label[for="${fieldKey}"]`)
+      if (labelNode) {
+        labelNode.scrollIntoView(true)
+      }
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+  .card{
+    margin-bottom: 24px;
+  }
+  .popover-wrapper {
+    /deep/ .antd-pro-pages-forms-style-errorPopover .ant-popover-inner-content {
+      min-width: 256px;
+      max-height: 290px;
+      padding: 0;
+      overflow: auto;
+    }
+  }
+  .antd-pro-pages-forms-style-errorIcon {
+    user-select: none;
+    margin-right: 24px;
+    color: #f5222d;
+    cursor: pointer;
+    i {
+          margin-right: 4px;
+    }
+  }
+  .antd-pro-pages-forms-style-errorListItem {
+    padding: 8px 16px;
+    list-style: none;
+    border-bottom: 1px solid #e8e8e8;
+    cursor: pointer;
+    transition: all .3s;
+
+    &:hover {
+      background: #e6f7ff;
+    }
+    .antd-pro-pages-forms-style-errorIcon {
+      float: left;
+      margin-top: 4px;
+      margin-right: 12px;
+      padding-bottom: 22px;
+      color: #f5222d;
+    }
+    .antd-pro-pages-forms-style-errorField {
+      margin-top: 2px;
+      color: rgba(0,0,0,.45);
+      font-size: 12px;
+    }
+  }
+</style>

+ 119 - 119
src/views/form/advancedForm/RepositoryForm.vue

@@ -1,119 +1,119 @@
-<template>
-  <a-form @submit="handleSubmit" :form="form" class="form">
-    <a-row class="form-row" :gutter="16">
-      <a-col :lg="6" :md="12" :sm="24">
-        <a-form-item label="仓库名">
-          <a-input
-            placeholder="请输入仓库名称"
-            v-decorator="[
-              'name',
-              {rules: [{ required: true, message: '请输入仓库名称', whitespace: true}]}
-            ]" />
-        </a-form-item>
-      </a-col>
-      <a-col :xl="{span: 7, offset: 1}" :lg="{span: 8}" :md="{span: 12}" :sm="24">
-        <a-form-item
-          label="仓库域名">
-          <a-input
-            addonBefore="http://"
-            addonAfter=".com"
-            placeholder="请输入"
-            v-decorator="[
-              'url',
-              {rules: [{ required: true, message: '请输入仓库域名', whitespace: true}, {validator: validate}]}
-            ]" />
-        </a-form-item>
-      </a-col>
-      <a-col :xl="{span: 9, offset: 1}" :lg="{span: 10}" :md="{span: 24}" :sm="24">
-        <a-form-item
-          label="仓库管理员">
-          <a-select placeholder="请选择管理员" v-decorator="[ 'owner', {rules: [{ required: true, message: '请选择管理员'}]} ]">
-            <a-select-option value="王同学">王同学</a-select-option>
-            <a-select-option value="李同学">李同学</a-select-option>
-            <a-select-option value="黄同学">黄同学</a-select-option>
-          </a-select>
-        </a-form-item>
-      </a-col>
-    </a-row>
-    <a-row class="form-row" :gutter="16">
-      <a-col :lg="6" :md="12" :sm="24">
-        <a-form-item
-          label="审批人">
-          <a-select placeholder="请选择审批员" v-decorator="[ 'approver', {rules: [{ required: true, message: '请选择审批员'}]} ]">
-            <a-select-option value="王晓丽">王晓丽</a-select-option>
-            <a-select-option value="李军">李军</a-select-option>
-          </a-select>
-        </a-form-item>
-      </a-col>
-      <a-col :xl="{span: 7, offset: 1}" :lg="{span: 8}" :md="{span: 12}" :sm="24">
-        <a-form-item
-          label="生效日期">
-          <a-range-picker
-            style="width: 100%"
-            v-decorator="[
-              'dateRange',
-              {rules: [{ required: true, message: '请选择生效日期'}]}
-            ]" />
-        </a-form-item>
-      </a-col>
-      <a-col :xl="{span: 9, offset: 1}" :lg="{span: 10}" :md="{span: 24}" :sm="24">
-        <a-form-item
-          label="仓库类型">
-          <a-select
-            placeholder="请选择仓库类型"
-            v-decorator="[
-              'type',
-              {rules: [{ required: true, message: '请选择仓库类型'}]}
-            ]" >
-            <a-select-option value="公开">公开</a-select-option>
-            <a-select-option value="私密">私密</a-select-option>
-          </a-select>
-        </a-form-item>
-      </a-col>
-    </a-row>
-    <a-form-item v-if="showSubmit">
-      <a-button htmlType="submit" >Submit</a-button>
-    </a-form-item>
-  </a-form>
-</template>
-
-<script>
-export default {
-  name: 'RepositoryForm',
-  props: {
-    showSubmit: {
-      type: Boolean,
-      default: false
-    }
-  },
-  data () {
-    return {
-      form: this.$form.createForm(this)
-    }
-  },
-  methods: {
-    handleSubmit (e) {
-      e.preventDefault()
-      this.form.validateFields((err, values) => {
-        if (!err) {
-          this.$notification['error']({
-            message: 'Received values of form:',
-            description: values
-          })
-        }
-      })
-    },
-    validate (rule, value, callback) {
-      const regex = /^user-(.*)$/
-      if (!regex.test(value)) {
-        callback(new Error('需要以 user- 开头'))
-      }
-      callback()
-    }
-  }
-}
-</script>
-
-<style scoped>
-
-</style>
+<template>
+  <a-form @submit="handleSubmit" :form="form" class="form">
+    <a-row class="form-row" :gutter="16">
+      <a-col :lg="6" :md="12" :sm="24">
+        <a-form-item label="仓库名">
+          <a-input
+            placeholder="请输入仓库名称"
+            v-decorator="[
+              'name',
+              {rules: [{ required: true, message: '请输入仓库名称', whitespace: true}]}
+            ]" />
+        </a-form-item>
+      </a-col>
+      <a-col :xl="{span: 7, offset: 1}" :lg="{span: 8}" :md="{span: 12}" :sm="24">
+        <a-form-item
+          label="仓库域名">
+          <a-input
+            addonBefore="http://"
+            addonAfter=".com"
+            placeholder="请输入"
+            v-decorator="[
+              'url',
+              {rules: [{ required: true, message: '请输入仓库域名', whitespace: true}, {validator: validate}]}
+            ]" />
+        </a-form-item>
+      </a-col>
+      <a-col :xl="{span: 9, offset: 1}" :lg="{span: 10}" :md="{span: 24}" :sm="24">
+        <a-form-item
+          label="仓库管理员">
+          <a-select placeholder="请选择管理员" v-decorator="[ 'owner', {rules: [{ required: true, message: '请选择管理员'}]} ]">
+            <a-select-option value="王同学">王同学</a-select-option>
+            <a-select-option value="李同学">李同学</a-select-option>
+            <a-select-option value="黄同学">黄同学</a-select-option>
+          </a-select>
+        </a-form-item>
+      </a-col>
+    </a-row>
+    <a-row class="form-row" :gutter="16">
+      <a-col :lg="6" :md="12" :sm="24">
+        <a-form-item
+          label="审批人">
+          <a-select placeholder="请选择审批员" v-decorator="[ 'approver', {rules: [{ required: true, message: '请选择审批员'}]} ]">
+            <a-select-option value="王晓丽">王晓丽</a-select-option>
+            <a-select-option value="李军">李军</a-select-option>
+          </a-select>
+        </a-form-item>
+      </a-col>
+      <a-col :xl="{span: 7, offset: 1}" :lg="{span: 8}" :md="{span: 12}" :sm="24">
+        <a-form-item
+          label="生效日期">
+          <a-range-picker
+            style="width: 100%"
+            v-decorator="[
+              'dateRange',
+              {rules: [{ required: true, message: '请选择生效日期'}]}
+            ]" />
+        </a-form-item>
+      </a-col>
+      <a-col :xl="{span: 9, offset: 1}" :lg="{span: 10}" :md="{span: 24}" :sm="24">
+        <a-form-item
+          label="仓库类型">
+          <a-select
+            placeholder="请选择仓库类型"
+            v-decorator="[
+              'type',
+              {rules: [{ required: true, message: '请选择仓库类型'}]}
+            ]" >
+            <a-select-option value="公开">公开</a-select-option>
+            <a-select-option value="私密">私密</a-select-option>
+          </a-select>
+        </a-form-item>
+      </a-col>
+    </a-row>
+    <a-form-item v-if="showSubmit">
+      <a-button htmlType="submit" >Submit</a-button>
+    </a-form-item>
+  </a-form>
+</template>
+
+<script>
+export default {
+  name: 'RepositoryForm',
+  props: {
+    showSubmit: {
+      type: Boolean,
+      default: false
+    }
+  },
+  data () {
+    return {
+      form: this.$form.createForm(this)
+    }
+  },
+  methods: {
+    handleSubmit (e) {
+      e.preventDefault()
+      this.form.validateFields((err, values) => {
+        if (!err) {
+          this.$notification['error']({
+            message: 'Received values of form:',
+            description: values
+          })
+        }
+      })
+    },
+    validate (rule, value, callback) {
+      const regex = /^user-(.*)$/
+      if (!regex.test(value)) {
+        callback(new Error('需要以 user- 开头'))
+      }
+      callback()
+    }
+  }
+}
+</script>
+
+<style scoped>
+
+</style>

+ 107 - 107
src/views/form/advancedForm/TaskForm.vue

@@ -1,107 +1,107 @@
-<template>
-  <a-form @submit="handleSubmit" :form="form" class="form">
-    <a-row class="form-row" :gutter="16">
-      <a-col :lg="6" :md="12" :sm="24">
-        <a-form-item
-          label="任务名">
-          <a-input placeholder="请输入任务名称" v-decorator="[ 'name2', {rules: [{ required: true, message: '请输入任务名称', whitespace: true}]} ]" />
-        </a-form-item>
-      </a-col>
-      <a-col :xl="{span: 7, offset: 1}" :lg="{span: 8}" :md="{span: 12}" :sm="24">
-        <a-form-item
-          label="任务描述">
-          <a-input placeholder="请输入任务描述" v-decorator="[ 'url2', {rules: [{ required: true, message: '请输入任务描述', whitespace: true}]} ]" />
-        </a-form-item>
-      </a-col>
-      <a-col :xl="{span: 9, offset: 1}" :lg="{span: 10}" :md="{span: 24}" :sm="24">
-        <a-form-item
-          label="执行人">
-          <a-select
-            placeholder="请选择执行人"
-            v-decorator="[
-              'owner2',
-              {rules: [{ required: true, message: '请选择执行人'}]}
-            ]" >
-            <a-select-option value="黄丽丽">黄丽丽</a-select-option>
-            <a-select-option value="李大刀">李大刀</a-select-option>
-          </a-select>
-        </a-form-item>
-      </a-col>
-    </a-row>
-    <a-row class="form-row" :gutter="16">
-      <a-col :lg="6" :md="12" :sm="24">
-        <a-form-item
-          label="责任人">
-          <a-select
-            placeholder="请选择责任人"
-            v-decorator="[
-              'approver2',
-              {rules: [{ required: true, message: '请选择责任人'}]}
-            ]" >
-            <a-select-option value="王伟">王伟</a-select-option>
-            <a-select-option value="李红军">李红军</a-select-option>
-          </a-select>
-        </a-form-item>
-      </a-col>
-      <a-col :xl="{span: 7, offset: 1}" :lg="{span: 8}" :md="{span: 12}" :sm="24">
-        <a-form-item
-          label="提醒时间">
-          <a-time-picker
-            style="width: 100%"
-            v-decorator="[
-              'dateRange2',
-              {rules: [{ required: true, message: '请选择提醒时间'}]}
-            ]" />
-        </a-form-item>
-      </a-col>
-      <a-col :xl="{span: 9, offset: 1}" :lg="{span: 10}" :md="{span: 24}" :sm="24">
-        <a-form-item
-          label="任务类型">
-          <a-select
-            placeholder="请选择任务类型"
-            v-decorator="[ 'type2', {rules: [{ required: true, message: '请选择任务类型'}]} ]" >
-            <a-select-option value="定时执行">定时执行</a-select-option>
-            <a-select-option value="周期执行">周期执行</a-select-option>
-          </a-select>
-        </a-form-item>
-      </a-col>
-    </a-row>
-    <a-form-item v-if="showSubmit">
-      <a-button htmlType="submit" >Submit</a-button>
-    </a-form-item>
-  </a-form>
-</template>
-
-<script>
-export default {
-  name: 'TaskForm',
-  props: {
-    showSubmit: {
-      type: Boolean,
-      default: false
-    }
-  },
-  data () {
-    return {
-      form: this.$form.createForm(this)
-    }
-  },
-  methods: {
-    handleSubmit (e) {
-      e.preventDefault()
-      this.form.validateFields((err, values) => {
-        if (!err) {
-          this.$notification['error']({
-            message: 'Received values of form:',
-            description: values
-          })
-        }
-      })
-    }
-  }
-}
-</script>
-
-<style scoped>
-
-</style>
+<template>
+  <a-form @submit="handleSubmit" :form="form" class="form">
+    <a-row class="form-row" :gutter="16">
+      <a-col :lg="6" :md="12" :sm="24">
+        <a-form-item
+          label="任务名">
+          <a-input placeholder="请输入任务名称" v-decorator="[ 'name2', {rules: [{ required: true, message: '请输入任务名称', whitespace: true}]} ]" />
+        </a-form-item>
+      </a-col>
+      <a-col :xl="{span: 7, offset: 1}" :lg="{span: 8}" :md="{span: 12}" :sm="24">
+        <a-form-item
+          label="任务描述">
+          <a-input placeholder="请输入任务描述" v-decorator="[ 'url2', {rules: [{ required: true, message: '请输入任务描述', whitespace: true}]} ]" />
+        </a-form-item>
+      </a-col>
+      <a-col :xl="{span: 9, offset: 1}" :lg="{span: 10}" :md="{span: 24}" :sm="24">
+        <a-form-item
+          label="执行人">
+          <a-select
+            placeholder="请选择执行人"
+            v-decorator="[
+              'owner2',
+              {rules: [{ required: true, message: '请选择执行人'}]}
+            ]" >
+            <a-select-option value="黄丽丽">黄丽丽</a-select-option>
+            <a-select-option value="李大刀">李大刀</a-select-option>
+          </a-select>
+        </a-form-item>
+      </a-col>
+    </a-row>
+    <a-row class="form-row" :gutter="16">
+      <a-col :lg="6" :md="12" :sm="24">
+        <a-form-item
+          label="责任人">
+          <a-select
+            placeholder="请选择责任人"
+            v-decorator="[
+              'approver2',
+              {rules: [{ required: true, message: '请选择责任人'}]}
+            ]" >
+            <a-select-option value="王伟">王伟</a-select-option>
+            <a-select-option value="李红军">李红军</a-select-option>
+          </a-select>
+        </a-form-item>
+      </a-col>
+      <a-col :xl="{span: 7, offset: 1}" :lg="{span: 8}" :md="{span: 12}" :sm="24">
+        <a-form-item
+          label="提醒时间">
+          <a-time-picker
+            style="width: 100%"
+            v-decorator="[
+              'dateRange2',
+              {rules: [{ required: true, message: '请选择提醒时间'}]}
+            ]" />
+        </a-form-item>
+      </a-col>
+      <a-col :xl="{span: 9, offset: 1}" :lg="{span: 10}" :md="{span: 24}" :sm="24">
+        <a-form-item
+          label="任务类型">
+          <a-select
+            placeholder="请选择任务类型"
+            v-decorator="[ 'type2', {rules: [{ required: true, message: '请选择任务类型'}]} ]" >
+            <a-select-option value="定时执行">定时执行</a-select-option>
+            <a-select-option value="周期执行">周期执行</a-select-option>
+          </a-select>
+        </a-form-item>
+      </a-col>
+    </a-row>
+    <a-form-item v-if="showSubmit">
+      <a-button htmlType="submit" >Submit</a-button>
+    </a-form-item>
+  </a-form>
+</template>
+
+<script>
+export default {
+  name: 'TaskForm',
+  props: {
+    showSubmit: {
+      type: Boolean,
+      default: false
+    }
+  },
+  data () {
+    return {
+      form: this.$form.createForm(this)
+    }
+  },
+  methods: {
+    handleSubmit (e) {
+      e.preventDefault()
+      this.form.validateFields((err, values) => {
+        if (!err) {
+          this.$notification['error']({
+            message: 'Received values of form:',
+            description: values
+          })
+        }
+      })
+    }
+  }
+}
+</script>
+
+<style scoped>
+
+</style>

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

@@ -1,69 +1,69 @@
-<template>
-  <div>
-    <a-form style="margin: 40px auto 0;">
-      <result title="操作成功" :is-success="true" description="预计两小时内到账" style="max-width: 560px;">
-        <div class="information">
-          <a-row>
-            <a-col :sm="8" :xs="24">付款账户:</a-col>
-            <a-col :sm="16" :xs="24">ant-design@alipay.com</a-col>
-          </a-row>
-          <a-row>
-            <a-col :sm="8" :xs="24">收款账户:</a-col>
-            <a-col :sm="16" :xs="24">test@example.com</a-col>
-          </a-row>
-          <a-row>
-            <a-col :sm="8" :xs="24">收款人姓名:</a-col>
-            <a-col :sm="16" :xs="24">辉夜</a-col>
-          </a-row>
-          <a-row>
-            <a-col :sm="8" :xs="24">转账金额:</a-col>
-            <a-col :sm="16" :xs="24"><span class="money">500</span> 元</a-col>
-          </a-row>
-        </div>
-        <div slot="action">
-          <a-button type="primary" @click="finish">再转一笔</a-button>
-          <a-button style="margin-left: 8px" @click="toOrderList">查看账单</a-button>
-        </div>
-      </result>
-    </a-form>
-  </div>
-</template>
-
-<script>
-import { Result } from '@/components'
-
-export default {
-  name: 'Step3',
-  components: {
-    Result
-  },
-  data () {
-    return {
-      loading: false
-    }
-  },
-  methods: {
-    finish () {
-      this.$emit('finish')
-    },
-    toOrderList () {
-      this.$router.push('/list/table-list')
-    }
-  }
-}
-</script>
-<style lang="less" scoped>
-  .information {
-    line-height: 22px;
-
-    .ant-row:not(:last-child) {
-      margin-bottom: 24px;
-    }
-  }
-  .money {
-    font-family: "Helvetica Neue",sans-serif;
-    font-weight: 500;
-    font-size: 20px;
-    line-height: 14px;
-  }
-</style>
+<template>
+  <div>
+    <a-form style="margin: 40px auto 0;">
+      <result title="操作成功" :is-success="true" description="预计两小时内到账" style="max-width: 560px;">
+        <div class="information">
+          <a-row>
+            <a-col :sm="8" :xs="24">付款账户:</a-col>
+            <a-col :sm="16" :xs="24">ant-design@alipay.com</a-col>
+          </a-row>
+          <a-row>
+            <a-col :sm="8" :xs="24">收款账户:</a-col>
+            <a-col :sm="16" :xs="24">test@example.com</a-col>
+          </a-row>
+          <a-row>
+            <a-col :sm="8" :xs="24">收款人姓名:</a-col>
+            <a-col :sm="16" :xs="24">辉夜</a-col>
+          </a-row>
+          <a-row>
+            <a-col :sm="8" :xs="24">转账金额:</a-col>
+            <a-col :sm="16" :xs="24"><span class="money">500</span> 元</a-col>
+          </a-row>
+        </div>
+        <div slot="action">
+          <a-button type="primary" @click="finish">再转一笔</a-button>
+          <a-button style="margin-left: 8px" @click="toOrderList">查看账单</a-button>
+        </div>
+      </result>
+    </a-form>
+  </div>
+</template>
+
+<script>
+import { Result } from '@/components'
+
+export default {
+  name: 'Step3',
+  components: {
+    Result
+  },
+  data () {
+    return {
+      loading: false
+    }
+  },
+  methods: {
+    finish () {
+      this.$emit('finish')
+    },
+    toOrderList () {
+      this.$router.push('/list/table-list')
+    }
+  }
+}
+</script>
+<style lang="less" scoped>
+  .information {
+    line-height: 22px;
+
+    .ant-row:not(:last-child) {
+      margin-bottom: 24px;
+    }
+  }
+  .money {
+    font-family: "Helvetica Neue",sans-serif;
+    font-weight: 500;
+    font-size: 20px;
+    line-height: 14px;
+  }
+</style>

+ 61 - 61
src/views/form/stepForm/StepForm.vue

@@ -1,62 +1,62 @@
-<template>
-  <a-card :bordered="false">
-    <a-steps class="steps" :current="currentTab">
-      <a-step title="填写转账信息" />
-      <a-step title="确认转账信息" />
-      <a-step title="完成" />
-    </a-steps>
-    <div class="content">
-      <step1 v-if="currentTab === 0" @nextStep="nextStep"/>
-      <step2 v-if="currentTab === 1" @nextStep="nextStep" @prevStep="prevStep"/>
-      <step3 v-if="currentTab === 2" @prevStep="prevStep" @finish="finish"/>
-    </div>
-  </a-card>
-</template>
-
-<script>
-import Step1 from './Step1'
-import Step2 from './Step2'
-import Step3 from './Step3'
-
-export default {
-  name: 'StepForm',
-  components: {
-    Step1,
-    Step2,
-    Step3
-  },
-  data () {
-    return {
-      description: '将一个冗长或用户不熟悉的表单任务分成多个步骤,指导用户完成。',
-      currentTab: 0,
-
-      // form
-      form: null
-    }
-  },
-  methods: {
-
-    // handler
-    nextStep () {
-      if (this.currentTab < 2) {
-        this.currentTab += 1
-      }
-    },
-    prevStep () {
-      if (this.currentTab > 0) {
-        this.currentTab -= 1
-      }
-    },
-    finish () {
-      this.currentTab = 0
-    }
-  }
-}
-</script>
-
-<style lang="less" scoped>
-  .steps {
-    max-width: 750px;
-    margin: 16px auto;
-  }
+<template>
+  <a-card :bordered="false">
+    <a-steps class="steps" :current="currentTab">
+      <a-step title="填写转账信息" />
+      <a-step title="确认转账信息" />
+      <a-step title="完成" />
+    </a-steps>
+    <div class="content">
+      <step1 v-if="currentTab === 0" @nextStep="nextStep"/>
+      <step2 v-if="currentTab === 1" @nextStep="nextStep" @prevStep="prevStep"/>
+      <step3 v-if="currentTab === 2" @prevStep="prevStep" @finish="finish"/>
+    </div>
+  </a-card>
+</template>
+
+<script>
+import Step1 from './Step1'
+import Step2 from './Step2'
+import Step3 from './Step3'
+
+export default {
+  name: 'StepForm',
+  components: {
+    Step1,
+    Step2,
+    Step3
+  },
+  data () {
+    return {
+      description: '将一个冗长或用户不熟悉的表单任务分成多个步骤,指导用户完成。',
+      currentTab: 0,
+
+      // form
+      form: null
+    }
+  },
+  methods: {
+
+    // handler
+    nextStep () {
+      if (this.currentTab < 2) {
+        this.currentTab += 1
+      }
+    },
+    prevStep () {
+      if (this.currentTab > 0) {
+        this.currentTab -= 1
+      }
+    },
+    finish () {
+      this.currentTab = 0
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+  .steps {
+    max-width: 750px;
+    margin: 16px auto;
+  }
 </style>

+ 163 - 163
src/views/list/StandardList.vue

@@ -1,164 +1,164 @@
-<template>
-  <div>
-    <a-card :bordered="false">
-      <a-row>
-        <a-col :sm="8" :xs="24">
-          <head-info title="我的待办" content="8个任务" :bordered="true"/>
-        </a-col>
-        <a-col :sm="8" :xs="24">
-          <head-info title="本周任务平均处理时间" content="32分钟" :bordered="true"/>
-        </a-col>
-        <a-col :sm="8" :xs="24">
-          <head-info title="本周完成任务数" content="24个"/>
-        </a-col>
-      </a-row>
-    </a-card>
-
-    <a-card
-      style="margin-top: 24px"
-      :bordered="false"
-      title="标准列表">
-
-      <div slot="extra">
-        <a-radio-group>
-          <a-radio-button>全部</a-radio-button>
-          <a-radio-button>进行中</a-radio-button>
-          <a-radio-button>等待中</a-radio-button>
-        </a-radio-group>
-        <a-input-search style="margin-left: 16px; width: 272px;" />
-      </div>
-
-      <div class="operate">
-        <a-button type="dashed" style="width: 100%" icon="plus">添加</a-button>
-      </div>
-
-      <a-list size="large" :pagination="{showSizeChanger: true, showQuickJumper: true, pageSize: 5, total: 50}">
-        <a-list-item :key="index" v-for="(item, index) in data">
-          <a-list-item-meta :description="item.description">
-            <a-avatar slot="avatar" size="large" shape="square" :src="item.avatar"/>
-            <a slot="title">{{ item.title }}</a>
-          </a-list-item-meta>
-          <div slot="actions">
-            <a>编辑</a>
-          </div>
-          <div slot="actions">
-            <a-dropdown>
-              <a-menu slot="overlay">
-                <a-menu-item><a>编辑</a></a-menu-item>
-                <a-menu-item><a>删除</a></a-menu-item>
-              </a-menu>
-              <a>更多<a-icon type="down"/></a>
-            </a-dropdown>
-          </div>
-          <div class="list-content">
-            <div class="list-content-item">
-              <span>Owner</span>
-              <p>{{ item.owner }}</p>
-            </div>
-            <div class="list-content-item">
-              <span>开始时间</span>
-              <p>{{ item.startAt }}</p>
-            </div>
-            <div class="list-content-item">
-              <a-progress :percent="item.progress.value" :status="!item.progress.status ? null : item.progress.status" style="width: 180px" />
-            </div>
-          </div>
-        </a-list-item>
-      </a-list>
-
-    </a-card>
-  </div>
-</template>
-
-<script>
-import HeadInfo from '@/components/tools/HeadInfo'
-
-const data = []
-data.push({
-  title: 'Alipay',
-  avatar: 'https://gw.alipayobjects.com/zos/rmsportal/WdGqmHpayyMjiEhcKoVE.png',
-  description: '那是一种内在的东西, 他们到达不了,也无法触及的',
-  owner: '付晓晓',
-  startAt: '2018-07-26 22:44',
-  progress: {
-    value: 90
-  }
-})
-data.push({
-  title: 'Angular',
-  avatar: 'https://gw.alipayobjects.com/zos/rmsportal/zOsKZmFRdUtvpqCImOVY.png',
-  description: '希望是一个好东西,也许是最好的,好东西是不会消亡的',
-  owner: '曲丽丽',
-  startAt: '2018-07-26 22:44',
-  progress: {
-    value: 54
-  }
-})
-data.push({
-  title: 'Ant Design',
-  avatar: 'https://gw.alipayobjects.com/zos/rmsportal/dURIMkkrRFpPgTuzkwnB.png',
-  description: '生命就像一盒巧克力,结果往往出人意料',
-  owner: '林东东',
-  startAt: '2018-07-26 22:44',
-  progress: {
-    value: 66
-  }
-})
-data.push({
-  title: 'Ant Design Pro',
-  avatar: 'https://gw.alipayobjects.com/zos/rmsportal/sfjbOqnsXXJgNCjCzDBL.png',
-  description: '城镇中有那么多的酒馆,她却偏偏走进了我的酒馆',
-  owner: '周星星',
-  startAt: '2018-07-26 22:44',
-  progress: {
-    value: 30
-  }
-})
-data.push({
-  title: 'Bootstrap',
-  avatar: 'https://gw.alipayobjects.com/zos/rmsportal/siCrBXXhmvTQGWPNLBow.png',
-  description: '那时候我只会想自己想要什么,从不想自己拥有什么',
-  owner: '吴加好',
-  startAt: '2018-07-26 22:44',
-  progress: {
-    status: 'exception',
-    value: 100
-  }
-})
-
-export default {
-  name: 'StandardList',
-  components: {
-    HeadInfo
-  },
-  data () {
-    return {
-      data
-    }
-  }
-}
-</script>
-
-<style lang="less" scoped>
-    .ant-avatar-lg {
-        width: 48px;
-        height: 48px;
-        line-height: 48px;
-    }
-
-    .list-content-item {
-        color: rgba(0, 0, 0, .45);
-        display: inline-block;
-        vertical-align: middle;
-        font-size: 14px;
-        margin-left: 40px;
-        span {
-            line-height: 20px;
-        }
-        p {
-            margin-top: 4px;
-            margin-bottom: 0;
-            line-height: 22px;
-        }
-    }
+<template>
+  <div>
+    <a-card :bordered="false">
+      <a-row>
+        <a-col :sm="8" :xs="24">
+          <head-info title="我的待办" content="8个任务" :bordered="true"/>
+        </a-col>
+        <a-col :sm="8" :xs="24">
+          <head-info title="本周任务平均处理时间" content="32分钟" :bordered="true"/>
+        </a-col>
+        <a-col :sm="8" :xs="24">
+          <head-info title="本周完成任务数" content="24个"/>
+        </a-col>
+      </a-row>
+    </a-card>
+
+    <a-card
+      style="margin-top: 24px"
+      :bordered="false"
+      title="标准列表">
+
+      <div slot="extra">
+        <a-radio-group>
+          <a-radio-button>全部</a-radio-button>
+          <a-radio-button>进行中</a-radio-button>
+          <a-radio-button>等待中</a-radio-button>
+        </a-radio-group>
+        <a-input-search style="margin-left: 16px; width: 272px;" />
+      </div>
+
+      <div class="operate">
+        <a-button type="dashed" style="width: 100%" icon="plus">添加</a-button>
+      </div>
+
+      <a-list size="large" :pagination="{showSizeChanger: true, showQuickJumper: true, pageSize: 5, total: 50}">
+        <a-list-item :key="index" v-for="(item, index) in data">
+          <a-list-item-meta :description="item.description">
+            <a-avatar slot="avatar" size="large" shape="square" :src="item.avatar"/>
+            <a slot="title">{{ item.title }}</a>
+          </a-list-item-meta>
+          <div slot="actions">
+            <a>编辑</a>
+          </div>
+          <div slot="actions">
+            <a-dropdown>
+              <a-menu slot="overlay">
+                <a-menu-item><a>编辑</a></a-menu-item>
+                <a-menu-item><a>删除</a></a-menu-item>
+              </a-menu>
+              <a>更多<a-icon type="down"/></a>
+            </a-dropdown>
+          </div>
+          <div class="list-content">
+            <div class="list-content-item">
+              <span>Owner</span>
+              <p>{{ item.owner }}</p>
+            </div>
+            <div class="list-content-item">
+              <span>开始时间</span>
+              <p>{{ item.startAt }}</p>
+            </div>
+            <div class="list-content-item">
+              <a-progress :percent="item.progress.value" :status="!item.progress.status ? null : item.progress.status" style="width: 180px" />
+            </div>
+          </div>
+        </a-list-item>
+      </a-list>
+
+    </a-card>
+  </div>
+</template>
+
+<script>
+import HeadInfo from '@/components/tools/HeadInfo'
+
+const data = []
+data.push({
+  title: 'Alipay',
+  avatar: 'https://gw.alipayobjects.com/zos/rmsportal/WdGqmHpayyMjiEhcKoVE.png',
+  description: '那是一种内在的东西, 他们到达不了,也无法触及的',
+  owner: '付晓晓',
+  startAt: '2018-07-26 22:44',
+  progress: {
+    value: 90
+  }
+})
+data.push({
+  title: 'Angular',
+  avatar: 'https://gw.alipayobjects.com/zos/rmsportal/zOsKZmFRdUtvpqCImOVY.png',
+  description: '希望是一个好东西,也许是最好的,好东西是不会消亡的',
+  owner: '曲丽丽',
+  startAt: '2018-07-26 22:44',
+  progress: {
+    value: 54
+  }
+})
+data.push({
+  title: 'Ant Design',
+  avatar: 'https://gw.alipayobjects.com/zos/rmsportal/dURIMkkrRFpPgTuzkwnB.png',
+  description: '生命就像一盒巧克力,结果往往出人意料',
+  owner: '林东东',
+  startAt: '2018-07-26 22:44',
+  progress: {
+    value: 66
+  }
+})
+data.push({
+  title: 'Ant Design Pro',
+  avatar: 'https://gw.alipayobjects.com/zos/rmsportal/sfjbOqnsXXJgNCjCzDBL.png',
+  description: '城镇中有那么多的酒馆,她却偏偏走进了我的酒馆',
+  owner: '周星星',
+  startAt: '2018-07-26 22:44',
+  progress: {
+    value: 30
+  }
+})
+data.push({
+  title: 'Bootstrap',
+  avatar: 'https://gw.alipayobjects.com/zos/rmsportal/siCrBXXhmvTQGWPNLBow.png',
+  description: '那时候我只会想自己想要什么,从不想自己拥有什么',
+  owner: '吴加好',
+  startAt: '2018-07-26 22:44',
+  progress: {
+    status: 'exception',
+    value: 100
+  }
+})
+
+export default {
+  name: 'StandardList',
+  components: {
+    HeadInfo
+  },
+  data () {
+    return {
+      data
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+    .ant-avatar-lg {
+        width: 48px;
+        height: 48px;
+        line-height: 48px;
+    }
+
+    .list-content-item {
+        color: rgba(0, 0, 0, .45);
+        display: inline-block;
+        vertical-align: middle;
+        font-size: 14px;
+        margin-left: 40px;
+        span {
+            line-height: 20px;
+        }
+        p {
+            margin-top: 4px;
+            margin-bottom: 0;
+            line-height: 22px;
+        }
+    }
 </style>

+ 77 - 77
src/views/list/search/SearchLayout.vue

@@ -1,78 +1,78 @@
-<template>
-  <div class="search-content">
-    <router-view />
-  </div>
-</template>
-
-<script>
-export default {
-  name: 'SearchLayout',
-  data () {
-    return {
-      tabs: {
-        items: [
-          {
-            key: '1',
-            title: '文章'
-          },
-          {
-            key: '2',
-            title: '项目'
-          },
-          {
-            key: '3',
-            title: '应用'
-          }
-        ],
-        active: () => {
-          switch (this.$route.path) {
-            case '/list/search/article':
-              return '1'
-            case '/list/search/project':
-              return '2'
-            case '/list/search/application':
-              return '3'
-            default:
-              return '1'
-          }
-        },
-        callback: (key) => {
-          switch (key) {
-            case '1':
-              this.$router.push('/list/search/article')
-              break
-            case '2':
-              this.$router.push('/list/search/project')
-              break
-            case '3':
-              this.$router.push('/list/search/application')
-              break
-            default:
-              this.$router.push('/workplace')
-          }
-        }
-      },
-      search: true
-    }
-  },
-  computed: {
-
-  },
-  methods: {
-  }
-}
-</script>
-
-<style lang="less" scoped>
-  .search-head{
-    background-color: #fff;
-    margin: -25px -24px -24px;
-    .search-input{
-      text-align: center;
-      margin-bottom: 16px;
-    }
-  }
-  .search-content{
-    margin-top: 48px;
-  }
+<template>
+  <div class="search-content">
+    <router-view />
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'SearchLayout',
+  data () {
+    return {
+      tabs: {
+        items: [
+          {
+            key: '1',
+            title: '文章'
+          },
+          {
+            key: '2',
+            title: '项目'
+          },
+          {
+            key: '3',
+            title: '应用'
+          }
+        ],
+        active: () => {
+          switch (this.$route.path) {
+            case '/list/search/article':
+              return '1'
+            case '/list/search/project':
+              return '2'
+            case '/list/search/application':
+              return '3'
+            default:
+              return '1'
+          }
+        },
+        callback: (key) => {
+          switch (key) {
+            case '1':
+              this.$router.push('/list/search/article')
+              break
+            case '2':
+              this.$router.push('/list/search/project')
+              break
+            case '3':
+              this.$router.push('/list/search/application')
+              break
+            default:
+              this.$router.push('/workplace')
+          }
+        }
+      },
+      search: true
+    }
+  },
+  computed: {
+
+  },
+  methods: {
+  }
+}
+</script>
+
+<style lang="less" scoped>
+  .search-head{
+    background-color: #fff;
+    margin: -25px -24px -24px;
+    .search-input{
+      text-align: center;
+      margin-bottom: 16px;
+    }
+  }
+  .search-content{
+    margin-top: 48px;
+  }
 </style>

+ 343 - 343
src/views/profile/advanced/Advanced.vue

@@ -1,343 +1,343 @@
-
-<template>
-  <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>
-      <detail-list-item term="订购产品">XX服务</detail-list-item>
-      <detail-list-item term="创建时间">2018-08-07</detail-list-item>
-      <detail-list-item term="关联单据"><a>12421</a></detail-list-item>
-      <detail-list-item term="生效日期">2018-08-07 ~ 2018-12-11</detail-list-item>
-      <detail-list-item term="备注">请于两个工作日内确认</detail-list-item>
-    </detail-list>
-    <a-row slot="extra" class="status-list">
-      <a-col :xs="12" :sm="12">
-        <div class="text">状态</div>
-        <div class="heading">待审批</div>
-      </a-col>
-      <a-col :xs="12" :sm="12">
-        <div class="text">订单金额</div>
-        <div class="heading">¥ 568.08</div>
-      </a-col>
-    </a-row>
-    <!-- actions -->
-    <template slot="action">
-      <a-button-group style="margin-right: 4px;">
-        <a-button>操作</a-button>
-        <a-button>操作</a-button>
-        <a-button><a-icon type="ellipsis"/></a-button>
-      </a-button-group>
-      <a-button type="primary" >主操作</a-button>
-    </template>
-
-    <a-card :bordered="false" title="流程进度">
-      <a-steps :direction="isMobile() && 'vertical' || 'horizontal'" :current="1" progressDot>
-        <a-step title="创建项目">
-        </a-step>
-        <a-step title="部门初审">
-        </a-step>
-        <a-step title="财务复核">
-        </a-step>
-        <a-step title="完成">
-        </a-step>
-      </a-steps>
-    </a-card>
-
-    <a-card style="margin-top: 24px" :bordered="false" title="用户信息">
-      <detail-list>
-        <detail-list-item term="用户姓名">付晓晓</detail-list-item>
-        <detail-list-item term="会员卡号">32943898021309809423</detail-list-item>
-        <detail-list-item term="身份证">3321944288191034921</detail-list-item>
-        <detail-list-item term="联系方式">18112345678</detail-list-item>
-        <detail-list-item term="联系地址">浙江省杭州市西湖区黄姑山路工专路交叉路口</detail-list-item>
-      </detail-list>
-      <detail-list title="信息组">
-        <detail-list-item term="某某数据">725</detail-list-item>
-        <detail-list-item term="该数据更新时间">2018-08-08</detail-list-item>
-        <detail-list-item ></detail-list-item>
-        <detail-list-item term="某某数据">725</detail-list-item>
-        <detail-list-item term="该数据更新时间">2018-08-08</detail-list-item>
-        <detail-list-item ></detail-list-item>
-      </detail-list>
-      <a-card type="inner" title="多层信息组">
-        <detail-list title="组名称" size="small">
-          <detail-list-item term="负责人">林东东</detail-list-item>
-          <detail-list-item term="角色码">1234567</detail-list-item>
-          <detail-list-item term="所属部门">XX公司-YY部</detail-list-item>
-          <detail-list-item term="过期时间">2018-08-08</detail-list-item>
-          <detail-list-item term="描述">这段描述很长很长很长很长很长很长很长很长很长很长很长很长很长很长...</detail-list-item>
-        </detail-list>
-        <a-divider style="margin: 16px 0" />
-        <detail-list title="组名称" size="small" :col="1">
-          <detail-list-item term="学名">	Citrullus lanatus (Thunb.) Matsum. et Nakai一年生蔓生藤本;茎、枝粗壮,具明显的棱。卷须较粗..</detail-list-item>
-        </detail-list>
-        <a-divider style="margin: 16px 0" />
-        <detail-list title="组名称" size="small" :col="2">
-          <detail-list-item term="负责人">付小小</detail-list-item>
-          <detail-list-item term="角色码">1234567</detail-list-item>
-        </detail-list>
-      </a-card>
-
-    </a-card>
-
-    <a-card style="margin-top: 24px" :bordered="false" title="用户近半年来电记录">
-      <div class="no-data"><a-icon type="frown-o"/>暂无数据</div>
-    </a-card>
-
-    <!-- 操作 -->
-    <a-card
-      style="margin-top: 24px"
-      :bordered="false"
-      :tabList="tabList"
-      :activeTabKey="activeTabKey"
-      @tabChange="(key) => {this.activeTabKey = key}"
-    >
-      <a-table
-        v-if="activeTabKey === '1'"
-        :columns="operationColumns"
-        :dataSource="operation1"
-        :pagination="false"
-      >
-        <template
-          slot="status"
-          slot-scope="status">
-          <a-badge :status="status | statusTypeFilter" :text="status | statusFilter"/>
-        </template>
-      </a-table>
-      <a-table
-        v-if="activeTabKey === '2'"
-        :columns="operationColumns"
-        :dataSource="operation2"
-        :pagination="false"
-      >
-        <template
-          slot="status"
-          slot-scope="status">
-          <a-badge :status="status | statusTypeFilter" :text="status | statusFilter"/>
-        </template>
-      </a-table>
-      <a-table
-        v-if="activeTabKey === '3'"
-        :columns="operationColumns"
-        :dataSource="operation3"
-        :pagination="false"
-      >
-        <template
-          slot="status"
-          slot-scope="status">
-          <a-badge :status="status | statusTypeFilter" :text="status | statusFilter"/>
-        </template>
-      </a-table>
-    </a-card>
-
-  </page-view>
-</template>
-
-<script>
-import { mixinDevice } from '@/utils/mixin'
-import { PageView } from '@/layouts'
-import DetailList from '@/components/tools/DetailList'
-
-const DetailListItem = DetailList.Item
-
-export default {
-  name: 'Advanced',
-  components: {
-    PageView,
-    DetailList,
-    DetailListItem
-  },
-  mixins: [mixinDevice],
-  data () {
-    return {
-      tabList: [
-        {
-          key: '1',
-          tab: '操作日志一'
-        },
-        {
-          key: '2',
-          tab: '操作日志二'
-        },
-        {
-          key: '3',
-          tab: '操作日志三'
-        }
-      ],
-      activeTabKey: '1',
-
-      operationColumns: [
-        {
-          title: '操作类型',
-          dataIndex: 'type',
-          key: 'type'
-        },
-        {
-          title: '操作人',
-          dataIndex: 'name',
-          key: 'name'
-        },
-        {
-          title: '执行结果',
-          dataIndex: 'status',
-          key: 'status',
-          scopedSlots: { customRender: 'status' }
-        },
-        {
-          title: '操作时间',
-          dataIndex: 'updatedAt',
-          key: 'updatedAt'
-        },
-        {
-          title: '备注',
-          dataIndex: 'remark',
-          key: 'remark'
-        }
-      ],
-      operation1: [
-        {
-          key: 'op1',
-          type: '订购关系生效',
-          name: '曲丽丽',
-          status: 'agree',
-          updatedAt: '2017-10-03  19:23:12',
-          remark: '-'
-        },
-        {
-          key: 'op2',
-          type: '财务复审',
-          name: '付小小',
-          status: 'reject',
-          updatedAt: '2017-10-03  19:23:12',
-          remark: '不通过原因'
-        },
-        {
-          key: 'op3',
-          type: '部门初审',
-          name: '周毛毛',
-          status: 'agree',
-          updatedAt: '2017-10-03  19:23:12',
-          remark: '-'
-        },
-        {
-          key: 'op4',
-          type: '提交订单',
-          name: '林东东',
-          status: 'agree',
-          updatedAt: '2017-10-03  19:23:12',
-          remark: '很棒'
-        },
-        {
-          key: 'op5',
-          type: '创建订单',
-          name: '汗牙牙',
-          status: 'agree',
-          updatedAt: '2017-10-03  19:23:12',
-          remark: '-'
-        }
-      ],
-      operation2: [
-        {
-          key: 'op2',
-          type: '财务复审',
-          name: '付小小',
-          status: 'reject',
-          updatedAt: '2017-10-03  19:23:12',
-          remark: '不通过原因'
-        },
-        {
-          key: 'op3',
-          type: '部门初审',
-          name: '周毛毛',
-          status: 'agree',
-          updatedAt: '2017-10-03  19:23:12',
-          remark: '-'
-        },
-        {
-          key: 'op4',
-          type: '提交订单',
-          name: '林东东',
-          status: 'agree',
-          updatedAt: '2017-10-03  19:23:12',
-          remark: '很棒'
-        }
-      ],
-      operation3: [
-        {
-          key: 'op2',
-          type: '财务复审',
-          name: '付小小',
-          status: 'reject',
-          updatedAt: '2017-10-03  19:23:12',
-          remark: '不通过原因'
-        },
-        {
-          key: 'op3',
-          type: '部门初审',
-          name: '周毛毛',
-          status: 'agree',
-          updatedAt: '2017-10-03  19:23:12',
-          remark: '-'
-        }
-      ]
-    }
-  },
-  filters: {
-    statusFilter (status) {
-      const statusMap = {
-        'agree': '成功',
-        'reject': '驳回'
-      }
-      return statusMap[status]
-    },
-    statusTypeFilter (type) {
-      const statusTypeMap = {
-        'agree': 'success',
-        'reject': 'error'
-      }
-      return statusTypeMap[type]
-    }
-  }
-}
-</script>
-
-<style lang="less" scoped>
-
-  .detail-layout {
-    margin-left: 44px;
-  }
-  .text {
-    color: rgba(0, 0, 0, .45);
-  }
-
-  .heading {
-    color: rgba(0, 0, 0, .85);
-    font-size: 20px;
-  }
-
-  .no-data {
-    color: rgba(0, 0, 0, .25);
-    text-align: center;
-    line-height: 64px;
-    font-size: 16px;
-
-    i {
-      font-size: 24px;
-      margin-right: 16px;
-      position: relative;
-      top: 3px;
-    }
-  }
-
-  .mobile {
-    .detail-layout {
-      margin-left: unset;
-    }
-    .text {
-
-    }
-    .status-list {
-      text-align: left;
-    }
-  }
-</style>
+
+<template>
+  <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>
+      <detail-list-item term="订购产品">XX服务</detail-list-item>
+      <detail-list-item term="创建时间">2018-08-07</detail-list-item>
+      <detail-list-item term="关联单据"><a>12421</a></detail-list-item>
+      <detail-list-item term="生效日期">2018-08-07 ~ 2018-12-11</detail-list-item>
+      <detail-list-item term="备注">请于两个工作日内确认</detail-list-item>
+    </detail-list>
+    <a-row slot="extra" class="status-list">
+      <a-col :xs="12" :sm="12">
+        <div class="text">状态</div>
+        <div class="heading">待审批</div>
+      </a-col>
+      <a-col :xs="12" :sm="12">
+        <div class="text">订单金额</div>
+        <div class="heading">¥ 568.08</div>
+      </a-col>
+    </a-row>
+    <!-- actions -->
+    <template slot="action">
+      <a-button-group style="margin-right: 4px;">
+        <a-button>操作</a-button>
+        <a-button>操作</a-button>
+        <a-button><a-icon type="ellipsis"/></a-button>
+      </a-button-group>
+      <a-button type="primary" >主操作</a-button>
+    </template>
+
+    <a-card :bordered="false" title="流程进度">
+      <a-steps :direction="isMobile() && 'vertical' || 'horizontal'" :current="1" progressDot>
+        <a-step title="创建项目">
+        </a-step>
+        <a-step title="部门初审">
+        </a-step>
+        <a-step title="财务复核">
+        </a-step>
+        <a-step title="完成">
+        </a-step>
+      </a-steps>
+    </a-card>
+
+    <a-card style="margin-top: 24px" :bordered="false" title="用户信息">
+      <detail-list>
+        <detail-list-item term="用户姓名">付晓晓</detail-list-item>
+        <detail-list-item term="会员卡号">32943898021309809423</detail-list-item>
+        <detail-list-item term="身份证">3321944288191034921</detail-list-item>
+        <detail-list-item term="联系方式">18112345678</detail-list-item>
+        <detail-list-item term="联系地址">浙江省杭州市西湖区黄姑山路工专路交叉路口</detail-list-item>
+      </detail-list>
+      <detail-list title="信息组">
+        <detail-list-item term="某某数据">725</detail-list-item>
+        <detail-list-item term="该数据更新时间">2018-08-08</detail-list-item>
+        <detail-list-item ></detail-list-item>
+        <detail-list-item term="某某数据">725</detail-list-item>
+        <detail-list-item term="该数据更新时间">2018-08-08</detail-list-item>
+        <detail-list-item ></detail-list-item>
+      </detail-list>
+      <a-card type="inner" title="多层信息组">
+        <detail-list title="组名称" size="small">
+          <detail-list-item term="负责人">林东东</detail-list-item>
+          <detail-list-item term="角色码">1234567</detail-list-item>
+          <detail-list-item term="所属部门">XX公司-YY部</detail-list-item>
+          <detail-list-item term="过期时间">2018-08-08</detail-list-item>
+          <detail-list-item term="描述">这段描述很长很长很长很长很长很长很长很长很长很长很长很长很长很长...</detail-list-item>
+        </detail-list>
+        <a-divider style="margin: 16px 0" />
+        <detail-list title="组名称" size="small" :col="1">
+          <detail-list-item term="学名">	Citrullus lanatus (Thunb.) Matsum. et Nakai一年生蔓生藤本;茎、枝粗壮,具明显的棱。卷须较粗..</detail-list-item>
+        </detail-list>
+        <a-divider style="margin: 16px 0" />
+        <detail-list title="组名称" size="small" :col="2">
+          <detail-list-item term="负责人">付小小</detail-list-item>
+          <detail-list-item term="角色码">1234567</detail-list-item>
+        </detail-list>
+      </a-card>
+
+    </a-card>
+
+    <a-card style="margin-top: 24px" :bordered="false" title="用户近半年来电记录">
+      <div class="no-data"><a-icon type="frown-o"/>暂无数据</div>
+    </a-card>
+
+    <!-- 操作 -->
+    <a-card
+      style="margin-top: 24px"
+      :bordered="false"
+      :tabList="tabList"
+      :activeTabKey="activeTabKey"
+      @tabChange="(key) => {this.activeTabKey = key}"
+    >
+      <a-table
+        v-if="activeTabKey === '1'"
+        :columns="operationColumns"
+        :dataSource="operation1"
+        :pagination="false"
+      >
+        <template
+          slot="status"
+          slot-scope="status">
+          <a-badge :status="status | statusTypeFilter" :text="status | statusFilter"/>
+        </template>
+      </a-table>
+      <a-table
+        v-if="activeTabKey === '2'"
+        :columns="operationColumns"
+        :dataSource="operation2"
+        :pagination="false"
+      >
+        <template
+          slot="status"
+          slot-scope="status">
+          <a-badge :status="status | statusTypeFilter" :text="status | statusFilter"/>
+        </template>
+      </a-table>
+      <a-table
+        v-if="activeTabKey === '3'"
+        :columns="operationColumns"
+        :dataSource="operation3"
+        :pagination="false"
+      >
+        <template
+          slot="status"
+          slot-scope="status">
+          <a-badge :status="status | statusTypeFilter" :text="status | statusFilter"/>
+        </template>
+      </a-table>
+    </a-card>
+
+  </page-view>
+</template>
+
+<script>
+import { mixinDevice } from '@/utils/mixin'
+import { PageView } from '@/layouts'
+import DetailList from '@/components/tools/DetailList'
+
+const DetailListItem = DetailList.Item
+
+export default {
+  name: 'Advanced',
+  components: {
+    PageView,
+    DetailList,
+    DetailListItem
+  },
+  mixins: [mixinDevice],
+  data () {
+    return {
+      tabList: [
+        {
+          key: '1',
+          tab: '操作日志一'
+        },
+        {
+          key: '2',
+          tab: '操作日志二'
+        },
+        {
+          key: '3',
+          tab: '操作日志三'
+        }
+      ],
+      activeTabKey: '1',
+
+      operationColumns: [
+        {
+          title: '操作类型',
+          dataIndex: 'type',
+          key: 'type'
+        },
+        {
+          title: '操作人',
+          dataIndex: 'name',
+          key: 'name'
+        },
+        {
+          title: '执行结果',
+          dataIndex: 'status',
+          key: 'status',
+          scopedSlots: { customRender: 'status' }
+        },
+        {
+          title: '操作时间',
+          dataIndex: 'updatedAt',
+          key: 'updatedAt'
+        },
+        {
+          title: '备注',
+          dataIndex: 'remark',
+          key: 'remark'
+        }
+      ],
+      operation1: [
+        {
+          key: 'op1',
+          type: '订购关系生效',
+          name: '曲丽丽',
+          status: 'agree',
+          updatedAt: '2017-10-03  19:23:12',
+          remark: '-'
+        },
+        {
+          key: 'op2',
+          type: '财务复审',
+          name: '付小小',
+          status: 'reject',
+          updatedAt: '2017-10-03  19:23:12',
+          remark: '不通过原因'
+        },
+        {
+          key: 'op3',
+          type: '部门初审',
+          name: '周毛毛',
+          status: 'agree',
+          updatedAt: '2017-10-03  19:23:12',
+          remark: '-'
+        },
+        {
+          key: 'op4',
+          type: '提交订单',
+          name: '林东东',
+          status: 'agree',
+          updatedAt: '2017-10-03  19:23:12',
+          remark: '很棒'
+        },
+        {
+          key: 'op5',
+          type: '创建订单',
+          name: '汗牙牙',
+          status: 'agree',
+          updatedAt: '2017-10-03  19:23:12',
+          remark: '-'
+        }
+      ],
+      operation2: [
+        {
+          key: 'op2',
+          type: '财务复审',
+          name: '付小小',
+          status: 'reject',
+          updatedAt: '2017-10-03  19:23:12',
+          remark: '不通过原因'
+        },
+        {
+          key: 'op3',
+          type: '部门初审',
+          name: '周毛毛',
+          status: 'agree',
+          updatedAt: '2017-10-03  19:23:12',
+          remark: '-'
+        },
+        {
+          key: 'op4',
+          type: '提交订单',
+          name: '林东东',
+          status: 'agree',
+          updatedAt: '2017-10-03  19:23:12',
+          remark: '很棒'
+        }
+      ],
+      operation3: [
+        {
+          key: 'op2',
+          type: '财务复审',
+          name: '付小小',
+          status: 'reject',
+          updatedAt: '2017-10-03  19:23:12',
+          remark: '不通过原因'
+        },
+        {
+          key: 'op3',
+          type: '部门初审',
+          name: '周毛毛',
+          status: 'agree',
+          updatedAt: '2017-10-03  19:23:12',
+          remark: '-'
+        }
+      ]
+    }
+  },
+  filters: {
+    statusFilter (status) {
+      const statusMap = {
+        'agree': '成功',
+        'reject': '驳回'
+      }
+      return statusMap[status]
+    },
+    statusTypeFilter (type) {
+      const statusTypeMap = {
+        'agree': 'success',
+        'reject': 'error'
+      }
+      return statusTypeMap[type]
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+
+  .detail-layout {
+    margin-left: 44px;
+  }
+  .text {
+    color: rgba(0, 0, 0, .45);
+  }
+
+  .heading {
+    color: rgba(0, 0, 0, .85);
+    font-size: 20px;
+  }
+
+  .no-data {
+    color: rgba(0, 0, 0, .25);
+    text-align: center;
+    line-height: 64px;
+    font-size: 16px;
+
+    i {
+      font-size: 24px;
+      margin-right: 16px;
+      position: relative;
+      top: 3px;
+    }
+  }
+
+  .mobile {
+    .detail-layout {
+      margin-left: unset;
+    }
+    .text {
+
+    }
+    .status-list {
+      text-align: left;
+    }
+  }
+</style>

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

@@ -1,255 +1,255 @@
-<template>
-  <page-view :title="title">
-    <a-card :bordered="false">
-      <detail-list title="退款申请">
-        <detail-list-item term="取货单号">1000000000</detail-list-item>
-        <detail-list-item term="状态">已取货</detail-list-item>
-        <detail-list-item term="销售单号">1234123421</detail-list-item>
-        <detail-list-item term="子订单">3214321432</detail-list-item>
-      </detail-list>
-      <a-divider style="margin-bottom: 32px"/>
-      <detail-list title="用户信息">
-        <detail-list-item term="用户姓名">付小小</detail-list-item>
-        <detail-list-item term="联系电话">18100000000</detail-list-item>
-        <detail-list-item term="常用快递">菜鸟仓储</detail-list-item>
-        <detail-list-item term="取货地址">浙江省杭州市西湖区万塘路18号</detail-list-item>
-        <detail-list-item term="备注">	无</detail-list-item>
-      </detail-list>
-      <a-divider style="margin-bottom: 32px"/>
-
-      <div class="title">退货商品</div>
-      <s-table
-        style="margin-bottom: 24px"
-        row-key="id"
-        :columns="goodsColumns"
-        :data="loadGoodsData">
-
-      </s-table>
-
-      <div class="title">退货进度</div>
-      <s-table
-        style="margin-bottom: 24px"
-        row-key="key"
-        :columns="scheduleColumns"
-        :data="loadScheduleData">
-
-        <template
-          slot="status"
-          slot-scope="status">
-          <a-badge :status="status" :text="status | statusFilter"/>
-        </template>
-
-      </s-table>
-    </a-card>
-  </page-view>
-</template>
-
-<script>
-import { PageView } from '@/layouts'
-import { STable } from '@/components'
-import DetailList from '@/components/tools/DetailList'
-const DetailListItem = DetailList.Item
-
-export default {
-  components: {
-    PageView,
-    DetailList,
-    DetailListItem,
-    STable
-  },
-  data () {
-    return {
-      goodsColumns: [
-        {
-          title: '商品编号',
-          dataIndex: 'id',
-          key: 'id'
-        },
-        {
-          title: '商品名称',
-          dataIndex: 'name',
-          key: 'name'
-        },
-        {
-          title: '商品条码',
-          dataIndex: 'barcode',
-          key: 'barcode'
-        },
-        {
-          title: '单价',
-          dataIndex: 'price',
-          key: 'price',
-          align: 'right'
-        },
-        {
-          title: '数量(件)',
-          dataIndex: 'num',
-          key: 'num',
-          align: 'right'
-        },
-        {
-          title: '金额',
-          dataIndex: 'amount',
-          key: 'amount',
-          align: 'right'
-        }
-      ],
-      // 加载数据方法 必须为 Promise 对象
-      loadGoodsData: () => {
-        return new Promise(resolve => {
-          resolve({
-            data: [
-              {
-                id: '1234561',
-                name: '矿泉水 550ml',
-                barcode: '12421432143214321',
-                price: '2.00',
-                num: '1',
-                amount: '2.00'
-              },
-              {
-                id: '1234562',
-                name: '凉茶 300ml',
-                barcode: '12421432143214322',
-                price: '3.00',
-                num: '2',
-                amount: '6.00'
-              },
-              {
-                id: '1234563',
-                name: '好吃的薯片',
-                barcode: '12421432143214323',
-                price: '7.00',
-                num: '4',
-                amount: '28.00'
-              },
-              {
-                id: '1234564',
-                name: '特别好吃的蛋卷',
-                barcode: '12421432143214324',
-                price: '8.50',
-                num: '3',
-                amount: '25.50'
-              }
-            ],
-            pageSize: 10,
-            pageNo: 1,
-            totalPage: 1,
-            totalCount: 10
-          })
-        }).then(res => {
-          return res
-        })
-      },
-
-      scheduleColumns: [
-        {
-          title: '时间',
-          dataIndex: 'time',
-          key: 'time'
-        },
-        {
-          title: '当前进度',
-          dataIndex: 'rate',
-          key: 'rate'
-        },
-        {
-          title: '状态',
-          dataIndex: 'status',
-          key: 'status',
-          scopedSlots: { customRender: 'status' }
-        },
-        {
-          title: '操作员ID',
-          dataIndex: 'operator',
-          key: 'operator'
-        },
-        {
-          title: '耗时',
-          dataIndex: 'cost',
-          key: 'cost'
-        }
-      ],
-      loadScheduleData: () => {
-        return new Promise(resolve => {
-          resolve({
-            data: [
-              {
-                key: '1',
-                time: '2017-10-01 14:10',
-                rate: '联系客户',
-                status: 'processing',
-                operator: '取货员 ID1234',
-                cost: '5mins'
-              },
-              {
-                key: '2',
-                time: '2017-10-01 14:05',
-                rate: '取货员出发',
-                status: 'success',
-                operator: '取货员 ID1234',
-                cost: '1h'
-              },
-              {
-                key: '3',
-                time: '2017-10-01 13:05',
-                rate: '取货员接单',
-                status: 'success',
-                operator: '取货员 ID1234',
-                cost: '5mins'
-              },
-              {
-                key: '4',
-                time: '2017-10-01 13:00',
-                rate: '申请审批通过',
-                status: 'success',
-                operator: '系统',
-                cost: '1h'
-              },
-              {
-                key: '5',
-                time: '2017-10-01 12:00',
-                rate: '发起退货申请',
-                status: 'success',
-                operator: '用户',
-                cost: '5mins'
-              }
-            ],
-            pageSize: 10,
-            pageNo: 1,
-            totalPage: 1,
-            totalCount: 10
-          })
-        }).then(res => {
-          return res
-        })
-      }
-    }
-  },
-  filters: {
-    statusFilter (status) {
-      const statusMap = {
-        'processing': '进行中',
-        'success': '完成',
-        'failed': '失败'
-      }
-      return statusMap[status]
-    }
-  },
-  computed: {
-    title () {
-      return this.$route.meta.title
-    }
-  }
-
-}
-</script>
-
-<style lang="less" scoped>
-  .title {
-    color: rgba(0,0,0,.85);
-    font-size: 16px;
-    font-weight: 500;
-    margin-bottom: 16px;
-  }
-</style>
+<template>
+  <page-view :title="title">
+    <a-card :bordered="false">
+      <detail-list title="退款申请">
+        <detail-list-item term="取货单号">1000000000</detail-list-item>
+        <detail-list-item term="状态">已取货</detail-list-item>
+        <detail-list-item term="销售单号">1234123421</detail-list-item>
+        <detail-list-item term="子订单">3214321432</detail-list-item>
+      </detail-list>
+      <a-divider style="margin-bottom: 32px"/>
+      <detail-list title="用户信息">
+        <detail-list-item term="用户姓名">付小小</detail-list-item>
+        <detail-list-item term="联系电话">18100000000</detail-list-item>
+        <detail-list-item term="常用快递">菜鸟仓储</detail-list-item>
+        <detail-list-item term="取货地址">浙江省杭州市西湖区万塘路18号</detail-list-item>
+        <detail-list-item term="备注">	无</detail-list-item>
+      </detail-list>
+      <a-divider style="margin-bottom: 32px"/>
+
+      <div class="title">退货商品</div>
+      <s-table
+        style="margin-bottom: 24px"
+        row-key="id"
+        :columns="goodsColumns"
+        :data="loadGoodsData">
+
+      </s-table>
+
+      <div class="title">退货进度</div>
+      <s-table
+        style="margin-bottom: 24px"
+        row-key="key"
+        :columns="scheduleColumns"
+        :data="loadScheduleData">
+
+        <template
+          slot="status"
+          slot-scope="status">
+          <a-badge :status="status" :text="status | statusFilter"/>
+        </template>
+
+      </s-table>
+    </a-card>
+  </page-view>
+</template>
+
+<script>
+import { PageView } from '@/layouts'
+import { STable } from '@/components'
+import DetailList from '@/components/tools/DetailList'
+const DetailListItem = DetailList.Item
+
+export default {
+  components: {
+    PageView,
+    DetailList,
+    DetailListItem,
+    STable
+  },
+  data () {
+    return {
+      goodsColumns: [
+        {
+          title: '商品编号',
+          dataIndex: 'id',
+          key: 'id'
+        },
+        {
+          title: '商品名称',
+          dataIndex: 'name',
+          key: 'name'
+        },
+        {
+          title: '商品条码',
+          dataIndex: 'barcode',
+          key: 'barcode'
+        },
+        {
+          title: '单价',
+          dataIndex: 'price',
+          key: 'price',
+          align: 'right'
+        },
+        {
+          title: '数量(件)',
+          dataIndex: 'num',
+          key: 'num',
+          align: 'right'
+        },
+        {
+          title: '金额',
+          dataIndex: 'amount',
+          key: 'amount',
+          align: 'right'
+        }
+      ],
+      // 加载数据方法 必须为 Promise 对象
+      loadGoodsData: () => {
+        return new Promise(resolve => {
+          resolve({
+            data: [
+              {
+                id: '1234561',
+                name: '矿泉水 550ml',
+                barcode: '12421432143214321',
+                price: '2.00',
+                num: '1',
+                amount: '2.00'
+              },
+              {
+                id: '1234562',
+                name: '凉茶 300ml',
+                barcode: '12421432143214322',
+                price: '3.00',
+                num: '2',
+                amount: '6.00'
+              },
+              {
+                id: '1234563',
+                name: '好吃的薯片',
+                barcode: '12421432143214323',
+                price: '7.00',
+                num: '4',
+                amount: '28.00'
+              },
+              {
+                id: '1234564',
+                name: '特别好吃的蛋卷',
+                barcode: '12421432143214324',
+                price: '8.50',
+                num: '3',
+                amount: '25.50'
+              }
+            ],
+            pageSize: 10,
+            pageNo: 1,
+            totalPage: 1,
+            totalCount: 10
+          })
+        }).then(res => {
+          return res
+        })
+      },
+
+      scheduleColumns: [
+        {
+          title: '时间',
+          dataIndex: 'time',
+          key: 'time'
+        },
+        {
+          title: '当前进度',
+          dataIndex: 'rate',
+          key: 'rate'
+        },
+        {
+          title: '状态',
+          dataIndex: 'status',
+          key: 'status',
+          scopedSlots: { customRender: 'status' }
+        },
+        {
+          title: '操作员ID',
+          dataIndex: 'operator',
+          key: 'operator'
+        },
+        {
+          title: '耗时',
+          dataIndex: 'cost',
+          key: 'cost'
+        }
+      ],
+      loadScheduleData: () => {
+        return new Promise(resolve => {
+          resolve({
+            data: [
+              {
+                key: '1',
+                time: '2017-10-01 14:10',
+                rate: '联系客户',
+                status: 'processing',
+                operator: '取货员 ID1234',
+                cost: '5mins'
+              },
+              {
+                key: '2',
+                time: '2017-10-01 14:05',
+                rate: '取货员出发',
+                status: 'success',
+                operator: '取货员 ID1234',
+                cost: '1h'
+              },
+              {
+                key: '3',
+                time: '2017-10-01 13:05',
+                rate: '取货员接单',
+                status: 'success',
+                operator: '取货员 ID1234',
+                cost: '5mins'
+              },
+              {
+                key: '4',
+                time: '2017-10-01 13:00',
+                rate: '申请审批通过',
+                status: 'success',
+                operator: '系统',
+                cost: '1h'
+              },
+              {
+                key: '5',
+                time: '2017-10-01 12:00',
+                rate: '发起退货申请',
+                status: 'success',
+                operator: '用户',
+                cost: '5mins'
+              }
+            ],
+            pageSize: 10,
+            pageNo: 1,
+            totalPage: 1,
+            totalCount: 10
+          })
+        }).then(res => {
+          return res
+        })
+      }
+    }
+  },
+  filters: {
+    statusFilter (status) {
+      const statusMap = {
+        'processing': '进行中',
+        'success': '完成',
+        'failed': '失败'
+      }
+      return statusMap[status]
+    }
+  },
+  computed: {
+    title () {
+      return this.$route.meta.title
+    }
+  }
+
+}
+</script>
+
+<style lang="less" scoped>
+  .title {
+    color: rgba(0,0,0,.85);
+    font-size: 16px;
+    font-weight: 500;
+    margin-bottom: 16px;
+  }
+</style>

+ 317 - 317
src/views/user/Login.vue

@@ -1,317 +1,317 @@
-<template>
-  <div class="main">
-    <a-form
-      id="formLogin"
-      class="user-layout-login"
-      ref="formLogin"
-      :form="form"
-      @submit="handleSubmit"
-    >
-      <a-tabs
-        :activeKey="customActiveKey"
-        :tabBarStyle="{ textAlign: 'center', borderBottom: 'unset' }"
-        @change="handleTabClick"
-      >
-        <a-tab-pane key="tab1" tab="账号密码登录">
-          <a-form-item>
-            <a-input
-              size="large"
-              type="text"
-              placeholder="账户: admin"
-              v-decorator="[
-                'username',
-                {rules: [{ required: true, message: '请输入帐户名或邮箱地址' }, { validator: handleUsernameOrEmail }], validateTrigger: 'change'}
-              ]"
-            >
-              <a-icon slot="prefix" type="user" :style="{ color: 'rgba(0,0,0,.25)' }"/>
-            </a-input>
-          </a-form-item>
-
-          <a-form-item>
-            <a-input
-              size="large"
-              type="password"
-              autocomplete="false"
-              placeholder="密码: admin or ant.design"
-              v-decorator="[
-                'password',
-                {rules: [{ required: true, message: '请输入密码' }], validateTrigger: 'blur'}
-              ]"
-            >
-              <a-icon slot="prefix" type="lock" :style="{ color: 'rgba(0,0,0,.25)' }"/>
-            </a-input>
-          </a-form-item>
-        </a-tab-pane>
-        <a-tab-pane key="tab2" tab="手机号登录">
-          <a-form-item>
-            <a-input size="large" type="text" placeholder="手机号" v-decorator="['mobile', {rules: [{ required: true, pattern: /^1[34578]\d{9}$/, message: '请输入正确的手机号' }], validateTrigger: 'change'}]">
-              <a-icon slot="prefix" type="mobile" :style="{ color: 'rgba(0,0,0,.25)' }"/>
-            </a-input>
-          </a-form-item>
-
-          <a-row :gutter="16">
-            <a-col class="gutter-row" :span="16">
-              <a-form-item>
-                <a-input size="large" type="text" placeholder="验证码" v-decorator="['captcha', {rules: [{ required: true, message: '请输入验证码' }], validateTrigger: 'blur'}]">
-                  <a-icon slot="prefix" type="mail" :style="{ color: 'rgba(0,0,0,.25)' }"/>
-                </a-input>
-              </a-form-item>
-            </a-col>
-            <a-col class="gutter-row" :span="8">
-              <a-button
-                class="getCaptcha"
-                tabindex="-1"
-                :disabled="state.smsSendBtn"
-                @click.stop.prevent="getCaptcha"
-                v-text="!state.smsSendBtn && '获取验证码' || (state.time+' s')"
-              ></a-button>
-            </a-col>
-          </a-row>
-        </a-tab-pane>
-      </a-tabs>
-
-      <a-form-item>
-        <a-checkbox v-decorator="['rememberMe']">自动登录</a-checkbox>
-        <router-link
-          :to="{ name: 'recover', params: { user: 'aaa'} }"
-          class="forge-password"
-          style="float: right;"
-        >忘记密码</router-link>
-      </a-form-item>
-
-      <a-form-item style="margin-top:24px">
-        <a-button
-          size="large"
-          type="primary"
-          htmlType="submit"
-          class="login-button"
-          :loading="state.loginBtn"
-          :disabled="state.loginBtn"
-        >确定</a-button>
-      </a-form-item>
-
-      <div class="user-login-other">
-        <span>其他登录方式</span>
-        <a>
-          <a-icon class="item-icon" type="alipay-circle"></a-icon>
-        </a>
-        <a>
-          <a-icon class="item-icon" type="taobao-circle"></a-icon>
-        </a>
-        <a>
-          <a-icon class="item-icon" type="weibo-circle"></a-icon>
-        </a>
-        <router-link class="register" :to="{ name: 'register' }">注册账户</router-link>
-      </div>
-    </a-form>
-
-    <two-step-captcha
-      v-if="requiredTwoStepCaptcha"
-      :visible="stepCaptchaVisible"
-      @success="stepCaptchaSuccess"
-      @cancel="stepCaptchaCancel"
-    ></two-step-captcha>
-  </div>
-</template>
-
-<script>
-import md5 from 'md5'
-import TwoStepCaptcha from '@/components/tools/TwoStepCaptcha'
-import { mapActions } from 'vuex'
-import { timeFix } from '@/utils/util'
-import { getSmsCaptcha, get2step } from '@/api/login'
-
-export default {
-  components: {
-    TwoStepCaptcha
-  },
-  data () {
-    return {
-      customActiveKey: 'tab1',
-      loginBtn: false,
-      // login type: 0 email, 1 username, 2 telephone
-      loginType: 0,
-      requiredTwoStepCaptcha: false,
-      stepCaptchaVisible: false,
-      form: this.$form.createForm(this),
-      state: {
-        time: 60,
-        loginBtn: false,
-        // login type: 0 email, 1 username, 2 telephone
-        loginType: 0,
-        smsSendBtn: false
-      }
-    }
-  },
-  created () {
-    get2step({ })
-      .then(res => {
-        this.requiredTwoStepCaptcha = res.result.stepCode
-      })
-      .catch(() => {
-        this.requiredTwoStepCaptcha = false
-      })
-    // this.requiredTwoStepCaptcha = true
-  },
-  methods: {
-    ...mapActions(['Login', 'Logout']),
-    // handler
-    handleUsernameOrEmail (rule, value, callback) {
-      const { state } = this
-      const regex = /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((\.[a-zA-Z0-9_-]{2,3}){1,2})$/
-      if (regex.test(value)) {
-        state.loginType = 0
-      } else {
-        state.loginType = 1
-      }
-      callback()
-    },
-    handleTabClick (key) {
-      this.customActiveKey = key
-      // this.form.resetFields()
-    },
-    handleSubmit (e) {
-      e.preventDefault()
-      const {
-        form: { validateFields },
-        state,
-        customActiveKey,
-        Login
-      } = this
-
-      state.loginBtn = true
-
-      const validateFieldsKey = customActiveKey === 'tab1' ? ['username', 'password'] : ['mobile', 'captcha']
-
-      validateFields(validateFieldsKey, { force: true }, (err, values) => {
-        if (!err) {
-          console.log('login form', values)
-          const loginParams = { ...values }
-          delete loginParams.username
-          loginParams[!state.loginType ? 'email' : 'username'] = values.username
-          loginParams.password = md5(values.password)
-          Login(loginParams)
-            .then((res) => this.loginSuccess(res))
-            .catch(err => this.requestFailed(err))
-            .finally(() => {
-              state.loginBtn = false
-            })
-        } else {
-          setTimeout(() => {
-            state.loginBtn = false
-          }, 600)
-        }
-      })
-    },
-    getCaptcha (e) {
-      e.preventDefault()
-      const { form: { validateFields }, state } = this
-
-      validateFields(['mobile'], { force: true }, (err, values) => {
-        if (!err) {
-          state.smsSendBtn = true
-
-          const interval = window.setInterval(() => {
-            if (state.time-- <= 0) {
-              state.time = 60
-              state.smsSendBtn = false
-              window.clearInterval(interval)
-            }
-          }, 1000)
-
-          const hide = this.$message.loading('验证码发送中..', 0)
-          getSmsCaptcha({ mobile: values.mobile }).then(res => {
-            setTimeout(hide, 2500)
-            this.$notification['success']({
-              message: '提示',
-              description: '验证码获取成功,您的验证码为:' + res.result.captcha,
-              duration: 8
-            })
-          }).catch(err => {
-            setTimeout(hide, 1)
-            clearInterval(interval)
-            state.time = 60
-            state.smsSendBtn = false
-            this.requestFailed(err)
-          })
-        }
-      })
-    },
-    stepCaptchaSuccess () {
-      this.loginSuccess()
-    },
-    stepCaptchaCancel () {
-      this.Logout().then(() => {
-        this.loginBtn = false
-        this.stepCaptchaVisible = false
-      })
-    },
-    loginSuccess (res) {
-      console.log(res)
-      this.$router.push({ name: 'dashboard' })
-      // 延迟 1 秒显示欢迎信息
-      setTimeout(() => {
-        this.$notification.success({
-          message: '欢迎',
-          description: `${timeFix()},欢迎回来`
-        })
-      }, 1000)
-    },
-    requestFailed (err) {
-      this.$notification['error']({
-        message: '错误',
-        description: ((err.response || {}).data || {}).message || '请求出现错误,请稍后再试',
-        duration: 4
-      })
-    }
-  }
-}
-</script>
-
-<style lang="less" scoped>
-.user-layout-login {
-  label {
-    font-size: 14px;
-  }
-
-  .getCaptcha {
-    display: block;
-    width: 100%;
-    height: 40px;
-  }
-
-  .forge-password {
-    font-size: 14px;
-  }
-
-  button.login-button {
-    padding: 0 15px;
-    font-size: 16px;
-    height: 40px;
-    width: 100%;
-  }
-
-  .user-login-other {
-    text-align: left;
-    margin-top: 24px;
-    line-height: 22px;
-
-    .item-icon {
-      font-size: 24px;
-      color: rgba(0, 0, 0, 0.2);
-      margin-left: 16px;
-      vertical-align: middle;
-      cursor: pointer;
-      transition: color 0.3s;
-
-      &:hover {
-        color: #1890ff;
-      }
-    }
-
-    .register {
-      float: right;
-    }
-  }
-}
-</style>
+<template>
+  <div class="main">
+    <a-form
+      id="formLogin"
+      class="user-layout-login"
+      ref="formLogin"
+      :form="form"
+      @submit="handleSubmit"
+    >
+      <a-tabs
+        :activeKey="customActiveKey"
+        :tabBarStyle="{ textAlign: 'center', borderBottom: 'unset' }"
+        @change="handleTabClick"
+      >
+        <a-tab-pane key="tab1" tab="账号密码登录">
+          <a-form-item>
+            <a-input
+              size="large"
+              type="text"
+              placeholder="账户: admin"
+              v-decorator="[
+                'username',
+                {rules: [{ required: true, message: '请输入帐户名或邮箱地址' }, { validator: handleUsernameOrEmail }], validateTrigger: 'change'}
+              ]"
+            >
+              <a-icon slot="prefix" type="user" :style="{ color: 'rgba(0,0,0,.25)' }"/>
+            </a-input>
+          </a-form-item>
+
+          <a-form-item>
+            <a-input
+              size="large"
+              type="password"
+              autocomplete="false"
+              placeholder="密码: admin or ant.design"
+              v-decorator="[
+                'password',
+                {rules: [{ required: true, message: '请输入密码' }], validateTrigger: 'blur'}
+              ]"
+            >
+              <a-icon slot="prefix" type="lock" :style="{ color: 'rgba(0,0,0,.25)' }"/>
+            </a-input>
+          </a-form-item>
+        </a-tab-pane>
+        <a-tab-pane key="tab2" tab="手机号登录">
+          <a-form-item>
+            <a-input size="large" type="text" placeholder="手机号" v-decorator="['mobile', {rules: [{ required: true, pattern: /^1[34578]\d{9}$/, message: '请输入正确的手机号' }], validateTrigger: 'change'}]">
+              <a-icon slot="prefix" type="mobile" :style="{ color: 'rgba(0,0,0,.25)' }"/>
+            </a-input>
+          </a-form-item>
+
+          <a-row :gutter="16">
+            <a-col class="gutter-row" :span="16">
+              <a-form-item>
+                <a-input size="large" type="text" placeholder="验证码" v-decorator="['captcha', {rules: [{ required: true, message: '请输入验证码' }], validateTrigger: 'blur'}]">
+                  <a-icon slot="prefix" type="mail" :style="{ color: 'rgba(0,0,0,.25)' }"/>
+                </a-input>
+              </a-form-item>
+            </a-col>
+            <a-col class="gutter-row" :span="8">
+              <a-button
+                class="getCaptcha"
+                tabindex="-1"
+                :disabled="state.smsSendBtn"
+                @click.stop.prevent="getCaptcha"
+                v-text="!state.smsSendBtn && '获取验证码' || (state.time+' s')"
+              ></a-button>
+            </a-col>
+          </a-row>
+        </a-tab-pane>
+      </a-tabs>
+
+      <a-form-item>
+        <a-checkbox v-decorator="['rememberMe']">自动登录</a-checkbox>
+        <router-link
+          :to="{ name: 'recover', params: { user: 'aaa'} }"
+          class="forge-password"
+          style="float: right;"
+        >忘记密码</router-link>
+      </a-form-item>
+
+      <a-form-item style="margin-top:24px">
+        <a-button
+          size="large"
+          type="primary"
+          htmlType="submit"
+          class="login-button"
+          :loading="state.loginBtn"
+          :disabled="state.loginBtn"
+        >确定</a-button>
+      </a-form-item>
+
+      <div class="user-login-other">
+        <span>其他登录方式</span>
+        <a>
+          <a-icon class="item-icon" type="alipay-circle"></a-icon>
+        </a>
+        <a>
+          <a-icon class="item-icon" type="taobao-circle"></a-icon>
+        </a>
+        <a>
+          <a-icon class="item-icon" type="weibo-circle"></a-icon>
+        </a>
+        <router-link class="register" :to="{ name: 'register' }">注册账户</router-link>
+      </div>
+    </a-form>
+
+    <two-step-captcha
+      v-if="requiredTwoStepCaptcha"
+      :visible="stepCaptchaVisible"
+      @success="stepCaptchaSuccess"
+      @cancel="stepCaptchaCancel"
+    ></two-step-captcha>
+  </div>
+</template>
+
+<script>
+import md5 from 'md5'
+import TwoStepCaptcha from '@/components/tools/TwoStepCaptcha'
+import { mapActions } from 'vuex'
+import { timeFix } from '@/utils/util'
+import { getSmsCaptcha, get2step } from '@/api/login'
+
+export default {
+  components: {
+    TwoStepCaptcha
+  },
+  data () {
+    return {
+      customActiveKey: 'tab1',
+      loginBtn: false,
+      // login type: 0 email, 1 username, 2 telephone
+      loginType: 0,
+      requiredTwoStepCaptcha: false,
+      stepCaptchaVisible: false,
+      form: this.$form.createForm(this),
+      state: {
+        time: 60,
+        loginBtn: false,
+        // login type: 0 email, 1 username, 2 telephone
+        loginType: 0,
+        smsSendBtn: false
+      }
+    }
+  },
+  created () {
+    get2step({ })
+      .then(res => {
+        this.requiredTwoStepCaptcha = res.result.stepCode
+      })
+      .catch(() => {
+        this.requiredTwoStepCaptcha = false
+      })
+    // this.requiredTwoStepCaptcha = true
+  },
+  methods: {
+    ...mapActions(['Login', 'Logout']),
+    // handler
+    handleUsernameOrEmail (rule, value, callback) {
+      const { state } = this
+      const regex = /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((\.[a-zA-Z0-9_-]{2,3}){1,2})$/
+      if (regex.test(value)) {
+        state.loginType = 0
+      } else {
+        state.loginType = 1
+      }
+      callback()
+    },
+    handleTabClick (key) {
+      this.customActiveKey = key
+      // this.form.resetFields()
+    },
+    handleSubmit (e) {
+      e.preventDefault()
+      const {
+        form: { validateFields },
+        state,
+        customActiveKey,
+        Login
+      } = this
+
+      state.loginBtn = true
+
+      const validateFieldsKey = customActiveKey === 'tab1' ? ['username', 'password'] : ['mobile', 'captcha']
+
+      validateFields(validateFieldsKey, { force: true }, (err, values) => {
+        if (!err) {
+          console.log('login form', values)
+          const loginParams = { ...values }
+          delete loginParams.username
+          loginParams[!state.loginType ? 'email' : 'username'] = values.username
+          loginParams.password = md5(values.password)
+          Login(loginParams)
+            .then((res) => this.loginSuccess(res))
+            .catch(err => this.requestFailed(err))
+            .finally(() => {
+              state.loginBtn = false
+            })
+        } else {
+          setTimeout(() => {
+            state.loginBtn = false
+          }, 600)
+        }
+      })
+    },
+    getCaptcha (e) {
+      e.preventDefault()
+      const { form: { validateFields }, state } = this
+
+      validateFields(['mobile'], { force: true }, (err, values) => {
+        if (!err) {
+          state.smsSendBtn = true
+
+          const interval = window.setInterval(() => {
+            if (state.time-- <= 0) {
+              state.time = 60
+              state.smsSendBtn = false
+              window.clearInterval(interval)
+            }
+          }, 1000)
+
+          const hide = this.$message.loading('验证码发送中..', 0)
+          getSmsCaptcha({ mobile: values.mobile }).then(res => {
+            setTimeout(hide, 2500)
+            this.$notification['success']({
+              message: '提示',
+              description: '验证码获取成功,您的验证码为:' + res.result.captcha,
+              duration: 8
+            })
+          }).catch(err => {
+            setTimeout(hide, 1)
+            clearInterval(interval)
+            state.time = 60
+            state.smsSendBtn = false
+            this.requestFailed(err)
+          })
+        }
+      })
+    },
+    stepCaptchaSuccess () {
+      this.loginSuccess()
+    },
+    stepCaptchaCancel () {
+      this.Logout().then(() => {
+        this.loginBtn = false
+        this.stepCaptchaVisible = false
+      })
+    },
+    loginSuccess (res) {
+      console.log(res)
+      this.$router.push({ name: 'dashboard' })
+      // 延迟 1 秒显示欢迎信息
+      setTimeout(() => {
+        this.$notification.success({
+          message: '欢迎',
+          description: `${timeFix()},欢迎回来`
+        })
+      }, 1000)
+    },
+    requestFailed (err) {
+      this.$notification['error']({
+        message: '错误',
+        description: ((err.response || {}).data || {}).message || '请求出现错误,请稍后再试',
+        duration: 4
+      })
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+.user-layout-login {
+  label {
+    font-size: 14px;
+  }
+
+  .getCaptcha {
+    display: block;
+    width: 100%;
+    height: 40px;
+  }
+
+  .forge-password {
+    font-size: 14px;
+  }
+
+  button.login-button {
+    padding: 0 15px;
+    font-size: 16px;
+    height: 40px;
+    width: 100%;
+  }
+
+  .user-login-other {
+    text-align: left;
+    margin-top: 24px;
+    line-height: 22px;
+
+    .item-icon {
+      font-size: 24px;
+      color: rgba(0, 0, 0, 0.2);
+      margin-left: 16px;
+      vertical-align: middle;
+      cursor: pointer;
+      transition: color 0.3s;
+
+      &:hover {
+        color: #1890ff;
+      }
+    }
+
+    .register {
+      float: right;
+    }
+  }
+}
+</style>

+ 318 - 318
src/views/user/Register.vue

@@ -1,318 +1,318 @@
-<template>
-  <div class="main user-layout-register">
-    <h3><span>注册</span></h3>
-    <a-form ref="formRegister" :form="form" id="formRegister">
-      <a-form-item>
-        <a-input
-          size="large"
-          type="text"
-          placeholder="邮箱"
-          v-decorator="['email', {rules: [{ required: true, type: 'email', message: '请输入邮箱地址' }], validateTrigger: ['change', 'blur']}]"
-        ></a-input>
-      </a-form-item>
-
-      <a-popover placement="rightTop" trigger="click" :visible="state.passwordLevelChecked">
-        <template slot="content">
-          <div :style="{ width: '240px' }" >
-            <div :class="['user-register', passwordLevelClass]">强度:<span>{{ passwordLevelName }}</span></div>
-            <a-progress :percent="state.percent" :showInfo="false" :strokeColor=" passwordLevelColor " />
-            <div style="margin-top: 10px;">
-              <span>请至少输入 6 个字符。请不要使用容易被猜到的密码。</span>
-            </div>
-          </div>
-        </template>
-        <a-form-item>
-          <a-input
-            size="large"
-            type="password"
-            @click="handlePasswordInputClick"
-            autocomplete="false"
-            placeholder="至少6位密码,区分大小写"
-            v-decorator="['password', {rules: [{ required: true, message: '至少6位密码,区分大小写'}, { validator: this.handlePasswordLevel }], validateTrigger: ['change', 'blur']}]"
-          ></a-input>
-        </a-form-item>
-      </a-popover>
-
-      <a-form-item>
-        <a-input
-          size="large"
-          type="password"
-          autocomplete="false"
-          placeholder="确认密码"
-          v-decorator="['password2', {rules: [{ required: true, message: '至少6位密码,区分大小写' }, { validator: this.handlePasswordCheck }], validateTrigger: ['change', 'blur']}]"
-        ></a-input>
-      </a-form-item>
-
-      <a-form-item>
-        <a-input size="large" placeholder="11 位手机号" v-decorator="['mobile', {rules: [{ required: true, message: '请输入正确的手机号', pattern: /^1[3456789]\d{9}$/ }, { validator: this.handlePhoneCheck } ], validateTrigger: ['change', 'blur'] }]">
-          <a-select slot="addonBefore" size="large" defaultValue="+86">
-            <a-select-option value="+86">+86</a-select-option>
-            <a-select-option value="+87">+87</a-select-option>
-          </a-select>
-        </a-input>
-      </a-form-item>
-      <!--<a-input-group size="large" compact>
-            <a-select style="width: 20%" size="large" defaultValue="+86">
-              <a-select-option value="+86">+86</a-select-option>
-              <a-select-option value="+87">+87</a-select-option>
-            </a-select>
-            <a-input style="width: 80%" size="large" placeholder="11 位手机号"></a-input>
-          </a-input-group>-->
-
-      <a-row :gutter="16">
-        <a-col class="gutter-row" :span="16">
-          <a-form-item>
-            <a-input size="large" type="text" placeholder="验证码" v-decorator="['captcha', {rules: [{ required: true, message: '请输入验证码' }], validateTrigger: 'blur'}]">
-              <a-icon slot="prefix" type="mail" :style="{ color: 'rgba(0,0,0,.25)' }"/>
-            </a-input>
-          </a-form-item>
-        </a-col>
-        <a-col class="gutter-row" :span="8">
-          <a-button
-            class="getCaptcha"
-            size="large"
-            :disabled="state.smsSendBtn"
-            @click.stop.prevent="getCaptcha"
-            v-text="!state.smsSendBtn && '获取验证码'||(state.time+' s')"></a-button>
-        </a-col>
-      </a-row>
-
-      <a-form-item>
-        <a-button
-          size="large"
-          type="primary"
-          htmlType="submit"
-          class="register-button"
-          :loading="registerBtn"
-          @click.stop.prevent="handleSubmit"
-          :disabled="registerBtn">注册
-        </a-button>
-        <router-link class="login" :to="{ name: 'login' }">使用已有账户登录</router-link>
-      </a-form-item>
-
-    </a-form>
-  </div>
-</template>
-
-<script>
-import { mixinDevice } from '@/utils/mixin.js'
-import { getSmsCaptcha } from '@/api/login'
-
-const levelNames = {
-  0: '低',
-  1: '低',
-  2: '中',
-  3: '强'
-}
-const levelClass = {
-  0: 'error',
-  1: 'error',
-  2: 'warning',
-  3: 'success'
-}
-const levelColor = {
-  0: '#ff0000',
-  1: '#ff0000',
-  2: '#ff7e05',
-  3: '#52c41a'
-}
-export default {
-  name: 'Register',
-  components: {
-  },
-  mixins: [mixinDevice],
-  data () {
-    return {
-      form: this.$form.createForm(this),
-
-      state: {
-        time: 60,
-        smsSendBtn: false,
-        passwordLevel: 0,
-        passwordLevelChecked: false,
-        percent: 10,
-        progressColor: '#FF0000'
-      },
-      registerBtn: false
-    }
-  },
-  computed: {
-    passwordLevelClass () {
-      return levelClass[this.state.passwordLevel]
-    },
-    passwordLevelName () {
-      return levelNames[this.state.passwordLevel]
-    },
-    passwordLevelColor () {
-      return levelColor[this.state.passwordLevel]
-    }
-  },
-  methods: {
-
-    handlePasswordLevel (rule, value, callback) {
-      let level = 0
-
-      // 判断这个字符串中有没有数字
-      if (/[0-9]/.test(value)) {
-        level++
-      }
-      // 判断字符串中有没有字母
-      if (/[a-zA-Z]/.test(value)) {
-        level++
-      }
-      // 判断字符串中有没有特殊符号
-      if (/[^0-9a-zA-Z_]/.test(value)) {
-        level++
-      }
-      this.state.passwordLevel = level
-      this.state.percent = level * 30
-      if (level >= 2) {
-        if (level >= 3) {
-          this.state.percent = 100
-        }
-        callback()
-      } else {
-        if (level === 0) {
-          this.state.percent = 10
-        }
-        callback(new Error('密码强度不够'))
-      }
-    },
-
-    handlePasswordCheck (rule, value, callback) {
-      const password = this.form.getFieldValue('password')
-      console.log('value', value)
-      if (value === undefined) {
-        callback(new Error('请输入密码'))
-      }
-      if (value && password && value.trim() !== password.trim()) {
-        callback(new Error('两次密码不一致'))
-      }
-      callback()
-    },
-
-    handlePhoneCheck (rule, value, callback) {
-      console.log('handlePhoneCheck, rule:', rule)
-      console.log('handlePhoneCheck, value', value)
-      console.log('handlePhoneCheck, callback', callback)
-
-      callback()
-    },
-
-    handlePasswordInputClick () {
-      if (!this.isMobile()) {
-        this.state.passwordLevelChecked = true
-        return
-      }
-      this.state.passwordLevelChecked = false
-    },
-
-    handleSubmit () {
-      const { form: { validateFields }, $router } = this
-      validateFields((err, values) => {
-        if (!err) {
-          $router.push({ name: 'registerResult', params: { ...values } })
-        }
-      })
-    },
-
-    getCaptcha (e) {
-      e.preventDefault()
-      const { form: { validateFields }, state, $message, $notification } = this
-
-      validateFields(['mobile'], { force: true },
-        (err, values) => {
-          if (!err) {
-            state.smsSendBtn = true
-
-            const interval = window.setInterval(() => {
-              if (state.time-- <= 0) {
-                state.time = 60
-                state.smsSendBtn = false
-                window.clearInterval(interval)
-              }
-            }, 1000)
-
-            const hide = $message.loading('验证码发送中..', 0)
-
-            getSmsCaptcha({ mobile: values.mobile }).then(res => {
-              setTimeout(hide, 2500)
-              $notification['success']({
-                message: '提示',
-                description: '验证码获取成功,您的验证码为:' + res.result.captcha,
-                duration: 8
-              })
-            }).catch(err => {
-              setTimeout(hide, 1)
-              clearInterval(interval)
-              state.time = 60
-              state.smsSendBtn = false
-              this.requestFailed(err)
-            })
-          }
-        }
-      )
-    },
-    requestFailed (err) {
-      this.$notification['error']({
-        message: '错误',
-        description: ((err.response || {}).data || {}).message || '请求出现错误,请稍后再试',
-        duration: 4
-      })
-      this.registerBtn = false
-    }
-  },
-  watch: {
-    'state.passwordLevel' (val) {
-      console.log(val)
-    }
-  }
-}
-</script>
-<style lang="less">
-  .user-register {
-
-    &.error {
-      color: #ff0000;
-    }
-
-    &.warning {
-      color: #ff7e05;
-    }
-
-    &.success {
-      color: #52c41a;
-    }
-
-  }
-
-  .user-layout-register {
-    .ant-input-group-addon:first-child {
-      background-color: #fff;
-    }
-  }
-</style>
-<style lang="less" scoped>
-  .user-layout-register {
-
-    & > h3 {
-      font-size: 16px;
-      margin-bottom: 20px;
-    }
-
-    .getCaptcha {
-      display: block;
-      width: 100%;
-      height: 40px;
-    }
-
-    .register-button {
-      width: 50%;
-    }
-
-    .login {
-      float: right;
-      line-height: 40px;
-    }
-  }
-</style>
+<template>
+  <div class="main user-layout-register">
+    <h3><span>注册</span></h3>
+    <a-form ref="formRegister" :form="form" id="formRegister">
+      <a-form-item>
+        <a-input
+          size="large"
+          type="text"
+          placeholder="邮箱"
+          v-decorator="['email', {rules: [{ required: true, type: 'email', message: '请输入邮箱地址' }], validateTrigger: ['change', 'blur']}]"
+        ></a-input>
+      </a-form-item>
+
+      <a-popover placement="rightTop" trigger="click" :visible="state.passwordLevelChecked">
+        <template slot="content">
+          <div :style="{ width: '240px' }" >
+            <div :class="['user-register', passwordLevelClass]">强度:<span>{{ passwordLevelName }}</span></div>
+            <a-progress :percent="state.percent" :showInfo="false" :strokeColor=" passwordLevelColor " />
+            <div style="margin-top: 10px;">
+              <span>请至少输入 6 个字符。请不要使用容易被猜到的密码。</span>
+            </div>
+          </div>
+        </template>
+        <a-form-item>
+          <a-input
+            size="large"
+            type="password"
+            @click="handlePasswordInputClick"
+            autocomplete="false"
+            placeholder="至少6位密码,区分大小写"
+            v-decorator="['password', {rules: [{ required: true, message: '至少6位密码,区分大小写'}, { validator: this.handlePasswordLevel }], validateTrigger: ['change', 'blur']}]"
+          ></a-input>
+        </a-form-item>
+      </a-popover>
+
+      <a-form-item>
+        <a-input
+          size="large"
+          type="password"
+          autocomplete="false"
+          placeholder="确认密码"
+          v-decorator="['password2', {rules: [{ required: true, message: '至少6位密码,区分大小写' }, { validator: this.handlePasswordCheck }], validateTrigger: ['change', 'blur']}]"
+        ></a-input>
+      </a-form-item>
+
+      <a-form-item>
+        <a-input size="large" placeholder="11 位手机号" v-decorator="['mobile', {rules: [{ required: true, message: '请输入正确的手机号', pattern: /^1[3456789]\d{9}$/ }, { validator: this.handlePhoneCheck } ], validateTrigger: ['change', 'blur'] }]">
+          <a-select slot="addonBefore" size="large" defaultValue="+86">
+            <a-select-option value="+86">+86</a-select-option>
+            <a-select-option value="+87">+87</a-select-option>
+          </a-select>
+        </a-input>
+      </a-form-item>
+      <!--<a-input-group size="large" compact>
+            <a-select style="width: 20%" size="large" defaultValue="+86">
+              <a-select-option value="+86">+86</a-select-option>
+              <a-select-option value="+87">+87</a-select-option>
+            </a-select>
+            <a-input style="width: 80%" size="large" placeholder="11 位手机号"></a-input>
+          </a-input-group>-->
+
+      <a-row :gutter="16">
+        <a-col class="gutter-row" :span="16">
+          <a-form-item>
+            <a-input size="large" type="text" placeholder="验证码" v-decorator="['captcha', {rules: [{ required: true, message: '请输入验证码' }], validateTrigger: 'blur'}]">
+              <a-icon slot="prefix" type="mail" :style="{ color: 'rgba(0,0,0,.25)' }"/>
+            </a-input>
+          </a-form-item>
+        </a-col>
+        <a-col class="gutter-row" :span="8">
+          <a-button
+            class="getCaptcha"
+            size="large"
+            :disabled="state.smsSendBtn"
+            @click.stop.prevent="getCaptcha"
+            v-text="!state.smsSendBtn && '获取验证码'||(state.time+' s')"></a-button>
+        </a-col>
+      </a-row>
+
+      <a-form-item>
+        <a-button
+          size="large"
+          type="primary"
+          htmlType="submit"
+          class="register-button"
+          :loading="registerBtn"
+          @click.stop.prevent="handleSubmit"
+          :disabled="registerBtn">注册
+        </a-button>
+        <router-link class="login" :to="{ name: 'login' }">使用已有账户登录</router-link>
+      </a-form-item>
+
+    </a-form>
+  </div>
+</template>
+
+<script>
+import { mixinDevice } from '@/utils/mixin.js'
+import { getSmsCaptcha } from '@/api/login'
+
+const levelNames = {
+  0: '低',
+  1: '低',
+  2: '中',
+  3: '强'
+}
+const levelClass = {
+  0: 'error',
+  1: 'error',
+  2: 'warning',
+  3: 'success'
+}
+const levelColor = {
+  0: '#ff0000',
+  1: '#ff0000',
+  2: '#ff7e05',
+  3: '#52c41a'
+}
+export default {
+  name: 'Register',
+  components: {
+  },
+  mixins: [mixinDevice],
+  data () {
+    return {
+      form: this.$form.createForm(this),
+
+      state: {
+        time: 60,
+        smsSendBtn: false,
+        passwordLevel: 0,
+        passwordLevelChecked: false,
+        percent: 10,
+        progressColor: '#FF0000'
+      },
+      registerBtn: false
+    }
+  },
+  computed: {
+    passwordLevelClass () {
+      return levelClass[this.state.passwordLevel]
+    },
+    passwordLevelName () {
+      return levelNames[this.state.passwordLevel]
+    },
+    passwordLevelColor () {
+      return levelColor[this.state.passwordLevel]
+    }
+  },
+  methods: {
+
+    handlePasswordLevel (rule, value, callback) {
+      let level = 0
+
+      // 判断这个字符串中有没有数字
+      if (/[0-9]/.test(value)) {
+        level++
+      }
+      // 判断字符串中有没有字母
+      if (/[a-zA-Z]/.test(value)) {
+        level++
+      }
+      // 判断字符串中有没有特殊符号
+      if (/[^0-9a-zA-Z_]/.test(value)) {
+        level++
+      }
+      this.state.passwordLevel = level
+      this.state.percent = level * 30
+      if (level >= 2) {
+        if (level >= 3) {
+          this.state.percent = 100
+        }
+        callback()
+      } else {
+        if (level === 0) {
+          this.state.percent = 10
+        }
+        callback(new Error('密码强度不够'))
+      }
+    },
+
+    handlePasswordCheck (rule, value, callback) {
+      const password = this.form.getFieldValue('password')
+      console.log('value', value)
+      if (value === undefined) {
+        callback(new Error('请输入密码'))
+      }
+      if (value && password && value.trim() !== password.trim()) {
+        callback(new Error('两次密码不一致'))
+      }
+      callback()
+    },
+
+    handlePhoneCheck (rule, value, callback) {
+      console.log('handlePhoneCheck, rule:', rule)
+      console.log('handlePhoneCheck, value', value)
+      console.log('handlePhoneCheck, callback', callback)
+
+      callback()
+    },
+
+    handlePasswordInputClick () {
+      if (!this.isMobile()) {
+        this.state.passwordLevelChecked = true
+        return
+      }
+      this.state.passwordLevelChecked = false
+    },
+
+    handleSubmit () {
+      const { form: { validateFields }, $router } = this
+      validateFields((err, values) => {
+        if (!err) {
+          $router.push({ name: 'registerResult', params: { ...values } })
+        }
+      })
+    },
+
+    getCaptcha (e) {
+      e.preventDefault()
+      const { form: { validateFields }, state, $message, $notification } = this
+
+      validateFields(['mobile'], { force: true },
+        (err, values) => {
+          if (!err) {
+            state.smsSendBtn = true
+
+            const interval = window.setInterval(() => {
+              if (state.time-- <= 0) {
+                state.time = 60
+                state.smsSendBtn = false
+                window.clearInterval(interval)
+              }
+            }, 1000)
+
+            const hide = $message.loading('验证码发送中..', 0)
+
+            getSmsCaptcha({ mobile: values.mobile }).then(res => {
+              setTimeout(hide, 2500)
+              $notification['success']({
+                message: '提示',
+                description: '验证码获取成功,您的验证码为:' + res.result.captcha,
+                duration: 8
+              })
+            }).catch(err => {
+              setTimeout(hide, 1)
+              clearInterval(interval)
+              state.time = 60
+              state.smsSendBtn = false
+              this.requestFailed(err)
+            })
+          }
+        }
+      )
+    },
+    requestFailed (err) {
+      this.$notification['error']({
+        message: '错误',
+        description: ((err.response || {}).data || {}).message || '请求出现错误,请稍后再试',
+        duration: 4
+      })
+      this.registerBtn = false
+    }
+  },
+  watch: {
+    'state.passwordLevel' (val) {
+      console.log(val)
+    }
+  }
+}
+</script>
+<style lang="less">
+  .user-register {
+
+    &.error {
+      color: #ff0000;
+    }
+
+    &.warning {
+      color: #ff7e05;
+    }
+
+    &.success {
+      color: #52c41a;
+    }
+
+  }
+
+  .user-layout-register {
+    .ant-input-group-addon:first-child {
+      background-color: #fff;
+    }
+  }
+</style>
+<style lang="less" scoped>
+  .user-layout-register {
+
+    & > h3 {
+      font-size: 16px;
+      margin-bottom: 20px;
+    }
+
+    .getCaptcha {
+      display: block;
+      width: 100%;
+      height: 40px;
+    }
+
+    .register-button {
+      width: 50%;
+    }
+
+    .login {
+      float: right;
+      line-height: 40px;
+    }
+  }
+</style>