391 lines
11 KiB
Vue
391 lines
11 KiB
Vue
<template>
|
||
<view class="page">
|
||
<!-- #ifdef APP -->
|
||
<statusBar></statusBar>
|
||
<!-- #endif -->
|
||
|
||
<!-- 导航栏 -->
|
||
<uni-nav-bar
|
||
:fixed="true"
|
||
:statusBar="true"
|
||
title="AI销冠系统"
|
||
leftIcon="left"
|
||
@clickLeft="goBack"
|
||
color="#333"
|
||
backgroundColor="#FFFFFF">
|
||
</uni-nav-bar>
|
||
|
||
<view class="content">
|
||
<!-- 标签页 -->
|
||
<view class="tabs" :style="{ top: computedNavbarTop || navbarTop }">
|
||
<view
|
||
class="tab-item"
|
||
:class="{ active: activeTab === 'practice' }"
|
||
@click="switchTab('practice')">
|
||
<text>场景</text>
|
||
</view>
|
||
<view
|
||
class="tab-item"
|
||
:class="{ active: activeTab === 'training' }"
|
||
@click="switchTab('training')">
|
||
<text>陪练</text>
|
||
</view>
|
||
<view
|
||
class="tab-item"
|
||
:class="{ active: activeTab === 'quality' }"
|
||
@click="switchTab('quality')">
|
||
<text>质检</text>
|
||
</view>
|
||
<view
|
||
class="tab-item"
|
||
:class="{ active: activeTab === 'material' }"
|
||
@click="switchTab('material')">
|
||
<text>素材</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 场景tab -->
|
||
<practice-tab
|
||
v-if="activeTab === 'practice'"
|
||
class="tab-content-container"
|
||
:style="{ top: computedContentTop || contentTop }"
|
||
@switch-to-training="handleSwitchToTraining"></practice-tab>
|
||
|
||
<!-- 陪练tab -->
|
||
<training-tab
|
||
v-if="activeTab === 'training'"
|
||
class="tab-content-container"
|
||
:style="{ top: computedContentTop || contentTop }"
|
||
@refresh-training-list="handleRefreshTrainingList"></training-tab>
|
||
|
||
<!-- 质检tab -->
|
||
<scroll-view
|
||
class="tab-content tab-content-container"
|
||
:style="{ top: computedContentTop || contentTop }"
|
||
scroll-y
|
||
v-if="activeTab === 'quality'">
|
||
<view class="champion-card">
|
||
<text class="champion-title">质检</text>
|
||
<text class="champion-desc">这里是质检相关的内容展示区域</text>
|
||
</view>
|
||
</scroll-view>
|
||
|
||
<!-- 素材tab -->
|
||
<material-tab
|
||
v-if="activeTab === 'material'"
|
||
class="tab-content-container"
|
||
:style="{ top: computedContentTop || contentTop }"></material-tab>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
// #ifdef APP
|
||
import statusBar from "@/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-status-bar";
|
||
// #endif
|
||
import PracticeTab from "./components/practice-tab.vue";
|
||
import TrainingTab from "./components/training-tab.vue";
|
||
import MaterialTab from "./components/material-tab.vue";
|
||
|
||
export default {
|
||
// #ifdef APP
|
||
components: {
|
||
statusBar,
|
||
PracticeTab,
|
||
TrainingTab,
|
||
MaterialTab
|
||
},
|
||
// #endif
|
||
// #ifndef APP
|
||
components: {
|
||
PracticeTab,
|
||
TrainingTab,
|
||
MaterialTab
|
||
},
|
||
// #endif
|
||
data() {
|
||
// 动态计算导航栏高度
|
||
const systemInfo = uni.getSystemInfoSync();
|
||
const statusBarHeight = systemInfo.statusBarHeight || 20; // 默认20px
|
||
const navbarHeight = 44; // navbar高度44px
|
||
// 转换为rpx:1px = 2rpx(在375px设计稿下)
|
||
const statusBarHeightRpx = statusBarHeight * 2;
|
||
const navbarHeightRpx = navbarHeight * 2;
|
||
const totalNavbarHeight = statusBarHeightRpx + navbarHeightRpx;
|
||
|
||
return {
|
||
navbarTop: totalNavbarHeight + 'rpx', // tab栏距离顶部的距离
|
||
contentTop: (totalNavbarHeight + 72) + 'rpx', // 内容区域距离顶部的距离(tab栏高度72rpx)
|
||
activeTab: 'practice'
|
||
}
|
||
},
|
||
computed: {
|
||
// 计算导航栏和内容区域的位置(使用计算属性确保响应式)
|
||
computedNavbarTop() {
|
||
const systemInfo = uni.getSystemInfoSync();
|
||
const statusBarHeight = systemInfo.statusBarHeight || 20;
|
||
const navbarHeight = 44;
|
||
const statusBarHeightRpx = statusBarHeight * 2;
|
||
const navbarHeightRpx = navbarHeight * 2;
|
||
const totalNavbarHeight = statusBarHeightRpx + navbarHeightRpx;
|
||
return totalNavbarHeight + 'rpx';
|
||
},
|
||
computedContentTop() {
|
||
const systemInfo = uni.getSystemInfoSync();
|
||
const statusBarHeight = systemInfo.statusBarHeight || 20;
|
||
const navbarHeight = 44;
|
||
const statusBarHeightRpx = statusBarHeight * 2;
|
||
const navbarHeightRpx = navbarHeight * 2;
|
||
const totalNavbarHeight = statusBarHeightRpx + navbarHeightRpx;
|
||
return (totalNavbarHeight + 72) + 'rpx'; // tab栏高度72rpx
|
||
}
|
||
},
|
||
onLoad() {
|
||
console.log('[FurnitureTopSales][onLoad] 页面加载');
|
||
// 更新导航栏位置
|
||
this.updateNavbarPosition();
|
||
// 页面加载时,如果默认是场景tab页,子组件会自动加载数据
|
||
},
|
||
onShow() {
|
||
console.log('[FurnitureTopSales][onShow] 页面显示');
|
||
// 每次页面显示时都重新计算导航栏位置,确保通过导航菜单进入时也能正确显示
|
||
// 使用 $nextTick 确保DOM更新后再设置样式
|
||
this.$nextTick(() => {
|
||
this.updateNavbarPosition();
|
||
// 再次延迟更新,确保样式已应用
|
||
setTimeout(() => {
|
||
this.updateNavbarPosition();
|
||
console.log('[FurnitureTopSales][onShow] 延迟更新后的位置:', {
|
||
navbarTop: this.navbarTop,
|
||
contentTop: this.contentTop
|
||
});
|
||
}, 50);
|
||
});
|
||
},
|
||
onReady() {
|
||
// 页面渲染完成后,检查tab栏是否显示
|
||
console.log('[FurnitureTopSales][onReady] 页面渲染完成');
|
||
console.log('[FurnitureTopSales][onReady] activeTab:', this.activeTab);
|
||
console.log('[FurnitureTopSales][onReady] navbarTop:', this.navbarTop);
|
||
console.log('[FurnitureTopSales][onReady] contentTop:', this.contentTop);
|
||
|
||
// 再次更新导航栏位置,确保渲染后位置正确
|
||
this.updateNavbarPosition();
|
||
|
||
const systemInfo = uni.getSystemInfoSync();
|
||
console.log('[FurnitureTopSales][onReady] 系统信息:', {
|
||
statusBarHeight: systemInfo.statusBarHeight,
|
||
windowHeight: systemInfo.windowHeight,
|
||
screenHeight: systemInfo.screenHeight
|
||
});
|
||
|
||
// 检查tab栏元素是否存在和可见
|
||
// #ifdef MP-WEIXIN
|
||
setTimeout(() => {
|
||
const query = uni.createSelectorQuery().in(this);
|
||
query.select('.tabs').boundingClientRect((data) => {
|
||
console.log('[FurnitureTopSales][onReady] tab栏元素信息:', data);
|
||
if (!data) {
|
||
console.error('[FurnitureTopSales][onReady] tab栏元素未找到!');
|
||
} else {
|
||
console.log('[FurnitureTopSales][onReady] tab栏位置:', {
|
||
top: data.top,
|
||
left: data.left,
|
||
width: data.width,
|
||
height: data.height,
|
||
expectedTop: this.navbarTop,
|
||
isVisible: data.width > 0 && data.height > 0,
|
||
isOnScreen: data.top >= 0 && data.top < systemInfo.windowHeight
|
||
});
|
||
|
||
// 如果tab栏不可见,强制更新位置
|
||
if (data.width === 0 || data.height === 0 || data.top < 0) {
|
||
console.warn('[FurnitureTopSales][onReady] tab栏不可见,强制更新位置');
|
||
this.updateNavbarPosition();
|
||
this.$forceUpdate();
|
||
}
|
||
}
|
||
}).exec();
|
||
}, 100);
|
||
// #endif
|
||
},
|
||
methods: {
|
||
// 计算并更新导航栏和内容区域的位置
|
||
updateNavbarPosition() {
|
||
console.log('[FurnitureTopSales][updateNavbarPosition] 开始更新导航栏位置');
|
||
const systemInfo = uni.getSystemInfoSync();
|
||
const statusBarHeight = systemInfo.statusBarHeight || 20; // 默认20px
|
||
const navbarHeight = 44; // navbar高度44px
|
||
// 转换为rpx:1px = 2rpx(在375px设计稿下)
|
||
const statusBarHeightRpx = statusBarHeight * 2;
|
||
const navbarHeightRpx = navbarHeight * 2;
|
||
const totalNavbarHeight = statusBarHeightRpx + navbarHeightRpx;
|
||
|
||
const navbarTop = totalNavbarHeight + 'rpx';
|
||
const contentTop = (totalNavbarHeight + 72) + 'rpx'; // tab栏高度72rpx
|
||
|
||
console.log('[FurnitureTopSales][updateNavbarPosition] 更新导航栏位置:', {
|
||
statusBarHeight: statusBarHeight,
|
||
navbarHeight: navbarHeight,
|
||
totalNavbarHeight: totalNavbarHeight,
|
||
navbarTop: navbarTop,
|
||
contentTop: contentTop,
|
||
currentNavbarTop: this.navbarTop,
|
||
currentContentTop: this.contentTop
|
||
});
|
||
|
||
// 强制更新,确保响应式数据变化被检测到
|
||
this.$set(this, 'navbarTop', navbarTop);
|
||
this.$set(this, 'contentTop', contentTop);
|
||
|
||
// 在微信小程序中,可能需要强制更新视图
|
||
// #ifdef MP-WEIXIN
|
||
this.$forceUpdate();
|
||
// #endif
|
||
},
|
||
goBack() {
|
||
// 检查页面栈,如果当前是第一个页面,则不能返回
|
||
const pages = getCurrentPages();
|
||
if (pages.length <= 1) {
|
||
// 如果是第一个页面,不执行返回操作
|
||
// 对于 tabBar 页面,通常不需要返回按钮,或者可以隐藏返回按钮
|
||
console.log('[FurnitureTopSales][goBack] 当前是第一个页面,无法返回');
|
||
return;
|
||
}
|
||
uni.navigateBack({
|
||
fail: (err) => {
|
||
console.error('[FurnitureTopSales][goBack] 返回失败:', err);
|
||
// 如果返回失败,可能是页面栈问题,忽略错误
|
||
}
|
||
});
|
||
},
|
||
switchTab(tab) {
|
||
this.activeTab = tab;
|
||
},
|
||
handleSwitchToTraining() {
|
||
// 处理从场景tab切换到陪练tab的事件
|
||
this.switchTab('training');
|
||
},
|
||
handleRefreshTrainingList() {
|
||
// 处理刷新陪练列表的事件
|
||
// 切换到陪练tab(如果不在的话)
|
||
this.switchTab('training');
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style>
|
||
page {
|
||
background-color: #F5F5F5;
|
||
}
|
||
|
||
.page {
|
||
min-height: 100vh;
|
||
background-color: #F5F5F5;
|
||
}
|
||
|
||
.content {
|
||
padding-top: 0; /* 移除padding-top,因为tab页和内容区域都使用绝对定位 */
|
||
margin-top: 0;
|
||
position: relative;
|
||
min-height: 100vh; /* 确保content有足够高度 */
|
||
}
|
||
|
||
/* 隐藏导航栏占位区域,避免产生空白 */
|
||
::v-deep .uni-navbar__placeholder {
|
||
display: none !important;
|
||
}
|
||
|
||
/* 标签页 */
|
||
.tabs {
|
||
display: flex !important; /* 强制显示 */
|
||
visibility: visible !important; /* 强制可见 */
|
||
opacity: 1 !important; /* 强制不透明 */
|
||
background-color: #FFFFFF;
|
||
border-bottom: 1px solid #E0E0E0;
|
||
padding: 0 16rpx;
|
||
margin-top: 0;
|
||
position: fixed; /* 改为fixed定位,确保始终可见 */
|
||
/* 导航栏高度:statusBar + navbar,动态计算 */
|
||
/* 使用内联样式动态设置top值 */
|
||
left: 0;
|
||
right: 0;
|
||
z-index: 999; /* 提高z-index确保在最上层 */
|
||
height: 72rpx; /* 明确设置标签页高度 */
|
||
box-sizing: border-box;
|
||
align-items: center;
|
||
/* 确保tab栏始终可见 */
|
||
will-change: top; /* 优化渲染性能 */
|
||
}
|
||
|
||
.tab-item {
|
||
padding: 24rpx 32rpx;
|
||
position: relative;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.tab-item text {
|
||
font-size: 30rpx;
|
||
color: #666;
|
||
}
|
||
|
||
.tab-item.active text {
|
||
color: #333;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.tab-item.active::after {
|
||
content: '';
|
||
position: absolute;
|
||
bottom: 0;
|
||
left: 32rpx;
|
||
right: 32rpx;
|
||
height: 4rpx;
|
||
background-color: #007AFF;
|
||
border-radius: 2rpx;
|
||
}
|
||
|
||
.champion-card {
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
background-color: #FFFFFF;
|
||
border-radius: 16rpx;
|
||
padding: 32rpx;
|
||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
min-height: 400rpx;
|
||
}
|
||
|
||
.champion-title {
|
||
font-size: 36rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.champion-desc {
|
||
font-size: 28rpx;
|
||
color: #999;
|
||
}
|
||
|
||
/* tab内容容器 */
|
||
.tab-content-container {
|
||
/* 使用绝对定位,从tab页底部开始,到底部导航栏结束 */
|
||
position: absolute;
|
||
/* top值通过内联样式动态设置 */
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 96rpx; /* 底部导航栏高度 */
|
||
overflow-y: auto;
|
||
background-color: #F5F5F5;
|
||
}
|
||
|
||
.tab-content {
|
||
padding: 24rpx 16rpx;
|
||
box-sizing: border-box;
|
||
}
|
||
</style> |