vue2 左侧路由

layout-components-Sidebar-FixiOSBug.js

export default {
  computed: {
    device() {
      return this.$store.state.app.device
    },
  },
  mounted() {
    this.fixBugIniOS()
  },
  methods: {
    fixBugIniOS() {
      const $subMenu = this.$refs.subMenu
      if ($subMenu) {
        const handleMouseleave = $subMenu.handleMouseleave
        $subMenu.handleMouseleave = e => {
          if (this.device === 'mobile') {
            return
          }
          handleMouseleave(e)
        }
      }
    },
  },
}

layout-components-Sidebar-index.vue

<template>
  <div>
    <el-scrollbar wrap-class="scrollbar-wrapper">
      <el-menu
        :default-active="$route.path"
        :collapse="isCollapse"
        :background-color="variables.menuBg"
        :text-color="variables.menuText"
        :unique-opened="false"
        :active-text-color="variables.menuActiveText"
        :collapse-transition="false"
        mode="vertical"
      >
        <sidebar-item v-for="route in routes" :key="route.path" :item="route" :base-path="route.path" />
      </el-menu>
    </el-scrollbar>
  </div>
</template>

<script>
import variables from '@/styles/variables.scss'
import { UserInfo } from '@/utils/auth'
import SidebarItem from './SidebarItem'
import { routerChange } from '@/router'
import { mapGetters } from 'vuex'
export default {
  components: { SidebarItem },
  data() {
    return {
      platName: UserInfo.value('platName'),
      options: [],
    }
  },
  computed: {
    ...mapGetters(['sidebar']),
    routes() {
      const routes = this.options
      return routes.find(route => route.path === '/')?.children || []
    },
    activeMenu() {
      const route = this.$route
      const { meta, path } = route
      // if set path, the sidebar will highlight the path you set
      if (meta.activeMenu) {
        return meta.activeMenu
      }
      return path
    },
    variables() {
      return variables
    },
    isCollapse() {
      return !this.sidebar.opened
      // return false
    },
  },
  created() {
    routerChange(routers => {
      this.options = routers
    })
  },
}
</script>

<style lang="scss" scoped>
.slider-title {
  font-size: 16px;
  height: 55px;
  line-height: 55px;
  padding-left: 20px;
  position: relative;
  &::after {
    content: ' ';
    position: absolute;
    width: 100%;
    height: 2px;
    left: 0;
    bottom: 0;
    background-color: #f5f6f7;
    z-index: 5;
  }
}
</style>

Item.vue

<script>
export default {
  name: 'MenuItem',
  functional: true,
  props: {
    icon: {
      type: String,
      default: ''
    },
    title: {
      type: String,
      default: ''
    }
  },
  render(h, context) {
    const { icon, title } = context.props;
    const vnodes = [];

    if (icon) {
      if (icon.includes('el-icon')) {
        vnodes.push(<i class={[icon, 'sub-el-icon']} />);
      } else {
        vnodes.push(<svg-icon icon-class={icon}/>);
      }
    }

    if (title) {
      vnodes.push(<span slot='title'>{(title)}</span>);
    }
    return vnodes;
  }
};
</script>

<style scoped>
.sub-el-icon {
  color: currentColor;
  width: 1em;
  height: 1em;
}
</style>

Link.vue

<template>
  <component :is="type" v-bind="linkProps(to)">
    <slot />
  </component>
</template>

<script>
import { isExternal } from '@/utils/validate';

export default {
  props: {
    to: {
      type: String,
      required: true
    }
  },
  computed: {
    isExternal() {
      return isExternal(this.to);
    },
    type() {
      if (this.isExternal) {
        return 'a';
      }
      return 'router-link';
    }
  },
  methods: {
    linkProps(to) {
      if (this.isExternal) {
        return {
          href: to,
          target: '_blank',
          rel: 'noopener'
        };
      }
      return {
        to: to
      };
    }
  }
};
</script>

Logo.vue

<template>
  <component :is="type" v-bind="linkProps(to)">
    <slot />
  </component>
