优化插件配置页面布局
This commit is contained in:
parent
e78f382ee4
commit
756c8d5abe
@ -1,19 +1,6 @@
|
||||
<template>
|
||||
<div class="config-form" :data-form-section="section">
|
||||
<p v-if="!groups.length" class="form-empty">当前插件配置不存在,请刷新配置或检查初始化状态。</p>
|
||||
<nav v-if="section === 'base' && groups.length > 1" class="form-sections" aria-label="基础配置分区">
|
||||
<button
|
||||
v-for="group in groups"
|
||||
:key="group.id"
|
||||
type="button"
|
||||
:class="{ active: activeGroup === group.id }"
|
||||
:aria-pressed="activeGroup === group.id"
|
||||
@click="activeGroup = group.id"
|
||||
>
|
||||
{{ group.title }}
|
||||
<span v-if="group.fields.some(field => errors[fieldKey(field)])" class="section-error">待修正</span>
|
||||
</button>
|
||||
</nav>
|
||||
<section
|
||||
v-for="group in groups"
|
||||
:key="group.id"
|
||||
|
||||
@ -5,14 +5,14 @@
|
||||
}
|
||||
|
||||
.settings-header {
|
||||
margin-bottom: 24px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.settings-header h3 {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--pm-text-primary);
|
||||
margin: 0 0 8px 0;
|
||||
margin: 0 0 6px;
|
||||
}
|
||||
|
||||
.settings-desc {
|
||||
@ -22,61 +22,112 @@
|
||||
}
|
||||
|
||||
.plugin-container {
|
||||
flex-grow: 1;
|
||||
border: 1px solid var(--pm-border-color);
|
||||
border-radius: var(--pm-radius-sm);
|
||||
background: var(--pm-bg-secondary);
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.plugin-list {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.plugin-section {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--pm-border-color);
|
||||
border-radius: var(--pm-radius-md);
|
||||
background: var(--pm-bg-secondary);
|
||||
box-shadow: 0 2px 8px rgb(15 23 42 / 4%);
|
||||
}
|
||||
|
||||
.custom-tabs > :deep(.el-tabs__header) {
|
||||
margin-bottom: 0;
|
||||
background: var(--pm-bg-primary);
|
||||
padding: 8px 12px;
|
||||
.plugin-section:hover:not(.is-disabled) {
|
||||
border-color: color-mix(in srgb, var(--pm-primary-color) 28%, var(--pm-border-color));
|
||||
}
|
||||
|
||||
.plugin-section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
min-height: 58px;
|
||||
padding: 10px 16px;
|
||||
border-bottom: 1px solid var(--pm-border-color);
|
||||
background: var(--pm-bg-primary);
|
||||
}
|
||||
|
||||
.custom-tabs > :deep(.el-tabs__header .el-tabs__nav-wrap::after),
|
||||
.custom-tabs > :deep(.el-tabs__header .el-tabs__active-bar) {
|
||||
display: none;
|
||||
.plugin-section-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.custom-tabs > :deep(.el-tabs__header .el-tabs__item) {
|
||||
height: 38px;
|
||||
margin-right: 6px;
|
||||
padding: 0 14px !important;
|
||||
border-radius: 8px;
|
||||
color: var(--pm-text-secondary);
|
||||
font-size: 13px;
|
||||
.plugin-section-index {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
flex: 0 0 30px;
|
||||
border-radius: 9px;
|
||||
color: var(--pm-primary-color);
|
||||
background: color-mix(in srgb, var(--pm-primary-color) 12%, var(--pm-bg-secondary));
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
letter-spacing: .04em;
|
||||
}
|
||||
|
||||
.custom-tabs > :deep(.el-tabs__header .el-tabs__item:hover:not(.is-active)) {
|
||||
.plugin-section-header h4 {
|
||||
overflow: hidden;
|
||||
margin: 0 0 2px;
|
||||
color: var(--pm-text-primary);
|
||||
background: var(--pm-bg-hover);
|
||||
font-size: 15px;
|
||||
font-weight: 650;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.custom-tabs > :deep(.el-tabs__header .el-tabs__item.is-active) {
|
||||
color: var(--settings-accent, var(--pm-primary-color));
|
||||
background: color-mix(in srgb, var(--pm-primary-color) 10%, var(--pm-bg-secondary));
|
||||
.plugin-section-header p {
|
||||
margin: 0;
|
||||
color: var(--pm-text-secondary);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.plugin-section-status {
|
||||
flex: 0 0 auto;
|
||||
padding: 4px 9px;
|
||||
border: 1px solid color-mix(in srgb, #16a34a 22%, var(--pm-border-color));
|
||||
border-radius: 999px;
|
||||
color: #15803d;
|
||||
background: #f0fdf4;
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.plugin-section-status.is-active {
|
||||
font-weight: 600;
|
||||
box-shadow: inset 0 -2px var(--settings-accent, var(--pm-primary-color));
|
||||
}
|
||||
|
||||
.custom-tabs > :deep(.el-tabs__header .el-tabs__item:focus-visible) {
|
||||
outline: 2px solid var(--settings-accent, var(--pm-primary-color));
|
||||
outline-offset: -2px;
|
||||
.plugin-section :deep(button:focus-visible),
|
||||
.plugin-section :deep(input:focus-visible),
|
||||
.plugin-section :deep(textarea:focus-visible),
|
||||
.plugin-section :deep(select:focus-visible) {
|
||||
outline: 2px solid var(--pm-primary-color);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.custom-tabs > :deep(.el-tabs__content) {
|
||||
height: 100%;
|
||||
.plugin-section-content {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.iframe-wrapper {
|
||||
height: calc(100vh - 250px);
|
||||
min-height: 400px;
|
||||
height: 320px;
|
||||
min-height: 240px;
|
||||
background: var(--pm-bg-secondary);
|
||||
}
|
||||
|
||||
iframe {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 0;
|
||||
@ -84,7 +135,32 @@ iframe {
|
||||
|
||||
.empty-state {
|
||||
display: flex;
|
||||
min-height: 260px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 300px;
|
||||
border: 1px dashed var(--pm-border-color);
|
||||
border-radius: var(--pm-radius-md);
|
||||
background: var(--pm-bg-secondary);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.settings-header {
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.plugin-list {
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.plugin-section-header {
|
||||
padding: 10px 12px;
|
||||
}
|
||||
|
||||
.plugin-section-status {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.iframe-wrapper {
|
||||
height: 280px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,11 +17,45 @@ export function usePage() {
|
||||
return aliases[name] || name
|
||||
}
|
||||
|
||||
const pluginEntries = computed(() => Object.entries(pluginList).map(([name, src]) => ({
|
||||
name,
|
||||
src,
|
||||
logicalID: pluginLogicalID(name)
|
||||
})))
|
||||
const pluginEntries = computed(() => {
|
||||
const order = {
|
||||
spam_block: 1,
|
||||
telegram_push: 2,
|
||||
wechat_push: 3
|
||||
}
|
||||
const labels = {
|
||||
spam_block: {
|
||||
label: '垃圾邮件拦截',
|
||||
description: '模型识别与过滤策略'
|
||||
},
|
||||
telegram_push: {
|
||||
label: 'Telegram 推送',
|
||||
description: '邮件通知与消息推送'
|
||||
},
|
||||
wechat_push: {
|
||||
label: '微信推送',
|
||||
description: '插件页面与推送参数'
|
||||
}
|
||||
}
|
||||
|
||||
return Object.entries(pluginList)
|
||||
.map(([name, src]) => {
|
||||
const logicalID = pluginLogicalID(name)
|
||||
const metadata = labels[logicalID] || {
|
||||
label: name,
|
||||
description: '插件配置页面'
|
||||
}
|
||||
|
||||
return {
|
||||
name,
|
||||
src,
|
||||
logicalID,
|
||||
label: metadata.label,
|
||||
description: metadata.description
|
||||
}
|
||||
})
|
||||
.sort((left, right) => (order[left.logicalID] || 99) - (order[right.logicalID] || 99))
|
||||
})
|
||||
|
||||
http.get('/api/v1/plugin/list').then(response => {
|
||||
if (Array.isArray(response.data) && response.data.length > 0) {
|
||||
@ -43,4 +77,4 @@ export function usePage() {
|
||||
pluginEntries,
|
||||
pluginFrames
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,27 +6,42 @@
|
||||
</div>
|
||||
|
||||
<div class="plugin-container">
|
||||
<el-tabs class="custom-tabs">
|
||||
<el-tab-pane v-for="entry in pluginEntries" :key="entry.src" :label="entry.name">
|
||||
<SpamBlockSettings v-if="entry.logicalID === 'spam_block'" />
|
||||
<TelegramSettings v-else-if="entry.logicalID === 'telegram_push'" />
|
||||
<div v-else class="iframe-wrapper">
|
||||
<iframe
|
||||
:src="entry.src"
|
||||
sandbox="allow-scripts"
|
||||
referrerpolicy="no-referrer"
|
||||
ref="pluginFrames"
|
||||
:data-plugin="entry.name"
|
||||
:title="entry.name"
|
||||
/>
|
||||
<div v-if="pluginEntries.length" class="plugin-list">
|
||||
<section
|
||||
v-for="(entry, index) in pluginEntries"
|
||||
:key="entry.src"
|
||||
class="plugin-section"
|
||||
>
|
||||
<header class="plugin-section-header">
|
||||
<div class="plugin-section-title">
|
||||
<span class="plugin-section-index">{{ String(index + 1).padStart(2, '0') }}</span>
|
||||
<div>
|
||||
<h4>{{ entry.label }}</h4>
|
||||
<p>{{ entry.description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<span class="plugin-section-status is-active">已启用</span>
|
||||
</header>
|
||||
|
||||
<div class="plugin-section-content">
|
||||
<SpamBlockSettings v-if="entry.logicalID === 'spam_block'" />
|
||||
<TelegramSettings v-else-if="entry.logicalID === 'telegram_push'" />
|
||||
<div v-else class="iframe-wrapper">
|
||||
<iframe
|
||||
:src="entry.src"
|
||||
sandbox="allow-scripts"
|
||||
referrerpolicy="no-referrer"
|
||||
ref="pluginFrames"
|
||||
:data-plugin="entry.name"
|
||||
:title="entry.name"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane v-if="pluginEntries.length === 0" :label="lang.no_plugins">
|
||||
<div class="empty-state">
|
||||
<el-empty :description="lang.no_plugins_installed" :image-size="120" />
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</section>
|
||||
</div>
|
||||
<div v-else class="empty-state">
|
||||
<el-empty :description="lang.no_plugins_installed" :image-size="120" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user