- 个人中心'平台规范与服务协议'点击直接进入 /pages/common/protocol(不再 showActionSheet 选择) - 协议页新增页内切换:用户服务协议 / 隐私保护指引(含暖阳主题适配) - 新增回归护栏 protocol-page.spec.ts(76 套件/601 用例全绿)
473 行
13 KiB
Vue
473 行
13 KiB
Vue
<template>
|
||
<view class="protocol-page" :class="{ 'theme-warm': currentThemeKey === 'warm' }">
|
||
<!-- 顶部状态栏与标题区 -->
|
||
<view class="protocol-header">
|
||
<view class="protocol-badge">合规法律条款</view>
|
||
<text class="protocol-title">{{ pageTitle }}</text>
|
||
<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">
|
||
<!-- 协议概要导言卡片 -->
|
||
<view class="intro-box">
|
||
<text class="intro-label">重要提示与导言:</text>
|
||
<text class="intro-text">{{ pageSummary }}</text>
|
||
</view>
|
||
|
||
<!-- 条款章节列表 -->
|
||
<view class="sections-list">
|
||
<view
|
||
v-for="(sec, idx) in formattedSections"
|
||
:key="idx"
|
||
class="section-item"
|
||
>
|
||
<view class="section-head" v-if="sec.title">
|
||
<view class="section-indicator"></view>
|
||
<text class="section-title">{{ sec.title }}</text>
|
||
</view>
|
||
<text class="section-body">{{ sec.body }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 法律声明与签署提示 -->
|
||
<view class="legal-notice">
|
||
<text class="notice-title">法律合规声明:</text>
|
||
<text class="notice-desc">
|
||
平台全面遵守国家网信办相关规定及微信小程序平台运营规范。平台依法对同城履约全链路提供合规安全守护、技术加密保障与7x24小时资金争议仲裁。如您对本协议内容有任何异议,请停止使用平台相关服务。
|
||
</text>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
|
||
<!-- 底部操作与确认栏 -->
|
||
<view class="protocol-bottom-bar">
|
||
<button class="btn-confirm-read btn-active" @click="handleBack">
|
||
我已阅读并知悉
|
||
</button>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed, onMounted } from 'vue';
|
||
import { onLoad, onShow } from '@dcloudio/uni-app';
|
||
import { getCurrentThemeKey, applyThemeToNativeUI } from '@/common/theme.js';
|
||
import { getPlatformConfigs } from '@/common/api.js';
|
||
|
||
const currentThemeKey = ref(getCurrentThemeKey());
|
||
const protocolType = ref('service'); // 'service' | 'privacy'
|
||
|
||
const protocolData = ref({
|
||
userAgreementTitle: '用户服务协议',
|
||
userAgreementContent: '1. 账号使用规范:用户注册与使用同城生活陪伴服务须符合国家法律法规,严禁利用平台从事任何违法违规行为。\n2. 资金与交易保障:平台提供全额担保交易,订单完成前款项由官方账户托管,碰面出示4位核销码确认后自动结算至大神钱包。\n3. 履约安全守则:所有同城活动须在合规公开商业街区进行,严禁私下导流与脱离平台交易。\n4. 争议与售后仲裁:若遇服务不符或搭子违约,可随时在订单中发起申诉,由平台运营仲裁专员裁决。',
|
||
privacyPolicyTitle: '隐私保护指引',
|
||
privacyPolicyContent: '1. 信息收集范围:为保障同城陪玩交易安全与现场4位核销码核验,平台在获得您授权后会依法收集手机号与必要位置信息。\n2. 数据加密保护:平台全面接入微信小程序官方安全风控规范,所有用户数据均实施高强度加密脱敏存储。\n3. 权限撤回与管理:您可随时在小程序系统设置中撤回位置或通讯授权,平台未经您的明确许可绝不会向任何第三方泄露您的隐私数据。',
|
||
summary: '严禁发布任何违规言论及不良欺诈行为,平台全面遵循微信内容安全风控与用户隐私保护标准。'
|
||
});
|
||
|
||
const pageTitle = computed(() => {
|
||
if (protocolType.value === 'privacy') {
|
||
return protocolData.value.privacyPolicyTitle || '隐私保护指引';
|
||
}
|
||
return protocolData.value.userAgreementTitle || '用户服务协议';
|
||
});
|
||
|
||
const pageSummary = computed(() => {
|
||
return protocolData.value.summary || '平台全面遵循微信官方内容安全风控与用户隐私保护标准。';
|
||
});
|
||
|
||
const rawContent = computed(() => {
|
||
if (protocolType.value === 'privacy') {
|
||
return protocolData.value.privacyPolicyContent || '';
|
||
}
|
||
return protocolData.value.userAgreementContent || '';
|
||
});
|
||
|
||
// 将纯文本条款拆分为结构化章节
|
||
const formattedSections = computed(() => {
|
||
const content = rawContent.value;
|
||
if (!content) return [];
|
||
|
||
const lines = content.split('\n').map(l => l.trim()).filter(Boolean);
|
||
const sections = [];
|
||
|
||
lines.forEach((line) => {
|
||
// 匹配如 1. 2. 或者 【】 格式的标题
|
||
const match = line.match(/^(\d+[\.\、]|【.+?】)(.+)/);
|
||
if (match) {
|
||
sections.push({
|
||
title: line.substring(0, line.indexOf(':') > -1 ? line.indexOf(':') + 1 : 12),
|
||
body: line.substring(line.indexOf(':') > -1 ? line.indexOf(':') + 1 : 12)
|
||
});
|
||
} else {
|
||
sections.push({
|
||
title: '',
|
||
body: line
|
||
});
|
||
}
|
||
});
|
||
|
||
return sections.length > 0 ? sections : [{ title: '条款内容', body: content }];
|
||
});
|
||
|
||
const loadConfig = async () => {
|
||
try {
|
||
const res = await getPlatformConfigs();
|
||
if (res && res.platform_protocol) {
|
||
const p = res.platform_protocol;
|
||
if (typeof p === 'object') {
|
||
if (p.userAgreementTitle) protocolData.value.userAgreementTitle = p.userAgreementTitle;
|
||
if (p.userAgreementContent) protocolData.value.userAgreementContent = p.userAgreementContent;
|
||
if (p.privacyPolicyTitle) protocolData.value.privacyPolicyTitle = p.privacyPolicyTitle;
|
||
if (p.privacyPolicyContent) protocolData.value.privacyPolicyContent = p.privacyPolicyContent;
|
||
if (p.summary) protocolData.value.summary = p.summary;
|
||
}
|
||
}
|
||
} catch (e) {
|
||
console.warn('[Protocol] 加载系统配置失败,降级展示默认条款:', e);
|
||
}
|
||
};
|
||
|
||
onLoad((options) => {
|
||
if (options && options.type) {
|
||
protocolType.value = options.type;
|
||
}
|
||
uni.setNavigationBarTitle({
|
||
title: protocolType.value === 'privacy' ? '隐私保护指引' : '用户服务协议'
|
||
});
|
||
});
|
||
|
||
// 页内切换协议类型(无需弹窗选择)
|
||
const switchType = (t) => {
|
||
if (protocolType.value === t) return;
|
||
protocolType.value = t;
|
||
uni.setNavigationBarTitle({
|
||
title: t === 'privacy' ? '隐私保护指引' : '用户服务协议'
|
||
});
|
||
};
|
||
|
||
onMounted(() => {
|
||
currentThemeKey.value = getCurrentThemeKey();
|
||
applyThemeToNativeUI(currentThemeKey.value);
|
||
loadConfig();
|
||
});
|
||
|
||
onShow(() => {
|
||
currentThemeKey.value = getCurrentThemeKey();
|
||
applyThemeToNativeUI(currentThemeKey.value);
|
||
});
|
||
|
||
const handleBack = () => {
|
||
uni.navigateBack({
|
||
fail: () => {
|
||
uni.switchTab({ url: '/pages/index/index' });
|
||
}
|
||
});
|
||
};
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
.protocol-page {
|
||
min-height: 100vh;
|
||
background-color: #0A0F1D;
|
||
display: flex;
|
||
flex-direction: column;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
/* 顶部标题区 */
|
||
.protocol-header {
|
||
padding: 40rpx 36rpx 24rpx;
|
||
background: #10172A;
|
||
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
|
||
|
||
.protocol-badge {
|
||
display: inline-block;
|
||
font-size: 20rpx;
|
||
font-weight: 600;
|
||
color: #10B981;
|
||
background: rgba(16, 185, 129, 0.15);
|
||
padding: 4rpx 16rpx;
|
||
border-radius: 8rpx;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.protocol-title {
|
||
display: block;
|
||
font-size: 38rpx;
|
||
font-weight: 800;
|
||
color: #F8FAFC;
|
||
letter-spacing: 1rpx;
|
||
margin-bottom: 10rpx;
|
||
}
|
||
|
||
.protocol-meta {
|
||
display: block;
|
||
font-size: 22rpx;
|
||
color: #64748B;
|
||
}
|
||
}
|
||
|
||
/* 协议类型切换 */
|
||
.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;
|
||
padding: 28rpx 32rpx calc(140rpx + env(safe-area-inset-bottom));
|
||
box-sizing: border-box;
|
||
|
||
.protocol-card {
|
||
background: #151D30;
|
||
border-radius: 24rpx;
|
||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||
padding: 32rpx;
|
||
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.3);
|
||
|
||
.intro-box {
|
||
background: rgba(99, 102, 241, 0.08);
|
||
border-left: 6rpx solid #6366F1;
|
||
padding: 20rpx 24rpx;
|
||
border-radius: 12rpx;
|
||
margin-bottom: 36rpx;
|
||
|
||
.intro-label {
|
||
font-size: 24rpx;
|
||
font-weight: 700;
|
||
color: #818CF8;
|
||
display: block;
|
||
margin-bottom: 6rpx;
|
||
}
|
||
.intro-text {
|
||
font-size: 24rpx;
|
||
color: #CBD5E1;
|
||
line-height: 1.6;
|
||
}
|
||
}
|
||
|
||
.sections-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 32rpx;
|
||
margin-bottom: 36rpx;
|
||
|
||
.section-item {
|
||
padding-bottom: 24rpx;
|
||
border-bottom: 1px solid rgba(255, 255, 255, 0.04);
|
||
|
||
&:last-child {
|
||
border-bottom: none;
|
||
padding-bottom: 0;
|
||
}
|
||
|
||
.section-head {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12rpx;
|
||
margin-bottom: 12rpx;
|
||
|
||
.section-indicator {
|
||
width: 8rpx;
|
||
height: 24rpx;
|
||
border-radius: 4rpx;
|
||
background: #10B981;
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 28rpx;
|
||
font-weight: 700;
|
||
color: #F8FAFC;
|
||
}
|
||
}
|
||
|
||
.section-body {
|
||
font-size: 26rpx;
|
||
color: #94A3B8;
|
||
line-height: 1.7;
|
||
display: block;
|
||
}
|
||
}
|
||
}
|
||
|
||
.legal-notice {
|
||
background: rgba(255, 255, 255, 0.02);
|
||
border-radius: 16rpx;
|
||
padding: 24rpx;
|
||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||
|
||
.notice-title {
|
||
font-size: 22rpx;
|
||
font-weight: 700;
|
||
color: #64748B;
|
||
display: block;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
.notice-desc {
|
||
font-size: 22rpx;
|
||
color: #64748B;
|
||
line-height: 1.6;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 底部操作栏 */
|
||
.protocol-bottom-bar {
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
padding: 20rpx 36rpx calc(24rpx + env(safe-area-inset-bottom));
|
||
background: rgba(10, 15, 29, 0.95);
|
||
backdrop-filter: blur(20px);
|
||
border-top: 1px solid rgba(255, 255, 255, 0.08);
|
||
box-sizing: border-box;
|
||
z-index: 99;
|
||
|
||
.btn-confirm-read {
|
||
width: 100%;
|
||
height: 84rpx;
|
||
border-radius: 42rpx;
|
||
background: #07C160;
|
||
color: #FFFFFF;
|
||
font-size: 28rpx;
|
||
font-weight: 700;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border: none;
|
||
|
||
&::after {
|
||
border: none;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 浅色暖阳模式适配 */
|
||
.theme-warm {
|
||
background-color: #FAF8F5;
|
||
|
||
.protocol-header {
|
||
background: #FFFFFF;
|
||
border-bottom-color: #E7E5E4;
|
||
|
||
.protocol-title {
|
||
color: #1C1917;
|
||
}
|
||
.protocol-meta {
|
||
color: #78716C;
|
||
}
|
||
}
|
||
|
||
.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;
|
||
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.04);
|
||
|
||
.intro-box {
|
||
background: #FFF7ED;
|
||
border-left-color: #EA580C;
|
||
|
||
.intro-label {
|
||
color: #EA580C;
|
||
}
|
||
.intro-text {
|
||
color: #78716C;
|
||
}
|
||
}
|
||
|
||
.sections-list .section-item {
|
||
border-bottom-color: #F5F5F4;
|
||
|
||
.section-head {
|
||
.section-indicator {
|
||
background: #EA580C;
|
||
}
|
||
.section-title {
|
||
color: #1C1917;
|
||
}
|
||
}
|
||
.section-body {
|
||
color: #57534E;
|
||
}
|
||
}
|
||
|
||
.legal-notice {
|
||
background: #FDFBF7;
|
||
border-color: #E7E5E4;
|
||
|
||
.notice-title {
|
||
color: #A8A29E;
|
||
}
|
||
.notice-desc {
|
||
color: #A8A29E;
|
||
}
|
||
}
|
||
}
|
||
|
||
.protocol-bottom-bar {
|
||
background: rgba(250, 248, 245, 0.95);
|
||
border-top-color: #E7E5E4;
|
||
}
|
||
}
|
||
</style>
|