</template>

<script>
import { isExternal } from '@crm-pro/utils'

export default {
  props: {
    to: {
      type: String,
      required: true,
    },
  },
  computed: {
    isExternal() {
      return isExternal(this.to)
    },
    type() {
      if (this.isExternal) {
        return 'a'
      }
      return 'router-link'
    },
  },
  methods: {
    linkProps(to) {
      if (this.isExternal) {
        return {
          href: to,
          target: '_blank',
          rel: 'noopener',
        }
      }
      return {
        to: to,
      }
    },
  },
}
</script>

SidebarItem.vue

<template>
  <div v-if="!item.hidden">
    <template
      v-if="
        hasOneShowingChild(item.children, item) &&
        (!onlyOneChild.children || onlyOneChild.noShowingChildren) &&
        !item.alwaysShow
      "
    >
      <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="onlyOneChild.meta.title" />
        </el-menu-item>
      </app-link>
    </template>

    <el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body>
      <template slot="title">
        <item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" />
      </template>
      <sidebar-item
        v-for="child in item.children"
        :key="child.path"
        :is-nest="true"
        :item="child"
        :base-path="resolvePath(child.path)"
        class="nest-menu"
      />
    </el-submenu>
  </div>
</template>

<script>
import path from 'path'
import { isExternal } from '@/utils/validate'
import Item from './Item'
import AppLink from './Link'
import FixiOSBug from './FixiOSBug'

export default {
  name: 'SidebarItem',
  components: { Item, AppLink },
  mixins: [FixiOSBug],
  props: {
    // route object
    item: {
      type: Object,
      required: true,
    },
    isNest: {
      type: Boolean,
      default: false,
    },
    basePath: {
      type: String,
      default: '',
    },
  },
  data() {
    this.onlyOneChild = null
    return {}
  },
  methods: {
    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
      if (showingChildren.length === 1) {
        return true
      }

      // Show parent if there are no child router to display
      if (showingChildren.length === 0) {
        this.onlyOneChild = { ...parent, path: '', noShowingChildren: true }
        return true
      }

      return false
    },
    resolvePath(routePath) {
      if (isExternal(routePath)) {
        return routePath
      }
      if (isExternal(this.basePath)) {
        return this.basePath
      }
      return path.resolve(this.basePath, routePath)
    },
  },
}
</script>

AppMain.vue

<template>
  <section class="app-main">
    <transition name="fade-transform" mode="out-in">
      <router-view />
    </transition>
  </section>
</template>

<script>
export default {
  name: 'AppMain'
};
</script>

<style lang="scss" scoped>
.app-main {
  height: 100%;
  margin-left: 0px;
  position: relative;
  background-color: #fff;
  padding: 20px;
}
.fixed-header+.app-main {
  padding-top: 50px;
}
</style>

<style lang="scss">
// fix css style bug in open el-dialog
.el-popup-parent--hidden {
  .fixed-header {
    padding-right: 15px;
  }
}
</style>

mixin-ResizeHandler.js

import store from '@/store';

const { body } = document;
const WIDTH = 992; // refer to Bootstrap's responsive design

export default {
  watch: {
    $route(route) {
      if (this.device === 'mobile' && this.sidebar.opened) {
        store.dispatch('app/closeSideBar', { withoutAnimation: false });
      }
    }
  },
  beforeMount() {
    window.addEventListener('resize', this.$_resizeHandler);
  },
  beforeDestroy() {
    window.removeEventListener('resize', this.$_resizeHandler);
  },
  mounted() {
    const isMobile = this.$_isMobile();
    if (isMobile) {
      store.dispatch('app/toggleDevice', 'mobile');
      store.dispatch('app/closeSideBar', { withoutAnimation: true });
    }
  },
  methods: {
    // use $_ for mixins properties
    // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
    $_isMobile() {
      const rect = body.getBoundingClientRect();
      return rect.width - 1 < WIDTH;
    },
    $_resizeHandler() {
      if (!document.hidden) {
        const isMobile = this.$_isMobile();
        store.dispatch('app/toggleDevice', isMobile ? 'mobile' : 'desktop');

        if (isMobile) {
          store.dispatch('app/closeSideBar', { withoutAnimation: true });
        }
      }
    }
  }
};

