From 8d85ed54b212fe37a2b38779dfb8a480a47b8a0f Mon Sep 17 00:00:00 2001 From: xcbyy <2578898740@qq.com> Date: Tue, 13 Oct 2026 08:35:47 +0800 Subject: [PATCH] feat(protocol): add dedicated full-text legal protocol page with smooth scroll view and link from login and user center --- pwuniapp/pages.json | 9 + pwuniapp/pages/auth/login.vue | 30 +- pwuniapp/pages/common/protocol.vue | 403 ++++++++++++++++++++++++++ pwuniapp/pages/user/index.vue | 32 +- server/tests/content-security.spec.ts | 41 ++- 5 files changed, 492 insertions(+), 23 deletions(-) create mode 100644 pwuniapp/pages/common/protocol.vue diff --git a/pwuniapp/pages.json b/pwuniapp/pages.json index 5da08f2..3b77df7 100644 --- a/pwuniapp/pages.json +++ b/pwuniapp/pages.json @@ -28,6 +28,15 @@ "backgroundColor": "#0A0F1D" } }, + { + "path": "pages/common/protocol", + "style": { + "navigationBarTitleText": "服务协议与隐私政策", + "navigationBarBackgroundColor": "#0A0F1D", + "navigationBarTextStyle": "white", + "backgroundColor": "#0A0F1D" + } + }, { "path": "pages/activity/list", "style": { diff --git a/pwuniapp/pages/auth/login.vue b/pwuniapp/pages/auth/login.vue index 8ff274d..bdf2361 100644 --- a/pwuniapp/pages/auth/login.vue +++ b/pwuniapp/pages/auth/login.vue @@ -249,20 +249,30 @@ const loginWithDemo = (acc) => { }; const openUserAgreement = () => { - uni.showModal({ - title: platformProtocol.value.userAgreementTitle || '用户服务协议', - content: platformProtocol.value.userAgreementContent, - showCancel: false, - confirmText: '我知道了' + uni.navigateTo({ + url: '/pages/common/protocol?type=service', + fail: () => { + uni.showModal({ + title: platformProtocol.value.userAgreementTitle || '用户服务协议', + content: platformProtocol.value.userAgreementContent, + showCancel: false, + confirmText: '我知道了' + }); + } }); }; const openPrivacyAgreement = () => { - uni.showModal({ - title: platformProtocol.value.privacyPolicyTitle || '隐私保护指引', - content: platformProtocol.value.privacyPolicyContent, - showCancel: false, - confirmText: '我知道了' + uni.navigateTo({ + url: '/pages/common/protocol?type=privacy', + fail: () => { + uni.showModal({ + title: platformProtocol.value.privacyPolicyTitle || '隐私保护指引', + content: platformProtocol.value.privacyPolicyContent, + showCancel: false, + confirmText: '我知道了' + }); + } }); }; diff --git a/pwuniapp/pages/common/protocol.vue b/pwuniapp/pages/common/protocol.vue new file mode 100644 index 0000000..93d5b49 --- /dev/null +++ b/pwuniapp/pages/common/protocol.vue @@ -0,0 +1,403 @@ + + + + + diff --git a/pwuniapp/pages/user/index.vue b/pwuniapp/pages/user/index.vue index a9a5257..2fb1d00 100644 --- a/pwuniapp/pages/user/index.vue +++ b/pwuniapp/pages/user/index.vue @@ -816,18 +816,26 @@ const openFaq = () => { }; const openAbout = () => { - const proto = platformConfigs.value.platform_protocol || {}; - if (privacyModalRef.value && privacyModalRef.value.show) { - privacyModalRef.value.show(); - } else { - const fullText = (proto.userAgreementContent ? `【用户服务协议】\n${proto.userAgreementContent}\n\n` : '') + - (proto.privacyPolicyContent ? `【隐私保护指引】\n${proto.privacyPolicyContent}` : (proto.content || proto.summary || '严禁发布任何违规言论及低俗诈骗行为,平台全面遵循微信内容安全风控与用户隐私保护标准。')); - uni.showModal({ - title: proto.title || '用户服务协议与隐私规范', - content: fullText, - showCancel: false - }); - } + 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 + }); + } + }); + } + }); }; const navTo = (url) => { diff --git a/server/tests/content-security.spec.ts b/server/tests/content-security.spec.ts index ed91613..c77c632 100644 --- a/server/tests/content-security.spec.ts +++ b/server/tests/content-security.spec.ts @@ -130,7 +130,7 @@ describe('ContentSecurityService & Compliance Architecture (TDD 微信内容安 const raw = fs.readFileSync(pagesJsonPath, 'utf-8'); const config = JSON.parse(raw); expect(Array.isArray(config.pages)).toBe(true); - expect(config.pages.length).toBe(15); + expect(config.pages.length).toBe(16); for (const pageItem of config.pages) { const vueFilePath = path.join(rootDir, 'pwuniapp', `${pageItem.path}.vue`); @@ -483,6 +483,45 @@ describe('ContentSecurityService & Compliance Architecture (TDD 微信内容安 expect(content).toContain('white-space: nowrap'); }); }); + + describe('13. 专属独立法律协议与隐私政策长文阅读页面验证 (pages/common/protocol.vue)', () => { + it('pages.json 必须成功注册 pages/common/protocol 且物理文件必须存在', () => { + const protoPath = path.join(rootDir, 'pwuniapp', 'pages', 'common', 'protocol.vue'); + expect(fs.existsSync(protoPath)).toBe(true); + const pagesJsonPath = path.join(rootDir, 'pwuniapp', 'pages.json'); + const pagesJson = fs.readFileSync(pagesJsonPath, 'utf-8'); + expect(pagesJson).toContain('pages/common/protocol'); + expect(pagesJson).toContain('服务协议与隐私政策'); + }); + + it('协议页面必须支持动态章节结构化拆分、长文上下滚动与双主题适配', () => { + const protoPath = path.join(rootDir, 'pwuniapp', 'pages', 'common', 'protocol.vue'); + const content = fs.readFileSync(protoPath, 'utf-8'); + expect(content).toContain('scroll-view'); + expect(content).toContain('formattedSections'); + expect(content).toContain('getPlatformConfigs'); + expect(content).toContain('applyThemeToNativeUI'); + expect(content).toContain('theme-warm'); + }); + + it('登录页与个人中心必须实现对专属协议页面的原生导航跳转', () => { + const loginPath = path.join(rootDir, 'pwuniapp', 'pages', 'auth', 'login.vue'); + const loginContent = fs.readFileSync(loginPath, 'utf-8'); + expect(loginContent).toContain('/pages/common/protocol?type=service'); + expect(loginContent).toContain('/pages/common/protocol?type=privacy'); + + const userPath = path.join(rootDir, 'pwuniapp', 'pages', 'user', 'index.vue'); + const userContent = fs.readFileSync(userPath, 'utf-8'); + expect(userContent).toContain('/pages/common/protocol?type='); + }); + + it('协议阅读页面代码严禁包含任何系统 Emoji', () => { + const protoPath = path.join(rootDir, 'pwuniapp', 'pages', 'common', 'protocol.vue'); + const content = fs.readFileSync(protoPath, 'utf-8'); + const emojiRegex = /(?:\p{Extended_Pictographic}|\p{Emoji_Presentation}|[\u2600-\u27BF])/u; + expect(emojiRegex.test(content)).toBe(false); + }); + }); });