mirror of
https://github.com/kuaifan/dootask.git
synced 2026-10-30 06:20:44 +00:00
feat(project): 移动端 App 内项目列表与详情切换增加右侧滑入/滑出动画
列表与详情为同一路由页面,路由级动画不会触发,改在项目页内部用 transition 处理: - 滑入期间保持列表与底部选项卡,结束后再隐藏,避免下层页面高度跳变 - 动画期间详情固定覆盖于列表及选项卡之上,按实际位置换算 top - 返回时等滑出动画结束再清空当前项目数据
This commit is contained in:
parent
9d07f88e8c
commit
093a1e9afb
@ -1119,6 +1119,8 @@ export default {
|
||||
this.releaseViewCover()
|
||||
this.viewLeavePath = from.path
|
||||
const isSubPage = route => /^manage-(setting|calendar|file)/.test(route.name || '')
|
||||
const isProjectList = route => route.name === 'manage-project' && !/^\d+$/.test(route.params.projectId)
|
||||
const isProjectPanel = route => route.name === 'manage-project' && /^\d+$/.test(route.params.projectId)
|
||||
if (this.$isEEUIApp && this.windowPortrait) {
|
||||
if (from.name === 'manage-application' && isSubPage(to)) {
|
||||
// 划入动画结束后再隐藏底部选项卡
|
||||
@ -1132,6 +1134,13 @@ export default {
|
||||
this.viewTransition = {enter: 'manage-view-stay', leave: 'mobile-dialog-leave-active manage-view-top'}
|
||||
return
|
||||
}
|
||||
if (isProjectList(from) && isProjectPanel(to)) {
|
||||
// 项目列表与详情为同一页面组件,划入动画由项目页处理并负责释放 tabbarHold
|
||||
this.tabbarHold = true
|
||||
this.tabbarTransition = 'none'
|
||||
this.viewTransition = {enter: '', leave: ''}
|
||||
return
|
||||
}
|
||||
}
|
||||
this.tabbarHold = false
|
||||
this.tabbarTransition = 'mobile-slide'
|
||||
|
||||
@ -1,7 +1,17 @@
|
||||
<template>
|
||||
<div class="page-project">
|
||||
<ProjectPanel v-if="projectId > 0" ref="panel"/>
|
||||
<ProjectList v-if="windowPortrait" v-show="projectId === 0"/>
|
||||
<transition
|
||||
:enter-active-class="panelTransition.enter"
|
||||
:leave-active-class="panelLeaveClass"
|
||||
@enter="onPanelCover"
|
||||
@leave="onPanelCover"
|
||||
@after-enter="onPanelEntered"
|
||||
@enter-cancelled="onPanelEntered"
|
||||
@after-leave="onPanelLeft"
|
||||
@leave-cancelled="onPanelLeft">
|
||||
<ProjectPanel v-if="projectId > 0" ref="panel"/>
|
||||
</transition>
|
||||
<ProjectList v-if="windowPortrait" v-show="projectId === 0 || listStay"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -12,6 +22,13 @@ import ProjectList from "./components/ProjectList";
|
||||
export default {
|
||||
components: {ProjectList, ProjectPanel},
|
||||
|
||||
data() {
|
||||
return {
|
||||
panelTransition: {enter: '', leave: ''},
|
||||
listStay: false, // 详情划入期间保持列表显示
|
||||
}
|
||||
},
|
||||
|
||||
deactivated() {
|
||||
this.$store.dispatch("forgetTaskCompleteTemp", true);
|
||||
},
|
||||
@ -19,6 +36,15 @@ export default {
|
||||
computed: {
|
||||
...mapState(['cacheProjects', 'wsOpenNum']),
|
||||
|
||||
/**
|
||||
* 划出类名须在详情渲染时即确定(Vue 离开动画读取的是元素上一次渲染时的类名)
|
||||
* 非返回列表的离开(切到其它页面)此时页面已被 keep-alive 移出,动画不可见
|
||||
* @returns {string}
|
||||
*/
|
||||
panelLeaveClass() {
|
||||
return this.$isEEUIApp && this.windowPortrait ? 'mobile-dialog-leave-active' : ''
|
||||
},
|
||||
|
||||
projectId() {
|
||||
const {projectId} = this.$route.params;
|
||||
return parseInt(/^\d+$/.test(projectId) ? projectId : 0);
|
||||
@ -26,6 +52,11 @@ export default {
|
||||
},
|
||||
|
||||
watch: {
|
||||
// 须在 projectId 之前定义:先确定动画再处理项目数据
|
||||
'$route'(to, from) {
|
||||
this.updatePanelTransition(to, from)
|
||||
},
|
||||
|
||||
projectId: {
|
||||
handler() {
|
||||
this.getProjectData();
|
||||
@ -43,9 +74,59 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
getProjectData() {
|
||||
/**
|
||||
* 移动端项目列表与详情切换动画(同一页面组件,路由动画不会触发)
|
||||
* @param to
|
||||
* @param from
|
||||
*/
|
||||
updatePanelTransition(to, from) {
|
||||
const isList = route => route.name === 'manage-project' && !/^\d+$/.test(route.params.projectId)
|
||||
const isPanel = route => route.name === 'manage-project' && /^\d+$/.test(route.params.projectId)
|
||||
this.listStay = false
|
||||
this.panelTransition = {enter: '', leave: ''}
|
||||
if (this.$isEEUIApp && this.windowPortrait) {
|
||||
if (isList(from) && isPanel(to)) {
|
||||
this.listStay = true
|
||||
this.panelTransition.enter = 'mobile-dialog-enter-active'
|
||||
} else if (isPanel(from) && isList(to)) {
|
||||
this.panelTransition.leave = 'mobile-dialog-leave-active'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onPanelCover(el) {
|
||||
if (this.panelTransition.enter || this.panelTransition.leave) {
|
||||
// 动画期间详情固定覆盖于列表及底部选项卡之上
|
||||
// fixed 的参照可能不是视口(祖先 will-change: transform),按实际位置换算 top
|
||||
el.setAttribute('data-panel-cover', '')
|
||||
el.style.top = `${this.$el.getBoundingClientRect().top - el.getBoundingClientRect().top}px`
|
||||
}
|
||||
},
|
||||
|
||||
releasePanelCover(el) {
|
||||
el.style.top = ''
|
||||
el.removeAttribute('data-panel-cover')
|
||||
},
|
||||
|
||||
onPanelEntered(el) {
|
||||
this.releasePanelCover(el)
|
||||
this.listStay = false
|
||||
this.$store.state.mobileTabbarHold = false
|
||||
},
|
||||
|
||||
onPanelLeft(el) {
|
||||
this.releasePanelCover(el)
|
||||
if (this.projectId <= 0) {
|
||||
this.$store.state.projectId = 0;
|
||||
}
|
||||
},
|
||||
|
||||
getProjectData() {
|
||||
if (this.projectId <= 0) {
|
||||
// 详情划出期间保留项目数据,动画结束后再清除
|
||||
if (!this.panelTransition.leave) {
|
||||
this.$store.state.projectId = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
const projectId = this.projectId;
|
||||
|
||||
12
resources/assets/sass/pages/page-project.scss
vendored
12
resources/assets/sass/pages/page-project.scss
vendored
@ -7,6 +7,18 @@
|
||||
width: 0;
|
||||
height: 100%;
|
||||
background-color: #fafafa;
|
||||
// 移动端列表与详情切换动画期间(top 由脚本按页面位置换算)
|
||||
&[data-panel-cover] {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: auto;
|
||||
height: auto;
|
||||
min-height: 0;
|
||||
z-index: 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user