layout-index.vue

<template>
  <div :class="classObj" class="app-wrapper">
    <el-container>
      <el-header height="50px" :style="{ display: 'flex' }">
        <div
          v-if="sidebar.opened"
          @click="handleClickOutside"
          :style="{
            width: '50px',
            height: '100%',
            fontSize: '20px',
            display: 'flex',
            justifyContent: 'center',
            alignItems: 'center',
            color: '#0046ff',
          }"
        >
          <i class="el-icon-s-fold"></i>
        </div>
        <div
          v-if="!sidebar.opened"
          @click="toggleSideBar"
          :style="{
            width: '50px',
            height: '100%',
            fontSize: '20px',
            display: 'flex',
            justifyContent: 'center',
            alignItems: 'center',
            color: '#0046ff',
          }"
        >
          <i class="el-icon-s-unfold"></i>
        </div>
        <topbar />
      </el-header>
      <el-container>
        <el-aside :width="elAsideConWidth" class="elAside-con">
          <app-side-bar class="sidebar-container" />
        </el-aside>
        <el-main>
          <app-main :class="appMainClass" />
        </el-main>
      </el-container>
    </el-container>
  </div>
</template>

<script>
import Topbar from '@/components/Topbar'
import { AppMain, AppSideBar } from './components'
import ResizeMixin from './mixin/ResizeHandler'
import { mapState } from 'vuex'

