1171 lines
34 KiB
Plaintext
1171 lines
34 KiB
Plaintext
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>企业单点登录平台 - 应用中心</title>
|
||
<link rel="stylesheet" href="/css/all.min.css">
|
||
<style>
|
||
/* 基础样式保持不变 */
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
body {
|
||
font-family: 'sans-serif';
|
||
color: #1e293b;
|
||
background: linear-gradient(to bottom, #f8fafc, #f1f5f9);
|
||
min-height: 100vh;
|
||
}
|
||
|
||
.container {
|
||
width: 100%;
|
||
max-width: 1280px;
|
||
margin: 0 auto;
|
||
padding: 0 1rem;
|
||
}
|
||
|
||
@media (min-width: 768px) {
|
||
.container {
|
||
padding: 0 1.5rem;
|
||
}
|
||
}
|
||
|
||
/* 自定义滚动条样式 */
|
||
::-webkit-scrollbar {
|
||
height: 4px;
|
||
width: 4px;
|
||
}
|
||
|
||
::-webkit-scrollbar-track {
|
||
background: transparent;
|
||
border-radius: 10px;
|
||
}
|
||
|
||
::-webkit-scrollbar-thumb {
|
||
background: #c1c1c1;
|
||
border-radius: 10px;
|
||
}
|
||
|
||
::-webkit-scrollbar-thumb:hover {
|
||
background: #a1a1a1;
|
||
}
|
||
|
||
/* 动画类 */
|
||
.animate-in {
|
||
animation-fill-mode: both;
|
||
}
|
||
|
||
.fade-in {
|
||
animation-name: fadeIn;
|
||
animation-duration: 200ms;
|
||
}
|
||
|
||
.slide-in-from-top-2 {
|
||
animation-name: slideInFromTop;
|
||
animation-duration: 200ms;
|
||
}
|
||
|
||
@keyframes fadeIn {
|
||
from { opacity: 0; }
|
||
to { opacity: 1; }
|
||
}
|
||
|
||
@keyframes slideInFromTop {
|
||
from { transform: translateY(-8px); }
|
||
to { transform: translateY(0); }
|
||
}
|
||
|
||
/* 隐藏滚动条但保留功能 */
|
||
.scrollbar-hide::-webkit-scrollbar {
|
||
display: none;
|
||
}
|
||
|
||
.scrollbar-hide {
|
||
-ms-overflow-style: none;
|
||
scrollbar-width: none;
|
||
}
|
||
|
||
/* 顶部导航栏 */
|
||
header {
|
||
position: sticky;
|
||
top: 0;
|
||
z-index: 50;
|
||
background-color: rgba(255, 255, 255, 0.8);
|
||
backdrop-filter: blur(12px);
|
||
border-bottom: 1px solid #e2e8f0;
|
||
transition: all 300ms;
|
||
}
|
||
|
||
.header-content {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 0.75rem 0;
|
||
}
|
||
|
||
.brand {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.75rem;
|
||
}
|
||
|
||
.brand-icon {
|
||
width: 2.5rem;
|
||
height: 2.5rem;
|
||
border-radius: 0.5rem;
|
||
background: linear-gradient(to bottom right, #4f46e5, #8b5cf6);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
box-shadow: 0 4px 6px -1px rgba(79, 70, 229, 0.1), 0 2px 4px -1px rgba(79, 70, 229, 0.06);
|
||
}
|
||
|
||
.brand-icon i {
|
||
color: white;
|
||
font-size: 1.25rem;
|
||
}
|
||
|
||
.brand-name {
|
||
font-size: 1.25rem;
|
||
font-weight: bold;
|
||
background-clip: text;
|
||
-webkit-background-clip: text;
|
||
color: transparent;
|
||
background-image: linear-gradient(to right, #3b82f6, #8b5cf6);
|
||
}
|
||
|
||
.search-user-area {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 1rem;
|
||
}
|
||
|
||
.search-box {
|
||
position: relative;
|
||
display: none;
|
||
}
|
||
|
||
@media (min-width: 768px) {
|
||
.search-box {
|
||
display: block;
|
||
}
|
||
}
|
||
|
||
.search-box input {
|
||
padding: 0.5rem 2.5rem 0.5rem 1rem;
|
||
border-radius: 9999px;
|
||
border: 1px solid #e2e8f0;
|
||
background-color: #f8fafc;
|
||
transition: all 300ms;
|
||
width: 14rem;
|
||
}
|
||
|
||
.search-box:hover input {
|
||
width: 16rem;
|
||
}
|
||
|
||
.search-box input:focus {
|
||
background-color: white;
|
||
outline: none;
|
||
ring: 2px solid rgba(59, 130, 246, 0.3);
|
||
border-color: #93c5fd;
|
||
width: 16rem;
|
||
}
|
||
|
||
.search-box i {
|
||
position: absolute;
|
||
left: 1rem;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
color: #94a3b8;
|
||
transition: color 300ms;
|
||
}
|
||
|
||
.search-box:focus-within i {
|
||
color: #3b82f6;
|
||
}
|
||
|
||
.user-menu-container {
|
||
position: relative;
|
||
}
|
||
|
||
.user-button {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
padding: 0.25rem;
|
||
border-radius: 9999px;
|
||
background-color: transparent;
|
||
border: none;
|
||
cursor: pointer;
|
||
transition: background-color 300ms;
|
||
}
|
||
|
||
.user-button:hover {
|
||
background-color: #f1f5f9;
|
||
}
|
||
|
||
.user-avatar {
|
||
width: 2.25rem;
|
||
height: 2.25rem;
|
||
border-radius: 9999px;
|
||
object-fit: cover;
|
||
border: 2px solid transparent;
|
||
transition: all 300ms;
|
||
}
|
||
|
||
.user-button:hover .user-avatar {
|
||
border-color: #e0e7ff;
|
||
box-shadow: 0 0 0 2px #e0e7ff;
|
||
}
|
||
|
||
.user-info {
|
||
display: none;
|
||
text-align: left;
|
||
}
|
||
|
||
@media (min-width: 768px) {
|
||
.user-info {
|
||
display: block;
|
||
}
|
||
}
|
||
|
||
.user-name {
|
||
font-size: 0.875rem;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.user-email {
|
||
font-size: 0.75rem;
|
||
color: #64748b;
|
||
}
|
||
|
||
.user-menu-icon {
|
||
font-size: 0.75rem;
|
||
color: #94a3b8;
|
||
transition: transform 200ms;
|
||
}
|
||
|
||
.user-menu-icon.rotated {
|
||
transform: rotate(180deg);
|
||
}
|
||
|
||
.user-dropdown {
|
||
position: absolute;
|
||
right: 0;
|
||
top: calc(100% + 0.5rem);
|
||
width: 12rem;
|
||
background-color: white;
|
||
border-radius: 0.5rem;
|
||
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||
border: 1px solid #f1f5f9;
|
||
padding: 0.5rem 0;
|
||
z-index: 50;
|
||
display: none;
|
||
}
|
||
|
||
.user-dropdown.show {
|
||
display: block;
|
||
animation: fadeIn 200ms, slideInFromTop 200ms;
|
||
}
|
||
|
||
.user-dropdown a {
|
||
display: block;
|
||
padding: 0.5rem 1rem;
|
||
font-size: 0.875rem;
|
||
color: #334155;
|
||
text-decoration: none;
|
||
transition: all 300ms;
|
||
}
|
||
|
||
.user-dropdown a:hover {
|
||
background-color: #f8fafc;
|
||
color: #4f46e5;
|
||
}
|
||
|
||
.user-dropdown a i {
|
||
margin-right: 0.5rem;
|
||
color: #94a3b8;
|
||
}
|
||
|
||
.user-dropdown-divider {
|
||
border-top: 1px solid #f1f5f9;
|
||
margin: 0.5rem 0;
|
||
}
|
||
|
||
.user-dropdown .logout {
|
||
color: #dc2626;
|
||
}
|
||
|
||
.user-dropdown .logout:hover {
|
||
background-color: #fee2e2;
|
||
}
|
||
|
||
/* 主要内容区 */
|
||
main {
|
||
padding: 2rem 0;
|
||
min-height: 75vh;
|
||
}
|
||
|
||
.page-title {
|
||
margin-bottom: 2rem;
|
||
}
|
||
|
||
@media (min-width: 768px) {
|
||
.page-title {
|
||
margin-bottom: 3rem;
|
||
}
|
||
}
|
||
|
||
.page-title h2 {
|
||
font-size: clamp(1.75rem, 4vw, 2.5rem);
|
||
font-weight: bold;
|
||
color: #1e293b;
|
||
margin-bottom: 0.75rem;
|
||
background-clip: text;
|
||
-webkit-background-clip: text;
|
||
color: transparent;
|
||
background-image: linear-gradient(to right, #1e293b, #64748b);
|
||
}
|
||
|
||
.page-title p {
|
||
color: #64748b;
|
||
max-width: 36rem;
|
||
}
|
||
|
||
/* 排序和分组控制区 */
|
||
.controls-container {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 1.5rem;
|
||
flex-wrap: wrap;
|
||
gap: 1rem;
|
||
}
|
||
|
||
/* 应用分组导航 */
|
||
.group-nav {
|
||
overflow-x: auto;
|
||
padding-bottom: 0.5rem;
|
||
flex-grow: 1;
|
||
}
|
||
|
||
.group-buttons {
|
||
display: flex;
|
||
gap: 0.5rem;
|
||
min-width: max-content;
|
||
}
|
||
|
||
.group-button {
|
||
padding: 0.625rem 1.25rem;
|
||
border-radius: 9999px;
|
||
transition: all 300ms;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
font-size: 0.875rem;
|
||
white-space: nowrap;
|
||
background-color: white;
|
||
color: #334155;
|
||
border: 1px solid #e2e8f0;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.group-button:hover {
|
||
background-color: #f8fafc;
|
||
border-color: #e0e7ff;
|
||
}
|
||
|
||
.group-button.active {
|
||
background-color: #4f46e5;
|
||
color: white;
|
||
box-shadow: 0 4px 6px -1px rgba(79, 70, 229, 0.25);
|
||
border-color: transparent;
|
||
}
|
||
|
||
/* 排序控制 */
|
||
.sort-control {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
background-color: white;
|
||
border: 1px solid #e2e8f0;
|
||
border-radius: 9999px;
|
||
padding: 0.375rem;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.sort-label {
|
||
font-size: 0.875rem;
|
||
color: #64748b;
|
||
padding-left: 0.75rem;
|
||
}
|
||
|
||
.sort-button {
|
||
padding: 0.25rem 0.75rem;
|
||
border-radius: 9999px;
|
||
border: none;
|
||
background-color: transparent;
|
||
color: #334155;
|
||
font-size: 0.875rem;
|
||
cursor: pointer;
|
||
transition: all 300ms;
|
||
}
|
||
|
||
.sort-button.active {
|
||
background-color: #4f46e5;
|
||
color: white;
|
||
}
|
||
|
||
.sort-button:not(.active):hover {
|
||
background-color: #f8fafc;
|
||
}
|
||
|
||
/* 应用列表网格 */
|
||
.apps-grid {
|
||
display: grid;
|
||
grid-template-columns: 1fr;
|
||
gap: 1.25rem;
|
||
}
|
||
|
||
@media (min-width: 640px) {
|
||
.apps-grid {
|
||
grid-template-columns: repeat(2, 1fr);
|
||
}
|
||
}
|
||
|
||
@media (min-width: 1024px) {
|
||
.apps-grid {
|
||
grid-template-columns: repeat(3, 1fr);
|
||
}
|
||
}
|
||
|
||
@media (min-width: 1280px) {
|
||
.apps-grid {
|
||
grid-template-columns: repeat(4, 1fr);
|
||
}
|
||
}
|
||
|
||
/* 应用卡片 */
|
||
.app-card {
|
||
background-color: white;
|
||
border-radius: 1rem;
|
||
border: 1px solid #f1f5f9;
|
||
overflow: hidden;
|
||
cursor: pointer;
|
||
transition: all 300ms;
|
||
position: relative;
|
||
}
|
||
|
||
.app-card:hover {
|
||
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||
transform: translateY(-0.25rem);
|
||
}
|
||
|
||
.app-card-header {
|
||
padding: 1.25rem 1.25rem 0;
|
||
}
|
||
|
||
.app-header-content {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: flex-start;
|
||
}
|
||
|
||
.app-icon-container {
|
||
width: 3.5rem;
|
||
height: 3.5rem;
|
||
border-radius: 0.5rem;
|
||
overflow: hidden;
|
||
border: 2px solid #f1f5f9;
|
||
transition: border-color 300ms;
|
||
}
|
||
|
||
.app-card:hover .app-icon-container {
|
||
border-color: #e0e7ff;
|
||
}
|
||
|
||
.app-icon {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
}
|
||
|
||
.app-tags {
|
||
display: flex;
|
||
gap: 0.25rem;
|
||
}
|
||
|
||
.app-tag {
|
||
padding: 0.125rem 0.5rem;
|
||
font-size: 0.75rem;
|
||
border-radius: 9999px;
|
||
}
|
||
|
||
.tag-recent {
|
||
background-color: #e0e7ff;
|
||
color: #4f46e5;
|
||
}
|
||
|
||
.tag-frequent {
|
||
background-color: #fed7d7;
|
||
color: #dc2626;
|
||
}
|
||
|
||
.app-card-body {
|
||
padding: 1.25rem;
|
||
padding-top: 1rem;
|
||
}
|
||
|
||
.app-title-container {
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
|
||
.app-name {
|
||
font-weight: 600;
|
||
color: #1e293b;
|
||
margin-bottom: 0.25rem;
|
||
transition: color 300ms;
|
||
}
|
||
|
||
.app-card:hover .app-name {
|
||
color: #4f46e5;
|
||
}
|
||
|
||
.app-group {
|
||
font-size: 0.75rem;
|
||
color: #64748b;
|
||
}
|
||
|
||
.app-description {
|
||
font-size: 0.875rem;
|
||
color: #475569;
|
||
margin-bottom: 1rem;
|
||
display: -webkit-box;
|
||
-webkit-line-clamp: 3;
|
||
-webkit-box-orient: vertical;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.app-card-footer {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding-top: 0.5rem;
|
||
border-top: 1px solid #f8fafc;
|
||
}
|
||
|
||
.app-usage {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.app-usage i {
|
||
font-size: 0.75rem;
|
||
color: #94a3b8;
|
||
margin-right: 0.25rem;
|
||
}
|
||
|
||
.usage-count {
|
||
font-size: 0.75rem;
|
||
color: #64748b;
|
||
}
|
||
|
||
.app-action {
|
||
width: 2rem;
|
||
height: 2rem;
|
||
border-radius: 9999px;
|
||
background-color: #f8fafc;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: #94a3b8;
|
||
transition: all 300ms;
|
||
}
|
||
|
||
.app-card:hover .app-action {
|
||
background-color: #e0e7ff;
|
||
color: #4f46e5;
|
||
}
|
||
|
||
.app-action i {
|
||
font-size: 0.875rem;
|
||
}
|
||
|
||
.app-accent {
|
||
position: absolute;
|
||
left: 0;
|
||
top: 0;
|
||
bottom: 0;
|
||
width: 0.375rem;
|
||
opacity: 0;
|
||
transition: opacity 300ms;
|
||
}
|
||
|
||
.app-card:hover .app-accent {
|
||
opacity: 1;
|
||
}
|
||
|
||
/* 无搜索结果时的空状态 */
|
||
.empty-state {
|
||
grid-column: 1 / -1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 4rem 0;
|
||
text-align: center;
|
||
}
|
||
|
||
.empty-icon-container {
|
||
width: 6rem;
|
||
height: 6rem;
|
||
margin-bottom: 1.25rem;
|
||
background-color: #f8fafc;
|
||
border-radius: 9999px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.empty-icon {
|
||
color: #cbd5e1;
|
||
font-size: 4rem;
|
||
}
|
||
|
||
.empty-title {
|
||
font-size: 1.25rem;
|
||
font-weight: 500;
|
||
color: #1e293b;
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
|
||
.empty-message {
|
||
color: #64748b;
|
||
max-width: 24rem;
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
|
||
.reset-button {
|
||
padding: 0.5rem 1rem;
|
||
background-color: #4f46e5;
|
||
color: white;
|
||
border-radius: 0.5rem;
|
||
border: none;
|
||
cursor: pointer;
|
||
transition: background-color 300ms;
|
||
font-size: 0.875rem;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.25rem;
|
||
}
|
||
|
||
.reset-button:hover {
|
||
background-color: #4338ca;
|
||
}
|
||
|
||
/* 页脚 */
|
||
footer {
|
||
background-color: white;
|
||
border-top: 1px solid #e2e8f0;
|
||
margin-top: 4rem;
|
||
}
|
||
|
||
.footer-content {
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 2rem 0;
|
||
}
|
||
|
||
@media (min-width: 768px) {
|
||
.footer-content {
|
||
flex-direction: row;
|
||
}
|
||
}
|
||
|
||
.copyright {
|
||
font-size: 0.875rem;
|
||
color: #64748b;
|
||
margin-bottom: 1rem;
|
||
}
|
||
|
||
@media (min-width: 768px) {
|
||
.copyright {
|
||
margin-bottom: 0;
|
||
}
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="min-h-screen bg-gradient-to-b from-slate-50 to-slate-100 font-sans text-slate-800">
|
||
<!-- 顶部导航栏 -->
|
||
<header class="sticky top-0 z-50 bg-white/80 backdrop-blur-md border-b border-slate-200 transition-all duration-300">
|
||
<div class="container">
|
||
<div class="header-content">
|
||
<!-- 品牌标识 -->
|
||
<div class="brand">
|
||
<div class="brand-icon">
|
||
<i class="fa fa-key text-white text-xl"></i>
|
||
</div>
|
||
<h1 class="brand-name">企业单点登录平台</h1>
|
||
</div>
|
||
|
||
<!-- 搜索与用户区域 -->
|
||
<div class="search-user-area">
|
||
<!-- 搜索框 -->
|
||
<div class="search-box">
|
||
<input
|
||
type="text"
|
||
placeholder="搜索应用..."
|
||
class="pl-10 pr-4 py-2 rounded-full border border-slate-200 bg-slate-50 focus:bg-white focus:outline-none focus:ring-2 focus:ring-indigo-500/30 focus:border-indigo-300 transition-all w-56"
|
||
>
|
||
</div>
|
||
|
||
<!-- 用户信息与菜单 -->
|
||
<div class="user-menu-container">
|
||
<button class="user-button" id="userButton">
|
||
<img
|
||
src="${user.avatar}"
|
||
alt="用户头像"
|
||
class="user-avatar"
|
||
>
|
||
<div class="user-info">
|
||
<p class="user-name">${user.username}</p>
|
||
<p class="user-email">${user.email}</p>
|
||
</div>
|
||
<i class="fa fa-chevron-down user-menu-icon" id="userMenuIcon"></i>
|
||
</button>
|
||
|
||
<!-- 用户菜单 -->
|
||
<div class="user-dropdown" id="userDropdown">
|
||
<#-- <a href="#" class="dropdown-item">-->
|
||
<#-- <i class="fa fa-user mr-2 text-slate-400"></i>个人资料-->
|
||
<#-- </a>-->
|
||
<#-- <a href="#" class="dropdown-item">-->
|
||
<#-- <i class="fa fa-cog mr-2 text-slate-400"></i>账户设置-->
|
||
<#-- </a>-->
|
||
<#-- <div class="user-dropdown-divider"></div>-->
|
||
<a href="/logout" class="dropdown-item logout">
|
||
<i class="fa fa-sign-out mr-2"></i>退出登录
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- 主要内容区 -->
|
||
<main class="container">
|
||
<!-- 页面标题区 -->
|
||
<div class="page-title">
|
||
<h2>应用中心</h2>
|
||
<p>
|
||
一站式访问您的所有企业应用,通过单点登录快速安全地接入各类业务系统
|
||
</p>
|
||
</div>
|
||
|
||
|
||
<!-- 排序和分组控制区 -->
|
||
<div class="controls-container">
|
||
<!-- 应用分组导航 -->
|
||
<div class="group-nav scrollbar-hide">
|
||
<div class="group-buttons">
|
||
<button class="group-button active" data-group="all">
|
||
<i class="fa fa-check-circle"></i>
|
||
<span>全部应用</span>
|
||
</button>
|
||
|
||
<#list appGroup as item>
|
||
<button class="group-button" data-group="${item.ssoAppGroupId}">
|
||
<i class="fa fa-th-large"></i>
|
||
<span>${item.name}</span>
|
||
</button>
|
||
</#list>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 排序控制 -->
|
||
<div class="sort-control">
|
||
<span class="sort-label">排序方式:</span>
|
||
<button class="sort-button active" data-sort="usage">按使用次数</button>
|
||
<button class="sort-button" data-sort="default">默认排序</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 应用列表网格 -->
|
||
<div class="apps-grid" id="appsGrid">
|
||
<!-- 应用卡片1 -->
|
||
<#list appList as item>
|
||
<div class="app-card" data-group="${item.ssoAppGroupId}" data-id="${item.ssoAppId}" data-url="${item.appUrl! '/'}">
|
||
<div class="app-card-header">
|
||
<div class="app-header-content">
|
||
<div class="app-icon-container">
|
||
<img src="${item.appIcon}" alt="${item.appName}" class="app-icon">
|
||
</div>
|
||
<div class="app-tags">
|
||
<span class="app-tag tag-recent">最近</span>
|
||
<span class="app-tag tag-frequent">常用</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="app-card-body">
|
||
<div class="app-title-container">
|
||
<h3 class="app-name">${item.appName}</h3>
|
||
<p class="app-group">${item.ssoAppGroup.name}</p>
|
||
</div>
|
||
|
||
<p class="app-description">${item.remark}</p>
|
||
|
||
<div class="app-card-footer">
|
||
<div class="app-usage">
|
||
<i class="fa fa-signal"></i>
|
||
<span class="usage-count">0次使用</span>
|
||
</div>
|
||
<div class="app-action">
|
||
<i class="fa fa-arrow-right"></i>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="app-accent" style="background-color: #4f46e5;"></div>
|
||
</div>
|
||
</#list>
|
||
|
||
</div>
|
||
</main>
|
||
|
||
<!-- 页脚 -->
|
||
<footer>
|
||
<div class="container">
|
||
<div class="footer-content">
|
||
<div class="copyright">
|
||
© 2025 企业单点登录平台. 保留所有权利.
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</footer>
|
||
</div>
|
||
|
||
<script>
|
||
// 应用使用数据管理
|
||
let appUsageData = {};
|
||
const APPS_STORAGE_KEY = 'appUsageData';
|
||
const DEFAULT_SORT = 'usage'; // 默认按使用次数排序
|
||
let currentSort = DEFAULT_SORT;
|
||
let currentGroup = 'all';
|
||
let currentSearch = '';
|
||
|
||
// 从本地存储加载应用使用数据
|
||
function loadAppUsageData() {
|
||
try {
|
||
const storedData = localStorage.getItem(APPS_STORAGE_KEY);
|
||
if (storedData) {
|
||
appUsageData = JSON.parse(storedData);
|
||
}
|
||
} catch (error) {
|
||
console.error('加载应用使用数据失败:', error);
|
||
appUsageData = {};
|
||
}
|
||
|
||
// 更新UI显示
|
||
updateUsageCounts();
|
||
}
|
||
|
||
// 保存应用使用数据到本地存储
|
||
function saveAppUsageData() {
|
||
try {
|
||
localStorage.setItem(APPS_STORAGE_KEY, JSON.stringify(appUsageData));
|
||
} catch (error) {
|
||
console.error('保存应用使用数据失败:', error);
|
||
}
|
||
}
|
||
|
||
// 更新应用使用次数
|
||
function updateAppUsage(appId) {
|
||
// 确保应用ID存在于数据中
|
||
if (!appUsageData[appId]) {
|
||
// 从DOM获取初始计数
|
||
const card = document.querySelector(`.app-card[data-id="`+appId+`"]`);
|
||
const countElem = card.querySelector('.usage-count');
|
||
const initialCount = parseInt(countElem.textContent) || 0;
|
||
appUsageData[appId] = initialCount;
|
||
}
|
||
|
||
// 增加使用次数
|
||
appUsageData[appId]++;
|
||
|
||
// 保存数据
|
||
saveAppUsageData();
|
||
|
||
// 更新UI
|
||
updateUsageCounts();
|
||
|
||
// 如果当前是按使用次数排序,则重新排序
|
||
if (currentSort === 'usage') {
|
||
sortApps();
|
||
}
|
||
}
|
||
|
||
// 更新UI上的使用次数显示
|
||
function updateUsageCounts() {
|
||
const appCards = document.querySelectorAll('.app-card');
|
||
|
||
appCards.forEach(card => {
|
||
const appId = card.getAttribute('data-id');
|
||
const countElem = card.querySelector('.usage-count');
|
||
|
||
// 获取当前计数
|
||
let count = appUsageData[appId];
|
||
|
||
// 如果没有记录,使用初始值
|
||
if (count === undefined) {
|
||
count = parseInt(countElem.textContent) || 0;
|
||
appUsageData[appId] = count;
|
||
}
|
||
|
||
// 更新显示
|
||
countElem.textContent = count+`次使用`;
|
||
|
||
// 更新标签 - 如果使用次数超过70,标记为常用
|
||
const tagContainer = card.querySelector('.app-tags');
|
||
let frequentTag = card.querySelector('.tag-frequent');
|
||
|
||
if (count > 70) {
|
||
if (!frequentTag) {
|
||
frequentTag = document.createElement('span');
|
||
frequentTag.className = 'app-tag tag-frequent';
|
||
frequentTag.textContent = '常用';
|
||
tagContainer.appendChild(frequentTag);
|
||
}
|
||
} else if (frequentTag) {
|
||
frequentTag.remove();
|
||
}
|
||
});
|
||
|
||
// 保存更新后的数据
|
||
saveAppUsageData();
|
||
}
|
||
|
||
// 对应用进行排序
|
||
function sortApps() {
|
||
const appsGrid = document.getElementById('appsGrid');
|
||
const appCards = Array.from(document.querySelectorAll('.app-card'));
|
||
|
||
// 根据当前排序方式排序
|
||
if (currentSort === 'usage') {
|
||
// 按使用次数降序排序
|
||
appCards.sort((a, b) => {
|
||
const idA = a.getAttribute('data-id');
|
||
const idB = b.getAttribute('data-id');
|
||
|
||
const countA = appUsageData[idA] || parseInt(a.querySelector('.usage-count').textContent) || 0;
|
||
const countB = appUsageData[idB] || parseInt(b.querySelector('.usage-count').textContent) || 0;
|
||
|
||
return countB - countA;
|
||
});
|
||
} else {
|
||
// 默认排序 - 按ID升序
|
||
appCards.sort((a, b) => {
|
||
const idA = parseInt(a.getAttribute('data-id'));
|
||
const idB = parseInt(b.getAttribute('data-id'));
|
||
return idA - idB;
|
||
});
|
||
}
|
||
|
||
// 先隐藏所有卡片,用于动画效果
|
||
appCards.forEach(card => {
|
||
card.style.opacity = '0';
|
||
card.style.transform = 'translateY(10px)';
|
||
});
|
||
|
||
// 重新添加到容器中(已排序)
|
||
appCards.forEach(card => {
|
||
appsGrid.appendChild(card);
|
||
|
||
// 添加动画效果
|
||
setTimeout(() => {
|
||
card.style.transition = 'opacity 0.3s, transform 0.3s';
|
||
card.style.opacity = '1';
|
||
card.style.transform = 'translateY(0)';
|
||
}, 50);
|
||
});
|
||
}
|
||
|
||
// 筛选应用(按组和搜索)
|
||
function filterApps() {
|
||
const appCards = document.querySelectorAll('.app-card');
|
||
|
||
appCards.forEach(card => {
|
||
const group = card.getAttribute('data-group');
|
||
const appName = card.querySelector('.app-name').textContent.toLowerCase();
|
||
const appDesc = card.querySelector('.app-description').textContent.toLowerCase();
|
||
const appGroup = card.querySelector('.app-group').textContent.toLowerCase();
|
||
|
||
// 组筛选
|
||
const groupMatch = currentGroup === 'all' || group === currentGroup;
|
||
|
||
// 搜索筛选
|
||
const searchMatch = !currentSearch ||
|
||
appName.includes(currentSearch) ||
|
||
appDesc.includes(currentSearch) ||
|
||
appGroup.includes(currentSearch);
|
||
|
||
// 显示或隐藏卡片
|
||
if (groupMatch && searchMatch) {
|
||
card.style.display = 'block';
|
||
} else {
|
||
card.style.display = 'none';
|
||
}
|
||
});
|
||
}
|
||
|
||
// 排序应用
|
||
function sortApps() {
|
||
// 先获取当前可见的应用
|
||
const visibleApps = Array.from(document.querySelectorAll('.app-card')).filter(card => {
|
||
return card.style.display !== 'none';
|
||
});
|
||
|
||
// 对可见应用进行排序
|
||
if (currentSort === 'usage') {
|
||
visibleApps.sort((a, b) => {
|
||
const idA = a.getAttribute('data-id');
|
||
const idB = b.getAttribute('data-id');
|
||
|
||
const countA = appUsageData[idA] || parseInt(a.querySelector('.usage-count').textContent) || 0;
|
||
const countB = appUsageData[idB] || parseInt(b.querySelector('.usage-count').textContent) || 0;
|
||
|
||
return countB - countA;
|
||
});
|
||
} else {
|
||
// 默认排序 - 按ID升序
|
||
visibleApps.sort((a, b) => {
|
||
const idA = parseInt(a.getAttribute('data-id'));
|
||
const idB = parseInt(b.getAttribute('data-id'));
|
||
return idA - idB;
|
||
});
|
||
}
|
||
|
||
// 重新排列可见应用
|
||
const appsGrid = document.getElementById('appsGrid');
|
||
|
||
visibleApps.forEach(card => {
|
||
appsGrid.appendChild(card);
|
||
});
|
||
}
|
||
|
||
// 初始化
|
||
function init() {
|
||
// 加载应用使用数据
|
||
loadAppUsageData();
|
||
|
||
// 用户菜单交互
|
||
const userButton = document.getElementById('userButton');
|
||
const userDropdown = document.getElementById('userDropdown');
|
||
const userMenuIcon = document.getElementById('userMenuIcon');
|
||
|
||
userButton.addEventListener('click', () => {
|
||
userDropdown.classList.toggle('show');
|
||
userMenuIcon.classList.toggle('rotated');
|
||
});
|
||
|
||
// 点击外部关闭用户菜单
|
||
document.addEventListener('click', (event) => {
|
||
if (!userButton.contains(event.target) && !userDropdown.contains(event.target)) {
|
||
userDropdown.classList.remove('show');
|
||
userMenuIcon.classList.remove('rotated');
|
||
}
|
||
});
|
||
|
||
// 应用分组筛选
|
||
const groupButtons = document.querySelectorAll('.group-button');
|
||
|
||
groupButtons.forEach(button => {
|
||
button.addEventListener('click', () => {
|
||
// 更新按钮状态
|
||
groupButtons.forEach(btn => {
|
||
btn.classList.remove('active');
|
||
btn.querySelector('i').className = 'fa fa-th-large';
|
||
});
|
||
|
||
button.classList.add('active');
|
||
button.querySelector('i').className = 'fa fa-check-circle';
|
||
|
||
// 更新当前组
|
||
currentGroup = button.getAttribute('data-group');
|
||
|
||
// 筛选应用
|
||
filterApps();
|
||
|
||
// 排序应用
|
||
sortApps();
|
||
});
|
||
});
|
||
|
||
// 应用点击事件
|
||
const appCards = document.querySelectorAll('.app-card');
|
||
appCards.forEach(card => {
|
||
card.addEventListener('click', () => {
|
||
const appId = card.getAttribute('data-id');
|
||
const appName = card.querySelector('.app-name').textContent;
|
||
|
||
// 更新使用次数
|
||
updateAppUsage(appId);
|
||
// 排序应用
|
||
sortApps();
|
||
console.log(`登录到应用: `+appName);
|
||
const url = card.getAttribute("data-url");
|
||
window.open(url, '_blank');
|
||
// 在实际应用中,这里会跳转到对应的应用
|
||
});
|
||
});
|
||
|
||
// 搜索功能
|
||
const searchInput = document.querySelector('.search-box input');
|
||
|
||
searchInput.addEventListener('input', () => {
|
||
currentSearch = searchInput.value.toLowerCase().trim();
|
||
|
||
// 筛选应用
|
||
filterApps();
|
||
|
||
// 排序应用
|
||
sortApps();
|
||
});
|
||
|
||
// 排序控制
|
||
const sortButtons = document.querySelectorAll('.sort-button');
|
||
|
||
sortButtons.forEach(button => {
|
||
button.addEventListener('click', () => {
|
||
// 更新按钮状态
|
||
sortButtons.forEach(btn => {
|
||
btn.classList.remove('active');
|
||
});
|
||
|
||
button.classList.add('active');
|
||
|
||
// 更新当前排序方式
|
||
currentSort = button.getAttribute('data-sort');
|
||
|
||
// 排序应用
|
||
sortApps();
|
||
});
|
||
});
|
||
|
||
// 排序应用
|
||
sortApps();
|
||
}
|
||
|
||
// 页面加载完成后初始化
|
||
document.addEventListener('DOMContentLoaded', init);
|
||
</script>
|
||
</body>
|
||
</html> |