refactor(admin): consolidate duplicate configuration
Remove duplicate comment, captcha, filing, and website-security configuration paths. Move front submission verification to the captcha scene matrix and retire obsolete admin routes and components.
This commit is contained in:
@@ -36,15 +36,6 @@ export const extensionsSection = {
|
||||
canonicalDomain: 'extensions',
|
||||
section: 'push',
|
||||
}),
|
||||
customPage({
|
||||
key: 'extensions-security',
|
||||
name: 'ExtensionsSecurity',
|
||||
label: '网站安全',
|
||||
route: '/extensions/security',
|
||||
component: 'ExtensionSecurity',
|
||||
title: '网站安全',
|
||||
desc: '扩展安全策略和第三方安全集成。',
|
||||
}),
|
||||
customPage({
|
||||
key: 'extensions-tools',
|
||||
name: 'ExtensionsTools',
|
||||
|
||||
@@ -1,176 +0,0 @@
|
||||
<template>
|
||||
<div class="aksrt-page-container">
|
||||
<PageHeader title="站点设置" desc="集中管理评论、分页、懒加载、头像源与备案信息。">
|
||||
<template #actions>
|
||||
<n-button type="primary" :loading="saving" @click="handleSave">保存全部</n-button>
|
||||
</template>
|
||||
</PageHeader>
|
||||
|
||||
<n-spin :show="loading" style="width:100%; min-height:200px;">
|
||||
<!-- 1. 评论 -->
|
||||
<n-card class="aksrt-section-card">
|
||||
<div class="aksrt-section-header">
|
||||
<span class="section-icon"><svg xmlns="http://www.w3.org/2000/svg" width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg></span>
|
||||
<h3>评论设置</h3>
|
||||
</div>
|
||||
<n-form :model="basic" label-placement="left" label-width="140" class="aksrt-form">
|
||||
<n-form-item label="全局评论开关">
|
||||
<n-switch v-model:value="basic.comment_enabled">
|
||||
<template #checked>已开启</template>
|
||||
<template #unchecked>已关闭</template>
|
||||
</n-switch>
|
||||
<template #feedback>
|
||||
<span class="aksrt-hint-text">关闭后全站禁用评论功能。</span>
|
||||
</template>
|
||||
</n-form-item>
|
||||
|
||||
</n-form>
|
||||
</n-card>
|
||||
|
||||
<!-- 2. 分页 -->
|
||||
<n-card class="aksrt-section-card">
|
||||
<div class="aksrt-section-header">
|
||||
<span class="section-icon"><svg xmlns="http://www.w3.org/2000/svg" width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg></span>
|
||||
<h3>分页设置</h3>
|
||||
</div>
|
||||
<n-form :model="basic" label-placement="left" label-width="140" class="aksrt-form">
|
||||
<n-form-item label="文章列表分页">
|
||||
<n-radio-group v-model:value="basic.paging_ajax_s">
|
||||
<n-space size="large">
|
||||
<n-radio-button value="0">数字翻页</n-radio-button>
|
||||
<n-radio-button value="1">加载更多</n-radio-button>
|
||||
<n-radio-button value="1">无限滚动</n-radio-button>
|
||||
</n-space>
|
||||
</n-radio-group>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="评论列表分页">
|
||||
<n-radio-group v-model:value="basic.comment_pagination_mode">
|
||||
<n-space size="large">
|
||||
<n-radio-button value="0">数字翻页</n-radio-button>
|
||||
<n-radio-button value="1">加载更多</n-radio-button>
|
||||
<n-radio-button value="1">无限滚动</n-radio-button>
|
||||
</n-space>
|
||||
</n-radio-group>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</n-card>
|
||||
|
||||
<!-- 3. 懒加载 -->
|
||||
<n-card class="aksrt-section-card">
|
||||
<div class="aksrt-section-header">
|
||||
<span class="section-icon"><svg xmlns="http://www.w3.org/2000/svg" width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg></span>
|
||||
<h3>图片懒加载</h3>
|
||||
</div>
|
||||
<n-form :model="basic" label-placement="left" label-width="140" class="aksrt-form">
|
||||
<n-form-item label="启用懒加载">
|
||||
<n-switch v-model:value="basic.lazyload_enabled">
|
||||
<template #checked>已开启</template>
|
||||
<template #unchecked>已关闭</template>
|
||||
</n-switch>
|
||||
<template #feedback>
|
||||
<span class="aksrt-hint-text">开启后图片在进入视口时才加载,可显著提升页面加载速度。</span>
|
||||
</template>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item v-if="basic.lazyload_enabled" label="自定义占位图 URL">
|
||||
<n-input v-model:value="basic.lazyload_placeholder_url" placeholder="留空使用默认占位图" />
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</n-card>
|
||||
|
||||
<!-- 5. 备案 -->
|
||||
<n-card class="aksrt-section-card">
|
||||
<div class="aksrt-section-header">
|
||||
<span class="section-icon"><svg xmlns="http://www.w3.org/2000/svg" width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg></span>
|
||||
<h3>备案信息</h3>
|
||||
</div>
|
||||
<n-form :model="basic" label-placement="left" label-width="120" class="aksrt-form">
|
||||
<n-form-item label="ICP 备案号">
|
||||
<n-input v-model:value="basic.icp_number" placeholder="例如:ICP 备 xxxxxxxx 号" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="公安备案号">
|
||||
<n-input v-model:value="basic.police_number" placeholder="例如:公网安备 xxxxxx 号" />
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</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 { useMessage } from 'naive-ui'
|
||||
import api from '@/api'
|
||||
import PageHeader from '@/components/PageHeader.vue'
|
||||
|
||||
const message = useMessage()
|
||||
const loading = ref(true)
|
||||
const saving = ref(false)
|
||||
|
||||
// 基础设置(canonical: aksrt_settings)
|
||||
// 后端 /settings/basic 返回基础字段;auth 相关字段由 /settings/auth 独立维护。
|
||||
const basic = reactive({
|
||||
comment_enabled: true,
|
||||
icp_number: '',
|
||||
police_number: '',
|
||||
lazyload_enabled: true,
|
||||
lazyload_placeholder_url: '',
|
||||
paging_ajax_s: '1',
|
||||
comment_pagination_mode: 'numbered',
|
||||
})
|
||||
|
||||
async function loadAll() {
|
||||
loading.value = true
|
||||
try {
|
||||
const basicRes = await api.getBasicSettings()
|
||||
if (basicRes?.code === 'success' && basicRes.data) {
|
||||
const d = basicRes.data
|
||||
basic.comment_enabled = d.comment_enabled !== false
|
||||
basic.icp_number = d.icp_number || ''
|
||||
basic.police_number = d.police_number || ''
|
||||
basic.lazyload_enabled = d.lazyload_enabled !== false
|
||||
basic.lazyload_placeholder_url = d.lazyload_placeholder_url || ''
|
||||
basic.paging_ajax_s = d.paging_ajax_s ?? '1'
|
||||
basic.comment_pagination_mode = d.comment_pagination_mode || 'numbered'
|
||||
}
|
||||
} catch (e) {
|
||||
message.error(e.message || '加载失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSave() {
|
||||
saving.value = true
|
||||
try {
|
||||
const basicPayload = {
|
||||
comment_enabled: basic.comment_enabled,
|
||||
icp_number: basic.icp_number,
|
||||
police_number: basic.police_number,
|
||||
lazyload_enabled: basic.lazyload_enabled,
|
||||
lazyload_placeholder_url: basic.lazyload_placeholder_url,
|
||||
paging_ajax_s: basic.paging_ajax_s,
|
||||
comment_pagination_mode: basic.comment_pagination_mode,
|
||||
}
|
||||
|
||||
const res = await api.saveBasicSettings(basicPayload)
|
||||
if (res?.code === 'success') {
|
||||
message.success('已保存')
|
||||
} else {
|
||||
throw new Error(res?.message || '保存失败')
|
||||
}
|
||||
} catch (e) {
|
||||
message.error(e.message || '保存失败')
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(loadAll)
|
||||
</script>
|
||||
@@ -1,311 +0,0 @@
|
||||
<template>
|
||||
<ExtensionsSectionPage
|
||||
title="网站安全"
|
||||
desc="扩展安全策略和第三方安全集成。"
|
||||
>
|
||||
<n-card title="安全概览" size="small" class="aksrt-security__section">
|
||||
<n-grid :cols="4" :x-gap="12" :y-gap="12">
|
||||
<n-grid-item v-for="item in securityItems" :key="item.key">
|
||||
<div class="aksrt-security__overview-card">
|
||||
<FaIcon :icon="item.icon" :size="18" :color="item.color" />
|
||||
<div>
|
||||
<strong>{{ item.value }}</strong>
|
||||
<p>{{ item.label }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</n-grid-item>
|
||||
</n-grid>
|
||||
</n-card>
|
||||
|
||||
<n-card title="安全能力" size="small" class="aksrt-security__section">
|
||||
<n-list bordered size="small">
|
||||
<n-list-item v-for="feat in features" :key="feat.key">
|
||||
<n-thing :title="feat.name" :description="feat.description">
|
||||
<template #avatar>
|
||||
<FaIcon :icon="feat.icon" :size="16" :color="feat.color" />
|
||||
</template>
|
||||
<template #header-extra>
|
||||
<n-space align="center" size="small">
|
||||
<n-tag :type="feat.enabled ? 'success' : 'default'" size="small">
|
||||
{{ feat.enabled ? '已启用' : '未启用' }}
|
||||
</n-tag>
|
||||
<router-link v-if="feat.settingsRoute" :to="feat.settingsRoute" class="aksrt-security__config-link">
|
||||
前往配置
|
||||
</router-link>
|
||||
</n-space>
|
||||
</template>
|
||||
</n-thing>
|
||||
</n-list-item>
|
||||
</n-list>
|
||||
</n-card>
|
||||
|
||||
<n-card title="在线测试" size="small" class="aksrt-security__section">
|
||||
<n-grid :cols="2" :x-gap="16" :y-gap="16">
|
||||
<n-grid-item>
|
||||
<n-form label-placement="top">
|
||||
<n-form-item label="IP 归属地测试">
|
||||
<n-input v-model:value="ipTest.ip" placeholder="输入 IPv4 / IPv6,例如 8.8.8.8" />
|
||||
</n-form-item>
|
||||
<n-space>
|
||||
<n-button :loading="ipTest.loading" @click="handleTestIp">测试 IP 接口</n-button>
|
||||
</n-space>
|
||||
<pre v-if="ipTest.result" class="aksrt-security__output">{{ ipTest.result }}</pre>
|
||||
</n-form>
|
||||
</n-grid-item>
|
||||
<n-grid-item>
|
||||
<n-form label-placement="top">
|
||||
<n-form-item label="内容审核测试">
|
||||
<n-input v-model:value="auditTest.text" type="textarea" :autosize="{ minRows: 4, maxRows: 8 }" placeholder="输入一段测试文本,检查当前审核策略是否生效" />
|
||||
</n-form-item>
|
||||
<n-space>
|
||||
<n-button :loading="auditTest.loading" @click="handleTestAudit">测试内容审核</n-button>
|
||||
</n-space>
|
||||
<pre v-if="auditTest.result" class="aksrt-security__output">{{ auditTest.result }}</pre>
|
||||
</n-form>
|
||||
</n-grid-item>
|
||||
</n-grid>
|
||||
</n-card>
|
||||
|
||||
<n-card title="风险开关" size="small" class="aksrt-security__section">
|
||||
<n-alert type="warning" size="small" style="margin-bottom: 12px;">
|
||||
以下开关会影响站点安全策略,请谨慎操作。
|
||||
</n-alert>
|
||||
<n-form label-placement="left" label-width="200" size="small">
|
||||
<n-form-item label="允许未审核评论显示">
|
||||
<n-switch v-model:value="riskSwitches.show_unapproved_comments" />
|
||||
<n-text depth="3" style="margin-left: 8px; font-size: 12px;">
|
||||
开启后未审核评论也会在前台显示(标记为待审核)
|
||||
</n-text>
|
||||
</n-form-item>
|
||||
<n-form-item label="允许前台投稿无需审核">
|
||||
<n-switch v-model:value="riskSwitches.skip_submit_moderation" />
|
||||
<n-text depth="3" style="margin-left: 8px; font-size: 12px;">
|
||||
开启后用户投稿直接发布,不经过审核流程
|
||||
</n-text>
|
||||
</n-form-item>
|
||||
<n-form-item label="允许游客评论">
|
||||
<n-switch v-model:value="riskSwitches.guest_comment" />
|
||||
<n-text depth="3" style="margin-left: 8px; font-size: 12px;">
|
||||
开启后未登录用户也可以发表评论
|
||||
</n-text>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<template #action>
|
||||
<n-space justify="end">
|
||||
<n-button type="primary" size="small" :loading="saving" @click="handleSaveRisk">保存风险设置</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
</n-card>
|
||||
|
||||
<n-card title="安全审计" size="small" class="aksrt-security__section">
|
||||
<n-empty v-if="auditLogs.length === 0" description="暂无审计记录" />
|
||||
<n-list v-else bordered size="small">
|
||||
<n-list-item v-for="log in auditLogs" :key="log.id">
|
||||
<n-thing :title="log.action" :description="log.detail">
|
||||
<template #header-extra>
|
||||
<n-text depth="3" style="font-size: 11px;">{{ log.time }}</n-text>
|
||||
</template>
|
||||
</n-thing>
|
||||
</n-list-item>
|
||||
</n-list>
|
||||
</n-card>
|
||||
</ExtensionsSectionPage>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useMessage } from 'naive-ui'
|
||||
import ExtensionsSectionPage from '@/pages/extensions/ExtensionsSectionPage.vue'
|
||||
import FaIcon from '@/components/form/FaIcon.vue'
|
||||
import api from '@/api/index.js'
|
||||
|
||||
const message = useMessage()
|
||||
const saving = ref(false)
|
||||
|
||||
const securityItems = ref([
|
||||
{ key: 'email_gate', icon: 'fas fa-envelope', color: '#1890ff', value: '—', label: '邮箱验证' },
|
||||
{ key: 'captcha', icon: 'fas fa-shield-alt', color: '#52c41a', value: '—', label: '验证码保护' },
|
||||
{ key: 'moderation', icon: 'fas fa-gavel', color: '#722ed1', value: '—', label: '内容审核' },
|
||||
{ key: 'ip_geo', icon: 'fas fa-map-marker-alt', color: '#fa8c16', value: '—', label: 'IP 归属地' },
|
||||
])
|
||||
|
||||
const features = ref([
|
||||
{ key: 'email_verification', icon: 'fas fa-envelope', color: '#1890ff', name: '邮箱验证', description: '新用户注册需要邮箱验证', enabled: false, settingsRoute: '/users/auth' },
|
||||
{ key: 'captcha_comment', icon: 'fas fa-shield-alt', color: '#52c41a', name: '评论验证码', description: '发表评论时需要人机验证', enabled: false, settingsRoute: '/permissions/login' },
|
||||
{ key: 'captcha_download', icon: 'fas fa-shield-alt', color: '#52c41a', name: '下载验证码', description: '下载资源时需要人机验证', enabled: false, settingsRoute: '/permissions/login' },
|
||||
{ key: 'content_audit', icon: 'fas fa-shield-virus', color: '#722ed1', name: '内容审核 API', description: '接入第三方内容安全审核', enabled: false, settingsRoute: '/extensions/audit' },
|
||||
{ key: 'ip_geolocation', icon: 'fas fa-map-marker-alt', color: '#fa8c16', name: 'IP 归属地', description: '在评论和用户主页显示 IP 归属地', enabled: false, settingsRoute: '/extensions/ip' },
|
||||
])
|
||||
|
||||
const riskSwitches = ref({
|
||||
show_unapproved_comments: false,
|
||||
skip_submit_moderation: false,
|
||||
guest_comment: false,
|
||||
})
|
||||
|
||||
const auditLogs = ref([])
|
||||
const ipTest = ref({ ip: '8.8.8.8', loading: false, result: '' })
|
||||
const auditTest = ref({ text: '这是一段用于审核测试的示例文本。', loading: false, result: '' })
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const res = await api.request('/settings/security')
|
||||
if (res?.data) {
|
||||
const d = res.data
|
||||
const svc = res.services || {}
|
||||
|
||||
const captcha = d.captcha || svc.captcha || {}
|
||||
const captchaOn = !!(captcha.enabled && captcha.configured)
|
||||
const captchaScenes = captcha.scenes || {}
|
||||
const audit = d.content_audit || svc.audit || {}
|
||||
const ipGeo = d.ip_geo || svc.ip_geo || {}
|
||||
|
||||
securityItems.value[0].value = d.email_gate_enabled ? '已开启' : '未开启'
|
||||
securityItems.value[0].color = d.email_gate_enabled ? '#52c41a' : '#8a909c'
|
||||
securityItems.value[1].value = captchaOn ? '已开启' : '未开启'
|
||||
securityItems.value[1].color = captchaOn ? '#52c41a' : '#8a909c'
|
||||
|
||||
if (audit.enabled) {
|
||||
securityItems.value[2].value = audit.configured ? '已配置' : '待补全'
|
||||
securityItems.value[2].color = audit.configured ? '#52c41a' : '#faad14'
|
||||
} else {
|
||||
securityItems.value[2].value = '未开启'
|
||||
securityItems.value[2].color = '#8a909c'
|
||||
}
|
||||
|
||||
if (ipGeo.enabled) {
|
||||
securityItems.value[3].value = ipGeo.configured ? '已配置' : '待补全'
|
||||
securityItems.value[3].color = ipGeo.configured ? '#52c41a' : '#faad14'
|
||||
} else {
|
||||
securityItems.value[3].value = '未开启'
|
||||
securityItems.value[3].color = '#8a909c'
|
||||
}
|
||||
|
||||
features.value[0].enabled = !!d.email_gate_enabled
|
||||
features.value[1].enabled = !!captchaScenes.comment
|
||||
features.value[2].enabled = !!captcha.download_active
|
||||
features.value[3].enabled = !!audit.enabled
|
||||
features.value[4].enabled = !!ipGeo.enabled
|
||||
|
||||
if (d.risk_switches) {
|
||||
riskSwitches.value = { ...riskSwitches.value, ...d.risk_switches }
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Endpoint may not exist yet
|
||||
}
|
||||
|
||||
try {
|
||||
const logRes = await api.request('/security/audit-log')
|
||||
if (logRes?.data?.logs) {
|
||||
auditLogs.value = logRes.data.logs.slice(0, 10)
|
||||
}
|
||||
} catch {
|
||||
// Endpoint may not exist yet
|
||||
}
|
||||
})
|
||||
|
||||
async function handleSaveRisk() {
|
||||
saving.value = true
|
||||
try {
|
||||
await api.request('/settings/security', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ risk_switches: riskSwitches.value }),
|
||||
})
|
||||
message.success('风险设置已保存')
|
||||
} catch (err) {
|
||||
message.error('保存失败: ' + (err?.message || '未知错误'))
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleTestIp() {
|
||||
ipTest.value.loading = true
|
||||
ipTest.value.result = ''
|
||||
try {
|
||||
const res = await api.testIpGeo(String(ipTest.value.ip || '').trim())
|
||||
const data = res?.data || {}
|
||||
ipTest.value.result = JSON.stringify(data, null, 2)
|
||||
if (data.ok) {
|
||||
message.success(data.display ? `IP 测试成功:${data.display}` : 'IP 接口测试完成')
|
||||
} else {
|
||||
message.warning(data.message || '未查询到归属地信息')
|
||||
}
|
||||
} catch (err) {
|
||||
message.error(err?.message || 'IP 测试失败')
|
||||
} finally {
|
||||
ipTest.value.loading = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleTestAudit() {
|
||||
auditTest.value.loading = true
|
||||
auditTest.value.result = ''
|
||||
try {
|
||||
const res = await api.testContentAudit(String(auditTest.value.text || '').trim())
|
||||
const data = res?.data || {}
|
||||
auditTest.value.result = JSON.stringify(data, null, 2)
|
||||
if (data.ok) {
|
||||
message.success(data.message || '内容审核测试完成')
|
||||
} else {
|
||||
message.warning(data.message || '内容审核未通过或配置异常')
|
||||
}
|
||||
} catch (err) {
|
||||
message.error(err?.message || '内容审核测试失败')
|
||||
} finally {
|
||||
auditTest.value.loading = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.aksrt-security__section {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.aksrt-security__overview-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 14px;
|
||||
border-radius: 8px;
|
||||
background: #fafafa;
|
||||
border: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.aksrt-security__overview-card strong {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
color: #1a1a2e;
|
||||
}
|
||||
|
||||
.aksrt-security__overview-card p {
|
||||
margin: 2px 0 0;
|
||||
font-size: 12px;
|
||||
color: #8a909c;
|
||||
}
|
||||
|
||||
.aksrt-security__config-link {
|
||||
font-size: 12px;
|
||||
color: #1890ff;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.aksrt-security__config-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.aksrt-security__output {
|
||||
margin: 12px 0 0;
|
||||
padding: 12px 14px;
|
||||
border-radius: 12px;
|
||||
background: #101828;
|
||||
color: #d0d7e2;
|
||||
font-size: 12px;
|
||||
line-height: 1.7;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
</style>
|
||||
@@ -90,7 +90,7 @@ const guides = [
|
||||
{ key: 'meilisearch', title: 'Meilisearch', desc: '配置搜索索引与同步策略', url: '#/global/search' },
|
||||
{ key: 'i18n', title: '多语言翻译', desc: '检查语言切换与术语配置', url: '#/global/i18n' },
|
||||
{ key: 'cdn', title: '静态资源与 CDN', desc: '核对前端资源地址与缓存策略', url: '#/global/common' },
|
||||
{ key: 'ip-geo', title: 'IP 归属地', desc: '验证 IP 查询服务与密钥配置', url: '#/extensions/security' },
|
||||
{ key: 'ip-geo', title: 'IP 归属地', desc: '验证 IP 查询服务与密钥配置', url: '#/extensions/ip' },
|
||||
{ key: 'payment', title: '支付与会员', desc: '核对网关、会员与充值配置', url: '#/payment/gateway' },
|
||||
{ key: 'credits', title: '积分与签到', desc: '检查积分规则、签到与邀请奖励', url: '#/users/signin' },
|
||||
{ key: 'badge', title: '等级与徽章', desc: '维护等级、认证与徽章体系', url: '#/users/badge' },
|
||||
|
||||
@@ -229,14 +229,8 @@ async function load() {
|
||||
const inviteReward = creditData.invite_reward || {}
|
||||
const enabledScenarios = Array.isArray(captchaData.enabled_scenarios)
|
||||
? captchaData.enabled_scenarios
|
||||
: null
|
||||
const disabledScenarios = Array.isArray(captchaData.disabled_scenarios)
|
||||
? captchaData.disabled_scenarios
|
||||
: []
|
||||
const sceneOn = (key) => {
|
||||
if (enabledScenarios) return enabledScenarios.includes(key)
|
||||
return !disabledScenarios.includes(key)
|
||||
}
|
||||
const sceneOn = (key) => enabledScenarios.includes(key)
|
||||
|
||||
creditSnapshot.value = creditData
|
||||
authSnapshot.value = authData
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
export const extensionsPageLoaders = {
|
||||
ExtensionTools: () => import('@/pages/extensions/ExtensionTools.vue'),
|
||||
ExtensionSecurity: () => import('@/pages/extensions/ExtensionSecurity.vue'),
|
||||
ExtensionAI: () => import('@/pages/extensions/ExtensionAI.vue'),
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ export const commonSchema = {
|
||||
icon: 'fas fa-cog',
|
||||
description: '管理站点常用开关、懒加载与备案信息等基础配置。',
|
||||
defaults: {
|
||||
comment_enabled: true,
|
||||
lazyload_enabled: true,
|
||||
lazyload_animation: 'blur',
|
||||
lazyload_placeholder: '',
|
||||
@@ -18,17 +17,10 @@ export const commonSchema = {
|
||||
lazyload_other: true,
|
||||
lazyload_widget: true,
|
||||
icp_filing: '',
|
||||
police_filing: '',
|
||||
footer_text: '',
|
||||
},
|
||||
groups: [
|
||||
{
|
||||
key: 'comment',
|
||||
title: '评论功能',
|
||||
icon: 'fas fa-comment',
|
||||
fields: [
|
||||
{ key: 'comment_enabled', label: '开启评论', type: 'switch', default: true, help: '全局评论开关,关闭后所有文章不再显示评论区' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'performance',
|
||||
title: '性能优化',
|
||||
@@ -72,6 +64,7 @@ export const commonSchema = {
|
||||
icon: 'fas fa-shield-alt',
|
||||
fields: [
|
||||
{ key: 'icp_filing', label: 'ICP 备案号', type: 'input', placeholder: '例如:京ICP备12345678号', help: '显示在页脚的备案信息' },
|
||||
{ key: 'police_filing', label: '公安备案号', type: 'input', placeholder: '例如:公网安备11000000000000号', help: '显示在页脚的备案信息' },
|
||||
{ key: 'footer_text', label: '页脚附加文字', type: 'input', placeholder: '自定义页脚文字', help: '显示在版权信息下方' },
|
||||
],
|
||||
},
|
||||
|
||||
@@ -824,9 +824,7 @@ export const pageDisplaySchemas = {
|
||||
footer_columns: 3,
|
||||
footer_copyright: true,
|
||||
footer_icp: true,
|
||||
icp_number: '',
|
||||
footer_police: true,
|
||||
police_number: '',
|
||||
footer_theme_custom: false,
|
||||
footer_theme_bg_custom: '',
|
||||
footer_theme_bg: '',
|
||||
@@ -857,9 +855,7 @@ export const pageDisplaySchemas = {
|
||||
{ key: 'footer_columns', label: '页脚列数', type: 'number', default: 3, min: 1, max: 4 },
|
||||
{ key: 'footer_copyright', label: '显示版权信息', type: 'switch', default: true },
|
||||
{ key: 'footer_icp', label: '显示ICP备案', type: 'switch', default: true },
|
||||
{ key: 'icp_number', label: 'ICP备案号', type: 'input', default: '' },
|
||||
{ key: 'footer_police', label: '显示公安备案', type: 'switch', default: true },
|
||||
{ key: 'police_number', label: '公安备案号', type: 'input', default: '' },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -20,7 +20,6 @@ export const permissionsSchemas = {
|
||||
'comment', 'login', 'register', 'bind', 'bind_phone',
|
||||
'lostpassword', 'change_password', 'change_email', 'verify_email', 'download', 'signin',
|
||||
],
|
||||
disabled_scenarios: [],
|
||||
tencent: {},
|
||||
aliyun: {},
|
||||
cloudflare: {},
|
||||
@@ -75,6 +74,7 @@ export const permissionsSchemas = {
|
||||
{ label: '邮箱验证', value: 'verify_email' },
|
||||
{ label: '资源下载', value: 'download' },
|
||||
{ label: '每日签到', value: 'signin' },
|
||||
{ label: '前台投稿', value: 'front_submit' },
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -23,7 +23,6 @@ export const submitSchema = {
|
||||
front_submit_draft_save: true,
|
||||
front_submit_leave_warning: true,
|
||||
front_submit_guest_enabled: false,
|
||||
front_submit_captcha: false,
|
||||
post_article_btn_txte: '发布文章',
|
||||
post_article_title_strlen_limit_min: 5,
|
||||
post_article_title_strlen_limit_max: 30,
|
||||
@@ -63,7 +62,6 @@ export const submitSchema = {
|
||||
{ key: 'post_article_cat', label: '允许投稿分类', type: 'taxonomy_select', taxonomy: 'category', multiple: true, default: [], help: '为空则允许全部分类', dependsOn: { key: 'front_submit_enabled', value: true } },
|
||||
{ key: 'front_submit_guest_enabled', label: '允许免登录投稿', type: 'switch', default: false, help: '开启后访客可投稿;文章归属到下方用户', dependsOn: { key: 'front_submit_enabled', value: true } },
|
||||
{ key: 'post_article_user', label: '免登录归属用户', type: 'user_select', default: 0, help: '免登录投稿时文章归属的用户,搜索选择', dependsOn: { key: 'front_submit_guest_enabled', value: true } },
|
||||
{ key: 'front_submit_captcha', label: '提交人机验证', type: 'switch', default: false, help: '提交正式投稿时校验人机验证(需在安全扩展启用 captcha)', dependsOn: { key: 'front_submit_enabled', value: true } },
|
||||
{ key: 'front_submit_tags', label: '标签输入', type: 'switch', default: true, dependsOn: { key: 'front_submit_enabled', value: true } },
|
||||
{ key: 'post_article_topics_s', label: '专题选择', type: 'switch', default: false, help: '投稿时可选择专题分类', dependsOn: { key: 'front_submit_enabled', value: true } },
|
||||
{ key: 'post_article_custom_filter_s', label: '高级筛选选择', type: 'switch', default: true, help: '投稿时可选择自定义筛选维度值', dependsOn: { key: 'front_submit_enabled', value: true } },
|
||||
|
||||
Reference in New Issue
Block a user