export default {
  name: 'Layout',
  components: {
    Topbar,
    AppMain,
    AppSideBar,
  },
  mixins: [ResizeMixin],
  data() {
    return {
      isShowSidebar: true,
      elAsideConWidth: '200px', // 55px
    }
  },
  computed: {
    ...mapState({
      sidebar: state => state.app.sidebar,
      device: state => state.app.device,
      showSettings: state => state.settings.showSettings,
      needTagsView: state => state.settings.tagsView,
      fixedHeader: state => state.settings.fixedHeader,
    }),
    classObj() {
      return {
        openSidebar: this.sidebar.opened,
        hideSidebar: !this.sidebar.opened,
      }
    },
    appMainClass() {
      const pathClass = this.$route.path.substr(1).replace(/\//g, '-')
      return {
        [pathClass]: true,
      }
    },
  },
  watch: {
    $route(to, from) {
      // 监听路由发生变化判断是否显示侧边栏
      this.handleShowSide()
    },
  },
  mounted() {
    // 判断路由是否是帐号管理并显示侧边栏
    this.handleShowSide()
    // console.log('this.sidebar.opened ===>',JSON.stringify(this.sidebar.opened));
    if (this.sidebar.opened === true || this.sidebar.opened === 1) {
      // this.toggleSideBar()
      this.elAsideConWidth = '200px'
    }
    if (this.sidebar.opened === false || this.sidebar.opened === 0) {
      // this.handleClickOutside()
      this.elAsideConWidth = '55px'
    }
  },
  methods: {
    handleShowSide() {
      this.isShowSidebar = true
    },
    toggleSideBar() {
      this.elAsideConWidth = '200px'
      this.$store.dispatch('app/toggleSideBar')
    },
    handleClickOutside() {
      this.elAsideConWidth = '55px'
      this.$store.dispatch('app/closeSideBar', { withoutAnimation: false })
    },
  },
}
</script>

<style lang="scss" scoped>
@import '~@/styles/mixin.scss';
@import '~@/styles/variables.scss';

.app-wrapper {
  @include clearfix;
  position: relative;
  height: 100vh;
  width: 100%;
  overflow: hidden;

  &.openSidebar {
    position: fixed;
    top: 0;
  }

  .elAside-con {
    // position: relative;
    .elAside-switch-1 {
      position: absolute;
      z-index: 10000000;
      left: 200px;
      top: 50px;
      color: black;
    }

    .elAside-switch-2 {
      position: absolute;
      z-index: 10000000;
      left: 55px;
      top: 50px;
      color: black;
    }
  }
}

.el-container {
  min-height: 100%;
}

.el-header {
  background-color: $white;
  line-height: 50px;
  position: fixed;
  left: 0;
  right: 0;
  top: 0;
  height: 50px;
  z-index: 99;
  box-shadow: 0 1px 2px 0 rgba(41, 48, 64, 0.2);
  padding: 0 20px 0 0;
}

.el-aside {
  z-index: 9;
}

.el-footer {
  background-color: $navBg;
  line-height: 50px;
}

.el-main {
  height: 100vh;
  padding: 66px 16px 0;
  background-color: #f5f6f7;
  margin: 0;
}

.app-side-bar {
  width: 240px;
}

.app-main {
  flex: 1;
  word-break: break-all;
  overflow: auto;
}

.performance-totalOverview {
  background-color: transparent;
  padding: 0;
}

.hideSidebar {
  .sidebar-container {
    width: 54px !important;
  }

  .main-container {
    margin-left: 54px;
  }

  .submenu-title-noDropdown {
    padding: 0 !important;
    position: relative;

    .el-tooltip {
      padding: 0 !important;

      .svg-icon {
        margin-left: 20px;
      }

      .sub-el-icon {
        margin-left: 19px;
      }
    }
  }

  .el-submenu {
    overflow: hidden;

    & > .el-submenu__title {
      padding: 0 !important;

      .svg-icon {
        margin-left: 20px;
      }

      .sub-el-icon {
        margin-left: 19px;
      }

      .el-submenu__icon-arrow {
        display: none;
      }
    }
  }

  .el-menu--collapse {
    .el-submenu {
      & > .el-submenu__title {
        & > span {
          height: 0;
          width: 0;
          overflow: hidden;
          visibility: hidden;
          display: inline-block;
        }
      }
    }
  }
}
</style>

import { UserInfo } from '@/utils/auth.js'文件

import Cookies from 'js-cookie'

// 本地存储的key值,要保证各系统中的一致性
const StoreKeys = {
  token: '',
  userinfo: '',
}

const UserStore = {
  get: key => JSON.parse(window.localStorage.getItem(key)),
  set: (key, value) => window.localStorage.setItem(key, JSON.stringify(value)),
  remove: key => window.localStorage.removeItem(key),
}

// 设置配置参数
export function setup(...param) {
  const { token, userinfo } = UserStore.get('cc-app-config') || {}
  console.log('setup 0 ==>', param, token, userinfo)
  if (!token || !userinfo) {
    return goHome()
  }

  StoreKeys.token = token
  StoreKeys.userinfo = userinfo
  console.log('setup 1==>', StoreKeys)
}

setup()

// local run
// Cookies.set(StoreKeys.token, 'ss111');
// UserStore.set({ token: 'ss111' });

/**
 * 生成登录用户实例,包含以下属性
    companyName: null
    deptName: null
    displayName: "系统管理员"
    email: null
    mobile: null
    token: "7fb2be19f6d947db8caaa7ce1148e0c3"
    expire: 3600 // 有效期s
    userId: "1348933878131302402"
    userName: "cdp-admin"
    userRoleNameList: null
    userType: "1",

   

    menuPermission: [],
 * @param {Object} store 用户信息存储实例
 * @returns 用户信息
 */
function createUserInfo(store) {
  const sKey = StoreKeys.userinfo
  let curInfo = store.get(sKey) || {}

  const userInfo = {
    // 获取、设置属性值
    value: (key, value) => {
      if (value === undefined) {
        return curInfo[key]
      }
      curInfo[key] = value
      // store.set(sKey, curInfo); // 值有变化,更新到store中
      return userInfo
    },
    remove: key => {
      delete curInfo[key]
      return userInfo
    },
    save: () => {
      store.set(sKey, curInfo)
    },
    clear: () => {
      curInfo = {}
      Cookies.remove(sKey)
    },
  }

  return userInfo
}

// 当前登录用户信息
export const UserInfo = createUserInfo(UserStore)

export function setView(resource) {
  const resourceKeys = [
    'productGroupId',
    'productGroupName',
    'projectId',
    'projectName',
    'pmProjectId',
    'pmProjectName',
  ]

  // 各资源之前为互斥操作,在设置之前先全部清除
  resourceKeys.forEach(key => {
    UserInfo.remove(key)
  })

  // 设置对应的参数
  resourceKeys.filter(key => resource[key] !== undefined).forEach(key => UserInfo.value(key, resource[key]))

  // 持久化
  UserInfo.save()

  goHome(false)
}

export function goHome(redirect, ...param) {
  console.log('获取首页 0 ==>', redirect, location, param)
  if (window.location.pathname === '/') {
    return
  }

  let url = '/?redirect=' + encodeURIComponent(location.pathname + location.hash)
  if (redirect === false) {
    url = '/'
  }
  console.log('获取首页 1 ==>', redirect, url, location)
  window.location = url
}

function createToken() {
  const sKey = StoreKeys.token
  let curToken = Cookies.get(sKey)
  return {
    get: () => {
      // console.log('进入这里0', curToken);
      if (!curToken) {
        return undefined
      }
      // console.log('进入这里1', curToken !== UserInfo.value('token'));
      if (curToken !== UserInfo.value('token')) {
        // console.log('###########', sKey)
        // 异常,用户token和保存的不一致,可能是被篡改
        Cookies.remove(sKey)
        return undefined
      }
      return curToken
    },
    set: value => {
      curToken = value
      Cookies.set(sKey, value)
    },
    clear: () => {
      curToken = ''
      Cookies.remove(sKey)
    },
  }
}

// Token信息
export const Token = createToken()

export default {
  clear: () => {
    Token.clear()
    UserInfo.clear()
  },
}

import { isExternal } from '@/utils/validate.js';文件

export function isExternal(path) {
  return /^(https?:|mailto:|tel:)/.test(path);
}

import Topbar from '@/components/Topbar.vue'

<template>
  <div v-if="!item.hidden">
    <template v-if="hasOneShowingChild(item.children, item) && (!onlyOneChild.children || onlyOneChild.noShowingChildren) && !item.alwaysShow">
      <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="onlyOneChild.meta.title" />
        </el-menu-item>
      </app-link>
    </template>

    <el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body>
      <template slot="title">
        <item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" />
      </template>
      <sidebar-item v-for="child in item.children" :key="child.path" :is-nest="true" :item="child" :base-path="resolvePath(child.path)" class="nest-menu" />
    </el-submenu>
  </div>
</template>

<script>
import path from 'path'
import { isExternal } from '@crm-pro/utils'
import Item from './Item'
import AppLink from './Link'
import FixiOSBug from './FixiOSBug'

export default {
  name: 'SidebarItem',
  components: { Item, AppLink },
  mixins: [FixiOSBug],
  props: {
    // route object
    item: {
      type: Object,
      required: true,
    },
    isNest: {
      type: Boolean,
      default: false,
    },
    basePath: {
      type: String,
      default: '',
    },
  },
  data() {
    this.onlyOneChild = null
    return {}
  },
  methods: {
    hasOneShowingChild(children = [], parent) {
      const showingChildren = children.filter((item) => {
        if (item.hidden) {
          return false
        } else {
          this.onlyOneChild = item
          return true
        }
      })

      // When there is only one child router, the child router is displayed by default
      if (showingChildren.length === 1) {
        return true
      }

      // Show parent if there are no child router to display
      if (showingChildren.length === 0) {
        this.onlyOneChild = { ...parent, path: '', noShowingChildren: true }
        return true
      }

      return false
    },
    resolvePath(routePath) {
      if (isExternal(routePath)) {
        return routePath
      }
      if (isExternal(this.basePath)) {
        return this.basePath
      }
      return path.resolve(this.basePath, routePath)
    },
  },
}
</script>

import Log from '@/utils/log'

import { parseTime } from './time'

const Config = {
  debug: true || /(&|\?)debug=true/.test(window.location.search),
  log: true,
  error: true,
}

window.console.setLog = (sType, bValue) => {
  Config[sType] = bValue
}

function showMsg(isShow, ...msg) {
  if (!isShow) {
    return
  }

  console.log(parseTime(new Date()), ...msg)
}
function MyLog(sModuleName) {
  return {
    debug: (...msg) => {
      // 调试信息,在发矶版本中会被关闭
      const title = `[debug]${sModuleName}`
      showMsg(Config.debug, title, ...msg)
    },
    log: (...msg) => {
      // 一般性信息闭
      const title = `[log]${sModuleName}`
      showMsg(Config.log, title, ...msg)
    },
    error: (...msg) => {
      // 错误信息在不强制关闭的情况下都要显示闭
      const title = `[error]${sModuleName}`
      showMsg(Config.error !== false, title, ...msg)
    },
  }
}

export default MyLog

import { parseTime } from './time'


export function parseTime(time, cFormat) {
  if (arguments.length === 0 || !time) {
    return null
  }
  const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
  let date
  if (typeof time === 'object') {
    date = time
  } else {
    if (typeof time === 'string') {
      if (/^[0-9]+$/.test(time)) {
        // support "1548221490638"
        time = parseInt(time)
      } else {
        time = time.replace(new RegExp(/-/gm), '/')
      }
    }

    if (typeof time === 'number' && time.toString().length === 10) {
      time = time * 1000
    }
    date = new Date(time)
  }
  const formatObj = {
    y: date.getFullYear(),
    m: date.getMonth() + 1,
    d: date.getDate(),
    h: date.getHours(),
    i: date.getMinutes(),
    s: date.getSeconds(),
    a: date.getDay(),
  }
  const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => {
    const value = formatObj[key]
    // Note: getDay() returns 0 on Sunday
    if (key === 'a') {
      return ['日', '一', '二', '三', '四', '五', '六'][value]
    }
    return value.toString().padStart(2, '0')
  })
  return time_str
}

