派生自 wuyushui/SewerAndRainNetwork

chenzeping
2021-03-30 c0c0d75b2ed0191877d8cadfb39dedad6eabb407
Merge branch 'develop' of http://xearth.cn:6600/r/wuyushui/SewerAndRainNetwork into develop
14个文件已添加
16个文件已修改
762 ■■■■ 已修改文件
.env 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
.env.production 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
.env.test 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/host.js 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/index.js 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mapApi.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mapUrl.js 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/app.config.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/login-page/page1.jpg 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/map-pages/basenav/flugas/exhaust/fq_blue.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/map-pages/basenav/flugas/exhaust/fq_bright_green.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/map-pages/basenav/flugas/exhaust/fq_gray.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/map-pages/basenav/flugas/exhaust/fq_green.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/map-pages/basenav/flugas/exhaust/fq_green_xc.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/map-pages/basenav/flugas/exhaust/fq_orange.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/map-pages/basenav/flugas/exhaust/fq_red.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/map-pages/basenav/flugas/exhaust/fq_yellow.png 补丁 | 查看 | 原始文档 | blame | 历史
src/components/BaseNav/PublicBounced/GasComponents/GasECharts.vue 194 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/BaseNav/PublicBounced/GasComponents/GasTable.vue 补丁 | 查看 | 原始文档 | blame | 历史
src/components/BaseNav/PublicBounced/PublicBounced.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/BaseNav/WasteWater/WasteWater.js 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main.js 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/index.js 29 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/map.js 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/index.js 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/modules/user.js 149 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/axios.js 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/navigation.js 167 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/tools.js 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/Login.vue 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.env
@@ -2,7 +2,7 @@
NODE_ENV=development
#vue项目 属性名必须以VUE_APP_开头,比如VUE_APP_XXX
VUE_APP_MOCK=true
VUE_APP_API_HOST=http://localhost:8080/
VUE_APP_API_HOST=http://xearth.cn:3000
VUE_APP_API_TOKEN_URL=https://auth.uat.siam.sinopec.com/
VUE_APP_API_BASE_URL=/api
VUE_APP_CLIENT_ID=no_apply
.env.production
@@ -1,7 +1,7 @@
# .env
NODE_ENV=production
VUE_APP_MOCK=false
VUE_APP_API_HOST=http://localhost:8080/
VUE_APP_API_HOST=http://localhost:3000/
VUE_APP_API_BASE_URL=/api
VUE_APP_API_TOKEN_URL=https://oauth.siam.sinopec.com/
VUE_APP_CLIENT_ID=no_apply
.env.test
@@ -1,7 +1,7 @@
# .env
NODE_ENV=test
VUE_APP_MOCK=false
VUE_APP_API_HOST=http://localhost:8080/
VUE_APP_API_HOST=http://localhost:3000/
VUE_APP_API_TOKEN_URL=https://auth.uat.siam.sinopec.com/
VUE_APP_API_BASE_URL=/api
VUE_APP_CLIENT_ID=no_apply
src/api/host.js
New file
@@ -0,0 +1,6 @@
/**
 * 服务HOST定义
 */
import { getSchemeHost } from '../utils/tools'
export const $HOST = getSchemeHost()
src/api/index.js
@@ -1,7 +1,4 @@
import * as mapApi from './mapApi'
import * as mapUrl from './mapUrl'
export default {
  mapApi,
  mapUrl
}
export default Object.assign({}, mapApi, mapUrl)
src/api/mapApi.js
@@ -3,13 +3,12 @@
// const $HOST = 'http://10.238.221.113'
// 测试环境IP:http://10.238.221.113
import axios from '@utils/axios'
import * as mapUrl from './mapUrl'
/**
 * 该方法配置
 */
class MapAPI {
  getToken (param) {
    axios.get(param.url, param.option)
export default {
  getUser (data) {
    return axios.get(mapUrl.GetUser, data)
  }
}
export default new MapAPI()
src/api/mapUrl.js
@@ -1,10 +1,6 @@
/**
 * 该文件配置接口的URL地址
 */
class ApiURLs {
  constructor () {
    this.APIURL_HELLOWORLD = 'http://www.baidu.com'
  }
}
import { $HOST } from './host'
export default new ApiURLs()
export const GetUser = $HOST + '/user/getUser'
src/app.config.js
@@ -11,7 +11,7 @@
      developmentOff: true // 设为true后在开发环境不会收集错误信息
    }
  },
  homeRouterName: 'Home',
  homeRouterName: 'mapTemplate',
  loginRouteName: 'Login',
  routeMode: 'history',
  tagNavCache: false,
