修复redirect过快导致路由未跳转的问题

代码优化
双层侧边栏导航准备工作
master
toesbieya 6 years ago
parent 875ad55d90
commit a6a19976e2
  1. 2
      vue/src/asset/style/variables.scss
  2. 3
      vue/src/component/Charts/PieChart.vue
  3. 2
      vue/src/component/ExtraArea/index.vue
  4. 1
      vue/src/component/checkbox/ColorCheckbox.vue
  5. 2
      vue/src/component/checkbox/Group.vue
  6. 2
      vue/src/component/checkbox/ImgCheckbox.vue
  7. 41
      vue/src/component/menu/NavMenu/index.vue
  8. 12
      vue/src/layout/component/Aside/TwoPart/index.vue
  9. 18
      vue/src/layout/component/Aside/TwoPart/root.vue
  10. 20
      vue/src/layout/component/Aside/TwoPart/sub.vue
  11. 61
      vue/src/layout/component/Aside/index.vue
  12. 1
      vue/src/layout/component/Header/component/Navbar/component/HeadMenu.vue
  13. 10
      vue/src/layout/component/Header/component/Navbar/index.vue
  14. 17
      vue/src/layout/component/Header/component/TagsView/ScrollPanel.vue
  15. 5
      vue/src/layout/component/SettingDrawer.vue
  16. 17
      vue/src/layout/mixin/menu.js
  17. 2
      vue/src/layout/store/app.js
  18. 3
      vue/src/layout/util.js
  19. 9
      vue/src/layout/布局设计.md
  20. 9
      vue/src/mixin/guide/base.js
  21. 3
      vue/src/router/define.js
  22. 16
      vue/src/router/index.js
  23. 38
      vue/src/util/browser.js
  24. 35
      vue/src/util/element-ui/elMenu.js
  25. 1
      vue/src/util/element-ui/elTree.js
  26. 2
      vue/src/util/excel/common.js
  27. 101
      vue/src/util/index.js
  28. 17
      vue/src/view/app/redirect.vue

@ -2,7 +2,7 @@
$max-mobile-width: 500px; $max-mobile-width: 500px;
//覆盖element颜色 //覆盖element颜色
$--color-primary: #1890ff; $--color-primary: #1890ff; //这里如果有改动需要同时修改vue.config.js中ThemeColorReplacer的配置
$--color-success: #21BA45; $--color-success: #21BA45;
$--color-warning: #FF5722; $--color-warning: #FF5722;
$--color-danger: #F44336; $--color-danger: #F44336;