/**
 * @param {number} time
 * @param {string} option
 * @returns {string}
 */
export function formatTime(time, option) {
  if (('' + time).length === 10) {
    time = parseInt(time) * 1000
  } else {
    time = +time
  }
  const d = new Date(time)
  const now = Date.now()

  const diff = (now - d) / 1000

  if (diff < 30) {
    return '刚刚'
  } else if (diff < 3600) {
    // less 1 hour
    return Math.ceil(diff / 60) + '分钟前'
  } else if (diff < 3600 * 24) {
    return Math.ceil(diff / 3600) + '小时前'
  } else if (diff < 3600 * 24 * 2) {
    return '1天前'
  }
  if (option) {
    return parseTime(time, option)
  } else {
    return d.getMonth() + 1 + '月' + d.getDate() + '日' + d.getHours() + '时' + d.getMinutes() + '分'
  }
}

import settings from '@/settings'

const defSettings = {};

module.exports = {
  ...defSettings,

  /**
   * @type {boolean} true | false
   * @description Whether fix the header
   */
  fixedHeader: false,

  /**
   * @type {boolean} true | false
   * @description Whether show the logo in sidebar
   */
  sidebarLogo: false,

 
  subProductVersion: ''
};

router-index.js

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

import Log from '@/utils/log'
import constantRoutes from './datas.js'
import store from '@/store'
import { get } from '../utils/request'

