Pārlūkot izejas kodu

0.0.8

完成模板对应动态路由功能的开发
Sean 6 mēneši atpakaļ
vecāks
revīzija
446514f2ca

+ 8 - 0
src/api/user.js

@@ -34,3 +34,11 @@ export function getMenu(token) {
     //params: { token }
     //params: { token }
   })
   })
 }
 }
+
+//获得图形验证码
+export function getImgCode() {
+  return request({
+    url: '/verifyCode',
+    method: 'get',
+  })
+}

BIN
src/assets/index/twbIconbgBlue.png


BIN
src/assets/index/twbIconbgPurple.png


BIN
src/assets/index/twbIconbgRed.png


BIN
src/assets/public/nav/logo.png


BIN
src/assets/public/sidebar/default/2 User.png


BIN
src/assets/public/sidebar/default/Folder.png


BIN
src/assets/public/sidebar/default/accessibility-outline.png


BIN
src/assets/public/sidebar/default/compass-outline.png


BIN
src/assets/public/sidebar/default/index.png


BIN
src/assets/public/sidebar/select/2 User.png


BIN
src/assets/public/sidebar/select/Discovery.png


+ 12 - 3
src/layout/components/Navbar.vue

@@ -1,9 +1,9 @@
 <template>
 <template>
   <div class="navbar">
   <div class="navbar">
     <div class="pageTitle">平台管理概况</div>
     <div class="pageTitle">平台管理概况</div>
-    <hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
-    <!--收缩左侧菜单-->
+    <!--收缩左侧菜单按钮-->
     <!-- <hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
     <!-- <hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
+    <hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
     <breadcrumb id="breadcrumb-container" class="breadcrumb-container" /> -->
     <breadcrumb id="breadcrumb-container" class="breadcrumb-container" /> -->
     <div class="right-menu">
     <div class="right-menu">
       <!--搜索,全屏,大小写转换和翻译-->
       <!--搜索,全屏,大小写转换和翻译-->
@@ -18,6 +18,7 @@
       </template> -->
       </template> -->
       <el-dropdown class="avatar-container right-menu-item hover-effect" trigger="click">
       <el-dropdown class="avatar-container right-menu-item hover-effect" trigger="click">
         <div class="avatar-wrapper">
         <div class="avatar-wrapper">
+          <img src="@/assets/public/nav/notice.png" class="user-notice">
           <img :src="avatar+'?imageView2/1/w/80/h/80'" class="user-avatar">
           <img :src="avatar+'?imageView2/1/w/80/h/80'" class="user-avatar">
           <span class="userName">{{this.$store.state.user.name}}</span>
           <span class="userName">{{this.$store.state.user.name}}</span>
           <!--向下按钮-->
           <!--向下按钮-->
@@ -96,7 +97,7 @@ export default {
 
 
 <style lang="scss" scoped>
 <style lang="scss" scoped>
 .navbar {
 .navbar {
-  height: 50px;
+  height: 60px;
   overflow: hidden;
   overflow: hidden;
   position: relative;
   position: relative;
   display:flex;
   display:flex;
@@ -182,6 +183,12 @@ export default {
           border-radius: 10px;
           border-radius: 10px;
         }
         }
 
 
+        .user-notice {
+          width: 20px;
+          height: 20px;
+          margin-right:20px;
+        }
+
         .el-icon-caret-bottom {
         .el-icon-caret-bottom {
           cursor: pointer;
           cursor: pointer;
           position: absolute;
           position: absolute;
@@ -193,4 +200,6 @@ export default {
     }
     }
   }
   }
 }
 }
+
+
 </style>
 </style>

+ 11 - 14
src/layout/components/Sidebar/Logo.vue

@@ -2,11 +2,9 @@
   <div class="sidebar-logo-container" :class="{'collapse':collapse}">
   <div class="sidebar-logo-container" :class="{'collapse':collapse}">
     <transition name="sidebarLogoFade">
     <transition name="sidebarLogoFade">
       <router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
       <router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
-        <img v-if="logo" :src="logo" class="sidebar-logo">
-        <h1 v-else class="sidebar-title">{{ title }} </h1>
+        <h1 class="sidebar-title">{{ title }} </h1>
       </router-link>
       </router-link>
       <router-link v-else key="expand" class="sidebar-logo-link" to="/">
       <router-link v-else key="expand" class="sidebar-logo-link" to="/">
-        <img v-if="logo" :src="logo" class="sidebar-logo">
         <h1 class="sidebar-title">{{ title }} </h1>
         <h1 class="sidebar-title">{{ title }} </h1>
       </router-link>
       </router-link>
     </transition>
     </transition>
@@ -24,8 +22,8 @@ export default {
   },
   },
   data() {
   data() {
     return {
     return {
-      title: 'Vue Element Admin',
-      logo: 'https://wpimg.wallstcn.com/69a1c46c-eb1c-4b46-8bd4-e9e686ef5251.png'
+      title: '政讯通运营管理平台',
+      //logo: '/assets/public/nav/logo.png'
     }
     }
   }
   }
 }
 }
