
管理员
站长
- UID
- 1
- 积分
- 224394
- 余额
- 13 R
- Moe币
- 84875
- 在线时间
- 279 小时
- 注册时间
- 2025-12-28
- 最后登录
- 2026-11-3
|
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { getTeenPublicInfo } from '../../api/user'
import { getToken } from '../../utils/auth'
const router = useRouter()
const loading = ref(true)
const info = ref<any>(null)
const isLoggedIn = ref(!!getToken())
async function fetchInfo() {
loading.value = true
try {
const res = await getTeenPublicInfo()
info.value = res.data
} catch {
/* 加载失败时展示兜底静态说明 */
} finally {
loading.value = false
}
}
function goSetting() {
if (!isLoggedIn.value) {
router.push('/login')
return
}
router.push('/profile/teen')
}
/**
* 保护措施详细说明,与「未成年人保护」设置页保持同一口径。
* 逐项写明生效范围与具体规则,供用户知悉及监管核验。
*/
const protectionDetails = [
{
name: '一、内容适龄过滤',
items: [
'全站作品均标注适龄分级:全年龄、12+、16+、18+ 四档。',
'开启后首页推荐、最近更新、本周最热、排行榜、分类浏览、搜索结果、相关推荐等全部入口,仅展示「全年龄」作品。',
'非全年龄作品即使通过链接直接访问详情页或阅读页,服务端也会拒绝返回内容。',
],
},
{
name: '二、使用时长管理',
items: [
'可设置每日累计使用上限,支持 15 / 30 / 45 / 60 / 90 / 120 分钟六档。',
'页面右下角常驻显示剩余可用时间倒计时。',
'达到上限后全站页面自动锁定,须监护人输入密码方可延长,每日最多延长 2 次。',
'使用时长由服务端记录,清除浏览器缓存或更换设备不会重置当日已用时长。',
],
},
{
name: '三、夜间禁用时段',
items: [
'可自定义夜间禁用(宵禁)起止时间,默认 22:00 至次日 06:00。',
'该时段内进入网站将直接锁定页面,不提供任何浏览与阅读入口。',
'支持跨零点时段设置。',
],
},
{
name: '四、消费功能全面关闭',
items: [
'禁止金币充值、开通 VIP 会员、购买付费章节、投月票打赏等全部消费行为。',
'拦截在页面与服务端接口两层生效,绕过前端直接调用接口同样被拒绝。',
'未成年人无法自行关闭本限制,须监护人验证密码后方可解除。',
],
},
{
name: '五、互动行为限制',
items: [
'不可发表评论与回复,降低接触不良言论及陌生人不当互动的风险。',
'评论区会明确提示当前处于未成年人保护模式,原有评论仍可正常阅读。',
],
},
{
name: '六、健康用眼提醒',
items: [
'连续浏览每满 30 分钟弹出休息提醒,引导远眺放松、起身活动、补充水分。',
'页面切换到后台或最小化时不计入连续使用时长。',
],
},
{
name: '七、家长监护与报告',
items: [
'开启、修改设置、延长时长、退出模式均需输入 4 位监护密码。',
'监护密码采用 BCrypt 加密存储,不以明文形式保存或展示。',
'监护人可凭密码查看最近 7 / 14 / 30 天使用报告:每日使用时长、日均时长、超限天数、被拦截次数。',
],
},
{
name: '八、不良内容举报',
items: [
'作品、章节、评论均提供「举报」入口,未登录访客也可提交举报。',
'举报原因涵盖色情低俗、暴力血腥、涉政违规、人身攻击、危害未成年人等类别。',
'举报由管理后台专人处理,处理进度可在「我的举报」中查看。',
],
},
]
/** 法规依据 */
const legalBasis = [
'《中华人民共和国未成年人保护法》「网络保护」专章',
'《未成年人网络保护条例》',
'《网络信息内容生态治理规定》',
]
onMounted(fetchInfo)
</script>
<template>
<div class="guide-page">
<div class="guide-container">
<div class="guide-hero">
<svg width="52" height="52" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6">
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />
<path d="M9 12l2 2 4-4" />
</svg>
<h1>{{ info?.title || '未成年人保护模式' }}</h1>
<p>
{{ info?.summary || '开启后将进入未成年人专属浏览环境:仅展示全年龄内容,限制使用时长与夜间使用,并关闭充值、会员开通、月票打赏等消费功能,需监护密码方可调整或退出。' }}
</p>
<button class="hero-btn" @click="goSetting">
{{ isLoggedIn ? '前往设置' : '登录后开启' }}
</button>
</div>
<el-skeleton v-if="loading" :rows="6" animated class="guide-skeleton" />
<template v-else>
<div class="guide-section">
<h2>保护措施详细说明</h2>
<p class="guide-intro">
开启「未成年人保护模式」后,以下八项措施同时生效。
其中内容过滤与消费限制在页面和服务端接口两层校验,无法通过复制链接或直接调用接口绕过。
</p>
<div class="protection-list">
<section v-for="f in protectionDetails" :key="f.name" class="protection-block">
<h3>{{ f.name }}</h3>
<ul>
<li v-for="(line, i) in f.items" :key="i">{{ line }}</li>
</ul>
</section>
</div>
</div>
<div class="guide-section" v-if="info?.howTo">
<h2>如何使用</h2>
<ul class="howto-list">
<li><strong>开启</strong>{{ info.howTo.enablePath }}</li>
<li><strong>调整或退出</strong>{{ info.howTo.closePath }}</li>
<li><strong>举报</strong>{{ info.howTo.reportPath }}</li>
</ul>
</div>
<div class="guide-section">
<h2>依据法规</h2>
<ul class="legal-list">
<li v-for="l in legalBasis" :key="l">{{ l }}</li>
</ul>
</div>
<div class="guide-section" v-if="info?.contact">
<h2>外部举报渠道</h2>
<div class="contact-box">
<div>{{ info.contact.hotline }}</div>
<div>{{ info.contact.website }}</div>
</div>
</div>
</template>
</div>
</div>
</template>
<style scoped>
.guide-page {
min-height: 100vh;
background: #f5f7fa;
}
.guide-container {
max-width: 860px;
margin: 0 auto;
padding: 24px;
}
.guide-hero {
padding: 34px 28px;
background: #fff;
border-radius: 14px;
text-align: center;
margin-bottom: 16px;
}
.guide-hero svg {
color: #00DC64;
margin-bottom: 10px;
}
.guide-hero h1 {
margin: 0 0 12px;
font-size: 24px;
color: #303133;
}
.guide-hero p {
max-width: 620px;
margin: 0 auto 22px;
font-size: 14px;
line-height: 1.8;
color: #606266;
}
.hero-btn {
border: none;
border-radius: 22px;
padding: 11px 30px;
font-size: 14px;
color: #fff;
background: #00DC64;
cursor: pointer;
transition: opacity 0.2s;
}
.hero-btn:hover {
opacity: 0.88;
}
.guide-skeleton {
padding: 24px;
background: #fff;
border-radius: 14px;
}
.guide-section {
padding: 22px 24px;
background: #fff;
border-radius: 14px;
margin-bottom: 16px;
}
.guide-section h2 {
margin: 0 0 16px;
font-size: 17px;
color: #303133;
}
.guide-intro {
margin: 0 0 20px;
padding: 13px 15px;
background: #f0f9eb;
border-radius: 10px;
font-size: 13px;
line-height: 1.8;
color: #529b2e;
}
.protection-list {
display: flex;
flex-direction: column;
gap: 20px;
}
.protection-block h3 {
margin: 0 0 8px;
font-size: 15px;
font-weight: 600;
color: #303133;
}
.protection-block ul {
margin: 0;
padding-left: 20px;
}
.protection-block li {
font-size: 13px;
line-height: 1.9;
color: #606266;
}
.legal-list {
margin: 0;
padding-left: 20px;
}
.legal-list li {
font-size: 13px;
line-height: 1.9;
color: #606266;
}
.howto-list {
margin: 0;
padding-left: 20px;
font-size: 14px;
line-height: 2;
color: #606266;
}
.howto-list strong {
margin-right: 8px;
color: #303133;
}
.contact-box {
padding: 14px 16px;
background: #fdf6ec;
border-radius: 10px;
font-size: 13px;
line-height: 1.9;
color: #b88230;
}
@media (max-width: 640px) {
.guide-container {
padding: 16px;
}
.guide-hero {
padding: 26px 18px;
}
.guide-section {
padding: 18px 16px;
}
}
</style>
|
|