refactor(account): 账户弹窗与退出确认改用页面通用弹窗外壳
This commit is contained in:
@@ -1,137 +1,300 @@
|
||||
<script setup lang="ts">
|
||||
import { onBeforeUnmount, watch } from 'vue'
|
||||
|
||||
import { useAccountPanel } from '@/composables/useAccountPanel'
|
||||
|
||||
const { panel, profile, loading, saving, error, contacts, password, strength, open, close, save } =
|
||||
useAccountPanel()
|
||||
defineExpose({ open })
|
||||
|
||||
/**
|
||||
* 弹窗挂在 body 上,卡片本身不持有焦点,`@keydown` 收不到按键,
|
||||
* 所以 Esc 关闭(原先由 el-dialog 的 `close-on-press-escape` 提供)要挂在 document 上。
|
||||
* `close()` 内部已挡掉保存中,这里不必再判一次。
|
||||
*/
|
||||
function handleEscape(event: KeyboardEvent) {
|
||||
if (event.key === 'Escape') close()
|
||||
}
|
||||
|
||||
watch(panel, (value) => {
|
||||
if (value) document.addEventListener('keydown', handleEscape)
|
||||
else document.removeEventListener('keydown', handleEscape)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => document.removeEventListener('keydown', handleEscape))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dialog
|
||||
:model-value="panel !== null"
|
||||
:title="panel === 'profile' ? '个人信息' : '修改密码'"
|
||||
:width="panel === 'profile' ? '560px' : '460px'"
|
||||
class="account-dialog"
|
||||
append-to-body
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="!saving"
|
||||
:show-close="!saving"
|
||||
@update:model-value="close"
|
||||
>
|
||||
<form id="account-form" @submit.prevent="save">
|
||||
<template v-if="panel === 'profile'">
|
||||
<p class="hint">基本信息由信息科统一维护,联系方式可自助修改</p>
|
||||
<p v-if="loading" role="status">正在加载个人信息…</p>
|
||||
<div v-else-if="profile" class="profile-grid">
|
||||
<label class="full"
|
||||
>ID(员工编号)<input :value="profile.employeeId ?? '—'" disabled
|
||||
/></label>
|
||||
<label>工号<input :value="profile.employeeNo || '—'" disabled /></label>
|
||||
<label>姓名<input :value="profile.displayName" disabled /></label>
|
||||
<label
|
||||
>状态<input
|
||||
:value="{ ENABLED: '启用', DISABLED: '停用', LOCKED: '锁定' }[profile.status]"
|
||||
disabled
|
||||
/></label>
|
||||
<label
|
||||
>院区 / 科室<input
|
||||
:value="[profile.campusName || '—', profile.departmentName || '—'].join(' / ')"
|
||||
disabled
|
||||
/></label>
|
||||
<label class="full"
|
||||
>角色 / 岗位<input
|
||||
:value="
|
||||
[
|
||||
profile.roles.map((role) => role.name).join('、'),
|
||||
profile.position,
|
||||
profile.professionalTitle,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' / ') || '—'
|
||||
"
|
||||
disabled
|
||||
/></label>
|
||||
<label
|
||||
>手机号<input
|
||||
v-model="contacts.phone"
|
||||
type="tel"
|
||||
maxlength="32"
|
||||
:disabled="saving"
|
||||
autocomplete="tel"
|
||||
/></label>
|
||||
<label
|
||||
>邮箱<input
|
||||
v-model="contacts.email"
|
||||
type="text"
|
||||
inputmode="email"
|
||||
maxlength="255"
|
||||
:disabled="saving"
|
||||
autocomplete="email"
|
||||
/></label>
|
||||
<p class="hint full">联系方式已脱敏;保持原值不修改,输入完整内容可替换,清空可删除。</p>
|
||||
</div>
|
||||
</template>
|
||||
<div v-else class="password-fields">
|
||||
<p class="hint">12–128 位,须同时包含大小写字母、数字和特殊字符。修改后需重新登录。</p>
|
||||
<label
|
||||
>当前密码<input
|
||||
v-model="password.current"
|
||||
type="password"
|
||||
autocomplete="current-password"
|
||||
maxlength="128"
|
||||
:disabled="saving"
|
||||
/></label>
|
||||
<label
|
||||
>新密码<input
|
||||
v-model="password.next"
|
||||
type="password"
|
||||
autocomplete="new-password"
|
||||
maxlength="128"
|
||||
:disabled="saving"
|
||||
/></label>
|
||||
<p class="hint" aria-live="polite">强度:{{ strength }}</p>
|
||||
<label
|
||||
>确认新密码<input
|
||||
v-model="password.confirm"
|
||||
type="password"
|
||||
autocomplete="new-password"
|
||||
maxlength="128"
|
||||
:disabled="saving"
|
||||
/></label>
|
||||
</div>
|
||||
<p v-if="error" class="error" role="alert">{{ error }}</p>
|
||||
</form>
|
||||
<template #footer>
|
||||
<el-button :disabled="saving" @click="close">取消</el-button>
|
||||
<el-button v-if="panel === 'profile' && !profile && !loading" @click="open('profile')"
|
||||
>重新加载</el-button
|
||||
<!-- 外壳走页面通用弹窗样式(与作废确认、文书预览等同一套 .dialog-* / .action-button),
|
||||
不再用 Element Plus 默认的 el-dialog / el-button —— 那套默认蓝与全局品牌色不一致。 -->
|
||||
<Teleport to="body">
|
||||
<div v-if="panel !== null" class="account-mask" role="presentation">
|
||||
<section
|
||||
class="account-card"
|
||||
:class="panel === 'profile' ? 'account-card--profile' : 'account-card--password'"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="account-dialog-title"
|
||||
>
|
||||
<el-button
|
||||
type="primary"
|
||||
native-type="submit"
|
||||
form="account-form"
|
||||
:loading="saving"
|
||||
:disabled="loading || (panel === 'profile' && !profile)"
|
||||
>{{ panel === 'profile' ? '保存' : '确认修改' }}</el-button
|
||||
>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<header class="account-header">
|
||||
<strong id="account-dialog-title">
|
||||
{{ panel === 'profile' ? '个人信息' : '修改密码' }}
|
||||
</strong>
|
||||
<button
|
||||
v-if="!saving"
|
||||
type="button"
|
||||
class="account-close"
|
||||
aria-label="关闭"
|
||||
@click="close"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<form id="account-form" class="account-body" @submit.prevent="save">
|
||||
<template v-if="panel === 'profile'">
|
||||
<p class="hint">基本信息由信息科统一维护,联系方式可自助修改</p>
|
||||
<p v-if="loading" role="status">正在加载个人信息…</p>
|
||||
<div v-else-if="profile" class="profile-grid">
|
||||
<label class="full"
|
||||
>ID(员工编号)<input :value="profile.employeeId ?? '—'" disabled
|
||||
/></label>
|
||||
<label>工号<input :value="profile.employeeNo || '—'" disabled /></label>
|
||||
<label>姓名<input :value="profile.displayName" disabled /></label>
|
||||
<label
|
||||
>状态<input
|
||||
:value="{ ENABLED: '启用', DISABLED: '停用', LOCKED: '锁定' }[profile.status]"
|
||||
disabled
|
||||
/></label>
|
||||
<label
|
||||
>院区 / 科室<input
|
||||
:value="[profile.campusName || '—', profile.departmentName || '—'].join(' / ')"
|
||||
disabled
|
||||
/></label>
|
||||
<label class="full"
|
||||
>角色 / 岗位<input
|
||||
:value="
|
||||
[
|
||||
profile.roles.map((role) => role.name).join('、'),
|
||||
profile.position,
|
||||
profile.professionalTitle,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' / ') || '—'
|
||||
"
|
||||
disabled
|
||||
/></label>
|
||||
<label
|
||||
>手机号<input
|
||||
v-model="contacts.phone"
|
||||
type="tel"
|
||||
maxlength="32"
|
||||
:disabled="saving"
|
||||
autocomplete="tel"
|
||||
/></label>
|
||||
<label
|
||||
>邮箱<input
|
||||
v-model="contacts.email"
|
||||
type="text"
|
||||
inputmode="email"
|
||||
maxlength="255"
|
||||
:disabled="saving"
|
||||
autocomplete="email"
|
||||
/></label>
|
||||
<p class="hint full">
|
||||
联系方式已脱敏;保持原值不修改,输入完整内容可替换,清空可删除。
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
<div v-else class="password-fields">
|
||||
<p class="hint">12–128 位,须同时包含大小写字母、数字和特殊字符。修改后需重新登录。</p>
|
||||
<label
|
||||
>当前密码<input
|
||||
v-model="password.current"
|
||||
type="password"
|
||||
autocomplete="current-password"
|
||||
maxlength="128"
|
||||
:disabled="saving"
|
||||
/></label>
|
||||
<label
|
||||
>新密码<input
|
||||
v-model="password.next"
|
||||
type="password"
|
||||
autocomplete="new-password"
|
||||
maxlength="128"
|
||||
:disabled="saving"
|
||||
/></label>
|
||||
<p class="hint" aria-live="polite">强度:{{ strength }}</p>
|
||||
<label
|
||||
>确认新密码<input
|
||||
v-model="password.confirm"
|
||||
type="password"
|
||||
autocomplete="new-password"
|
||||
maxlength="128"
|
||||
:disabled="saving"
|
||||
/></label>
|
||||
</div>
|
||||
<p v-if="error" class="error" role="alert">{{ error }}</p>
|
||||
</form>
|
||||
|
||||
<footer class="account-footer">
|
||||
<span class="account-spacer" />
|
||||
<button
|
||||
type="button"
|
||||
class="action-button action-button--ghost"
|
||||
:disabled="saving"
|
||||
@click="close"
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
<button
|
||||
v-if="panel === 'profile' && !profile && !loading"
|
||||
type="button"
|
||||
class="action-button action-button--ghost"
|
||||
@click="open('profile')"
|
||||
>
|
||||
重新加载
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
form="account-form"
|
||||
class="action-button"
|
||||
:disabled="saving || loading || (panel === 'profile' && !profile)"
|
||||
>
|
||||
{{ saving ? '处理中…' : panel === 'profile' ? '保存' : '确认修改' }}
|
||||
</button>
|
||||
</footer>
|
||||
</section>
|
||||
</div>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.account-mask {
|
||||
position: fixed;
|
||||
z-index: 50;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
padding: 16px;
|
||||
overflow-y: auto;
|
||||
background: rgb(9 40 52 / 45%);
|
||||
}
|
||||
|
||||
.account-card {
|
||||
display: flex;
|
||||
max-width: 96vw;
|
||||
max-height: calc(100vh - 32px);
|
||||
flex-direction: column;
|
||||
margin: auto;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
border-radius: 14px;
|
||||
box-shadow: var(--sh-modal);
|
||||
animation: account-pop 0.18s ease;
|
||||
}
|
||||
|
||||
.account-card--profile {
|
||||
width: 560px;
|
||||
}
|
||||
|
||||
.account-card--password {
|
||||
width: 460px;
|
||||
}
|
||||
|
||||
.account-header {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
padding: 12px 18px;
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.account-header strong {
|
||||
color: var(--ink);
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.account-close {
|
||||
padding: 0;
|
||||
margin-left: auto;
|
||||
color: var(--mut);
|
||||
font-size: 20px;
|
||||
line-height: 1;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.account-close:hover {
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.account-body {
|
||||
min-height: 0;
|
||||
padding: 12px 18px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.account-footer {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
padding: 10px 18px;
|
||||
background: #f8fbfc;
|
||||
border-top: 1px solid var(--line);
|
||||
border-radius: 0 0 14px 14px;
|
||||
}
|
||||
|
||||
.account-spacer {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.action-button {
|
||||
flex-shrink: 0;
|
||||
padding: 8px 16px;
|
||||
color: #fff;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
background: var(--brand);
|
||||
border-radius: 7px;
|
||||
}
|
||||
|
||||
.action-button:hover:not(:disabled) {
|
||||
background: var(--brand-d);
|
||||
}
|
||||
|
||||
.action-button--ghost {
|
||||
color: var(--brand);
|
||||
background: #fff;
|
||||
border: 1px solid var(--brand);
|
||||
}
|
||||
|
||||
.action-button--ghost:hover:not(:disabled) {
|
||||
background: var(--brand-l);
|
||||
}
|
||||
|
||||
.action-button:disabled {
|
||||
color: #9aabb3;
|
||||
cursor: not-allowed;
|
||||
background: #eef3f5;
|
||||
}
|
||||
|
||||
.profile-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.full {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
label {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
font-size: 12px;
|
||||
color: var(--mut);
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
@@ -140,32 +303,48 @@ input {
|
||||
border-radius: 6px;
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
input:disabled {
|
||||
background: #f3f7f9;
|
||||
}
|
||||
|
||||
input:focus {
|
||||
outline: 2px solid var(--brand);
|
||||
outline-offset: 1px;
|
||||
}
|
||||
|
||||
.hint {
|
||||
margin: 0 0 14px;
|
||||
color: var(--mut);
|
||||
font-size: 12px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.password-fields {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.password-fields .hint {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: var(--err);
|
||||
}
|
||||
:global(.account-dialog) {
|
||||
max-width: calc(100vw - 32px);
|
||||
|
||||
@keyframes account-pop {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.96) translateY(8px);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 520px) {
|
||||
.profile-grid {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import { computed, onBeforeUnmount, ref, watch } from 'vue'
|
||||
import AccountPanel from './AccountPanel.vue'
|
||||
import { RouterLink } from 'vue-router'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
|
||||
import { useLoginStore } from '@/stores/login'
|
||||
|
||||
const loginStore = useLoginStore()
|
||||
const accountPanel = ref<InstanceType<typeof AccountPanel>>()
|
||||
const accountMenuOpen = ref(false)
|
||||
const logoutConfirmOpen = ref(false)
|
||||
const loggingOut = ref(false)
|
||||
const identity = computed(() => {
|
||||
const labels = [
|
||||
@@ -46,6 +46,18 @@ const visibleMenuGroups = computed(() =>
|
||||
.filter((group) => group.items.length),
|
||||
)
|
||||
|
||||
/** 打开二次确认(确认弹窗用页面通用样式,不再走 ElMessageBox 的 Element 默认外观) */
|
||||
function requestLogout() {
|
||||
if (loggingOut.value) return
|
||||
accountMenuOpen.value = false
|
||||
logoutConfirmOpen.value = true
|
||||
}
|
||||
|
||||
function cancelLogout() {
|
||||
if (loggingOut.value) return
|
||||
logoutConfirmOpen.value = false
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出登录:二次确认 → 注销服务端会话 → 清掉本地身份数据 → **整页**回登录页。
|
||||
*
|
||||
@@ -58,21 +70,8 @@ const visibleMenuGroups = computed(() =>
|
||||
* 本地身份数据(token / tokenExpiresAt / userInfo,其中 userInfo 里含 permissions)
|
||||
* 由 `loginStore.logout()` → `clearSession()` → `clearAuthStorage()` 删除。
|
||||
*/
|
||||
async function handleLogout() {
|
||||
async function confirmLogout() {
|
||||
if (loggingOut.value) return
|
||||
accountMenuOpen.value = false
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm('退出后需要重新登录才能继续使用,确定退出当前账号吗?', '退出登录', {
|
||||
confirmButtonText: '退出登录',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
confirmButtonClass: 'logout-confirm-button',
|
||||
})
|
||||
} catch {
|
||||
// 用户点了取消,保持在当前页面
|
||||
return
|
||||
}
|
||||
|
||||
loggingOut.value = true
|
||||
try {
|
||||
@@ -84,6 +83,18 @@ async function handleLogout() {
|
||||
// 用 replace 而不是 assign:退出后按浏览器后退不该再回到已登录页面
|
||||
window.location.replace('/login')
|
||||
}
|
||||
|
||||
/** 确认弹窗挂在 body 上,Esc 关闭要挂 document 才收得到(与 AccountPanel 同一处理) */
|
||||
function handleEscape(event: KeyboardEvent) {
|
||||
if (event.key === 'Escape') cancelLogout()
|
||||
}
|
||||
|
||||
watch(logoutConfirmOpen, (value) => {
|
||||
if (value) document.addEventListener('keydown', handleEscape)
|
||||
else document.removeEventListener('keydown', handleEscape)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => document.removeEventListener('keydown', handleEscape))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -160,18 +171,59 @@ async function handleLogout() {
|
||||
<span>工号 {{ loginStore.user?.employeeNo || '—' }}</span>
|
||||
<span>{{ loginStore.user?.campusName || '—' }} · {{ loginStore.department }}</span>
|
||||
</div>
|
||||
<button type="button" @click="openAccount('profile')">
|
||||
<span aria-hidden="true">👤</span>个人信息
|
||||
</button>
|
||||
<button type="button" @click="openAccount('password')">
|
||||
<span aria-hidden="true">🔑</span>修改密码
|
||||
</button>
|
||||
<button class="account-logout" type="button" :disabled="loggingOut" @click="handleLogout">
|
||||
<span aria-hidden="true">⏻</span>{{ loggingOut ? '退出中…' : '退出登录' }}
|
||||
<button type="button" @click="openAccount('profile')">个人信息</button>
|
||||
<button type="button" @click="openAccount('password')">修改密码</button>
|
||||
<button class="account-logout" type="button" :disabled="loggingOut" @click="requestLogout">
|
||||
退出登录
|
||||
</button>
|
||||
</div>
|
||||
</el-popover>
|
||||
<AccountPanel ref="accountPanel" />
|
||||
|
||||
<!-- 退出二次确认:与账户弹窗同一套页面通用外壳与按钮(不再用 ElMessageBox 默认外观) -->
|
||||
<Teleport to="body">
|
||||
<div v-if="logoutConfirmOpen" class="logout-mask" role="presentation">
|
||||
<section
|
||||
class="logout-card"
|
||||
role="alertdialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="logout-dialog-title"
|
||||
>
|
||||
<header class="logout-header">
|
||||
<strong id="logout-dialog-title">退出登录</strong>
|
||||
<button
|
||||
v-if="!loggingOut"
|
||||
type="button"
|
||||
class="logout-close"
|
||||
aria-label="关闭"
|
||||
@click="cancelLogout"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</header>
|
||||
<div class="logout-body">退出后需要重新登录才能继续使用,确定退出当前账号吗?</div>
|
||||
<footer class="logout-footer">
|
||||
<span class="logout-spacer" />
|
||||
<button
|
||||
type="button"
|
||||
class="action-button action-button--ghost"
|
||||
:disabled="loggingOut"
|
||||
@click="cancelLogout"
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="action-button action-button--danger"
|
||||
:disabled="loggingOut"
|
||||
@click="confirmLogout"
|
||||
>
|
||||
{{ loggingOut ? '退出中…' : '退出登录' }}
|
||||
</button>
|
||||
</footer>
|
||||
</section>
|
||||
</div>
|
||||
</Teleport>
|
||||
</aside>
|
||||
</template>
|
||||
|
||||
@@ -360,7 +412,6 @@ async function handleLogout() {
|
||||
.account-actions button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
padding: 9px 12px;
|
||||
background: transparent;
|
||||
@@ -378,4 +429,127 @@ async function handleLogout() {
|
||||
border-top: 1px solid var(--line);
|
||||
color: #d0534f;
|
||||
}
|
||||
|
||||
/* 退出二次确认弹窗:外壳与按钮沿用页面通用弹窗样式(与 AccountPanel 同一套) */
|
||||
.logout-mask {
|
||||
position: fixed;
|
||||
z-index: 50;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
padding: 16px;
|
||||
overflow-y: auto;
|
||||
background: rgb(9 40 52 / 45%);
|
||||
}
|
||||
|
||||
.logout-card {
|
||||
display: flex;
|
||||
width: 440px;
|
||||
max-width: 96vw;
|
||||
max-height: calc(100vh - 32px);
|
||||
flex-direction: column;
|
||||
margin: auto;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
border-radius: 14px;
|
||||
box-shadow: var(--sh-modal);
|
||||
animation: logout-pop 0.18s ease;
|
||||
}
|
||||
|
||||
.logout-header {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
padding: 12px 18px;
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.logout-header strong {
|
||||
color: var(--ink);
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.logout-close {
|
||||
padding: 0;
|
||||
margin-left: auto;
|
||||
color: var(--mut);
|
||||
font-size: 20px;
|
||||
line-height: 1;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.logout-close:hover {
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.logout-body {
|
||||
min-height: 0;
|
||||
padding: 16px 18px;
|
||||
overflow-y: auto;
|
||||
color: var(--ink);
|
||||
font-size: 13px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.logout-footer {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
padding: 10px 18px;
|
||||
background: #f8fbfc;
|
||||
border-top: 1px solid var(--line);
|
||||
border-radius: 0 0 14px 14px;
|
||||
}
|
||||
|
||||
.logout-spacer {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.action-button {
|
||||
flex-shrink: 0;
|
||||
padding: 8px 16px;
|
||||
color: #fff;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
background: var(--brand);
|
||||
border-radius: 7px;
|
||||
}
|
||||
|
||||
.action-button--ghost {
|
||||
color: var(--brand);
|
||||
background: #fff;
|
||||
border: 1px solid var(--brand);
|
||||
}
|
||||
|
||||
.action-button--ghost:hover:not(:disabled) {
|
||||
background: var(--brand-l);
|
||||
}
|
||||
|
||||
.action-button--danger {
|
||||
color: var(--err);
|
||||
background: #fff;
|
||||
border: 1px solid var(--err);
|
||||
}
|
||||
|
||||
.action-button--danger:hover:not(:disabled) {
|
||||
background: var(--err-l);
|
||||
}
|
||||
|
||||
.action-button:disabled {
|
||||
color: #9aabb3;
|
||||
cursor: not-allowed;
|
||||
background: #eef3f5;
|
||||
}
|
||||
|
||||
@keyframes logout-pop {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.96) translateY(8px);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -72,12 +72,3 @@ a {
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 「退出登录」二次确认的确认按钮:终止会话不可逆,按危险操作着色。
|
||||
MessageBox 挂在 body 上,组件里的 scoped 选择器命中不了,只能放全局。 */
|
||||
.el-message-box .logout-confirm-button,
|
||||
.el-message-box .logout-confirm-button:hover,
|
||||
.el-message-box .logout-confirm-button:focus {
|
||||
background-color: var(--err);
|
||||
border-color: var(--err);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user