const log = Log('router')

function filterRouter(routers) {
  function _filter(nodes) {
    return nodes.filter(node => {
      // 不支持的跳由,直接过滤掉
      if (node.supported === false) {
        log.debug(node.name, 'is not supported')
        return false
      }

      // 有子路由,且没有符合条件的子路由时,过滤掉
      if (node.children) {
        node.children = _filter(node.children)
        if (node.children.length === 0) {
          log.debug(node.name, 'has no children')
          return false
        }
      }

      // 默认保留
      return true
    })
  }
  // console.log(_filter(routers))
  return _filter(routers)
}
export function getMenuPermission() {
  return get('base/user/getMenuPermission').then(({ data }) => {
    const optList = data.operationList.reduce((result, item) => {
      result[item] = true
      return result
    }, {})
    store.dispatch('user/resetOptList', optList)
    resetRouter(router, data.menuNameList)
  })
}

const router = new Router({
  // mode: 'history', // require service support
  scrollBehavior: () => ({ y: 0 }),
  routes: filterRouter(constantRoutes),
})

export default function createRouter() {
  return getMenuPermission().then(() => router)
}

// 路由通知
const onRouterChange = {
  data: null,
  fn: [],
}

function resetRouter(router, menuList) {
  function checkExist(item, menuList) {
    if (item.name && !menuList.includes(item.name)) {
      return false
    }
    if (item.children) {
      item.children = item.children.filter(item1 => checkExist(item1, menuList))
      return item.children.length
    }
    return true
  }
  // 把过滤好的路由缓存,并通知订阅者
  const routes = router.options.routes.filter(item => checkExist(item, menuList))
  onRouterChange.data = routes
  onRouterChange.fn.forEach(cb => cb(routes))
}

