mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-15 11:58:34 +08:00
refactor(approval-grants-ui): header aligned with project conventions
This commit is contained in:
parent
65cf53779a
commit
b9c5ec03d4
@ -1,20 +1,56 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="settings-section">
|
<div class="settings-section">
|
||||||
|
<!--
|
||||||
|
Header layout follows the McpServers / ToolGuard style: section-header
|
||||||
|
container with btn-secondary / btn-primary native buttons on the right.
|
||||||
|
Icons come from @element-plus/icons-vue per the user's directive, even
|
||||||
|
though the rest of the page uses native CSS buttons — el-icon renders
|
||||||
|
inline cleanly inside a regular button.
|
||||||
|
|
||||||
|
The three actions deliberately sit at different visual weights:
|
||||||
|
- btn-primary "新增策略" → the daily action, dominant.
|
||||||
|
- btn-secondary "刷新" → utility, equal-but-second.
|
||||||
|
- 危险路径 "创建全工具白名单" 折到 "更多 ⋯" 下拉,红色文字 — 不会再
|
||||||
|
以与日常操作并排同等地位的姿态出现。
|
||||||
|
-->
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<div>
|
<div class="section-header__title">
|
||||||
<h2 class="section-title">{{ t('approval.grant.title') }}</h2>
|
<h2 class="section-title">{{ t('approval.grant.title') }}</h2>
|
||||||
|
<el-tag
|
||||||
|
v-if="total > 0"
|
||||||
|
type="warning"
|
||||||
|
size="small"
|
||||||
|
effect="light"
|
||||||
|
round
|
||||||
|
disable-transitions
|
||||||
|
class="active-summary"
|
||||||
|
>
|
||||||
|
{{ t('approval.grant.chipLabel', { count: total }) }}
|
||||||
|
</el-tag>
|
||||||
<p class="section-desc">{{ t('approval.grant.desc') }}</p>
|
<p class="section-desc">{{ t('approval.grant.desc') }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-actions">
|
<div class="section-header__actions">
|
||||||
<el-button :icon="Refresh" plain @click="loadGrants" :loading="loading">
|
<button class="btn-secondary" @click="loadGrants" :disabled="loading">
|
||||||
|
<el-icon :size="14" :class="{ spin: loading }"><Refresh /></el-icon>
|
||||||
{{ t('common.refresh') }}
|
{{ t('common.refresh') }}
|
||||||
</el-button>
|
</button>
|
||||||
<el-button :icon="Plus" type="primary" plain @click="openCreateDialog(false)">
|
<button class="btn-primary" @click="openCreateDialog(false)">
|
||||||
|
<el-icon :size="14"><Plus /></el-icon>
|
||||||
{{ t('approval.grant.createBtn') }}
|
{{ t('approval.grant.createBtn') }}
|
||||||
</el-button>
|
</button>
|
||||||
<el-button :icon="Unlock" type="danger" plain @click="openCreateDialog(true)">
|
<el-dropdown trigger="click" placement="bottom-end" @command="onMoreCommand">
|
||||||
{{ t('approval.grant.createWorkspaceBtn') }}
|
<button class="btn-secondary btn-more" :title="t('common.more')">
|
||||||
</el-button>
|
<el-icon :size="16"><MoreFilled /></el-icon>
|
||||||
|
</button>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item command="workspaceWide" class="danger-item">
|
||||||
|
<el-icon><Unlock /></el-icon>
|
||||||
|
{{ t('approval.grant.createWorkspaceBtn') }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -228,6 +264,7 @@ import { ElMessage, ElMessageBox } from 'element-plus'
|
|||||||
import {
|
import {
|
||||||
Delete,
|
Delete,
|
||||||
Lock,
|
Lock,
|
||||||
|
MoreFilled,
|
||||||
Plus,
|
Plus,
|
||||||
Refresh,
|
Refresh,
|
||||||
Unlock,
|
Unlock,
|
||||||
@ -307,6 +344,12 @@ async function loadGrants() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onMoreCommand(cmd: string | number | object) {
|
||||||
|
if (cmd === 'workspaceWide') {
|
||||||
|
openCreateDialog(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function openCreateDialog(workspaceWide: boolean) {
|
function openCreateDialog(workspaceWide: boolean) {
|
||||||
Object.assign(form, emptyForm())
|
Object.assign(form, emptyForm())
|
||||||
if (workspaceWide) {
|
if (workspaceWide) {
|
||||||
@ -424,10 +467,62 @@ onMounted(loadGrants)
|
|||||||
<style scoped>
|
<style scoped>
|
||||||
@import '@/views/Security/shared.css';
|
@import '@/views/Security/shared.css';
|
||||||
|
|
||||||
.header-actions {
|
/* Title row: title + small summary tag on one line, description below. The
|
||||||
|
tag sits next to the title so the daily-glance question — "is auto-approve
|
||||||
|
active in this workspace right now?" — gets answered without scrolling. */
|
||||||
|
.section-header__title {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px 12px;
|
||||||
|
}
|
||||||
|
.section-header__title .section-title { margin: 0; }
|
||||||
|
.section-header__title .section-desc {
|
||||||
|
flex-basis: 100%;
|
||||||
|
margin: 4px 0 0;
|
||||||
|
}
|
||||||
|
.active-summary {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Header actions: matches McpServers / ToolGuard layout (.section-header__actions
|
||||||
|
+ native .btn-primary / .btn-secondary). Visual hierarchy intentionally
|
||||||
|
primary > secondary > "more …" so the daily action dominates and the
|
||||||
|
workspace-wide rule lives one click deeper. */
|
||||||
|
.section-header__actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
flex-shrink: 0; /* keep buttons from being squeezed by the title block */
|
||||||
|
}
|
||||||
|
.section-header__actions .btn-primary,
|
||||||
|
.section-header__actions .btn-secondary {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
white-space: nowrap; /* prevent labels from wrapping into vertical text */
|
||||||
|
}
|
||||||
|
.btn-more {
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
.spin {
|
||||||
|
animation: spin 0.8s linear infinite;
|
||||||
|
}
|
||||||
|
@keyframes spin {
|
||||||
|
from { transform: rotate(0deg); }
|
||||||
|
to { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Danger items inside the More dropdown — visually distinct red text so the
|
||||||
|
workspace-wide path always feels like a dangerous action even when collapsed
|
||||||
|
into the overflow menu. */
|
||||||
|
:global(.el-dropdown-menu__item.danger-item) {
|
||||||
|
color: var(--mc-danger, #b91c1c);
|
||||||
|
}
|
||||||
|
:global(.el-dropdown-menu__item.danger-item:hover) {
|
||||||
|
background: #fef2f2;
|
||||||
|
color: #991b1b;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scope-id {
|
.scope-id {
|
||||||
|
|||||||
@ -141,7 +141,7 @@
|
|||||||
@click="goAutoApproveSettings"
|
@click="goAutoApproveSettings"
|
||||||
:title="t('approval.grant.title')"
|
:title="t('approval.grant.title')"
|
||||||
>
|
>
|
||||||
🔓
|
<el-icon :size="14"><Unlock /></el-icon>
|
||||||
<span>{{ t('approval.grant.chipLabel', { count: autoApproveSummary.count }) }}</span>
|
<span>{{ t('approval.grant.chipLabel', { count: autoApproveSummary.count }) }}</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@ -219,7 +219,7 @@ import McTooltip from '@/components/common/McTooltip.vue'
|
|||||||
import { useNotificationCenter } from '@/composables/useNotificationCenter'
|
import { useNotificationCenter } from '@/composables/useNotificationCenter'
|
||||||
import { useWorkspaceStore } from '@/stores/useWorkspaceStore'
|
import { useWorkspaceStore } from '@/stores/useWorkspaceStore'
|
||||||
import { applyLocale, currentLocale, type AppLocale } from '@/i18n'
|
import { applyLocale, currentLocale, type AppLocale } from '@/i18n'
|
||||||
import { SwitchButton, Lock } from '@element-plus/icons-vue'
|
import { SwitchButton, Lock, Unlock } from '@element-plus/icons-vue'
|
||||||
import ChangePasswordDialog from '@/components/ChangePasswordDialog.vue'
|
import ChangePasswordDialog from '@/components/ChangePasswordDialog.vue'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user