perf: 优化移动端布局

This commit is contained in:
kuaifan 2025-04-09 23:09:37 +08:00
parent bc417b9eea
commit 841ed4e682
15 changed files with 122 additions and 53 deletions

View File

@ -1,9 +1,16 @@
<template> <template>
<div id="app" class="app-view" :style="appStyle"> <div id="app" class="app-view">
<!--顶部状态栏-->
<div class="child-status-bar"></div>
<!--主路由视图-->
<keep-alive> <keep-alive>
<router-view class="child-view" :style="childStyle" @hook:mounted.once="onRouterViewMounted"></router-view> <router-view class="child-view" @hook:mounted.once="onRouterViewMounted"/>
</keep-alive> </keep-alive>
<!--底部导航栏-->
<div class="child-navigation-bar"></div>
<!--任务操作--> <!--任务操作-->
<TaskOperation/> <TaskOperation/>
@ -25,9 +32,6 @@
<!--身份提示--> <!--身份提示-->
<AuthException/> <AuthException/>
<!--网络提示-->
<NetworkException v-if="windowLandscape"/>
<!--引导页--> <!--引导页-->
<GuidePage/> <GuidePage/>
@ -36,6 +40,9 @@
<!--移动端通知--> <!--移动端通知-->
<MobileNotification/> <MobileNotification/>
<!--网络提示-->
<NetworkException v-if="windowLandscape"/>
</div> </div>
</template> </template>
@ -47,15 +54,27 @@
left: 0; left: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
} display: flex;
.child-view { flex-direction: column;
position: absolute;
top: 0; .child-status-bar {
left: 0; flex-shrink: 0;
right: 0; height: var(--status-bar-height);
bottom: 0; background-color: var(--status-bar-color);
will-change: transform; }
transition: all .3s cubic-bezier(.55, 0, .1, 1);
.child-navigation-bar {
flex-shrink: 0;
height: var(--navigation-bar-height);
background-color: var(--navigation-bar-color);
}
.child-view {
flex: 1;
height: 0;
will-change: transform;
transition: all .3s cubic-bezier(.55, 0, .1, 1);
}
} }
</style> </style>
<script> <script>
@ -78,7 +97,8 @@ export default {
mixins: [ctrlPressed], mixins: [ctrlPressed],
components: { components: {
MobileBack, MobileNotification, MobileBack,
MobileNotification,
AuthException, AuthException,
MeetingManager, MeetingManager,
DropdownMenu, DropdownMenu,
@ -93,7 +113,6 @@ export default {
data() { data() {
return { return {
appInter: null, appInter: null,
appBackgroundColor: "#f8f8f8",
countDown: Math.min(30, 60 - $A.daytz().second()), countDown: Math.min(30, 60 - $A.daytz().second()),
lastCheckUpgradeYmd: $A.daytz().format('YYYY-MM-DD'), lastCheckUpgradeYmd: $A.daytz().format('YYYY-MM-DD'),
} }
@ -115,18 +134,31 @@ export default {
}, },
computed: { computed: {
...mapState(['ws', 'themeConf', 'windowOrientation', 'safeAreaSize']), ...mapState(['ws', 'themeConf', 'windowOrientation', 'safeAreaSize', 'mobileTabbar']),
appStyle({appBackgroundColor}) { statusColor({routeName}) {
return { if (!routeName) {
backgroundColor: appBackgroundColor, return null
} }
if (['manage-messenger', 'manage-project'].includes(routeName)) {
return '#f8f8f8'
}
if (routeName.startsWith('manage-setting')) {
return '#f8f8f8'
}
return null
}, },
childStyle({safeAreaSize}) { navigationColor({statusColor, mobileTabbar}) {
return statusColor || (mobileTabbar ? '#f8f8f8' : null)
},
rootStyle() {
return { return {
top: `${safeAreaSize.top}px`, '--status-bar-height': `${this.safeAreaSize.top}px`,
bottom: `${safeAreaSize.bottom}px`, '--status-bar-color': this.statusColor || '#ffffff',
'--navigation-bar-height': `${this.safeAreaSize.bottom}px`,
'--navigation-bar-color': this.navigationColor || '#ffffff',
} }
}, },
}, },
@ -183,6 +215,15 @@ export default {
immediate: true immediate: true
}, },
rootStyle: {
handler(style) {
for (const key in style) {
document.documentElement.style.setProperty(key, style[key])
}
},
immediate: true
},
windowTouch: { windowTouch: {
handler(support) { handler(support) {
if (support) { if (support) {
@ -625,9 +666,9 @@ export default {
if (!$A.isJson(event)) { if (!$A.isJson(event)) {
return; return;
} }
this.$store.state.keyboardType = event.keyboardType; this.$store.state.keyboardShow = event.keyboardType === 'show';
this.$store.state.keyboardHeight = event.keyboardHeight; this.$store.state.keyboardHeight = event.keyboardHeight;
$A.eeuiAppShakeToEditEnabled(this.$store.state.keyboardType === 'show') $A.eeuiAppShakeToEditEnabled(this.$store.state.keyboardShow)
} }
// //
window.__onNotificationPermissionStatus = (ret) => { window.__onNotificationPermissionStatus = (ret) => {

View File

@ -338,14 +338,17 @@ const $preload = async () => {
} }
return return
} }
$A.eeuiAppGetSafeAreaInsets().then(data => { const pageInfo = $A.eeuiAppGetPageInfo() || {};
const proportion = data.height / window.outerHeight if (pageInfo.pageName === 'firstPage') {
store.state.safeAreaSize = { $A.eeuiAppGetSafeAreaInsets().then(data => {
top: Math.round(data.top / proportion * 100) / 100, const proportion = data.height / window.outerHeight
bottom: Math.round(data.bottom / proportion * 100) / 100, store.state.safeAreaSize = {
data top: Math.round(data.top / proportion * 100) / 100,
} bottom: Math.round(data.bottom / proportion * 100) / 100,
}).catch(console.warn) data
}
}).catch(console.warn)
}
} }
await store.dispatch("preload"); await store.dispatch("preload");

View File

@ -85,7 +85,9 @@ export default {
const pageX = event.type === 'touchmove' ? event.targetTouches[0].pageX : event.pageX; const pageX = event.type === 'touchmove' ? event.targetTouches[0].pageX : event.pageX;
const pageY = event.type === 'touchmove' ? event.targetTouches[0].pageY : event.pageY; const pageY = event.type === 'touchmove' ? event.targetTouches[0].pageY : event.pageY;
if (typeof this.isScrolling === 'undefined') { if (typeof this.isScrolling === 'undefined') {
this.isScrolling = !!(this.isScrolling || Math.abs(pageY - this.touchesStart.y) > Math.abs(pageX - this.touchesStart.x)); const verticalMove = Math.abs(pageY - this.touchesStart.y);
const horizontalMove = Math.abs(pageX - this.touchesStart.x) * 1.5; //
this.isScrolling = verticalMove > horizontalMove;
} }
if (this.isScrolling) { if (this.isScrolling) {
this.isTouched = false; this.isTouched = false;

View File

@ -15,6 +15,7 @@
<script> <script>
import emitter from "../../store/events"; import emitter from "../../store/events";
import {mapState} from "vuex";
export default { export default {
name: "MobileNotification", name: "MobileNotification",
@ -44,9 +45,11 @@ export default {
}, },
computed: { computed: {
notifyStyle() { ...mapState(['safeAreaSize']),
notifyStyle({windowScrollY, safeAreaSize}) {
return { return {
marginTop: this.$store.state.windowScrollY + 'px', marginTop: (windowScrollY + safeAreaSize.top) + 'px',
}; };
}, },
}, },
@ -64,7 +67,7 @@ export default {
this.show = true; this.show = true;
this.timer && clearTimeout(this.timer); this.timer && clearTimeout(this.timer);
if (this.duration > 0) { if (this.duration > 0) {
this.timer = setTimeout(this.close, this.duration) // this.timer = setTimeout(this.close, this.duration)
} }
$A.eeuiAppSendMessage({ $A.eeuiAppSendMessage({
action: 'setVibrate', action: 'setVibrate',

View File

@ -143,7 +143,7 @@ export default {
computed: { computed: {
...mapState([ ...mapState([
'themeName', 'themeName',
'keyboardType' 'keyboardShow'
]), ]),
isFullscreen({windowWidth}) { isFullscreen({windowWidth}) {
@ -248,7 +248,7 @@ export default {
}, },
onTouchstart() { onTouchstart() {
if (this.keyboardType === "show") { if (this.keyboardShow) {
$A.eeuiAppKeyboardHide(); $A.eeuiAppKeyboardHide();
} }
}, },

View File

@ -61,7 +61,7 @@
// 获取页面信息 // 获取页面信息
eeuiAppGetPageInfo(pageName) { eeuiAppGetPageInfo(pageName) {
return this.eeuiModule()?.getPageInfo(pageName); return this.eeuiModule()?.getPageInfo(pageName || "");
}, },
// 打开app新页面 // 打开app新页面

View File

@ -559,9 +559,10 @@ export default {
'cacheTranscriptionLanguage', 'cacheTranscriptionLanguage',
'cacheKeyboard', 'cacheKeyboard',
'keyboardType', 'keyboardShow',
'keyboardHeight', 'keyboardHeight',
'isModKey', 'isModKey',
'safeAreaSize',
]), ]),
...mapGetters(['getDialogDraft', 'getDialogQuote']), ...mapGetters(['getDialogDraft', 'getDialogQuote']),
@ -613,8 +614,8 @@ export default {
}, },
recordConvertFooterStyle() { recordConvertFooterStyle() {
const {recordConvertFocus, keyboardType, keyboardHeight} = this; const {recordConvertFocus, keyboardShow, keyboardHeight} = this;
return (recordConvertFocus && keyboardType === 'show' && keyboardHeight > 120) ? { return (recordConvertFocus && keyboardShow && keyboardHeight > 120) ? {
alignItems: 'flex-start', alignItems: 'flex-start',
transform: 'translateY(12px)' transform: 'translateY(12px)'
} : {} } : {}
@ -719,10 +720,12 @@ export default {
return this.getDialogQuote(this.dialogId)?.type === 'update' return this.getDialogQuote(this.dialogId)?.type === 'update'
}, },
chatInputBoxStyle({iOSDevices, fullInput, viewportHeight}) { chatInputBoxStyle({iOSDevices, fullInput, keyboardShow, viewportHeight, safeAreaSize}) {
const style = {} const style = {}
if (iOSDevices && fullInput && viewportHeight > 0) { if (iOSDevices && fullInput && keyboardShow && viewportHeight > 0) {
style.height = Math.max(100, viewportHeight - 70) + 'px' style.height = Math.max(100, viewportHeight - 70 - safeAreaSize.top) + 'px'
} else {
style.paddingBottom = `${safeAreaSize.bottom}px`
} }
return style return style
} }

View File

@ -36,8 +36,8 @@ export default {
}, },
// 键盘状态仅iOS // 键盘状态仅iOS
keyboardType: null, // show|hide keyboardShow: false, // 键盘可见
keyboardHeight: 0, // 键盘高度 keyboardHeight: 0, // 键盘高度
// 是否按下Ctrl/Command键 // 是否按下Ctrl/Command键
isModKey: false, isModKey: false,

View File

@ -1,4 +1,5 @@
@import "var"; @import "var";
@import "root";
@import "fileicon"; @import "fileicon";
@import "element"; @import "element";
@import "fonts-ft"; @import "fonts-ft";

View File

@ -6,7 +6,8 @@
max-width: none; max-width: none;
.ivu-modal-content { .ivu-modal-content {
margin-top: 46px; margin-top: calc(var(--status-bar-height) + 46px);
margin-bottom: 0;
border-top-left-radius: 18px !important; border-top-left-radius: 18px !important;
border-top-right-radius: 18px !important; border-top-right-radius: 18px !important;
@ -16,6 +17,7 @@
.search-list { .search-list {
max-height: none; max-height: none;
padding-bottom: var(--navigation-bar-height);
} }
} }
} }

View File

@ -455,7 +455,8 @@
max-width: none; max-width: none;
.ivu-modal-content { .ivu-modal-content {
margin-top: 46px; margin-top: calc(var(--status-bar-height) + 46px);
margin-bottom: 0;
border-top-left-radius: 18px !important; border-top-left-radius: 18px !important;
border-top-right-radius: 18px !important; border-top-right-radius: 18px !important;
} }
@ -477,7 +478,7 @@
max-height: none; max-height: none;
ul { ul {
padding-bottom: 0; padding-bottom: var(--navigation-bar-height);
> li { > li {
&:last-child { &:last-child {

View File

@ -279,6 +279,10 @@ body {
.ivu-modal { .ivu-modal {
top: 100px; top: 100px;
padding-bottom: 100px; padding-bottom: 100px;
@media (max-width: 768px) {
top: 60px;
padding-bottom: 60px;
}
@media (max-height: 900px) { @media (max-height: 900px) {
top: 35px; top: 35px;
padding-bottom: 35px; padding-bottom: 35px;
@ -364,6 +368,8 @@ body {
.ivu-modal-content { .ivu-modal-content {
border-radius: 18px; border-radius: 18px;
margin-top: var(--status-bar-height);
margin-bottom: var(--navigation-bar-height);
.ivu-modal-close { .ivu-modal-close {
.ivu-icon-ios-close { .ivu-icon-ios-close {

View File

@ -988,7 +988,8 @@
.chat-input-full-input { .chat-input-full-input {
.ivu-modal { .ivu-modal {
.ivu-modal-content { .ivu-modal-content {
margin-top: 46px; margin-top: calc(var(--status-bar-height) + 46px) !important;
margin-bottom: 0 !important;
border-top-left-radius: 18px !important; border-top-left-radius: 18px !important;
border-top-right-radius: 18px !important; border-top-right-radius: 18px !important;
.ivu-modal-body { .ivu-modal-body {

View File

@ -71,7 +71,7 @@ body.window-portrait {
} }
} }
.calendar-box { .calendar-box {
padding: 0 24px 5px; padding: 0;
} }
} }
} }

6
resources/assets/sass/root.scss vendored Normal file
View File

@ -0,0 +1,6 @@
:root {
--status-bar-height: 0px;
--status-bar-color: #ffffff;
--navigation-bar-height: 0px;
--navigation-bar-color: #ffffff;
}