export function routerChange(cb) {
  log.debug('onRouterChange')
  if (onRouterChange.data) {
    cb(onRouterChange.data)
  }
  onRouterChange.fn.push(cb)
}

router-data.js

export default [
  {
    path: '/',
    component: () => import('@/layout'),
    redirect: '/workbench',
    children: [
      {
        path: 'workbench',
        // hidden: true,
        component: () => import('@/views/workbench/index'),
        meta: { title: '他', icon: 'el-icon-postcard', activeMenu: 'workbench' },
      },

      {
        path: 'www',
        component: () => import('@/views/index'),
        name: '',
        meta: { title: '12', icon: 'el-icon-s-cooperation', activeMenu: 'www' },
        children: [
          {
            path: '/www/list',
            component: () => import('@/views/www/qqq'),
            meta: { title: '123', icon: '', parent: '12', activeMenu: '/www/list' },
          },
          {
            path: 'ccc',
            component: () => import('@/views/index'),
            name: '123',
            meta: { title: '123', icon: '', parent: '12', activeMenu: 'ccc' },
            children: [
              {
                path: '/ccc/list',
                name: '88',
                component: () => import('@/views/ggg/index.vue'),
                meta: {
                  title: '88',
                  icon: '',
                  parent: '123',
                  activeMenu: '/ccc/listt',
                },
              },
            ],
          },
        ],
      },
      {
        path: '/404',
        component: () => import('@/views/404'),
        hidden: true,
      },
    ],
  },
]

import { get } from '../utils/request'

import axios from 'axios'
import { Message, Loading } from 'ccxd-ux'
import auth, { Token, goHome } from '@/utils/auth'
import qs from 'qs'
import cacheRequest from './requestCache'

let loadingInstance1 = null
const loadingList = [
  '/baidu',
 
]

const Config = {
  baseUrl: '/api/v1/',
}

// 补全URL路径
// 除以下两种情况,都会增加Config.baseUrl前缀
//   1。 以http:// or https://开头
//   2。 以 / 开头
function getUrl(url, params) {
  let sUrl = url
  Object.keys(params || {}).forEach(name => {
    sUrl = sUrl.replace(`{${name}}`, params[name])
  })

  // 以 / 开头的路径不需增加前缀
  if (sUrl.charAt(0) === '/') {
    return sUrl
  }

  // 以http:// or https://开头的路径不需增加前缀
  if (/^https?:\/\//.test(sUrl)) {
    return sUrl
  }

  // 所有的相对路径都要增加前缀
  return `${Config.baseUrl}${sUrl}`
}

function showError(errMsg) {
  const sMsg = errMsg || 'Error'
  Message({
    message: sMsg,
    type: 'error',
    duration: 5000,
  })

  return Promise.reject(new Error(sMsg))
}