src/assets/images/login-page/page1.jpg
src/assets/images/map-pages/basenav/flugas/exhaust/fq_blue.png
src/assets/images/map-pages/basenav/flugas/exhaust/fq_bright_green.png
src/assets/images/map-pages/basenav/flugas/exhaust/fq_gray.png
src/assets/images/map-pages/basenav/flugas/exhaust/fq_green.png
src/assets/images/map-pages/basenav/flugas/exhaust/fq_green_xc.png
src/assets/images/map-pages/basenav/flugas/exhaust/fq_orange.png
src/assets/images/map-pages/basenav/flugas/exhaust/fq_red.png
src/assets/images/map-pages/basenav/flugas/exhaust/fq_yellow.png
src/components/BaseNav/PublicBounced/GasComponents/GasECharts.vue
@@ -7,10 +7,12 @@
    <div class="border_corner border_corner_left_bottom"></div>
    <div class="border_corner border_corner_right_bottom"></div>
    <div class="main">
      <div class="main-echarts">
        <div id="echarts" style="width:100%;height:100%" ref="main"></div>
        <div id="echarts" ref="main"></div>
        <div class="border_corner border_corner_left_top"></div>
        <div class="border_corner border_corner_right_top"></div>
        <div class="border_corner border_corner_left_bottom"></div>
        <div class="border_corner border_corner_right_bottom"></div>
      </div>
    </div>
  </div>
