Files
WAI_Project_UNIX/components/tabbar.uvue
2026-01-21 01:37:34 +08:00

260 lines
6.0 KiB
Plaintext

<template>
<cl-footer
:pt="{
content: {
className: '!p-0 !overflow-visible'
}
}"
>
<view class="custom-tabbar" :class="{ 'is-dark': isDark }">
<view
class="custom-tabbar-item"
v-for="(item, index) in currentList"
:key="index"
@tap="onTabClick(item)"
>
<!-- 普通图标 -->
<cl-image
v-if="!item.isCenter"
:src="path == item.pagePath ? item.selectedIconPath : item.iconPath"
:height="56"
:width="56"
></cl-image>
<!-- 文字 -->
<cl-text
v-if="!item.isCenter && item.text != ''"
:pt="{
className: parseClass([
'text-xs mt-1',
[path == item.pagePath, 'text-primary-500', 'text-surface-400']
])
}"
>{{ item.text }}</cl-text
>
<!-- 中间占位 -->
<view v-if="item.isCenter" style="width: 56rpx; height: 56rpx;"></view>
</view>
<!-- 悬浮的大LOGO按钮 -->
<view
class="center-btn-wrapper"
v-if="centerBtnConfig.visible"
@tap="onCenterClick"
>
<image
class="center-btn-image"
:src="centerBtnConfig.iconPath != '' ? centerBtnConfig.iconPath : '/static/images/logo_center_btn.png'"
mode="aspectFit"
/>
</view>
</view>
</cl-footer>
</template>
<script setup lang="ts">
import { ctx, isDark, parseClass, router, useCool, parseObject } from "@/cool";
import { t } from "@/locale";
import { computed, onMounted, ref } from "vue";
defineOptions({
name: "custom-tabbar"
});
type TabItem = {
text: string;
pagePath: string;
iconPath: string;
selectedIconPath: string;
isCenter?: boolean;
};
type CenterBtnConfig = {
visible: boolean;
iconPath: string;
pagePath: string;
};
const { service } = useCool();
const path = computed(() => router.path());
// 默认 TabBar 列表
const defaultList = computed<TabItem[]>(() => {
return (ctx.tabBar.list ?? []).map((e): TabItem => {
return {
iconPath: e.iconPath!,
selectedIconPath: e.selectedIconPath!,
pagePath: e.pagePath,
text: t(e.text?.replaceAll("%", "")!)
};
});
});
// 动态配置
const dynamicList = ref<TabItem[]>([]);
const centerBtnConfig = ref<CenterBtnConfig>({
visible: false,
iconPath: "",
pagePath: ""
});
// 最终使用的列表
const currentList = computed<TabItem[]>(() => {
if (dynamicList.value.length > 0) {
return dynamicList.value;
}
// 如果有中间按钮,需要在默认列表中间插入占位
if (centerBtnConfig.value.visible) {
const list = [...defaultList.value];
const centerIndex = Math.floor(list.length / 2);
list.splice(centerIndex, 0, {
text: "",
pagePath: "",
iconPath: "",
selectedIconPath: "",
isCenter: true
} as TabItem);
return list;
}
return defaultList.value;
});
// 加载配置
/* Loading Config Logic */
async function loadConfig() {
// 先设置默认的中间按钮配置
centerBtnConfig.value = {
visible: true,
iconPath: "/static/images/logo_center_btn.png",
pagePath: "/pages/seed/store"
} as CenterBtnConfig;
try {
const res = await service.base.comm.param({ key: "app_tabbar" });
if (res != null && res != '') {
const config = parseObject<UTSJSONObject>(res);
if (config != null) {
// 解析列表
const listData = config.getArray<UTSJSONObject>("list");
if (listData != null && listData.length > 0) {
const newList : TabItem[] = [];
listData.forEach((e) => {
newList.push({
text: e.getString("text") ?? "",
pagePath: e.getString("pagePath") ?? "",
iconPath: e.getString("iconPath") ?? "",
selectedIconPath: e.getString("selectedIconPath") ?? "",
isCenter: e.getBoolean("isCenter") ?? false
} as TabItem);
});
dynamicList.value = newList;
}
// 解析中间按钮
const btn = config.get("centerBtn");
if (btn != null) {
const btnObj = btn as UTSJSONObject;
centerBtnConfig.value = {
visible: btnObj.getBoolean("visible") ?? true,
iconPath: btnObj.getString("iconPath") ?? "/static/images/logo_center_btn.png",
pagePath: btnObj.getString("pagePath") ?? "/pages/seed/store"
} as CenterBtnConfig;
}
}
}
} catch (e) {
console.log("使用默认 TabBar 配置");
}
}
function onTabClick(item: TabItem) {
if (!item.isCenter && item.pagePath != "") {
router.push({
path: item.pagePath
});
}
}
function onCenterClick() {
if (centerBtnConfig.value.pagePath != "") {
router.push({
path: centerBtnConfig.value.pagePath
});
}
}
onMounted(() => {
loadConfig();
});
// 隐藏原生 tabBar
// #ifndef MP
if (ctx.tabBar.list != null) {
uni.hideTabBar();
}
// #endif
</script>
<style lang="scss" scoped>
.custom-tabbar {
display: flex;
flex-direction: row;
align-items: center;
position: relative;
min-height: 120rpx;
padding-top: 10rpx;
padding-bottom: env(safe-area-inset-bottom);
background: #ffffff;
overflow: visible;
box-sizing: border-box;
border-top: 1rpx solid #f0f0f0;
}
.custom-tabbar-item {
flex: 1; /* 关键:确保所有项(包括占位符)平分宽度 */
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100rpx;
}
.center-placeholder {
width: 120rpx; /* 与大按钮宽度接近,确保空间对齐 */
height: 50rpx;
}
.center-btn-wrapper {
position: absolute;
left: 50%;
bottom: 50rpx; /* 进一步调高,应对不同机型的安全区 */
transform: translateX(-50%);
width: 130rpx;
height: 130rpx;
background: #ffffff;
border-radius: 65rpx;
box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.08);
display: flex;
align-items: center;
justify-content: center;
z-index: 100;
border: 4rpx solid #ffffff;
.center-btn-image {
width: 110rpx;
height: 110rpx;
border-radius: 55rpx;
}
}
.center-btn-wrapper::after {
content: "";
position: absolute;
top: -1rpx; left: -1rpx; right: -1rpx; bottom: -1rpx;
border-radius: 66rpx;
border: 1rpx solid #eee;
z-index: -1;
}
</style>