You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
OneAuth/new/ui/layout/default.html

262 lines
5.3 KiB
HTML

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!DOCTYPE html>
<html>
<head>
<title>Auth Layout</title>
<style>
.layout-container {
display: flex;
flex-direction: column;
height: 100vh;
width: 100%;
}
.header {
user-select: none;
height: 60px;
background: #1e88e5;
color: white;
display: flex;
align-items: center;
padding: 0 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.header-title {
font-size: 18px;
font-weight: bold;
}
.header-user {
margin-left: auto;
display: flex;
align-items: center;
gap: 10px;
}
.user-avatar {
width: 32px;
height: 32px;
border-radius: 50%;
object-fit: cover;
}
.user-name {
font-size: 14px;
}
.logout-btn {
padding: 5px 10px;
background: #fff;
color: #1e88e5;
border: none;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s;
}
.logout-btn:hover {
background: #f5f5f5;
}
.main-container {
display: flex;
flex: 1;
overflow: hidden;
}
.menu {
width: 200px;
background: #f5f5f5;
border-right: 1px solid #ddd;
padding: 20px 0;
}
.menu-item {
padding: 10px 20px;
cursor: pointer;
transition: background 0.3s;
}
.menu-item:hover {
background: #e0e0e0;
}
.menu-item a {
display: block;
color: inherit;
text-decoration: none;
}
.content {
flex: 1;
overflow-y: auto;
width: 100%;
height: 100%;
}
.footer {
height: 40px;
background: #f5f5f5;
border-top: 1px solid #ddd;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
color: #666;
}
</style>
</head>
<body style="height: 100%;width: 100%;margin: 0;">
<div class="layout-container">
<header class="header">
<div class="header-title">
<a href="/">应用权限管理</a>
</div>
<div class="header-user" v-if="user.name">
<img :src="user.icon" class="user-avatar" alt="用户头像">
<span class="user-name">{{ user.name }}</span>
<button @click="logout" class="logout-btn">
退出
</button>
</div>
<div class="header-user" v-else>
<span>未登录</span>
<a :href="$env.root ? $env.root + '/login' : '/login'" class="logout-btn">
登录
</a>
</div>
</header>
<div class="main-container">
<vslot v='user' class="menu" name='menu'>
<div class="menu-item">
<a href="/app">应用管理</a>
</div>
<div class="menu-item">
<a href="/profile">个人中心</a>
</div>
<div class="menu-item">
<a href="/settings">系统设置</a>
</div>
</vslot>
<vslot class="content">
</vslot>
</div>
<footer class="footer">
Copyright © 2025 veypi. All Rights Reserved..
</footer>
</div>
</body>
<script setup>
user = {}
$env.user = user
class TokenService {
constructor() {
this.tokenKey = 'access';
this.refreshTokenKey = 'refresh';
}
setToken(token) {
localStorage.setItem(this.tokenKey, token);
}
getToken() {
return localStorage.getItem(this.tokenKey);
}
setRefreshToken(refreshToken) {
localStorage.setItem(this.refreshTokenKey, refreshToken);
}
getRefreshToken() {
return localStorage.getItem(this.refreshTokenKey);
}
clearTokens() {
localStorage.removeItem(this.tokenKey);
localStorage.removeItem(this.refreshTokenKey);
}
hasToken() {
return !!this.getToken();
}
parseToken(token) {
try {
const base64Url = token.split('.')[1];
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
return JSON.parse(window.atob(base64));
} catch (error) {
console.error('Token解析失败:', error);
return null;
}
}
async refreshToken() {
const refreshToken = this.getRefreshToken();
if (!refreshToken) {
logout();
this.clearTokens();
return;
}
try {
let data = await api.Post('/api/token', {refresh: refreshToken})
this.setToken(data);
Object.assign(user, this.parseToken(data));
$env.user = user
} catch (e) {
console.error('Token刷新失败:', error);
logout();
}
}
isTokenExpired() {
const token = this.getToken();
if (!token) return true;
const decoded = this.parseToken(token);
if (!decoded) return true;
const currentTime = Date.now() / 1000;
return decoded.exp < currentTime;
}
}
const tokenService = new TokenService();
logout = () => {
let url = '/login';
if ($env.root) {
url = $env.root + url;
}
tokenService.clearTokens();
location.href = url;
}
await tokenService.refreshToken();
// 设置API请求拦截器自动添加token
api.wrapFetch((url, options) => {
const token = tokenService.getToken();
if (token) {
if (!options) {
options = {};
}
if (!options.headers) {
options.headers = {};
}
options.headers.Authorization = `Bearer ${token}`;
}
return fetch(url, options);
});
</script>
</html>