</template>
@@ -22,18 +24,27 @@
      const myChart = this.$echarts.init(this.$refs.main)
      const option = {
        title: {
          text: '折线图堆叠'
          // text: '折线图堆叠'
        },
        // color: '#fff',
        color: ['#5470c6', '#91CC75', '#EE6666', '#FF0087'],
        tooltip: {
          trigger: 'axis'
          trigger: 'axis',
          axisPointer: {
            type: 'cross',
            label: {
              color: '#1a4245'
            }
          }
        },
        // legend: {
        //   data: ['邮件营销', '联盟广告', '视频广告', '直接访问', '搜索引擎']
        // },
        legend: {
          data: ['氮氧化物', '二氧化硫', '烟尘', '废气流量']
          // pageTextStyle: {
          //   color: '#fff'
          // }
        },
        grid: {
          left: '3%',
          right: '4%',
          right: '3%',
          bottom: '3%',
          containLabel: true
        },
@@ -42,63 +53,98 @@
            saveAsImage: {}
          }
        },
        // 图标缩放设置
        dataZoom: [{
          type: 'inside',
          start: 0,
          end: 100
        }, {
          start: 0,
          end: 100,
          show: false,
          // handleIcon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z',
          handleSize: '80%',
          handleStyle: {
            color: '#fff',
            shadowBlur: 3,
            shadowColor: 'rgba(0, 0, 0, 0.6)',
            shadowOffsetX: 2,
            shadowOffsetY: 2
          }
        }],
        // x轴的设置
        xAxis: {
          type: 'category',
          boundaryGap: false,
          data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
          axisLabel: {
          data: ['12:00:01', '12:00:02', '12:00:03', '12:00:04', '12:00:05', '12:00:06', '12:00:07'],
          axisLabel: { // x轴全部显示
            rotate: 20,
            interval: 0,
            textStyle: {
              color: '#7edae8' // 坐标的字体颜色
              color: '#fff'
            }
          },
          axisLine: {
            show: false, // 隐藏坐标轴
          splitLine: { // 网格垂直线为虚线
            show: true,
            lineStyle: {
              color: '#00eeff' // 坐标轴的颜色
              type: 'dashed'
            }
          },
          axisTick: { // x 轴刻度显示
            show: false
          },
          axisLine: {
            lineStyle: {
              color: '#FFFFFF',
              width: 1 // 这里是为了突出显示加上的
            }
          }
        },
        // Y 轴的设置
        yAxis: [{
          type: 'value',
          // name: yname, // 后期图标Y轴显示单位
          name: '浓度(mg/m³)',
          axisLabel: {
            formatter: '{value}',
            textStyle: {
              color: '#7edae8' // 坐标的字体颜色
              color: '#fff' // 坐标的字体颜色
            }
          },
          axisPointer: {
            snap: true // 自动吸附最近的点
          },
          splitLine: {
            show: false // y轴 网格线不显示
          },
          axisLine: {
            show: false, // 隐藏坐标轴
            lineStyle: {
              color: '#00eeff' // 坐标轴的颜色
              color: '#ffffff', // 坐标轴的颜色
              width: 1
            }
          }
        }],
        series: [
          {
            name: '邮件营销',
            name: '氮氧化物',
            type: 'line',
            stack: '总量',
            data: [120, 132, 101, 134, 90, 230, 210]
          },
          {
            name: '联盟广告',
            type: 'line',
            stack: '总量',
            data: [220, 182, 191, 234, 290, 330, 310]
          },
          {
            name: '视频广告',
            name: '二氧化硫',
            type: 'line',
            stack: '总量',
            data: [150, 232, 201, 154, 190, 330, 410]
          },
          {
            name: '直接访问',
            name: '烟尘',
            type: 'line',
            stack: '总量',
            data: [320, 332, 301, 334, 390, 330, 320]
          },
          {
            name: '搜索引擎',
            name: '废气流量',
            type: 'line',
            stack: '总量',
            data: [820, 932, 901, 934, 1290, 1330, 1320]
@@ -115,50 +161,48 @@
</script>
<style scoped lang="less">
.win {
  width: 100%;
  height: 100%;
  position: relative;
  //display: inline-block;
  background-color: rgba(33, 41, 69,0.9);
}
.border_corner{
  z-index: 2500;
  position: absolute;
  width: 10px;
  height: 10px;
  background: rgba(0,0,0,0);
  border: 1.5px solid #47d5ea;
}
.border_corner_left_top{
  top: 0;
  left: 0;
  border-right: none;
  border-bottom: none;
}
.border_corner_right_top{
  top: 0;
  right: 0;
  border-left: none;
  border-bottom: none;
}
.border_corner_left_bottom{
  bottom: 0;
  left: 0;
  border-right: none;
  border-top: none;
}
.border_corner_right_bottom{
  bottom: 0;
  right: 0;
  border-left: none;
  border-top: none;
}
.main {
  width: 100%;
  height: 100%;
  .main-echarts{
    border: 1px solid #396d83;
  }
}
    .win {
        /*width: 100%;*/
        /*height: 100%;*/
        position: relative;
        //display: inline-block;
        background-color: rgba(33, 41, 69,0.9);
    }
    #echarts{
        width: 600px;
        height: 260px;
    }
    .border_corner{
        z-index: 2500;
        position: absolute;
        width: 14px;
        height: 14px;
        background: rgba(0,0,0,0);
        border: 2px solid #47d5ea;
    }
    .border_corner_left_top{
        top: 0;
        left: 0;
        border-right: none;
        border-bottom: none;
    }
    .border_corner_right_top{
        top: 0;
        right: 0;
        border-left: none;
        border-bottom: none;
    }
    .border_corner_left_bottom{
        bottom: 0;
        left: 0;
        border-right: none;
        border-top: none;
        border-bottom-left-radius: 4px;
    }
    .border_corner_right_bottom{
        bottom: 0;
        right: 0;
        border-left: none;
        border-top: none;
    }
</style>
src/components/BaseNav/PublicBounced/GasComponents/GasTable.vue
src/components/BaseNav/PublicBounced/PublicBounced.vue
@@ -98,6 +98,5 @@
      height: 100%;
    }
  }
}
</style>
src/components/BaseNav/WasteWater/WasteWater.js
@@ -52,16 +52,17 @@
      const Icon = new WasteWaterIcon({ iconUrl: iconUrl })
      // let url = Icon.options.iconUrl
      const marker = this.L.marker.magic([positionX, positionY], { icon: Icon, magic: 'vanishIn' })
      // const marker = this.L.marker.magic([positionX, positionY], { icon: Icon, magic: 'vanishIn' }) // 带显示动画
      const marker = this.L.marker([positionX, positionY], { icon: Icon })
      marker.bindPopup(() => {
        return this.WasteWaterPopup.$el
      }, {
        className: 's-map-popup',
        minWidth: 1000,
        closeButton: true,
        autoClose: false
      })
      // marker.bindPopup(() => {
      //   return this.WasteWaterPopup.$el
      // }, {
      //   className: 's-map-popup',
      //   minWidth: 1000,
      //   closeButton: true,
      //   autoClose: false
      // })
      // 划过出现 展示数据
      marker.bindTooltip(data[i].Name, {
        permanent: true,
@@ -74,7 +75,7 @@
        try {
          // console.log(e)
          this.EffectOfPulse(e.target.getLatLng())
          this.WasteWaterPopup.setDate(data[i])
          this.WasteWaterPopup.setData(data[i])
          return this.WasteWaterPopup.$el
        } catch (error) {
          console.log(error)
src/main.js
@@ -15,12 +15,13 @@
import '@/utils/dialogDrag.js'
import '@components/plugin/leaflet-measure-path/leaflet-measure-path.css'
import '@components/plugin/leaflet-measure-path/leaflet-measure-path'
const appConfig = require('@/app.config')
Vue.config.productionTip = false
Vue.use(ElementUI)
Vue.prototype.$cancels = []
Vue.prototype.L = L
Vue.prototype.$config = appConfig
Vue.prototype.$echarts = echarts // 挂载echarts
// 注册指令7
// registerDirectives(Vue)
src/router/index.js
@@ -1,10 +1,11 @@
import Vue from 'vue'
import Router from 'vue-router'
import { routes } from './routes'
import { getToken } from '@/utils/navigation'
import 'nprogress/nprogress.css'
const appConfig = require('@/app.config')
const { routeMode } = appConfig
const { homeRouterName, loginRouteName, routeMode } = appConfig
const LOGIN_PAGE_ROUTE_NAME = loginRouteName
Vue.use(Router)
const baseName = process.env.NODE_ENV === 'production' ? `/${appConfig.projectName}/` : '/'
const router = new Router({
@@ -17,12 +18,32 @@
    cancel()
  })
  Vue.prototype.$cancels = []
  const token = getToken()
  if (!token && to.name !== LOGIN_PAGE_ROUTE_NAME) {
    // 未登录且要跳转的页面不是登录页
    next({
      name: LOGIN_PAGE_ROUTE_NAME // 跳转到登录页
    })
  } else if (!token && to.name === LOGIN_PAGE_ROUTE_NAME) {
    // 未登陆且要跳转的页面是登录页
    next() // 跳转
  } else if (token && to.name === LOGIN_PAGE_ROUTE_NAME) {
    // 已登录且要跳转的页面是登录页
    next({
      name: homeRouterName // 跳转到homeName页
    })
  } else if (!token) {
    next({
      name: LOGIN_PAGE_ROUTE_NAME // 跳转到登录页
    })
  } else {
    next()
  }
  // 不需要登录认证的路由
  if (Object.hasOwnProperty.call(to.meta, 'noLoginIdentify') && to.meta.noLoginIdentify) {
    next()
    return
  }
  next()
  // next()
})
router.beforeResolve((to, from, next) => {
  next()
src/router/map.js
@@ -9,8 +9,10 @@
 * }
 */
const MapTemplate = (r) => require.ensure([], () => r(require('../views/MapTemplate')), 'frame')
const Login = (r) => require.ensure([], () => r(require('../views/Login')), 'frame')
const routes = [
  { path: '/mapTemplate', name: 'MapTemplate', meta: { statusBgc: 0 }, component: MapTemplate }
  { path: '/mapTemplate', name: 'MapTemplate', meta: { statusBgc: 0 }, component: MapTemplate },
  { path: '/Login', name: 'Login', meta: { statusBgc: 0 }, component: Login }
]
// 所有上面定义的路由都要写在下面的routes里
src/store/index.js
@@ -1,6 +1,7 @@
import Vue from 'vue'
import Vuex from 'vuex'
import map from './modules/map'
import user from './modules/user'
// import app from './modules/app'
Vue.use(Vuex)
@@ -13,6 +14,7 @@
  },
  modules: {
    // app,
    map
    map,
    user
  }
})
src/store/modules/user.js
New file
@@ -0,0 +1,149 @@
import $API from '../../api'
import $http from '@/utils/axios'
import { setToken, getToken, setAccount, getAccount } from '@/utils/navigation'
// import { replaceUrlParams } from '@/utils/tools'
const state = {
  account: '',
  userName: '',
  userId: '',
  avator: '',
  orgUnitName: '',
  orgUnitCode: '',
  orgUnitLevel: '',
  orgSectorName: '',
  orgSectorCode: '',
  isAdmin: null,
  token: getToken(),
  access: 0,
  hasGetInfo: false
}
const mutations = {
  setAvator (state, avator) {
    state.avator = avator
  },
  setUserId (state, id) {
    state.userId = id
  },
  setAccount (state, account) {
    state.account = account
    setAccount(account)
  },
  setUserName (state, name) {
    state.userName = name
  },
  setOrgUnitName (state, orgUnitName) {
    state.orgUnitName = orgUnitName
  },
  setOrgUnitCode (state, orgUnitCode) {
    state.orgUnitCode = orgUnitCode
  },
  setOrgSectorName (state, orgSectorName) {
    state.orgSectorName = orgSectorName
  },
  setOrgSectorCode (state, orgSectorCode) {
    state.orgSectorCode = orgSectorCode
  },
  setOrgUnitLevel (state, orgUnitLevel) {
    state.orgUnitLevel = orgUnitLevel
  },
  setAdmin (state, isAdmin) {
    state.isAdmin = isAdmin
  },
  setAccess (state, access) {
    state.access = access
  },
  setToken (state, token) {
    state.token = token
    setToken(token)
  },
  setHasGetInfo (state, status) {
    state.hasGetInfo = status
  }
}
const actions = {
  // 登录
  handleLogin ({ commit }, { account, password }) {
    return new Promise((resolve, reject) => {
      $http.post($API.GetUser, {
        userName: account,
        passWord: password
      }).then(data => {
        console.log(data)
        commit('setToken', '12345668')
        commit('setAccount', account)
        resolve()
      }).catch(err => {
        reject(err)
      })
    })
  },
  // 退出登录
  handleLogOut ({ state, commit }) {
    return new Promise((resolve, reject) => {
      commit('setToken', '')
      commit('setAccount', '')
      commit('setAccess', 0)
      resolve()
    })
  },
  // 获取用户信息
  getUserInfo ({ state, commit, dispatch }) {
    const account = getAccount() || 'admin'
    return new Promise((resolve, reject) => {
      try {
        $http.get($API.AAA_GET_USER_INFO, {
          token: state.token
        }).then(response => {
          const data = response.data
          commit('setAvator', data.avator)
          // commit('setOrgUnitName', data.orgUnitName)
          // commit('setOrgUnitCode', data.orgUnitCode)
          commit('setUserId', data.userId)
          commit('setAccess', data.access)
          commit('setHasGetInfo', true)
          if (account === 'tianjun') {
            // admin的场景走mock
            commit('setAccount', data.account)
            commit('setUserName', data.userName)
            resolve(data)
          } else {
            $http.get($API.MDM_GET_USER_ACCOUNT_DETAIL, {
              account: account
            }).then(response => {
              const d = response.data
              if (d) {
                commit('setAccount', d.unifiedIdentityAcc)
                commit('setUserName', d.userName)
                // 获取用户后,获取组织机构
                dispatch('getUserOrgInfo').then(() => {
                  resolve(d)
                }).catch(err => {
                  alert('不能获取用户组织机构')
                  reject(err)
                })
              } else {
                commit('setToken', '')
                commit('setAccount', '')
                commit('setAccess', 0)
                resolve(false)
              }
            })
          }
        }).catch(err => {
          reject(err)
        })
      } catch (error) {
        reject(error)
      }
    })
  }
}
export default {
  state,
  mutations,
  actions
}
src/utils/axios.js
@@ -83,8 +83,7 @@
// 创建axios实例
const Service = axios.create({
  timeout: 5000,
  baseURL: 'http://10.246.162.140:8080/'
  timeout: 1000
})
const CancelToken = axios.CancelToken
@@ -140,14 +139,14 @@
// respone拦截器
Service.interceptors.response.use(
  response => {
    // const res = response.data
    // if (Number(res.code) !== 200 && Number(res.code) !== 0) {
    //   $T.warning(res.message)
    //   return Promise.reject(res.message)
    // } else {
    //   return res
    // }
    return response.data
    const res = response.data
    /* if (Number(res.code) !== 200 && Number(res.code) !== 0) {
      $T.warning(res.message)
      return Promise.reject(res.message)
    } else {
      return res
    } */
    return res
  },
  error => {
    if (error.message && error.message.includes('timeout')) {
src/utils/navigation.js
New file
@@ -0,0 +1,167 @@
import Cookies from 'js-cookie'
const appConfig = require('@/app.config')
const { cookieExpires } = appConfig
export const TOKEN_KEY = 'token'
export const ACCOUNT_KEY = 'account'
export const setToken = (token) => {
  Cookies.set(TOKEN_KEY, token, { expires: cookieExpires || 1 })
}
export const getToken = () => {
  const token = Cookies.get(TOKEN_KEY)
  if (token !== 'undefined') return token
  else return false
}
export const setAccount = (account) => {
  Cookies.set(ACCOUNT_KEY, account, { expires: cookieExpires || 1 })
}
export const getAccount = () => {
  const account = Cookies.get(ACCOUNT_KEY)
  if (account) return account
  else return false
}
export const hasChild = (item) => {
  return item.children && item.children.length !== 0
}
/**
 * @description 本地存储和获取标签导航列表
 */
export const setTagNavListToLocalstorage = list => {
  localStorage.tagNaveList = JSON.stringify(list)
}
/**
 * @returns {Array} 其中的每个元素只包含路由原信息中的name, path, meta三项
 */
export const getTagNavListFromLocalstorage = () => {
  const list = localStorage.tagNaveList
  return list ? JSON.parse(list) : []
}
/**
 * @param {Array} routers 路由列表数组
 * @description 用于找到路由列表中name为home的对象
 */
export const getHomeRouter = (routers, homeName = 'Home') => {
  let i = -1
  const len = routers.length
  let homeRoute = {}
  while (++i < len) {
    const item = routers[i]
    if (item.children && item.children.length) {
      const res = getHomeRouter(item.children, homeName)
      if (res.name) return res
    } else {
      if (item.name === homeName) homeRoute = item
    }
  }
  return homeRoute
}
/**
 * @param {*} list 现有标签导航列表
 * @param {*} newRoute 新添加的路由原信息对象
 * @description 如果该newRoute已经存在则不再添加
 */
export const getNewTagList = (list, newRoute) => {
  const { name, path, meta } = newRoute
  const newList = [...list]
  if (newList.findIndex(item => item.name === name) >= 0) return newList
  else newList.push({ name, path, meta })
  return newList
}
/**
 * @param {Number} times 回调函数需要执行的次数
 * @param {Function} callback 回调函数
 */
export const doCustomTimes = (times, callback) => {
  let i = -1
  while (++i < times) {
    callback(i)
  }
}
export const findNodeUpper = (ele, tag) => {
  if (ele.parentNode) {
    if (ele.parentNode.tagName === tag.toUpperCase()) {
      return ele.parentNode
    } else {
      return findNodeUpper(ele.parentNode, tag)
    }
  }
}
export const findNodeUpperByClasses = (ele, classes) => {
  const parentNode = ele.parentNode
  if (parentNode) {
    const classList = parentNode.classList
    if (classList && classes.every(className => classList.contains(className))) {
      return parentNode
    } else {
      return findNodeUpperByClasses(parentNode, classes)
    }
  }
}
export const findNodeDownward = (ele, tag) => {
  const tagName = tag.toUpperCase()
  if (ele.childNodes.length) {
    let i = -1
    const len = ele.childNodes.length
    while (++i < len) {
      const child = ele.childNodes[i]
      if (child.tagName === tagName) return child
      else return findNodeDownward(child, tag)
    }
  }
}
export const localSave = (key, value) => {
  localStorage.setItem(key, value)
}
export const localRead = (key) => {
  return localStorage.getItem(key) || ''
}
// scrollTop animation
export const scrollTop = (el, from = 0, to, duration = 500, endCallback) => {
  if (!window.requestAnimationFrame) {
    window.requestAnimationFrame = (
      window.webkitRequestAnimationFrame ||
      window.mozRequestAnimationFrame ||
      window.msRequestAnimationFrame ||
      function (callback) {
        return window.setTimeout(callback, 1000 / 60)
      }
    )
  }
  const difference = Math.abs(from - to)
  const step = Math.ceil(difference / duration * 50)
  const scroll = (start, end, step) => {
    if (start === end) {
      endCallback && endCallback()
      return
    }
    let d = (start + step > end) ? end : start + step
    if (start > end) {
      d = (start - step < end) ? end : start - step
    }
    if (el === window) {
      window.scrollTo(d, d)
    } else {
      el.scrollTop = d
    }
    window.requestAnimationFrame(() => scroll(d, end, step))
  }
  scroll(from, to, step)
}
src/utils/tools.js
@@ -25,7 +25,16 @@
  })
  return arr
}
/**
 * 根据不同的协议,替换env配置文件中的URL协议
 *
 * @return {String}
 */
