feat(users): govern profiles and access
Normalize fixed profile fields, profile actions, and the five-column capability matrix for comments, uploads, and downloads.
This commit is contained in:
@@ -7,37 +7,15 @@ export const permissionsSection = {
|
||||
route: '/permissions',
|
||||
children: [
|
||||
customPage({
|
||||
key: 'permissions-comment',
|
||||
name: 'PermissionsComment',
|
||||
label: '评论权限',
|
||||
route: '/permissions/comment',
|
||||
component: 'CommentPermissions',
|
||||
title: '评论权限',
|
||||
desc: '游客评论、频率限制、敏感词和审核策略。',
|
||||
key: 'permissions-capabilities',
|
||||
name: 'PermissionsCapabilities',
|
||||
label: '基本权限',
|
||||
route: '/permissions/capabilities',
|
||||
component: 'PermissionsCapabilities',
|
||||
title: '基本权限 / 能力矩阵',
|
||||
desc: '统一管理用户、投稿、评论、上传和下载的准入能力。',
|
||||
canonicalDomain: 'permissions',
|
||||
section: 'comment',
|
||||
}),
|
||||
customPage({
|
||||
key: 'permissions-attachment',
|
||||
name: 'PermissionsAttachment',
|
||||
label: '附件权限',
|
||||
route: '/permissions/attachment',
|
||||
component: 'AttachmentPermissions',
|
||||
title: '附件权限',
|
||||
desc: '上传角色、文件类型、大小限制和审核状态。',
|
||||
canonicalDomain: 'permissions',
|
||||
section: 'attachment',
|
||||
}),
|
||||
customPage({
|
||||
key: 'permissions-download',
|
||||
name: 'PermissionsDownload',
|
||||
label: '下载权限',
|
||||
route: '/permissions/download',
|
||||
component: 'DownloadPermissions',
|
||||
title: '下载权限',
|
||||
desc: '登录要求、积分要求、角色要求和 VIP 特权。',
|
||||
canonicalDomain: 'permissions',
|
||||
section: 'download',
|
||||
section: 'capabilities',
|
||||
}),
|
||||
schemaPage({
|
||||
key: 'permissions-login',
|
||||
@@ -52,23 +30,23 @@ export const permissionsSection = {
|
||||
schemaPage({
|
||||
key: 'permissions-moderation',
|
||||
name: 'PermissionsModeration',
|
||||
label: '审核和风控',
|
||||
label: '内容审核',
|
||||
route: '/permissions/moderation',
|
||||
title: '审核和风控',
|
||||
title: '内容审核',
|
||||
desc: '评论审核、反垃圾、限流与风控配置。',
|
||||
canonicalDomain: 'permissions',
|
||||
section: 'moderation',
|
||||
}),
|
||||
customPage({
|
||||
key: 'permissions-capabilities',
|
||||
name: 'PermissionsCapabilities',
|
||||
label: '基本权限',
|
||||
route: '/permissions/capabilities',
|
||||
component: 'PermissionsCapabilities',
|
||||
title: '基本权限 / 能力矩阵',
|
||||
desc: '统一管理用户治理、投稿和评论的角色能力矩阵。',
|
||||
key: 'permissions-attachment',
|
||||
name: 'PermissionsAttachment',
|
||||
label: '附件配置',
|
||||
route: '/permissions/attachment',
|
||||
component: 'AttachmentPermissions',
|
||||
title: '附件配置',
|
||||
desc: '管理文件大小、类型、安全限制和审核策略。',
|
||||
canonicalDomain: 'permissions',
|
||||
section: 'capabilities',
|
||||
section: 'attachment',
|
||||
}),
|
||||
],
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
>
|
||||
<template #actions>
|
||||
<n-space>
|
||||
<n-button quaternary @click="go('/permissions/download')">下载权限</n-button>
|
||||
<n-button quaternary @click="go('/permissions/capabilities')">基本权限</n-button>
|
||||
<n-button quaternary @click="go('/payment/gateway')">收款接口</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
@@ -18,15 +18,9 @@
|
||||
<h3>当前联动摘要</h3>
|
||||
</div>
|
||||
<n-space>
|
||||
<n-tag round :type="form.download_login ? 'warning' : 'default'">
|
||||
登录门槛:{{ form.download_login ? '已启用' : '已关闭' }}
|
||||
</n-tag>
|
||||
<n-tag round :type="form.download_credit ? 'warning' : 'default'">
|
||||
积分购买模式:{{ form.download_credit ? '已启用' : '已关闭' }}
|
||||
</n-tag>
|
||||
<n-tag round :type="form.download_captcha ? 'warning' : 'default'">
|
||||
下载验证码:{{ form.download_captcha ? '已启用' : '已关闭' }}
|
||||
</n-tag>
|
||||
<n-tag round type="info">
|
||||
默认积分价:{{ form.download_default_cost }} {{ currencyName }}
|
||||
</n-tag>
|
||||
@@ -44,10 +38,6 @@
|
||||
<h3>下载门槛</h3>
|
||||
</div>
|
||||
<n-form :model="form" label-placement="left" label-width="180" class="aksrt-form">
|
||||
<n-form-item label="下载需要登录">
|
||||
<n-switch v-model:value="form.download_login" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="允许积分购买模式">
|
||||
<n-switch v-model:value="form.download_credit" />
|
||||
<template #feedback>
|
||||
@@ -55,10 +45,6 @@
|
||||
</template>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="下载启用验证码">
|
||||
<n-switch v-model:value="form.download_captcha" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item :label="`默认积分价(${currencyName})`">
|
||||
<n-input-number v-model:value="form.download_default_cost" :min="0" :max="100000" style="width: 180px;" />
|
||||
<template #feedback>
|
||||
@@ -140,9 +126,7 @@ const creditSnapshot = ref({})
|
||||
const overview = ref({})
|
||||
|
||||
const form = reactive({
|
||||
download_login: true,
|
||||
download_credit: true,
|
||||
download_captcha: false,
|
||||
download_default_cost: 0,
|
||||
payment_paid_post_types: 'post',
|
||||
payment_vip_free_download: false,
|
||||
@@ -185,9 +169,7 @@ async function load() {
|
||||
creditSnapshot.value = creditData
|
||||
overview.value = extractData(overviewRes)
|
||||
|
||||
form.download_login = featureData.download_login !== false
|
||||
form.download_credit = featureData.download_credit !== false
|
||||
form.download_captcha = !!featureData.download_captcha
|
||||
form.download_default_cost = Number(creditData.download_default_cost || 0)
|
||||
form.payment_paid_post_types = schemaData.payment_paid_post_types || 'post'
|
||||
form.payment_vip_free_download = !!schemaData.payment_vip_free_download
|
||||
@@ -206,9 +188,7 @@ async function handleSave() {
|
||||
try {
|
||||
const nextFeatures = {
|
||||
...featureSnapshot.value,
|
||||
download_login: form.download_login,
|
||||
download_credit: form.download_credit,
|
||||
download_captcha: form.download_captcha,
|
||||
}
|
||||
const nextCredits = {
|
||||
...creditSnapshot.value,
|
||||
|
||||
@@ -24,12 +24,6 @@
|
||||
</template>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item v-if="basic.comment_enabled" label="必须注册才能评论">
|
||||
<n-switch v-model:value="basic.comment_requires_registration">
|
||||
<template #checked>是</template>
|
||||
<template #unchecked>否</template>
|
||||
</n-switch>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</n-card>
|
||||
|
||||
@@ -123,7 +117,6 @@ const saving = ref(false)
|
||||
// 后端 /settings/basic 返回基础字段;auth 相关字段由 /settings/auth 独立维护。
|
||||
const basic = reactive({
|
||||
comment_enabled: true,
|
||||
comment_requires_registration: false,
|
||||
icp_number: '',
|
||||
police_number: '',
|
||||
lazyload_enabled: true,
|
||||
@@ -139,7 +132,6 @@ async function loadAll() {
|
||||
if (basicRes?.code === 'success' && basicRes.data) {
|
||||
const d = basicRes.data
|
||||
basic.comment_enabled = d.comment_enabled !== false
|
||||
basic.comment_requires_registration = !!d.comment_requires_registration
|
||||
basic.icp_number = d.icp_number || ''
|
||||
basic.police_number = d.police_number || ''
|
||||
basic.lazyload_enabled = d.lazyload_enabled !== false
|
||||
@@ -159,7 +151,6 @@ async function handleSave() {
|
||||
try {
|
||||
const basicPayload = {
|
||||
comment_enabled: basic.comment_enabled,
|
||||
comment_requires_registration: basic.comment_requires_registration,
|
||||
icp_number: basic.icp_number,
|
||||
police_number: basic.police_number,
|
||||
lazyload_enabled: basic.lazyload_enabled,
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
<div class="aksrt-page-container">
|
||||
<PageHeader
|
||||
title="附件治理"
|
||||
desc="统一管理全站上传行为:头像、封面、投稿图片、评论图片、背景图和资源附件。"
|
||||
desc="管理各类上传文件的大小、安全限制和审核策略;准入权限请在“基本权限”中配置。"
|
||||
>
|
||||
<template #actions>
|
||||
<n-space>
|
||||
<n-button quaternary @click="go('/permissions/download')">下载权限</n-button>
|
||||
<n-button quaternary @click="goModeration">审核策略</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
@@ -34,31 +33,10 @@
|
||||
<!-- Avatar & Cover -->
|
||||
<n-card class="aksrt-section-card">
|
||||
<div class="aksrt-section-header">
|
||||
<h3>头像与封面</h3>
|
||||
<h3>用户资料卡背景图</h3>
|
||||
</div>
|
||||
<n-form :model="form" label-placement="left" label-width="200" class="aksrt-form">
|
||||
<n-form-item label="允许用户上传头像">
|
||||
<n-switch v-model:value="form.allow_avatar_upload" />
|
||||
<template #feedback>
|
||||
<span class="aksrt-hint-text">关闭后用户使用项目内置默认头像,且不能自行更换。</span>
|
||||
</template>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="允许用户上传封面图">
|
||||
<n-switch v-model:value="form.allow_cover_upload" />
|
||||
<template #feedback>
|
||||
<span class="aksrt-hint-text">控制用户中心公开主页封面图上传入口。</span>
|
||||
</template>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="允许用户上传背景图">
|
||||
<n-switch v-model:value="form.allow_background_upload" />
|
||||
<template #feedback>
|
||||
<span class="aksrt-hint-text">控制用户中心公开可用的背景图上传入口。</span>
|
||||
</template>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="头像/封面大小上限 (MB)">
|
||||
<n-form-item label="头像/用户资料卡背景图大小上限 (MB)">
|
||||
<n-input-number v-model:value="form.avatar_max_size" :min="1" :max="10" style="max-width:160px;" />
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
@@ -70,24 +48,10 @@
|
||||
<h3>内容上传</h3>
|
||||
</div>
|
||||
<n-form :model="form" label-placement="left" label-width="200" class="aksrt-form">
|
||||
<n-form-item label="允许投稿插入图片">
|
||||
<n-switch v-model:value="form.allow_submit_images" />
|
||||
<template #feedback>
|
||||
<span class="aksrt-hint-text">前台投稿编辑器中允许插入图片。</span>
|
||||
</template>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="投稿图片大小上限 (MB)">
|
||||
<n-input-number v-model:value="form.submit_image_max_size" :min="1" :max="20" style="max-width:160px;" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="允许评论插入图片">
|
||||
<n-switch v-model:value="form.allow_comment_images" />
|
||||
<template #feedback>
|
||||
<span class="aksrt-hint-text">评论工具栏中允许插入图片链接或上传图片。</span>
|
||||
</template>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="评论图片大小上限 (MB)">
|
||||
<n-input-number v-model:value="form.comment_image_max_size" :min="1" :max="10" style="max-width:160px;" />
|
||||
</n-form-item>
|
||||
@@ -100,13 +64,6 @@
|
||||
<h3>资源附件</h3>
|
||||
</div>
|
||||
<n-form :model="form" label-placement="left" label-width="200" class="aksrt-form">
|
||||
<n-form-item label="允许资源附件上传">
|
||||
<n-switch v-model:value="form.allow_resource_upload" />
|
||||
<template #feedback>
|
||||
<span class="aksrt-hint-text">后台资源编辑器中允许上传附件文件。</span>
|
||||
</template>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="资源附件大小上限 (MB)">
|
||||
<n-input-number v-model:value="form.resource_max_size" :min="1" :max="200" style="max-width:160px;" />
|
||||
</n-form-item>
|
||||
@@ -158,27 +115,19 @@ const saving = ref(false)
|
||||
const moderationSnapshot = ref({})
|
||||
|
||||
const form = reactive({
|
||||
allow_avatar_upload: true,
|
||||
allow_cover_upload: true,
|
||||
allow_background_upload: false,
|
||||
avatar_max_size: 2,
|
||||
allow_submit_images: true,
|
||||
submit_image_max_size: 5,
|
||||
allow_comment_images: false,
|
||||
comment_image_max_size: 2,
|
||||
allow_resource_upload: true,
|
||||
resource_max_size: 50,
|
||||
ai_moderate_background: true,
|
||||
ai_moderate_submit: false,
|
||||
})
|
||||
|
||||
const summaryItems = computed(() => [
|
||||
{ key: 'avatar', icon: 'fas fa-user-circle', label: '头像上传', enabled: form.allow_avatar_upload },
|
||||
{ key: 'cover', icon: 'fas fa-image', label: '封面上传', enabled: form.allow_cover_upload },
|
||||
{ key: 'background', icon: 'fas fa-photo-video', label: '背景图上传', enabled: form.allow_background_upload },
|
||||
{ key: 'submit', icon: 'fas fa-edit', label: '投稿图片', enabled: form.allow_submit_images },
|
||||
{ key: 'comment', icon: 'fas fa-comment-dots', label: '评论图片', enabled: form.allow_comment_images },
|
||||
{ key: 'resource', icon: 'fas fa-file-archive', label: '资源附件', enabled: form.allow_resource_upload },
|
||||
{ key: 'profile', icon: 'fas fa-id-card', label: '资料卡图片', enabled: true },
|
||||
{ key: 'submit', icon: 'fas fa-edit', label: '投稿图片', enabled: true },
|
||||
{ key: 'comment', icon: 'fas fa-comment-dots', label: '评论图片', enabled: true },
|
||||
{ key: 'resource', icon: 'fas fa-file-archive', label: '资源附件', enabled: true },
|
||||
])
|
||||
|
||||
function go(path) {
|
||||
@@ -199,15 +148,9 @@ async function load() {
|
||||
const moderation = moderationRes?.data ?? moderationRes ?? {}
|
||||
|
||||
// Map permission fields
|
||||
form.allow_avatar_upload = permission.allow_avatar_upload !== false
|
||||
form.allow_cover_upload = permission.allow_cover_upload !== false
|
||||
form.allow_background_upload = !!permission.allow_background_upload
|
||||
form.avatar_max_size = Number(permission.avatar_max_size || 2)
|
||||
form.allow_submit_images = permission.allow_submit_images !== false
|
||||
form.submit_image_max_size = Number(permission.submit_image_max_size || 5)
|
||||
form.allow_comment_images = !!permission.allow_comment_images
|
||||
form.comment_image_max_size = Number(permission.comment_image_max_size || 2)
|
||||
form.allow_resource_upload = permission.allow_resource_upload !== false
|
||||
form.resource_max_size = Number(permission.resource_max_size || 50)
|
||||
|
||||
form.ai_moderate_background = moderation.ai_moderate_background !== false
|
||||
@@ -226,15 +169,9 @@ async function handleSave() {
|
||||
try {
|
||||
await Promise.all([
|
||||
api.savePermissionsAttachmentSettings({
|
||||
allow_avatar_upload: form.allow_avatar_upload,
|
||||
allow_cover_upload: form.allow_cover_upload,
|
||||
allow_background_upload: form.allow_background_upload,
|
||||
avatar_max_size: form.avatar_max_size,
|
||||
allow_submit_images: form.allow_submit_images,
|
||||
submit_image_max_size: form.submit_image_max_size,
|
||||
allow_comment_images: form.allow_comment_images,
|
||||
comment_image_max_size: form.comment_image_max_size,
|
||||
allow_resource_upload: form.allow_resource_upload,
|
||||
resource_max_size: form.resource_max_size,
|
||||
}),
|
||||
api.savePermissionsModerationSettings({
|
||||
|
||||
@@ -1,154 +0,0 @@
|
||||
<template>
|
||||
<div class="aksrt-page-container">
|
||||
<PageHeader
|
||||
title="评论权限"
|
||||
desc="管理评论入口的登录要求与回复权限,并查看当前验证码与审核策略状态。"
|
||||
>
|
||||
<template #actions>
|
||||
<n-space>
|
||||
<n-button quaternary @click="go('/permissions/login')">人机验证</n-button>
|
||||
<n-button quaternary @click="go('/permissions/moderation')">审核策略</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
</PageHeader>
|
||||
|
||||
<n-spin :show="loading" style="width:100%; min-height:200px;">
|
||||
<n-card class="aksrt-section-card">
|
||||
<div class="aksrt-section-header">
|
||||
<h3>当前联动状态</h3>
|
||||
</div>
|
||||
<n-space>
|
||||
<n-tag :type="status.commentCaptchaEnabled ? 'success' : 'default'" round>
|
||||
评论验证码:{{ status.commentCaptchaEnabled ? '已启用' : '未启用' }}
|
||||
</n-tag>
|
||||
<n-tag :type="status.moderationEnabled ? 'warning' : 'default'" round>
|
||||
审核链路:{{ status.moderationEnabled ? '已启用' : '未启用' }}
|
||||
</n-tag>
|
||||
<n-tag :type="status.rateLimitEnabled ? 'warning' : 'default'" round>
|
||||
评论限流:{{ status.rateLimitEnabled ? '已启用' : '未启用' }}
|
||||
</n-tag>
|
||||
<n-tag :type="status.keywordRuleCount > 0 ? 'error' : 'default'" round>
|
||||
敏感词:{{ status.keywordRuleCount }} 条
|
||||
</n-tag>
|
||||
</n-space>
|
||||
</n-card>
|
||||
|
||||
<n-card class="aksrt-section-card">
|
||||
<div class="aksrt-section-header">
|
||||
<h3>评论准入</h3>
|
||||
</div>
|
||||
<n-form :model="form" label-placement="left" label-width="180" class="aksrt-form">
|
||||
<n-form-item label="评论需要登录">
|
||||
<n-switch v-model:value="form.comment_require_login" />
|
||||
<template #feedback>
|
||||
<span class="aksrt-hint-text">开启后,游客只能浏览评论,不能直接提交评论。</span>
|
||||
</template>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="允许楼中回复">
|
||||
<n-switch v-model:value="form.comment_allow_reply" />
|
||||
<template #feedback>
|
||||
<span class="aksrt-hint-text">关闭后,前台评论列表将不再展示回复按钮。</span>
|
||||
</template>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</n-card>
|
||||
|
||||
<n-card class="aksrt-section-card">
|
||||
<n-alert type="info" :show-icon="false">
|
||||
评论排序、分页方式、头像大小和空状态文案继续在“文章&列表 -> 评论设置”中维护;本页只负责准入与治理联动。
|
||||
</n-alert>
|
||||
</n-card>
|
||||
|
||||
<div class="aksrt-form-actions">
|
||||
<n-button type="primary" :loading="saving" @click="handleSave">保存配置</n-button>
|
||||
</div>
|
||||
</n-spin>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useMessage } from 'naive-ui'
|
||||
import api from '@/api'
|
||||
import PageHeader from '@/components/PageHeader.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const message = useMessage()
|
||||
|
||||
const loading = ref(true)
|
||||
const saving = ref(false)
|
||||
|
||||
const form = reactive({
|
||||
comment_require_login: false,
|
||||
comment_allow_reply: true,
|
||||
})
|
||||
|
||||
const status = reactive({
|
||||
commentCaptchaEnabled: false,
|
||||
moderationEnabled: false,
|
||||
rateLimitEnabled: false,
|
||||
keywordRuleCount: 0,
|
||||
})
|
||||
|
||||
function go(path) {
|
||||
router.push(path)
|
||||
}
|
||||
|
||||
function splitKeywords(value) {
|
||||
return String(value || '')
|
||||
.split(/\r?\n/)
|
||||
.map((item) => item.trim())
|
||||
.filter(Boolean)
|
||||
}
|
||||
|
||||
async function load() {
|
||||
loading.value = true
|
||||
try {
|
||||
const [commentData, captchaRes, moderationRes] = await Promise.all([
|
||||
api.getSchemaPage('post_list', 'comment'),
|
||||
api.getPermissionsLoginSettings(),
|
||||
api.getPermissionsModerationSettings(),
|
||||
])
|
||||
|
||||
const comment = commentData?.data ?? commentData ?? {}
|
||||
const captcha = captchaRes?.data ?? captchaRes ?? {}
|
||||
const moderation = moderationRes?.data ?? moderationRes ?? {}
|
||||
const enabledScenarios = Array.isArray(captcha.enabled_scenarios) ? captcha.enabled_scenarios : null
|
||||
const disabledScenarios = Array.isArray(captcha.disabled_scenarios) ? captcha.disabled_scenarios : []
|
||||
const commentSceneOn = enabledScenarios
|
||||
? enabledScenarios.includes('comment')
|
||||
: !disabledScenarios.includes('comment')
|
||||
|
||||
form.comment_require_login = !!comment.comment_require_login
|
||||
form.comment_allow_reply = comment.comment_allow_reply !== false
|
||||
|
||||
status.commentCaptchaEnabled = !!captcha.enabled && !!captcha.active_provider && commentSceneOn
|
||||
status.moderationEnabled = !!moderation.enabled
|
||||
status.rateLimitEnabled = !!moderation.rate_limit_enabled
|
||||
status.keywordRuleCount = splitKeywords(moderation.blocked_keywords).length
|
||||
} catch (error) {
|
||||
message.error(error.message || '加载评论权限失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSave() {
|
||||
saving.value = true
|
||||
try {
|
||||
await api.saveSchemaPage('post_list', 'comment', {
|
||||
comment_require_login: form.comment_require_login,
|
||||
comment_allow_reply: form.comment_allow_reply,
|
||||
})
|
||||
message.success('评论权限已保存')
|
||||
} catch (error) {
|
||||
message.error(error.message || '保存失败')
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(load)
|
||||
</script>
|
||||
@@ -1,161 +0,0 @@
|
||||
<template>
|
||||
<div class="aksrt-page-container">
|
||||
<PageHeader
|
||||
title="下载权限"
|
||||
desc="管理资源下载的登录、积分购买模式与验证码门槛。付费解锁走收银台订单,下载 token 不再按次扣积分。"
|
||||
>
|
||||
<template #actions>
|
||||
<n-space>
|
||||
<n-button quaternary @click="go('/payment/paid')">资源下载页</n-button>
|
||||
<n-button quaternary @click="go('/permissions/login')">人机验证</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
</PageHeader>
|
||||
|
||||
<n-spin :show="loading" style="width:100%; min-height:200px;">
|
||||
<n-card class="aksrt-section-card">
|
||||
<div class="aksrt-section-header">
|
||||
<h3>当前门槛摘要</h3>
|
||||
</div>
|
||||
<n-space>
|
||||
<n-tag :type="form.download_login ? 'warning' : 'default'" round>
|
||||
登录要求:{{ form.download_login ? '已启用' : '已关闭' }}
|
||||
</n-tag>
|
||||
<n-tag :type="form.download_credit ? 'warning' : 'default'" round>
|
||||
积分购买模式:{{ form.download_credit ? '已启用' : '已关闭' }}
|
||||
</n-tag>
|
||||
<n-tag :type="form.download_captcha ? 'warning' : 'default'" round>
|
||||
下载验证码:{{ form.download_captcha ? '已启用' : '已关闭' }}
|
||||
</n-tag>
|
||||
<n-tag type="info" round>默认积分价参考:{{ defaultCost }}</n-tag>
|
||||
<n-tag type="info" round>Token TTL:10 分钟</n-tag>
|
||||
</n-space>
|
||||
</n-card>
|
||||
|
||||
<n-card class="aksrt-section-card">
|
||||
<div class="aksrt-section-header">
|
||||
<h3>下载门槛</h3>
|
||||
</div>
|
||||
<n-form :model="form" label-placement="left" label-width="180" class="aksrt-form">
|
||||
<n-form-item label="下载需要登录">
|
||||
<n-switch v-model:value="form.download_login" />
|
||||
<template #feedback>
|
||||
<span class="aksrt-hint-text">关闭后,单个资源的登录要求仍可通过资源元字段单独覆盖。</span>
|
||||
</template>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="允许积分购买模式">
|
||||
<n-switch v-model:value="form.download_credit" />
|
||||
<template #feedback>
|
||||
<span class="aksrt-hint-text">开启后,文章 pay_modo=积分 可走收银台一次购买解锁;下载 token 不再按次扣积分。关闭时积分模式资源按免费/需登录策略处理。</span>
|
||||
</template>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="下载启用验证码">
|
||||
<n-switch v-model:value="form.download_captcha" />
|
||||
<template #feedback>
|
||||
<span class="aksrt-hint-text">具体验证码服务商与场景白名单仍在「人机验证」页维护。</span>
|
||||
</template>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</n-card>
|
||||
|
||||
<n-card class="aksrt-section-card">
|
||||
<n-alert type="info" :show-icon="false">
|
||||
实际价格以文章「付费内容设置」为准;本页只维护全局访问门槛。VIP 白名单与收银台策略见「支付 & 付费」相关页面。
|
||||
</n-alert>
|
||||
</n-card>
|
||||
|
||||
<div class="aksrt-form-actions">
|
||||
<n-button type="primary" :loading="saving" @click="handleSave">保存配置</n-button>
|
||||
</div>
|
||||
</n-spin>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useMessage } from 'naive-ui'
|
||||
import api from '@/api'
|
||||
import PageHeader from '@/components/PageHeader.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const message = useMessage()
|
||||
|
||||
const loading = ref(true)
|
||||
const saving = ref(false)
|
||||
const defaultCost = ref(0)
|
||||
const featureSnapshot = ref({
|
||||
resource_cpt_enabled: true,
|
||||
search_include_resource: true,
|
||||
download_login: true,
|
||||
download_credit: true,
|
||||
download_captcha: false,
|
||||
})
|
||||
|
||||
const form = reactive({
|
||||
download_login: true,
|
||||
download_credit: true,
|
||||
download_captcha: false,
|
||||
})
|
||||
|
||||
function go(path) {
|
||||
router.push(path)
|
||||
}
|
||||
|
||||
async function load() {
|
||||
loading.value = true
|
||||
try {
|
||||
const [featureRes, creditRes] = await Promise.all([
|
||||
api.getCmsFeatures(),
|
||||
api.getCreditSettings(),
|
||||
])
|
||||
|
||||
const features = featureRes?.cms_features ?? featureRes?.data?.cms_features ?? {}
|
||||
const creditSettings = creditRes?.data ?? creditRes ?? {}
|
||||
|
||||
featureSnapshot.value = {
|
||||
resource_cpt_enabled: features.resource_cpt_enabled !== false,
|
||||
search_include_resource: features.search_include_resource !== false,
|
||||
download_login: features.download_login !== false,
|
||||
download_credit: features.download_credit !== false,
|
||||
download_captcha: !!features.download_captcha,
|
||||
}
|
||||
|
||||
form.download_login = featureSnapshot.value.download_login
|
||||
form.download_credit = featureSnapshot.value.download_credit
|
||||
form.download_captcha = featureSnapshot.value.download_captcha
|
||||
defaultCost.value = Number(creditSettings.download_default_cost || 0)
|
||||
} catch (error) {
|
||||
message.error(error.message || '加载下载权限失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSave() {
|
||||
saving.value = true
|
||||
try {
|
||||
await api.saveCmsFeatures({
|
||||
...featureSnapshot.value,
|
||||
download_login: form.download_login,
|
||||
download_credit: form.download_credit,
|
||||
download_captcha: form.download_captcha,
|
||||
})
|
||||
featureSnapshot.value = {
|
||||
...featureSnapshot.value,
|
||||
download_login: form.download_login,
|
||||
download_credit: form.download_credit,
|
||||
download_captcha: form.download_captcha,
|
||||
}
|
||||
message.success('下载权限已保存')
|
||||
} catch (error) {
|
||||
message.error(error.message || '保存失败')
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(load)
|
||||
</script>
|
||||
@@ -12,8 +12,7 @@
|
||||
</PageHeader>
|
||||
|
||||
<n-alert type="warning" :show-icon="true" style="margin: 12px 0 16px;">
|
||||
转账/提现(balance_transfer、points_transfer、balance_withdraw)默认 <b>exclude all</b>,与 Zibll 一致:未在矩阵中为角色/等级/VIP/认证打开前,前台入口会隐藏或拒绝。
|
||||
medal_manually_set 控制谁可授予徽章;管理员 manage_options 始终可通过。
|
||||
每项能力都通过“所有人、已登录、等级、VIP、已认证”五列治理;管理员(manage_options)始终可通过。
|
||||
</n-alert>
|
||||
|
||||
|
||||
|
||||
@@ -1,49 +1,30 @@
|
||||
<template>
|
||||
<div class="aksrt-page-container">
|
||||
<PageHeader
|
||||
title="用户功能"
|
||||
desc="统一配置用户中心和公开主页的功能模块,让前台页面和后台字段使用同一份契约。"
|
||||
>
|
||||
<PageHeader title="用户功能" desc="用户中心的资料、背景图与公开资料均使用统一的能力矩阵和隐私规则。">
|
||||
<template #actions>
|
||||
<n-space>
|
||||
<n-button quaternary @click="go('/permissions/capabilities')">基本权限</n-button>
|
||||
<n-button quaternary @click="go('/page-display/user-home')">公开主页</n-button>
|
||||
<n-button quaternary @click="go('/permissions/attachment')">附件权限</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
</PageHeader>
|
||||
|
||||
<n-spin :show="loading" style="width: 100%; min-height: 200px;">
|
||||
<n-spin :show="loading" style="width: 100%; min-height: 180px;">
|
||||
<n-card class="aksrt-section-card">
|
||||
<div class="aksrt-section-header">
|
||||
<h3>当前联动摘要</h3>
|
||||
</div>
|
||||
<n-space>
|
||||
<n-tag type="info" round>固定入口:个人信息 / 账号设置 / 消息通知</n-tag>
|
||||
<n-tag round :type="form.user_cover_upload ? 'success' : 'default'">
|
||||
背景图编辑位:{{ form.user_cover_upload ? '已展示' : '已隐藏' }}
|
||||
</n-tag>
|
||||
<n-tag round :type="backgroundUploadAllowed ? 'success' : 'warning'">
|
||||
背景图上传权限:{{ backgroundUploadAllowed ? '已开放' : '未开放' }}
|
||||
</n-tag>
|
||||
<n-tag round :type="homeSummary.sidebar ? 'success' : 'default'">
|
||||
公开主页侧栏:{{ homeSummary.sidebar ? '开启' : '关闭' }}
|
||||
</n-tag>
|
||||
<div class="aksrt-section-header"><h3>固定资料与操作入口</h3></div>
|
||||
<n-space vertical size="large">
|
||||
<n-alert type="info" :show-icon="false">
|
||||
个人资料固定为昵称、个人签名、隐私设置、性别、个人网站、QQ、微信和 GitHub。公开资料由访问者身份过滤,原始社交资料不会下发给无权访客。
|
||||
</n-alert>
|
||||
<n-alert type="info" :show-icon="false">
|
||||
背景图操作入口始终显示在资料卡右下角的三点菜单中;“上传头像”和“上传用户资料卡背景图”由基本权限矩阵控制。
|
||||
</n-alert>
|
||||
</n-space>
|
||||
</n-card>
|
||||
|
||||
<n-card class="aksrt-section-card">
|
||||
<div class="aksrt-section-header">
|
||||
<h3>用户中心面板</h3>
|
||||
</div>
|
||||
<div class="aksrt-section-header"><h3>用户中心面板</h3></div>
|
||||
<n-form label-placement="left" label-width="170" class="aksrt-form">
|
||||
<n-form-item label="固定面板">
|
||||
<n-space>
|
||||
<n-tag round>个人信息</n-tag>
|
||||
<n-tag round>账号设置</n-tag>
|
||||
<n-tag round>消息通知</n-tag>
|
||||
</n-space>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="可切换业务面板">
|
||||
<n-checkbox-group v-model:value="form.dynamicTabs">
|
||||
<n-space vertical>
|
||||
@@ -53,40 +34,10 @@
|
||||
<n-checkbox value="credits">账户余额 / 签到</n-checkbox>
|
||||
</n-space>
|
||||
</n-checkbox-group>
|
||||
<template #feedback>
|
||||
<span class="aksrt-hint-text">“个人信息”始终保留在首位;积分面板仅在积分系统启用时显示。</span>
|
||||
</template>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</n-card>
|
||||
|
||||
<n-card class="aksrt-section-card">
|
||||
<div class="aksrt-section-header">
|
||||
<h3>公开资料编辑位</h3>
|
||||
</div>
|
||||
<n-form :model="form" label-placement="left" label-width="170" class="aksrt-form">
|
||||
<n-form-item label="背景图编辑位">
|
||||
<n-switch v-model:value="form.user_cover_upload" />
|
||||
<template #feedback>
|
||||
<span class="aksrt-hint-text">这里只控制前台入口显示;真正的上传权限仍由“附件权限”页治理。</span>
|
||||
</template>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="公开社交链接">
|
||||
<n-switch v-model:value="form.user_social_links" />
|
||||
<template #feedback>
|
||||
<span class="aksrt-hint-text">关闭后,用户中心联系方式编辑区和公开主页联系卡片会一起隐藏。</span>
|
||||
</template>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</n-card>
|
||||
|
||||
<n-card class="aksrt-section-card">
|
||||
<n-alert type="info" :show-icon="false">
|
||||
公开主页的封面、侧栏、关注按钮和公开 Tab 仍在“页面&显示 / 用户主页”维护;本页负责“用户中心能否编辑哪些内容”。
|
||||
</n-alert>
|
||||
</n-card>
|
||||
|
||||
<div class="aksrt-form-actions">
|
||||
<n-button type="primary" :loading="saving" @click="handleSave">保存配置</n-button>
|
||||
</div>
|
||||
@@ -103,61 +54,22 @@ import PageHeader from '@/components/PageHeader.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const message = useMessage()
|
||||
|
||||
const loading = ref(true)
|
||||
const saving = ref(false)
|
||||
const backgroundUploadAllowed = ref(false)
|
||||
const homeSummary = reactive({
|
||||
tabs: [],
|
||||
sidebar: true,
|
||||
})
|
||||
|
||||
const form = reactive({
|
||||
dynamicTabs: ['posts', 'comments', 'favorites'],
|
||||
user_cover_upload: true,
|
||||
user_social_links: true,
|
||||
})
|
||||
|
||||
function go(path) {
|
||||
router.push(path)
|
||||
}
|
||||
|
||||
function extractData(response) {
|
||||
return response?.data ?? response ?? {}
|
||||
}
|
||||
const form = reactive({ dynamicTabs: ['posts', 'comments', 'favorites'] })
|
||||
|
||||
function go(path) { router.push(path) }
|
||||
function extractData(response) { return response?.data ?? response ?? {} }
|
||||
function parseTabs(value) {
|
||||
const items = Array.isArray(value)
|
||||
? value
|
||||
: String(value || '')
|
||||
.split(/[\s,]+/)
|
||||
.filter(Boolean)
|
||||
|
||||
return items
|
||||
.map((item) => String(item).trim())
|
||||
.filter((item) => ['posts', 'comments', 'favorites', 'credits'].includes(item))
|
||||
const values = Array.isArray(value) ? value : String(value || '').split(/[\s,]+/)
|
||||
return values.map((value) => String(value).trim()).filter((value) => ['posts', 'comments', 'favorites', 'credits'].includes(value))
|
||||
}
|
||||
|
||||
async function load() {
|
||||
loading.value = true
|
||||
try {
|
||||
const [featureRes, homeRes, permissionRes] = await Promise.all([
|
||||
api.getSchemaPage('users', 'features'),
|
||||
api.getSchemaPage('page_display', 'user-home'),
|
||||
api.getPermissionsAttachmentSettings(),
|
||||
])
|
||||
|
||||
const featureData = extractData(featureRes)
|
||||
const homeData = extractData(homeRes)
|
||||
const permissionData = extractData(permissionRes)
|
||||
|
||||
form.dynamicTabs = parseTabs(featureData.user_center_tabs || 'profile,posts,comments,favorites')
|
||||
form.user_cover_upload = featureData.user_cover_upload !== false
|
||||
form.user_social_links = featureData.user_social_links !== false
|
||||
|
||||
homeSummary.tabs = parseTabs(homeData.user_home_tabs || 'posts,comments,favorites')
|
||||
homeSummary.sidebar = homeData.user_home_sidebar !== false
|
||||
backgroundUploadAllowed.value = !!permissionData.allow_background_upload
|
||||
const data = extractData(await api.getSchemaPage('users', 'features'))
|
||||
form.dynamicTabs = parseTabs(data.user_center_tabs || 'profile,posts,comments,favorites')
|
||||
} catch (error) {
|
||||
message.error(error.message || '加载用户功能配置失败')
|
||||
} finally {
|
||||
@@ -168,11 +80,7 @@ async function load() {
|
||||
async function handleSave() {
|
||||
saving.value = true
|
||||
try {
|
||||
await api.saveSchemaPage('users', 'features', {
|
||||
user_center_tabs: ['profile', ...form.dynamicTabs].join(','),
|
||||
user_cover_upload: form.user_cover_upload,
|
||||
user_social_links: form.user_social_links,
|
||||
})
|
||||
await api.saveSchemaPage('users', 'features', { user_center_tabs: ['profile', ...form.dynamicTabs].join(',') })
|
||||
message.success('用户功能配置已保存')
|
||||
} catch (error) {
|
||||
message.error(error.message || '保存失败')
|
||||
|
||||
@@ -10,10 +10,8 @@ export const corePageLoaders = {
|
||||
BannerManager: () => import('@/pages/content/BannerManager.vue'),
|
||||
PostPageSettings: () => import('@/pages/post-list/PostPageSettings.vue'),
|
||||
CommentSettings: () => import('@/pages/post-list/CommentSettings.vue'),
|
||||
CommentPermissions: () => import('@/pages/permissions/CommentPermissions.vue'),
|
||||
PermissionsCapabilities: () => import('@/pages/permissions/PermissionsCapabilities.vue'),
|
||||
AttachmentPermissions: () => import('@/pages/permissions/AttachmentPermissions.vue'),
|
||||
DownloadPermissions: () => import('@/pages/permissions/DownloadPermissions.vue'),
|
||||
CmsCredits: () => import('@/pages/CmsCredits.vue'),
|
||||
ResourceDownload: () => import('@/pages/ResourceDownload.vue'),
|
||||
ThemeInfoPage: () => import('@/pages/theme/ThemeInfoPage.vue'),
|
||||
|
||||
@@ -15,7 +15,6 @@ export const orderSchema = {
|
||||
payment_order_max_minutes: 30,
|
||||
pay_mark: '付费',
|
||||
pay_box_position: 'top',
|
||||
pay_free_logged_show: false,
|
||||
payment_show_ip: false,
|
||||
payment_show_device: false,
|
||||
payment_show_order_no: true,
|
||||
@@ -58,7 +57,6 @@ export const orderSchema = {
|
||||
fields: [
|
||||
{ key: 'pay_mark', label: '付费标记', type: 'input', default: '付费' },
|
||||
{ key: 'pay_box_position', label: '付费框位置', type: 'radio', default: 'top', options: [{ label: '文章框顶部(封面后)', value: 'box_top' }, { label: '正文顶部', value: 'top' }, { label: '正文底部', value: 'bottom' }, { label: '文章框底部(相关前)', value: 'box_bottom' }] },
|
||||
{ key: 'pay_free_logged_show', label: '登录用户显示免费', type: 'switch', default: false },
|
||||
{ key: 'payment_show_ip', label: '显示用户IP', type: 'switch', default: false },
|
||||
{ key: 'payment_show_device', label: '显示设备信息', type: 'switch', default: false },
|
||||
{ key: 'payment_show_order_no', label: '显示订单号', type: 'switch', default: true },
|
||||
|
||||
@@ -16,7 +16,6 @@ export const paidSchema = {
|
||||
pay_box_position: 'top',
|
||||
payment_download_enable: false,
|
||||
payment_down_alone_page: false,
|
||||
pay_free_logged_show: false,
|
||||
payment_download_remote_s: true,
|
||||
payment_download_verify_nonce: true,
|
||||
payment_download_user_log: 50,
|
||||
@@ -56,7 +55,6 @@ export const paidSchema = {
|
||||
{ key: 'pay_show_user', label: '显示购买用户', type: 'switch', default: true },
|
||||
{ key: 'pay_show_paycount', label: '显示购买次数', type: 'switch', default: true },
|
||||
{ key: 'pay_box_position', label: '付费框位置', type: 'radio', default: 'top', options: [{ label: '文章框顶部(封面后)', value: 'box_top' }, { label: '正文顶部', value: 'top' }, { label: '正文底部', value: 'bottom' }, { label: '文章框底部(相关前)', value: 'box_bottom' }] },
|
||||
{ key: 'pay_free_logged_show', label: '免费资源需登录后下载', type: 'switch', default: false, help: '开启后,价格为 0 的下载资源也要求登录才可下载(对齐 zibll pay_free_logged_show)。' },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -209,35 +209,6 @@ export const permissionsSchemas = {
|
||||
],
|
||||
},
|
||||
|
||||
/**
|
||||
* Comment permissions
|
||||
*/
|
||||
comment: {
|
||||
key: 'permissions.comment',
|
||||
title: '评论权限',
|
||||
icon: 'fas fa-comment-dots',
|
||||
description: '控制评论功能的开关和权限要求。',
|
||||
defaults: {
|
||||
comment_allow: true,
|
||||
comment_require_login: false,
|
||||
comment_nested_depth: 3,
|
||||
comment_allow_reply: true,
|
||||
},
|
||||
groups: [
|
||||
{
|
||||
key: 'basic',
|
||||
title: '评论基础',
|
||||
icon: 'fas fa-cog',
|
||||
fields: [
|
||||
{ key: 'comment_allow', label: '允许评论', type: 'switch', help: '全局开启评论功能' },
|
||||
{ key: 'comment_require_login', label: '需登录评论', type: 'switch', help: '只有登录用户才能评论' },
|
||||
{ key: 'comment_nested_depth', label: '嵌套层级', type: 'slider', min: 1, max: 5, default: 3 },
|
||||
{ key: 'comment_allow_reply', label: '允许回复', type: 'switch' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
/**
|
||||
* Attachment permissions
|
||||
*/
|
||||
@@ -247,27 +218,18 @@ export const permissionsSchemas = {
|
||||
icon: 'fas fa-paperclip',
|
||||
description: '控制文件上传和附件下载权限。',
|
||||
defaults: {
|
||||
allow_avatar_upload: true,
|
||||
allow_cover_upload: true,
|
||||
allow_background_upload: false,
|
||||
avatar_max_size: 2,
|
||||
allow_submit_images: true,
|
||||
submit_image_max_size: 5,
|
||||
allow_comment_images: false,
|
||||
comment_image_max_size: 2,
|
||||
allow_resource_upload: true,
|
||||
resource_max_size: 50,
|
||||
},
|
||||
groups: [
|
||||
{
|
||||
key: 'profile',
|
||||
title: '头像与封面',
|
||||
title: '用户资料卡背景图',
|
||||
icon: 'fas fa-user-circle',
|
||||
fields: [
|
||||
{ key: 'allow_avatar_upload', label: '允许用户上传头像', type: 'switch' },
|
||||
{ key: 'allow_cover_upload', label: '允许用户上传封面图', type: 'switch' },
|
||||
{ key: 'allow_background_upload', label: '允许用户上传背景图', type: 'switch' },
|
||||
{ key: 'avatar_max_size', label: '头像/封面大小上限 (MB)', type: 'number', min: 1, max: 10 },
|
||||
{ key: 'avatar_max_size', label: '头像/用户资料卡背景图大小上限 (MB)', type: 'number', min: 1, max: 10 },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -275,9 +237,7 @@ export const permissionsSchemas = {
|
||||
title: '内容上传',
|
||||
icon: 'fas fa-images',
|
||||
fields: [
|
||||
{ key: 'allow_submit_images', label: '允许投稿插入图片', type: 'switch' },
|
||||
{ key: 'submit_image_max_size', label: '投稿图片大小上限 (MB)', type: 'number', min: 1, max: 20 },
|
||||
{ key: 'allow_comment_images', label: '允许评论插入图片', type: 'switch' },
|
||||
{ key: 'comment_image_max_size', label: '评论图片大小上限 (MB)', type: 'number', min: 1, max: 10 },
|
||||
],
|
||||
},
|
||||
@@ -286,23 +246,10 @@ export const permissionsSchemas = {
|
||||
title: '资源附件',
|
||||
icon: 'fas fa-file-archive',
|
||||
fields: [
|
||||
{ key: 'allow_resource_upload', label: '允许资源附件上传', type: 'switch' },
|
||||
{ key: 'resource_max_size', label: '资源附件大小上限 (MB)', type: 'number', min: 1, max: 200 },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
/**
|
||||
* Download permissions — live UI is DownloadPermissions.vue via cms_features.
|
||||
* Keep empty schema so SchemaPage cannot mis-write aksrt_settings.
|
||||
*/
|
||||
download: {
|
||||
key: 'permissions.download',
|
||||
title: '下载权限',
|
||||
icon: 'fas fa-download',
|
||||
description: '下载门槛由 DownloadPermissions 页维护(cms_features);人机验证服务商在「人机验证」页。',
|
||||
defaults: {},
|
||||
groups: [],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -11,14 +11,11 @@ export const commentSchema = {
|
||||
comment_paging_ajax_ias_max: 3,
|
||||
comment_nested_depth: 3,
|
||||
comment_avatar_size: 48,
|
||||
comment_require_login: false,
|
||||
comment_allow_reply: true,
|
||||
comment_close_comments: false,
|
||||
comment_edit_window: 10,
|
||||
comment_smilie: true,
|
||||
comment_code: true,
|
||||
comment_img: true,
|
||||
comment_upload_img: false,
|
||||
comment_like_s: true,
|
||||
comment_hot_threshold: 10,
|
||||
comment_quick_s: true,
|
||||
@@ -56,11 +53,9 @@ export const commentSchema = {
|
||||
},
|
||||
{
|
||||
key: 'permissions',
|
||||
title: '评论权限',
|
||||
title: '评论设置',
|
||||
icon: 'fas fa-lock',
|
||||
fields: [
|
||||
{ key: 'comment_require_login', label: '评论需要登录', type: 'switch', default: false },
|
||||
{ key: 'comment_allow_reply', label: '允许回复', type: 'switch', default: true },
|
||||
{ key: 'comment_close_comments', label: '关闭评论', type: 'switch', default: false },
|
||||
{ key: 'comment_edit_window', label: '作者自助编辑时限 (分钟)', type: 'number', default: 10, min: 0, max: 1440 },
|
||||
],
|
||||
|
||||
@@ -5,8 +5,6 @@ export const featuresSchema = {
|
||||
description: '配置用户中心和个人主页的功能模块。',
|
||||
defaults: {
|
||||
user_center_tabs: 'profile,level,posts,comments,favorites,vip,credits,revenue,auth',
|
||||
user_cover_upload: true,
|
||||
user_social_links: true,
|
||||
user_desc_std: '这家伙很懒…',
|
||||
user_city_s: true,
|
||||
user_city_type: 'province',
|
||||
@@ -25,8 +23,6 @@ export const featuresSchema = {
|
||||
icon: 'fas fa-user-shield',
|
||||
fields: [
|
||||
{ key: 'user_center_tabs', label: '用户中心 Tab', type: 'input', default: 'profile,level,comments,favorites,vip,credits,revenue,auth', help: '逗号分隔。level=我的等级;creator=创作中心;revenue=推广收益中心;auth=官方认证;等级、VIP、订单、创作中心、收益中心和认证会按功能开关自动注入' },
|
||||
{ key: 'user_cover_upload', label: '封面上传', type: 'switch', default: true },
|
||||
{ key: 'user_social_links', label: '社交链接', type: 'switch', default: true },
|
||||
{ key: 'user_desc_std', label: '默认签名', type: 'input', default: '这家伙很懒…', help: '用户未设置个人简介时显示的默认文案' },
|
||||
{ key: 'user_city_s', label: '显示城市(IP 归属地)', type: 'switch', default: true, help: '根据用户登录 IP 在用户主页与用户中心显示归属地;需同时在「扩展 → IP归属地」启用并勾选对应展示位置' },
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user