@@ -44,19 +42,18 @@ export default {
 .sidebar-logo-container {
 .sidebar-logo-container {
   position: relative;
   position: relative;
   width: 100%;
   width: 100%;
-  height: 50px;
-  line-height: 50px;
-  background: #2b2f3a;
+  height: 104px;
+  line-height: 96px;
+  background: #fff;
   text-align: center;
   text-align: center;
   overflow: hidden;
   overflow: hidden;
 
 
   & .sidebar-logo-link {
   & .sidebar-logo-link {
     height: 100%;
     height: 100%;
     width: 100%;
     width: 100%;
-
     & .sidebar-logo {
     & .sidebar-logo {
-      width: 32px;
-      height: 32px;
+      width: 198px;
+      height: 23px;
       vertical-align: middle;
       vertical-align: middle;
       margin-right: 12px;
       margin-right: 12px;
     }
     }
@@ -64,10 +61,10 @@ export default {
     & .sidebar-title {
     & .sidebar-title {
       display: inline-block;
       display: inline-block;
       margin: 0;
       margin: 0;
-      color: #fff;
-      font-weight: 600;
+      color: #45464E;
+      font-weight: 800;
       line-height: 50px;
       line-height: 50px;
-      font-size: 14px;
+      font-size: 22px;
       font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
       font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
       vertical-align: middle;
       vertical-align: middle;
     }
     }

+ 39 - 29
src/layout/components/Sidebar/SidebarItem.vue

@@ -1,17 +1,24 @@
 <template>
 <template>
   <div v-if="!item.hidden">
   <div v-if="!item.hidden">
-    <template v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow">
+    <!-- 一级菜单 -->
+    <template v-if="hasOneShowingChild(item.children, item) && (!onlyOneChild.children || onlyOneChild.noShowingChildren) && !item.alwaysShow">
       <app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)">
       <app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)">
-        <el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}">
-          <item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="generateTitle(onlyOneChild.meta.title)" />
+        <el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown': !isNest}">
+          <!-- 动态显示图标 -->
+          <img v-if="!isNest && onlyOneChild.meta" :src="getIcon(onlyOneChild)" class="sidebar-icon" />
+          <span>{{ generateTitle(onlyOneChild.meta.title) }}</span>
         </el-menu-item>
         </el-menu-item>
       </app-link>
       </app-link>
     </template>
     </template>
 
 
+    <!-- 子菜单 -->
     <el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body>
     <el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body>
       <template slot="title">
       <template slot="title">
-        <item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="generateTitle(item.meta.title)" />
+        <!-- 动态显示图标 -->
+        <img v-if="!isNest && item.meta" :src="getIcon(item)" class="sidebar-icon" />
+        <span>{{ generateTitle(item.meta.title) }}</span>
       </template>
       </template>
+      <!-- 递归渲染子菜单 -->
       <sidebar-item
       <sidebar-item
         v-for="child in item.children"
         v-for="child in item.children"
         :key="child.path"
         :key="child.path"
@@ -26,18 +33,15 @@
 
 
 <script>
 <script>
 import path from 'path'
 import path from 'path'
-import { generateTitle } from '@/utils/i18n'
 import { isExternal } from '@/utils/validate'
 import { isExternal } from '@/utils/validate'
-import Item from './Item'
 import AppLink from './Link'
 import AppLink from './Link'
 import FixiOSBug from './FixiOSBug'
 import FixiOSBug from './FixiOSBug'
 
 
 export default {
 export default {
   name: 'SidebarItem',
   name: 'SidebarItem',
-  components: { Item, AppLink },
+  components: { AppLink },
   mixins: [FixiOSBug],
   mixins: [FixiOSBug],
   props: {
   props: {
-    // route object
     item: {
     item: {
       type: Object,
       type: Object,
       required: true
       required: true
@@ -52,34 +56,21 @@ export default {
     }
     }
   },
   },
   data() {
   data() {
-    // To fix https://github.com/PanJiaChen/vue-admin-template/issues/237
-    // TODO: refactor with render function
-    this.onlyOneChild = null
-    return {}
+    return {
+      onlyOneChild: null
+    }
   },
   },
   methods: {
   methods: {
     hasOneShowingChild(children = [], parent) {
     hasOneShowingChild(children = [], parent) {
-      const showingChildren = children.filter(item => {
-        if (item.hidden) {
-          return false
-        } else {
-          // Temp set(will be used if only has one showing child)
-          this.onlyOneChild = item
-          return true
-        }
-      })
-
-      // When there is only one child router, the child router is displayed by default
+      const showingChildren = children.filter(item => !item.hidden)
       if (showingChildren.length === 1) {
       if (showingChildren.length === 1) {
+        this.onlyOneChild = showingChildren[0]
         return true
         return true
       }
       }
-
-      // Show parent if there are no child router to display
       if (showingChildren.length === 0) {
       if (showingChildren.length === 0) {
-        this.onlyOneChild = { ... parent, path: '', noShowingChildren: true }
+        this.onlyOneChild = { ...parent, path: '', noShowingChildren: true }
         return true
         return true
       }
       }
-
       return false
       return false
     },
     },
     resolvePath(routePath) {
     resolvePath(routePath) {
@@ -91,8 +82,27 @@ export default {
       }
       }
       return path.resolve(this.basePath, routePath)
       return path.resolve(this.basePath, routePath)
     },
     },
-
-    generateTitle
+    generateTitle(metaTitle) {
+      if (this.$te(metaTitle)) {
+        return this.$t(metaTitle)
+      }
+      return metaTitle || 'No Title'
+    },
+    // 动态获取图标
+    getIcon(item) {
+      return this.$route.path === this.resolvePath(item.path)
+        ? item.meta.iconSelected // 选中状态
+        : item.meta.iconDefault  // 未选中状态
+    }
   }
   }
 }
 }
 </script>
 </script>
+
+<style scoped>
+.sidebar-icon {
+  width: 20px;
+  height: 20px;
+  margin-right: 12px;
+  vertical-align: middle;
+}
+</style>

+ 9 - 1
src/layout/components/Sidebar/index.vue

@@ -41,7 +41,8 @@ export default {
       return path
       return path
     },
     },
     showLogo() {
     showLogo() {
-      return this.$store.state.settings.sidebarLogo
+      //return this.$store.state.settings.sidebarLogo
+      return true;
     },
     },
     variables() {
     variables() {
       return variables
       return variables
@@ -52,3 +53,10 @@ export default {
   }
   }
 }
 }
 </script>
 </script>
+<style lang="less" scoped>
+  .zxtLeftMeut {
+    a.router-link-active {
+      background:#000
+    }
+  }
+</style>

+ 2 - 2
src/layout/components/TagsView/index.vue

@@ -227,9 +227,9 @@ export default {
         margin-right: 15px;
         margin-right: 15px;
       }
       }
       &.active {
       &.active {
-        background-color: #42b983;
+        background-color: #5570F1;
         color: #fff;
         color: #fff;
-        border-color: #42b983;
+        border-color: #5570F1;
         &::before {
         &::before {
           content: '';
           content: '';
           background: #fff;
           background: #fff;

+ 2 - 2
src/layout/index.vue

@@ -8,9 +8,9 @@
         <tags-view v-if="needTagsView" />
         <tags-view v-if="needTagsView" />
       </div>
       </div>
       <app-main />
       <app-main />
-      <right-panel v-if="showSettings">
+      <!-- <right-panel v-if="showSettings">
         <settings />
         <settings />
-      </right-panel>
+      </right-panel> -->
     </div>
     </div>
   </div>
   </div>
 </template>
 </template>

+ 22 - 3
src/router/index.js

@@ -73,13 +73,32 @@ export const constantRoutes = [
   {
   {
     path: '/',
     path: '/',
     component: Layout,
     component: Layout,
-    redirect: '/dashboard',
+    redirect: '/dashboard', //访问/的时候会跳转到dashboard
     children: [
     children: [
       {
       {
         path: 'dashboard',
         path: 'dashboard',
         component: () => import('@/views/dashboard/index'),
         component: () => import('@/views/dashboard/index'),
-        name: 'Dashboard',
-        meta: { title: 'dashboard', icon: 'dashboard', affix: true }
+        name: '首页',
+        meta: {
+          title: '首页',
+          //icon: require('@/assets/public/sidebar/select/index.png'),
+          iconDefault: require('@/assets/public/sidebar/default/index.png'),
+          iconSelected: require('@/assets/public/sidebar/select/index.png'),
+          affix: true ,
+        }
+      }
+    ]
+  },
+  //增加新的路由 站点列表
+  //注意必须含有component:Layout项目否则会导致页面找不到模板
+  //必须含有children中的path且两个path必须一致
+  {
+    path: '/left/websiteList',
+    component: Layout,
+    children: [
+      {
+        path: '/left/websiteList',
+        component: () => import('@/views/system-settings/websiteList')
       }
       }
     ]
     ]
   },
   },

+ 2 - 1
src/store/modules/app.js

@@ -3,7 +3,8 @@ import { getLanguage } from '@/lang/index'
 
 
 const state = {
 const state = {
   sidebar: {
   sidebar: {
-    opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true,
+    //opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true,
+    opened:true,
     withoutAnimation: false
     withoutAnimation: false
   },
   },
   device: 'desktop',
   device: 'desktop',

+ 12 - 1
src/store/modules/user.js

@@ -1,4 +1,4 @@
-import { login, logout, getInfo, getMenu } from '@/api/user'
+import { login, logout, getInfo, getMenu ,getImgCode } from '@/api/user'
 import { getToken, setToken, removeToken } from '@/utils/auth'
 import { getToken, setToken, removeToken } from '@/utils/auth'
 import router, { resetRouter } from '@/router'
 import router, { resetRouter } from '@/router'
 
 
@@ -79,6 +79,17 @@ const actions = {
     })
     })
   },
   },
 
 
+  // 获取图形验证码
+  getImgCode() {
+    return new Promise((resolve, reject) => {
+      getImgCode({}).then(response => {
+        resolve(response)
+      }).catch(error => {
+        reject(error)
+      })
+    })
+  },
+
   // 用户退出
   // 用户退出
   // logout({ commit, state, dispatch }) {
   // logout({ commit, state, dispatch }) {
   //   return new Promise((resolve, reject) => {
   //   return new Promise((resolve, reject) => {

+ 23 - 2
src/styles/sidebar.scss

@@ -10,7 +10,8 @@
   .sidebar-container {
   .sidebar-container {
     transition: width 0.28s;
     transition: width 0.28s;
     width: $sideBarWidth !important;
     width: $sideBarWidth !important;
-    background-color: $menuBg;
+    // background-color: $menuBg;
+    background:#fff;
     height: 100%;
     height: 100%;
     position: fixed;
     position: fixed;
     font-size: 0px;
     font-size: 0px;
@@ -82,7 +83,7 @@
 
 
     & .nest-menu .el-submenu>.el-submenu__title,
     & .nest-menu .el-submenu>.el-submenu__title,
     & .el-submenu .el-menu-item {
     & .el-submenu .el-menu-item {
-      min-width: $sideBarWidth !important;
+      //min-width: $sideBarWidth !important;
       background-color: $subMenuBg !important;
       background-color: $subMenuBg !important;
 
 
       &:hover {
       &:hover {
@@ -183,6 +184,26 @@
       transition: none;
       transition: none;
     }
     }
   }
   }
+
+  /*修改左侧菜单*/
+  /*选中状态*/
+  .el-scrollbar {
+    .scrollbar-wrapper {
+      .el-scrollbar__view {
+        padding:0 30px;
+        .el-menu {
+          .router-link-exact-active {
+            .is-active {
+              background:#5570F1 !important;
+              border-radius: 12px !important;
+              //margin: 30px 30px;
+            }
+          }
+        }
+      }
+    }
+  }
+
 }
 }
 
 
 // when menu collapsed
 // when menu collapsed

+ 8 - 8
src/styles/variables.scss

@@ -9,17 +9,17 @@ $yellow:#FEC171;
 $panGreen: #30B08F;
 $panGreen: #30B08F;
 
 
 // sidebar
 // sidebar
-$menuText:#bfcbd9;
-$menuActiveText:#409EFF;
-$subMenuActiveText:#f4f4f5; // https://github.com/ElemeFE/element/issues/12951
+$menuText:#53545C;
+$menuActiveText:#fff;
+$subMenuActiveText:#rgb(83, 84, 92); // https://github.com/ElemeFE/element/issues/12951
 
 
-$menuBg:#304156;
-$menuHover:#263445;
+$menuBg:#FFF;
+$menuHover:#FFF;
 
 
-$subMenuBg:#1f2d3d;
-$subMenuHover:#001528;
+$subMenuBg:#FFF;
+$subMenuHover:#FFF;
 
 
-$sideBarWidth: 210px;
+$sideBarWidth: 290px;
 
 
 // the :export directive is the magic sauce for webpack
 // the :export directive is the magic sauce for webpack
 // https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
 // https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass

+ 2 - 1
src/utils/request.js

@@ -6,7 +6,8 @@ import { getToken } from '@/utils/auth'
 // create an axios instance
 // create an axios instance
 const service = axios.create({
 const service = axios.create({
   //千万不能在这里使用绝对地址,这会导致webpack的devserve不生效
   //千万不能在这里使用绝对地址,这会导致webpack的devserve不生效
-  baseURL: 'http://192.168.1.201:9501',
+  baseURL: 'http://192.168.1.201:9501', //老刘服务器
+  //baseURL: 'http://101.254.114.211:9501', //测试服务器
   //baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
   //baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
   //withCredentials: true, // send cookies when cross-domain requests
   //withCredentials: true, // send cookies when cross-domain requests
   timeout: 5000 // request timeout
   timeout: 5000 // request timeout

+ 85 - 0
src/views/dashboard/admin/components/RingChart.vue

@@ -0,0 +1,85 @@
+<template>
+  <div :class="className" :style="{ height: height, width: width }"></div>
+</template>
+
+<script>
+import echarts from 'echarts';
+require('echarts/theme/macarons'); // echarts 主题
+import resize from './mixins/resize';
+
+export default {
+  mixins: [resize],
+  props: {
+    className: {
+      type: String,
+      default: 'chart',
+    },
+    width: {
+      type: String,
+      default: '100%',
+    },
+    height: {
+      type: String,
+      default: '300px',
+    },
+  },
+  data() {
+    return {
+      chart: null,
+    };
+  },
+  mounted() {
+    this.$nextTick(() => {
+      this.initChart();
+    });
+  },
+  beforeDestroy() {
+    if (!this.chart) {
+      return;
+    }
+    this.chart.dispose();
+    this.chart = null;
+  },
+  methods: {
+    initChart() {
+      this.chart = echarts.init(this.$el, 'macarons');
+
+      this.chart.setOption({
+        tooltip: {
+          trigger: 'item',
+          formatter: '{a} <br/>{b} : {c} ({d}%)',
+        },
+        legend: {
+          left: 'center',
+          bottom: '10',
+          data: ['Category A', 'Category B', 'Category C', 'Category D', 'Category E'],
+        },
+        series: [
+          {
+            name: 'Example Data',
+            type: 'pie',
+            radius: ['50%', '70%'], // 环形图,使用内外径
+            center: ['50%', '40%'],
+            data: [
+              { value: 1048, name: 'Category A' },
+              { value: 735, name: 'Category B' },
+              { value: 580, name: 'Category C' },
+              { value: 484, name: 'Category D' },
+              { value: 300, name: 'Category E' },
+            ],
+            label: {
+              show: true,
+              formatter: '{b}: {c} ({d}%)',
+            },
+            labelLine: {
+              show: true,
+            },
+            animationEasing: 'cubicInOut',
+            animationDuration: 2600,
+          },
+        ],
+      });
+    },
+  },
+};
+</script>

+ 23 - 14
src/views/dashboard/admin/index.vue

@@ -6,7 +6,9 @@
       <el-col :xs="24" :sm="24" :lg="8">
       <el-col :xs="24" :sm="24" :lg="8">
         <div class="topWindowBox">
         <div class="topWindowBox">
           <div class="twbTitle">
           <div class="twbTitle">
-            <div class="twbIconbgRed"></div>网站数量
+            <div class="twbIconbgRed">
+              <img src="@/assets/index/twbIconbgBlue.png"/>
+            </div>网站数量
           </div>
           </div>
           <div class="twbNumber">1,000</div>
           <div class="twbNumber">1,000</div>
           <div class="twbStatus"><img src="@/assets/index/arrow-up.png"/> +12% <span>较上周</span></div>
           <div class="twbStatus"><img src="@/assets/index/arrow-up.png"/> +12% <span>较上周</span></div>
@@ -15,7 +17,9 @@
       <el-col :xs="24" :sm="24" :lg="8">
       <el-col :xs="24" :sm="24" :lg="8">
         <div class="topWindowBox">
         <div class="topWindowBox">
           <div class="twbTitle">
           <div class="twbTitle">
-            <div class="twbIconbgBlue"></div>内容发布数量
+            <div class="twbIconbgBlue">
+              <img src="@/assets/index/twbIconbgRed.png"/>
+            </div>内容发布数量
           </div>
           </div>
           <div class="twbNumber">68,888</div>
           <div class="twbNumber">68,888</div>
           <div class="twbStatus"><img src="@/assets/index/arrow-up.png"/> +18% <span>较昨天</span></div>
           <div class="twbStatus"><img src="@/assets/index/arrow-up.png"/> +18% <span>较昨天</span></div>
@@ -24,7 +28,9 @@
       <el-col :xs="24" :sm="24" :lg="8">
       <el-col :xs="24" :sm="24" :lg="8">
         <div class="topWindowBox">
         <div class="topWindowBox">
           <div class="twbTitle">
           <div class="twbTitle">
-            <div class="twbIconbgPurple"></div>公共栏目数量
+            <div class="twbIconbgPurple">
+              <img src="@/assets/index/twbIconbgPurple.png"/>
+            </div>公共栏目数量
           </div>
           </div>
           <div class="twbNumber">24</div>
           <div class="twbNumber">24</div>
           <div class="twbStatusDown"><img src="@/assets/index/arrow-down.png"/> -2% <span>较上个月</span></div>
           <div class="twbStatusDown"><img src="@/assets/index/arrow-down.png"/> -2% <span>较上个月</span></div>
@@ -40,7 +46,8 @@
       </el-col>
       </el-col>
       <el-col :xs="24" :sm="24" :lg="8">
       <el-col :xs="24" :sm="24" :lg="8">
         <div class="chart-wrapper">
         <div class="chart-wrapper">
-          <pie-chart />
+          <!-- <pie-chart /> -->
+          <ring-chart />
         </div>
         </div>
       </el-col>
       </el-col>
     </el-row>
     </el-row>
@@ -58,6 +65,7 @@ import BarChart from './components/BarChart'
 import TransactionTable from './components/TransactionTable'
 import TransactionTable from './components/TransactionTable'
 import TodoList from './components/TodoList'
 import TodoList from './components/TodoList'
 import BoxCard from './components/BoxCard'
 import BoxCard from './components/BoxCard'
+import RingChart from './components/RingChart' //圆环图
 
 
 const lineChartData = {
 const lineChartData = {
   newVisitis: {
   newVisitis: {
@@ -89,7 +97,8 @@ export default {
     BarChart,
     BarChart,
     TransactionTable,
     TransactionTable,
     TodoList,
     TodoList,
-    BoxCard
+    BoxCard,
+    RingChart
   },
   },
   data() {
   data() {
     return {
     return {
@@ -124,15 +133,15 @@ export default {
         border-radius: 50%;
         border-radius: 50%;
         margin-right: 15px;
         margin-right: 15px;
       }
       }
-      .twbIconbgRed {
-        background: #EA79BA;
-      }
-      .twbIconbgBlue {
-        background: #6DACE7;
-      }
-      .twbIconbgPurple{
-        background: #AA7AEB;
-      }
+      // .twbIconbgRed {
+      //   background: #EA79BA;
+      // }
+      // .twbIconbgBlue {
+      //   background: #6DACE7;
+      // }
+      // .twbIconbgPurple{
+      //   background: #AA7AEB;
+      // }
     }
     }
     .twbNumber {
     .twbNumber {
       font-size: 30px;
       font-size: 30px;

+ 15 - 8
src/views/login/index.vue

@@ -214,7 +214,7 @@ export default {
               this.loading = false
               this.loading = false
             })
             })
         } else {
         } else {
-          console.log('error submit!!')
+          console.log('表单填写错误!请检查!')
           return false
           return false
         }
         }
       })
       })
@@ -222,14 +222,21 @@ export default {
     // 请求图形验证码
     // 请求图形验证码
     getImgCode() {
     getImgCode() {
       const that = this
       const that = this
-      request({
-        url: 'http://192.168.1.201:9501/verifyCode',
-        method: 'get'
-      }).then(response => {
-        that.codeImg = response.data
-      }).catch(error => {
-        console.log(error)
+      this.$store.dispatch('user/getImgCode').then( res => {
+        console.log(res)
+        that.codeImg = res.data
+      }).catch(() => {
+        this.loading = false
       })
       })
+
+      // request({
+      //   url: 'http://101.254.114.211:9501/verifyCode',
+      //   method: 'get'
+      // }).then(response => {
+      //   that.codeImg = response.data
+      // }).catch(error => {
+      //   console.log(error)
+      // })
     },
     },
     getOtherQuery(query) {
     getOtherQuery(query) {
       return Object.keys(query).reduce((acc, cur) => {
       return Object.keys(query).reduce((acc, cur) => {

+ 379 - 0
src/views/system-settings/websiteList.vue

@@ -0,0 +1,379 @@
+<template>
+  <div class="app-container">
+    <div class="filter-container">
+      <el-input v-model="listQuery.title" :placeholder="$t('table.title')" style="width: 200px;" class="filter-item" @keyup.enter.native="handleFilter" />
+      <el-select v-model="listQuery.importance" :placeholder="$t('table.importance')" clearable style="width: 90px" class="filter-item">
+        <el-option v-for="item in importanceOptions" :key="item" :label="item" :value="item" />
+      </el-select>
+      <el-select v-model="listQuery.type" :placeholder="$t('table.type')" clearable class="filter-item" style="width: 130px">
+        <el-option v-for="item in calendarTypeOptions" :key="item.key" :label="item.display_name+'('+item.key+')'" :value="item.key" />
+      </el-select>
+      <el-select v-model="listQuery.sort" style="width: 140px" class="filter-item" @change="handleFilter">
+        <el-option v-for="item in sortOptions" :key="item.key" :label="item.label" :value="item.key" />
+      </el-select>
+      <el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">
+        {{ $t('table.search') }}
+      </el-button>
+      <el-button class="filter-item" style="margin-left: 10px;" type="primary" icon="el-icon-edit" @click="handleCreate">
+        {{ $t('table.add') }}
+      </el-button>
+      <el-button v-waves :loading="downloadLoading" class="filter-item" type="primary" icon="el-icon-download" @click="handleDownload">
+        {{ $t('table.export') }}
+      </el-button>
+      <el-checkbox v-model="showReviewer" class="filter-item" style="margin-left:15px;" @change="tableKey=tableKey+1">
+        {{ $t('table.reviewer') }}
+      </el-checkbox>
+    </div>
+
+    <el-table
+      :key="tableKey"
+      v-loading="listLoading"
+      :data="list"
+      border
+      fit
+      highlight-current-row
+      style="width: 100%;"
+      @sort-change="sortChange"
+    >
+      <el-table-column :label="$t('table.id')" prop="id" sortable="custom" align="center" width="80" :class-name="getSortClass('id')">
+        <template slot-scope="{row}">
+          <span>{{ row.id }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column :label="$t('table.date')" width="150px" align="center">
+        <template slot-scope="{row}">
+          <span>{{ row.timestamp | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column :label="$t('table.title')" min-width="150px">
+        <template slot-scope="{row}">
+          <span class="link-type" @click="handleUpdate(row)">{{ row.title }}</span>
+          <el-tag>{{ row.type | typeFilter }}</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column :label="$t('table.author')" width="110px" align="center">
+        <template slot-scope="{row}">
+          <span>{{ row.author }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column v-if="showReviewer" :label="$t('table.reviewer')" width="110px" align="center">
+        <template slot-scope="{row}">
+          <span style="color:red;">{{ row.reviewer }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column :label="$t('table.importance')" width="80px">
+        <template slot-scope="{row}">
+          <svg-icon v-for="n in +row.importance" :key="n" icon-class="star" class="meta-item__icon" />
+        </template>
+      </el-table-column>
+      <el-table-column :label="$t('table.readings')" align="center" width="95">
+        <template slot-scope="{row}">
+          <span v-if="row.pageviews" class="link-type" @click="handleFetchPv(row.pageviews)">{{ row.pageviews }}</span>
+          <span v-else>0</span>
+        </template>
+      </el-table-column>
+      <el-table-column :label="$t('table.status')" class-name="status-col" width="100">
+        <template slot-scope="{row}">
+          <el-tag :type="row.status | statusFilter">
+            {{ row.status }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column :label="$t('table.actions')" align="center" width="230" class-name="small-padding fixed-width">
+        <template slot-scope="{row,$index}">
+          <el-button type="primary" size="mini" @click="handleUpdate(row)">
+            {{ $t('table.edit') }}
+          </el-button>
+          <el-button v-if="row.status!='published'" size="mini" type="success" @click="handleModifyStatus(row,'published')">
+            {{ $t('table.publish') }}
+          </el-button>
+          <el-button v-if="row.status!='draft'" size="mini" @click="handleModifyStatus(row,'draft')">
+            {{ $t('table.draft') }}
+          </el-button>
+          <el-button v-if="row.status!='deleted'" size="mini" type="danger" @click="handleDelete(row,$index)">
+            {{ $t('table.delete') }}
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="getList" />
+
+    <el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible">
+      <el-form ref="dataForm" :rules="rules" :model="temp" label-position="left" label-width="70px" style="width: 400px; margin-left:50px;">
+        <el-form-item :label="$t('table.type')" prop="type">
+          <el-select v-model="temp.type" class="filter-item" placeholder="Please select">
+            <el-option v-for="item in calendarTypeOptions" :key="item.key" :label="item.display_name" :value="item.key" />
+          </el-select>
+        </el-form-item>
+        <el-form-item :label="$t('table.date')" prop="timestamp">
+          <el-date-picker v-model="temp.timestamp" type="datetime" placeholder="Please pick a date" />
+        </el-form-item>
+        <el-form-item :label="$t('table.title')" prop="title">
+          <el-input v-model="temp.title" />
+        </el-form-item>
+        <el-form-item :label="$t('table.status')">
+          <el-select v-model="temp.status" class="filter-item" placeholder="Please select">
+            <el-option v-for="item in statusOptions" :key="item" :label="item" :value="item" />
+          </el-select>
+        </el-form-item>
+        <el-form-item :label="$t('table.importance')">
+          <el-rate v-model="temp.importance" :colors="['#99A9BF', '#F7BA2A', '#FF9900']" :max="3" style="margin-top:8px;" />
+        </el-form-item>
+        <el-form-item :label="$t('table.remark')">
+          <el-input v-model="temp.remark" :autosize="{ minRows: 2, maxRows: 4}" type="textarea" placeholder="Please input" />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="dialogFormVisible = false">
+          {{ $t('table.cancel') }}
+        </el-button>
+        <el-button type="primary" @click="dialogStatus==='create'?createData():updateData()">
+          {{ $t('table.confirm') }}
+        </el-button>
+      </div>
+    </el-dialog>
+
+    <el-dialog :visible.sync="dialogPvVisible" title="Reading statistics">
+      <el-table :data="pvData" border fit highlight-current-row style="width: 100%">
+        <el-table-column prop="key" label="Channel" />
+        <el-table-column prop="pv" label="Pv" />
+      </el-table>
+      <span slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="dialogPvVisible = false">{{ $t('table.confirm') }}</el-button>
+      </span>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { fetchList, fetchPv, createArticle, updateArticle } from '@/api/article'
+import waves from '@/directive/waves' // waves directive
+import { parseTime } from '@/utils'
+import Pagination from '@/components/Pagination' // secondary package based on el-pagination
+
+const calendarTypeOptions = [
+  { key: 'CN', display_name: 'China' },
+  { key: 'US', display_name: 'USA' },
+  { key: 'JP', display_name: 'Japan' },
+  { key: 'EU', display_name: 'Eurozone' }
+]
+
+// arr to obj, such as { CN : "China", US : "USA" }
+const calendarTypeKeyValue = calendarTypeOptions.reduce((acc, cur) => {
+  acc[cur.key] = cur.display_name
+  return acc
+}, {})
+
+export default {
+  name: 'ComplexTable',
+  components: { Pagination },
+  directives: { waves },
+  filters: {
+    statusFilter(status) {
+      const statusMap = {
+        published: 'success',
+        draft: 'info',
+        deleted: 'danger'
+      }
+      return statusMap[status]
+    },
+    typeFilter(type) {
+      return calendarTypeKeyValue[type]
+    }
+  },
+  data() {
+    return {
+      tableKey: 0,
+      list: null,
+      total: 0,
+      listLoading: true,
+      listQuery: {
+        page: 1,
+        limit: 20,
+        importance: undefined,
+        title: undefined,
+        type: undefined,
+        sort: '+id'
+      },
+      importanceOptions: [1, 2, 3],
+      calendarTypeOptions,
+      sortOptions: [{ label: 'ID Ascending', key: '+id' }, { label: 'ID Descending', key: '-id' }],
+      statusOptions: ['published', 'draft', 'deleted'],
+      showReviewer: false,
+      temp: {
+        id: undefined,
+        importance: 1,
+        remark: '',
+        timestamp: new Date(),
+        title: '',
+        type: '',
+        status: 'published'
+      },
+      dialogFormVisible: false,
+      dialogStatus: '',
+      textMap: {
+        update: 'Edit',
+        create: 'Create'
+      },
+      dialogPvVisible: false,
+      pvData: [],
+      rules: {
+        type: [{ required: true, message: 'type is required', trigger: 'change' }],
+        timestamp: [{ type: 'date', required: true, message: 'timestamp is required', trigger: 'change' }],
+        title: [{ required: true, message: 'title is required', trigger: 'blur' }]
+      },
+      downloadLoading: false
+    }
+  },
+  created() {
+    this.getList()
+  },
+  methods: {
+    getList() {
+      this.listLoading = true
+      fetchList(this.listQuery).then(response => {
+        this.list = response.data.items
+        this.total = response.data.total
+
+        // Just to simulate the time of the request
+        setTimeout(() => {
+          this.listLoading = false
+        }, 1.5 * 1000)
+      })
+    },
+    handleFilter() {
+      this.listQuery.page = 1
+      this.getList()
+    },
+    handleModifyStatus(row, status) {
+      this.$message({
+        message: '操作成功',
+        type: 'success'
+      })
+      row.status = status
+    },
+    sortChange(data) {
+      const { prop, order } = data
+      if (prop === 'id') {
+        this.sortByID(order)
+      }
+    },
+    sortByID(order) {
+      if (order === 'ascending') {
+        this.listQuery.sort = '+id'
+      } else {
+        this.listQuery.sort = '-id'
+      }
+      this.handleFilter()
+    },
+    resetTemp() {
+      this.temp = {
+        id: undefined,
+        importance: 1,
+        remark: '',
+        timestamp: new Date(),
+        title: '',
+        status: 'published',
+        type: ''
+      }
+    },
+    handleCreate() {
+      this.resetTemp()
+      this.dialogStatus = 'create'
+      this.dialogFormVisible = true
+      this.$nextTick(() => {
+        this.$refs['dataForm'].clearValidate()
+      })
+    },
+    createData() {
+      this.$refs['dataForm'].validate((valid) => {
+        if (valid) {
+          this.temp.id = parseInt(Math.random() * 100) + 1024 // mock a id
+          this.temp.author = 'vue-element-admin'
+          createArticle(this.temp).then(() => {
+            this.list.unshift(this.temp)
+            this.dialogFormVisible = false
+            this.$notify({
+              title: '成功',
+              message: '创建成功',
+              type: 'success',
+              duration: 2000
+            })
+          })
+        }
+      })
+    },
+    handleUpdate(row) {
+      this.temp = Object.assign({}, row) // copy obj
+      this.temp.timestamp = new Date(this.temp.timestamp)
+      this.dialogStatus = 'update'
+      this.dialogFormVisible = true
+      this.$nextTick(() => {
+        this.$refs['dataForm'].clearValidate()
+      })
+    },
+    updateData() {
+      this.$refs['dataForm'].validate((valid) => {
+        if (valid) {
+          const tempData = Object.assign({}, this.temp)
+          tempData.timestamp = +new Date(tempData.timestamp) // change Thu Nov 30 2017 16:41:05 GMT+0800 (CST) to 1512031311464
+          updateArticle(tempData).then(() => {
+            const index = this.list.findIndex(v => v.id === this.temp.id)
+            this.list.splice(index, 1, this.temp)
+            this.dialogFormVisible = false
+            this.$notify({
+              title: '成功',
+              message: '更新成功',
+              type: 'success',
+              duration: 2000
+            })
+          })
+        }
+      })
+    },
+    handleDelete(row, index) {
+      this.$notify({
+        title: '成功',
+        message: '删除成功',
+        type: 'success',
+        duration: 2000
+      })
+      this.list.splice(index, 1)
+    },
+    handleFetchPv(pv) {
+      fetchPv(pv).then(response => {
+        this.pvData = response.data.pvData
+        this.dialogPvVisible = true
+      })
+    },
+    handleDownload() {
+      this.downloadLoading = true
+      import('@/vendor/Export2Excel').then(excel => {
+        const tHeader = ['timestamp', 'title', 'type', 'importance', 'status']
+        const filterVal = ['timestamp', 'title', 'type', 'importance', 'status']
+        const data = this.formatJson(filterVal)
+        excel.export_json_to_excel({
+          header: tHeader,
+          data,
+          filename: 'table-list'
+        })
+        this.downloadLoading = false
+      })
+    },
+    formatJson(filterVal) {
+      return this.list.map(v => filterVal.map(j => {
+        if (j === 'timestamp') {
+          return parseTime(v[j])
+        } else {
+          return v[j]
+        }
+      }))
+    },
+    getSortClass: function(key) {
+      const sort = this.listQuery.sort
+      return sort === `+${key}` ? 'ascending' : 'descending'
+    }
+  }
+}
+</script>