release(main): 同步 dev 最新候选改动
Storage Persistence Guard / storage-persistence-audit (push) Has been cancelled
Client Quality Gates / Shared client and Web (push) Has been cancelled
Client Quality Gates / macOS Desktop (push) Has been cancelled
Client Quality Gates / Shared client and Web (pull_request) Has been cancelled
Client Quality Gates / macOS Desktop (pull_request) Has been cancelled
Storage Persistence Guard / storage-persistence-audit (pull_request) Has been cancelled
Storage Persistence Guard / storage-persistence-audit (push) Has been cancelled
Client Quality Gates / Shared client and Web (push) Has been cancelled
Client Quality Gates / macOS Desktop (push) Has been cancelled
Client Quality Gates / Shared client and Web (pull_request) Has been cancelled
Client Quality Gates / macOS Desktop (pull_request) Has been cancelled
Storage Persistence Guard / storage-persistence-audit (pull_request) Has been cancelled
This commit is contained in:
@@ -2,17 +2,20 @@
|
||||
<div class="template-selector">
|
||||
<div class="selector-header">
|
||||
<div class="selector-title-group">
|
||||
<h3>角色权限概览</h3>
|
||||
<h3>角色策略概览</h3>
|
||||
</div>
|
||||
<el-tag effect="plain" size="small" type="info" round>{{ templates.length }} 个角色</el-tag>
|
||||
</div>
|
||||
|
||||
<div class="template-grid">
|
||||
<div
|
||||
<button
|
||||
v-for="template in sortedTemplates"
|
||||
:key="template.id"
|
||||
type="button"
|
||||
class="template-card"
|
||||
:class="{ inactive: template.category && !isRoleActive(template.category) }"
|
||||
:disabled="!template.category"
|
||||
:aria-label="template.category ? `配置 ${template.name} 权限` : template.name"
|
||||
@click="template.category && emit('edit-role', template.category!)"
|
||||
>
|
||||
<div class="card-top">
|
||||
@@ -21,7 +24,9 @@
|
||||
<span class="card-name">{{ template.name }}</span>
|
||||
<span v-if="template.description" class="card-desc">{{ template.description }}</span>
|
||||
</div>
|
||||
<el-tag v-if="!template.is_system" size="small" effect="dark" round type="success" class="card-badge">自定义</el-tag>
|
||||
<span :class="['card-status', isRoleActive(template.category || '') ? 'card-status--active' : 'card-status--inactive']">
|
||||
{{ isRoleActive(template.category || '') ? '使用中' : '未启用' }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="card-stats">
|
||||
<template v-if="props.currentPermissions && template.category && props.currentPermissions[template.category]">
|
||||
@@ -31,11 +36,11 @@
|
||||
<div class="stat-numbers">
|
||||
<span class="stat enabled">
|
||||
<span class="stat-dot green" />
|
||||
{{ countCurrentEnabled(template.category) }} 启用
|
||||
{{ countCurrentEnabled(template.category) }} 已授权
|
||||
</span>
|
||||
<span class="stat disabled">
|
||||
<span class="stat-dot red" />
|
||||
{{ countCurrentDisabled(template.category) }} 禁用
|
||||
{{ countCurrentDisabled(template.category) }} 未授权
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
@@ -46,16 +51,17 @@
|
||||
<div class="stat-numbers">
|
||||
<span class="stat enabled">
|
||||
<span class="stat-dot green" />
|
||||
{{ countEnabled(template) }} 启用
|
||||
{{ countEnabled(template) }} 已授权
|
||||
</span>
|
||||
<span class="stat disabled">
|
||||
<span class="stat-dot red" />
|
||||
{{ countDisabled(template) }} 禁用
|
||||
{{ countDisabled(template) }} 未授权
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-action">配置权限 <span aria-hidden="true">→</span></div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -94,10 +100,10 @@ const sortedTemplates = computed(() =>
|
||||
|
||||
const roleIcon = (category: string | null) => {
|
||||
const icons: Record<string, string> = {
|
||||
PM: "👑", CRA: "📋", PV: "🔍",
|
||||
QA: "🏥", CTA: "📦",
|
||||
PM: "PM", CRA: "CRA", PV: "PV",
|
||||
QA: "QA", CTA: "CTA",
|
||||
};
|
||||
return icons[category ?? ""] ?? "📄";
|
||||
return icons[category ?? ""] ?? "—";
|
||||
};
|
||||
|
||||
const isRoleActive = (role: string) => props.activeRoles?.includes(role) ?? false;
|
||||
@@ -155,38 +161,45 @@ watch(() => props.refreshKey, loadTemplates);
|
||||
|
||||
<style scoped>
|
||||
.template-selector {
|
||||
padding: 8px 0 12px;
|
||||
padding: 4px 0 6px;
|
||||
}
|
||||
|
||||
.selector-header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 16px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.selector-title-group h3 {
|
||||
margin: 0 0 4px;
|
||||
font-size: 15px;
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #1a2332;
|
||||
}
|
||||
|
||||
.template-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
||||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||
align-items: start;
|
||||
gap: 12px;
|
||||
margin-bottom: 12px;
|
||||
gap: 8px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.template-card {
|
||||
position: relative;
|
||||
padding: 12px 14px;
|
||||
width: 100%;
|
||||
min-height: 122px;
|
||||
padding: 8px 10px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 12px;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
cursor: pointer;
|
||||
color: inherit;
|
||||
font: inherit;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: left;
|
||||
transition: all 0.2s ease;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
@@ -197,11 +210,19 @@ watch(() => props.refreshKey, loadTemplates);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.template-card:focus-visible {
|
||||
outline: 3px solid rgba(59, 130, 246, 0.25);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.template-card:disabled {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.template-card.inactive {
|
||||
border-style: dashed;
|
||||
border-color: #e2e8f0;
|
||||
background: #fafbfc;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.template-card.inactive:hover {
|
||||
@@ -213,12 +234,21 @@ watch(() => props.refreshKey, loadTemplates);
|
||||
.card-top {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
margin-bottom: 12px;
|
||||
gap: 7px;
|
||||
margin-bottom: 7px;
|
||||
}
|
||||
|
||||
.card-icon {
|
||||
font-size: 24px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 7px;
|
||||
background: #eef5fa;
|
||||
color: #315f7a;
|
||||
font-size: 9px;
|
||||
font-weight: 800;
|
||||
line-height: 1;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
@@ -232,7 +262,7 @@ watch(() => props.refreshKey, loadTemplates);
|
||||
}
|
||||
|
||||
.card-name {
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #1a2332;
|
||||
}
|
||||
@@ -242,13 +272,49 @@ watch(() => props.refreshKey, loadTemplates);
|
||||
}
|
||||
|
||||
.card-desc {
|
||||
font-size: 11px;
|
||||
font-size: 10px;
|
||||
color: #94a3b8;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.card-status {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
padding: 2px 5px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 999px;
|
||||
font-size: 9.5px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.card-status--active {
|
||||
border-color: #b9ddcc;
|
||||
background: #edf8f2;
|
||||
color: #267054;
|
||||
}
|
||||
|
||||
.card-status--inactive {
|
||||
border-color: #dbe3ea;
|
||||
background: #f5f7f9;
|
||||
color: #7a8997;
|
||||
}
|
||||
|
||||
.card-action {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 4px;
|
||||
margin-top: auto;
|
||||
padding-top: 6px;
|
||||
border-top: 1px solid #edf1f5;
|
||||
color: #416b83;
|
||||
font-size: 10.5px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.card-badge {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
@@ -256,11 +322,11 @@ watch(() => props.refreshKey, loadTemplates);
|
||||
.card-stats {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.stat-bar {
|
||||
height: 4px;
|
||||
height: 3px;
|
||||
border-radius: 2px;
|
||||
background: #f1f5f9;
|
||||
overflow: hidden;
|
||||
@@ -275,20 +341,20 @@ watch(() => props.refreshKey, loadTemplates);
|
||||
|
||||
.stat-numbers {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.stat {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
font-size: 12px;
|
||||
font-size: 10.5px;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.stat-dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
@@ -300,4 +366,22 @@ watch(() => props.refreshKey, loadTemplates);
|
||||
.template-card.inactive .stat-dot.red { background: #cbd5e1; }
|
||||
.template-card.inactive .stat-fill { background: #cbd5e1; }
|
||||
|
||||
@media (max-width: 1180px) {
|
||||
.template-grid {
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 920px) {
|
||||
.template-grid {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.template-grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user