export function getSchemeHost () {
  const protocol = window.location.protocol
  const host = process.env.VUE_APP_API_HOST
  return host.replace(/scheme/g, protocol)
}
/**
 * collection数据转换为标准JSON
 * @param obj collection数据
src/views/Login.vue
New file
@@ -0,0 +1,124 @@
<template>
    <div class="login-wrap" @keydown.enter="handleSubmit">
        <div class="content">
            <el-card class="box-card">
                <div slot="header" class="login-title clearfix">
                    <el-icon name="star-on"></el-icon>
                    <span>欢迎登录</span>
                </div>
                <div class="form-content">
                    <el-form :model="form" :rules="formRules" ref="loginForm" class="demo-ruleForm">
                        <el-form-item prop="account">
                            <el-input size="small" v-model="form.account" placeholder="请输入用户名">
                                <el-button slot="prepend" icon="el-icon-search" style="padding: 12px 10px;"></el-button>
                            </el-input>
                        </el-form-item>
                        <el-form-item prop="password">
                            <el-input size="small" type="password" v-model="form.password" auto-complete="off"
                                      placeholder="请输入密码">
                                <el-button slot="prepend" icon="el-icon-search" style="padding: 12px 10px;"></el-button>
                            </el-input>
                        </el-form-item>
                        <el-form-item>
                            <el-button size="small" type="primary" @click="handleSubmit" style="width: 100%">登录
                            </el-button>
                        </el-form-item>
                    </el-form>
                </div>
                <p class="login-tip">输入任意用户名和密码即可</p>
            </el-card>
        </div>
    </div>
</template>
<script>
import { mapActions } from 'vuex'
export default {
  name: 'Login',
  components: {},
  data () {
    return {
      form: {
        account: 'admin',
        password: 'admin'
      },
      formRules: {
        account: [
          { required: true, message: '账号不能为空', trigger: 'blur' }
        ],
        password: [
          { required: true, message: '密码不能为空', trigger: 'blur' }
        ]
      }
    }
  },
  methods: {
    ...mapActions([
      'handleLogin',
      'getUserInfo'
    ]),
    handleSubmit () {
      this.$refs.loginForm.validate((valid) => {
        if (valid) {
          this.handleLogin({
            account: this.form.account,
            password: this.form.password
          }).then(response => {
            this.$router.push({
              name: this.$config.homeRouterName
            })
          })
        }
      })
    }
  }
}
</script>
<style lang="less">
    .login-wrap {
        position: relative;
        background-image: url('../assets/images/login-page/page1.jpg');
        background-size: cover;
        background-position: center;
        width: 100%;
        height: 100%;
        overflow: hidden;
        .content {
            position: absolute;
            right: 160px;
            top: 25%;
        }
        .box-card {
            width: 300px;
        }
        .clearfix:before,
        .clearfix:after {
            display: table;
            content: "";
        }
        .clearfix:after {
            clear: both
        }
        .form-content {
            padding: 10px 0 0;
        }
        .login-title {
            width: 100%;
            font-size: 14px;
            color: #1c2438;
            font-weight: 700;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }
        .login-tip {
            font-size: 10px;
            text-align: center;
            color: #c3c3c3;
        }
    }
</style>