> 技术文档 > vue3+ts+uniapp+unibest 微信小程序(第二篇)—— 图文详解自定义背景图页面布局、普通页面布局、分页表单页面布局_wd-navbar

vue3+ts+uniapp+unibest 微信小程序(第二篇)—— 图文详解自定义背景图页面布局、普通页面布局、分页表单页面布局_wd-navbar


文章目录

    • 简介
    • 一、自定义背景图布局
        • 1.1 效果预览
        • 1.2 实现思路
        • 1.3 custom-page 组件全量代码
        • 1.4 页面使用
    • 二、普通页面布局
        • 2.1 效果预览
        • 2.2 实现思路
        • 2.3 公共样式部分
        • 2.4 页面使用
    • 三、分页表单页面布局
        • 3.1 效果预览
        • 3.2 实现思路
        • 3.3 页面代码

简介

开发工具:VsCode
技术栈:vue3 + Ts + uni-app + unibest + Wot Design Uni
简介:图文结合,十分钟带你搞定微信小程序常见页面布局


一、自定义背景图布局

1.1 效果预览

vue3+ts+uniapp+unibest 微信小程序(第二篇)—— 图文详解自定义背景图页面布局、普通页面布局、分页表单页面布局_wd-navbar

1.2 实现思路
  • 因为小程序原生顶部导航栏只有白色、黑色两种背景色,所以使用自定义导航栏
  • 提高代码复用率,采用组件封装得形式。代码见 ——》步骤 1.3 custom-page 组件全量代码
  • wot design uni 组件库的 wd-navbar 组件刚好符合需求
  • 关键:wd-navbar 组件有一个属性(placeholder:固定在顶部时,在标签位置生成一个等高的占位元素),解决了顶部高度计算的问题
  • 具体文档所在位置如下图

vue3+ts+uniapp+unibest 微信小程序(第二篇)—— 图文详解自定义背景图页面布局、普通页面布局、分页表单页面布局_wd-navbar


vue3+ts+uniapp+unibest 微信小程序(第二篇)—— 图文详解自定义背景图页面布局、普通页面布局、分页表单页面布局_wd-navbar

1.3 custom-page 组件全量代码
<template> <view class=\"h-full w-full bg-[#F5F5F5] custom-page-container\"> <!-- 自定义顶部背景图(可选) --> <image src=\"@/static/images/home/bg.png\" class=\"custom-page-bg\" /> <!-- 页面内容 --> <view class=\"custom-page-main\"> <!-- 自定义顶部--导航栏 --> <wd-navbar :title=\"title\" safeAreaInsetTop placeholder :left-arrow=\"showBack\" @click-left=\"handleClickLeft\" custom-class=\"wd-navbar-custom\" ></wd-navbar> <!-- 页面主体--功能版块 --> <view class=\"custom-page-content\"> <!-- 页面主体内容--插槽 --> <view :class=\"showPagePadding ? \'page-content-p\' : \'\'\" class=\"custom-slot-content\"> <slot name=\"content\"></slot> </view> <!-- 页面底部按钮--插槽 --> <slot name=\"footer\"></slot> </view> </view> </view></template><script lang=\"ts\" setup>defineProps({ // navbar标题 title: { type: String, default: \'\', required: true, }, // 是否显示返回按钮 showBack: { type: Boolean, default: true, }, // 页面主体是否需要展示左右边距 showPagePadding: { type: Boolean, default: true, },})/** * 返回上一级 */function handleClickLeft() { uni.navigateBack()}</script><style lang=\"scss\" scoped>.custom-page-container { position: relative; .custom-page-bg { position: absolute; top: 0; left: 0; z-index: 1; width: 100%; height: 668rpx; } .custom-page-main { position: absolute; top: 0; left: 0; z-index: 2; display: flex; flex-direction: column; width: 100%; height: 100%; overflow: hidden; .custom-page-content { display: flex; flex: 1; flex-direction: column; justify-content: space-between; max-height: 100%; overflow: hidden; .custom-slot-content { flex: 1; overflow: hidden; } } }}</style>
1.4 页面使用

vue3+ts+uniapp+unibest 微信小程序(第二篇)—— 图文详解自定义背景图页面布局、普通页面布局、分页表单页面布局_wd-navbar


二、普通页面布局

2.1 效果预览

vue3+ts+uniapp+unibest 微信小程序(第二篇)—— 图文详解自定义背景图页面布局、普通页面布局、分页表单页面布局_wd-navbar

2.2 实现思路
  • 整个页面 flex 布局,中间部分 flex:1。
  • 抽离公共布局样式,不采用组件封装方式,减小心智负担
2.3 公共样式部分
// /src/style/public.scss// 公共页面布局样式--简易版.custom-page-simple { display: flex; flex-direction: column; width: 100%; height: 100vh; overflow: hidden; background-color: $open-bg-grey; .page-main { flex: 1; padding: 28rpx; margin: 28rpx; overflow: auto; background: $open-bg-primary; border-radius: $open-border-radius; } .page-footer { width: 100%; height: 130rpx; padding: 30rpx 28rpx; background-color: $open-text-color-inverse; box-shadow: 0rpx 0rpx 27.78rpx 0rpx rgba(9, 197, 133, 0.25); }}
2.4 页面使用

