155 lines
3.7 KiB
TypeScript
155 lines
3.7 KiB
TypeScript
import { headerModel } from "@/model/header.model";
|
|
import {
|
|
View,
|
|
Text,
|
|
ScrollView,
|
|
CommonEventFunction,
|
|
} from "@tarojs/components";
|
|
import Taro from "@tarojs/taro";
|
|
import { clsx } from "clsx";
|
|
import { FC, memo, PropsWithChildren } from "react";
|
|
import { useRecoilValue } from "recoil";
|
|
import type { loadMoreConfig, refresherConfig } from "./hooks";
|
|
|
|
const PageLoader = memo(() => {
|
|
return (
|
|
<View className="h-screen w-screen flex items-center justify-center bg-red-500 text-white">
|
|
<Text>Loading...</Text>
|
|
</View>
|
|
);
|
|
});
|
|
|
|
interface LayoutHeaderProps {
|
|
className?: string;
|
|
hideBack?: boolean;
|
|
}
|
|
|
|
// 自定义导航栏
|
|
const LayoutHeader = memo(
|
|
({
|
|
className,
|
|
hideBack = true,
|
|
children,
|
|
}: PropsWithChildren<LayoutHeaderProps>) => {
|
|
const header = useRecoilValue(headerModel);
|
|
|
|
const capsStyles = {
|
|
width: `${header.capsuleWidth}px`,
|
|
height: `${header.capsuleHeight}px`,
|
|
};
|
|
|
|
const handlerBack = () => {
|
|
Taro.navigateBack();
|
|
};
|
|
|
|
return (
|
|
<View className={className}>
|
|
<View style={{ height: `${header.statusBar}px` }}></View>
|
|
<View
|
|
className="flex flex-row items-center"
|
|
style={{ height: `${header.customBar}px` }}
|
|
>
|
|
<View
|
|
className="flex items-center"
|
|
style={{
|
|
...capsStyles,
|
|
marginLeft: `${header.capsulePadding}px`,
|
|
}}
|
|
>
|
|
{hideBack ? (
|
|
<View
|
|
className="h-full aspect-square pl-[5px] flex justify-center items-center"
|
|
onClick={handlerBack}
|
|
>
|
|
<Text>返回</Text>
|
|
</View>
|
|
) : null}
|
|
</View>
|
|
<View className="flex-1 flex justify-center items-center truncate w-full">
|
|
{children}
|
|
</View>
|
|
<View
|
|
style={{ ...capsStyles, marginRight: `${header.capsulePadding}px` }}
|
|
></View>
|
|
</View>
|
|
</View>
|
|
);
|
|
},
|
|
);
|
|
|
|
interface LayoutContainerProps {
|
|
className?: string;
|
|
refresherConfig?: refresherConfig;
|
|
refresherEnabled?: boolean;
|
|
loadMore?: loadMoreConfig;
|
|
onScroll?: CommonEventFunction;
|
|
}
|
|
// 页面容器
|
|
const LayoutContainer = memo(
|
|
({
|
|
className,
|
|
refresherConfig,
|
|
loadMore,
|
|
onScroll,
|
|
children,
|
|
}: PropsWithChildren<LayoutContainerProps>) => {
|
|
return (
|
|
<ScrollView
|
|
className={clsx("flex-1 overflow-hidden", className)}
|
|
scrollY
|
|
{...(refresherConfig ?? {})}
|
|
{...(loadMore ?? {})}
|
|
scrollWithAnimation
|
|
enableFlex
|
|
onScroll={onScroll}
|
|
>
|
|
{children}
|
|
</ScrollView>
|
|
);
|
|
},
|
|
);
|
|
|
|
interface LayoutFooterProps {
|
|
className?: string;
|
|
}
|
|
|
|
// 自定义页脚 底部安全区,暂时未适配 Android
|
|
const LayoutFooter = memo(({ className, children }: PropsWithChildren<LayoutFooterProps>) => {
|
|
return (
|
|
<View className={clsx("", className)}>
|
|
{children}
|
|
<View className="min-h-6 safe_bottom"></View>
|
|
</View>
|
|
);
|
|
});
|
|
|
|
interface LayoutProps {
|
|
loadding: boolean;
|
|
}
|
|
|
|
interface LayoutComponent extends FC<PropsWithChildren<LayoutProps>> {
|
|
Header: FC<PropsWithChildren<LayoutHeaderProps>>;
|
|
Footer: FC<PropsWithChildren>;
|
|
Container: FC<PropsWithChildren<LayoutContainerProps>>;
|
|
}
|
|
|
|
const LayoutBase = ({
|
|
children,
|
|
loadding = true,
|
|
}: PropsWithChildren<LayoutProps>) => {
|
|
if (loadding) {
|
|
return <PageLoader></PageLoader>;
|
|
}
|
|
return <View className="h-screen w-screen flex flex-col">{children}</View>;
|
|
};
|
|
|
|
const Layout = LayoutBase as unknown as LayoutComponent;
|
|
|
|
Layout.Header = LayoutHeader;
|
|
|
|
Layout.Container = LayoutContainer;
|
|
|
|
Layout.Footer = LayoutFooter;
|
|
|
|
export default Layout;
|