fix(order-list): add immersive custom navigation bar and return button with dual-theme adaptation

这个提交包含在:
xcbyy
2026-10-13 15:00:12 +08:00
父节点 e0236d1489
当前提交 63b18512f6
共有 2 个文件被更改,包括 160 次插入 和 15 次删除

查看文件

@@ -1,21 +1,37 @@
<template>
<view class="order-list-container" :class="{ 'theme-warm': currentThemeKey === 'warm' }" :style="navStyle">
<!-- 顶部状态分类 Tab -->
<view class="tab-header">
<view
v-for="tab in tabs"
:key="tab.key"
class="tab-item btn-active"
:class="{ active: currentTab === tab.key }"
@click="switchTab(tab.key)"
>
<text class="tab-text">{{ tab.name }}</text>
<view class="tab-indicator" v-if="currentTab === tab.key"></view>
<!-- 顶部沉浸式自定义导航栏 -->
<view class="custom-nav-bar">
<view class="nav-content">
<view class="btn-nav-back btn-active" @click="handleBack">
<image class="nav-back-icon" :src="ICONS.chevronLeft" mode="aspectFit" />
</view>
<view class="nav-title-wrap">
<text class="nav-title">我的订单</text>
</view>
<!-- 胶囊右侧对称避让占位 -->
<view class="nav-capsule-placeholder"></view>
</view>
</view>
<!-- 订单列表 -->
<view class="orders-wrap">
<!-- 主体内容区域 -->
<view class="order-content-body">
<!-- 顶部状态分类 Tab -->
<view class="tab-header">
<view
v-for="tab in tabs"
:key="tab.key"
class="tab-item btn-active"
:class="{ active: currentTab === tab.key }"
@click="switchTab(tab.key)"
>
<text class="tab-text">{{ tab.name }}</text>
<view class="tab-indicator" v-if="currentTab === tab.key"></view>
</view>
</view>
<!-- 订单列表 -->
<view class="orders-wrap">
<view
v-for="order in filteredOrders"
:key="order.id"
@@ -91,6 +107,7 @@
<button class="empty-btn btn-active" @click="goToExplore">前往发现大厅寻找大神</button>
</view>
</view>
</view>
<!-- 评价大神服务体验交互弹窗 -->
<view class="review-modal-mask" v-if="reviewModalVisible" @click.self="closeReviewModal">
@@ -350,10 +367,23 @@ let countdownTimer = null;
const navStyle = computed(() => {
return {
'--safe-header-gap': `${navInfo.value.totalHeaderHeight}px`
'--safe-header-gap': `${navInfo.value.safeHeaderGap}px`,
'--capsule-right-space': `${navInfo.value.capsuleRightSpace}px`,
'--capsule-height': `${navInfo.value.capsuleHeight}px`
};
});
const handleBack = () => {
const pages = getCurrentPages();
if (pages && pages.length > 1) {
uni.navigateBack();
} else {
uni.switchTab({
url: '/pages/index/index'
});
}
};
const onThemeChanged = (key) => {
currentThemeKey.value = key;
applyThemeToNativeUI(key);
@@ -806,7 +836,70 @@ const submitReview = async () => {
.order-list-container {
min-height: 100vh;
background-color: #0A0F1D;
padding: var(--safe-header-gap, 36rpx) 32rpx calc(160rpx + env(safe-area-inset-bottom));
display: flex;
flex-direction: column;
box-sizing: border-box;
}
/* 顶部沉浸式自定义顶栏 */
.custom-nav-bar {
position: sticky;
top: 0;
z-index: 100;
padding-top: var(--safe-header-gap, 44px);
background: rgba(10, 15, 29, 0.92);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
.nav-content {
position: relative;
height: var(--capsule-height, 44px);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24rpx;
.btn-nav-back {
width: 64rpx;
height: 64rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.12);
.nav-back-icon {
width: 36rpx;
height: 36rpx;
}
}
.nav-title-wrap {
position: absolute;
left: 50%;
transform: translateX(-50%);
text-align: center;
.nav-title {
font-size: 32rpx;
font-weight: 700;
color: #F8FAFC;
letter-spacing: 1rpx;
}
}
.nav-capsule-placeholder {
width: var(--capsule-right-space, 96px);
flex-shrink: 0;
}
}
}
.order-content-body {
padding: 24rpx 32rpx calc(160rpx + env(safe-area-inset-bottom));
flex: 1;
}
.tab-header {
@@ -1133,6 +1226,22 @@ const submitReview = async () => {
.order-list-container.theme-warm {
background-color: #FDFBF7;
.custom-nav-bar {
background: rgba(255, 253, 249, 0.95);
border-bottom: 1px solid rgba(234, 88, 12, 0.1);
.nav-content {
.btn-nav-back {
background: #FFF7ED;
border-color: #FED7AA;
}
.nav-title-wrap .nav-title {
color: #1C1917;
}
}
}
.tab-header {
background-color: #FFFFFF;
border-color: #F0ECE4;

查看文件

@@ -836,6 +836,42 @@ describe('ContentSecurityService & Compliance Architecture (TDD 微信内容安
expect(emojiRegex.test(fs.readFileSync(serverIndexPath, 'utf-8'))).toBe(false);
});
});
describe('19. 订单列表页面沉浸式导航栏与返回按钮规范验证 (pages/order/list.vue)', () => {
const orderListPath = path.join(rootDir, 'pwuniapp', 'pages', 'order', 'list.vue');
it('订单列表页面必须包含 custom-nav-bar 沉浸式导航栏与返回上一级按钮', () => {
expect(fs.existsSync(orderListPath)).toBe(true);
const content = fs.readFileSync(orderListPath, 'utf-8');
expect(content).toContain('custom-nav-bar');
expect(content).toContain('btn-nav-back');
expect(content).toContain('handleBack');
expect(content).toContain('nav-title');
expect(content).toContain('我的订单');
expect(content).toContain('nav-capsule-placeholder');
});
it('必须包含智能返回上一级与首页兜底路由控制逻辑', () => {
const content = fs.readFileSync(orderListPath, 'utf-8');
expect(content).toContain('getCurrentPages()');
expect(content).toContain('uni.navigateBack()');
expect(content).toContain('uni.switchTab');
});
it('必须适配微信小程序胶囊安全避让变量与冷暖双主题', () => {
const content = fs.readFileSync(orderListPath, 'utf-8');
expect(content).toContain('--safe-header-gap');
expect(content).toContain('--capsule-right-space');
expect(content).toContain('--capsule-height');
expect(content).toContain('.order-list-container.theme-warm');
});
it('订单列表页面源码与测试严禁包含任何系统 Emoji 违规字符', () => {
const content = fs.readFileSync(orderListPath, 'utf-8');
const emojiRegex = /(?:\p{Extended_Pictographic}|\p{Emoji_Presentation}|[\u2600-\u27BF])/u;
expect(emojiRegex.test(content)).toBe(false);
});
});
});