feat(form): add custom navigation bar for form page across multiple platforms
适配APP、微信小程序、华为快应用和鸿蒙元服务,新增自定义导航栏,支持返回和首页按钮,修正多端导航栏适配逻辑
This commit is contained in:
@@ -1,5 +1,24 @@
|
||||
<template>
|
||||
<view :style="themeColor">
|
||||
<!--
|
||||
自绘导航栏(仅在 navigationStyle:custom 的端渲染:APP / 微信小程序 / 华为快应用 / 鸿蒙元服务)
|
||||
- pages.json 已为这几端开启 navigationStyle:custom,系统不再提供原生 navbar
|
||||
- 左侧:hasBackStack 为 true 时显示返回箭头(点击 navigateBack),showHomeBtn=true 时再叠加 Home 胶囊
|
||||
- 中央:表单标题(form_name)
|
||||
- 整体高度 = 状态栏高度 + 44px 内容区,由 customNavbarStyle 动态算
|
||||
-->
|
||||
<view class="form-home-navbar" :style="customNavbarStyle">
|
||||
<view class="form-home-navbar__left">
|
||||
<view v-if="hasBackStack" class="form-home-navbar__back" @tap="goBack" hover-class="form-home-navbar__btn--hover">
|
||||
<text class="form-home-navbar__back-icon">‹</text>
|
||||
</view>
|
||||
<view v-if="showHomeBtn" class="form-home-capsule" @tap="goHome" hover-class="form-home-capsule--hover">
|
||||
<text class="iconfont icon-shouye1 form-home-capsule__icon"></text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="form-home-navbar__title">{{ navbarTitle }}</view>
|
||||
<view class="form-home-navbar__placeholder" />
|
||||
</view>
|
||||
<block v-if="detail">
|
||||
<view class="page">
|
||||
<view class="system-form-wrap">
|
||||
@@ -23,13 +42,9 @@
|
||||
|
||||
<script>
|
||||
|
||||
import diyForm from '@/components-diy/diy-form.vue';
|
||||
import scroll from '@/common/js/scroll-view.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
diyForm
|
||||
},
|
||||
mixins: [scroll],
|
||||
data() {
|
||||
return {
|
||||
@@ -41,8 +56,13 @@ export default {
|
||||
uniacid: 0,
|
||||
isFromMpWebView: false, // 是否是从微信小程序的WebView跳转过来的
|
||||
hideNavbar: false, // 查询参数中支持隐藏Navbar
|
||||
showHomeBtn: false, // 查询参数 show_home=1 时,在系统标题栏左侧显示 Home 按钮
|
||||
urlParams: {}, // 通用 URL 透传参数收集
|
||||
handleSubmit: null, // create 的 this 绑定版本,作为 :onSubmit 传给 diy-form(见 created)
|
||||
navbarHeight: 44, // 自绘 navbar 内容区高度(不含状态栏),跨端兜底 44px
|
||||
statusBarHeight: 20, // 系统状态栏高度,自绘 navbar 时需要预留顶部空隙
|
||||
navbarTitle: '', // 自绘 navbar 中央显示的标题
|
||||
hasBackStack: false, // 自绘 navbar 左上角是否显示返回箭头:页面栈 > 1 时显示
|
||||
|
||||
// #ifdef H5
|
||||
formTitle: '', // 查询参数传入的Title
|
||||
@@ -58,6 +78,20 @@ export default {
|
||||
this.handleSubmit = this.create.bind(this);
|
||||
},
|
||||
computed: {
|
||||
// 自绘 navbar 的高度样式
|
||||
// - 内容区高度 = navbarHeight(兜底 44px)
|
||||
// - 整体高度 = 状态栏高度 + 内容区高度
|
||||
// - padding-top 把内容区下移到状态栏之下,避免被刘海/水滴遮挡
|
||||
customNavbarStyle() {
|
||||
try {
|
||||
const sys = uni.getSystemInfoSync();
|
||||
const sb = sys.statusBarHeight || 0;
|
||||
const h = this.navbarHeight || 44;
|
||||
return `height: ${sb + h}px; padding-top: ${sb}px;`;
|
||||
} catch (e) {
|
||||
return 'height: 64px; padding-top: 20px;';
|
||||
}
|
||||
},
|
||||
// 构造 diy-form raw 模式期望的 value 形态:
|
||||
// - jsonData:透传给 ns-newform(已是 ns-newform 期望的 [{ controller, value, val }])
|
||||
// - formConfig:后端返回 camelCase(如 formPaddingX / formBgColor / labelFontSize),
|
||||
@@ -98,11 +132,30 @@ export default {
|
||||
this.uniacid = options.uniacid
|
||||
this.id = options.id || 0
|
||||
this.isFromMpWebView = options.from === 'mp-webview';
|
||||
this.hideNavbar = Boolean(options?.hide_navbar);
|
||||
this.hideNavbar = this.$util.coerceBool(options?.hide_navbar);
|
||||
// show_home=1 时在系统标题栏左侧显示 Home 按钮,点击回到首页 INDEX_PAGE_URL
|
||||
// 解析规则统一走 this.$util.coerceBool:支持 '1' / 'true' / 1 / true 四种写法
|
||||
this.showHomeBtn = this.$util.coerceBool(options?.show_home);
|
||||
|
||||
// 自绘 navbar 需要用到状态栏高度 / navbar 内容区高度(跨端兜底)
|
||||
try {
|
||||
const sys = uni.getSystemInfoSync();
|
||||
if (sys.statusBarHeight) this.statusBarHeight = sys.statusBarHeight;
|
||||
// uniapp 在不同端 menuButton / navigationBar 高度略有差异,给个安全兜底即可
|
||||
if (typeof sys.navbarHeight === 'number' && sys.navbarHeight > 0) {
|
||||
this.navbarHeight = sys.navbarHeight;
|
||||
}
|
||||
} catch (e) { /* keep defaults */ }
|
||||
|
||||
// 自绘 navbar 是否显示返回箭头:getCurrentPages().length > 1 即页面有栈
|
||||
try {
|
||||
const pages = getCurrentPages ? getCurrentPages() : [];
|
||||
this.hasBackStack = Array.isArray(pages) && pages.length > 1;
|
||||
} catch (e) { /* keep default */ }
|
||||
|
||||
// 通用 URL 参数收集:除业务保留字段外,其余全部透传给后端
|
||||
// 后端会做卫生过滤并广播事件,供 recommend 等插件消费
|
||||
const reservedKeys = ['uniacid', 'id', 'scene', 'from', 'hide_navbar', 'title', 'enable_set_title'];
|
||||
const reservedKeys = ['uniacid', 'id', 'scene', 'from', 'hide_navbar', 'show_home', 'title', 'enable_set_title'];
|
||||
this.urlParams = {};
|
||||
for (let key in options) {
|
||||
if (reservedKeys.indexOf(key) !== -1) continue;
|
||||
@@ -145,6 +198,17 @@ export default {
|
||||
this.updateNavbarTitle();
|
||||
},
|
||||
methods: {
|
||||
// 返回上一页;用 navigateBack,栈深度>1 时由 hasBackStack 控制按钮可见性
|
||||
goBack() {
|
||||
uni.navigateBack({ delta: 1 });
|
||||
},
|
||||
// 返回首页;
|
||||
// INDEX_PAGE_URL 是 TabBar 根页面,不能用裸 uni.redirectTo(会报"can not redirectTo a tabBar page")
|
||||
// 必须用 this.$util.redirectTo,内部对 TabBar 页面自动走 uni.switchTab
|
||||
// (鸿蒙元服务下还会用 reLaunch 兜底,见 common/js/util.js)
|
||||
goHome() {
|
||||
this.$util.redirectTo(this.$util.INDEX_PAGE_URL, {}, 'redirectTo');
|
||||
},
|
||||
getData() {
|
||||
this.$api.sendRequest({
|
||||
url: '/form/api/form/info',
|
||||
@@ -172,7 +236,15 @@ export default {
|
||||
formName = this.formTitle;
|
||||
}
|
||||
// #endif
|
||||
// H5 端:系统原生 navbar —— uni.setNavigationBarTitle 改原生标题
|
||||
// #ifdef H5
|
||||
uni.setNavigationBarTitle({ title: formName });
|
||||
// #endif
|
||||
// 非 H5 端:custom 模式下没有原生 navbar,直接写 data.navbarTitle,
|
||||
// 由模板里的 .form-home-navbar__title 显示
|
||||
// #ifndef H5
|
||||
this.navbarTitle = formName;
|
||||
// #endif
|
||||
},
|
||||
// 由 diy-form 内置提交按钮触发:diy-form handleSubmit 已先做 verify(),
|
||||
// 仅在通过时才通过 :onSubmit 函数 prop 调用本方法(跨 H5 / MP 一致,不走 $emit / $listeners)。
|
||||
@@ -223,6 +295,109 @@ page {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
/*
|
||||
* 自绘 navbar(非 H5 端:APP / 微信小程序 / 华为快应用 / 鸿蒙元服务)
|
||||
* - pages.json 已为这几端开启 navigationStyle:custom,系统不再提供原生 navbar
|
||||
* - 整体高度由 inline style 注入:statusBarHeight + 44px
|
||||
* - 左侧 Home 胶囊 + 中央标题 + 右侧占位,标题过长自动省略
|
||||
*/
|
||||
|
||||
/* --- 自绘 navbar 容器 --- */
|
||||
.form-home-navbar {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
background: #ffffff;
|
||||
/* 高度由 inline style 通过 customNavbarStyle 注入:statusBarHeight + 44px */
|
||||
/* padding-top 把内容区下移到状态栏之下,避免被刘海/水滴遮挡 */
|
||||
/* 浮在表单内容之上,避免被 diy-form 等组件覆盖 */
|
||||
z-index: 100;
|
||||
/* 让内容靠 flex 自动落到 padding-top 之下的区域 */
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.form-home-navbar__left {
|
||||
/* 不再用 absolute,跟着 navbar 的 padding-top 下移 */
|
||||
flex: none;
|
||||
/* 宽度自适应:返回按钮 56rpx + Home 胶囊 56rpx + 间距 12rpx + 左 padding 16rpx = 最多 ~140rpx */
|
||||
min-width: 88rpx;
|
||||
max-width: 200rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding-left: 16rpx;
|
||||
box-sizing: border-box;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.form-home-navbar__back {
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.form-home-navbar__back-icon {
|
||||
font-size: 48rpx;
|
||||
line-height: 1;
|
||||
color: #000;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.form-home-navbar__btn--hover {
|
||||
background: rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.form-home-navbar__title {
|
||||
flex: 1;
|
||||
height: 44px;
|
||||
line-height: 44px;
|
||||
text-align: center;
|
||||
font-size: 32rpx;
|
||||
color: #000;
|
||||
font-weight: 500;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.form-home-navbar__placeholder {
|
||||
/* 与 __left 对称,保持标题视觉居中 */
|
||||
flex: none;
|
||||
min-width: 88rpx;
|
||||
max-width: 200rpx;
|
||||
}
|
||||
|
||||
/* --- Home 胶囊主体 --- */
|
||||
.form-home-capsule {
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.form-home-capsule--hover {
|
||||
background: rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
/*
|
||||
* Home 图标 —— 使用项目统一的 iconfont icon-shouye1(unicode \e02d)
|
||||
* 与 pages_goods/detail.vue 的首页按钮共用同一个图标,跨端视觉一致
|
||||
*/
|
||||
.form-home-capsule__icon {
|
||||
font-size: 32rpx;
|
||||
line-height: 1;
|
||||
color: #000;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.system-form-wrap {
|
||||
overflow: hidden;
|
||||
margin: 0 0 60rpx 0;
|
||||
|
||||
Reference in New Issue
Block a user