vue3+ts+uniapp+unibest 微信小程序(第二篇)—— 图文详解自定义背景图页面布局、普通页面布局、分页表单页面布局_wd-navbar


三、分页表单页面布局

3.1 效果预览

vue3+ts+uniapp+unibest 微信小程序(第二篇)—— 图文详解自定义背景图页面布局、普通页面布局、分页表单页面布局_wd-navbar
vue3+ts+uniapp+unibest 微信小程序(第二篇)—— 图文详解自定义背景图页面布局、普通页面布局、分页表单页面布局_wd-navbar

3.2 实现思路
  • 使用 z-paging 组件
  • z-paging 官方文档:z-paging官方文档
  • 组件安装方式参考官方,没有比这个更清晰的了
3.3 页面代码
<route lang=\"json5\" type=\"page\">{ layout: \'default\', style: { navigationBarTitleText: \'公告列表\', }, custom: true,}</route><template> <z-paging ref=\"pagingRef\" v-model=\"noticeList\" :paging-style=\"{ backgroundColor: \'#fff\' }\" :default-page-size=\"pageQuery.size\" @query=\"queryList\" > <!-- 顶部搜索栏 --> <template #top> <wd-search ref=\"searchRef\" v-model=\"pageQuery.someText\" placeholder=\"请输入搜索内容\" placeholder-left hide-cancel :maxlength=\"50\" @change=\"handleSearchChangeDebounce\" /> </template> <!-- 通告列表 --> <div class=\"notice-list\"> <div class=\"notice-item\" v-for=\"(item, index) in noticeList\" :key=\"index\"> <!-- 标题 --> <div class=\"notice-title\">{{ item.title }}</div> <!-- 内容 --> <span v-if=\"!item.details || item.details.length < 58\" class=\"notice-content\"> {{ item.details || \'--\' }} </span> <wd-collapse v-else v-model=\"item.showMoreContent\" viewmore custom-class=\"notice-content\" :line-num=\"2\" > {{ item.details || \'--\' }} </wd-collapse> <!-- 发布时间 --> <div class=\"notice-publish-time\"> {{ \'发布时间:\' + parseTime(item.updateTime, \'{y}-{m}-{d} {h}:{i}\') }} </div> <!-- 图片、视频列表 --> <div class=\"notice-file flex items-center\"> <div class=\"notice-file-item\" v-for=\"(file, fileIndex) in item.pictureUrlList\" :key=\"fileIndex\" > <wd-img  :width=\"100\"  :height=\"75\"  :src=\"file\"  :enable-preview=\"true\"  mode=\"aspectFill\" /> </div> <div class=\"notice-file-item\" v-for=\"(file, fileIndex) in item.videoUrlList\" :key=\"fileIndex\" > <video :src=\"file\" :id=\"`video${fileIndex}`\" controls></video> </div> </div> </div> </div> </z-paging></template><script lang=\"ts\" setup>import { debounce } from \'lodash-es\'import { useUserStore } from \'@/store\'import { fetchNoticePageList } from \'@/api/fitness-test/notice\'import { INoticePageQuery, INoticePageResponseListItem } from \'@/api/fitness-test/notice/types\'import { parseTime } from \'@/utils/business\'const userStore = useUserStore()// 通知公列表--数据const noticeList = ref<INoticePageResponseListItem[]>([])/** ================================ 分页请求 start ================================== */// z-paging 组件实例const pagingRef = ref<ZPagingRef>()// 分页查询参数const pageQuery = reactive<Omit<INoticePageQuery, \'schoolId\'>>({ current: 1, size: 6, someText: \'\',})// 搜索框实例const searchRef = ref()// 数据总数const total = ref(0)// 学校idconst schoolId = computed(() => userStore.getUserInfo.schoolId)/** * 分页查询 * @param pageNo 当前页 * @param pageSize 每页条数 */function queryList(pageNo: number, pageSize: number) { // console.log(\'queryList\', pageNo, pageSize) fetchNoticePageList({ current: pageNo, size: pageSize, schoolId: schoolId.value, someText: pageQuery.someText, }) .then((res) => { const { count, data, size, current } = res.data // 赋值给total total.value = count // 赋值给pageQuery.size pageQuery.size = size // 赋值给pageQuery.current pageQuery.current = current const resultList = (data || []).map((item) => { return { ...item, showMoreContent: false, pictureUrlList: item.pictureUrlList || [], videoUrlList: item.videoUrlList || [], } }) // 将请求的结果数组传递给z-paging // 参考文档:https://z-paging.zxlee.cn/api/methods/main.html#%E6%95%B0%E6%8D%AE%E5%88%B7%E6%96%B0-%E5%A4%84%E7%90%86%E6%96%B9%E6%B3%95 pagingRef.value?.completeByTotal([...resultList], total.value) }) .catch((err) => { console.log(err) pagingRef.value?.complete(false) })}// 为提升性能,避免高频触发接口,搜索框改变时触发用 防抖 函数const handleSearchChangeDebounce = debounce(handleSearchChange, 500)/** * 搜索框改变时触发 * @param value 搜索框的值 */function handleSearchChange(ipt: { value: string }) { // console.log(\'handleSearchChange\', ipt) // 重新请求 pagingRef.value?.reload()}/** ================================ 分页请求 end ================================== */</script>