fix: optimize sso integration layout
This commit is contained in:
@@ -299,30 +299,39 @@ function showError(error: unknown, fallback: string) {
|
|||||||
<el-button :loading="savingUrl" @click="savePublicUrl">保存地址</el-button>
|
<el-button :loading="savingUrl" @click="savePublicUrl">保存地址</el-button>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<el-tabs :model-value="activeTab" @tab-change="switchTab">
|
<el-tabs class="sso-tabs" :model-value="activeTab" @tab-change="switchTab">
|
||||||
<el-tab-pane label="接入应用" name="applications">
|
<el-tab-pane label="接入应用" name="applications">
|
||||||
<el-table :data="clients" stripe empty-text="还没有接入应用">
|
<div class="sso-table-shell">
|
||||||
<el-table-column prop="name" label="应用名称" min-width="180" />
|
<el-table :data="clients" stripe empty-text="还没有接入应用" table-layout="fixed" style="width: 100%">
|
||||||
<el-table-column prop="appId" label="应用ID" min-width="180" />
|
<el-table-column label="应用" min-width="170">
|
||||||
<el-table-column label="回跳地址" min-width="240" show-overflow-tooltip>
|
|
||||||
<template #default="{ row }">{{ row.redirectUris.join("、") || "未配置" }}</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="identityCount" label="绑定用户" width="100" align="center" />
|
|
||||||
<el-table-column label="新用户策略" min-width="190">
|
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
|
<div class="sso-app-cell">
|
||||||
|
<strong>{{ row.name }}</strong>
|
||||||
|
<span>{{ row.appId }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="账号策略" min-width="170">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div class="sso-policy-cell">
|
||||||
<span v-if="row.allowAutoRegister">自动注册 · {{ row.defaultEntitlementPlanName || "未配置权益" }}</span>
|
<span v-if="row.allowAutoRegister">自动注册 · {{ row.defaultEntitlementPlanName || "未配置权益" }}</span>
|
||||||
<span v-else>仅已有学员</span>
|
<span v-else>仅已有学员</span>
|
||||||
|
<small>已绑定 {{ row.identityCount }} 人</small>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="最近使用" width="180">
|
<el-table-column label="回跳地址" min-width="170" show-overflow-tooltip>
|
||||||
<template #default="{ row }">{{ formatTime(row.lastUsedAt) }}</template>
|
<template #default="{ row }">{{ row.redirectUris.join("、") || "未配置" }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="状态" width="100" align="center">
|
<el-table-column label="运行状态" width="148">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-switch v-model="row.status" :active-value="1" :inactive-value="0" @change="toggleClient(row)" />
|
<div class="sso-status-cell">
|
||||||
|
<div><el-switch v-model="row.status" :active-value="1" :inactive-value="0" @change="toggleClient(row)" /><span>{{ row.status === 1 ? "已启用" : "已停用" }}</span></div>
|
||||||
|
<small>最近使用:{{ formatTime(row.lastUsedAt) }}</small>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" width="190" fixed="right" align="center">
|
<el-table-column label="操作" width="150" align="right">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<div class="sso-row-actions">
|
<div class="sso-row-actions">
|
||||||
<el-button size="small" @click="openEditDialog(row)">编辑</el-button>
|
<el-button size="small" @click="openEditDialog(row)">编辑</el-button>
|
||||||
@@ -331,6 +340,22 @@ function showError(error: unknown, fallback: string) {
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
</div>
|
||||||
|
<div class="sso-app-cards">
|
||||||
|
<el-empty v-if="!clients.length" description="还没有接入应用" :image-size="72" />
|
||||||
|
<article v-for="client in clients" v-else :key="client.id" class="sso-app-card">
|
||||||
|
<div class="sso-app-card-head">
|
||||||
|
<div class="sso-app-cell"><strong>{{ client.name }}</strong><span>{{ client.appId }}</span></div>
|
||||||
|
<div class="sso-card-status"><el-switch v-model="client.status" :active-value="1" :inactive-value="0" @change="toggleClient(client)" /><span>{{ client.status === 1 ? "已启用" : "已停用" }}</span></div>
|
||||||
|
</div>
|
||||||
|
<dl>
|
||||||
|
<div><dt>账号策略</dt><dd>{{ client.allowAutoRegister ? `自动注册 · ${client.defaultEntitlementPlanName || "未配置权益"}` : "仅已有学员" }}(已绑定 {{ client.identityCount }} 人)</dd></div>
|
||||||
|
<div><dt>回跳地址</dt><dd>{{ client.redirectUris.join("、") || "未配置" }}</dd></div>
|
||||||
|
<div><dt>最近使用</dt><dd>{{ formatTime(client.lastUsedAt) }}</dd></div>
|
||||||
|
</dl>
|
||||||
|
<div class="sso-card-actions"><el-button size="small" @click="openEditDialog(client)">编辑</el-button><el-button size="small" @click="rotateSecret(client)">更新密钥</el-button></div>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
<el-tab-pane label="账号绑定" name="identities">
|
<el-tab-pane label="账号绑定" name="identities">
|
||||||
@@ -386,28 +411,50 @@ function showError(error: unknown, fallback: string) {
|
|||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
|
|
||||||
<el-dialog v-model="clientDialogOpen" :title="dialogTitle" width="620px" destroy-on-close>
|
<el-dialog v-model="clientDialogOpen" class="sso-client-dialog" :title="dialogTitle" width="min(720px, calc(100vw - 32px))" destroy-on-close align-center>
|
||||||
<el-form label-position="top">
|
<el-form class="sso-client-form" label-position="top">
|
||||||
|
<section class="sso-form-section">
|
||||||
|
<div class="sso-section-heading">
|
||||||
|
<strong>基本信息</strong>
|
||||||
|
<span>用于识别接入方,应用 ID 创建后不可修改。</span>
|
||||||
|
</div>
|
||||||
<div class="sso-form-grid">
|
<div class="sso-form-grid">
|
||||||
<el-form-item label="应用名称"><el-input v-model="clientForm.name" placeholder="例如:学员服务 App" /></el-form-item>
|
<el-form-item label="应用名称"><el-input v-model="clientForm.name" placeholder="例如:学员服务 App" /></el-form-item>
|
||||||
<el-form-item label="应用ID"><el-input v-model="clientForm.appId" :disabled="Boolean(editingClientId)" placeholder="例如:student-app" /></el-form-item>
|
<el-form-item label="应用 ID"><el-input v-model="clientForm.appId" :disabled="Boolean(editingClientId)" placeholder="例如:student-app" /></el-form-item>
|
||||||
</div>
|
</div>
|
||||||
<el-form-item label="允许回跳地址">
|
</section>
|
||||||
<el-input v-model="clientForm.redirectUrisText" type="textarea" :rows="4" placeholder="每行一个完整 HTTPS 地址;不需要回跳时可以留空" />
|
<section class="sso-form-section">
|
||||||
|
<div class="sso-section-heading">
|
||||||
|
<strong>登录后回跳</strong>
|
||||||
|
<span>每行一个完整 HTTPS 地址,不需要回跳时可以留空。</span>
|
||||||
|
</div>
|
||||||
|
<el-form-item class="sso-redirect-field" label="允许回跳地址">
|
||||||
|
<el-input v-model="clientForm.redirectUrisText" type="textarea" :rows="3" placeholder="https://student.example.com/home" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<div class="sso-auto-register">
|
</section>
|
||||||
<div>
|
<section class="sso-form-section">
|
||||||
<strong>自动注册新学员</strong>
|
<div class="sso-section-heading">
|
||||||
<p>可信应用传入本地尚不存在的已验证手机号和姓名时,自动创建学员并分配默认权益。</p>
|
<strong>访问与账号策略</strong>
|
||||||
|
<span>控制应用是否可用,以及首次进入时如何处理新学员。</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="sso-setting-list">
|
||||||
|
<div class="sso-setting-row">
|
||||||
|
<div><strong>启用应用</strong><span>停用后,该应用将无法继续申请或使用免登录。</span></div>
|
||||||
|
<el-switch v-model="clientForm.status" :active-value="1" :inactive-value="0" />
|
||||||
|
</div>
|
||||||
|
<div class="sso-setting-row">
|
||||||
|
<div><strong>自动注册新学员</strong><span>传入已验证手机号和姓名时,可自动创建账号并分配权益。</span></div>
|
||||||
<el-switch v-model="clientForm.allowAutoRegister" />
|
<el-switch v-model="clientForm.allowAutoRegister" />
|
||||||
</div>
|
</div>
|
||||||
<el-form-item v-if="clientForm.allowAutoRegister" label="默认权益版本">
|
<div v-if="clientForm.allowAutoRegister" class="sso-entitlement-setting">
|
||||||
<el-select v-model="clientForm.defaultEntitlementPlanId" placeholder="请选择自动注册学员的权益" style="width: 100%">
|
<el-form-item label="新学员默认权益">
|
||||||
|
<el-select v-model="clientForm.defaultEntitlementPlanId" placeholder="请选择权益版本" style="width: 100%">
|
||||||
<el-option v-for="plan in entitlementPlans" :key="plan.id" :label="plan.name" :value="plan.id" />
|
<el-option v-for="plan in entitlementPlans" :key="plan.id" :label="plan.name" :value="plan.id" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="启用应用"><el-switch v-model="clientForm.status" :active-value="1" :inactive-value="0" /></el-form-item>
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button @click="clientDialogOpen = false">取消</el-button>
|
<el-button @click="clientDialogOpen = false">取消</el-button>
|
||||||
@@ -430,21 +477,382 @@ function showError(error: unknown, fallback: string) {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.sso-page { display: grid; gap: 20px; }
|
.sso-page {
|
||||||
.sso-public-url { display: grid; grid-template-columns: minmax(250px, 1fr) minmax(320px, 1.3fr) auto; gap: 18px; align-items: center; padding: 20px 22px; border: 1px solid #dce8e4; border-radius: 16px; background: #fff; }
|
container-type: inline-size;
|
||||||
.sso-public-url p { margin: 6px 0 0; color: #71817d; font-size: 13px; }
|
display: grid;
|
||||||
.sso-row-actions { display: flex; justify-content: center; gap: 8px; white-space: nowrap; }
|
gap: 20px;
|
||||||
.sso-filters { display: flex; gap: 12px; margin-bottom: 16px; }
|
width: 100%;
|
||||||
.sso-filters .el-select { width: 190px; }
|
min-width: 0;
|
||||||
.sso-filters .el-input { max-width: 360px; }
|
max-width: 100%;
|
||||||
.sso-form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
|
overflow: hidden;
|
||||||
.sso-auto-register { display: flex; align-items: center; justify-content: space-between; gap: 24px; margin-bottom: 18px; padding: 16px; border: 1px solid #dce8e4; border-radius: 12px; background: #f8fbfa; }
|
}
|
||||||
.sso-auto-register p { margin: 6px 0 0; color: #71817d; font-size: 13px; line-height: 1.6; }
|
|
||||||
.sso-secret-block { display: grid; grid-template-columns: 90px minmax(0, 1fr); gap: 12px 16px; align-items: start; margin-top: 20px; }
|
.sso-public-url {
|
||||||
.sso-secret-block label { color: #6c7d79; }
|
display: grid;
|
||||||
.sso-secret-block code { padding: 10px 12px; overflow-wrap: anywhere; border-radius: 8px; background: #f3f7f5; color: #173d34; }
|
grid-template-columns: minmax(230px, 1fr) minmax(280px, 1.2fr) auto;
|
||||||
@media (max-width: 980px) {
|
gap: 18px;
|
||||||
.sso-public-url { grid-template-columns: 1fr; }
|
align-items: center;
|
||||||
.sso-filters { flex-wrap: wrap; }
|
min-width: 0;
|
||||||
|
padding: 20px 22px;
|
||||||
|
border: 1px solid #dce8e4;
|
||||||
|
border-radius: 16px;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-public-url > *,
|
||||||
|
.sso-tabs,
|
||||||
|
.sso-table-shell {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-public-url p {
|
||||||
|
margin: 6px 0 0;
|
||||||
|
color: #71817d;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.sso-tabs .el-tabs__content),
|
||||||
|
:deep(.sso-tabs .el-tab-pane) {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-table-shell {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid #e1eae7;
|
||||||
|
border-radius: 14px;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-app-cell,
|
||||||
|
.sso-policy-cell,
|
||||||
|
.sso-status-cell {
|
||||||
|
display: grid;
|
||||||
|
gap: 5px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-app-cell strong,
|
||||||
|
.sso-policy-cell span {
|
||||||
|
overflow: hidden;
|
||||||
|
color: #253b35;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-app-cell span,
|
||||||
|
.sso-policy-cell small,
|
||||||
|
.sso-status-cell small {
|
||||||
|
overflow: hidden;
|
||||||
|
color: #80908b;
|
||||||
|
font-size: 12px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-status-cell > div,
|
||||||
|
.sso-card-status {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: center;
|
||||||
|
color: #4f625c;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-row-actions,
|
||||||
|
.sso-card-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 8px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-row-actions :deep(.el-button + .el-button),
|
||||||
|
.sso-card-actions :deep(.el-button + .el-button) {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-app-cards {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-filters {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-filters .el-select {
|
||||||
|
width: 190px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-filters .el-input {
|
||||||
|
max-width: 360px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-client-form {
|
||||||
|
display: grid;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-form-section {
|
||||||
|
min-width: 0;
|
||||||
|
padding: 18px 20px;
|
||||||
|
border: 1px solid #e0e9e6;
|
||||||
|
border-radius: 14px;
|
||||||
|
background: #fbfcfc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-section-heading {
|
||||||
|
display: grid;
|
||||||
|
gap: 4px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-section-heading strong {
|
||||||
|
color: #203a33;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-section-heading span {
|
||||||
|
color: #7a8b86;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.55;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-form-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-client-form :deep(.el-form-item) {
|
||||||
|
min-width: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-client-form :deep(.el-form-item__label) {
|
||||||
|
height: auto;
|
||||||
|
padding-bottom: 7px;
|
||||||
|
color: #465a54;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-client-form :deep(.el-input__wrapper) {
|
||||||
|
min-height: 42px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-redirect-field :deep(.el-textarea__inner) {
|
||||||
|
min-height: 82px !important;
|
||||||
|
padding: 11px 13px;
|
||||||
|
line-height: 1.55;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-setting-list {
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid #e0e9e6;
|
||||||
|
border-radius: 12px;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-setting-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr) auto;
|
||||||
|
gap: 24px;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 72px;
|
||||||
|
padding: 14px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-setting-row + .sso-setting-row,
|
||||||
|
.sso-entitlement-setting {
|
||||||
|
border-top: 1px solid #e7eeec;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-setting-row > div {
|
||||||
|
display: grid;
|
||||||
|
gap: 5px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-setting-row strong {
|
||||||
|
color: #324942;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-setting-row span {
|
||||||
|
color: #7a8b86;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-entitlement-setting {
|
||||||
|
padding: 14px 16px 16px;
|
||||||
|
background: #f5faf8;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.sso-client-dialog) {
|
||||||
|
max-height: calc(100vh - 32px);
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.sso-client-dialog .el-dialog__header) {
|
||||||
|
margin-right: 0;
|
||||||
|
padding: 22px 26px 17px;
|
||||||
|
border-bottom: 1px solid #e7eeec;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.sso-client-dialog .el-dialog__title) {
|
||||||
|
color: #1f3932;
|
||||||
|
font-size: 21px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.sso-client-dialog .el-dialog__body) {
|
||||||
|
max-height: calc(100vh - 178px);
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 20px 26px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.sso-client-dialog .el-dialog__footer) {
|
||||||
|
padding: 16px 26px 20px;
|
||||||
|
border-top: 1px solid #e7eeec;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-secret-block {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 90px minmax(0, 1fr);
|
||||||
|
gap: 12px 16px;
|
||||||
|
align-items: start;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-secret-block label {
|
||||||
|
color: #6c7d79;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-secret-block code {
|
||||||
|
padding: 10px 12px;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #f3f7f5;
|
||||||
|
color: #173d34;
|
||||||
|
}
|
||||||
|
|
||||||
|
@container (max-width: 940px) {
|
||||||
|
.sso-public-url {
|
||||||
|
grid-template-columns: minmax(0, 1fr) auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-public-url > div {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-table-shell {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-app-cards {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-app-cards > .el-empty {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-app-card {
|
||||||
|
display: grid;
|
||||||
|
gap: 16px;
|
||||||
|
min-width: 0;
|
||||||
|
padding: 18px;
|
||||||
|
border: 1px solid #dfe9e6;
|
||||||
|
border-radius: 14px;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-app-card-head {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-card-status {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-app-card dl {
|
||||||
|
display: grid;
|
||||||
|
gap: 12px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-app-card dl > div {
|
||||||
|
display: grid;
|
||||||
|
gap: 4px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-app-card dt {
|
||||||
|
color: #82918d;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-app-card dd {
|
||||||
|
margin: 0;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
color: #41554f;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.55;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container (max-width: 660px) {
|
||||||
|
.sso-app-cards,
|
||||||
|
.sso-public-url {
|
||||||
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-public-url > div {
|
||||||
|
grid-column: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-public-url .el-button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-filters {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-filters .el-select,
|
||||||
|
.sso-filters .el-input {
|
||||||
|
width: 100%;
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 760px) {
|
||||||
|
.sso-form-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-form-section {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.sso-client-dialog .el-dialog__header),
|
||||||
|
:deep(.sso-client-dialog .el-dialog__body),
|
||||||
|
:deep(.sso-client-dialog .el-dialog__footer) {
|
||||||
|
padding-right: 18px;
|
||||||
|
padding-left: 18px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -162,3 +162,48 @@ final result: passed
|
|||||||
- 未发现剩余 P0、P1、P2 问题。
|
- 未发现剩余 P0、P1、P2 问题。
|
||||||
|
|
||||||
final result: passed
|
final result: passed
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 应用接入页面设计验收
|
||||||
|
|
||||||
|
- 源视觉依据:`/var/folders/hn/vg5lxj0j60d2cp0k6vv_k0mw0000gn/T/codex-clipboard-70a7e9c2-c058-4665-b236-e73ccef6d2b0.png`
|
||||||
|
- 浏览器实现截图:`development_records/ui-evidence/sso-integration-2026-08-03/implementation.png`
|
||||||
|
- 同屏对比图:`development_records/ui-evidence/sso-integration-2026-08-03/comparison.png`
|
||||||
|
- 验收地址:`http://localhost:5174/`
|
||||||
|
- 状态:管理员已登录,进入“应用接入”,打开“新增接入应用”弹窗
|
||||||
|
- 视口:1380 × 900 CSS px,设备像素密度 1
|
||||||
|
- 源图像素:1380 × 1026;实现图像素:1380 × 900
|
||||||
|
- 归一化方式:保持两张图 1380 px 等宽,不缩放;对比图仅将实现图底部补白至 1026 px,避免密度或缩放造成误判
|
||||||
|
|
||||||
|
## 全视图对比证据
|
||||||
|
|
||||||
|
- 原界面将基础字段、回跳地址、自动注册和启用开关连续堆叠,字段说明与控件边界不清,底部留白失衡。
|
||||||
|
- 新界面拆分为“基本信息”“登录后回跳”“访问与账号策略”三个语义区,主次关系、上下间距与操作路径清晰。
|
||||||
|
- 应用列表合并重复维度并限制列宽;在容器宽度不足时切换为卡片布局,不再依赖横向滚动。
|
||||||
|
|
||||||
|
## 聚焦区域对比证据
|
||||||
|
|
||||||
|
- 表单区:输入框高度、标签间距、两列表单对齐、文本域高度均保持一致;说明文字从占位符中分离,阅读层级更稳定。
|
||||||
|
- 策略区:“启用应用”和“自动注册新学员”采用同一行式设置组件,开关右对齐;自动注册开启后才展示默认权益,减少无关内容。
|
||||||
|
- 标签页:在 1380 px 视口下,页面、标签内容和表格的 `scrollWidth` 均等于 `clientWidth`;无页面级横向滚动。
|
||||||
|
|
||||||
|
## 发现项
|
||||||
|
|
||||||
|
- 未发现仍需处理的 P0、P1 或 P2 问题。
|
||||||
|
- P3:后台整体框架在小于约 1180 px 的窗口仍有全局最小宽度限制;该行为不是本次应用接入页面引入,且不影响当前桌面端验收范围。
|
||||||
|
|
||||||
|
## 交互与运行验证
|
||||||
|
|
||||||
|
- 已验证进入应用接入、切换“接入应用 / 账号绑定 / 登录审计”、打开与关闭新增应用弹窗;自动注册开关可正常显示和隐藏默认权益配置。
|
||||||
|
- 已验证 1380 × 900 与 1280 × 720 桌面视口;弹窗内容过高时仅弹窗正文区域滚动,头部和底部操作保持可见。
|
||||||
|
- 已检查浏览器控制台,新增错误和警告为 0。
|
||||||
|
- 已执行前端类型检查与生产构建,构建通过。
|
||||||
|
|
||||||
|
## 对比历史
|
||||||
|
|
||||||
|
1. 初始问题:弹窗内容拥挤、策略控件分散;应用列表固定列宽总和过大,引发横向滚动。
|
||||||
|
2. 修复:分区重构弹窗;合并列表信息列;补充容器宽度约束和窄容器卡片布局。
|
||||||
|
3. 复验:同屏对比中信息层级与留白已改善;桌面视口下页面级横向滚动已消除。
|
||||||
|
|
||||||
|
final result: passed
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 341 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 64 KiB |
Reference in New Issue
Block a user