feat:tabbar

This commit is contained in:
dzc committed 2026-08-24 17:29:33 +08:00
1 parent f4adf95e7a
commit 6d267bcfc5
11 files changed
+142 -26

No files matched your search

+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "",
"appid": "",
"appid": "wx11771c7bf2913cb2",
"description": "",
"versionName": "1.0.0",
"versionCode": "100",
@@ -50,7 +50,7 @@
"quickapp": {},
/* 小程序特有相关 */
"mp-weixin": {
"appid": "",
"appid": "wx11771c7bf2913cb2",
"setting": {
"urlCheck": false
},
+1
View File
@@ -1,5 +1,6 @@
<template>
<crud-layout title="通知公告" :bg="filePath(SERVICE_IMAGE.blueBg)">
<view class="h-30rpx" />
<view class="notice-container">
<view class="notice-list">
<view v-if="noticeList.length === 0" class="empty-tip"> 暂无公告 </view>
+2
View File
@@ -73,6 +73,8 @@ const doMenu = async (v: any) => {
// 是否是具体页面
const routePath = v.page.split("?")[0]
if (v.page.startsWith("/") && __ROUTES__.some((route) => route.path === routePath)) {
// 直接页面入口也要先组装所属业务 Tabbar,再进入目标页面。
await tabbarStore.componetTabbarList(v.page)
router.jump(v.page)
return
}
+40 -9
View File
@@ -1,24 +1,55 @@
<script setup lang="ts">
import { ref, reactive } from "vue"
import { onLoad, onShow } from "@dcloudio/uni-app"
import { reactive } from "vue"
import { onLoad } from "@dcloudio/uni-app"
const state = reactive({
url: ""
url: "",
})
onLoad((options: any) => {
if (options.url) {
state.url = decodeURIComponent(options.url)
// 只记录地址结构,不输出 token,便于定位微信 WebView 域名校验问题。
try {
const url = new URL(state.url)
console.log("[web-view] 加载地址:", {
origin: url.origin,
pathname: url.pathname,
queryKeys: Array.from(url.searchParams.keys()),
})
} catch (error) {
console.error("[web-view] 地址解析失败:", error)
}
}
})
const handleWebViewLoad = (event: any) => {
console.log("[web-view] 加载完成:", event?.detail?.src || state.url)
}
const handleWebViewError = (event: any) => {
console.error("[web-view] 加载失败:", {
url: state.url,
detail: event?.detail,
})
uni.showToast({ title: "第三方页面加载失败,请检查业务域名配置", icon: "none" })
}
</script>
<template>
<crud-layout mainIsScroll tabBarType="just-flot-back">
<web-view :update-title="false" :fullscreen="false" :webview-styles="{
width: '100%',
height: '100%',
backgroundColor: '#fff'
}" :src="state.url" />
<web-view class="mall-web-view" :update-title="false" :fullscreen="false" @load="handleWebViewLoad"
@error="handleWebViewError" :webview-styles="{
width: '100%',
height: '100%',
backgroundColor: '#fff'
}" :src="state.url" />
</crud-layout>
</template>
</template>
<style lang="scss" scoped>
.mall-web-view {
width: 100%;
height: 100%;
}
</style>
+28 -3
View File
@@ -33,8 +33,18 @@ function normalizeMallUrl(rawUrl: string) {
const [, origin, rawPathname = "/", search = "", hash = ""] = matched
const queryItems = search.slice(1).split("&").filter(Boolean)
const queryKeys = queryItems.map((item) => decodeURIComponent(item.split("=")[0] || ""))
const hasToken = queryItems.some((item) => /^token=.+/i.test(item))
const decodeQueryPart = (value: string) => {
try {
return decodeURIComponent(value)
} catch {
return value
}
}
const queryKeys = queryItems.map((item) => decodeQueryPart(item.split("=")[0] || ""))
const hasToken = queryItems.some((item) => {
const [key, ...valueParts] = item.split("=")
return decodeQueryPart(key || "").toLowerCase() === "token" && valueParts.join("=")
})
let pathname = rawPathname
// 宁波银行正常登录页是 /h5/thirdApp;部分环境会错误返回带 token 的根路径。
@@ -51,7 +61,22 @@ function normalizeMallUrl(rawUrl: string) {
if (pathname === "/" || !hasToken) {
throw new Error("商城登录地址异常,请联系管理员")
}
return `${origin}${pathname}${search}${hash}`
// 宁波银行 H5 通过 platform 判断是否运行在微信小程序中。设置为 XR 后,
// H5 左上角返回按钮会调用 wx.miniProgram.navigateBack(),直接返回小程序上一页,
// 不会先消耗宁波银行 H5 自己的 history。覆盖旧值,避免后端返回其他平台参数。
const queryWithoutPlatform = queryItems.filter((item) => {
const key = decodeQueryPart(item.split("=")[0] || "").toLowerCase()
return key !== "platform"
})
queryWithoutPlatform.push("platform=XR")
const normalizedSearch = `?${queryWithoutPlatform.join("&")}`
console.log("[happyMall] 跳转平台:", {
platform: "XR",
queryKeys: queryWithoutPlatform.map((item) => decodeQueryPart(item.split("=")[0] || "")),
})
return `${origin}${pathname}${normalizedSearch}${hash}`
}
onLoad(async () => {
@@ -93,6 +93,10 @@ function initBackStyle() {
function tabbarChange(index: number) {
const target = tabbarStore.tabbarList[index]
if (!target) return
if (target.pagePath === "/pages/root/dashboard") {
// 从业务导航返回工作台时,让首页根 Tabbar 同步选中“首页”。
tabbarStore.setRootActiveIndex(0)
}
jump(target.pagePath, {}, 3)
}
+65 -12
View File
@@ -118,16 +118,62 @@ const TABBAR_CONSTANT: Array<TabbarItem> = [
launchParam: ["integrated"],
auth: ["user"],
},
{
pagePath: "/pages/integrated/notice/index",
text: "通知公告",
iconPath: "/static/tabbar/notice.png",
selectedIconPath: "/static/tabbar/notice-select.png",
launchParam: ["notice"],
auth: ["user"],
},
{
pagePath: "/pages/integrated/feedback/index",
text: "意见反馈",
iconPath: "/static/tabbar/feedback.png",
selectedIconPath: "/static/tabbar/feedback-select.png",
launchParam: ["feedback"],
auth: ["user"],
},
{
pagePath: "/pages/tabbar/my",
text: "我的",
iconPath: "/static/tabbar/my.png",
selectedIconPath: "/static/tabbar/my-select.png",
launchParam: ["canteen", "selfMerchant", "onlineShop", "coopMerchant", "integrated", "prosperityShop", "prosperityShopMchList"],
launchParam: [
"canteen",
"selfMerchant",
"onlineShop",
"coopMerchant",
"integrated",
"prosperityShop",
"prosperityShopMchList",
"notice",
"feedback",
],
auth: ["user", "merchant", "doorAdmin"],
},
]
const WORKBENCH_TABBAR_ITEM: TabbarItem = {
pagePath: "/pages/root/dashboard",
text: "工作台",
iconPath: "/static/tabbar/home.png",
selectedIconPath: "/static/tabbar/home-select.png",
launchParam: [],
auth: [],
}
function getTabbarLaunchParam(url?: string) {
const launchParam = getLaunchParam(url)
if (launchParam) return launchParam
if (!url) return ""
// 常用功能也可能直接配置 Tabbar 页面路径,需要由页面反推出所属业务场景。
const pagePath = url.split("?")[0]
const tabbarItem = TABBAR_CONSTANT.find((item) => item.pagePath === pagePath)
return tabbarItem?.launchParam[0] || ""
}
const defaultRootTabbarList: Array<TabbarItem> = [
{
pagePath: "/pages/root/dashboard",
@@ -166,13 +212,17 @@ export const useTabbarStore = defineStore("tabbarStore", {
}),
getters: {
isDashboardPage(state) {
return `/${state.currentRoute}` === "/pages/root/dashboard" || `/${state.currentRoute}` === "/pages/tabbar/my"
const currentRoute = state.currentRoute.startsWith("/") ? state.currentRoute : `/${state.currentRoute}`
return currentRoute === "/pages/root/dashboard" || currentRoute === "/pages/tabbar/my"
},
isTabbar(state) {
return state.tabbarList.some((item) => item.pagePath === `/${state.currentRoute}`)
const currentRoute = state.currentRoute.startsWith("/") ? state.currentRoute : `/${state.currentRoute}`
// 工作台只作为业务 Tabbar 的返回入口,进入页面后仍展示首页自己的根 Tabbar。
return currentRoute !== WORKBENCH_TABBAR_ITEM.pagePath && state.tabbarList.some((item) => item.pagePath === currentRoute)
},
activeIndex(state) {
return state.tabbarList.findIndex((item) => item.pagePath === `/${state.currentRoute}`)
const currentRoute = state.currentRoute.startsWith("/") ? state.currentRoute : `/${state.currentRoute}`
return state.tabbarList.findIndex((item) => item.pagePath === currentRoute)
},
pageTabbarList(state) {
return state.tabbarList.map((item) => {
@@ -196,22 +246,25 @@ export const useTabbarStore = defineStore("tabbarStore", {
},
actions: {
async componetTabbarList(launchParam?: string) {
let _launchParam = ""
if (launchParam) {
_launchParam = getLaunchParam(launchParam)
} else {
_launchParam = getLaunchParam()
}
if (this.launchParam === _launchParam) return
const _launchParam = getTabbarLaunchParam(launchParam)
const hasMyTabbar = this.tabbarList.some((item) => item.pagePath === "/pages/tabbar/my")
const hasWorkbenchTabbar = this.tabbarList.some((item) => item.pagePath === WORKBENCH_TABBAR_ITEM.pagePath)
// 热更新或旧状态中可能已有相同场景但尚未加入工作台,此时仍需重新组装。
if (this.launchParam === _launchParam && (!hasMyTabbar || hasWorkbenchTabbar)) return
const appInfo = await getAppInfo()
if (!_launchParam || !appInfo) return
const tabbarList: Array<TabbarItem> = []
TABBAR_CONSTANT.forEach((item, index) => {
TABBAR_CONSTANT.forEach((item) => {
const visible = item.launchParam.includes(_launchParam)
if (visible) {
tabbarList.push(item)
}
})
const myTabbarIndex = tabbarList.findIndex((item) => item.pagePath === "/pages/tabbar/my")
if (myTabbarIndex !== -1) {
// 仅对带“我的”的业务导航生效,并保证“工作台”紧邻其左侧。
tabbarList.splice(myTabbarIndex, 0, WORKBENCH_TABBAR_ITEM)
}
this.tabbarList = tabbarList
this.launchParam = _launchParam
},
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB