新增空闲锁屏、修复401等问题,新增文件版本管理、共享库等占位符

This commit is contained in:
Cheng Zhou
2026-01-13 13:59:07 +08:00
parent b3cd8e03f2
commit 0c7c03069a
21 changed files with 829 additions and 40 deletions
+149
View File
@@ -0,0 +1,149 @@
<template>
<teleport to="body">
<div class="lock-screen" role="dialog" aria-modal="true">
<div class="lock-backdrop" />
<div class="lock-panel">
<h2 class="lock-title">已锁定</h2>
<p class="lock-desc">30 分钟无操作请输入密码解锁</p>
<el-input
ref="passwordInput"
v-model="password"
type="password"
placeholder="请输入登录密码"
show-password
@keyup.enter="onUnlock"
/>
<div class="lock-actions">
<el-button type="primary" :loading="loading" @click="onUnlock">解锁</el-button>
<el-button @click="onLogout">退出登录</el-button>
</div>
<p v-if="errorMessage" class="lock-error">{{ errorMessage }}</p>
</div>
</div>
</teleport>
</template>
<script setup lang="ts">
import { nextTick, onMounted, ref, watch } from "vue";
import { ElMessage } from "element-plus";
import { useAuthStore } from "../store/auth";
import { useSessionStore } from "../store/session";
import { UNLOCK_MAX_ATTEMPTS, unlockWithPassword, forceLogout } from "../session/sessionManager";
const auth = useAuthStore();
const session = useSessionStore();
const password = ref("");
const loading = ref(false);
const errorMessage = ref("");
const passwordInput = ref<{ focus?: () => void } | null>(null);
const focusPassword = () => {
nextTick(() => {
passwordInput.value?.focus?.();
});
};
const onUnlock = async () => {
if (loading.value) return;
errorMessage.value = "";
const email = auth.user?.email;
if (!email) {
ElMessage.error("无法获取用户信息,请重新登录");
forceLogout();
return;
}
if (!password.value) {
errorMessage.value = "请输入密码";
focusPassword();
return;
}
loading.value = true;
try {
await unlockWithPassword(email, password.value);
password.value = "";
} catch (err: any) {
session.incrementUnlockAttempt();
const message = err?.response?.data?.detail || err?.response?.data?.message || "密码错误";
errorMessage.value = message;
if (session.unlockAttempts >= UNLOCK_MAX_ATTEMPTS) {
ElMessage.error("错误次数过多,请重新登录");
forceLogout();
}
} finally {
loading.value = false;
focusPassword();
}
};
const onLogout = () => {
forceLogout();
};
onMounted(() => {
focusPassword();
});
watch(
() => session.locked,
(value) => {
if (value) {
focusPassword();
}
}
);
</script>
<style scoped>
.lock-screen {
position: fixed;
inset: 0;
z-index: 2000;
display: grid;
place-items: center;
}
.lock-backdrop {
position: absolute;
inset: 0;
background: rgba(7, 10, 20, 0.35);
backdrop-filter: blur(10px);
}
.lock-panel {
position: relative;
z-index: 1;
width: min(420px, 92vw);
background: #ffffff;
border-radius: 16px;
padding: 28px;
box-shadow: 0 18px 48px rgba(15, 20, 35, 0.2);
display: flex;
flex-direction: column;
gap: 16px;
}
.lock-title {
margin: 0;
font-size: 22px;
font-weight: 600;
color: #0f172a;
}
.lock-desc {
margin: 0;
color: #64748b;
font-size: 14px;
}
.lock-actions {
display: flex;
gap: 12px;
justify-content: flex-end;
}
.lock-error {
margin: 0;
color: #d8342c;
font-size: 13px;
}
</style>