Files
2026-09-24 16:25:22 +08:00

116 lines
3.7 KiB
Plaintext

<template>
<view class="hr-page" :style="{ backgroundColor: pageBg }">
<!-- 无导航栏时:普通状态栏占位 -->
<hr-statusbar v-if="navTitle == '' && !hiddenStatusbar" :bg="statusBg"></hr-statusbar>
<!-- 导航栏:使用组件库 x-navbar -->
<x-navbar v-if="navTitle != ''" :title="navTitle" :show-nav-back="showBack" :height="48" :bg-color="navBg"
:title-color="navTitleColor" :is-place="true" :static-transparent="statusBg == 'transparent'">
<template v-slot:right>
<view class="hr-row hr-navright" @click="onRightClick">
<slot name="right"></slot>
</view>
</template>
</x-navbar>
<!-- 滚动主体:外层 .hr-flow 提供「内容区」定位基准(不含导航栏/底栏) -->
<view v-if="!noScroll" class="hr-flow">
<scroll-view class="hr-scroll" :scroll-y="true" :show-scrollbar="false">
<slot></slot>
</scroll-view>
<!--
加载中覆盖层(组件库 xLoading)
用绝对定位铺满整个内容区:绝对定位元素不参与 flex 布局,
高度必然等于内容区,不受 scroll-view 压缩影响。
-->
<view v-if="xloading && loading" class="hr-loading-mask">
<x-loading label="加载中…" :icon-size="'44rpx'" :text-size="'26rpx'" :vertical="true"
color="#2563EB" text-color="#6B7280"></x-loading>
</view>
</view>
<view v-else class="hr-scroll">
<slot></slot>
</view>
<!-- 底部固定操作条 -->
<slot name="footer"></slot>
<!-- 底部 TabBar -->
<slot name="tabbar"></slot>
<!-- 覆盖层(底部选择器 / 加载遮罩等,不随内容滚动) -->
<slot name="overlay"></slot>
</view>
</template>
<script lang="uts" setup>
import { computed, ref, onMounted } from 'vue'
import { hrSession } from '@/utils/role.uts'
import { mockRequest } from '@/utils/request.uts'
/**
* 页面骨架
* 统一承载「状态栏占位 / 导航栏 / 可滚动主体 / 底部固定条 / 底部 TabBar」五段结构,
* 各业务页只需关心自己的内容,避免每页重复搭建骨架。
*
* 列表页只要传 :xloading="true",请求期间就自动用组件库 xLoading 展示「加载中」,无需在页面里写 loading。
*/
const props = defineProps({
/** 页面底色 */
pageBg : { type: String, default: '#F3F4F6' },
/** 状态栏占位背景色;传 transparent 表示内容顶到状态栏下方 */
statusBg : { type: String, default: '#FFFFFF' },
/** 导航栏标题,为空则不渲染导航栏 */
navTitle : { type: String, default: '' },
/** 导航栏背景色 */
navBg : { type: String, default: '#FFFFFF' },
/** 导航栏标题颜色 */
navTitleColor : { type: String, default: '#1F2937' },
/** 是否显示返回按钮 */
showBack : { type: Boolean, default: true },
/** 是否隐藏状态栏占位(内容自绘状态区时使用) */
hiddenStatusbar : { type: Boolean, default: false },
/** 关闭内部滚动(三段式作业页自行控制滚动区) */
noScroll : { type: Boolean, default: false },
/** 是否展示「加载中」覆盖层(请求期间用组件库 xLoading) */
xloading : { type: Boolean, default: false }
})
const emits = defineEmits<{
(e : 'right-click') : void
}>()
/** 数据请求中:请求期间显示 xLoading */
const loading = ref(true)
onMounted(() => {
if (!props.xloading) {
loading.value = false
return
}
mockRequest(() => {
loading.value = false
})
})
/** 返回失败时的兜底页(按角色回到各自的一级页) */
const backErrorPath = computed(() : string => {
return hrSession.role == 'keeper' ? '/pages/warehouse/scan' : '/pages/home/index'
})
function onRightClick() : void {
emits('right-click')
}
</script>
<style>
.hr-navright {
height: 96rpx;
padding-left: 16rpx;
padding-right: 8rpx;
}
</style>