1. add default avatar and multiple notification icons 2. update finance archive note and add form styles 3. add new pages: edit-profile, security, password, change-phone, search, help 4. refactor navigation action layout to support multiple actions 5. add search button to home page 6. add security code component and account service APIs 7. add help and search content services 8. update message notice to use svg icons 9. update asset section in finance assessment 10. update manifest appid and pack options
104 lines
4.6 KiB
Plaintext
104 lines
4.6 KiB
Plaintext
import { ref, onMounted, onUnmounted } from 'vue'
|
|
import { onResize } from '@dcloudio/uni-app'
|
|
|
|
export type PageLayout = {
|
|
pageStyle: () => string,
|
|
contentStyle: (offset: number, minimum: boolean) => string,
|
|
statusStyle: (offset: number) => string,
|
|
navigationStyle: () => string,
|
|
navigationTitleStyle: () => string,
|
|
navigationActionStyle: (headerHeight: number, actionCount: number) => string,
|
|
navigationBrandStyle: (headerHeight: number) => string,
|
|
bottomStyle: (padding: number) => string
|
|
}
|
|
|
|
// 原生渲染不支持 vh/calc,统一以窗口尺寸计算,并随旋转更新。
|
|
export function usePageLayout(): PageLayout {
|
|
const windowInfo = ref(uni.getWindowInfo())
|
|
const capsuleLeft = ref(-1)
|
|
const capsuleTop = ref(0)
|
|
const capsuleHeight = ref(0)
|
|
const readCapsule = () => {
|
|
// #ifdef MP-WEIXIN
|
|
try {
|
|
const capsule = uni.getMenuButtonBoundingClientRect()
|
|
const valid = capsule.width > 0 && capsule.height > 0 && capsule.left > 0 && capsule.left < windowInfo.value.windowWidth
|
|
capsuleLeft.value = valid ? capsule.left : -1
|
|
capsuleTop.value = valid ? capsule.top : 0
|
|
capsuleHeight.value = valid ? capsule.height : 0
|
|
} catch (_error) { capsuleLeft.value = -1; capsuleTop.value = 0; capsuleHeight.value = 0 }
|
|
// #endif
|
|
}
|
|
readCapsule()
|
|
const refresh = () => { windowInfo.value = uni.getWindowInfo(); readCapsule() }
|
|
onMounted(() => refresh())
|
|
// #ifdef APP
|
|
onResize((_options) => { refresh() })
|
|
// #endif
|
|
// #ifndef APP
|
|
const handleResize = (_options: OnWindowResizeCallbackResult) => { refresh() }
|
|
uni.onWindowResize(handleResize)
|
|
onUnmounted(() => { uni.offWindowResize(handleResize) })
|
|
// #endif
|
|
const rpx = (value: number): number => value * windowInfo.value.windowWidth / 750
|
|
const status = (): number => Math.max(windowInfo.value.statusBarHeight ?? 0, windowInfo.value.safeAreaInsets?.top ?? 0)
|
|
const navigationStyle = (): string => {
|
|
// 只在微信端横向避让胶囊,保持业务导航原有高度。
|
|
// #ifdef MP-WEIXIN
|
|
const reserved = capsuleLeft.value > 0 ? windowInfo.value.windowWidth - capsuleLeft.value + 8 : 104
|
|
return 'padding-right:' + reserved + 'px;'
|
|
// #endif
|
|
// #ifndef MP-WEIXIN
|
|
return ''
|
|
// #endif
|
|
}
|
|
const navigationTitleStyle = (): string => {
|
|
// 标题左右使用相同留白,以屏幕中心对齐,同时避开微信胶囊。
|
|
// #ifdef MP-WEIXIN
|
|
const reserved = capsuleLeft.value > 0 ? windowInfo.value.windowWidth - capsuleLeft.value + 8 : 104
|
|
const inset = Math.max(rpx(112), reserved)
|
|
return 'left:' + inset + 'px;right:' + inset + 'px;'
|
|
// #endif
|
|
// #ifndef MP-WEIXIN
|
|
return ''
|
|
// #endif
|
|
}
|
|
const navigationActionStyle = (headerHeight: number, actionCount: number): string => {
|
|
// 微信操作按钮与胶囊等高、顶部对齐,整组向左展开;App 保持导航行内居中。
|
|
// #ifdef MP-WEIXIN
|
|
const size = capsuleHeight.value > 0 ? capsuleHeight.value : 32
|
|
const top = capsuleHeight.value > 0 ? Math.max(0, capsuleTop.value - status()) : Math.max(0, (rpx(headerHeight) - size) / 2)
|
|
const right = capsuleLeft.value > 0 ? windowInfo.value.windowWidth - capsuleLeft.value + 8 : 104
|
|
const width = size * actionCount + rpx(16) * Math.max(0, actionCount - 1)
|
|
return 'position:absolute;top:' + top + 'px;right:' + right + 'px;width:' + width + 'px;height:' + size + 'px;'
|
|
// #endif
|
|
// #ifndef MP-WEIXIN
|
|
return ''
|
|
// #endif
|
|
}
|
|
const navigationBrandStyle = (headerHeight: number): string => {
|
|
// Logo 保留自身比例,与胶囊共享垂直中心,不改变登录页和启动页的 Logo。
|
|
// #ifdef MP-WEIXIN
|
|
const height = rpx(68)
|
|
const center = capsuleHeight.value > 0 ? capsuleTop.value - status() + capsuleHeight.value / 2 : rpx(headerHeight) / 2
|
|
return 'align-self:flex-start;margin-top:' + Math.max(0, center - height / 2) + 'px;height:' + height + 'px;'
|
|
// #endif
|
|
// #ifndef MP-WEIXIN
|
|
return ''
|
|
// #endif
|
|
}
|
|
const pageStyle = (): string => 'min-height:' + windowInfo.value.windowHeight + 'px;'
|
|
const contentStyle = (offset: number, minimum: boolean): string => {
|
|
// #ifdef WEB
|
|
return (minimum ? 'min-height:' : 'height:') + 'calc(100vh - ' + status() + 'px - ' + offset + 'rpx);'
|
|
// #endif
|
|
// #ifndef WEB
|
|
const height = Math.max(0, windowInfo.value.windowHeight - status() - rpx(offset))
|
|
return (minimum ? 'min-height:' : 'height:') + height + 'px;'
|
|
// #endif
|
|
}
|
|
const statusStyle = (offset: number): string => 'height:' + (status() + rpx(offset)) + 'px;'
|
|
const bottomStyle = (padding: number): string => 'padding-bottom:' + (rpx(padding) + (windowInfo.value.safeAreaInsets?.bottom ?? 0)) + 'px;'
|
|
return { pageStyle, contentStyle, statusStyle, navigationStyle, navigationTitleStyle, navigationActionStyle, navigationBrandStyle, bottomStyle }
|
|
}
|