// create an axios instance
const service = axios.create({
  // baseURL: Config.baseUrl,
  // withCredentials: true, // send cookies when cross-domain requests
  timeout: 150000, // request timeout
  paramsSerializer: function (params) {
    return qs.stringify(params, { arrayFormat: 'brackets' })
  },
})

// request interceptor
service.interceptors.request.use(
  config => {
    // do something before request is sent

    if (config.method === 'get') {
      // 如果是get请求,且params是数组类型如arr=[1,2],则转换成arr=1&arr=2
      config.paramsSerializer = function (params) {
        return qs.stringify(params, { arrayFormat: 'repeat' })
      }
    }
    // 控制台视图中必须是登录后访问,因此肯定存在token
    config.headers.common['Authorization'] = Token.get() || sessionStorage.getItem('bToken')
    // 产品视图时,header中需要增加projectId
    config.headers.common['ResourceView'] = `null:null`
    if (Object.prototype.toString.call(sessionStorage.getItem('loginType')) !== '[object Null]') {
      config.headers.common['type'] = sessionStorage.getItem('loginType')
    }
    // loadingInstance1 = Loading.service({ fullscreen: true })
    if (loadingList.indexOf(config.url) >= 0 || loadingList.length == 0) {
      loadingInstance1 = Loading.service({
        lock: true,
        text: '数据正在加载,请稍后...',
        spinner: 'el-icon-loading',
        background: 'rgba(0, 0, 0, 0.01)',
      })
    }
    return config
  },
  error => {
    // do something with request error
    loadingInstance1 && loadingInstance1.close()
    console.log(error) // for debug
    return Promise.reject(error)
  }
)

// response interceptor
service.interceptors.response.use(
  /**
   * If you want to get http information such as headers or status
   * Please return  response => response
   */

  /**
   * Determine the request status by custom code
   * Here is just an example
   * You can also judge the status by HTTP Status Code
   */
  response => {
    //如果接口返回不是'application/json'类型,就走下载
    loadingInstance1 && loadingInstance1.close()
    // if (response.headers['content-type'] !== 'application/json') {
    if (!response.headers['content-type'] || response.headers['content-type'].indexOf('json') < 0) {
      let name = ''
      if (response.headers['content-disposition']) {
        name =
          response.headers['content-disposition']
            .split(';')
            .map(item => {
              const [key, value] = item.split('=')
              return { key, value }
            })
            .filter(item => item.key === 'filename')
            .map(item => item.value)[0] || 'file'
      } else {
        name = response.config.url.split('/').pop()
      }
      let blob = new Blob([response.data])
      let url = window.URL.createObjectURL(blob)
      let aLink = document.createElement('a')
      aLink.style.display = 'none'
      aLink.href = url
      aLink.setAttribute('download', decodeURI(name))
      document.body.appendChild(aLink)
      aLink.click()
      document.body.removeChild(aLink) // 下载完成移除元素
      window.URL.revokeObjectURL(url) // 释放掉blob对象
      return response.data
    } else {
      const res = response.data
      if (
        res.code === 20000 ||
        res.code === 0 ||
        res.code === 3110200005 ||
        res.respCode === '1000' ||
        res.respCode === 1000
      ) {
        return res
      }
      // 状态码202xx为无效token,重新登录
      if (res.code >= 20200 && res.code < 20300) {
        auth.clear()
        goHome(true, 0)
      }

      // if the custom code is not 20000, it is judged as an error.
      return showError(res.message)
    }
  },
  error => {
    // loadingInstance1 && loadingInstance1.close()
    console.log('err' + error) // for debug
    return showError(error.message)
  }
)

export default service

export function get(url, params, config = {}) {
  const sUrl = getUrl(url, params)
  const fnRequest = () => service.get(sUrl, { params, ...config })
  return cacheRequest(sUrl, fnRequest)
}

export function post(sUrl, data, config = {}) {
  return service.post(getUrl(sUrl, data), data, config)
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值