@ -17,7 +17,6 @@ export default {
data: { data: {
deep: true, deep: true,
handler(val) { handler(val) {
if (!window.echarts) return
this.init(val) this.init(val)
} }
} }
@ -25,7 +24,7 @@ export default {
methods: { methods: {
init(data) { init(data) {
this.chart.setOption({ this.chart && this.chart.setOption({
title: { title: {
text: this.title, text: this.title,
left: 'center', left: 'center',

@ -12,7 +12,7 @@
</el-row> </el-row>
</template> </template>
<script type="text/jsx"> <script>
export default { export default {
name: "ExtraArea", name: "ExtraArea",

@ -20,6 +20,7 @@ export default {
.color-checkbox { .color-checkbox {
width: 20px; width: 20px;
height: 20px; height: 20px;
margin: 5px;
border-radius: 2px; border-radius: 2px;
cursor: pointer; cursor: pointer;
text-align: center; text-align: center;

@ -15,7 +15,7 @@ export default {
<style> <style>
.checkbox-group { .checkbox-group {
display: flex; display: flex;
justify-content: space-around; flex-wrap: wrap;
width: 100%; width: 100%;
} }
</style> </style>

@ -23,6 +23,8 @@ export default {
@import "~@/asset/style/variables.scss"; @import "~@/asset/style/variables.scss";
.img-checkbox { .img-checkbox {
width: 55px;
margin: 10px;
border-radius: 2px; border-radius: 2px;
cursor: pointer; cursor: pointer;
position: relative; position: relative;

@ -0,0 +1,41 @@
<script type="text/jsx">
import Item from './item'
export default {
name: "NavMenu",
functional: true,
props: {
menus: Array,
mode: {type: String, default: 'vertical'},
collapse: Boolean,
showParentOnCollapse: Boolean,
showIconMaxDepth: {type: Number, default: 1}
},
render(h, context) {
const {data, props: {mode, menus, collapse, showParentOnCollapse, showIconMaxDepth}} = context
return (
<el-menu
ref="menu"
class={`el-menu--${mode}`}
mode={mode}
collapse={collapse}
collapse-transition={false}
{...data}
>
{menus.map(m => (
<Item
menu={m}
show-parent={showParentOnCollapse}
collapse={collapse}
show-icon-max-depth={showIconMaxDepth}
/>
))}
</el-menu>
)
}
}
</script>

@ -0,0 +1,12 @@
<script>
export default {
name: "TwoPartSidebar",
data() {
return {}
},
methods: {},
mounted() {
}
}
</script>

@ -0,0 +1,18 @@
<script type="text/jsx">
export default {
name: "TwoPartRootSidebar",
data() {
return {
//
mouseOutside: true
}
},
methods: {},
mounted() {
}
}
</script>

@ -0,0 +1,20 @@
<template>
</template>
<script>
export default {
name: "TwoPartSubSidebar",
data() {
return {}
},
methods: {},
mounted() {
}
}
</script>
<style scoped>
</style>

@ -2,10 +2,10 @@
import menuMixin from "@/layout/mixin/menu" import menuMixin from "@/layout/mixin/menu"
import {getters as appGetters} from "@/layout/store/app" import {getters as appGetters} from "@/layout/store/app"
import {getters as asideGetters, mutations as asideMutations} from "@/layout/store/aside" import {getters as asideGetters, mutations as asideMutations} from "@/layout/store/aside"
import Logo from './Logo' import Logo from '@/layout/component/Aside/Logo'
import NavMenuItem from '@/component/menu/NavMenu/item' import NavMenuItem from '@/component/menu/NavMenu/item'
import {getSidebarMenus, getActiveMenuByRoute} from "@/layout/util" import {getSidebarMenus, getActiveMenuByRoute} from "@/layout/util"
import {getTopDistance} from "@/util/browser" import {moveToActiveMenuVertically} from "@/util/element-ui/elMenu"
export default { export default {
name: 'Aside', name: 'Aside',
@ -23,7 +23,6 @@ export default {
computed: { computed: {
isMobile: () => appGetters.isMobile, isMobile: () => appGetters.isMobile,
activeRootMenu: () => appGetters.activeRootMenu,
// //
menus: () => getSidebarMenus(), menus: () => getSidebarMenus(),
@ -48,7 +47,7 @@ export default {
if (!this.mouseOutside) return true if (!this.mouseOutside) return true
// true // true
return this.collapse && this.openedMenus.length > 0 return this.collapse && this.openedMenuNum > 0
}, },
//truefalsepc //truefalsepc
@ -104,11 +103,6 @@ export default {
} }
}, },
//ele
activeRootMenu(v) {
v && this.resetActiveMenu()
},
// //
show(v) { show(v) {
v && this.$nextTick(this.moveToCurrentMenu) v && this.$nextTick(this.moveToCurrentMenu)
@ -153,52 +147,7 @@ export default {
// //
moveToCurrentMenu() { moveToCurrentMenu() {
const menu = this.$refs.menu, cur = this.activeMenu moveToActiveMenuVertically(this.$refs.menu)
if (!menu || !cur) return
const curInstance = menu.items[cur]
if (!curInstance) return
let el = curInstance.$el
//
if (this.collapse) {
let rootParent = curInstance
while (rootParent.$parent.$options.componentName !== 'ElMenu') {
rootParent = rootParent.$parent
}
el = rootParent.$el
}
/*
* 这里考虑了菜单展开时的200ms动画时间
* 为什么不分情况讨论比如当subMenu已经是展开状态时无需延时滚动
* 但这种情况无法判断因为这时menu.openedMenus已经包含了subMenu无论subMenu之前是否展开
* 所以统一延时300ms
* */
window.setTimeout(() => this.scrollMenuIntoView(el, menu.$el), 300)
},
//
scrollMenuIntoView(el, container) {
const {scrollTop, scrollHeight, offsetHeight: menuHeight} = container
//
if (scrollHeight <= menuHeight) return
const elHeight = el.offsetHeight, between = getTopDistance(el, container)
//undefined
let distance
if (between < 0) distance = between
else if (between + elHeight > menuHeight) {
distance = between + elHeight - menuHeight
}
if (distance !== undefined) {
container.scrollTo({top: scrollTop + distance, behavior: 'smooth'})
}
} }
}, },
@ -229,7 +178,7 @@ export default {
<nav-menu-item <nav-menu-item
menu={m} menu={m}
show-parent={asideGetters.showParentOnCollapse} show-parent={asideGetters.showParentOnCollapse}
collapse={asideGetters.collapse} collapse={this.collapse}
show-icon-max-depth={1} show-icon-max-depth={1}
/> />
))} ))}

@ -81,6 +81,7 @@ export default {
else this.activeMenu = getActiveMenuByRoute({path, meta}) else this.activeMenu = getActiveMenuByRoute({path, meta})
}, },
onSelect(index) { onSelect(index) {
//
if (this.navMode === 'mix' && index === appGetters.activeRootMenu) { if (this.navMode === 'mix' && index === appGetters.activeRootMenu) {
return return
} }

@ -4,7 +4,7 @@ import GuideMixin from '@/mixin/guide'
import Bell from "./component/Bell" import Bell from "./component/Bell"
import Hamburger from './component/Hamburger' import Hamburger from './component/Hamburger'
import HeadMenu from "./component/HeadMenu" import HeadMenu from "./component/HeadMenu"
import SettingDrawer from './component/SettingDrawer' import SettingDrawer from '@/layout/component/SettingDrawer'
import {getters as appGetters} from "@/layout/store/app" import {getters as appGetters} from "@/layout/store/app"
import {getters as asideGetters, mutations as asideMutations} from "@/layout/store/aside" import {getters as asideGetters, mutations as asideMutations} from "@/layout/store/aside"
import {getSidebarMenus} from "@/layout/util" import {getSidebarMenus} from "@/layout/util"
@ -42,13 +42,13 @@ export default {
// //
// //
// //
// //
// //
const hasSidebarMenus = getSidebarMenus().length > 0, const hasSidebarMenus = getSidebarMenus().length > 0,
isMobile = appGetters.isMobile, isMobile = appGetters.isMobile,
notHeadMode = appGetters.navMode !== 'head', correctMode = ['aside','mix'].includes(appGetters.navMode),
notAutoHidden = !asideGetters.autoHide, notAutoHidden = !asideGetters.autoHide,
renderHamburger = hasSidebarMenus && (isMobile || notHeadMode && notAutoHidden) renderHamburger = hasSidebarMenus && (isMobile || correctMode && notAutoHidden)
if (renderHamburger) { if (renderHamburger) {
const active = isMobile ? asideGetters.show : !asideGetters.collapse const active = isMobile ? asideGetters.show : !asideGetters.collapse
@ -56,7 +56,7 @@ export default {
} }
}, },
renderHeadMenuMenu() { renderHeadMenuMenu() {
if (appGetters.navMode !== 'aside' && !appGetters.isMobile) { if (['head', 'mix'].includes(appGetters.navMode) && !appGetters.isMobile) {
return <head-menu always-show/> return <head-menu always-show/>
} }
}, },

@ -35,7 +35,9 @@ export default {
}, },
moveToTarget(currentTag, tagInstances) { moveToTarget(currentTag, tagInstances) {
const {offsetWidth: $containerWidth, scrollWidth, scrollLeft} = this.$el const {offsetWidth: containerWidth, scrollWidth, scrollLeft} = this.$el
if (containerWidth >= scrollWidth) return
let firstTag = null let firstTag = null
let lastTag = null let lastTag = null
@ -47,12 +49,12 @@ export default {
} }
if (firstTag === currentTag) { if (firstTag === currentTag) {
this.scrollLeft(0) return this.scrollLeft(0)
} }
else if (lastTag === currentTag) { if (lastTag === currentTag) {
this.scrollLeft(scrollWidth - $containerWidth) return this.scrollLeft(scrollWidth - containerWidth)
} }
else {
// find preTag and nextTag // find preTag and nextTag
const currentIndex = tagInstances.findIndex(item => item === currentTag) const currentIndex = tagInstances.findIndex(item => item === currentTag)
const prevTag = tagInstances[currentIndex - 1] const prevTag = tagInstances[currentIndex - 1]
@ -64,13 +66,12 @@ export default {
// the tag's offsetLeft before of prevTag // the tag's offsetLeft before of prevTag
const beforePrevTagOffsetLeft = prevTag.$el.offsetLeft - this.between const beforePrevTagOffsetLeft = prevTag.$el.offsetLeft - this.between
if (afterNextTagOffsetLeft > scrollLeft + $containerWidth) { if (afterNextTagOffsetLeft > scrollLeft + containerWidth) {
this.scrollLeft(afterNextTagOffsetLeft - $containerWidth) this.scrollLeft(afterNextTagOffsetLeft - containerWidth)
} }
else if (beforePrevTagOffsetLeft < scrollLeft) { else if (beforePrevTagOffsetLeft < scrollLeft) {
this.scrollLeft(beforePrevTagOffsetLeft) this.scrollLeft(beforePrevTagOffsetLeft)
} }
}
}, },
scrollLeft(val) { scrollLeft(val) {

@ -112,6 +112,11 @@ export default {
value: 'mix', value: 'mix',
img: 'https://gw.alipayobjects.com/zos/antfincdn/x8Ob%26B8cy8/LCkqqYNmvBEbokSDscrm.svg' img: 'https://gw.alipayobjects.com/zos/antfincdn/x8Ob%26B8cy8/LCkqqYNmvBEbokSDscrm.svg'
}, },
{
title: '双层侧边栏导航',
value: 'aside-two-part',
img: 'https://gw.alipayobjects.com/zos/antfincdn/XwFOFbLkSM/LCkqqYNmvBEbokSDscrm.svg'
}
], ],
setting: { setting: {

@ -9,10 +9,11 @@ export default {
data() { data() {
return { return {
//当前激活的菜单的fullPath //当前激活的菜单的fullPath
//之所以手动维护是因为el-menu在点击后就会设置activeIndex
activeMenu: '', activeMenu: '',
//el-menu的openedMenus //用于判断鼠标是否在弹出菜单内
openedMenus: [] openedMenuNum: 0
} }
}, },
@ -23,11 +24,7 @@ export default {
if (isExternal(fullPath)) { if (isExternal(fullPath)) {
window.open(fullPath) window.open(fullPath)
//重置高亮菜单 //重置高亮菜单
const menu = this.$refs.menu return this.resetActiveMenu()
if (menu && menu.updateActiveIndex) {
menu.updateActiveIndex(this.activeMenu)
}
return
} }
//触发的菜单路径是当前路由时,根据参数判断是否进行刷新 //触发的菜单路径是当前路由时,根据参数判断是否进行刷新
@ -39,11 +36,11 @@ export default {
else this.$router.push(fullPath) else this.$router.push(fullPath)
}, },
resetActiveMenu() {
//由于侧边栏菜单数组更新后,el-menu不一定会更新(当数组中不存在单级菜单时) //由于侧边栏菜单数组更新后,el-menu不一定会更新(当数组中不存在单级菜单时)
//所以手动更新el-menu的当前高亮菜单 //所以手动更新el-menu的当前高亮菜单
resetActiveMenu() {
const menu = this.$refs.menu const menu = this.$refs.menu
menu && menu.updateActiveIndex() menu && menu.updateActiveIndex(this.activeMenu)
}, },
//渲染el-menu时监听其展开菜单 //渲染el-menu时监听其展开菜单
@ -58,7 +55,7 @@ export default {
if (!menu) return if (!menu) return
this.watchOpenedMenusCallback = menu.$watch('openedMenus', v => { this.watchOpenedMenusCallback = menu.$watch('openedMenus', v => {
this.openedMenus = [...v] this.openedMenuNum = v.length
}) })
} }
} }

@ -15,7 +15,7 @@ const state = {
//主题色 //主题色
color: '#1890ff', color: '#1890ff',
//导航模式 //导航模式,'aside'、'aside-two-part'、'head'、'mix'
navMode: 'mix' navMode: 'mix'
} }

@ -17,12 +17,15 @@ export function getSidebarMenus() {
} }
switch (appGetters.navMode) { switch (appGetters.navMode) {
case 'aside': case 'aside':
case 'aside-two-part':
return menus return menus
case 'head': case 'head':
return [] return []
case 'mix': case 'mix':
const root = menus.find(i => i.path === appGetters.activeRootMenu) const root = menus.find(i => i.path === appGetters.activeRootMenu)
return root ? root.children || [] : [] return root ? root.children || [] : []
default:
return []
} }
} }

@ -10,6 +10,8 @@
3.混合导航:顶部菜单为完整菜单的所有根节点,侧边栏菜单为当前激活的顶部菜单的子级 3.混合导航:顶部菜单为完整菜单的所有根节点,侧边栏菜单为当前激活的顶部菜单的子级
4.双层侧边栏导航:侧边栏分为最左侧的根节点栏、二级子菜单,顶部菜单不渲染
### 移动端 ### 移动端
1.侧边栏导航:侧边栏菜单为完整菜单,顶部菜单不渲染,侧边栏为抽屉模式 1.侧边栏导航:侧边栏菜单为完整菜单,顶部菜单不渲染,侧边栏为抽屉模式
@ -18,6 +20,8 @@
3.混合导航:设置无效,效果等同于侧边栏导航 3.混合导航:设置无效,效果等同于侧边栏导航
4.双层侧边栏导航:设置无效,效果等同于侧边栏导航
## 侧边栏 ## 侧边栏
### 桌面端 ### 桌面端
@ -25,6 +29,11 @@
未设置自动隐藏时,折叠状态依据用户设置 未设置自动隐藏时,折叠状态依据用户设置
设置了自动隐藏时,折叠状态无效,侧边栏为抽屉模式 设置了自动隐藏时,折叠状态无效,侧边栏为抽屉模式
当导航模式为*双层侧边栏导航*时,以下设置无效:
- 折叠
- 折叠时显示父级
- 自动隐藏
### 移动端 ### 移动端
侧边栏只能为抽屉模式 侧边栏只能为抽屉模式

@ -1,4 +1,11 @@
import {delAllUrlParam} from "@/util" //删除所有url参数
function delAllUrlParam() {
let paramStartIndex = location.href.indexOf('?')
if (paramStartIndex > -1) {
const href = location.href.substring(0, paramStartIndex)
history.replaceState(null, null, [...href].join(''))
}
}
//将引导步骤中函数的this绑定为组件实例 //将引导步骤中函数的this绑定为组件实例
function transformGuideSteps(instance, steps) { function transformGuideSteps(instance, steps) {

@ -18,8 +18,7 @@ export default [
component: Layout, component: Layout,
children: [ children: [
{ {
path: ':path(.*)', path: '*',
props: true,
component: Redirect component: Redirect
} }
] ]

@ -33,8 +33,20 @@ registerGuardian(router)
export default router export default router
export function addDynamicRoutes(jsonTree) { /**
* 向外部暴露的动态路由添加方法只能被调用一次
*
* @param jsonTree {array} 路由配置项构成的树型数组
*/
export const addDynamicRoutes = (() => {
let init = false
return (jsonTree) => {
if (init) return
init = true
jsonTree = deepClone(jsonTree) jsonTree = deepClone(jsonTree)
const endRoute = {path: '*', redirect: '/404'} const endRoute = {path: '*', redirect: '/404'}
router.addRoutes([...generateRoutes(jsonTree), endRoute]) router.addRoutes([...generateRoutes(jsonTree), endRoute])
} }
})()

@ -19,7 +19,7 @@ export function isDom(obj) {
} }
/** /**
* 获取元素的内宽去除padding * 获取元素的内宽扣除左右padding后
* *
* @param el {HTMLElement} * @param el {HTMLElement}
* @return {number} * @return {number}
@ -101,7 +101,9 @@ export function loadExternalResource(url, type = 'js') {
if (type === "css") { if (type === "css") {
const links = Array.from(document.getElementsByTagName('link')) const links = Array.from(document.getElementsByTagName('link'))
if (links.some(link => link.getAttribute('href') === url)) return resolve() if (links.some(link => link.getAttribute('href') === url)) {
return resolve()
}
tag = document.createElement("link") tag = document.createElement("link")
tag.rel = "stylesheet" tag.rel = "stylesheet"
@ -109,7 +111,9 @@ export function loadExternalResource(url, type = 'js') {
} }
else if (type === "js") { else if (type === "js") {
const scripts = Array.from(document.getElementsByTagName('script')) const scripts = Array.from(document.getElementsByTagName('script'))
if (scripts.some(script => script.getAttribute('src') === url)) return resolve() if (scripts.some(script => script.getAttribute('src') === url)) {
return resolve()
}
tag = document.createElement("script") tag = document.createElement("script")
tag.src = url tag.src = url
@ -124,6 +128,34 @@ export function loadExternalResource(url, type = 'js') {
}) })
} }
/**
* 将dom按最小距离垂直地滚动至视窗内
* 比如dom在视窗下那么会滚动到视窗底部
*
* @param child {HTMLElement} 需要滚动的dom
* @param parent {HTMLElement|Window} 包含child的容器
*/
export function scrollIntoViewVertically(child, parent = window) {
const {scrollTop, scrollHeight, offsetHeight: containerHeight} = parent
//当菜单高度不足以滚动时跳过
if (scrollHeight <= containerHeight) return
const elHeight = child.offsetHeight, between = getTopDistance(child, parent)
//计算需要滚动的距离,undefined说明不需要滚动
let distance
if (between < 0) distance = between
else if (between + elHeight > containerHeight) {
distance = between + elHeight - containerHeight
}
if (distance !== undefined) {
parent.scrollTo({top: scrollTop + distance, behavior: 'smooth'})
}
}
/** /**
* 平滑滚动至指定的位置 * 平滑滚动至指定的位置
* *

@ -0,0 +1,35 @@
import {scrollIntoViewVertically} from "@/util/browser"
/**
* 将当前激活的菜单移动到视窗中仅限垂直菜单
*
* @param menu el-menu实例
*/
export function moveToActiveMenuVertically(menu) {
if (!menu) return
const cur = menu.activeIndex
if (!cur) return
const curInstance = menu.items[cur]
if (!curInstance) return
let el = curInstance.$el
//当侧边栏折叠时,需要滚动至可视区域的元素是激活菜单的最顶层父节点
if (menu.collapse) {
let rootParent = curInstance
while (rootParent.$parent.$options.componentName !== 'ElMenu') {
rootParent = rootParent.$parent
}
el = rootParent.$el
}
/*
* 这里考虑了菜单展开时的200ms动画时间
* 为什么不分情况讨论比如当subMenu已经是展开状态时无需延时滚动
* 但这种情况无法判断因为这时menu.openedMenus已经包含了subMenu无论subMenu之前是否展开
* 所以统一延时300ms
* */
window.setTimeout(() => scrollIntoViewVertically(el, menu.$el), 300)
}

@ -1,5 +1,6 @@
/** /**
* el-tree展开折叠控制 * el-tree展开折叠控制
*
* @param ref el-tree实例 * @param ref el-tree实例
* @param action 'expand' | 'collapse' * @param action 'expand' | 'collapse'
* @param level 展开的节点最大深度为0时匹配全部节点 * @param level 展开的节点最大深度为0时匹配全部节点

@ -1,5 +1,5 @@
import {isEmpty} from "@/util"
import request from "@/api/request" import request from "@/api/request"
import {isEmpty} from "@/util"
import {elError} from "@/util/message" import {elError} from "@/util/message"
import {download} from "@/util/file" import {download} from "@/util/file"
import {flatTree} from "@/util/tree" import {flatTree} from "@/util/tree"

@ -1,41 +1,77 @@
/**
* 判断是否为空值undefinednull'' 都视为空值
*
* @param str 不定参数
* @return {boolean} 若为空值返回true否则返回false
*/
export function isEmpty(...str) { export function isEmpty(...str) {
return str.some(i => i === undefined || i === null || i === '') return str.some(i => i === undefined || i === null || i === '')
} }
/**
* 当传入空值时返回默认值
*
* @param v 传入值
* @param defaultValue 当传入值为空值(使用{@link #isEmpty}判断)返回的值
* @return {string|*}
*/
export function emptyOrDefault(v, defaultValue = '') { export function emptyOrDefault(v, defaultValue = '') {
return isEmpty(v) ? defaultValue : v return isEmpty(v) ? defaultValue : v
} }
/**
* 字符串替换全部
*
* @param str 原字符串
* @param substr 被替换的字符串
* @param replacement 替换的字符串
* @return {string}
*/
export function replaceAll(str, substr, replacement) {
if (str == null) return str
return str.replace(new RegExp(substr, 'gm'), replacement)
}
/**
* 根据传入值的类型返回基础起始值
*
* @param v
* @return {boolean|{}|string|*[]|number|null}
*/
export function getInitialValue(v) { export function getInitialValue(v) {
if (v === undefined || v === null) v = null if (v === undefined || v === null) return null
else if (typeof v === 'string') v = '' if (typeof v === 'string') return ''
else if (typeof v === 'boolean') v = false if (typeof v === 'boolean') return false
else if (typeof v === 'number') v = 0 if (typeof v === 'number') return 0
else if (typeof v === 'object') v = {} if (typeof v === 'object') return {}
else if (Array.isArray(v)) v = [] if (Array.isArray(v)) return []
} }
//简单重置对象属性 /**
* 简单重置对象属性遇到对象时会递归重置
* 重置方法使用{@link #getInitialValue}
*
* @param obj 需要重置的对象
*/
export function resetObj(obj) { export function resetObj(obj) {
if (isEmpty(obj)) return if (isEmpty(obj)) return
Object.keys(obj).forEach(key => { Object.keys(obj).forEach(key => {
if (Array.isArray(obj[key])) { if (obj[key] !== null && typeof obj[key] === 'object') {
obj[key] = []
}
else if (typeof obj[key] === 'number') {
obj[key] = 0
}
else if (typeof obj[key] === 'boolean') {
obj[key] = false
}
else if (obj[key] !== null && typeof obj[key] === 'object') {
resetObj(obj[key]) resetObj(obj[key])
} }
else obj[key] = null else obj[key] = getInitialValue(obj[key])
}) })
} }
//简单合并对象 /**
* 将source合并到target中
* 仅对target中存在的键进行合并
* 仅当遇到对象值时进行递归
*
* @param target
* @param source
*/
export function mergeObj(target, source) { export function mergeObj(target, source) {
if (isEmpty(target, source)) return if (isEmpty(target, source)) return
@ -64,6 +100,13 @@ export function mergeObj(target, source) {
} }
} }
/**
* 日期格式化
*
* @param fmt {string} 格式y(+..y)-M(+M)-d(+d)-H(+H)-m(+m)-s(+s)-S-毫秒
* @param date {Date} 可选被格式化的日期
* @return {string} 格式化后的日期字符串
*/
export function timeFormat(fmt, date = new Date()) { export function timeFormat(fmt, date = new Date()) {
if (isEmpty(fmt)) fmt = 'yyyy-MM-dd HH:mm:ss' if (isEmpty(fmt)) fmt = 'yyyy-MM-dd HH:mm:ss'
@ -73,7 +116,6 @@ export function timeFormat(fmt, date = new Date()) {
"H+": date.getHours(), //小时 "H+": date.getHours(), //小时
"m+": date.getMinutes(), //分 "m+": date.getMinutes(), //分
"s+": date.getSeconds(), //秒 "s+": date.getSeconds(), //秒
"q+": Math.floor((date.getMonth() + 3) / 3), //季度
"S": date.getMilliseconds() //毫秒 "S": date.getMilliseconds() //毫秒
} }
@ -85,7 +127,7 @@ export function timeFormat(fmt, date = new Date()) {
for (const k in o) { for (const k in o) {
if (new RegExp(`(${k})`).test(fmt)) { if (new RegExp(`(${k})`).test(fmt)) {
const firstMatch = RegExp.$1 const firstMatch = RegExp.$1
const replace = firstMatch.length === 1 ? o[k] : ("00" + o[k]).substring(("" + o[k]).length) const replace = firstMatch.length === 1 ? o[k] + "" : ("00" + o[k]).substring(("" + o[k]).length)
fmt = fmt.replace(firstMatch, [...replace].join('')) fmt = fmt.replace(firstMatch, [...replace].join(''))
} }
} }
@ -93,6 +135,14 @@ export function timeFormat(fmt, date = new Date()) {
return fmt return fmt
} }
/**
* 防抖
*
* @param func {function} 原函数
* @param wait {number} 防抖间隔单位毫秒
* @param immediate {boolean} 是否立即执行一次
* @return {function} 经过防抖包装后的函数
*/
export function debounce(func, wait = 100, immediate = false) { export function debounce(func, wait = 100, immediate = false) {
let timeout, args, context, timestamp, result let timeout, args, context, timestamp, result
@ -183,15 +233,6 @@ export function waitUntilSuccess(success, callback, interval = 1000, maxTryTime
}) })
} }
//删除所有url参数
export function delAllUrlParam() {
let paramStartIndex = location.href.indexOf('?')
if (paramStartIndex > -1) {
const href = location.href.substring(0, paramStartIndex)
history.replaceState(null, null, [...href].join(''))
}
}
//将传入对象的所有函数的this绑定为其自身 //将传入对象的所有函数的this绑定为其自身
export function bindThis(obj, root = obj) { export function bindThis(obj, root = obj) {
if (!obj || typeof obj !== 'object') return if (!obj || typeof obj !== 'object') return

@ -1,17 +1,10 @@
<script> <script>
export default { import {replaceAll} from "@/util"
props: {
path: {type: String, default: ''}
},
created() { export default {
const {query} = this.$route mounted() {
//使name+params const {fullPath} = this.$route
if ('params' in query) { this.$router.replace(`${replaceAll(fullPath, '/redirect', '')}`)
const {name, params} = JSON.parse(query.params)
this.$router.replace({name, params})
}
else this.$router.replace({path: `/${this.path}`, query})
}, },
render: h => h() render: h => h()

Loading…
Cancel
Save