feat(mini): 平台规范与服务协议页面化(页内切换,替代弹窗)

- 个人中心'平台规范与服务协议'点击直接进入 /pages/common/protocol(不再 showActionSheet 选择)

- 协议页新增页内切换:用户服务协议 / 隐私保护指引(含暖阳主题适配)

- 新增回归护栏 protocol-page.spec.ts(76 套件/601 用例全绿)
这个提交包含在:
xcbyy
2026-10-17 18:04:33 +08:00
父节点 2599dce315
当前提交 e719de59a2
共有 3 个文件被更改,包括 106 次插入 和 17 次删除

查看文件

@@ -7,6 +7,24 @@
<text class="protocol-meta">生效版本:v2.4.0 · 适用全平台所有注册与履约用户</text>
</view>
<!-- 协议切换(用户服务协议 / 隐私保护指引) -->
<view class="protocol-tabs">
<view
class="ptab"
:class="{ active: protocolType === 'service' }"
@click="switchType('service')"
>
用户服务协议
</view>
<view
class="ptab"
:class="{ active: protocolType === 'privacy' }"
@click="switchType('privacy')"
>
隐私保护指引
</view>
</view>
<!-- 协议条款滚动主体区域 -->
<scroll-view class="protocol-scroll-body" scroll-y enable-flex>
<view class="protocol-card">
@@ -139,6 +157,15 @@ onLoad((options) => {
});
});
// 页内切换协议类型(无需弹窗选择)
const switchType = (t) => {
if (protocolType.value === t) return;
protocolType.value = t;
uni.setNavigationBarTitle({
title: t === 'privacy' ? '隐私保护指引' : '用户服务协议'
});
};
onMounted(() => {
currentThemeKey.value = getCurrentThemeKey();
applyThemeToNativeUI(currentThemeKey.value);
@@ -201,6 +228,32 @@ const handleBack = () => {
}
}
/* 协议类型切换 */
.protocol-tabs {
display: flex;
gap: 12rpx;
padding: 20rpx 32rpx 0;
background: #10172A;
.ptab {
flex: 1;
text-align: center;
padding: 16rpx 0;
font-size: 26rpx;
color: #94A3B8;
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.06);
border-radius: 14rpx;
&.active {
color: #0A0F1D;
background: #10B981;
border-color: #10B981;
font-weight: 700;
}
}
}
/* 协议主体滚动区 */
.protocol-scroll-body {
flex: 1;
@@ -349,6 +402,22 @@ const handleBack = () => {
}
}
.protocol-tabs {
background: #FFFFFF;
.ptab {
color: #78716C;
background: #F5F5F4;
border-color: #E7E5E4;
&.active {
color: #FFFFFF;
background: #EA580C;
border-color: #EA580C;
}
}
}
.protocol-scroll-body .protocol-card {
background: #FFFFFF;
border-color: #E7E5E4;

查看文件

@@ -1189,23 +1189,15 @@ const openFaq = () => {
};
const openAbout = () => {
uni.showActionSheet({
itemList: ['用户服务协议', '隐私保护指引'],
success: (res) => {
const type = res.tapIndex === 1 ? 'privacy' : 'service';
uni.navigateTo({
url: `/pages/common/protocol?type=${type}`,
fail: () => {
const proto = platformConfigs.value.platform_protocol || {};
const fullText = type === 'privacy'
? (proto.privacyPolicyContent || '平台全面遵守微信小程序开发者隐私规范要求,对用户数据实施高强度加密保护。')
: (proto.userAgreementContent || '平台遵循合法合规与安全原则,所有同城活动履约均须在公开透明街区展开。');
uni.showModal({
title: type === 'privacy' ? (proto.privacyPolicyTitle || '隐私保护指引') : (proto.userAgreementTitle || '用户服务协议'),
content: fullText,
showCancel: false
});
}
// 进入独立协议页面(页内可切换"用户服务协议 / 隐私保护指引"),不再使用弹窗选择
uni.navigateTo({
url: '/pages/common/protocol?type=service',
fail: () => {
const proto = platformConfigs.value.platform_protocol || {};
uni.showModal({
title: proto.userAgreementTitle || '用户服务协议',
content: proto.userAgreementContent || '平台遵循合法合规与安全原则,所有同城活动履约均须在公开透明街区展开。',
showCancel: false
});
}
});

查看文件

@@ -0,0 +1,28 @@
// 已读取AGENTS.md
import * as fs from 'fs';
import * as path from 'path';
/**
* 第六十批回归闸门:平台规范与服务协议页面化(页内切换,非弹窗)
*/
describe('第六十批服务协议页面化闸门', () => {
const rootDir = path.resolve(__dirname, '../../');
const protocol = fs.readFileSync(path.join(rootDir, 'pwuniapp/pages/common/protocol.vue'), 'utf-8');
const userIndex = fs.readFileSync(path.join(rootDir, 'pwuniapp/pages/user/index.vue'), 'utf-8');
it('协议页支持页内切换服务协议/隐私政策', () => {
expect(protocol).toContain('protocol-tabs');
expect(protocol).toContain('const switchType =');
expect(protocol).toContain("switchType('privacy')");
expect(protocol).toContain("switchType('service')");
});
it('个人中心直接进入协议页,不再弹窗选择', () => {
const start = userIndex.indexOf('const openAbout');
expect(start).toBeGreaterThan(-1);
const end = userIndex.indexOf('};', start);
const block = userIndex.slice(start, end + 2);
expect(block).toContain("url: '/pages/common/protocol?type=service'");
expect(block).not.toContain('showActionSheet');
});
});